meshRefinementRefine.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) 2011-2017 OpenFOAM Foundation
9  Copyright (C) 2015-2023 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 "meshRefinement.H"
30 #include "trackedParticle.H"
31 #include "syncTools.H"
32 #include "Time.H"
33 #include "refinementSurfaces.H"
34 #include "refinementFeatures.H"
35 #include "shellSurfaces.H"
36 #include "faceSet.H"
37 #include "decompositionMethod.H"
38 #include "fvMeshDistribute.H"
39 #include "polyTopoChange.H"
40 #include "mapDistributePolyMesh.H"
41 #include "Cloud.H"
42 //#include "globalIndex.H"
43 #include "cellSet.H"
44 #include "treeDataCell.H"
45 
46 #include "cellCuts.H"
47 #include "refineCell.H"
48 #include "hexCellLooper.H"
49 #include "meshCutter.H"
50 
51 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
52 
53 namespace Foam
54 {
55  //- To compare normals
56  static bool less(const vector& x, const vector& y)
57  {
58  for (direction i = 0; i < vector::nComponents; i++)
59  {
60  if (x[i] < y[i])
61  {
62  return true;
63  }
64  else if (x[i] > y[i])
65  {
66  return false;
67  }
68  }
69  // All components the same
70  return false;
71  }
72 
73 
74  //- To compare normals
75  class normalLess
76  {
77  const vectorList& values_;
78 
79  public:
80 
82  :
83  values_(values)
84  {}
85 
86  bool operator()(const label a, const label b) const
87  {
88  return less(values_[a], values_[b]);
89  }
90  };
91 
92 
93  //- Template specialization for pTraits<labelList> so we can have fields
94  template<>
95  class pTraits<labelList>
96  {
97 
98  public:
99 
100  //- Component type
101  typedef labelList cmptType;
102  };
103 
104  //- Template specialization for pTraits<labelList> so we can have fields
105  template<>
106  class pTraits<vectorList>
107  {
108 
109  public:
110 
111  //- Component type
112  typedef vectorList cmptType;
113  };
114 }
115 
116 
117 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
118 
119 // Get faces (on the new mesh) that have in some way been affected by the
120 // mesh change. Picks up all faces but those that are between two
121 // unrefined faces. (Note that of an unchanged face the edge still might be
122 // split but that does not change any face centre or cell centre.
123 Foam::labelList Foam::meshRefinement::getChangedFaces
124 (
125  const mapPolyMesh& map,
126  const labelList& oldCellsToRefine
127 )
128 {
129  const polyMesh& mesh = map.mesh();
130 
131  labelList changedFaces;
132 
133  // For reporting: number of masterFaces changed
134  label nMasterChanged = 0;
135  bitSet isMasterFace(syncTools::getMasterFaces(mesh));
136 
137  {
138  // Mark any face on a cell which has been added or changed
139  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
140  // Note that refining a face changes the face centre (for a warped face)
141  // which changes the cell centre. This again changes the cellcentre-
142  // cellcentre edge across all faces of the cell.
143  // Note: this does not happen for unwarped faces but unfortunately
144  // we don't have this information.
145 
146  const labelList& faceOwner = mesh.faceOwner();
147  const labelList& faceNeighbour = mesh.faceNeighbour();
148  const cellList& cells = mesh.cells();
149  const label nInternalFaces = mesh.nInternalFaces();
150 
151  // Mark refined cells on old mesh
152  bitSet oldRefineCell(map.nOldCells(), oldCellsToRefine);
153 
154  // Mark refined faces
155  bitSet refinedInternalFace(nInternalFaces);
156 
157  // 1. Internal faces
158 
159  for (label faceI = 0; faceI < nInternalFaces; faceI++)
160  {
161  label oldOwn = map.cellMap()[faceOwner[faceI]];
162  label oldNei = map.cellMap()[faceNeighbour[faceI]];
163 
164  if
165  (
166  oldOwn >= 0 && !oldRefineCell.test(oldOwn)
167  && oldNei >= 0 && !oldRefineCell.test(oldNei)
168  )
169  {
170  // Unaffected face since both neighbours were not refined.
171  }
172  else
173  {
174  refinedInternalFace.set(faceI);
175  }
176  }
177 
178 
179  // 2. Boundary faces
180 
181  boolList refinedBoundaryFace(mesh.nBoundaryFaces(), false);
182 
183  forAll(mesh.boundaryMesh(), patchI)
184  {
185  const polyPatch& pp = mesh.boundaryMesh()[patchI];
186 
187  label faceI = pp.start();
188 
189  forAll(pp, i)
190  {
191  label oldOwn = map.cellMap()[faceOwner[faceI]];
192 
193  if (oldOwn >= 0 && !oldRefineCell.test(oldOwn))
194  {
195  // owner did exist and wasn't refined.
196  }
197  else
198  {
199  refinedBoundaryFace[faceI-nInternalFaces] = true;
200  }
201  faceI++;
202  }
203  }
204 
205  // Synchronise refined face status
207  (
208  mesh,
209  refinedBoundaryFace,
210  orEqOp<bool>()
211  );
212 
213 
214  // 3. Mark all faces affected by refinement. Refined faces are in
215  // - refinedInternalFace
216  // - refinedBoundaryFace
217  boolList changedFace(mesh.nFaces(), false);
218 
219  forAll(refinedInternalFace, faceI)
220  {
221  if (refinedInternalFace.test(faceI))
222  {
223  const cell& ownFaces = cells[faceOwner[faceI]];
224  forAll(ownFaces, ownI)
225  {
226  changedFace[ownFaces[ownI]] = true;
227  }
228  const cell& neiFaces = cells[faceNeighbour[faceI]];
229  forAll(neiFaces, neiI)
230  {
231  changedFace[neiFaces[neiI]] = true;
232  }
233  }
234  }
235 
236  forAll(refinedBoundaryFace, i)
237  {
238  if (refinedBoundaryFace[i])
239  {
240  const cell& ownFaces = cells[faceOwner[i+nInternalFaces]];
241  forAll(ownFaces, ownI)
242  {
243  changedFace[ownFaces[ownI]] = true;
244  }
245  }
246  }
247 
249  (
250  mesh,
251  changedFace,
252  orEqOp<bool>()
253  );
254 
255 
256  // Now we have in changedFace marked all affected faces. Pack.
257  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
258 
259  changedFaces = findIndices(changedFace, true);
260 
261  // Count changed master faces.
262  nMasterChanged = 0;
263 
264  forAll(changedFace, faceI)
265  {
266  if (changedFace[faceI] && isMasterFace.test(faceI))
267  {
268  nMasterChanged++;
269  }
270  }
271 
272  }
273 
275  {
276  Pout<< "getChangedFaces : Detected "
277  << " local:" << changedFaces.size()
278  << " global:" << returnReduce(nMasterChanged, sumOp<label>())
279  << " changed faces out of " << mesh.globalData().nTotalFaces()
280  << endl;
281 
282  faceSet changedFacesSet(mesh, "changedFaces", changedFaces);
283  Pout<< "getChangedFaces : Writing " << changedFaces.size()
284  << " changed faces to faceSet " << changedFacesSet.name()
285  << endl;
286  changedFacesSet.write();
287  }
288 
289  return changedFaces;
290 }
291 
292 
293 // Mark cell for refinement (if not already marked). Return false if
294 // refinelimit hit. Keeps running count (in nRefine) of cells marked for
295 // refinement
296 bool Foam::meshRefinement::markForRefine
297 (
298  const label markValue,
299  const label nAllowRefine,
300 
301  label& cellValue,
302  label& nRefine
303 )
304 {
305  if (cellValue == -1)
306  {
307  cellValue = markValue;
308  nRefine++;
309  }
310 
311  return nRefine <= nAllowRefine;
312 }
313 
314 
315 void Foam::meshRefinement::markFeatureCellLevel
316 (
317  const pointField& keepPoints,
318  labelList& maxFeatureLevel
319 ) const
320 {
321  // We want to refine all cells containing a feature edge.
322  // - don't want to search over whole mesh
323  // - don't want to build octree for whole mesh
324  // - so use tracking to follow the feature edges
325  //
326  // 1. find non-manifold points on feature edges (i.e. start of feature edge
327  // or 'knot')
328  // 2. seed particle starting at keepPoint going to this non-manifold point
329  // 3. track particles to their non-manifold point
330  //
331  // 4. track particles across their connected feature edges, marking all
332  // visited cells with their level (through trackingData)
333  // 5. do 4 until all edges have been visited.
334 
335 
336  // Find all start cells of features. Is done by tracking from keepPoint.
337  Cloud<trackedParticle> startPointCloud
338  (
339  mesh_,
340  Foam::zero{},
341  "startPointCloud"
342  );
343 
344 
345  // Features are identical on all processors. Number them so we know
346  // what to seed. Do this on only the processor that
347  // holds the keepPoint.
348 
349  for (const point& keepPoint : keepPoints)
350  {
351  const label celli = mesh_.cellTree().findInside(keepPoint);
352 
353  if (celli != -1)
354  {
355  // I am the processor that holds the keepPoint
356 
357  forAll(features_, feati)
358  {
359  const edgeMesh& featureMesh = features_[feati];
360  const label featureLevel = features_.levels()[feati][0];
361  const labelListList& pointEdges = featureMesh.pointEdges();
362 
363  // Find regions on edgeMesh
364  labelList edgeRegion;
365  label nRegions = featureMesh.regions(edgeRegion);
366 
367 
368  bitSet regionVisited(nRegions);
369 
370 
371  // 1. Seed all 'knots' in edgeMesh
372 
373 
374  forAll(pointEdges, pointi)
375  {
376  if (pointEdges[pointi].size() != 2)
377  {
379  {
380  Pout<< "Adding particle from point:" << pointi
381  << " coord:" << featureMesh.points()[pointi]
382  << " since number of emanating edges:"
383  << pointEdges[pointi].size()
384  << endl;
385  }
386 
387  // Non-manifold point. Create particle.
388  startPointCloud.addParticle
389  (
390  new trackedParticle
391  (
392  mesh_,
393  keepPoint,
394  celli,
395  featureMesh.points()[pointi], // endpos
396  featureLevel, // level
397  feati, // featureMesh
398  pointi, // end point
399  -1 // feature edge
400  )
401  );
402 
403  // Mark
404  if (pointEdges[pointi].size() > 0)
405  {
406  label e0 = pointEdges[pointi][0];
407  label regioni = edgeRegion[e0];
408  regionVisited.set(regioni);
409  }
410  }
411  }
412 
413 
414  // 2. Any regions that have not been visited at all? These can
415  // only be circular regions!
416  forAll(featureMesh.edges(), edgei)
417  {
418  if (regionVisited.set(edgeRegion[edgei]))
419  {
420  const edge& e = featureMesh.edges()[edgei];
421  label pointi = e.start();
423  {
424  Pout<< "Adding particle from point:" << pointi
425  << " coord:" << featureMesh.points()[pointi]
426  << " on circular region:" << edgeRegion[edgei]
427  << endl;
428  }
429 
430  // Non-manifold point. Create particle.
431  startPointCloud.addParticle
432  (
433  new trackedParticle
434  (
435  mesh_,
436  keepPoint,
437  celli,
438  featureMesh.points()[pointi], // endpos
439  featureLevel, // level
440  feati, // featureMesh
441  pointi, // end point
442  -1 // feature edge
443  )
444  );
445  }
446  }
447  }
448  }
449  }
450 
451 
452  // Largest refinement level of any feature passed through
453  maxFeatureLevel = labelList(mesh_.nCells(), -1);
454 
455  // Whether edge has been visited.
456  List<bitSet> featureEdgeVisited(features_.size());
457 
458  forAll(features_, featI)
459  {
460  featureEdgeVisited[featI].setSize(features_[featI].edges().size());
461  featureEdgeVisited[featI] = false;
462  }
463 
464  // Database to pass into trackedParticle::move
465  trackedParticle::trackingData td
466  (
467  startPointCloud,
468  maxFeatureLevel,
469  featureEdgeVisited
470  );
471 
472 
473  // Track all particles to their end position (= starting feature point)
474  // Note that the particle might have started on a different processor
475  // so this will transfer across nicely until we can start tracking proper.
476  scalar maxTrackLen = 2.0*mesh_.bounds().mag();
477 
479  {
480  Pout<< "Tracking " << startPointCloud.size()
481  << " particles over distance " << maxTrackLen
482  << " to find the starting cell" << endl;
483  }
484  startPointCloud.move(startPointCloud, td, maxTrackLen);
485 
486 
487  // Reset levels
488  maxFeatureLevel = -1;
489  forAll(features_, featI)
490  {
491  featureEdgeVisited[featI] = false;
492  }
493 
494 
495  // Start with empty cloud
496  Cloud<trackedParticle> cloud(mesh_, Foam::zero{}, "featureCloud");
497 
499  {
500  Pout<< "Constructing cloud for cell marking" << endl;
501  }
502 
503  for (const trackedParticle& startTp : startPointCloud)
504  {
505  const label featI = startTp.i();
506  const label pointI = startTp.j();
507 
508  const edgeMesh& featureMesh = features_[featI];
509  const labelList& pEdges = featureMesh.pointEdges()[pointI];
510 
511  // Now shoot particles down all pEdges.
512  forAll(pEdges, pEdgeI)
513  {
514  label edgeI = pEdges[pEdgeI];
515 
516  if (featureEdgeVisited[featI].set(edgeI))
517  {
518  // Unvisited edge. Make the particle go to the other point
519  // on the edge.
520 
521  const edge& e = featureMesh.edges()[edgeI];
522  label otherPointi = e.otherVertex(pointI);
523 
524  trackedParticle* tp(new trackedParticle(startTp));
525  tp->start() = tp->position();
526  tp->end() = featureMesh.points()[otherPointi];
527  tp->j() = otherPointi;
528  tp->k() = edgeI;
529 
531  {
532  Pout<< "Adding particle for point:" << pointI
533  << " coord:" << tp->position()
534  << " feature:" << featI
535  << " to track to:" << tp->end()
536  << endl;
537  }
538 
539  cloud.addParticle(tp);
540  }
541  }
542  }
543 
544  startPointCloud.clear();
545 
546 
547  while (true)
548  {
549  // Track all particles to their end position.
551  {
552  Pout<< "Tracking " << cloud.size()
553  << " particles over distance " << maxTrackLen
554  << " to mark cells" << endl;
555  }
556  cloud.move(cloud, td, maxTrackLen);
557 
558  // Make particle follow edge.
559  for (trackedParticle& tp : cloud)
560  {
561  const label featI = tp.i();
562  const label pointI = tp.j();
563 
564  const edgeMesh& featureMesh = features_[featI];
565  const labelList& pEdges = featureMesh.pointEdges()[pointI];
566 
567  // Particle now at pointI. Check connected edges to see which one
568  // we have to visit now.
569 
570  bool keepParticle = false;
571 
572  forAll(pEdges, i)
573  {
574  label edgeI = pEdges[i];
575 
576  if (featureEdgeVisited[featI].set(edgeI))
577  {
578  // Unvisited edge. Make the particle go to the other point
579  // on the edge.
580 
581  const edge& e = featureMesh.edges()[edgeI];
582  label otherPointi = e.otherVertex(pointI);
583 
584  tp.start() = tp.position();
585  tp.end() = featureMesh.points()[otherPointi];
586  tp.j() = otherPointi;
587  tp.k() = edgeI;
588  keepParticle = true;
589  break;
590  }
591  }
592 
593  if (!keepParticle)
594  {
595  // Particle at 'knot' where another particle already has been
596  // seeded. Delete particle.
597  cloud.deleteParticle(tp);
598  }
599  }
600 
601 
603  {
604  Pout<< "Remaining particles " << cloud.size() << endl;
605  }
606 
607  if (!returnReduceOr(cloud.size()))
608  {
609  break;
610  }
611  }
612 
613 
614 
615  //if (debug&meshRefinement::FEATURESEEDS)
616  //{
617  // forAll(maxFeatureLevel, cellI)
618  // {
619  // if (maxFeatureLevel[cellI] != -1)
620  // {
621  // Pout<< "Feature went through cell:" << cellI
622  // << " coord:" << mesh_.cellCentres()[cellI]
623  // << " level:" << maxFeatureLevel[cellI]
624  // << endl;
625  // }
626  // }
627  //}
628 }
629 
630 
631 // Calculates list of cells to refine based on intersection with feature edge.
632 Foam::label Foam::meshRefinement::markFeatureRefinement
633 (
634  const pointField& keepPoints,
635  const label nAllowRefine,
636 
637  labelList& refineCell,
638  label& nRefine
639 ) const
640 {
641  // Largest refinement level of any feature passed through
642  labelList maxFeatureLevel;
643  markFeatureCellLevel(keepPoints, maxFeatureLevel);
644 
645  // See which cells to refine. maxFeatureLevel will hold highest level
646  // of any feature edge that passed through.
647 
648  const labelList& cellLevel = meshCutter_.cellLevel();
649 
650  label oldNRefine = nRefine;
651 
652  forAll(maxFeatureLevel, cellI)
653  {
654  if (maxFeatureLevel[cellI] > cellLevel[cellI])
655  {
656  // Mark
657  if
658  (
659  !markForRefine
660  (
661  0, // surface (n/a)
662  nAllowRefine,
663  refineCell[cellI],
664  nRefine
665  )
666  )
667  {
668  // Reached limit
669  break;
670  }
671  }
672  }
673 
674  if
675  (
676  returnReduce(nRefine, sumOp<label>())
677  > returnReduce(nAllowRefine, sumOp<label>())
678  )
679  {
680  Info<< "Reached refinement limit." << endl;
681  }
682 
683  return returnReduce(nRefine-oldNRefine, sumOp<label>());
684 }
685 
686 
687 // Mark cells for distance-to-feature based refinement.
688 Foam::label Foam::meshRefinement::markInternalDistanceToFeatureRefinement
689 (
690  const label nAllowRefine,
691 
692  labelList& refineCell,
693  label& nRefine
694 ) const
695 {
696  const labelList& cellLevel = meshCutter_.cellLevel();
697  const pointField& cellCentres = mesh_.cellCentres();
698 
699  // Detect if there are any distance shells
700  if (features_.maxDistance() <= 0.0)
701  {
702  return 0;
703  }
704 
705  label oldNRefine = nRefine;
706 
707  // Collect cells to test
708  pointField testCc(cellLevel.size()-nRefine);
709  labelList testLevels(cellLevel.size()-nRefine);
710  label testI = 0;
711 
712  forAll(cellLevel, cellI)
713  {
714  if (refineCell[cellI] == -1)
715  {
716  testCc[testI] = cellCentres[cellI];
717  testLevels[testI] = cellLevel[cellI];
718  testI++;
719  }
720  }
721 
722  // Do test to see whether cells are inside/outside shell with higher level
723  labelList maxLevel;
724  features_.findHigherLevel(testCc, testLevels, maxLevel);
725 
726  // Mark for refinement. Note that we didn't store the original cellID so
727  // now just reloop in same order.
728  testI = 0;
729  forAll(cellLevel, cellI)
730  {
731  if (refineCell[cellI] == -1)
732  {
733  if (maxLevel[testI] > testLevels[testI])
734  {
735  bool reachedLimit = !markForRefine
736  (
737  maxLevel[testI], // mark with any positive value
738  nAllowRefine,
739  refineCell[cellI],
740  nRefine
741  );
742 
743  if (reachedLimit)
744  {
745  if (debug)
746  {
747  Pout<< "Stopped refining internal cells"
748  << " since reaching my cell limit of "
749  << mesh_.nCells()+7*nRefine << endl;
750  }
751  break;
752  }
753  }
754  testI++;
755  }
756  }
757 
758  if
759  (
760  returnReduce(nRefine, sumOp<label>())
761  > returnReduce(nAllowRefine, sumOp<label>())
762  )
763  {
764  Info<< "Reached refinement limit." << endl;
765  }
766 
767  return returnReduce(nRefine-oldNRefine, sumOp<label>());
768 }
769 
770 
771 // Mark cells for non-surface intersection based refinement.
772 Foam::label Foam::meshRefinement::markInternalRefinement
773 (
774  const label nAllowRefine,
775 
776  labelList& refineCell,
777  label& nRefine
778 ) const
779 {
780  const labelList& cellLevel = meshCutter_.cellLevel();
781  const pointField& cellCentres = mesh_.cellCentres();
782 
783  label oldNRefine = nRefine;
784 
785  // Collect cells to test
786  pointField testCc(cellLevel.size()-nRefine);
787  labelList testLevels(cellLevel.size()-nRefine);
788  label testI = 0;
789 
790  forAll(cellLevel, cellI)
791  {
792  if (refineCell[cellI] == -1)
793  {
794  testCc[testI] = cellCentres[cellI];
795  testLevels[testI] = cellLevel[cellI];
796  testI++;
797  }
798  }
799 
800  // Do test to see whether cells are inside/outside shell with higher level
801  labelList maxLevel;
802  shells_.findHigherLevel(testCc, testLevels, maxLevel);
803 
804  // Mark for refinement. Note that we didn't store the original cellID so
805  // now just reloop in same order.
806  testI = 0;
807  forAll(cellLevel, cellI)
808  {
809  if (refineCell[cellI] == -1)
810  {
811  if (maxLevel[testI] > testLevels[testI])
812  {
813  bool reachedLimit = !markForRefine
814  (
815  maxLevel[testI], // mark with any positive value
816  nAllowRefine,
817  refineCell[cellI],
818  nRefine
819  );
820 
821  if (reachedLimit)
822  {
823  if (debug)
824  {
825  Pout<< "Stopped refining internal cells"
826  << " since reaching my cell limit of "
827  << mesh_.nCells()+7*nRefine << endl;
828  }
829  break;
830  }
831  }
832  testI++;
833  }
834  }
835 
836  if
837  (
838  returnReduce(nRefine, sumOp<label>())
839  > returnReduce(nAllowRefine, sumOp<label>())
840  )
841  {
842  Info<< "Reached refinement limit." << endl;
843  }
844 
845  return returnReduce(nRefine-oldNRefine, sumOp<label>());
846 }
847 
848 
849 Foam::label Foam::meshRefinement::unmarkInternalRefinement
850 (
851  labelList& refineCell,
852  label& nRefine
853 ) const
854 {
855  const labelList& cellLevel = meshCutter_.cellLevel();
856  const pointField& cellCentres = mesh_.cellCentres();
857 
858  label oldNRefine = nRefine;
859 
860  // Collect cells to test
861  pointField testCc(nRefine);
862  labelList testLevels(nRefine);
863  label testI = 0;
864 
865  forAll(cellLevel, cellI)
866  {
867  if (refineCell[cellI] >= 0)
868  {
869  testCc[testI] = cellCentres[cellI];
870  testLevels[testI] = cellLevel[cellI];
871  testI++;
872  }
873  }
874 
875  // Do test to see whether cells are inside/outside shell with higher level
876  labelList levelShell;
877  limitShells_.findLevel(testCc, testLevels, levelShell);
878 
879  // Mark for refinement. Note that we didn't store the original cellID so
880  // now just reloop in same order.
881  testI = 0;
882  forAll(cellLevel, cellI)
883  {
884  if (refineCell[cellI] >= 0)
885  {
886  if (levelShell[testI] != -1)
887  {
888  refineCell[cellI] = -1;
889  nRefine--;
890  }
891  testI++;
892  }
893  }
894 
895  return returnReduce(oldNRefine-nRefine, sumOp<label>());
896 }
897 
898 
899 // Collect faces that are intersected and whose neighbours aren't yet marked
900 // for refinement.
901 Foam::labelList Foam::meshRefinement::getRefineCandidateFaces
902 (
903  const labelList& refineCell
904 ) const
905 {
906  labelList testFaces(mesh_.nFaces());
907 
908  label nTest = 0;
909 
910  const labelList& surfIndex = surfaceIndex();
911 
912  labelList boundaryRefineCell;
913  syncTools::swapBoundaryCellList(mesh_, refineCell, boundaryRefineCell);
914 
915  forAll(surfIndex, faceI)
916  {
917  if (surfIndex[faceI] != -1)
918  {
919  label own = mesh_.faceOwner()[faceI];
920 
921  if (mesh_.isInternalFace(faceI))
922  {
923  label nei = mesh_.faceNeighbour()[faceI];
924 
925  if (refineCell[own] == -1 || refineCell[nei] == -1)
926  {
927  testFaces[nTest++] = faceI;
928  }
929  }
930  else
931  {
932  const label bFacei = faceI - mesh_.nInternalFaces();
933  if (refineCell[own] == -1 || boundaryRefineCell[bFacei] == -1)
934  {
935  testFaces[nTest++] = faceI;
936  }
937  }
938  }
939  }
940  testFaces.setSize(nTest);
941 
942  return testFaces;
943 }
944 
945 
946 // Mark cells for surface intersection based refinement.
947 Foam::label Foam::meshRefinement::markSurfaceRefinement
948 (
949  const label nAllowRefine,
950  const labelList& neiLevel,
951  const pointField& neiCc,
952 
953  labelList& refineCell,
954  label& nRefine
955 ) const
956 {
957  const labelList& cellLevel = meshCutter_.cellLevel();
958 
959  label oldNRefine = nRefine;
960 
961  // Use cached surfaceIndex_ to detect if any intersection. If so
962  // re-intersect to determine level wanted.
963 
964  // Collect candidate faces
965  // ~~~~~~~~~~~~~~~~~~~~~~~
966 
967  labelList testFaces(getRefineCandidateFaces(refineCell));
968 
969  // Collect segments
970  // ~~~~~~~~~~~~~~~~
971 
972  pointField start(testFaces.size());
973  pointField end(testFaces.size());
974  labelList minLevel(testFaces.size());
975 
976  calcCellCellRays
977  (
978  neiCc,
979  neiLevel,
980  testFaces,
981  start,
982  end,
983  minLevel
984  );
985 
986 
987  // Do test for higher intersections
988  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
989 
990  labelList surfaceHit;
991  labelList surfaceMinLevel;
992  surfaces_.findHigherIntersection
993  (
994  shells_,
995  start,
996  end,
997  minLevel,
998 
999  surfaceHit,
1000  surfaceMinLevel
1001  );
1002 
1003 
1004  // Mark cells for refinement
1005  // ~~~~~~~~~~~~~~~~~~~~~~~~~
1006 
1007  forAll(testFaces, i)
1008  {
1009  label faceI = testFaces[i];
1010 
1011  label surfI = surfaceHit[i];
1012 
1013  if (surfI != -1)
1014  {
1015  // Found intersection with surface with higher wanted
1016  // refinement. Check if the level field on that surface
1017  // specifies an even higher level. Note:this is weird. Should
1018  // do the check with the surfaceMinLevel whilst intersecting the
1019  // surfaces?
1020 
1021  label own = mesh_.faceOwner()[faceI];
1022 
1023  if (surfaceMinLevel[i] > cellLevel[own])
1024  {
1025  // Owner needs refining
1026  if
1027  (
1028  !markForRefine
1029  (
1030  surfI,
1031  nAllowRefine,
1032  refineCell[own],
1033  nRefine
1034  )
1035  )
1036  {
1037  break;
1038  }
1039  }
1040 
1041  if (mesh_.isInternalFace(faceI))
1042  {
1043  label nei = mesh_.faceNeighbour()[faceI];
1044  if (surfaceMinLevel[i] > cellLevel[nei])
1045  {
1046  // Neighbour needs refining
1047  if
1048  (
1049  !markForRefine
1050  (
1051  surfI,
1052  nAllowRefine,
1053  refineCell[nei],
1054  nRefine
1055  )
1056  )
1057  {
1058  break;
1059  }
1060  }
1061  }
1062  }
1063  }
1064 
1065  if
1066  (
1067  returnReduce(nRefine, sumOp<label>())
1068  > returnReduce(nAllowRefine, sumOp<label>())
1069  )
1070  {
1071  Info<< "Reached refinement limit." << endl;
1072  }
1073 
1074  return returnReduce(nRefine-oldNRefine, sumOp<label>());
1075 }
1076 
1077 
1078 // Count number of matches of first argument in second argument
1079 Foam::label Foam::meshRefinement::countMatches
1080 (
1081  const List<point>& normals1,
1082  const List<point>& normals2,
1083  const scalar tol
1084 ) const
1085 {
1086  label nMatches = 0;
1087 
1088  forAll(normals1, i)
1089  {
1090  const vector& n1 = normals1[i];
1091 
1092  forAll(normals2, j)
1093  {
1094  const vector& n2 = normals2[j];
1095 
1096  if (magSqr(n1-n2) < tol)
1097  {
1098  nMatches++;
1099  break;
1100  }
1101  }
1102  }
1103 
1104  return nMatches;
1105 }
1106 
1107 
1108 //bool Foam::meshRefinement::highCurvature
1109 //(
1110 // const scalar minCosAngle,
1111 // const point& p0,
1112 // const vector& n0,
1113 // const point& p1,
1114 // const vector& n1
1115 //) const
1116 //{
1117 // return ((n0&n1) < minCosAngle);
1118 //}
1119 bool Foam::meshRefinement::highCurvature
1120 (
1121  const scalar minCosAngle,
1122  const scalar lengthScale,
1123  const point& p0,
1124  const vector& n0,
1125  const point& p1,
1126  const vector& n1
1127 ) const
1128 {
1129  const scalar cosAngle = (n0&n1);
1130 
1131  if (cosAngle < minCosAngle)
1132  {
1133  // Sharp feature, independent of intersection points
1134  return true;
1135  }
1136  else if (cosAngle > 1-1e-6)
1137  {
1138  // Co-planar
1139  return false;
1140  }
1141  else if (lengthScale > SMALL)
1142  {
1143  // Calculate radius of curvature
1144 
1145  vector axis(n0 ^ n1);
1146  const plane pl0(p0, (n0 ^ axis));
1147  const scalar r1 = pl0.normalIntersect(p1, n1);
1148 
1149  //const point radiusPoint(p1+r1*n1);
1150  //DebugVar(radiusPoint);
1151  const plane pl1(p1, (n1 ^ axis));
1152  const scalar r0 = pl1.normalIntersect(p0, n0);
1153 
1154  //const point radiusPoint(p0+r0*n0);
1155  //DebugVar(radiusPoint);
1156  //- Take average radius. Bit ad hoc
1157  const scalar radius = 0.5*(mag(r1)+mag(r0));
1158 
1159  if (radius < lengthScale)
1160  {
1161  return true;
1162  }
1163  else
1164  {
1165  return false;
1166  }
1167  }
1168  else
1169  {
1170  return false;
1171  }
1172 }
1173 //XXXXX
1174 
1175 
1176 // Mark cells for surface curvature based refinement.
1177 Foam::label Foam::meshRefinement::markSurfaceCurvatureRefinement
1178 (
1179  const scalar curvature,
1180  const label nAllowRefine,
1181  const labelList& neiLevel,
1182  const pointField& neiCc,
1183 
1184  labelList& refineCell,
1185  label& nRefine
1186 ) const
1187 {
1188  const labelList& cellLevel = meshCutter_.cellLevel();
1189  const pointField& cellCentres = mesh_.cellCentres();
1190 
1191  label oldNRefine = nRefine;
1192 
1193  // 1. local test: any cell on more than one surface gets refined
1194  // (if its current level is < max of the surface max level)
1195 
1196  // 2. 'global' test: any cell on only one surface with a neighbour
1197  // on a different surface gets refined (if its current level etc.)
1198 
1199 
1200  const bitSet isMasterFace(syncTools::getMasterFaces(mesh_));
1201 
1202 
1203  // Collect candidate faces (i.e. intersecting any surface and
1204  // owner/neighbour not yet refined.
1205  labelList testFaces(getRefineCandidateFaces(refineCell));
1206 
1207  // Collect segments
1208  pointField start(testFaces.size());
1209  pointField end(testFaces.size());
1210  labelList minLevel(testFaces.size());
1211 
1212  // Note: uses isMasterFace otherwise could be call to calcCellCellRays
1213  forAll(testFaces, i)
1214  {
1215  label faceI = testFaces[i];
1216 
1217  label own = mesh_.faceOwner()[faceI];
1218 
1219  if (mesh_.isInternalFace(faceI))
1220  {
1221  label nei = mesh_.faceNeighbour()[faceI];
1222 
1223  start[i] = cellCentres[own];
1224  end[i] = cellCentres[nei];
1225  minLevel[i] = min(cellLevel[own], cellLevel[nei]);
1226  }
1227  else
1228  {
1229  label bFaceI = faceI - mesh_.nInternalFaces();
1230 
1231  start[i] = cellCentres[own];
1232  end[i] = neiCc[bFaceI];
1233 
1234  if (!isMasterFace[faceI])
1235  {
1236  std::swap(start[i], end[i]);
1237  }
1238 
1239  minLevel[i] = min(cellLevel[own], neiLevel[bFaceI]);
1240  }
1241  }
1242 
1243  // Extend segments a bit
1244  {
1245  const vectorField smallVec(ROOTSMALL*(end-start));
1246  start -= smallVec;
1247  end += smallVec;
1248  }
1249 
1250 
1251  // If no curvature supplied behave as before
1252  const bool hasCurvatureLevels = (max(surfaces_.maxCurvatureLevel()) > 0);
1253 
1254 
1255  // Test for all intersections (with surfaces of higher max level than
1256  // minLevel) and cache per cell the interesting inter
1257  labelListList cellSurfLevels(mesh_.nCells());
1258  List<pointList> cellSurfLocations(mesh_.nCells());
1259  List<vectorList> cellSurfNormals(mesh_.nCells());
1260 
1261  {
1262  // Per segment the intersection point of the surfaces
1263  List<pointList> surfaceLocation;
1264  // Per segment the normals of the surfaces
1265  List<vectorList> surfaceNormal;
1266  // Per segment the list of levels of the surfaces
1267  labelListList surfaceLevel;
1268 
1269  surfaces_.findAllIntersections
1270  (
1271  start,
1272  end,
1273  minLevel, // max level of surface has to be bigger
1274  // than min level of neighbouring cells
1275 
1276  labelList(surfaces_.maxLevel().size(), 0), // min level
1277  surfaces_.maxLevel(),
1278 
1279  surfaceLocation,
1280  surfaceNormal,
1281  surfaceLevel
1282  );
1283 
1284 
1285  // Sort the data according to surface location. This will guarantee
1286  // that on coupled faces both sides visit the intersections in
1287  // the same order so will decide the same
1288  labelList visitOrder;
1289  forAll(surfaceNormal, pointI)
1290  {
1291  pointList& pLocations = surfaceLocation[pointI];
1292  vectorList& pNormals = surfaceNormal[pointI];
1293  labelList& pLevel = surfaceLevel[pointI];
1294 
1295  sortedOrder(pNormals, visitOrder, normalLess(pLocations));
1296 
1297  pLocations = List<point>(pLocations, visitOrder);
1298  pNormals = List<vector>(pNormals, visitOrder);
1299  pLevel = labelUIndList(pLevel, visitOrder);
1300  }
1301 
1302 
1303  //- At some point could just return the intersected surface+region
1304  // and derive all the surface information (maxLevel, curvatureLevel)
1305  // from that - we're now doing it inside refinementSurfaces itself.
1307  //List<labelList> hitSurface;
1308  //List<pointList> hitLocation;
1309  //List<labelList> hitRegion;
1310  //List<vectorField> hitNormal;
1311  //surfaces_.findAllIntersections
1312  //(
1313  // identity(surfaces_.surfaces()), // all refinement geometries
1314  // start,
1315  // end,
1316  //
1317  // hitSurface,
1318  // hitLocation,
1319  // hitRegion,
1320  // hitNormal
1321  //);
1322  //
1325  //const auto& maxLevel = surfaces_.maxLevel();
1326  //labelList visitOrder;
1327  //DynamicList<label> valid;
1328  //forAll(hitSurface, segmenti)
1329  //{
1330  // const label meshLevel = minLevel[segmenti];
1331  //
1332  // auto& fSurface = hitSurface[segmenti];
1333  // auto& fLocation = hitLocation[segmenti];
1334  // auto& fRegion = hitRegion[segmenti];
1335  // auto& fNormal = hitNormal[segmenti];
1336  //
1337  // // Sort the data according to intersection location. This will
1338  // // guarantee
1339  // // that on coupled faces both sides visit the intersections in
1340  // // the same order so will decide the same
1341  // sortedOrder(fLocation, visitOrder, normalLess(hfLocation));
1342  // fLocation = List<point>(fLocation, visitOrder);
1343  // fSurface = labelUIndList(fSurface, visitOrder);
1344  // fRegion = labelUIndList(fRegion, visitOrder);
1345  // fNormal = List<vector>(fNormal, visitOrder);
1346  //
1347  // // Filter out any intersections with surfaces outside cell level.
1348  // // Note that min refinement level of surfaces is ignored.
1349  // valid.clear();
1350  // forAll(fSurface, hiti)
1351  // {
1352  // const label regioni =
1353  // surfaces_.globalRegion(fSurface[hiti], fRegion[hiti]);
1354  // if (meshLevel < maxLevel[regioni]) //&& >= minLevel(regioni)
1355  // {
1356  // valid.append(hiti);
1357  // }
1358  // }
1359  // fLocation = List<point>(fLocation, valid);
1360  // fSurface = labelUIndList(fSurface, valid);
1361  // fRegion = labelUIndList(fRegion, valid);
1362  // fNormal = List<vector>(fNormal, valid);
1363  //}
1364 
1365  // Clear out unnecessary data
1366  start.clear();
1367  end.clear();
1368  minLevel.clear();
1369 
1370  // Convert face-wise data to cell.
1371  forAll(testFaces, i)
1372  {
1373  label faceI = testFaces[i];
1374  label own = mesh_.faceOwner()[faceI];
1375 
1376  const pointList& fPoints = surfaceLocation[i];
1377  const vectorList& fNormals = surfaceNormal[i];
1378  const labelList& fLevels = surfaceLevel[i];
1379 
1380  forAll(fNormals, hitI)
1381  {
1382  if (fLevels[hitI] > cellLevel[own])
1383  {
1384  cellSurfLevels[own].append(fLevels[hitI]);
1385  cellSurfLocations[own].append(fPoints[hitI]);
1386  cellSurfNormals[own].append(fNormals[hitI]);
1387  }
1388 
1389  if (mesh_.isInternalFace(faceI))
1390  {
1391  label nei = mesh_.faceNeighbour()[faceI];
1392  if (fLevels[hitI] > cellLevel[nei])
1393  {
1394  cellSurfLevels[nei].append(fLevels[hitI]);
1395  cellSurfLocations[nei].append(fPoints[hitI]);
1396  cellSurfNormals[nei].append(fNormals[hitI]);
1397  }
1398  }
1399  }
1400  }
1401  }
1402 
1403 
1404 
1405  // Bit of statistics
1406  if (debug)
1407  {
1408  label nSet = 0;
1409  label nNormals = 0;
1410  forAll(cellSurfNormals, cellI)
1411  {
1412  const vectorList& normals = cellSurfNormals[cellI];
1413  if (normals.size())
1414  {
1415  nSet++;
1416  nNormals += normals.size();
1417  }
1418  }
1419  reduce(nSet, sumOp<label>());
1420  reduce(nNormals, sumOp<label>());
1421  Info<< "markSurfaceCurvatureRefinement :"
1422  << " cells:" << mesh_.globalData().nTotalCells()
1423  << " of which with normals:" << nSet
1424  << " ; total normals stored:" << nNormals
1425  << endl;
1426  }
1427 
1428 
1429 
1430  bool reachedLimit = false;
1431 
1432 
1433  // 1. Check normals per cell
1434  // ~~~~~~~~~~~~~~~~~~~~~~~~~
1435 
1436  for
1437  (
1438  label cellI = 0;
1439  !reachedLimit && cellI < cellSurfLocations.size();
1440  cellI++
1441  )
1442  {
1443  const pointList& points = cellSurfLocations[cellI];
1444  const vectorList& normals = cellSurfNormals[cellI];
1445  const labelList& levels = cellSurfLevels[cellI];
1446 
1447  // Current cell size
1448  const scalar cellSize =
1449  meshCutter_.level0EdgeLength()/pow(2.0, cellLevel[cellI]);
1450 
1451  // n^2 comparison of all normals in a cell
1452  for (label i = 0; !reachedLimit && i < normals.size(); i++)
1453  {
1454  for (label j = i+1; !reachedLimit && j < normals.size(); j++)
1455  {
1456  // TBD: calculate curvature size (if curvatureLevel specified)
1457  // and pass in instead of cellSize
1458 
1459  //if ((normals[i] & normals[j]) < curvature)
1460  if
1461  (
1462  highCurvature
1463  (
1464  curvature,
1465  (hasCurvatureLevels ? cellSize : scalar(0)),
1466  points[i],
1467  normals[i],
1468  points[j],
1469  normals[j]
1470  )
1471  )
1472  {
1473  const label maxLevel = max(levels[i], levels[j]);
1474 
1475  if (cellLevel[cellI] < maxLevel)
1476  {
1477  if
1478  (
1479  !markForRefine
1480  (
1481  maxLevel,
1482  nAllowRefine,
1483  refineCell[cellI],
1484  nRefine
1485  )
1486  )
1487  {
1488  if (debug)
1489  {
1490  Pout<< "Stopped refining since reaching my cell"
1491  << " limit of " << mesh_.nCells()+7*nRefine
1492  << endl;
1493  }
1494  reachedLimit = true;
1495  break;
1496  }
1497  }
1498  }
1499  }
1500  }
1501  }
1502 
1503 
1504 
1505  // 2. Find out a measure of surface curvature
1506  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1507  // Look at normals between neighbouring surfaces
1508  // Loop over all faces. Could only be checkFaces, except if they're coupled
1509 
1510  // Internal faces
1511  for
1512  (
1513  label faceI = 0;
1514  !reachedLimit && faceI < mesh_.nInternalFaces();
1515  faceI++
1516  )
1517  {
1518  label own = mesh_.faceOwner()[faceI];
1519  label nei = mesh_.faceNeighbour()[faceI];
1520 
1521  const pointList& ownPoints = cellSurfLocations[own];
1522  const vectorList& ownNormals = cellSurfNormals[own];
1523  const labelList& ownLevels = cellSurfLevels[own];
1524  const pointList& neiPoints = cellSurfLocations[nei];
1525  const vectorList& neiNormals = cellSurfNormals[nei];
1526  const labelList& neiLevels = cellSurfLevels[nei];
1527 
1528 
1529  // Special case: owner normals set is a subset of the neighbour
1530  // normals. Do not do curvature refinement since other cell's normals
1531  // do not add any information. Avoids weird corner extensions of
1532  // refinement regions.
1533  bool ownIsSubset =
1534  countMatches(ownNormals, neiNormals)
1535  == ownNormals.size();
1536 
1537  bool neiIsSubset =
1538  countMatches(neiNormals, ownNormals)
1539  == neiNormals.size();
1540 
1541 
1542  if (!ownIsSubset && !neiIsSubset)
1543  {
1544  // Current average cell size. Is min? max? average?
1545  const scalar cellSize =
1546  meshCutter_.level0EdgeLength()
1547  / pow(2.0, min(cellLevel[own], cellLevel[nei]));
1548 
1549  // n^2 comparison of between ownNormals and neiNormals
1550  for (label i = 0; !reachedLimit && i < ownNormals.size(); i++)
1551  {
1552  for (label j = 0; !reachedLimit && j < neiNormals.size(); j++)
1553  {
1554  // Have valid data on both sides. Check curvature.
1555  //if ((ownNormals[i] & neiNormals[j]) < curvature)
1556  if
1557  (
1558  highCurvature
1559  (
1560  curvature,
1561  (hasCurvatureLevels ? cellSize : scalar(0)),
1562  ownPoints[i],
1563  ownNormals[i],
1564  neiPoints[j],
1565  neiNormals[j]
1566  )
1567  )
1568  {
1569  // See which side to refine.
1570  if (cellLevel[own] < ownLevels[i])
1571  {
1572  if
1573  (
1574  !markForRefine
1575  (
1576  ownLevels[i],
1577  nAllowRefine,
1578  refineCell[own],
1579  nRefine
1580  )
1581  )
1582  {
1583  if (debug)
1584  {
1585  Pout<< "Stopped refining since reaching"
1586  << " my cell limit of "
1587  << mesh_.nCells()+7*nRefine << endl;
1588  }
1589  reachedLimit = true;
1590  break;
1591  }
1592  }
1593  if (cellLevel[nei] < neiLevels[j])
1594  {
1595  if
1596  (
1597  !markForRefine
1598  (
1599  neiLevels[j],
1600  nAllowRefine,
1601  refineCell[nei],
1602  nRefine
1603  )
1604  )
1605  {
1606  if (debug)
1607  {
1608  Pout<< "Stopped refining since reaching"
1609  << " my cell limit of "
1610  << mesh_.nCells()+7*nRefine << endl;
1611  }
1612  reachedLimit = true;
1613  break;
1614  }
1615  }
1616  }
1617  }
1618  }
1619  }
1620  }
1621 
1622 
1623  // Send over surface point/normal to neighbour cell.
1624 // labelListList neiSurfaceLevel;
1625 // syncTools::swapBoundaryCellList(mesh_, cellSurfLevels, neiSurfaceLevel);
1626  List<vectorList> neiSurfacePoints;
1627  syncTools::swapBoundaryCellList(mesh_, cellSurfLocations, neiSurfacePoints);
1628  List<vectorList> neiSurfaceNormals;
1629  syncTools::swapBoundaryCellList(mesh_, cellSurfNormals, neiSurfaceNormals);
1630 
1631  // Boundary faces
1632  for
1633  (
1634  label faceI = mesh_.nInternalFaces();
1635  !reachedLimit && faceI < mesh_.nFaces();
1636  faceI++
1637  )
1638  {
1639  label own = mesh_.faceOwner()[faceI];
1640  label bFaceI = faceI - mesh_.nInternalFaces();
1641 
1642  const pointList& ownPoints = cellSurfLocations[own];
1643  const vectorList& ownNormals = cellSurfNormals[own];
1644  const labelList& ownLevels = cellSurfLevels[own];
1645 
1646  const pointList& neiPoints = neiSurfacePoints[bFaceI];
1647  const vectorList& neiNormals = neiSurfaceNormals[bFaceI];
1648  //const labelList& neiLevels = neiSurfaceLevel[bFacei];
1649 
1650  // Special case: owner normals set is a subset of the neighbour
1651  // normals. Do not do curvature refinement since other cell's normals
1652  // do not add any information. Avoids weird corner extensions of
1653  // refinement regions.
1654  bool ownIsSubset =
1655  countMatches(ownNormals, neiNormals)
1656  == ownNormals.size();
1657 
1658  bool neiIsSubset =
1659  countMatches(neiNormals, ownNormals)
1660  == neiNormals.size();
1661 
1662 
1663  if (!ownIsSubset && !neiIsSubset)
1664  {
1665  // Current average cell size. Is min? max? average?
1666  const scalar cellSize =
1667  meshCutter_.level0EdgeLength()
1668  / pow(2.0, min(cellLevel[own], neiLevel[bFaceI]));
1669 
1670  // n^2 comparison of between ownNormals and neiNormals
1671  for (label i = 0; !reachedLimit && i < ownNormals.size(); i++)
1672  {
1673  for (label j = 0; !reachedLimit && j < neiNormals.size(); j++)
1674  {
1675  // Have valid data on both sides. Check curvature.
1676  //if ((ownNormals[i] & neiNormals[j]) < curvature)
1677  if
1678  (
1679  highCurvature
1680  (
1681  curvature,
1682  (hasCurvatureLevels ? cellSize : scalar(0)),
1683  ownPoints[i],
1684  ownNormals[i],
1685  neiPoints[j],
1686  neiNormals[j]
1687  )
1688  )
1689  {
1690  if (cellLevel[own] < ownLevels[i])
1691  {
1692  if
1693  (
1694  !markForRefine
1695  (
1696  ownLevels[i],
1697  nAllowRefine,
1698  refineCell[own],
1699  nRefine
1700  )
1701  )
1702  {
1703  if (debug)
1704  {
1705  Pout<< "Stopped refining since reaching"
1706  << " my cell limit of "
1707  << mesh_.nCells()+7*nRefine
1708  << endl;
1709  }
1710  reachedLimit = true;
1711  break;
1712  }
1713  }
1714  }
1715  }
1716  }
1717  }
1718  }
1719 
1720 
1721  if
1722  (
1723  returnReduce(nRefine, sumOp<label>())
1724  > returnReduce(nAllowRefine, sumOp<label>())
1725  )
1726  {
1727  Info<< "Reached refinement limit." << endl;
1728  }
1729 
1730  return returnReduce(nRefine-oldNRefine, sumOp<label>());
1731 }
1732 
1733 
1735 (
1736  const scalar planarCos,
1737  const vector& point0,
1738  const vector& normal0,
1739 
1740  const vector& point1,
1741  const vector& normal1
1742 ) const
1743 {
1744  //- Hits differ and angles are oppositeish and
1745  // hits have a normal distance
1746  vector d = point1-point0;
1747  scalar magD = mag(d);
1748 
1749  if (magD > mergeDistance())
1750  {
1751  scalar cosAngle = (normal0 & normal1);
1752 
1753  vector avg = Zero;
1754  if (cosAngle < (-1+planarCos))
1755  {
1756  // Opposite normals
1757  avg = 0.5*(normal0-normal1);
1758  }
1759  else if (cosAngle > (1-planarCos))
1760  {
1761  avg = 0.5*(normal0+normal1);
1762  }
1763 
1764  if (avg != vector::zero)
1765  {
1766  avg /= mag(avg);
1767 
1768  // Check normal distance of intersection locations
1769  if (mag(avg&d) > mergeDistance())
1770  {
1771  return true;
1772  }
1773  }
1774  }
1775 
1776  return false;
1777 }
1778 
1779 
1780 // Mark small gaps
1782 (
1783  const scalar planarCos,
1784  const label level0,
1785  const vector& point0,
1786  const vector& normal0,
1787 
1788  const label level1,
1789  const vector& point1,
1790  const vector& normal1
1791 ) const
1792 {
1793  //const scalar edge0Len = meshCutter_.level0EdgeLength();
1794 
1795  //- Hits differ and angles are oppositeish and
1796  // hits have a normal distance
1797  vector d = point1-point0;
1798  scalar magD = mag(d);
1799 
1800  if (magD > mergeDistance())
1801  {
1802  scalar cosAngle = (normal0 & normal1);
1803 
1804  vector avgNormal = Zero;
1805  if (cosAngle < (-1+planarCos))
1806  {
1807  // Opposite normals
1808  avgNormal = 0.5*(normal0-normal1);
1809  }
1810  else if (cosAngle > (1-planarCos))
1811  {
1812  avgNormal = 0.5*(normal0+normal1);
1813  }
1814 
1815  if (avgNormal != vector::zero)
1816  {
1817  avgNormal /= mag(avgNormal);
1818 
1819  //scalar normalDist = mag(d&avgNormal);
1820  //const scalar avgCellSize = edge0Len/pow(2.0, 0.5*(level0+level1));
1821  //if (normalDist > 1*avgCellSize)
1822  //{
1823  // Pout<< "** skipping large distance " << endl;
1824  // return false;
1825  //}
1826 
1827  // Check average normal with respect to intersection locations
1828  if (mag(avgNormal&d/magD) > Foam::cos(degToRad(45.0)))
1829  {
1830  return true;
1831  }
1832  }
1833  }
1834 
1835  return false;
1836 }
1837 
1838 
1839 bool Foam::meshRefinement::checkProximity
1840 (
1841  const scalar planarCos,
1842  const label nAllowRefine,
1843 
1844  const label surfaceLevel, // current intersection max level
1845  const vector& surfaceLocation, // current intersection location
1846  const vector& surfaceNormal, // current intersection normal
1847 
1848  const label cellI,
1849 
1850  label& cellMaxLevel, // cached max surface level for this cell
1851  vector& cellMaxLocation, // cached surface location for this cell
1852  vector& cellMaxNormal, // cached surface normal for this cell
1853 
1854  labelList& refineCell,
1855  label& nRefine
1856 ) const
1857 {
1858  const labelList& cellLevel = meshCutter_.cellLevel();
1859 
1860  // Test if surface applicable
1861  if (surfaceLevel > cellLevel[cellI])
1862  {
1863  if (cellMaxLevel == -1)
1864  {
1865  // First visit of cell. Store
1866  cellMaxLevel = surfaceLevel;
1867  cellMaxLocation = surfaceLocation;
1868  cellMaxNormal = surfaceNormal;
1869  }
1870  else
1871  {
1872  // Second or more visit.
1873  // Check if
1874  // - different location
1875  // - opposite surface
1876 
1877  bool closeSurfaces = isNormalGap
1878  (
1879  planarCos,
1880  cellLevel[cellI], //cellMaxLevel,
1881  cellMaxLocation,
1882  cellMaxNormal,
1883  cellLevel[cellI], //surfaceLevel,
1884  surfaceLocation,
1885  surfaceNormal
1886  );
1887 
1888  // Set normal to that of highest surface. Not really necessary
1889  // over here but we reuse cellMax info when doing coupled faces.
1890  if (surfaceLevel > cellMaxLevel)
1891  {
1892  cellMaxLevel = surfaceLevel;
1893  cellMaxLocation = surfaceLocation;
1894  cellMaxNormal = surfaceNormal;
1895  }
1896 
1897 
1898  if (closeSurfaces)
1899  {
1900  //Pout<< "Found gap:" << nl
1901  // << " location:" << surfaceLocation
1902  // << "\tnormal :" << surfaceNormal << nl
1904  // << "\tnormal :" << cellMaxNormal << nl
1905  // << "\tcos :" << (surfaceNormal&cellMaxNormal) << nl
1906  // << endl;
1907 
1908  return markForRefine
1909  (
1910  surfaceLevel, // mark with any non-neg number.
1911  nAllowRefine,
1912  refineCell[cellI],
1913  nRefine
1914  );
1915  }
1916  }
1917  }
1918 
1919  // Did not reach refinement limit.
1920  return true;
1921 }
1922 
1923 
1924 Foam::label Foam::meshRefinement::markProximityRefinement
1925 (
1926  const scalar planarCos,
1927 
1928  const labelList& surfaceMinLevel,
1929  const labelList& surfaceMaxLevel,
1930 
1931  const label nAllowRefine,
1932  const labelList& neiLevel,
1933  const pointField& neiCc,
1934 
1935  labelList& refineCell,
1936  label& nRefine
1937 ) const
1938 {
1939  const labelList& cellLevel = meshCutter_.cellLevel();
1940 
1941  label oldNRefine = nRefine;
1942 
1943  // 1. local test: any cell on more than one surface gets refined
1944  // (if its current level is < max of the surface max level)
1945 
1946  // 2. 'global' test: any cell on only one surface with a neighbour
1947  // on a different surface gets refined (if its current level etc.)
1948 
1949 
1950  // Collect candidate faces (i.e. intersecting any surface and
1951  // owner/neighbour not yet refined.
1952  const labelList testFaces(getRefineCandidateFaces(refineCell));
1953 
1954  // Collect segments
1955  pointField start(testFaces.size());
1956  pointField end(testFaces.size());
1957  labelList minLevel(testFaces.size());
1958 
1959  calcCellCellRays
1960  (
1961  neiCc,
1962  neiLevel,
1963  testFaces,
1964  start,
1965  end,
1966  minLevel
1967  );
1968 
1969 
1970  // Test for all intersections (with surfaces of higher gap level than
1971  // minLevel) and cache per cell the max surface level and the local normal
1972  // on that surface.
1973  labelList cellMaxLevel(mesh_.nCells(), -1);
1974  vectorField cellMaxNormal(mesh_.nCells(), Zero);
1975  pointField cellMaxLocation(mesh_.nCells(), Zero);
1976 
1977  {
1978  // Per segment the normals of the surfaces
1979  List<vectorList> surfaceLocation;
1980  List<vectorList> surfaceNormal;
1981  // Per segment the list of levels of the surfaces
1982  labelListList surfaceLevel;
1983 
1984  surfaces_.findAllIntersections
1985  (
1986  start,
1987  end,
1988  minLevel, // gap level of surface has to be bigger
1989  // than min level of neighbouring cells
1990 
1991  surfaceMinLevel,
1992  surfaceMaxLevel,
1993 
1994  surfaceLocation,
1995  surfaceNormal,
1996  surfaceLevel
1997  );
1998  // Clear out unnecessary data
1999  start.clear();
2000  end.clear();
2001  minLevel.clear();
2002 
2003 
2005 
2006  forAll(testFaces, i)
2007  {
2008  label faceI = testFaces[i];
2009  label own = mesh_.faceOwner()[faceI];
2010 
2011  const labelList& fLevels = surfaceLevel[i];
2012  const vectorList& fPoints = surfaceLocation[i];
2013  const vectorList& fNormals = surfaceNormal[i];
2014 
2015  forAll(fLevels, hitI)
2016  {
2017  checkProximity
2018  (
2019  planarCos,
2020  nAllowRefine,
2021 
2022  fLevels[hitI],
2023  fPoints[hitI],
2024  fNormals[hitI],
2025 
2026  own,
2027  cellMaxLevel[own],
2028  cellMaxLocation[own],
2029  cellMaxNormal[own],
2030 
2031  refineCell,
2032  nRefine
2033  );
2034  }
2035 
2036  if (mesh_.isInternalFace(faceI))
2037  {
2038  label nei = mesh_.faceNeighbour()[faceI];
2039 
2040  forAll(fLevels, hitI)
2041  {
2042  checkProximity
2043  (
2044  planarCos,
2045  nAllowRefine,
2046 
2047  fLevels[hitI],
2048  fPoints[hitI],
2049  fNormals[hitI],
2050 
2051  nei,
2052  cellMaxLevel[nei],
2053  cellMaxLocation[nei],
2054  cellMaxNormal[nei],
2055 
2056  refineCell,
2057  nRefine
2058  );
2059  }
2060  }
2061  }
2062  }
2063 
2064  // 2. Find out a measure of surface curvature and region edges.
2065  // Send over surface region and surface normal to neighbour cell.
2066 
2067  labelList neiBndMaxLevel(mesh_.nBoundaryFaces());
2068  pointField neiBndMaxLocation(mesh_.nBoundaryFaces());
2069  vectorField neiBndMaxNormal(mesh_.nBoundaryFaces());
2070 
2071  for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
2072  {
2073  label bFaceI = faceI-mesh_.nInternalFaces();
2074  label own = mesh_.faceOwner()[faceI];
2075 
2076  neiBndMaxLevel[bFaceI] = cellMaxLevel[own];
2077  neiBndMaxLocation[bFaceI] = cellMaxLocation[own];
2078  neiBndMaxNormal[bFaceI] = cellMaxNormal[own];
2079  }
2080  syncTools::swapBoundaryFaceList(mesh_, neiBndMaxLevel);
2081  syncTools::swapBoundaryFaceList(mesh_, neiBndMaxLocation);
2082  syncTools::swapBoundaryFaceList(mesh_, neiBndMaxNormal);
2083 
2084  // Loop over all faces. Could only be checkFaces.. except if they're coupled
2085 
2086  // Internal faces
2087  for (label faceI = 0; faceI < mesh_.nInternalFaces(); faceI++)
2088  {
2089  label own = mesh_.faceOwner()[faceI];
2090  label nei = mesh_.faceNeighbour()[faceI];
2091 
2092  if (cellMaxLevel[own] != -1 && cellMaxLevel[nei] != -1)
2093  {
2094  // Have valid data on both sides. Check planarCos.
2095  if
2096  (
2097  isNormalGap
2098  (
2099  planarCos,
2100  cellLevel[own], //cellMaxLevel[own],
2101  cellMaxLocation[own],
2102  cellMaxNormal[own],
2103  cellLevel[nei], //cellMaxLevel[nei],
2104  cellMaxLocation[nei],
2105  cellMaxNormal[nei]
2106  )
2107  )
2108  {
2109  // See which side to refine
2110  if (cellLevel[own] < cellMaxLevel[own])
2111  {
2112  if
2113  (
2114  !markForRefine
2115  (
2116  cellMaxLevel[own],
2117  nAllowRefine,
2118  refineCell[own],
2119  nRefine
2120  )
2121  )
2122  {
2123  if (debug)
2124  {
2125  Pout<< "Stopped refining since reaching my cell"
2126  << " limit of " << mesh_.nCells()+7*nRefine
2127  << endl;
2128  }
2129  break;
2130  }
2131  }
2132 
2133  if (cellLevel[nei] < cellMaxLevel[nei])
2134  {
2135  if
2136  (
2137  !markForRefine
2138  (
2139  cellMaxLevel[nei],
2140  nAllowRefine,
2141  refineCell[nei],
2142  nRefine
2143  )
2144  )
2145  {
2146  if (debug)
2147  {
2148  Pout<< "Stopped refining since reaching my cell"
2149  << " limit of " << mesh_.nCells()+7*nRefine
2150  << endl;
2151  }
2152  break;
2153  }
2154  }
2155  }
2156  }
2157  }
2158  // Boundary faces
2159  for (label faceI = mesh_.nInternalFaces(); faceI < mesh_.nFaces(); faceI++)
2160  {
2161  label own = mesh_.faceOwner()[faceI];
2162  label bFaceI = faceI - mesh_.nInternalFaces();
2163 
2164  if (cellLevel[own] < cellMaxLevel[own] && neiBndMaxLevel[bFaceI] != -1)
2165  {
2166  // Have valid data on both sides. Check planarCos.
2167  if
2168  (
2169  isNormalGap
2170  (
2171  planarCos,
2172  cellLevel[own], //cellMaxLevel[own],
2173  cellMaxLocation[own],
2174  cellMaxNormal[own],
2175  neiLevel[bFaceI], //neiBndMaxLevel[bFaceI],
2176  neiBndMaxLocation[bFaceI],
2177  neiBndMaxNormal[bFaceI]
2178  )
2179  )
2180  {
2181  if
2182  (
2183  !markForRefine
2184  (
2185  cellMaxLevel[own],
2186  nAllowRefine,
2187  refineCell[own],
2188  nRefine
2189  )
2190  )
2191  {
2192  if (debug)
2193  {
2194  Pout<< "Stopped refining since reaching my cell"
2195  << " limit of " << mesh_.nCells()+7*nRefine
2196  << endl;
2197  }
2198  break;
2199  }
2200  }
2201  }
2202  }
2203 
2204  if
2205  (
2206  returnReduce(nRefine, sumOp<label>())
2207  > returnReduce(nAllowRefine, sumOp<label>())
2208  )
2209  {
2210  Info<< "Reached refinement limit." << endl;
2211  }
2212 
2213  return returnReduce(nRefine-oldNRefine, sumOp<label>());
2214 }
2215 
2216 
2217 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2218 
2219 // Calculate list of cells to refine. Gets for any edge (start - end)
2220 // whether it intersects the surface. Does more accurate test and checks
2221 // the wanted level on the surface intersected.
2222 // Does approximate precalculation of how many cells can be refined before
2223 // hitting overall limit maxGlobalCells.
2225 (
2226  const pointField& keepPoints,
2227  const scalar curvature,
2228  const scalar planarAngle,
2229 
2230  const bool featureRefinement,
2231  const bool featureDistanceRefinement,
2232  const bool internalRefinement,
2233  const bool surfaceRefinement,
2234  const bool curvatureRefinement,
2235  const bool smallFeatureRefinement,
2236  const bool gapRefinement,
2237  const bool bigGapRefinement,
2238  const bool spreadGapSize,
2239  const label maxGlobalCells,
2240  const label maxLocalCells
2241 ) const
2242 {
2243  label totNCells = mesh_.globalData().nTotalCells();
2244 
2245  labelList cellsToRefine;
2246 
2247  if (totNCells >= maxGlobalCells)
2248  {
2249  Info<< "No cells marked for refinement since reached limit "
2250  << maxGlobalCells << '.' << endl;
2251  }
2252  else
2253  {
2254  // Every cell I refine adds 7 cells. Estimate the number of cells
2255  // I am allowed to refine.
2256  // Assume perfect distribution so can only refine as much the fraction
2257  // of the mesh I hold. This prediction step prevents us having to do
2258  // lots of reduces to keep count of the total number of cells selected
2259  // for refinement.
2260 
2261  //scalar fraction = scalar(mesh_.nCells())/totNCells;
2262  //label myMaxCells = label(maxGlobalCells*fraction);
2263  //label nAllowRefine = (myMaxCells - mesh_.nCells())/7;
2265  //
2266  //Pout<< "refineCandidates:" << nl
2267  // << " total cells:" << totNCells << nl
2268  // << " local cells:" << mesh_.nCells() << nl
2269  // << " local fraction:" << fraction << nl
2270  // << " local allowable cells:" << myMaxCells << nl
2271  // << " local allowable refinement:" << nAllowRefine << nl
2272  // << endl;
2273 
2274  //- Disable refinement shortcut. nAllowRefine is per processor limit.
2275  label nAllowRefine = labelMax / Pstream::nProcs();
2276 
2277  // Marked for refinement (>= 0) or not (-1). Actual value is the
2278  // index of the surface it intersects / shell it is inside.
2279  labelList refineCell(mesh_.nCells(), -1);
2280  label nRefine = 0;
2281 
2282 
2283  // Swap neighbouring cell centres and cell level
2284  labelList neiLevel(mesh_.nBoundaryFaces());
2285  pointField neiCc(mesh_.nBoundaryFaces());
2286  calcNeighbourData(neiLevel, neiCc);
2287 
2288 
2289  const scalar planarCos = Foam::cos(degToRad(planarAngle));
2290 
2291 
2292  // Cells pierced by feature lines
2293  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2294 
2295  if (featureRefinement)
2296  {
2297  label nFeatures = markFeatureRefinement
2298  (
2299  keepPoints,
2300  nAllowRefine,
2301 
2302  refineCell,
2303  nRefine
2304  );
2305 
2306  Info<< "Marked for refinement due to explicit features "
2307  << ": " << nFeatures << " cells." << endl;
2308  }
2309 
2310  // Inside distance-to-feature shells
2311  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2312 
2313  if (featureDistanceRefinement)
2314  {
2315  label nShell = markInternalDistanceToFeatureRefinement
2316  (
2317  nAllowRefine,
2318 
2319  refineCell,
2320  nRefine
2321  );
2322  Info<< "Marked for refinement due to distance to explicit features "
2323  ": " << nShell << " cells." << endl;
2324  }
2325 
2326  // Inside refinement shells
2327  // ~~~~~~~~~~~~~~~~~~~~~~~~
2328 
2329  if (internalRefinement)
2330  {
2331  label nShell = markInternalRefinement
2332  (
2333  nAllowRefine,
2334 
2335  refineCell,
2336  nRefine
2337  );
2338  Info<< "Marked for refinement due to refinement shells "
2339  << ": " << nShell << " cells." << endl;
2340  }
2341 
2342  // Refinement based on intersection of surface
2343  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2344 
2345  if (surfaceRefinement)
2346  {
2347  label nSurf = markSurfaceRefinement
2348  (
2349  nAllowRefine,
2350  neiLevel,
2351  neiCc,
2352 
2353  refineCell,
2354  nRefine
2355  );
2356  Info<< "Marked for refinement due to surface intersection "
2357  << ": " << nSurf << " cells." << endl;
2358 
2359  // Refine intersected-cells only inside gaps. See
2360  // markInternalGapRefinement to refine all cells inside gaps.
2361  if
2362  (
2363  planarCos >= -1
2364  && planarCos <= 1
2365  && max(shells_.maxGapLevel()) > 0
2366  )
2367  {
2368  label nGapSurf = markSurfaceGapRefinement
2369  (
2370  planarCos,
2371  nAllowRefine,
2372  neiLevel,
2373  neiCc,
2374 
2375  refineCell,
2376  nRefine
2377  );
2378  Info<< "Marked for refinement due to surface intersection"
2379  << " (at gaps)"
2380  << ": " << nGapSurf << " cells." << endl;
2381  }
2382  }
2383 
2384  // Refinement based on curvature of surface
2385  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2386 
2387  if
2388  (
2389  curvatureRefinement
2390  && (curvature >= -1 && curvature <= 1)
2391  && (surfaces_.minLevel() != surfaces_.maxLevel())
2392  )
2393  {
2394  label nCurv = markSurfaceCurvatureRefinement
2395  (
2396  curvature,
2397  nAllowRefine,
2398  neiLevel,
2399  neiCc,
2400 
2401  refineCell,
2402  nRefine
2403  );
2404  Info<< "Marked for refinement due to curvature/regions "
2405  << ": " << nCurv << " cells." << endl;
2406  }
2407 
2408 
2409  // Refinement based on features smaller than cell size
2410  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2411 
2412  if (smallFeatureRefinement)
2413  {
2414  label nGap = 0;
2415  if
2416  (
2417  planarCos >= -1
2418  && planarCos <= 1
2419  && max(shells_.maxGapLevel()) > 0
2420  )
2421  {
2422  nGap = markSmallFeatureRefinement
2423  (
2424  planarCos,
2425  nAllowRefine,
2426  neiLevel,
2427  neiCc,
2428 
2429  refineCell,
2430  nRefine
2431  );
2432  }
2433  Info<< "Marked for refinement due to close opposite surfaces "
2434  << ": " << nGap << " cells." << endl;
2435 
2436  label nCurv = 0;
2437  if (max(surfaces_.maxCurvatureLevel()) > 0)
2438  {
2439  nCurv = markSurfaceFieldRefinement
2440  (
2441  nAllowRefine,
2442  neiLevel,
2443  neiCc,
2444 
2445  refineCell,
2446  nRefine
2447  );
2448  Info<< "Marked for refinement"
2449  << " due to curvature "
2450  << ": " << nCurv << " cells." << endl;
2451  }
2452  }
2453 
2454 
2455  // Refinement based on gap (only neighbouring cells)
2456  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2457 
2458  const labelList& surfaceGapLevel = surfaces_.gapLevel();
2459 
2460  if
2461  (
2462  gapRefinement
2463  && (planarCos >= -1 && planarCos <= 1)
2464  && (max(surfaceGapLevel) > -1)
2465  )
2466  {
2467  Info<< "Specified gap level : " << max(surfaceGapLevel)
2468  << ", planar angle " << planarAngle << endl;
2469 
2470  label nGap = markProximityRefinement
2471  (
2472  planarCos,
2473 
2474  labelList(surfaceGapLevel.size(), -1), // surface min level
2475  surfaceGapLevel, // surface max level
2476 
2477  nAllowRefine,
2478  neiLevel,
2479  neiCc,
2480 
2481  refineCell,
2482  nRefine
2483  );
2484  Info<< "Marked for refinement due to close opposite surfaces "
2485  << ": " << nGap << " cells." << endl;
2486  }
2487 
2488 
2489  // Refinement based on gaps larger than cell size
2490  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2491 
2492  if
2493  (
2494  bigGapRefinement
2495  && (planarCos >= -1 && planarCos <= 1)
2496  && max(shells_.maxGapLevel()) > 0
2497  )
2498  {
2499  // Refine based on gap information provided by shell and nearest
2500  // surface
2501  labelList numGapCells;
2502  scalarField gapSize;
2503  label nGap = markInternalGapRefinement
2504  (
2505  planarCos,
2506  spreadGapSize,
2507  nAllowRefine,
2508 
2509  refineCell,
2510  nRefine,
2511  numGapCells,
2512  gapSize
2513  );
2514  Info<< "Marked for refinement due to opposite surfaces"
2515  << " "
2516  << ": " << nGap << " cells." << endl;
2517  }
2518 
2519 
2520  // Limit refinement
2521  // ~~~~~~~~~~~~~~~~
2522 
2523  if (limitShells_.shells().size())
2524  {
2525  label nUnmarked = unmarkInternalRefinement(refineCell, nRefine);
2526  if (nUnmarked > 0)
2527  {
2528  Info<< "Unmarked for refinement due to limit shells"
2529  << " : " << nUnmarked << " cells." << endl;
2530  }
2531  }
2532 
2533 
2534 
2535  // Pack cells-to-refine
2536  // ~~~~~~~~~~~~~~~~~~~~
2537 
2538  cellsToRefine.setSize(nRefine);
2539  nRefine = 0;
2540 
2541  forAll(refineCell, cellI)
2542  {
2543  if (refineCell[cellI] != -1)
2544  {
2545  cellsToRefine[nRefine++] = cellI;
2546  }
2547  }
2548  }
2549 
2550  return cellsToRefine;
2551 }
2552 
2553 
2555 (
2556  const labelList& cellsToRefine
2557 )
2558 {
2559  // Mesh changing engine.
2560  polyTopoChange meshMod(mesh_);
2561 
2562  // Play refinement commands into mesh changer.
2563  meshCutter_.setRefinement(cellsToRefine, meshMod);
2564 
2565  // Remove any unnecessary fields
2566  mesh_.clearOut();
2567  mesh_.moving(false);
2568 
2569  // Create mesh (no inflation), return map from old to new mesh.
2570  autoPtr<mapPolyMesh> mapPtr = meshMod.changeMesh(mesh_, false);
2571  mapPolyMesh& map = *mapPtr;
2572 
2573  // Update fields
2574  mesh_.updateMesh(map);
2575 
2576  // Optionally inflate mesh
2577  if (map.hasMotionPoints())
2578  {
2579  mesh_.movePoints(map.preMotionPoints());
2580  }
2581  else
2582  {
2583  // Delete mesh volumes.
2584  mesh_.clearOut();
2585  }
2586 
2587  // Reset the instance for if in overwrite mode
2588  mesh_.setInstance(timeName());
2589 
2590  // Update intersection info
2591  updateMesh(map, getChangedFaces(map, cellsToRefine));
2592 
2593  return mapPtr;
2594 }
2595 
2596 
2597 // Load balancing
2599 (
2600  const string& msg,
2601  decompositionMethod& decomposer,
2602  fvMeshDistribute& distributor,
2603  labelList& cellsToRefine,
2604  const scalar maxLoadUnbalance,
2605  const label maxCellUnbalance
2606 )
2607 {
2608  autoPtr<mapDistributePolyMesh> distMap;
2609 
2610  if (Pstream::nProcs() > 1)
2611  {
2612  // First check if we need to balance at all. Precalculate number of
2613  // cells after refinement and see what maximum difference is.
2614  const scalar nNewCells =
2615  scalar(mesh_.nCells() + 7*cellsToRefine.size());
2616  const scalar nNewCellsAll = returnReduce(nNewCells, sumOp<scalar>());
2617  const scalar nIdealNewCells = nNewCellsAll / Pstream::nProcs();
2618  const scalar unbalance = returnReduce
2619  (
2620  mag(1.0-nNewCells/nIdealNewCells),
2621  maxOp<scalar>()
2622  );
2623 
2624  // Trigger the balancing to avoid too early balancing for better
2625  // scaling performance.
2626  const scalar nNewCellsOnly = scalar(7*cellsToRefine.size());
2627 
2628  const label maxNewCells =
2629  label(returnReduce(nNewCellsOnly, maxOp<scalar>()));
2630 
2631  const label maxDeltaCells =
2632  label(mag(returnReduce(nNewCells, maxOp<scalar>())-nIdealNewCells));
2633 
2634  // New trigger to avoid too early balancing
2635  // 1. Check if globally one proc exceeds the maxCellUnbalance value
2636  // related to the new added cells at the refinement loop
2637  // 2. Check if globally one proc exceeds the maxCellUnbalance based on
2638  // the average cell count a proc should have
2639  if
2640  (
2641  (maxNewCells <= maxCellUnbalance)
2642  && (maxDeltaCells <= maxCellUnbalance)
2643  )
2644  {
2645  Info<< "Skipping balancing since trigger value not reached:" << "\n"
2646  << " Trigger cell count: " << maxCellUnbalance << nl
2647  << " Max new cell count in proc: " << maxNewCells << nl
2648  << " Max difference between new cells and balanced: "
2649  << maxDeltaCells << nl
2650  << " Max load unbalance " << maxLoadUnbalance
2651  << nl <<endl;
2652  }
2653  else
2654  {
2655  if (unbalance <= maxLoadUnbalance)
2656  {
2657  Info<< "Skipping balancing since max unbalance " << unbalance
2658  << " is less than allowable " << maxLoadUnbalance
2659  << endl;
2660  }
2661  else
2662  {
2663  Info<< "Balancing since max unbalance " << unbalance
2664  << " is larger than allowable " << maxLoadUnbalance
2665  << endl;
2666 
2667  scalarField cellWeights(mesh_.nCells(), 1);
2668  forAll(cellsToRefine, i)
2669  {
2670  cellWeights[cellsToRefine[i]] += 7;
2671  }
2672 
2673  distMap = balance
2674  (
2675  false, //keepZoneFaces
2676  false, //keepBaffles
2677  cellWeights,
2678  decomposer,
2679  distributor
2680  );
2681 
2682  // Update cells to refine
2683  distMap().distributeCellIndices(cellsToRefine);
2684 
2685  Info<< "Balanced mesh in = "
2686  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2687 
2688  printMeshInfo(debug, "After balancing " + msg, true);
2689 
2690 
2692  {
2693  Pout<< "Writing balanced " << msg
2694  << " mesh to time " << timeName() << endl;
2695  write
2696  (
2697  debugType(debug),
2698  writeType(writeLevel() | WRITEMESH),
2699  mesh_.time().path()/timeName()
2700  );
2701  Pout<< "Dumped debug data in = "
2702  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2703 
2704  // test all is still synced across proc patches
2705  checkData();
2706  }
2707  }
2708  }
2709  }
2710 
2711  return distMap;
2712 }
2713 
2714 
2715 // Do refinement of consistent set of cells followed by truncation and
2716 // load balancing.
2719 (
2720  const string& msg,
2721  decompositionMethod& decomposer,
2722  fvMeshDistribute& distributor,
2723  const labelList& cellsToRefine,
2724  const scalar maxLoadUnbalance,
2725  const label maxCellUnbalance
2726 )
2727 {
2728  // Refinement
2729  // ~~~~~~~~~~
2730 
2731  refine(cellsToRefine);
2732 
2734  {
2735  Pout<< "Writing refined but unbalanced " << msg
2736  << " mesh to time " << timeName() << endl;
2738  (
2739  debugType(debug),
2740  writeType(writeLevel() | WRITEMESH),
2741  mesh_.time().path()/timeName()
2742  );
2743  Pout<< "Dumped debug data in = "
2744  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2745 
2746  // test all is still synced across proc patches
2747  checkData();
2748  }
2749 
2750  Info<< "Refined mesh in = "
2751  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2752  printMeshInfo(debug, "After refinement " + msg, true);
2753 
2754 
2755  // Load balancing
2756  // ~~~~~~~~~~~~~~
2757 
2758  labelList noCellsToRefine;
2759 
2760  auto distMap = balance
2761  (
2762  msg,
2763  decomposer,
2764  distributor,
2765  noCellsToRefine, // mesh is already refined; no need to predict
2766  maxLoadUnbalance,
2767  maxCellUnbalance
2768  );
2769 
2770  return distMap;
2771 }
2772 
2773 
2774 // Do load balancing followed by refinement of consistent set of cells.
2777 (
2778  const string& msg,
2779  decompositionMethod& decomposer,
2780  fvMeshDistribute& distributor,
2781  const labelList& initCellsToRefine,
2782  const scalar maxLoadUnbalance,
2783  const label maxCellUnbalance
2784 )
2785 {
2786  labelList cellsToRefine(initCellsToRefine);
2787 
2788  //{
2789  // globalIndex globalCells(mesh_.nCells());
2790  //
2791  // Info<< "** Distribution before balancing/refining:" << endl;
2792  // for (const int procI : Pstream::allProcs())
2793  // {
2794  // Info<< " " << procI << '\t'
2795  // << globalCells.localSize(procI) << endl;
2796  // }
2797  // Info<< endl;
2798  //}
2799  //{
2800  // globalIndex globalCells(cellsToRefine.size());
2801  //
2802  // Info<< "** Cells to be refined:" << endl;
2803  // for (const int procI : Pstream::allProcs())
2804  // {
2805  // Info<< " " << procI << '\t'
2806  // << globalCells.localSize(procI) << endl;
2807  // }
2808  // Info<< endl;
2809  //}
2810 
2811 
2812 
2813  // Load balancing
2814  // ~~~~~~~~~~~~~~
2815 
2816  auto distMap = balance
2817  (
2818  msg,
2819  decomposer,
2820  distributor,
2821  cellsToRefine,
2822  maxLoadUnbalance,
2823  maxCellUnbalance
2824  );
2825 
2826 
2827  // Refinement
2828  // ~~~~~~~~~~
2829  // Note: uses updated cellsToRefine
2830 
2831  refine(cellsToRefine);
2832 
2834  {
2835  Pout<< "Writing refined " << msg
2836  << " mesh to time " << timeName() << endl;
2837  write
2838  (
2839  debugType(debug),
2840  writeType(writeLevel() | WRITEMESH),
2841  mesh_.time().path()/timeName()
2842  );
2843  Pout<< "Dumped debug data in = "
2844  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2845 
2846  // test all is still synced across proc patches
2847  checkData();
2848  }
2849 
2850  Info<< "Refined mesh in = "
2851  << mesh_.time().cpuTimeIncrement() << " s" << endl;
2852 
2853  //{
2854  // globalIndex globalCells(mesh_.nCells());
2855  //
2856  // Info<< "** After refinement distribution:" << endl;
2857  // for (const int procI : Pstream::allProcs())
2858  // {
2859  // Info<< " " << procI << '\t'
2860  // << globalCells.localSize(procI) << endl;
2861  // }
2862  // Info<< endl;
2863  //}
2864 
2865  printMeshInfo(debug, "After refinement " + msg, true);
2866 
2867  return distMap;
2868 }
2869 
2870 
2872 (
2873  const label maxGlobalCells,
2874  const label maxLocalCells,
2875  const labelList& currentLevel,
2876  const direction dir
2877 ) const
2878 {
2879  const labelList& cellLevel = meshCutter_.cellLevel();
2880  const pointField& cellCentres = mesh_.cellCentres();
2881 
2882  label totNCells = mesh_.globalData().nTotalCells();
2883 
2884  labelList cellsToRefine;
2885 
2886  if (totNCells >= maxGlobalCells)
2887  {
2888  Info<< "No cells marked for refinement since reached limit "
2889  << maxGlobalCells << '.' << endl;
2890  }
2891  else
2892  {
2893  // Disable refinement shortcut. nAllowRefine is per processor limit.
2894  label nAllowRefine = labelMax / Pstream::nProcs();
2895 
2896  // Marked for refinement (>= 0) or not (-1). Actual value is the
2897  // index of the surface it intersects / shell it is inside
2898  labelList refineCell(mesh_.nCells(), -1);
2899  label nRefine = 0;
2900 
2901  // Find cells inside the shells with directional levels
2902  labelList insideShell;
2903  shells_.findDirectionalLevel
2904  (
2905  cellCentres,
2906  cellLevel,
2907  currentLevel, // current directional level
2908  dir,
2909  insideShell
2910  );
2911 
2912  // Mark for refinement
2913  forAll(insideShell, celli)
2914  {
2915  if (insideShell[celli] >= 0)
2916  {
2917  bool reachedLimit = !markForRefine
2918  (
2919  insideShell[celli], // mark with any positive value
2920  nAllowRefine,
2921  refineCell[celli],
2922  nRefine
2923  );
2924 
2925  if (reachedLimit)
2926  {
2927  if (debug)
2928  {
2929  Pout<< "Stopped refining cells"
2930  << " since reaching my cell limit of "
2931  << mesh_.nCells()+nAllowRefine << endl;
2932  }
2933  break;
2934  }
2935  }
2936  }
2937 
2938  // Limit refinement
2939  // ~~~~~~~~~~~~~~~~
2940 
2941  {
2942  label nUnmarked = unmarkInternalRefinement(refineCell, nRefine);
2943  if (nUnmarked > 0)
2944  {
2945  Info<< "Unmarked for refinement due to limit shells"
2946  << " : " << nUnmarked << " cells." << endl;
2947  }
2948  }
2949 
2950 
2951 
2952  // Pack cells-to-refine
2953  // ~~~~~~~~~~~~~~~~~~~~
2954 
2955  cellsToRefine.setSize(nRefine);
2956  nRefine = 0;
2957 
2958  forAll(refineCell, cellI)
2959  {
2960  if (refineCell[cellI] != -1)
2961  {
2962  cellsToRefine[nRefine++] = cellI;
2963  }
2964  }
2965  }
2966 
2967  return cellsToRefine;
2968 }
2969 
2970 
2972 (
2973  const string& msg,
2974  const direction cmpt,
2975  const labelList& cellsToRefine
2976 )
2977 {
2978  // Set splitting direction
2979  vector refDir(Zero);
2980  refDir[cmpt] = 1;
2981  List<refineCell> refCells(cellsToRefine.size());
2982  forAll(cellsToRefine, i)
2983  {
2984  refCells[i] = refineCell(cellsToRefine[i], refDir);
2985  }
2986 
2987  // How to walk circumference of cells
2988  hexCellLooper cellWalker(mesh_);
2989 
2990  // Analyse cuts
2991  cellCuts cuts(mesh_, cellWalker, refCells);
2992 
2993  // Cell cutter
2994  Foam::meshCutter meshRefiner(mesh_);
2995 
2996  polyTopoChange meshMod(mesh_);
2997 
2998  // Insert mesh refinement into polyTopoChange.
2999  meshRefiner.setRefinement(cuts, meshMod);
3000 
3001  // Remove any unnecessary fields
3002  mesh_.clearOut();
3003  mesh_.moving(false);
3004 
3005  autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh_, false);
3006 
3007  // Update fields
3008  mesh_.updateMesh(*morphMap);
3009 
3010  // Move mesh (since morphing does not do this)
3011  if (morphMap().hasMotionPoints())
3012  {
3013  mesh_.movePoints(morphMap().preMotionPoints());
3014  }
3015  else
3016  {
3017  // Delete mesh volumes.
3018  mesh_.clearOut();
3019  }
3020 
3021  // Reset the instance for if in overwrite mode
3022  mesh_.setInstance(timeName());
3023 
3024  // Update stored refinement pattern
3025  meshRefiner.updateMesh(*morphMap);
3026 
3027  // Update intersection info
3028  updateMesh(*morphMap, getChangedFaces(*morphMap, cellsToRefine));
3029 
3030  return morphMap;
3031 }
3032 
3033 
3034 // ************************************************************************* //
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
uint8_t direction
Definition: direction.H:46
A list of face labels.
Definition: faceSet.H:47
static void syncBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues, const CombineOp &cop, const TransformOp &top, const bool parRun=UPstream::parRun())
Synchronize values on boundary faces only.
label nOldCells() const
Number of old cells.
Definition: mapPolyMesh.H:469
const labelList & cellMap() const
Old cell map.
Definition: mapPolyMesh.H:534
autoPtr< mapDistributePolyMesh > refineAndBalance(const string &msg, decompositionMethod &decomposer, fvMeshDistribute &distributor, const labelList &cellsToRefine, const scalar maxLoadUnbalance, const label maxCellUnbalance)
Refine some cells and rebalance.
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
static bitSet getMasterFaces(const polyMesh &mesh)
Get per face whether it is uncoupled or a master of a coupled set of faces.
Definition: syncTools.C:119
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1122
labelList sortedOrder(const UList< T > &input)
Return the (stable) sort order for the list.
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
bool isNormalGap(const scalar planarCos, const label level0, const vector &point0, const vector &normal0, const label level1, const vector &point1, const vector &normal1) const
Is local topology a small gap normal to the test vector.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
std::enable_if< std::is_same< bool, TypeT >::value, bool >::type set(const label i, bool val=true)
A bitSet::set() method for a list of bool.
Definition: List.H:489
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
A traits class, which is primarily used for primitives and vector-space.
Definition: pTraits.H:75
static void syncFaceList(const polyMesh &mesh, UList< T > &faceValues, const CombineOp &cop)
Synchronize values on all mesh faces.
Definition: syncTools.H:432
labelList findIndices(const ListType &input, typename ListType::const_reference val, label start=0)
Linear search to find all occurrences of given element.
const cellList & cells() const
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
labelList refineCandidates(const pointField &keepPoints, const scalar curvature, const scalar planarAngle, const bool featureRefinement, const bool featureDistanceRefinement, const bool internalRefinement, const bool surfaceRefinement, const bool curvatureRefinement, const bool smallFeatureRefinement, const bool gapRefinement, const bool bigGapRefinement, const bool spreadGapSize, const label maxGlobalCells, const label maxLocalCells) const
Calculate list of cells to refine.
List< vector > vectorList
List of vector.
Definition: vectorList.H:32
label nFaces() const noexcept
Number of mesh faces.
autoPtr< mapPolyMesh > directionalRefine(const string &msg, const direction cmpt, const labelList &cellsToRefine)
Directionally refine in direction cmpt.
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
Description of cuts across cells.
Definition: cellCuts.H:106
static bool less(const vector &x, const vector &y)
To compare normals.
bool hasMotionPoints() const
Has valid preMotionPoints?
Definition: mapPolyMesh.H:764
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:157
const fvMesh & mesh() const
Reference to mesh.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:164
word timeName
Definition: getTimeIndex.H:3
scalar y
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1065
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
dimensionedScalar cos(const dimensionedScalar &ds)
const cellShapeList & cells
const pointField & points
const dimensionedScalar b
Wien displacement law constant: default SI units: [m.K].
Definition: createFields.H:27
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:137
label checkData(const fvMesh &mesh, const instantList &timeDirs, wordList &objectNames)
Check if fields are good to use (available at all times)
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:608
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Cuts (splits) cells.
Definition: meshCutter.H:134
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1116
virtual bool write(const bool writeOnProc=true) const
Write using setting from DB.
const globalMeshData & globalData() const
Return parallel info (demand-driven)
Definition: polyMesh.C:1311
label nInternalFaces() const noexcept
Number of internal faces.
autoPtr< mapDistributePolyMesh > balance(const bool keepZoneFaces, const bool keepBaffles, const scalarField &cellWeights, decompositionMethod &decomposer, fvMeshDistribute &distributor)
Redecompose according to cell count.
Vector< scalar > vector
Definition: vector.H:57
Container with cells to refine. Refinement given as single direction.
Definition: refineCell.H:52
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
labelList directionalRefineCandidates(const label maxGlobalCells, const label maxLocalCells, const labelList &currentLevel, const direction dir) const
Calculate list of cells to directionally refine.
bool test(const label pos) const
Test for True value at specified position, never auto-vivify entries.
Definition: bitSet.H:326
autoPtr< mapDistributePolyMesh > balanceAndRefine(const string &msg, decompositionMethod &decomposer, fvMeshDistribute &distributor, const labelList &cellsToRefine, const scalar maxLoadUnbalance, const label maxCellUnbalance)
Balance before refining some cells.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
int debug
Static debugging option.
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:201
autoPtr< mapPolyMesh > refine(const labelList &cellsToRefine)
Refine some cells.
static void swapBoundaryCellList(const polyMesh &mesh, const UList< T > &cellData, List< T > &neighbourCellData)
Swap to obtain neighbour cell values for all boundary faces.
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
To compare normals.
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
A cell is defined as a list of faces with extra functionality.
Definition: cell.H:53
Direct mesh changes based on v1.3 polyTopoChange syntax.
const polyMesh & mesh() const
Return polyMesh.
Definition: mapPolyMesh.H:437
normalLess(const vectorList &values)
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
messageStream Info
Information stream (stdout output on master, null elsewhere)
label nTotalFaces() const noexcept
Return total number of faces in decomposed mesh. Not compensated for duplicate faces! ...
constexpr label labelMax
Definition: label.H:55
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Definition: zero.H:57
writeType
Enumeration for what to write. Used as a bit-pattern.
Field< vector > vectorField
Specialisation of Field<T> for vector.
debugType
Enumeration for what to debug. Used as a bit-pattern.
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
bool isGap(const scalar, const vector &, const vector &, const vector &, const vector &) const
Is local topology a small gap?
List< point > pointList
List of point.
Definition: pointList.H:32
List< label > labelList
A List of labels.
Definition: List.H:62
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
label nBoundaryFaces() const noexcept
Number of boundary faces (== nFaces - nInternalFaces)
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
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)
const pointField & preMotionPoints() const
Pre-motion point positions.
Definition: mapPolyMesh.H:756
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues)
Swap coupled boundary face values. Uses eqOp.
Definition: syncTools.H:485
bool operator()(const label a, const label b) const
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127