medialAxisMeshMover.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2014-2015 OpenFOAM Foundation
9  Copyright (C) 2015-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
29 #include "medialAxisMeshMover.H"
31 #include "pointFields.H"
32 #include "valuePointPatchFields.H"
33 #include "PointEdgeWave.H"
34 #include "meshRefinement.H"
35 #include "unitConversion.H"
36 #include "PatchTools.H"
37 #include "OBJstream.H"
38 #include "PointData.H"
40 #include "pointSet.H"
41 
42 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
43 
44 namespace Foam
45 {
46  defineTypeNameAndDebug(medialAxisMeshMover, 0);
47 
49  (
50  externalDisplacementMeshMover,
51  medialAxisMeshMover,
52  dictionary
53  );
54 }
55 
56 
57 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
58 
59 // Tries and find a medial axis point. Done by comparing vectors to nearest
60 // wall point for both vertices of edge.
61 bool Foam::medialAxisMeshMover::isMaxEdge
62 (
63  const List<PointData<vector>>& pointWallDist,
64  const label edgeI,
65  const scalar minCos,
66  const bool disableWallEdges
67 ) const
68 {
69  const pointField& points = mesh().points();
70  const edge& e = mesh().edges()[edgeI];
71 
72  if (disableWallEdges)
73  {
74  // 1. Do not mark edges with one side on moving wall.
75  vector v0(points[e[0]] - pointWallDist[e[0]].origin());
76  scalar magV0(mag(v0));
77  if (magV0 < SMALL)
78  {
79  return false;
80  }
81 
82  vector v1(points[e[1]] - pointWallDist[e[1]].origin());
83  scalar magV1(mag(v1));
84  if (magV1 < SMALL)
85  {
86  return false;
87  }
88  }
89 
91  //vector v0(points[e[0]] - pointWallDist[e[0]].origin());
92  //scalar magV0(mag(v0));
93  //vector v1(points[e[1]] - pointWallDist[e[1]].origin());
94  //scalar magV1(mag(v1));
95  //if (magV0 < SMALL && magV1 < SMALL)
96  //{
97  // return false;
98  //}
99 
101  //v0 /= magV0;
102  //v1 /= magV1;
103  //
105  //if ((v0 & v1) < minCos)
106  //{
107  // return true;
108  //}
109  //else
110  //{
111  // return false;
112  //}
113 
114  //- Detect based on extrusion vector differing for both endpoints
115  // the idea is that e.g. a sawtooth wall can still be extruded
116  // successfully as long as it is done all to the same direction.
117  if ((pointWallDist[e[0]].data() & pointWallDist[e[1]].data()) < minCos)
118  {
119  return true;
120  }
121 
122  return false;
123 }
124 
125 
126 void Foam::medialAxisMeshMover::update(const dictionary& coeffDict)
127 {
128  Info<< typeName
129  << " : Calculating distance to Medial Axis ..." << endl;
130 
131  const pointField& points = mesh().points();
132 
133  const indirectPrimitivePatch& pp = adaptPatchPtr_();
134  const labelList& meshPoints = pp.meshPoints();
135 
136 
137  // Read a few parameters
138  // ~~~~~~~~~~~~~~~~~~~~~
139 
140  //- Smooth surface normals
141  const label nSmoothSurfaceNormals
142  (
143  meshRefinement::get<label>
144  (
145  coeffDict,
146  "nSmoothSurfaceNormals",
147  dryRun_
148  )
149  );
150 
151  const scalar minMedialAxisAngle
152  (
153  meshRefinement::get<scalar>
154  (
155  coeffDict,
156  "minMedialAxisAngle",
157  dryRun_
158  )
159  );
160 
161  const scalar minMedialAxisAngleCos(Foam::cos(degToRad(minMedialAxisAngle)));
162 
163  //- Feature angle when to stop adding layers
164  const scalar featureAngle
165  (
166  meshRefinement::get<scalar>(coeffDict, "featureAngle", dryRun_)
167  );
168 
169  //- When to slip along wall
170  const scalar slipFeatureAngle
171  (
172  coeffDict.getOrDefault<scalar>("slipFeatureAngle", (0.5*featureAngle))
173  );
174 
175  //- Smooth internal normals
176  const label nSmoothNormals
177  (
178  meshRefinement::get<label>(coeffDict, "nSmoothNormals", dryRun_)
179  );
180 
181  //- Number of edges walking out
182  const label nMedialAxisIter = coeffDict.getOrDefault<label>
183  (
184  "nMedialAxisIter",
186  );
187 
188  const bool disableWallEdges = coeffDict.getOrDefault<bool>
189  (
190  "disableWallEdges",
191  false
192  );
193 
194 
195 
196  // Predetermine mesh edges
197  // ~~~~~~~~~~~~~~~~~~~~~~~
198 
199  // Precalulate (mesh) master point/edge (only relevant for shared pts/edges)
200  const bitSet isMeshMasterPoint(syncTools::getMasterPoints(mesh()));
201  const bitSet isMeshMasterEdge(syncTools::getMasterEdges(mesh()));
202  // Precalculate meshEdge per pp edge
203  const labelList meshEdges
204  (
205  pp.meshEdges
206  (
207  mesh().edges(),
208  mesh().pointEdges()
209  )
210  );
211 
212  // Precalulate (patch) master point/edge
213  const bitSet isPatchMasterPoint
214  (
216  (
217  mesh(),
218  meshPoints
219  )
220  );
221  const bitSet isPatchMasterEdge
222  (
224  (
225  mesh(),
226  meshEdges
227  )
228  );
229 
230  // Determine pointNormal
231  // ~~~~~~~~~~~~~~~~~~~~~
232 
233  pointField pointNormals(PatchTools::pointNormals(mesh(), pp));
234 
235  // Smooth patch normal vectors
236  fieldSmoother_.smoothPatchNormals
237  (
238  nSmoothSurfaceNormals,
239  isPatchMasterPoint,
240  isPatchMasterEdge,
241  pp,
242  pointNormals
243  );
244 
245 
246  // Calculate distance to pp points
247  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
248 
249  // Distance to wall
250  List<PointData<vector>> pointWallDist(mesh().nPoints());
251 
252  // Dummy additional info for PointEdgeWave
253  int dummyTrackData = 0;
254 
255 
256  // 1. Calculate distance to points where displacement is specified.
257  {
258  // Seed data.
259  List<PointData<vector>> wallInfo(meshPoints.size());
260 
261  forAll(meshPoints, patchPointI)
262  {
263  label pointI = meshPoints[patchPointI];
264  wallInfo[patchPointI] = PointData<vector>
265  (
266  points[pointI],
267  0.0,
268  pointNormals[patchPointI] // surface normals
269  );
270  }
271 
272  // Do all calculations
273  List<PointData<vector>> edgeWallDist(mesh().nEdges());
274  PointEdgeWave<PointData<vector>> wallDistCalc
275  (
276  mesh(),
277  meshPoints,
278  wallInfo,
279  pointWallDist,
280  edgeWallDist,
281  0, // max iterations
282  dummyTrackData
283  );
284  wallDistCalc.iterate(nMedialAxisIter);
285 
286  const label nUnvisit = returnReduce
287  (
288  wallDistCalc.nUnvisitedPoints(),
289  sumOp<label>()
290  );
291 
292  if (nUnvisit > 0)
293  {
294  if (nMedialAxisIter > 0)
295  {
296  Info<< typeName
297  << " : Limited walk to " << nMedialAxisIter
298  << " steps. Not visited " << nUnvisit
299  << " out of " << mesh().globalData().nTotalPoints()
300  << " points" << endl;
301  }
302  else
303  {
305  << "Walking did not visit all points." << nl
306  << " Did not visit " << nUnvisit
307  << " out of " << mesh().globalData().nTotalPoints()
308  << " points. This is not necessarily a problem" << nl
309  << " and might be due to faceZones splitting of part"
310  << " of the domain." << nl << endl;
311  }
312  }
313  }
314 
315 
316  // 2. Find points with max distance and transport information back to
317  // wall.
318  {
319  List<pointEdgePoint> pointMedialDist(mesh().nPoints());
320  List<pointEdgePoint> edgeMedialDist(mesh().nEdges());
321 
322  // Seed point data.
323  DynamicList<pointEdgePoint> maxInfo(meshPoints.size());
324  DynamicList<label> maxPoints(meshPoints.size());
325 
326  // 1. Medial axis points
327 
328  const edgeList& edges = mesh().edges();
329 
330  forAll(edges, edgeI)
331  {
332  const edge& e = edges[edgeI];
333 
334  if
335  (
336  !pointWallDist[e[0]].valid(dummyTrackData)
337  || !pointWallDist[e[1]].valid(dummyTrackData)
338  )
339  {
340  // Unvisited point. See above about nUnvisit warning
341  forAll(e, ep)
342  {
343  label pointI = e[ep];
344 
345  if (!pointMedialDist[pointI].valid(dummyTrackData))
346  {
347  maxPoints.append(pointI);
348  maxInfo.append
349  (
350  pointEdgePoint
351  (
352  points[pointI],
353  0.0
354  )
355  );
356  pointMedialDist[pointI] = maxInfo.last();
357  }
358  }
359 
360  }
361  else if
362  (
363  isMaxEdge
364  (
365  pointWallDist,
366  edgeI,
367  minMedialAxisAngleCos,
368  disableWallEdges
369  )
370  )
371  {
372  // Both end points of edge have very different nearest wall
373  // point. Mark both points as medial axis points.
374 
375  // Approximate medial axis location on edge.
376  //const point medialAxisPt = e.centre(points);
377  vector eVec = e.vec(points);
378  scalar eMag = mag(eVec);
379  if (eMag > VSMALL)
380  {
381  eVec /= eMag;
382 
383  // Calculate distance along edge
384  const point& p0 = points[e[0]];
385  const point& origin0 = pointWallDist[e[0]].origin();
386  const point& p1 = points[e[1]];
387  const point& origin1 = pointWallDist[e[1]].origin();
388  scalar dist0 = (p0-origin0) & eVec;
389  scalar dist1 = (origin1-p1) & eVec;
390  scalar s = 0.5*(dist1+eMag+dist0);
391 
392  point medialAxisPt(vector::max);
393  if (s <= dist0)
394  {
395  // Make sure point is not on wall. Note that this
396  // check used to be inside isMaxEdge.
397  if (magSqr((p0-origin0)) > Foam::sqr(SMALL))
398  {
399  medialAxisPt = p0;
400  }
401  }
402  else if (s >= dist0+eMag)
403  {
404  // Make sure point is not on wall. Note that this
405  // check used to be inside isMaxEdge.
406  if (magSqr((p1-origin1)) > Foam::sqr(SMALL))
407  {
408  medialAxisPt = p1;
409  }
410  }
411  else
412  {
413  medialAxisPt = p0+(s-dist0)*eVec;
414  }
415 
416  if (medialAxisPt != vector::max)
417  {
418  forAll(e, ep)
419  {
420  label pointI = e[ep];
421 
422  if (!pointMedialDist[pointI].valid(dummyTrackData))
423  {
424  maxPoints.append(pointI);
425  maxInfo.append
426  (
427  pointEdgePoint
428  (
429  medialAxisPt, //points[pointI],
430  magSqr(points[pointI]-medialAxisPt)//0.0
431  )
432  );
433  pointMedialDist[pointI] = maxInfo.last();
434  }
435  }
436  }
437  }
438  }
439  }
440 
441 
442  // 2. Seed non-adapt patches
443  const polyBoundaryMesh& patches = mesh().boundaryMesh();
444 
445  labelHashSet adaptPatches(adaptPatchIDs_);
446 
447 
448  forAll(patches, patchI)
449  {
450  const polyPatch& pp = patches[patchI];
451  const pointPatchVectorField& pvf =
452  pointDisplacement().boundaryField()[patchI];
453 
454  if
455  (
456  !pp.coupled()
457  && !isA<emptyPolyPatch>(pp)
458  && !adaptPatches.found(patchI)
459  )
460  {
461  const labelList& meshPoints = pp.meshPoints();
462 
463  // Check the type of the patchField. The types are
464  // - fixedValue (0 or more layers) but the >0 layers have
465  // already been handled in the adaptPatches loop
466  // - constraint (but not coupled) types, e.g. symmetryPlane,
467  // slip.
468  if (pvf.fixesValue())
469  {
470  // Disable all movement on fixedValue patchFields
471  Info<< typeName
472  << " : Inserting all points on patch " << pp.name()
473  << endl;
474 
475  forAll(meshPoints, i)
476  {
477  label pointI = meshPoints[i];
478  if (!pointMedialDist[pointI].valid(dummyTrackData))
479  {
480  maxPoints.append(pointI);
481  maxInfo.append
482  (
483  pointEdgePoint
484  (
485  points[pointI],
486  0.0
487  )
488  );
489  pointMedialDist[pointI] = maxInfo.last();
490  }
491  }
492  }
493  else
494  {
495  // Based on geometry: analyse angle w.r.t. nearest moving
496  // point. In the pointWallDist we transported the
497  // normal as the passive vector. Note that this points
498  // out of the originating wall so inside of the domain
499  // on this patch.
500  Info<< typeName
501  << " : Inserting points on patch " << pp.name()
502  << " if angle to nearest layer patch > "
503  << slipFeatureAngle << " degrees." << endl;
504 
505  scalar slipFeatureAngleCos = Foam::cos
506  (
507  degToRad(slipFeatureAngle)
508  );
509  pointField pointNormals
510  (
512  );
513 
514  forAll(meshPoints, i)
515  {
516  label pointI = meshPoints[i];
517 
518  if
519  (
520  pointWallDist[pointI].valid(dummyTrackData)
521  && !pointMedialDist[pointI].valid(dummyTrackData)
522  )
523  {
524  // Check if angle not too large.
525  scalar cosAngle =
526  (
527  -pointWallDist[pointI].data()
528  & pointNormals[i]
529  );
530  if (cosAngle > slipFeatureAngleCos)
531  {
532  // Extrusion direction practically perpendicular
533  // to the patch. Disable movement at the patch.
534 
535  maxPoints.append(pointI);
536  maxInfo.append
537  (
538  pointEdgePoint
539  (
540  points[pointI],
541  0.0
542  )
543  );
544  pointMedialDist[pointI] = maxInfo.last();
545  }
546  else
547  {
548  // Extrusion direction makes angle with patch
549  // so allow sliding - don't insert zero points
550  }
551  }
552  }
553  }
554  }
555  }
556 
557  maxInfo.shrink();
558  maxPoints.shrink();
559 
560 
561  if (debug)
562  {
563  mkDir(mesh().time().timePath());
564  OBJstream str(mesh().time().timePath()/"medialSurfacePoints.obj");
565 
566  pointSet seedPoints
567  (
568  mesh(),
569  "medialSurfacePoints",
570  maxPoints
571  );
572 
573  Info<< typeName
574  << " : Writing estimated medial surface:" << nl << incrIndent
575  << indent << "locations : " << str.name() << nl
576  << indent << "pointSet : " << seedPoints.name() << nl
577  << decrIndent << endl;
578 
579  for (const auto& info : maxInfo)
580  {
581  str.write(info.origin());
582  }
583  seedPoints.write();
584  }
585 
586 
587  // Do all calculations
588  PointEdgeWave<pointEdgePoint> medialDistCalc
589  (
590  mesh(),
591  maxPoints,
592  maxInfo,
593 
594  pointMedialDist,
595  edgeMedialDist,
596  0, // max iterations
597  dummyTrackData
598  );
599  medialDistCalc.iterate(2*nMedialAxisIter);
600 
601 
602  // Extract medial axis distance as pointScalarField
603  forAll(pointMedialDist, pointI)
604  {
605  if (pointMedialDist[pointI].valid(dummyTrackData))
606  {
607  medialDist_[pointI] = Foam::sqrt
608  (
609  pointMedialDist[pointI].distSqr()
610  );
611  medialVec_[pointI] = pointMedialDist[pointI].origin();
612  }
613  else
614  {
615  // Unvisited. Do as if on medial axis so unmoving
616  medialDist_[pointI] = 0.0;
617  medialVec_[pointI] = point(1, 0, 0);
618  }
619  }
620  }
621 
622  // Extract transported surface normals as pointVectorField
623  forAll(dispVec_, i)
624  {
625  if (!pointWallDist[i].valid(dummyTrackData))
626  {
627  dispVec_[i] = vector(1, 0, 0);
628  }
629  else
630  {
631  dispVec_[i] = pointWallDist[i].data();
632  }
633  }
634 
635  // Smooth normal vectors. Do not change normals on pp.meshPoints
636  fieldSmoother_.smoothNormals
637  (
638  nSmoothNormals,
639  isMeshMasterPoint,
640  isMeshMasterEdge,
641  meshPoints,
642  dispVec_
643  );
644 
645  // Calculate ratio point medial distance to point wall distance
646  forAll(medialRatio_, pointI)
647  {
648  if (!pointWallDist[pointI].valid(dummyTrackData))
649  {
650  medialRatio_[pointI] = 0.0;
651  }
652  else
653  {
654  scalar wDist2 = pointWallDist[pointI].distSqr();
655  scalar mDist = medialDist_[pointI];
656 
657  if (wDist2 < sqr(SMALL) && mDist < SMALL)
658  //- Note: maybe less strict:
659  //(
660  // wDist2 < sqr(meshRefiner_.mergeDistance())
661  // && mDist < meshRefiner_.mergeDistance()
662  //)
663  {
664  medialRatio_[pointI] = 0.0;
665  }
666  else
667  {
668  medialRatio_[pointI] = mDist / (Foam::sqrt(wDist2) + mDist);
669  }
670  }
671  }
672 
673 
674  if (debug)
675  {
676  Info<< typeName
677  << " : Writing medial axis fields:" << nl << incrIndent
678  << indent << "ratio of medial distance to wall distance : "
679  << medialRatio_.name() << nl
680  << indent << "distance to nearest medial axis : "
681  << medialDist_.name() << nl
682  << indent << "nearest medial axis location : "
683  << medialVec_.name() << nl
684  << indent << "normal at nearest wall : "
685  << dispVec_.name() << nl
686  << decrIndent << endl;
687 
688  dispVec_.write();
689  medialRatio_.write();
690  medialDist_.write();
691  medialVec_.write();
692  }
693 }
694 
695 
696 bool Foam::medialAxisMeshMover::unmarkExtrusion
697 (
698  const label patchPointI,
699  pointField& patchDisp,
700  List<snappyLayerDriver::extrudeMode>& extrudeStatus
701 )
702 {
703  if (extrudeStatus[patchPointI] == snappyLayerDriver::EXTRUDE)
704  {
705  extrudeStatus[patchPointI] = snappyLayerDriver::NOEXTRUDE;
706  patchDisp[patchPointI] = Zero;
707  return true;
708  }
709  else if (extrudeStatus[patchPointI] == snappyLayerDriver::EXTRUDEREMOVE)
710  {
711  extrudeStatus[patchPointI] = snappyLayerDriver::NOEXTRUDE;
712  patchDisp[patchPointI] = Zero;
713  return true;
714  }
715 
716  return false;
717 }
718 
719 
720 void Foam::medialAxisMeshMover::syncPatchDisplacement
721 (
722  const scalarField& minThickness,
723  pointField& patchDisp,
724  List<snappyLayerDriver::extrudeMode>& extrudeStatus
725 ) const
726 {
727  const indirectPrimitivePatch& pp = adaptPatchPtr_();
728  const labelList& meshPoints = pp.meshPoints();
729 
730  //label nChangedTotal = 0;
731 
732  while (true)
733  {
734  label nChanged = 0;
735 
736  // Sync displacement (by taking min)
738  (
739  mesh(),
740  meshPoints,
741  patchDisp,
742  minMagSqrEqOp<vector>(),
743  point::rootMax // null value
744  );
745 
746  // Unmark if displacement too small
747  forAll(patchDisp, i)
748  {
749  if (mag(patchDisp[i]) < minThickness[i])
750  {
751  if (unmarkExtrusion(i, patchDisp, extrudeStatus))
752  {
753  nChanged++;
754  }
755  }
756  }
757 
758  //labelList syncPatchNLayers(patchNLayers);
759  //
760  //syncTools::syncPointList
761  //(
762  // mesh(),
763  // meshPoints,
764  // syncPatchNLayers,
765  // minEqOp<label>(),
766  // labelMax // null value
767  //);
768  //
771  //forAll(syncPatchNLayers, i)
772  //{
773  // if (syncPatchNLayers[i] != patchNLayers[i])
774  // {
775  // if
776  // (
777  // unmarkExtrusion
778  // (
779  // i,
780  // patchDisp,
781  // patchNLayers,
782  // extrudeStatus
783  // )
784  // )
785  // {
786  // nChanged++;
787  // }
788  // }
789  //}
790  //
791  //syncTools::syncPointList
792  //(
793  // mesh(),
794  // meshPoints,
795  // syncPatchNLayers,
796  // maxEqOp<label>(),
797  // labelMin // null value
798  //);
799  //
802  //forAll(syncPatchNLayers, i)
803  //{
804  // if (syncPatchNLayers[i] != patchNLayers[i])
805  // {
806  // if
807  // (
808  // unmarkExtrusion
809  // (
810  // i,
811  // patchDisp,
812  // patchNLayers,
813  // extrudeStatus
814  // )
815  // )
816  // {
817  // nChanged++;
818  // }
819  // }
820  //}
821 
822  //nChangedTotal += nChanged;
823 
824  if (!returnReduceOr(nChanged))
825  {
826  break;
827  }
828  }
829 
830  //Info<< "Prevented extrusion on "
831  // << returnReduce(nChangedTotal, sumOp<label>())
832  // << " coupled patch points during syncPatchDisplacement." << endl;
833 }
834 
835 
836 // Stop layer growth where mesh wraps around edge with a
837 // large feature angle
838 void Foam::medialAxisMeshMover::
839 handleFeatureAngleLayerTerminations
840 (
841  const scalar minCos,
842  const bitSet& isPatchMasterPoint,
843  const labelList& meshEdges,
844  List<snappyLayerDriver::extrudeMode>& extrudeStatus,
845  pointField& patchDisp,
846  label& nPointCounter
847 ) const
848 {
849  const indirectPrimitivePatch& pp = adaptPatchPtr_();
850 
851  // Mark faces that have all points extruded
852  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
853 
854  boolList extrudedFaces(pp.size(), true);
855 
856  forAll(pp.localFaces(), faceI)
857  {
858  const face& f = pp.localFaces()[faceI];
859 
860  forAll(f, fp)
861  {
862  if (extrudeStatus[f[fp]] == snappyLayerDriver::NOEXTRUDE)
863  {
864  extrudedFaces[faceI] = false;
865  break;
866  }
867  }
868  }
869 
870 
871 
872  //label nOldPointCounter = nPointCounter;
873 
874  // Detect situation where two featureedge-neighbouring faces are partly or
875  // not extruded and the edge itself is extruded. In this case unmark the
876  // edge for extrusion.
877 
878 
879  List<List<point>> edgeFaceNormals(pp.nEdges());
880  List<List<bool>> edgeFaceExtrude(pp.nEdges());
881 
882  const labelListList& edgeFaces = pp.edgeFaces();
883  const vectorField& faceNormals = pp.faceNormals();
884 
885  forAll(edgeFaces, edgeI)
886  {
887  const labelList& eFaces = edgeFaces[edgeI];
888 
889  edgeFaceNormals[edgeI].setSize(eFaces.size());
890  edgeFaceExtrude[edgeI].setSize(eFaces.size());
891  forAll(eFaces, i)
892  {
893  label faceI = eFaces[i];
894  edgeFaceNormals[edgeI][i] = faceNormals[faceI];
895  edgeFaceExtrude[edgeI][i] = extrudedFaces[faceI];
896  }
897  }
898 
900  (
901  mesh(),
902  meshEdges,
903  edgeFaceNormals,
904  ListOps::appendEqOp<point>(),
905  List<point>() // null value
906  );
907 
909  (
910  mesh(),
911  meshEdges,
912  edgeFaceExtrude,
913  ListOps::appendEqOp<bool>(),
914  List<bool>() // null value
915  );
916 
917 
918  forAll(edgeFaceNormals, edgeI)
919  {
920  const List<point>& eFaceNormals = edgeFaceNormals[edgeI];
921  const List<bool>& eFaceExtrude = edgeFaceExtrude[edgeI];
922 
923  if (eFaceNormals.size() == 2)
924  {
925  const edge& e = pp.edges()[edgeI];
926  label v0 = e[0];
927  label v1 = e[1];
928 
929  if
930  (
931  extrudeStatus[v0] != snappyLayerDriver::NOEXTRUDE
932  || extrudeStatus[v1] != snappyLayerDriver::NOEXTRUDE
933  )
934  {
935  if (!eFaceExtrude[0] || !eFaceExtrude[1])
936  {
937  const vector& n0 = eFaceNormals[0];
938  const vector& n1 = eFaceNormals[1];
939 
940  if ((n0 & n1) < minCos)
941  {
942  if (unmarkExtrusion(v0, patchDisp, extrudeStatus))
943  {
944  if (isPatchMasterPoint[v0])
945  {
946  nPointCounter++;
947  }
948  }
949  if (unmarkExtrusion(v1, patchDisp, extrudeStatus))
950  {
951  if (isPatchMasterPoint[v1])
952  {
953  nPointCounter++;
954  }
955  }
956  }
957  }
958  }
959  }
960  }
961 
962  //Info<< "Added "
963  // << returnReduce(nPointCounter-nOldPointCounter, sumOp<label>())
964  // << " point not to extrude due to minCos "
965  // << minCos << endl;
966 }
967 
968 
969 // Find isolated islands (points, edges and faces and layer terminations)
970 // in the layer mesh and stop any layer growth at these points.
971 void Foam::medialAxisMeshMover::findIsolatedRegions
972 (
973  const scalar minCosLayerTermination,
974  const bool detectExtrusionIsland,
975  const bitSet& isPatchMasterPoint,
976  const bitSet& isPatchMasterEdge,
977  const labelList& meshEdges,
978  const scalarField& minThickness,
979  List<snappyLayerDriver::extrudeMode>& extrudeStatus,
980  pointField& patchDisp
981 ) const
982 {
983  const indirectPrimitivePatch& pp = adaptPatchPtr_();
984  const labelListList& pointFaces = pp.pointFaces();
985  const labelList& meshPoints = pp.meshPoints();
986 
987 
988  Info<< typeName << " : Removing isolated regions ..." << nl
989  << indent << "- if partially extruded faces make angle < "
990  << Foam::radToDeg(Foam::acos(minCosLayerTermination)) << nl;
991  if (detectExtrusionIsland)
992  {
993  Info<< indent << "- if exclusively surrounded by non-extruded points"
994  << nl;
995  }
996  else
997  {
998  Info<< indent << "- if exclusively surrounded by non-extruded faces"
999  << nl;
1000  }
1001 
1002  // Keep count of number of points unextruded
1003  label nPointCounter = 0;
1004 
1005  while (true)
1006  {
1007  // Stop layer growth where mesh wraps around edge with a
1008  // large feature angle
1009  if (minCosLayerTermination > -1)
1010  {
1011  handleFeatureAngleLayerTerminations
1012  (
1013  minCosLayerTermination,
1014  isPatchMasterPoint,
1015  meshEdges,
1016 
1017  extrudeStatus,
1018  patchDisp,
1019  nPointCounter
1020  );
1021 
1022  syncPatchDisplacement(minThickness, patchDisp, extrudeStatus);
1023  }
1024 
1025 
1026  // Detect either:
1027  // - point where all surrounding points are not extruded
1028  // (detectExtrusionIsland)
1029  // or
1030  // - point where all the faces surrounding it are not fully
1031  // extruded
1032 
1033  boolList keptPoints(pp.nPoints(), false);
1034 
1035  if (detectExtrusionIsland)
1036  {
1037  // Do not extrude from point where all neighbouring
1038  // points are not grown
1039  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1040 
1041  labelList islandPoint(pp.size(), -1);
1042  forAll(pp, faceI)
1043  {
1044  const face& f = pp.localFaces()[faceI];
1045 
1046  forAll(f, fp)
1047  {
1048  if (extrudeStatus[f[fp]] != snappyLayerDriver::NOEXTRUDE)
1049  {
1050  if (islandPoint[faceI] == -1)
1051  {
1052  // First point to extrude
1053  islandPoint[faceI] = f[fp];
1054  }
1055  else if (islandPoint[faceI] != -2)
1056  {
1057  // Second or more point to extrude
1058  islandPoint[faceI] = -2;
1059  }
1060  }
1061  }
1062  }
1063 
1064  // islandPoint:
1065  // -1 : no point extruded on face
1066  // -2 : >= 2 points extruded on face
1067  // >=0: label of point extruded
1068 
1069  // Check all surrounding faces that I am the islandPoint
1070  forAll(pointFaces, patchPointI)
1071  {
1072  if (extrudeStatus[patchPointI] != snappyLayerDriver::NOEXTRUDE)
1073  {
1074  const labelList& pFaces = pointFaces[patchPointI];
1075 
1076  forAll(pFaces, i)
1077  {
1078  label faceI = pFaces[i];
1079  if (islandPoint[faceI] != patchPointI)
1080  {
1081  keptPoints[patchPointI] = true;
1082  break;
1083  }
1084  }
1085  }
1086  }
1087  }
1088  else
1089  {
1090  // Do not extrude from point where all neighbouring
1091  // faces are not grown
1092  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1093 
1094  boolList extrudedFaces(pp.size(), true);
1095  forAll(pp.localFaces(), faceI)
1096  {
1097  const face& f = pp.localFaces()[faceI];
1098  forAll(f, fp)
1099  {
1100  if (extrudeStatus[f[fp]] == snappyLayerDriver::NOEXTRUDE)
1101  {
1102  extrudedFaces[faceI] = false;
1103  break;
1104  }
1105  }
1106  }
1107 
1108  const labelListList& pointFaces = pp.pointFaces();
1109 
1110  forAll(keptPoints, patchPointI)
1111  {
1112  const labelList& pFaces = pointFaces[patchPointI];
1113 
1114  forAll(pFaces, i)
1115  {
1116  label faceI = pFaces[i];
1117  if (extrudedFaces[faceI])
1118  {
1119  keptPoints[patchPointI] = true;
1120  break;
1121  }
1122  }
1123  }
1124  }
1125 
1126 
1128  (
1129  mesh(),
1130  meshPoints,
1131  keptPoints,
1132  orEqOp<bool>(),
1133  false // null value
1134  );
1135 
1136  label nChanged = 0;
1137 
1138  forAll(keptPoints, patchPointI)
1139  {
1140  if (!keptPoints[patchPointI])
1141  {
1142  if (unmarkExtrusion(patchPointI, patchDisp, extrudeStatus))
1143  {
1144  nPointCounter++;
1145  nChanged++;
1146  }
1147  }
1148  }
1149 
1150 
1151  if (!returnReduceOr(nChanged))
1152  {
1153  break;
1154  }
1155  }
1156 
1157  const edgeList& edges = pp.edges();
1158 
1159 
1160  // Count number of mesh edges using a point
1161  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1162 
1163  labelList isolatedPoint(pp.nPoints(), Zero);
1164 
1165  forAll(edges, edgeI)
1166  {
1167  if (isPatchMasterEdge[edgeI])
1168  {
1169  const edge& e = edges[edgeI];
1170 
1171  label v0 = e[0];
1172  label v1 = e[1];
1173 
1174  if (extrudeStatus[v1] != snappyLayerDriver::NOEXTRUDE)
1175  {
1176  isolatedPoint[v0] += 1;
1177  }
1178  if (extrudeStatus[v0] != snappyLayerDriver::NOEXTRUDE)
1179  {
1180  isolatedPoint[v1] += 1;
1181  }
1182  }
1183  }
1184 
1186  (
1187  mesh(),
1188  meshPoints,
1189  isolatedPoint,
1190  plusEqOp<label>(),
1191  label(0) // null value
1192  );
1193 
1194  // stop layer growth on isolated faces
1195  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1196  forAll(pp, faceI)
1197  {
1198  const face& f = pp.localFaces()[faceI];
1199  bool failed = false;
1200  forAll(f, fp)
1201  {
1202  if (isolatedPoint[f[fp]] > 2)
1203  {
1204  failed = true;
1205  break;
1206  }
1207  }
1208  bool allPointsExtruded = true;
1209  if (!failed)
1210  {
1211  forAll(f, fp)
1212  {
1213  if (extrudeStatus[f[fp]] == snappyLayerDriver::NOEXTRUDE)
1214  {
1215  allPointsExtruded = false;
1216  break;
1217  }
1218  }
1219 
1220  if (allPointsExtruded)
1221  {
1222  forAll(f, fp)
1223  {
1224  if
1225  (
1226  unmarkExtrusion
1227  (
1228  f[fp],
1229  patchDisp,
1230  extrudeStatus
1231  )
1232  )
1233  {
1234  nPointCounter++;
1235  }
1236  }
1237  }
1238  }
1239  }
1240 
1241  Info<< typeName
1242  << " : Number of isolated points extrusion stopped : "
1243  << returnReduce(nPointCounter, sumOp<label>()) << endl;
1244 }
1245 
1246 
1247 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1248 
1249 Foam::medialAxisMeshMover::medialAxisMeshMover
1250 (
1251  const dictionary& dict,
1252  const List<labelPair>& baffles,
1253  pointVectorField& pointDisplacement,
1254  const bool dryRun
1255 )
1256 :
1257  externalDisplacementMeshMover(dict, baffles, pointDisplacement, dryRun),
1258  adaptPatchIDs_(getFixedValueBCs(pointDisplacement)),
1259  adaptPatchPtr_(getPatch(mesh(), adaptPatchIDs_)),
1260  scale_
1261  (
1262  IOobject
1263  (
1264  "scale",
1265  pointDisplacement.time().timeName(),
1266  pointDisplacement.db(),
1267  IOobject::NO_READ,
1268  IOobject::AUTO_WRITE
1269  ),
1270  pMesh(),
1271  dimensionedScalar("scale", dimless, 1.0)
1272  ),
1273  oldPoints_(mesh().points()),
1274  meshMover_
1275  (
1276  const_cast<polyMesh&>(mesh()),
1277  const_cast<pointMesh&>(pMesh()),
1278  adaptPatchPtr_(),
1279  pointDisplacement,
1280  scale_,
1281  oldPoints_,
1282  adaptPatchIDs_,
1283  dict,
1284  dryRun
1285  ),
1286  fieldSmoother_(mesh()),
1287  dispVec_
1288  (
1289  IOobject
1290  (
1291  "dispVec",
1292  pointDisplacement.time().timeName(),
1293  pointDisplacement.db(),
1294  IOobject::NO_READ,
1295  IOobject::NO_WRITE,
1296  IOobject::NO_REGISTER
1297  ),
1298  pMesh(),
1300  ),
1301  medialRatio_
1302  (
1303  IOobject
1304  (
1305  "medialRatio",
1306  pointDisplacement.time().timeName(),
1307  pointDisplacement.db(),
1308  IOobject::NO_READ,
1309  IOobject::NO_WRITE,
1310  IOobject::NO_REGISTER
1311  ),
1312  pMesh(),
1314  ),
1315  medialDist_
1316  (
1317  IOobject
1318  (
1319  "pointMedialDist",
1320  pointDisplacement.time().timeName(),
1321  pointDisplacement.db(),
1322  IOobject::NO_READ,
1323  IOobject::NO_WRITE,
1324  IOobject::NO_REGISTER
1325  ),
1326  pMesh(),
1328  ),
1329  medialVec_
1330  (
1331  IOobject
1332  (
1333  "medialVec",
1334  pointDisplacement.time().timeName(),
1335  pointDisplacement.db(),
1336  IOobject::NO_READ,
1337  IOobject::NO_WRITE,
1338  IOobject::NO_REGISTER
1339  ),
1340  pMesh(),
1342  )
1343 {
1344  update(dict);
1345 }
1346 
1347 
1348 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1349 
1351 {}
1352 
1353 
1354 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1355 
1356 void Foam::medialAxisMeshMover::calculateDisplacement
1357 (
1358  const dictionary& coeffDict,
1359  const scalarField& minThickness,
1360  List<snappyLayerDriver::extrudeMode>& extrudeStatus,
1361  pointField& patchDisp
1362 )
1363 {
1364  Info<< typeName << " : Smoothing using Medial Axis ..." << endl;
1365 
1366  const indirectPrimitivePatch& pp = *adaptPatchPtr_;
1367  const labelList& meshPoints = pp.meshPoints();
1368 
1369 
1370  // Read settings
1371  // ~~~~~~~~~~~~~
1372 
1373  //- (lambda-mu) smoothing of internal displacement
1374  const label nSmoothDisplacement =
1375  coeffDict.getOrDefault("nSmoothDisplacement", 0);
1376 
1377  //- Layer thickness too big
1378  const scalar maxThicknessToMedialRatio =
1379  coeffDict.get<scalar>("maxThicknessToMedialRatio");
1380 
1381  //- Feature angle when to stop adding layers
1382  const scalar featureAngle = coeffDict.get<scalar>("featureAngle");
1383 
1384  //- Stop layer growth where mesh wraps around sharp edge
1385  scalar layerTerminationAngle = coeffDict.getOrDefault<scalar>
1386  (
1387  "layerTerminationAngle",
1388  0.5*featureAngle
1389  );
1390  scalar minCosLayerTermination = Foam::cos(degToRad(layerTerminationAngle));
1391 
1392  //- Smoothing wanted patch thickness
1393  const label nSmoothPatchThickness =
1394  coeffDict.get<label>("nSmoothThickness");
1395 
1396  //- Number of edges walking out
1397  const label nMedialAxisIter = coeffDict.getOrDefault<label>
1398  (
1399  "nMedialAxisIter",
1401  );
1402 
1403  //- Use strict extrusionIsland detection
1404  const bool detectExtrusionIsland = coeffDict.getOrDefault
1405  (
1406  "detectExtrusionIsland",
1407  false
1408  );
1409 
1410 
1411  // Precalulate master points/edge (only relevant for shared points/edges)
1412  const bitSet isMeshMasterPoint(syncTools::getMasterPoints(mesh()));
1413  const bitSet isMeshMasterEdge(syncTools::getMasterEdges(mesh()));
1414  // Precalculate meshEdge per pp edge
1415  const labelList meshEdges
1416  (
1417  pp.meshEdges
1418  (
1419  mesh().edges(),
1420  mesh().pointEdges()
1421  )
1422  );
1423 
1424  // Precalulate (patch) master point/edge
1425  const bitSet isPatchMasterPoint
1426  (
1428  (
1429  mesh(),
1430  meshPoints
1431  )
1432  );
1433  const bitSet isPatchMasterEdge
1434  (
1436  (
1437  mesh(),
1438  meshEdges
1439  )
1440  );
1441 
1442 
1443  scalarField thickness(mag(patchDisp));
1444 
1445  forAll(thickness, patchPointI)
1446  {
1447  if (extrudeStatus[patchPointI] == snappyLayerDriver::NOEXTRUDE)
1448  {
1449  thickness[patchPointI] = 0.0;
1450  }
1451  }
1452 
1453  label numThicknessRatioExclude = 0;
1454 
1455  // reduce thickness where thickness/medial axis distance large
1456  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1457 
1458  autoPtr<OBJstream> str;
1459  if (debug)
1460  {
1461  str.reset
1462  (
1463  new OBJstream
1464  (
1465  mesh().time().path()
1466  / "thicknessRatioExcludePoints_"
1467  + mesh().time().timeName()
1468  + ".obj"
1469  )
1470  );
1471  Info<< typeName
1472  << " : Writing points with too large an extrusion distance to "
1473  << str().name() << endl;
1474  }
1475 
1476  autoPtr<OBJstream> medialVecStr;
1477  if (debug)
1478  {
1479  medialVecStr.reset
1480  (
1481  new OBJstream
1482  (
1483  mesh().time().path()
1484  / "thicknessRatioExcludeMedialVec_"
1485  + mesh().time().timeName()
1486  + ".obj"
1487  )
1488  );
1489  Info<< typeName
1490  << " : Writing medial axis vectors on points with too large"
1491  << " an extrusion distance to " << medialVecStr().name() << endl;
1492  }
1493 
1494  forAll(meshPoints, patchPointI)
1495  {
1496  if (extrudeStatus[patchPointI] != snappyLayerDriver::NOEXTRUDE)
1497  {
1498  label pointI = meshPoints[patchPointI];
1499 
1500  //- Option 1: look only at extrusion thickness v.s. distance
1501  // to nearest (medial axis or static) point.
1502  scalar mDist = medialDist_[pointI];
1503  scalar thicknessRatio = thickness[patchPointI]/(mDist+VSMALL);
1504 
1505  //- Option 2: Look at component in the direction
1506  // of nearest (medial axis or static) point.
1507  const vector n = normalised(patchDisp[patchPointI]);
1508  const vector mVec =
1509  normalised
1510  (
1511  medialVec_[pointI] - mesh().points()[pointI]
1512  );
1513 
1514  thicknessRatio *= (n & mVec);
1515 
1516  if (thicknessRatio > maxThicknessToMedialRatio)
1517  {
1518  // Truncate thickness.
1519  if (debug&2)
1520  {
1521  Pout<< "truncating displacement at "
1522  << mesh().points()[pointI]
1523  << " from " << thickness[patchPointI]
1524  << " to "
1525  << 0.5
1526  *(
1527  minThickness[patchPointI]
1528  +thickness[patchPointI]
1529  )
1530  << " medial direction:" << mVec
1531  << " extrusion direction:" << n
1532  << " with thicknessRatio:" << thicknessRatio
1533  << endl;
1534  }
1535 
1536  thickness[patchPointI] =
1537  0.5*(minThickness[patchPointI]+thickness[patchPointI]);
1538 
1539  patchDisp[patchPointI] = thickness[patchPointI]*n;
1540 
1541  if (isPatchMasterPoint[patchPointI])
1542  {
1543  numThicknessRatioExclude++;
1544  }
1545 
1546  if (str)
1547  {
1548  const point& pt = mesh().points()[pointI];
1549  str().writeLine(pt, pt+patchDisp[patchPointI]);
1550  }
1551  if (medialVecStr)
1552  {
1553  const point& pt = mesh().points()[pointI];
1554  medialVecStr().writeLine(pt, medialVec_[pointI]);
1555  }
1556  }
1557  }
1558  }
1559 
1560  Info<< typeName << " : Reducing layer thickness at "
1561  << returnReduce(numThicknessRatioExclude, sumOp<label>())
1562  << " nodes where thickness to medial axis distance is large " << endl;
1563 
1564 
1565  // find points where layer growth isolated to a lone point, edge or face
1566 
1567  findIsolatedRegions
1568  (
1569  minCosLayerTermination,
1570  detectExtrusionIsland,
1571 
1572  isPatchMasterPoint,
1573  isPatchMasterEdge,
1574  meshEdges,
1575  minThickness,
1576 
1577  extrudeStatus,
1578  patchDisp
1579  );
1580 
1581  // Update thickness for changed extrusion
1582  forAll(thickness, patchPointI)
1583  {
1584  if (extrudeStatus[patchPointI] == snappyLayerDriver::NOEXTRUDE)
1585  {
1586  thickness[patchPointI] = 0.0;
1587  }
1588  }
1589 
1590 
1591  // Smooth layer thickness on moving patch. Since some locations will have
1592  // disabled the extrusion this might cause big jumps in wanted displacement
1593  // for neighbouring patch points. So smooth the wanted displacement
1594  // before actually trying to move the mesh.
1595  fieldSmoother_.minSmoothField
1596  (
1597  nSmoothPatchThickness,
1598  isPatchMasterPoint,
1599  isPatchMasterEdge,
1600  pp,
1601  minThickness,
1602  thickness
1603  );
1604 
1605 
1606  // Dummy additional info for PointEdgeWave
1607  int dummyTrackData = 0;
1608 
1609  List<PointData<scalar>> pointWallDist(mesh().nPoints());
1610 
1611  const pointField& points = mesh().points();
1612  // 1. Calculate distance to points where displacement is specified.
1613  // This wave is used to transport layer thickness
1614  {
1615  // Distance to wall and medial axis on edges.
1616  List<PointData<scalar>> edgeWallDist(mesh().nEdges());
1617  labelList wallPoints(meshPoints.size());
1618 
1619  // Seed data.
1620  List<PointData<scalar>> wallInfo(meshPoints.size());
1621 
1622  forAll(meshPoints, patchPointI)
1623  {
1624  label pointI = meshPoints[patchPointI];
1625  wallPoints[patchPointI] = pointI;
1626  wallInfo[patchPointI] = PointData<scalar>
1627  (
1628  points[pointI],
1629  0.0,
1630  thickness[patchPointI] // transport layer thickness
1631  );
1632  }
1633 
1634  // Do all calculations
1635  PointEdgeWave<PointData<scalar>> wallDistCalc
1636  (
1637  mesh(),
1638  wallPoints,
1639  wallInfo,
1640  pointWallDist,
1641  edgeWallDist,
1642  0, // max iterations
1643  dummyTrackData
1644  );
1645  wallDistCalc.iterate(nMedialAxisIter);
1646  }
1647 
1648 
1649  // Calculate scaled displacement vector
1650  pointField& displacement = pointDisplacement_;
1651 
1652  forAll(displacement, pointI)
1653  {
1654  if (!pointWallDist[pointI].valid(dummyTrackData))
1655  {
1656  displacement[pointI] = Zero;
1657  }
1658  else
1659  {
1660  // 1) displacement on nearest wall point, scaled by medialRatio
1661  // (wall distance / medial distance)
1662  // 2) pointWallDist[pointI].data() is layer thickness transported
1663  // from closest wall point.
1664  // 3) shrink in opposite direction of addedPoints
1665  displacement[pointI] =
1666  -medialRatio_[pointI]
1667  *pointWallDist[pointI].data()
1668  *dispVec_[pointI];
1669  }
1670  }
1671 
1672 
1673  // Smear displacement away from fixed values (medialRatio=0 or 1)
1674  if (nSmoothDisplacement > 0)
1675  {
1676  bitSet isToBeSmoothed(displacement.size(), false);
1677 
1678  forAll(displacement, i)
1679  {
1680  if (medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL)
1681  {
1682  isToBeSmoothed.set(i);
1683  }
1684  }
1685 
1686  fieldSmoother_.smoothLambdaMuDisplacement
1687  (
1688  nSmoothDisplacement,
1689  isMeshMasterPoint,
1690  isMeshMasterEdge,
1691  isToBeSmoothed,
1692  displacement
1693  );
1694  }
1695 }
1696 
1697 
1698 bool Foam::medialAxisMeshMover::shrinkMesh
1699 (
1700  const dictionary& meshQualityDict,
1701  const label nAllowableErrors,
1702  labelList& checkFaces
1703 )
1704 {
1705  //- Number of attempts shrinking the mesh
1706  const label nSnap = meshQualityDict.get<label>("nRelaxIter");
1707 
1708 
1709  // Make sure displacement boundary conditions is uptodate with
1710  // internal field
1711  meshMover_.setDisplacementPatchFields();
1712 
1713  Info<< typeName << " : Moving mesh ..." << endl;
1714  scalar oldErrorReduction = -1;
1715 
1716  bool meshOk = false;
1717 
1718  for (label iter = 0; iter < 2*nSnap ; iter++)
1719  {
1720  Info<< typeName
1721  << " : Iteration " << iter << endl;
1722  if (iter == nSnap)
1723  {
1724  Info<< typeName
1725  << " : Displacement scaling for error reduction set to 0."
1726  << endl;
1727  oldErrorReduction = meshMover_.setErrorReduction(0.0);
1728  }
1729 
1730  if
1731  (
1732  meshMover_.scaleMesh
1733  (
1734  checkFaces,
1735  baffles_,
1736  meshMover_.paramDict(),
1737  meshQualityDict,
1738  true,
1739  nAllowableErrors
1740  )
1741  )
1742  {
1743  Info<< typeName << " : Successfully moved mesh" << endl;
1744  meshOk = true;
1745  break;
1746  }
1747  }
1748 
1749  if (oldErrorReduction >= 0)
1750  {
1751  meshMover_.setErrorReduction(oldErrorReduction);
1752  }
1753 
1754  Info<< typeName << " : Finished moving mesh ..." << endl;
1755 
1756  return meshOk;
1757 }
1758 
1759 
1761 (
1762  const dictionary& moveDict,
1763  const label nAllowableErrors,
1764  labelList& checkFaces
1765 )
1766 {
1767  // Read a few settings
1768  // ~~~~~~~~~~~~~~~~~~~
1769 
1770  //- Name of field specifying min thickness
1771  const word minThicknessName = moveDict.get<word>("minThicknessName");
1772 
1773 
1774  // Extract out patch-wise displacement
1775  const indirectPrimitivePatch& pp = adaptPatchPtr_();
1776 
1777  scalarField zeroMinThickness;
1778  if (minThicknessName == "none")
1779  {
1780  zeroMinThickness = scalarField(pp.nPoints(), Zero);
1781  }
1782  const scalarField& minThickness =
1783  (
1784  (minThicknessName == "none")
1785  ? zeroMinThickness
1786  : mesh().lookupObject<scalarField>(minThicknessName)
1787  );
1788 
1789 
1790  pointField patchDisp
1791  (
1792  pointDisplacement_.internalField(),
1793  pp.meshPoints()
1794  );
1795 
1797  (
1798  pp.nPoints(),
1800  );
1801  forAll(extrudeStatus, pointI)
1802  {
1803  if (mag(patchDisp[pointI]) <= minThickness[pointI]+SMALL)
1804  {
1805  extrudeStatus[pointI] = snappyLayerDriver::NOEXTRUDE;
1806  }
1807  }
1808 
1809 
1810  // Solve displacement
1811  calculateDisplacement(moveDict, minThickness, extrudeStatus, patchDisp);
1812 
1813  //- Move mesh according to calculated displacement
1814  return shrinkMesh
1815  (
1816  moveDict, // meshQualityDict,
1817  nAllowableErrors, // nAllowableErrors
1818  checkFaces
1819  );
1820 }
1821 
1822 
1824 {
1826 
1827  // Update motionSmoother for new geometry (moves adaptPatchPtr_)
1828  meshMover_.movePoints();
1829 
1830  // Assume current mesh location is correct (reset oldPoints, scale)
1831  meshMover_.correct();
1832 }
1833 
1834 
1835 // ************************************************************************* //
label nPoints() const
Number of points supporting patch faces.
const List< face_type > & localFaces() const
Return patch faces addressing into local point list.
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
dimensionedScalar acos(const dimensionedScalar &ds)
Variant of pointEdgePoint with some transported additional data. Templated on the transported data ty...
const Type & lookupObject(const word &name, const bool recursive=false) const
Lookup and return const reference to the object of the given Type. Fatal if not found or the wrong ty...
virtual void movePoints(const pointField &)
Update local data for geometry changes.
Ostream & indent(Ostream &os)
Indent stream.
Definition: Ostream.H:493
virtual const fileName & name() const
The name of the stream.
Definition: IOstream.C:33
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Unit conversion functions.
label iterate(const label maxIter)
Iterate until no changes or maxIter reached. Returns actual.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
T * data() noexcept
Return pointer to the underlying array serving as data storage.
Definition: UListI.H:272
dimensionedScalar sqrt(const dimensionedScalar &ds)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type get(const label i) const
Return bool value at specified position, always false for out-of-range access.
Definition: UList.H:782
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
static bitSet getMasterEdges(const polyMesh &mesh, const labelList &meshEdges)
Determine master edge for subset of edges. If coupled.
quaternion normalised(const quaternion &q)
Return the normalised (unit) quaternion of the given quaternion.
Definition: quaternionI.H:674
const dimensionSet dimless
Dimensionless.
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T. FatalIOError if not found, or if the number of tokens is incorrect.
PrimitivePatch< IndirectList< face >, const pointField & > indirectPrimitivePatch
A PrimitivePatch with an IndirectList for the faces, const reference for the point field...
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
Macros for easy insertion into run-time selection tables.
const labelList & meshPoints() const
Return labelList of mesh points in patch.
label nTotalPoints() const noexcept
Return total number of points in decomposed mesh. Not compensated for duplicate points! ...
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1078
Mesh representing a set of points created from polyMesh.
Definition: pointMesh.H:45
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:37
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition: HashSet.H:85
const Field< point_type > & faceNormals() const
Return face unit normals for patch.
word timeName
Definition: getTimeIndex.H:3
A list of faces which address into the list of points.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:316
dynamicFvMesh & mesh
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
dimensionedScalar cos(const dimensionedScalar &ds)
const pointField & points
virtual bool move(const dictionary &, const label nAllowableErrors, labelList &checkFaces)
Move mesh using current pointDisplacement boundary values.
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:608
label nPoints
Do not extrude. No layers added.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
static bitSet getMasterPoints(const polyMesh &mesh)
Get per point whether it is uncoupled or a master of a coupled set of points.
Definition: syncTools.C:61
virtual void movePoints(const pointField &)
Update local data for geometry changes.
static bitSet getMasterEdges(const polyMesh &mesh)
Get per edge whether it is uncoupled or a master of a coupled set of edges.
Definition: syncTools.C:90
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
const labelListList & edgeFaces() const
Return edge-face addressing.
const globalMeshData & globalData() const
Return parallel info (demand-driven)
Definition: polyMesh.C:1311
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
Vector< scalar > vector
Definition: vector.H:57
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
const labelListList & pointFaces() const
Return point-face addressing.
label nEdges() const
Number of edges in patch.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
An OFstream that keeps track of vertices and provides convenience output methods for OBJ files...
Definition: OBJstream.H:55
int debug
Static debugging option.
mesh update()
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
defineTypeNameAndDebug(combustionModel, 0)
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:511
labelList f(nPoints)
pointPatchField< vector > pointPatchVectorField
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
For use with FaceCellWave. Determines topological distance to starting faces.
Definition: wallPoints.H:59
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
vector point
Point is a vector.
Definition: point.H:37
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:59
#define WarningInFunction
Report a warning using Foam::Warning.
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
constexpr scalar radToDeg(const scalar rad) noexcept
Conversion from radians to degrees.
labelList meshEdges(const edgeList &allEdges, const labelListList &cellEdges, const labelList &faceCells) const
Return labels of patch edges in the global edge list using cell addressing.
static bitSet getMasterPoints(const polyMesh &mesh, const labelList &meshPoints)
Determine master point for subset of points. If coupled.
const polyBoundaryMesh & patches
messageStream Info
Information stream (stdout output on master, null elsewhere)
label n
Field< vector > vectorField
Specialisation of Field<T> for vector.
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
static void syncEdgeList(const polyMesh &mesh, List< T > &edgeValues, const CombineOp &cop, const T &nullValue, const TransformOp &top, const FlipOp &fop)
Synchronize values on all mesh edges.
List< label > labelList
A List of labels.
Definition: List.H:62
volScalarField & p
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< FaceList, PointField > &, const bitSet &flipMap=bitSet::null())
Return parallel consistent point normals for patches using mesh points.
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Wave propagation of information through grid. Every iteration information goes through one layer of e...
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:502
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
List< bool > boolList
A List of bools.
Definition: List.H:60
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
const volScalarField & p0
Definition: EEqn.H:36
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127