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 | Copyright (C) 2014 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "medialAxisMeshMover.H"
28 #include "pointFields.H"
29 #include "valuePointPatchFields.H"
30 #include "PointEdgeWave.H"
31 #include "meshRefinement.H"
32 #include "unitConversion.H"
33 #include "PatchTools.H"
34 #include "OBJstream.H"
35 #include "pointData.H"
37 
38 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39 
40 namespace Foam
41 {
42  defineTypeNameAndDebug(medialAxisMeshMover, 0);
43 
45  (
46  externalDisplacementMeshMover,
47  medialAxisMeshMover,
48  dictionary
49  );
50 }
51 
52 
53 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
54 
55 Foam::labelList Foam::medialAxisMeshMover::getFixedValueBCs
56 (
57  const pointVectorField& fld
58 )
59 {
60  DynamicList<label> adaptPatchIDs;
61  forAll(fld.boundaryField(), patchI)
62  {
63  const pointPatchField<vector>& patchFld =
64  fld.boundaryField()[patchI];
65 
66  if (isA<valuePointPatchField<vector> >(patchFld))
67  {
68  if (isA<zeroFixedValuePointPatchField<vector> >(patchFld))
69  {
70  // Special condition of fixed boundary condition. Does not
71  // get adapted
72  }
73  else
74  {
75  adaptPatchIDs.append(patchI);
76  }
77  }
78  }
79  return adaptPatchIDs;
80 }
81 
82 
84 Foam::medialAxisMeshMover::getPatch
85 (
86  const polyMesh& mesh,
87  const labelList& patchIDs
88 )
89 {
90  const polyBoundaryMesh& patches = mesh.boundaryMesh();
91 
92  // Count faces.
93  label nFaces = 0;
94 
95  forAll(patchIDs, i)
96  {
97  const polyPatch& pp = patches[patchIDs[i]];
98 
99  nFaces += pp.size();
100  }
101 
102  // Collect faces.
103  labelList addressing(nFaces);
104  nFaces = 0;
105 
106  forAll(patchIDs, i)
107  {
108  const polyPatch& pp = patches[patchIDs[i]];
109 
110  label meshFaceI = pp.start();
111 
112  forAll(pp, i)
113  {
114  addressing[nFaces++] = meshFaceI++;
115  }
116  }
117 
118  return autoPtr<indirectPrimitivePatch>
119  (
121  (
122  IndirectList<face>(mesh.faces(), addressing),
123  mesh.points()
124  )
125  );
126 }
127 
128 
129 void Foam::medialAxisMeshMover::smoothPatchNormals
130 (
131  const label nSmoothDisp,
132  const PackedBoolList& isPatchMasterPoint,
133  const PackedBoolList& isPatchMasterEdge,
134  pointField& normals
135 ) const
136 {
137  const indirectPrimitivePatch& pp = adaptPatchPtr_();
138  const edgeList& edges = pp.edges();
139  const labelList& meshPoints = pp.meshPoints();
140 
141  // Get smoothly varying internal normals field.
142  Info<< typeName << " : Smoothing normals ..." << endl;
143 
144  scalarField edgeWeights(edges.size());
145  scalarField invSumWeight(meshPoints.size());
147  (
148  mesh(),
149  isPatchMasterEdge,
150  meshPoints,
151  edges,
152  edgeWeights,
153  invSumWeight
154  );
155 
156 
158  for (label iter = 0; iter < nSmoothDisp; iter++)
159  {
161  (
162  mesh(),
163  isPatchMasterEdge,
164  meshPoints,
165  edges,
166  edgeWeights,
167  normals,
168  average
169  );
170  average *= invSumWeight;
171 
172  // Do residual calculation every so often.
173  if ((iter % 10) == 0)
174  {
175  scalar resid = meshRefinement::gAverage
176  (
177  isPatchMasterPoint,
178  mag(normals-average)()
179  );
180  Info<< " Iteration " << iter << " residual " << resid << endl;
181  }
182 
183  // Transfer to normals vector field
184  forAll(average, pointI)
185  {
186  // full smoothing neighbours + point value
187  average[pointI] = 0.5*(normals[pointI]+average[pointI]);
188  normals[pointI] = average[pointI];
189  normals[pointI] /= mag(normals[pointI]) + VSMALL;
190  }
191  }
192 }
193 
194 
195 // Smooth normals in interior.
196 void Foam::medialAxisMeshMover::smoothNormals
197 (
198  const label nSmoothDisp,
199  const PackedBoolList& isMeshMasterPoint,
200  const PackedBoolList& isMeshMasterEdge,
201  const labelList& fixedPoints,
202  pointVectorField& normals
203 ) const
204 {
205  // Get smoothly varying internal normals field.
206  Info<< typeName
207  << " : Smoothing normals in interior ..." << endl;
208 
209  const edgeList& edges = mesh().edges();
210 
211  // Points that do not change.
212  PackedBoolList isFixedPoint(mesh().nPoints());
213 
214  // Internal points that are fixed
215  forAll(fixedPoints, i)
216  {
217  label meshPointI = fixedPoints[i];
218  isFixedPoint.set(meshPointI, 1);
219  }
220 
221  // Make sure that points that are coupled to meshPoints but not on a patch
222  // are fixed as well
223  syncTools::syncPointList(mesh(), isFixedPoint, maxEqOp<unsigned int>(), 0);
224 
225 
226  // Correspondence between local edges/points and mesh edges/points
227  const labelList meshPoints(identity(mesh().nPoints()));
228 
229  // Calculate inverse sum of weights
230 
231  scalarField edgeWeights(mesh().nEdges());
232  scalarField invSumWeight(meshPoints.size());
234  (
235  mesh(),
236  isMeshMasterEdge,
237  meshPoints,
238  edges,
239  edgeWeights,
240  invSumWeight
241  );
242 
244  for (label iter = 0; iter < nSmoothDisp; iter++)
245  {
247  (
248  mesh(),
249  isMeshMasterEdge,
250  meshPoints,
251  edges,
252  edgeWeights,
253  normals,
254  average
255  );
256  average *= invSumWeight;
257 
258  // Do residual calculation every so often.
259  if ((iter % 10) == 0)
260  {
261  scalar resid = meshRefinement::gAverage
262  (
263  isMeshMasterPoint,
264  mag(normals-average)()
265  );
266  Info<< " Iteration " << iter << " residual " << resid << endl;
267  }
268 
269 
270  // Transfer to normals vector field
271  forAll(average, pointI)
272  {
273  if (isFixedPoint.get(pointI) == 0)
274  {
275  //full smoothing neighbours + point value
276  average[pointI] = 0.5*(normals[pointI]+average[pointI]);
277  normals[pointI] = average[pointI];
278  normals[pointI] /= mag(normals[pointI]) + VSMALL;
279  }
280  }
281  }
282 }
283 
284 
285 // Tries and find a medial axis point. Done by comparing vectors to nearest
286 // wall point for both vertices of edge.
287 bool Foam::medialAxisMeshMover::isMaxEdge
288 (
289  const List<pointData>& pointWallDist,
290  const label edgeI,
291  const scalar minCos
292 ) const
293 {
294  const pointField& points = mesh().points();
295 
296  // Do not mark edges with one side on moving wall.
297 
298  const edge& e = mesh().edges()[edgeI];
299 
300  vector v0(points[e[0]] - pointWallDist[e[0]].origin());
301  scalar magV0(mag(v0));
302 
303  if (magV0 < SMALL)
304  {
305  return false;
306  }
307 
308  vector v1(points[e[1]] - pointWallDist[e[1]].origin());
309  scalar magV1(mag(v1));
310 
311  if (magV1 < SMALL)
312  {
313  return false;
314  }
315 
316 
317  //- Detect based on vector to nearest point differing for both endpoints
318  //v0 /= magV0;
319  //v1 /= magV1;
320  //
322  //if ((v0 & v1) < minCos)
323  //{
324  // return true;
325  //}
326  //else
327  //{
328  // return false;
329  //}
330 
331  //- Detect based on extrusion vector differing for both endpoints
332  // the idea is that e.g. a sawtooth wall can still be extruded
333  // successfully as long as it is done all to the same direction.
334  if ((pointWallDist[e[0]].v() & pointWallDist[e[1]].v()) < minCos)
335  {
336  return true;
337  }
338  else
339  {
340  return false;
341  }
342 }
343 
344 
345 void Foam::medialAxisMeshMover::update(const dictionary& coeffDict)
346 {
347  Info<< typeName
348  << " : Calculating distance to Medial Axis ..." << endl;
349 
350  const pointField& points = mesh().points();
351 
352  const indirectPrimitivePatch& pp = adaptPatchPtr_();
353  const labelList& meshPoints = pp.meshPoints();
354 
355 
356  // Read a few parameters
357  // ~~~~~~~~~~~~~~~~~~~~~
358 
359  //- Smooth surface normals
360  const label nSmoothSurfaceNormals = readLabel
361  (
362  coeffDict.lookup("nSmoothSurfaceNormals")
363  );
364 
365  //- When is medial axis
366  word angleKey = "minMedialAxisAngle";
367  if (!coeffDict.found(angleKey))
368  {
369  // Backwards compatibility
370  angleKey = "minMedianAxisAngle";
371  }
372  scalar minMedialAxisAngleCos = Foam::cos
373  (
374  degToRad(readScalar(coeffDict.lookup(angleKey)))
375  );
376 
377  //- Feature angle when to stop adding layers
378  const scalar featureAngle = readScalar(coeffDict.lookup("featureAngle"));
379 
380  //- When to slip along wall
381  const scalar slipFeatureAngle =
382  (
383  coeffDict.found("slipFeatureAngle")
384  ? readScalar(coeffDict.lookup("slipFeatureAngle"))
385  : 0.5*featureAngle
386  );
387 
388  //- Smooth internal normals
389  const label nSmoothNormals = readLabel
390  (
391  coeffDict.lookup("nSmoothNormals")
392  );
393 
394  //- Number of edges walking out
395  const label nMedialAxisIter = coeffDict.lookupOrDefault<label>
396  (
397  "nMedialAxisIter",
399  );
400 
401 
402  // Predetermine mesh edges
403  // ~~~~~~~~~~~~~~~~~~~~~~~
404 
405  // Precalulate (mesh) master point/edge (only relevant for shared pts/edges)
406  const PackedBoolList isMeshMasterPoint(syncTools::getMasterPoints(mesh()));
407  const PackedBoolList isMeshMasterEdge(syncTools::getMasterEdges(mesh()));
408  // Precalculate meshEdge per pp edge
409  const labelList meshEdges
410  (
411  pp.meshEdges
412  (
413  mesh().edges(),
414  mesh().pointEdges()
415  )
416  );
417 
418  // Precalulate (patch) master point/edge
419  const PackedBoolList isPatchMasterPoint
420  (
422  (
423  mesh(),
424  meshPoints
425  )
426  );
427  const PackedBoolList isPatchMasterEdge
428  (
430  (
431  mesh(),
432  meshEdges
433  )
434  );
435 
436  // Determine pointNormal
437  // ~~~~~~~~~~~~~~~~~~~~~
438 
439  pointField pointNormals(PatchTools::pointNormals(mesh(), pp));
440 
441  // Smooth patch normal vectors
442  smoothPatchNormals
443  (
444  nSmoothSurfaceNormals,
445  isPatchMasterPoint,
446  isPatchMasterEdge,
447  pointNormals
448  );
449 
450 
451  // Calculate distance to pp points
452  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
453 
454  // Distance to wall
455  List<pointData> pointWallDist(mesh().nPoints());
456 
457  // Dummy additional info for PointEdgeWave
458  int dummyTrackData = 0;
459 
460 
461  // 1. Calculate distance to points where displacement is specified.
462  {
463  // Seed data.
464  List<pointData> wallInfo(meshPoints.size());
465 
466  forAll(meshPoints, patchPointI)
467  {
468  label pointI = meshPoints[patchPointI];
469  wallInfo[patchPointI] = pointData
470  (
471  points[pointI],
472  0.0,
473  pointI, // passive scalar
474  pointNormals[patchPointI] // surface normals
475  );
476  }
477 
478  // Do all calculations
479  List<pointData> edgeWallDist(mesh().nEdges());
480  PointEdgeWave<pointData> wallDistCalc
481  (
482  mesh(),
483  meshPoints,
484  wallInfo,
485  pointWallDist,
486  edgeWallDist,
487  0, // max iterations
488  dummyTrackData
489  );
490  wallDistCalc.iterate(nMedialAxisIter);
491 
492  label nUnvisit = returnReduce
493  (
494  wallDistCalc.getUnsetPoints(),
495  sumOp<label>()
496  );
497 
498  if (nUnvisit > 0)
499  {
500  if (nMedialAxisIter > 0)
501  {
502  Info<< typeName
503  << " : Limited walk to " << nMedialAxisIter
504  << " steps. Not visited " << nUnvisit
505  << " out of " << mesh().globalData().nTotalPoints()
506  << " points" << endl;
507  }
508  else
509  {
510  WarningIn
511  (
512  "medialAxisMeshMover::"
513  "medialAxisSmoothingInfo(..)"
514  ) << "Walking did not visit all points." << nl
515  << " Did not visit " << nUnvisit
516  << " out of " << mesh().globalData().nTotalPoints()
517  << " points. This is not necessarily a problem" << nl
518  << " and might be due to faceZones splitting of part"
519  << " of the domain." << nl << endl;
520  }
521  }
522  }
523 
524 
525  // 2. Find points with max distance and transport information back to
526  // wall.
527  {
528  List<pointData> pointMedialDist(mesh().nPoints());
529  List<pointData> edgeMedialDist(mesh().nEdges());
530 
531  // Seed point data.
532  DynamicList<pointData> maxInfo(meshPoints.size());
533  DynamicList<label> maxPoints(meshPoints.size());
534 
535  // 1. Medial axis points
536 
537  const edgeList& edges = mesh().edges();
538 
539  forAll(edges, edgeI)
540  {
541  const edge& e = edges[edgeI];
542 
543  if
544  (
545  !pointWallDist[e[0]].valid(dummyTrackData)
546  || !pointWallDist[e[1]].valid(dummyTrackData)
547  )
548  {
549  // Unvisited point. See above about nUnvisit warning
550  forAll(e, ep)
551  {
552  label pointI = e[ep];
553 
554  if (!pointMedialDist[pointI].valid(dummyTrackData))
555  {
556  maxPoints.append(pointI);
557  maxInfo.append
558  (
559  pointData
560  (
561  points[pointI],
562  0.0,
563  pointI, // passive data
564  vector::zero // passive data
565  )
566  );
567  pointMedialDist[pointI] = maxInfo.last();
568  }
569  }
570 
571  }
572  else if (isMaxEdge(pointWallDist, edgeI, minMedialAxisAngleCos))
573  {
574  // Both end points of edge have very different nearest wall
575  // point. Mark both points as medial axis points.
576 
577  // Approximate medial axis location on edge.
578  //const point medialAxisPt = e.centre(points);
579  vector eVec = e.vec(points);
580  scalar eMag = mag(eVec);
581  if (eMag > VSMALL)
582  {
583  eVec /= eMag;
584 
585  // Calculate distance along edge
586  const point& p0 = points[e[0]];
587  const point& p1 = points[e[1]];
588  scalar dist0 = (p0-pointWallDist[e[0]].origin()) & eVec;
589  scalar dist1 = (pointWallDist[e[1]].origin()-p1) & eVec;
590  scalar s = 0.5*(dist1+eMag+dist0);
591 
592  point medialAxisPt;
593  if (s <= dist0)
594  {
595  medialAxisPt = p0;
596  }
597  else if (s >= dist0+eMag)
598  {
599  medialAxisPt = p1;
600  }
601  else
602  {
603  medialAxisPt = p0+(s-dist0)*eVec;
604  }
605 
606  forAll(e, ep)
607  {
608  label pointI = e[ep];
609 
610  if (!pointMedialDist[pointI].valid(dummyTrackData))
611  {
612  maxPoints.append(pointI);
613  maxInfo.append
614  (
615  pointData
616  (
617  medialAxisPt, //points[pointI],
618  magSqr(points[pointI]-medialAxisPt),//0.0,
619  pointI, // passive data
620  vector::zero // passive data
621  )
622  );
623  pointMedialDist[pointI] = maxInfo.last();
624  }
625  }
626  }
627  }
628  }
629 
630 
631  // 2. Seed non-adapt patches
632  const polyBoundaryMesh& patches = mesh().boundaryMesh();
633 
634  labelHashSet adaptPatches(adaptPatchIDs_);
635 
636 
637  forAll(patches, patchI)
638  {
639  const polyPatch& pp = patches[patchI];
640  const pointPatchVectorField& pvf =
641  pointDisplacement().boundaryField()[patchI];
642 
643  if
644  (
645  !pp.coupled()
646  && !isA<emptyPolyPatch>(pp)
647  && !adaptPatches.found(patchI)
648  )
649  {
650  const labelList& meshPoints = pp.meshPoints();
651 
652  // Check the type of the patchField. The types are
653  // - fixedValue (0 or more layers) but the >0 layers have
654  // already been handled in the adaptPatches loop
655  // - constraint (but not coupled) types, e.g. symmetryPlane,
656  // slip.
657  if (pvf.fixesValue())
658  {
659  // Disable all movement on fixedValue patchFields
660  Info<< typeName
661  << " : Inserting all points on patch " << pp.name()
662  << endl;
663 
664  forAll(meshPoints, i)
665  {
666  label pointI = meshPoints[i];
667  if (!pointMedialDist[pointI].valid(dummyTrackData))
668  {
669  maxPoints.append(pointI);
670  maxInfo.append
671  (
672  pointData
673  (
674  points[pointI],
675  0.0,
676  pointI, // passive data
677  vector::zero // passive data
678  )
679  );
680  pointMedialDist[pointI] = maxInfo.last();
681  }
682  }
683  }
684  else
685  {
686  // Based on geometry: analyse angle w.r.t. nearest moving
687  // point. In the pointWallDist we transported the
688  // normal as the passive vector. Note that this points
689  // out of the originating wall so inside of the domain
690  // on this patch.
691  Info<< typeName
692  << " : Inserting points on patch " << pp.name()
693  << " if angle to nearest layer patch > "
694  << slipFeatureAngle << " degrees." << endl;
695 
696  scalar slipFeatureAngleCos = Foam::cos
697  (
698  degToRad(slipFeatureAngle)
699  );
700  pointField pointNormals
701  (
703  );
704 
705  forAll(meshPoints, i)
706  {
707  label pointI = meshPoints[i];
708 
709  if
710  (
711  pointWallDist[pointI].valid(dummyTrackData)
712  && !pointMedialDist[pointI].valid(dummyTrackData)
713  )
714  {
715  // Check if angle not too large.
716  scalar cosAngle =
717  (
718  -pointWallDist[pointI].v()
719  & pointNormals[i]
720  );
721  if (cosAngle > slipFeatureAngleCos)
722  {
723  // Extrusion direction practically perpendicular
724  // to the patch. Disable movement at the patch.
725 
726  maxPoints.append(pointI);
727  maxInfo.append
728  (
729  pointData
730  (
731  points[pointI],
732  0.0,
733  pointI, // passive data
734  vector::zero // passive data
735  )
736  );
737  pointMedialDist[pointI] = maxInfo.last();
738  }
739  else
740  {
741  // Extrusion direction makes angle with patch
742  // so allow sliding - don't insert zero points
743  }
744  }
745  }
746  }
747  }
748  }
749 
750  maxInfo.shrink();
751  maxPoints.shrink();
752 
753  // Do all calculations
754  PointEdgeWave<pointData> medialDistCalc
755  (
756  mesh(),
757  maxPoints,
758  maxInfo,
759 
760  pointMedialDist,
761  edgeMedialDist,
762  0, // max iterations
763  dummyTrackData
764  );
765  medialDistCalc.iterate(2*nMedialAxisIter);
766 
767 
768  // Extract medial axis distance as pointScalarField
769  forAll(pointMedialDist, pointI)
770  {
771  if (pointMedialDist[pointI].valid(dummyTrackData))
772  {
773  medialDist_[pointI] = Foam::sqrt
774  (
775  pointMedialDist[pointI].distSqr()
776  );
777  medialVec_[pointI] = pointMedialDist[pointI].origin();
778  }
779  else
780  {
781  // Unvisited. Do as if on medial axis so unmoving
782  medialDist_[pointI] = 0.0;
783  medialVec_[pointI] = point(1, 0, 0);
784  }
785  }
786  }
787 
788  // Extract transported surface normals as pointVectorField
789  forAll(dispVec_, i)
790  {
791  if (!pointWallDist[i].valid(dummyTrackData))
792  {
793  dispVec_[i] = vector(1, 0, 0);
794  }
795  else
796  {
797  dispVec_[i] = pointWallDist[i].v();
798  }
799  }
800 
801  // Smooth normal vectors. Do not change normals on pp.meshPoints
802  smoothNormals
803  (
804  nSmoothNormals,
805  isMeshMasterPoint,
806  isMeshMasterEdge,
807  meshPoints,
808  dispVec_
809  );
810 
811  // Calculate ratio point medial distance to point wall distance
812  forAll(medialRatio_, pointI)
813  {
814  if (!pointWallDist[pointI].valid(dummyTrackData))
815  {
816  medialRatio_[pointI] = 0.0;
817  }
818  else
819  {
820  scalar wDist2 = pointWallDist[pointI].distSqr();
821  scalar mDist = medialDist_[pointI];
822 
823  if (wDist2 < sqr(SMALL) && mDist < SMALL)
824  //- Note: maybe less strict:
825  //(
826  // wDist2 < sqr(meshRefiner_.mergeDistance())
827  // && mDist < meshRefiner_.mergeDistance()
828  //)
829  {
830  medialRatio_[pointI] = 0.0;
831  }
832  else
833  {
834  medialRatio_[pointI] = mDist / (Foam::sqrt(wDist2) + mDist);
835  }
836  }
837  }
838 
839 
840  if (debug)
841  {
842  Info<< typeName
843  << " : Writing medial axis fields:" << nl
844  << incrIndent
845  << "ratio of medial distance to wall distance : "
846  << medialRatio_.name() << nl
847  << "distance to nearest medial axis : "
848  << medialDist_.name() << nl
849  << "nearest medial axis location : "
850  << medialVec_.name() << nl
851  << "normal at nearest wall : "
852  << dispVec_.name() << nl
853  << decrIndent << nl
854  << endl;
855 
856  dispVec_.write();
857  medialRatio_.write();
858  medialDist_.write();
859  medialVec_.write();
860  }
861 }
862 
863 
864 bool Foam::medialAxisMeshMover::unmarkExtrusion
865 (
866  const label patchPointI,
867  pointField& patchDisp,
868  List<autoLayerDriver::extrudeMode>& extrudeStatus
869 )
870 {
871  if (extrudeStatus[patchPointI] == autoLayerDriver::EXTRUDE)
872  {
873  extrudeStatus[patchPointI] = autoLayerDriver::NOEXTRUDE;
874  patchDisp[patchPointI] = vector::zero;
875  return true;
876  }
877  else if (extrudeStatus[patchPointI] == autoLayerDriver::EXTRUDEREMOVE)
878  {
879  extrudeStatus[patchPointI] = autoLayerDriver::NOEXTRUDE;
880  patchDisp[patchPointI] = vector::zero;
881  return true;
882  }
883  else
884  {
885  return false;
886  }
887 }
888 
889 
890 void Foam::medialAxisMeshMover::syncPatchDisplacement
891 (
892  const scalarField& minThickness,
893  pointField& patchDisp,
894  List<autoLayerDriver::extrudeMode>& extrudeStatus
895 ) const
896 {
897  const indirectPrimitivePatch& pp = adaptPatchPtr_();
898  const labelList& meshPoints = pp.meshPoints();
899 
900  label nChangedTotal = 0;
901 
902  while (true)
903  {
904  label nChanged = 0;
905 
906  // Sync displacement (by taking min)
908  (
909  mesh(),
910  meshPoints,
911  patchDisp,
912  minMagSqrEqOp<vector>(),
913  point::rootMax // null value
914  );
915 
916  // Unmark if displacement too small
917  forAll(patchDisp, i)
918  {
919  if (mag(patchDisp[i]) < minThickness[i])
920  {
921  if (unmarkExtrusion(i, patchDisp, extrudeStatus))
922  {
923  nChanged++;
924  }
925  }
926  }
927 
928  //labelList syncPatchNLayers(patchNLayers);
929  //
930  //syncTools::syncPointList
931  //(
932  // mesh(),
933  // meshPoints,
934  // syncPatchNLayers,
935  // minEqOp<label>(),
936  // labelMax // null value
937  //);
938  //
941  //forAll(syncPatchNLayers, i)
942  //{
943  // if (syncPatchNLayers[i] != patchNLayers[i])
944  // {
945  // if
946  // (
947  // unmarkExtrusion
948  // (
949  // i,
950  // patchDisp,
951  // patchNLayers,
952  // extrudeStatus
953  // )
954  // )
955  // {
956  // nChanged++;
957  // }
958  // }
959  //}
960  //
961  //syncTools::syncPointList
962  //(
963  // mesh(),
964  // meshPoints,
965  // syncPatchNLayers,
966  // maxEqOp<label>(),
967  // labelMin // null value
968  //);
969  //
972  //forAll(syncPatchNLayers, i)
973  //{
974  // if (syncPatchNLayers[i] != patchNLayers[i])
975  // {
976  // if
977  // (
978  // unmarkExtrusion
979  // (
980  // i,
981  // patchDisp,
982  // patchNLayers,
983  // extrudeStatus
984  // )
985  // )
986  // {
987  // nChanged++;
988  // }
989  // }
990  //}
991 
992  nChangedTotal += nChanged;
993 
994  if (!returnReduce(nChanged, sumOp<label>()))
995  {
996  break;
997  }
998  }
999 
1000  //Info<< "Prevented extrusion on "
1001  // << returnReduce(nChangedTotal, sumOp<label>())
1002  // << " coupled patch points during syncPatchDisplacement." << endl;
1003 }
1004 
1005 
1006 void Foam::medialAxisMeshMover::minSmoothField
1007 (
1008  const label nSmoothDisp,
1009  const PackedBoolList& isPatchMasterPoint,
1010  const PackedBoolList& isPatchMasterEdge,
1011  const scalarField& fieldMin,
1012  scalarField& field
1013 ) const
1014 {
1015  const indirectPrimitivePatch& pp = adaptPatchPtr_();
1016  const edgeList& edges = pp.edges();
1017  const labelList& meshPoints = pp.meshPoints();
1018 
1019  scalarField edgeWeights(edges.size());
1020  scalarField invSumWeight(meshPoints.size());
1022  (
1023  mesh(),
1024  isPatchMasterEdge,
1025  meshPoints,
1026  edges,
1027  edgeWeights,
1028  invSumWeight
1029  );
1030 
1031  // Get smoothly varying patch field.
1032  Info<< typeName << " : Smoothing field ..." << endl;
1033 
1034  for (label iter = 0; iter < nSmoothDisp; iter++)
1035  {
1036  scalarField average(pp.nPoints());
1038  (
1039  mesh(),
1040  isPatchMasterEdge,
1041  meshPoints,
1042  edges,
1043  edgeWeights,
1044  field,
1045  average
1046  );
1047  average *= invSumWeight;
1048 
1049  // Transfer to field
1050  forAll(field, pointI)
1051  {
1052  //full smoothing neighbours + point value
1053  average[pointI] = 0.5*(field[pointI]+average[pointI]);
1054 
1055  // perform monotonic smoothing
1056  if
1057  (
1058  average[pointI] < field[pointI]
1059  && average[pointI] >= fieldMin[pointI]
1060  )
1061  {
1062  field[pointI] = average[pointI];
1063  }
1064  }
1065 
1066  // Do residual calculation every so often.
1067  if ((iter % 10) == 0)
1068  {
1069  scalar resid = meshRefinement::gAverage
1070  (
1071  isPatchMasterPoint,
1072  mag(field-average)()
1073  );
1074  Info<< " Iteration " << iter << " residual " << resid << endl;
1075  }
1076  }
1077 }
1078 
1079 // Stop layer growth where mesh wraps around edge with a
1080 // large feature angle
1081 void Foam::medialAxisMeshMover::
1082 handleFeatureAngleLayerTerminations
1083 (
1084  const scalar minCos,
1085  const PackedBoolList& isPatchMasterPoint,
1086  const labelList& meshEdges,
1087  List<autoLayerDriver::extrudeMode>& extrudeStatus,
1088  pointField& patchDisp,
1089  label& nPointCounter
1090 ) const
1091 {
1092  const indirectPrimitivePatch& pp = adaptPatchPtr_();
1093 
1094  // Mark faces that have all points extruded
1095  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1096 
1097  boolList extrudedFaces(pp.size(), true);
1098 
1099  forAll(pp.localFaces(), faceI)
1100  {
1101  const face& f = pp.localFaces()[faceI];
1102 
1103  forAll(f, fp)
1104  {
1105  if (extrudeStatus[f[fp]] == autoLayerDriver::NOEXTRUDE)
1106  {
1107  extrudedFaces[faceI] = false;
1108  break;
1109  }
1110  }
1111  }
1112 
1113 
1114 
1115  //label nOldPointCounter = nPointCounter;
1116 
1117  // Detect situation where two featureedge-neighbouring faces are partly or
1118  // not extruded and the edge itself is extruded. In this case unmark the
1119  // edge for extrusion.
1120 
1121 
1122  List<List<point> > edgeFaceNormals(pp.nEdges());
1123  List<List<bool> > edgeFaceExtrude(pp.nEdges());
1124 
1125  const labelListList& edgeFaces = pp.edgeFaces();
1126  const vectorField& faceNormals = pp.faceNormals();
1127 
1128  forAll(edgeFaces, edgeI)
1129  {
1130  const labelList& eFaces = edgeFaces[edgeI];
1131 
1132  edgeFaceNormals[edgeI].setSize(eFaces.size());
1133  edgeFaceExtrude[edgeI].setSize(eFaces.size());
1134  forAll(eFaces, i)
1135  {
1136  label faceI = eFaces[i];
1137  edgeFaceNormals[edgeI][i] = faceNormals[faceI];
1138  edgeFaceExtrude[edgeI][i] = extrudedFaces[faceI];
1139  }
1140  }
1141 
1143  (
1144  mesh(),
1145  meshEdges,
1146  edgeFaceNormals,
1147  globalMeshData::ListPlusEqOp<List<point> >(), // combine operator
1148  List<point>() // null value
1149  );
1150 
1152  (
1153  mesh(),
1154  meshEdges,
1155  edgeFaceExtrude,
1156  globalMeshData::ListPlusEqOp<List<bool> >(), // combine operator
1157  List<bool>() // null value
1158  );
1159 
1160 
1161  forAll(edgeFaceNormals, edgeI)
1162  {
1163  const List<point>& eFaceNormals = edgeFaceNormals[edgeI];
1164  const List<bool>& eFaceExtrude = edgeFaceExtrude[edgeI];
1165 
1166  if (eFaceNormals.size() == 2)
1167  {
1168  const edge& e = pp.edges()[edgeI];
1169  label v0 = e[0];
1170  label v1 = e[1];
1171 
1172  if
1173  (
1174  extrudeStatus[v0] != autoLayerDriver::NOEXTRUDE
1175  || extrudeStatus[v1] != autoLayerDriver::NOEXTRUDE
1176  )
1177  {
1178  if (!eFaceExtrude[0] || !eFaceExtrude[1])
1179  {
1180  const vector& n0 = eFaceNormals[0];
1181  const vector& n1 = eFaceNormals[1];
1182 
1183  if ((n0 & n1) < minCos)
1184  {
1185  if (unmarkExtrusion(v0, patchDisp, extrudeStatus))
1186  {
1187  if (isPatchMasterPoint[v0])
1188  {
1189  nPointCounter++;
1190  }
1191  }
1192  if (unmarkExtrusion(v1, patchDisp, extrudeStatus))
1193  {
1194  if (isPatchMasterPoint[v1])
1195  {
1196  nPointCounter++;
1197  }
1198  }
1199  }
1200  }
1201  }
1202  }
1203  }
1204 
1205  //Info<< "Added "
1206  // << returnReduce(nPointCounter-nOldPointCounter, sumOp<label>())
1207  // << " point not to extrude." << endl;
1208 }
1209 
1210 
1211 // Find isolated islands (points, edges and faces and layer terminations)
1212 // in the layer mesh and stop any layer growth at these points.
1213 void Foam::medialAxisMeshMover::findIsolatedRegions
1214 (
1215  const scalar minCosLayerTermination,
1216  const bool detectExtrusionIsland,
1217  const PackedBoolList& isPatchMasterPoint,
1218  const PackedBoolList& isPatchMasterEdge,
1219  const labelList& meshEdges,
1220  const scalarField& minThickness,
1221  List<autoLayerDriver::extrudeMode>& extrudeStatus,
1222  pointField& patchDisp
1223 ) const
1224 {
1225  const indirectPrimitivePatch& pp = adaptPatchPtr_();
1226  const labelListList& pointFaces = pp.pointFaces();
1227  const labelList& meshPoints = pp.meshPoints();
1228 
1229  Info<< typeName << " : Removing isolated regions ..." << endl;
1230 
1231  // Keep count of number of points unextruded
1232  label nPointCounter = 0;
1233 
1234 
1235  autoPtr<OBJstream> str;
1236  if (debug)
1237  {
1238  str.reset
1239  (
1240  new OBJstream
1241  (
1242  mesh().time().path()
1243  / "islandExcludePoints_"
1244  + mesh().time().timeName()
1245  + ".obj"
1246  )
1247  );
1248  Info<< typeName
1249  << " : Writing points surrounded by non-extruded points to "
1250  << str().name() << endl;
1251  }
1252 
1253  while (true)
1254  {
1255  // Stop layer growth where mesh wraps around edge with a
1256  // large feature angle
1257  handleFeatureAngleLayerTerminations
1258  (
1259  minCosLayerTermination,
1260  isPatchMasterPoint,
1261  meshEdges,
1262 
1263  extrudeStatus,
1264  patchDisp,
1265  nPointCounter
1266  );
1267 
1268  syncPatchDisplacement(minThickness, patchDisp, extrudeStatus);
1269 
1270 
1271 
1272  // Detect either:
1273  // - point where all surrounding points are not extruded
1274  // (detectExtrusionIsland)
1275  // or
1276  // - point where all the faces surrounding it are not fully
1277  // extruded
1278 
1279  boolList keptPoints(pp.nPoints(), false);
1280 
1281  if (detectExtrusionIsland)
1282  {
1283  // Do not extrude from point where all neighbouring
1284  // points are not grown
1285  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1286 
1287  labelList islandPoint(pp.size(), -1);
1288  forAll(pp, faceI)
1289  {
1290  const face& f = pp.localFaces()[faceI];
1291 
1292  forAll(f, fp)
1293  {
1294  if (extrudeStatus[f[fp]] != autoLayerDriver::NOEXTRUDE)
1295  {
1296  if (islandPoint[faceI] == -1)
1297  {
1298  // First point to extrude
1299  islandPoint[faceI] = f[fp];
1300  }
1301  else if (islandPoint[faceI] != -2)
1302  {
1303  // Second or more point to extrude
1304  islandPoint[faceI] = -2;
1305  }
1306  }
1307  }
1308  }
1309 
1310  // islandPoint:
1311  // -1 : no point extruded on face
1312  // -2 : >= 2 points extruded on face
1313  // >=0: label of point extruded
1314 
1315  // Check all surrounding faces that I am the islandPoint
1316  forAll(pointFaces, patchPointI)
1317  {
1318  if (extrudeStatus[patchPointI] != autoLayerDriver::NOEXTRUDE)
1319  {
1320  const labelList& pFaces = pointFaces[patchPointI];
1321 
1322  forAll(pFaces, i)
1323  {
1324  label faceI = pFaces[i];
1325  if (islandPoint[faceI] != patchPointI)
1326  {
1327  keptPoints[patchPointI] = true;
1328  break;
1329  }
1330  }
1331  }
1332  }
1333  }
1334  else
1335  {
1336  // Do not extrude from point where all neighbouring
1337  // faces are not grown
1338  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1339 
1340  boolList extrudedFaces(pp.size(), true);
1341  forAll(pp.localFaces(), faceI)
1342  {
1343  const face& f = pp.localFaces()[faceI];
1344  forAll(f, fp)
1345  {
1346  if (extrudeStatus[f[fp]] == autoLayerDriver::NOEXTRUDE)
1347  {
1348  extrudedFaces[faceI] = false;
1349  break;
1350  }
1351  }
1352  }
1353 
1354  const labelListList& pointFaces = pp.pointFaces();
1355 
1356  forAll(keptPoints, patchPointI)
1357  {
1358  const labelList& pFaces = pointFaces[patchPointI];
1359 
1360  forAll(pFaces, i)
1361  {
1362  label faceI = pFaces[i];
1363  if (extrudedFaces[faceI])
1364  {
1365  keptPoints[patchPointI] = true;
1366  break;
1367  }
1368  }
1369  }
1370  }
1371 
1372 
1374  (
1375  mesh(),
1376  meshPoints,
1377  keptPoints,
1378  orEqOp<bool>(),
1379  false // null value
1380  );
1381 
1382  label nChanged = 0;
1383 
1384  forAll(keptPoints, patchPointI)
1385  {
1386  if (!keptPoints[patchPointI])
1387  {
1388  if (unmarkExtrusion(patchPointI, patchDisp, extrudeStatus))
1389  {
1390  nPointCounter++;
1391  nChanged++;
1392 
1393  if (str.valid())
1394  {
1395  str().write(pp.points()[meshPoints[patchPointI]]);
1396  }
1397  }
1398  }
1399  }
1400 
1401 
1402  if (returnReduce(nChanged, sumOp<label>()) == 0)
1403  {
1404  break;
1405  }
1406  }
1407 
1408  const edgeList& edges = pp.edges();
1409 
1410 
1411  // Count number of mesh edges using a point
1412  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1413 
1414  labelList isolatedPoint(pp.nPoints(),0);
1415 
1416  forAll(edges, edgeI)
1417  {
1418  if (isPatchMasterEdge[edgeI])
1419  {
1420  const edge& e = edges[edgeI];
1421 
1422  label v0 = e[0];
1423  label v1 = e[1];
1424 
1425  if (extrudeStatus[v1] != autoLayerDriver::NOEXTRUDE)
1426  {
1427  isolatedPoint[v0] += 1;
1428  }
1429  if (extrudeStatus[v0] != autoLayerDriver::NOEXTRUDE)
1430  {
1431  isolatedPoint[v1] += 1;
1432  }
1433  }
1434  }
1435 
1437  (
1438  mesh(),
1439  meshPoints,
1440  isolatedPoint,
1441  plusEqOp<label>(),
1442  label(0) // null value
1443  );
1444 
1445  // stop layer growth on isolated faces
1446  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1447  forAll(pp, faceI)
1448  {
1449  const face& f = pp.localFaces()[faceI];
1450  bool failed = false;
1451  forAll(f, fp)
1452  {
1453  if (isolatedPoint[f[fp]] > 2)
1454  {
1455  failed = true;
1456  break;
1457  }
1458  }
1459  bool allPointsExtruded = true;
1460  if (!failed)
1461  {
1462  forAll(f, fp)
1463  {
1464  if (extrudeStatus[f[fp]] == autoLayerDriver::NOEXTRUDE)
1465  {
1466  allPointsExtruded = false;
1467  break;
1468  }
1469  }
1470 
1471  if (allPointsExtruded)
1472  {
1473  forAll(f, fp)
1474  {
1475  if
1476  (
1477  unmarkExtrusion
1478  (
1479  f[fp],
1480  patchDisp,
1481  extrudeStatus
1482  )
1483  )
1484  {
1485  nPointCounter++;
1486 
1487  if (str.valid())
1488  {
1489  str().write(pp.points()[meshPoints[f[fp]]]);
1490  }
1491  }
1492  }
1493  }
1494  }
1495  }
1496 
1497  reduce(nPointCounter, sumOp<label>());
1498  Info<< typeName
1499  << " : Number of isolated points extrusion stopped : "<< nPointCounter
1500  << endl;
1501 }
1502 
1503 
1504 void Foam::medialAxisMeshMover::smoothLambdaMuDisplacement
1505 (
1506  const label nSmoothDisp,
1507  const PackedBoolList& isMeshMasterPoint,
1508  const PackedBoolList& isMeshMasterEdge,
1509  vectorField& displacement
1510 ) const
1511 {
1512  const edgeList& edges = mesh().edges();
1513 
1514  // Correspondence between local edges/points and mesh edges/points
1515  const labelList meshPoints(identity(mesh().nPoints()));
1516 
1517  // Calculate inverse sum of weights
1518  scalarField edgeWeights(mesh().nEdges());
1519  scalarField invSumWeight(meshPoints.size());
1521  (
1522  mesh(),
1523  isMeshMasterEdge,
1524  meshPoints,
1525  edges,
1526  edgeWeights,
1527  invSumWeight
1528  );
1529 
1530  // Get smoothly varying patch field.
1531  Info<< typeName << " : Smoothing displacement ..." << endl;
1532 
1533  const scalar lambda = 0.33;
1534  const scalar mu = -0.34;
1535 
1537 
1538  for (label iter = 0; iter < nSmoothDisp; iter++)
1539  {
1541  (
1542  mesh(),
1543  isMeshMasterEdge,
1544  meshPoints,
1545  edges,
1546  edgeWeights,
1547  displacement,
1548  average
1549  );
1550  average *= invSumWeight;
1551 
1552  forAll(displacement, i)
1553  {
1554  if (medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL)
1555  {
1556  displacement[i] = (1-lambda)*displacement[i]+lambda*average[i];
1557  }
1558  }
1559 
1561  (
1562  mesh(),
1563  isMeshMasterEdge,
1564  meshPoints,
1565  edges,
1566  edgeWeights,
1567  displacement,
1568  average
1569  );
1570  average *= invSumWeight;
1571 
1572 
1573  forAll(displacement, i)
1574  {
1575  if (medialRatio_[i] > SMALL && medialRatio_[i] < 1-SMALL)
1576  {
1577  displacement[i] = (1-mu)*displacement[i]+mu*average[i];
1578  }
1579  }
1580 
1581 
1582  // Do residual calculation every so often.
1583  if ((iter % 10) == 0)
1584  {
1585  scalar resid = meshRefinement::gAverage
1586  (
1587  isMeshMasterPoint,
1588  mag(displacement-average)()
1589  );
1590  Info<< " Iteration " << iter << " residual " << resid << endl;
1591  }
1592  }
1593 }
1594 
1595 
1596 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
1597 
1598 Foam::medialAxisMeshMover::medialAxisMeshMover
1600  const dictionary& dict,
1601  const List<labelPair>& baffles,
1602  pointVectorField& pointDisplacement
1603 )
1604 :
1605  externalDisplacementMeshMover(dict, baffles, pointDisplacement),
1606  adaptPatchIDs_(getFixedValueBCs(pointDisplacement)),
1607  adaptPatchPtr_(getPatch(mesh(), adaptPatchIDs_)),
1608  scale_
1609  (
1610  IOobject
1611  (
1612  "scale",
1613  pointDisplacement.time().timeName(),
1614  pointDisplacement.db(),
1617  ),
1618  pMesh(),
1619  dimensionedScalar("scale", dimless, 1.0)
1620  ),
1621  oldPoints_(mesh().points()),
1622  meshMover_
1623  (
1624  const_cast<polyMesh&>(mesh()),
1625  const_cast<pointMesh&>(pMesh()),
1626  adaptPatchPtr_(),
1627  pointDisplacement,
1628  scale_,
1629  oldPoints_,
1630  adaptPatchIDs_,
1631  dict
1632  ),
1633  dispVec_
1634  (
1635  IOobject
1636  (
1637  "dispVec",
1638  pointDisplacement.time().timeName(),
1639  pointDisplacement.db(),
1642  false
1643  ),
1644  pMesh(),
1646  ),
1647  medialRatio_
1648  (
1649  IOobject
1650  (
1651  "medialRatio",
1652  pointDisplacement.time().timeName(),
1653  pointDisplacement.db(),
1656  false
1657  ),
1658  pMesh(),
1659  dimensionedScalar("medialRatio", dimless, 0.0)
1660  ),
1661  medialDist_
1662  (
1663  IOobject
1664  (
1665  "pointMedialDist",
1666  pointDisplacement.time().timeName(),
1667  pointDisplacement.db(),
1670  false
1671  ),
1672  pMesh(),
1673  dimensionedScalar("pointMedialDist", dimLength, 0.0)
1674  ),
1675  medialVec_
1676  (
1677  IOobject
1678  (
1679  "medialVec",
1680  pointDisplacement.time().timeName(),
1681  pointDisplacement.db(),
1684  false
1685  ),
1686  pMesh(),
1688  )
1689 {
1690  update(dict);
1691 }
1692 
1693 
1694 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
1695 
1697 {}
1698 
1699 
1700 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
1701 
1702 void Foam::medialAxisMeshMover::calculateDisplacement
1703 (
1704  const dictionary& coeffDict,
1705  const scalarField& minThickness,
1706  List<autoLayerDriver::extrudeMode>& extrudeStatus,
1707  pointField& patchDisp
1708 )
1709 {
1710  Info<< typeName << " : Smoothing using Medial Axis ..." << endl;
1711 
1712  const indirectPrimitivePatch& pp = adaptPatchPtr_;
1713  const labelList& meshPoints = pp.meshPoints();
1714 
1715 
1716  // Read settings
1717  // ~~~~~~~~~~~~~
1718 
1719  //- (lambda-mu) smoothing of internal displacement
1720  const label nSmoothDisplacement = coeffDict.lookupOrDefault
1721  (
1722  "nSmoothDisplacement",
1723  0
1724  );
1725 
1726  //- Layer thickness too big
1727  const scalar maxThicknessToMedialRatio = readScalar
1728  (
1729  coeffDict.lookup("maxThicknessToMedialRatio")
1730  );
1731 
1732  //- Feature angle when to stop adding layers
1733  const scalar featureAngle = readScalar(coeffDict.lookup("featureAngle"));
1734 
1735  //- Stop layer growth where mesh wraps around sharp edge
1736  const scalar minCosLayerTermination = Foam::cos
1737  (
1738  degToRad(0.5*featureAngle)
1739  );
1740 
1741  //- Smoothing wanted patch thickness
1742  const label nSmoothPatchThickness = readLabel
1743  (
1744  coeffDict.lookup("nSmoothThickness")
1745  );
1746 
1747  //- Number of edges walking out
1748  const label nMedialAxisIter = coeffDict.lookupOrDefault<label>
1749  (
1750  "nMedialAxisIter",
1752  );
1753 
1754  //- Use strict extrusionIsland detection
1755  const Switch detectExtrusionIsland = coeffDict.lookupOrDefault<Switch>
1756  (
1757  "detectExtrusionIsland",
1758  false
1759  );
1760 
1761 
1762  // Precalulate master points/edge (only relevant for shared points/edges)
1763  const PackedBoolList isMeshMasterPoint(syncTools::getMasterPoints(mesh()));
1764  const PackedBoolList isMeshMasterEdge(syncTools::getMasterEdges(mesh()));
1765  // Precalculate meshEdge per pp edge
1766  const labelList meshEdges
1767  (
1768  pp.meshEdges
1769  (
1770  mesh().edges(),
1771  mesh().pointEdges()
1772  )
1773  );
1774 
1775  // Precalulate (patch) master point/edge
1776  const PackedBoolList isPatchMasterPoint
1777  (
1779  (
1780  mesh(),
1781  meshPoints
1782  )
1783  );
1784  const PackedBoolList isPatchMasterEdge
1785  (
1787  (
1788  mesh(),
1789  meshEdges
1790  )
1791  );
1792 
1793 
1794  scalarField thickness(patchDisp.size());
1795 
1796  thickness = mag(patchDisp);
1797 
1798  forAll(thickness, patchPointI)
1799  {
1800  if (extrudeStatus[patchPointI] == autoLayerDriver::NOEXTRUDE)
1801  {
1802  thickness[patchPointI] = 0.0;
1803  }
1804  }
1805 
1806  label numThicknessRatioExclude = 0;
1807 
1808  // reduce thickness where thickness/medial axis distance large
1809  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1810 
1811  autoPtr<OBJstream> str;
1812  if (debug)
1813  {
1814  str.reset
1815  (
1816  new OBJstream
1817  (
1818  mesh().time().path()
1819  / "thicknessRatioExcludePoints_"
1820  + mesh().time().timeName()
1821  + ".obj"
1822  )
1823  );
1824  Info<< typeName
1825  << " : Writing points with too large an extrusion distance to "
1826  << str().name() << endl;
1827  }
1828 
1829  autoPtr<OBJstream> medialVecStr;
1830  if (debug)
1831  {
1832  medialVecStr.reset
1833  (
1834  new OBJstream
1835  (
1836  mesh().time().path()
1837  / "thicknessRatioExcludeMedialVec_"
1838  + mesh().time().timeName()
1839  + ".obj"
1840  )
1841  );
1842  Info<< typeName
1843  << " : Writing medial axis vectors on points with too large"
1844  << " an extrusion distance to " << medialVecStr().name() << endl;
1845  }
1846 
1847  forAll(meshPoints, patchPointI)
1848  {
1849  if (extrudeStatus[patchPointI] != autoLayerDriver::NOEXTRUDE)
1850  {
1851  label pointI = meshPoints[patchPointI];
1852 
1853  //- Option 1: look only at extrusion thickness v.s. distance
1854  // to nearest (medial axis or static) point.
1855  scalar mDist = medialDist_[pointI];
1856  scalar thicknessRatio = thickness[patchPointI]/(mDist+VSMALL);
1857 
1858  //- Option 2: Look at component in the direction
1859  // of nearest (medial axis or static) point.
1860  vector n =
1861  patchDisp[patchPointI]
1862  / (mag(patchDisp[patchPointI]) + VSMALL);
1863  vector mVec = mesh().points()[pointI]-medialVec_[pointI];
1864  mVec /= mag(mVec)+VSMALL;
1865  thicknessRatio *= (n&mVec);
1866 
1867  if (thicknessRatio > maxThicknessToMedialRatio)
1868  {
1869  // Truncate thickness.
1870  if (debug&2)
1871  {
1872  Pout<< "truncating displacement at "
1873  << mesh().points()[pointI]
1874  << " from " << thickness[patchPointI]
1875  << " to "
1876  << 0.5
1877  *(
1878  minThickness[patchPointI]
1879  +thickness[patchPointI]
1880  )
1881  << " medial direction:" << mVec
1882  << " extrusion direction:" << n
1883  << " with thicknessRatio:" << thicknessRatio
1884  << endl;
1885  }
1886 
1887  thickness[patchPointI] =
1888  0.5*(minThickness[patchPointI]+thickness[patchPointI]);
1889 
1890  patchDisp[patchPointI] = thickness[patchPointI]*n;
1891 
1892  if (isPatchMasterPoint[patchPointI])
1893  {
1894  numThicknessRatioExclude++;
1895  }
1896 
1897  if (str.valid())
1898  {
1899  const point& pt = mesh().points()[pointI];
1900  str().write(linePointRef(pt, pt+patchDisp[patchPointI]));
1901  }
1902  if (medialVecStr.valid())
1903  {
1904  const point& pt = mesh().points()[pointI];
1905  medialVecStr().write
1906  (
1907  linePointRef
1908  (
1909  pt,
1910  medialVec_[pointI]
1911  )
1912  );
1913  }
1914  }
1915  }
1916  }
1917 
1918  reduce(numThicknessRatioExclude, sumOp<label>());
1919  Info<< typeName << " : Reducing layer thickness at "
1920  << numThicknessRatioExclude
1921  << " nodes where thickness to medial axis distance is large " << endl;
1922 
1923 
1924  // find points where layer growth isolated to a lone point, edge or face
1925 
1926  findIsolatedRegions
1927  (
1928  minCosLayerTermination,
1929  detectExtrusionIsland,
1930 
1931  isPatchMasterPoint,
1932  isPatchMasterEdge,
1933  meshEdges,
1934  minThickness,
1935 
1936  extrudeStatus,
1937  patchDisp
1938  );
1939 
1940  // Update thickess for changed extrusion
1941  forAll(thickness, patchPointI)
1942  {
1943  if (extrudeStatus[patchPointI] == autoLayerDriver::NOEXTRUDE)
1944  {
1945  thickness[patchPointI] = 0.0;
1946  }
1947  }
1948 
1949 
1950  // smooth layer thickness on moving patch
1951  minSmoothField
1952  (
1953  nSmoothPatchThickness,
1954  isPatchMasterPoint,
1955  isPatchMasterEdge,
1956  minThickness,
1957 
1958  thickness
1959  );
1960 
1961 
1962  // Dummy additional info for PointEdgeWave
1963  int dummyTrackData = 0;
1964 
1965  List<pointData> pointWallDist(mesh().nPoints());
1966 
1967  const pointField& points = mesh().points();
1968  // 1. Calculate distance to points where displacement is specified.
1969  // This wave is used to transport layer thickness
1970  {
1971  // Distance to wall and medial axis on edges.
1972  List<pointData> edgeWallDist(mesh().nEdges());
1973  labelList wallPoints(meshPoints.size());
1974 
1975  // Seed data.
1976  List<pointData> wallInfo(meshPoints.size());
1977 
1978  forAll(meshPoints, patchPointI)
1979  {
1980  label pointI = meshPoints[patchPointI];
1981  wallPoints[patchPointI] = pointI;
1982  wallInfo[patchPointI] = pointData
1983  (
1984  points[pointI],
1985  0.0,
1986  thickness[patchPointI], // transport layer thickness
1987  vector::zero // passive vector
1988  );
1989  }
1990 
1991  // Do all calculations
1992  PointEdgeWave<pointData> wallDistCalc
1993  (
1994  mesh(),
1995  wallPoints,
1996  wallInfo,
1997  pointWallDist,
1998  edgeWallDist,
1999  0, // max iterations
2000  dummyTrackData
2001  );
2002  wallDistCalc.iterate(nMedialAxisIter);
2003  }
2004 
2005 
2006  // Calculate scaled displacement vector
2007  pointField& displacement = pointDisplacement_;
2008 
2009  forAll(displacement, pointI)
2010  {
2011  if (!pointWallDist[pointI].valid(dummyTrackData))
2012  {
2013  displacement[pointI] = vector::zero;
2014  }
2015  else
2016  {
2017  // 1) displacement on nearest wall point, scaled by medialRatio
2018  // (wall distance / medial distance)
2019  // 2) pointWallDist[pointI].s() is layer thickness transported
2020  // from closest wall point.
2021  // 3) shrink in opposite direction of addedPoints
2022  displacement[pointI] =
2023  -medialRatio_[pointI]
2024  *pointWallDist[pointI].s()
2025  *dispVec_[pointI];
2026  }
2027  }
2028 
2029 
2030  // Smear displacement away from fixed values (medialRatio=0 or 1)
2031  if (nSmoothDisplacement > 0)
2032  {
2033  smoothLambdaMuDisplacement
2034  (
2035  nSmoothDisplacement,
2036  isMeshMasterPoint,
2037  isMeshMasterEdge,
2038  displacement
2039  );
2040  }
2041 }
2042 
2043 
2044 bool Foam::medialAxisMeshMover::shrinkMesh
2045 (
2046  const dictionary& meshQualityDict,
2047  const label nAllowableErrors,
2048  labelList& checkFaces
2049 )
2050 {
2051  //- Number of attempts shrinking the mesh
2052  const label nSnap = readLabel(meshQualityDict.lookup("nRelaxIter"));
2053 
2054 
2055 
2056 
2057  // Make sure displacement boundary conditions is uptodate with
2058  // internal field
2059  meshMover_.setDisplacementPatchFields();
2060 
2061  Info<< typeName << " : Moving mesh ..." << endl;
2062  scalar oldErrorReduction = -1;
2063 
2064  bool meshOk = false;
2065 
2066  for (label iter = 0; iter < 2*nSnap ; iter++)
2067  {
2068  Info<< typeName
2069  << " : Iteration " << iter << endl;
2070  if (iter == nSnap)
2071  {
2072  Info<< typeName
2073  << " : Displacement scaling for error reduction set to 0."
2074  << endl;
2075  oldErrorReduction = meshMover_.setErrorReduction(0.0);
2076  }
2077 
2078  if
2079  (
2080  meshMover_.scaleMesh
2081  (
2082  checkFaces,
2083  baffles_,
2084  meshMover_.paramDict(),
2085  meshQualityDict,
2086  true,
2087  nAllowableErrors
2088  )
2089  )
2090  {
2091  Info<< typeName << " : Successfully moved mesh" << endl;
2092  meshOk = true;
2093  break;
2094  }
2095  }
2096 
2097  if (oldErrorReduction >= 0)
2098  {
2099  meshMover_.setErrorReduction(oldErrorReduction);
2100  }
2101 
2102  Info<< typeName << " : Finished moving mesh ..." << endl;
2103 
2104  return meshOk;
2105 }
2106 
2107 
2110  const dictionary& moveDict,
2111  const label nAllowableErrors,
2112  labelList& checkFaces
2113 )
2114 {
2115  // Read a few settings
2116  // ~~~~~~~~~~~~~~~~~~~
2117 
2118  //- Name of field specifying min thickness
2119  const word minThicknessName = word(moveDict.lookup("minThicknessName"));
2120 
2121 
2122  // The points have moved so before calculation update
2123  // the mesh and motionSolver accordingly
2124  movePoints(mesh().points());
2125  //
2127  //pointDisplacement_.boundaryField().updateCoeffs();
2128 
2129 
2130  // Extract out patch-wise displacement
2131  const indirectPrimitivePatch& pp = adaptPatchPtr_();
2132 
2133  scalarField zeroMinThickness;
2134  if (minThicknessName == "none")
2135  {
2136  zeroMinThickness = scalarField(pp.nPoints(), 0.0);
2137  }
2138  const scalarField& minThickness =
2139  (
2140  (minThicknessName == "none")
2141  ? zeroMinThickness
2142  : mesh().lookupObject<scalarField>(minThicknessName)
2143  );
2144 
2145 
2146  pointField patchDisp
2147  (
2148  pointDisplacement_.internalField(),
2149  pp.meshPoints()
2150  );
2151 
2153  (
2154  pp.nPoints(),
2156  );
2157  forAll(extrudeStatus, pointI)
2158  {
2159  if (mag(patchDisp[pointI]) <= minThickness[pointI]+SMALL)
2160  {
2161  extrudeStatus[pointI] = autoLayerDriver::NOEXTRUDE;
2162  }
2163  }
2164 
2165 
2166  // Solve displacement
2167  calculateDisplacement(moveDict, minThickness, extrudeStatus, patchDisp);
2168 
2169  //- Move mesh according to calculated displacement
2170  return shrinkMesh
2171  (
2172  moveDict, // meshQualityDict,
2173  nAllowableErrors, // nAllowableErrors
2174  checkFaces
2175  );
2176 }
2177 
2178 
2180 {
2182 
2183  // Update local data for new geometry
2184  adaptPatchPtr_().movePoints(p);
2185 
2186  // Update motionSmoother for new geometry
2187  meshMover_.movePoints();
2188 
2189  // Assume corrent mesh location is correct
2190  meshMover_.correct();
2191 }
2192 
2193 
2194 // ************************************************************************* //
void reduce(const List< UPstream::commsStruct > &comms, T &Value, const BinaryOp &bop, const int tag, const label comm)
dimensionedScalar sqrt(const dimensionedScalar &ds)
virtual void movePoints(const pointField &)
Update local data for geometry changes.
dimensioned< Type > average(const DimensionedField< Type, GeoMesh > &df)
OFstream which keeps track of vertices.
Definition: OBJstream.H:53
const pointField & points
const dimensionedScalar mu
Atomic mass unit.
Definition: createFields.H:13
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
vector point
Point is a vector.
Definition: point.H:41
labelList meshEdges(const edgeList &allEdges, const labelListList &cellEdges, const labelList &faceCells) const
Return labels of patch edges in the global edge list using.
dimensioned< scalar > mag(const dimensioned< Type > &)
GeometricBoundaryField & boundaryField()
Return reference to GeometricBoundaryField.
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
dimensioned< scalar > magSqr(const dimensioned< Type > &)
void reset(T *=0)
If object pointer already set, delete object and set to given.
Definition: autoPtrI.H:114
bool valid() const
Return true if the autoPtr valid (ie, the pointer is set).
Definition: autoPtrI.H:83
static T gAverage(const PackedBoolList &isMasterElem, const UList< T > &values)
Helper: calculate average.
A bit-packed bool list.
A class for handling words, derived from string.
Definition: word.H:59
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
static word timeName(const scalar, const int precision=precision_)
Return time name of given scalar time.
Definition: Time.C:741
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:76
static PackedBoolList getMasterEdges(const polyMesh &)
Get per edge whether it is uncoupled or a master of a.
Definition: syncTools.C:112
static const Vector rootMax
Definition: Vector.H:84
PrimitivePatch< face, IndirectList, const pointField & > indirectPrimitivePatch
Foam::indirectPrimitivePatch.
static void syncEdgeList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh edges.
A simple wrapper around bool so that it can be read as a word: true/false, on/off, yes/no, y/n, t/f, or none.
Definition: Switch.H:60
static PackedBoolList getMasterPoints(const polyMesh &)
Get per point whether it is uncoupled or a master of a.
Definition: syncTools.C:68
messageStream Info
Mesh representing a set of points created from polyMesh.
Definition: pointMesh.H:48
dynamicFvMesh & mesh
A list of keyword definitions, which are a keyword followed by any number of values (e...
Definition: dictionary.H:137
const dimensionSet dimLength(0, 1, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:50
Variant of pointEdgePoint with some transported additional data. WIP - should be templated on data li...
Definition: pointData.H:53
label nPoints() const
Return number of points supporting patch faces.
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Namespace for OpenFOAM.
label readLabel(Istream &is)
Definition: label.H:64
A line primitive.
Definition: line.H:56
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
label n
Ostream & incrIndent(Ostream &os)
Increment the indent level.
Definition: Ostream.H:228
dictionary dict
static const char nl
Definition: Ostream.H:260
void setSize(const label)
Reset size of List.
Definition: List.C:318
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
Ostream & decrIndent(Ostream &os)
Decrement the indent level.
Definition: Ostream.H:235
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:91
#define WarningIn(functionName)
Report a warning using Foam::Warning.
virtual const pointField & points() const
Return raw points.
Definition: polyMesh.C:1035
volScalarField & p
Definition: createFields.H:51
static void weightedSum(const polyMesh &mesh, const PackedBoolList &isMasterEdge, const labelList &meshPoints, const edgeList &edges, const scalarField &edgeWeights, const Field< Type > &data, Field< Type > &sum)
Helper: weighted sum (over all subset of mesh points) by.
Field< vector > vectorField
Specialisation of Field<T> for vector.
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
#define forAll(list, i)
Definition: UList.H:421
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if succesful.
Definition: doubleScalar.H:63
dimensionedScalar lambda(laminarTransport.lookup("lambda"))
virtual Ostream & write(const token &)=0
Write next token to stream.
static PackedBoolList getMasterEdges(const polyMesh &mesh, const labelList &meshEdges)
Determine master edge for subset of edges. If coupled.
dimensionedScalar cos(const dimensionedScalar &ds)
static PackedBoolList getMasterPoints(const polyMesh &mesh, const labelList &meshPoints)
Determine master point for subset of points. If coupled.
const Type & lookupObject(const word &name) const
Lookup and return the object of the given Type.
Macros for easy insertion into run-time selection tables.
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Unit conversion functions.
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
static void syncPointList(const polyMesh &, List< T > &, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
label iterate(const label maxIter)
Iterate until no changes or maxIter reached. Returns actual.
label nTotalPoints() const
Return total number of points in decomposed mesh. Not.
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:421
List< edge > edgeList
Definition: edgeList.H:38
HashSet< label, Hash< label > > labelHashSet
A HashSet with label keys.
Definition: HashSet.H:210
A list of faces which address into the list of points.
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< Face, FaceList, PointField, PointType > &)
Return parallel consistent point normals for patches using mesh points.
static void calculateEdgeWeights(const polyMesh &mesh, const PackedBoolList &isMasterEdge, const labelList &meshPoints, const edgeList &edges, scalarField &edgeWeights, scalarField &invSumWeight)
Helper: calculate edge weights (1/length)
virtual const fileName & name() const
Return the name of the stream.
Definition: IOstream.H:297
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:452
T lookupOrDefault(const word &, const T &, bool recursive=false, bool patternMatch=true) const
Find and return a T,.
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const Vector zero
Definition: Vector.H:80
scalar degToRad(const scalar deg)
Conversion from degrees to radians.
Wave propagation of information through grid. Every iteration information goes through one layer of e...
Definition: PointEdgeWave.H:85
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
Definition: pointFields.H:50
const Time & time() const
Return time.
Definition: IOobject.C:251
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:47
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:45
virtual void movePoints(const pointField &)
Update local data for geometry changes.
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints,-1);const cellModel &hex=*(cellModeller::lookup("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]=cellShape(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
dimensionedSymmTensor sqr(const dimensionedVector &dv)
label nPoints
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
virtual bool move(const dictionary &, const label nAllowableErrors, labelList &checkFaces)
Move mesh using current pointDisplacement boundary values.
const objectRegistry & db() const
Return the local objectRegistry.
Definition: IOobject.C:245
bool isA(const Type &t)
Check if a dynamic_cast to typeid is possible.
Definition: typeInfo.H:134
addToRunTimeSelectionTable(ensightPart, ensightPartCells, istream)
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:117
Virtual base class for mesh movers with externally provided displacement field giving the boundary co...
const globalMeshData & globalData() const
Return parallel info.
Definition: polyMesh.C:1200
defineTypeNameAndDebug(combustionModel, 0)
word timeName
Definition: getTimeIndex.H:3
prefixOSstream Pout(cout,"Pout")
Definition: IOstreams.H:53
List< bool > boolList
Bool container classes.
Definition: boolList.H:50
pointPatchField< vector > pointPatchVectorField