backgroundMeshDecomposition.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2019 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 
27 #include "meshSearch.H"
28 #include "conformationSurfaces.H"
30 #include "Time.H"
31 #include "Random.H"
32 #include "pointConversion.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38  defineTypeNameAndDebug(backgroundMeshDecomposition, 0);
39 }
40 
41 
42 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
43 
45 (
46  const List<label>& toProc
47 )
48 {
49  // Determine send map
50  // ~~~~~~~~~~~~~~~~~~
51 
52  // 1. Count
53  labelList nSend(Pstream::nProcs(), 0);
54 
55  forAll(toProc, i)
56  {
57  label proci = toProc[i];
58 
59  nSend[proci]++;
60  }
61 
62 
63  // 2. Size sendMap
64  labelListList sendMap(Pstream::nProcs());
65 
66  forAll(nSend, proci)
67  {
68  sendMap[proci].setSize(nSend[proci]);
69 
70  nSend[proci] = 0;
71  }
72 
73  // 3. Fill sendMap
74  forAll(toProc, i)
75  {
76  label proci = toProc[i];
77 
78  sendMap[proci][nSend[proci]++] = i;
79  }
80 
81  // 4. Send over how many I need to receive
82  labelList recvSizes;
83  Pstream::exchangeSizes(sendMap, recvSizes);
84 
85 
86  // Determine receive map
87  // ~~~~~~~~~~~~~~~~~~~~~
88 
89  labelListList constructMap(Pstream::nProcs());
90 
91  // Local transfers first
92  constructMap[Pstream::myProcNo()] = identity
93  (
94  sendMap[Pstream::myProcNo()].size()
95  );
96 
97  label constructSize = constructMap[Pstream::myProcNo()].size();
98 
99  forAll(constructMap, proci)
100  {
101  if (proci != Pstream::myProcNo())
102  {
103  label nRecv = recvSizes[proci];
104 
105  constructMap[proci].setSize(nRecv);
106 
107  for (label i = 0; i < nRecv; i++)
108  {
109  constructMap[proci][i] = constructSize++;
110  }
111  }
112  }
113 
114  return autoPtr<mapDistribute>
115  (
116  new mapDistribute
117  (
118  constructSize,
119  move(sendMap),
120  move(constructMap)
121  )
122  );
123 }
124 
125 
126 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
127 
128 void Foam::backgroundMeshDecomposition::initialRefinement()
129 {
130  volScalarField cellWeights
131  (
132  IOobject
133  (
134  "cellWeights",
135  mesh_.time().timeName(),
136  mesh_,
139  ),
140  mesh_,
142  zeroGradientFvPatchScalarField::typeName
143  );
144 
145  const conformationSurfaces& geometry = geometryToConformTo_;
146 
147  decompositionMethod& decomposer = decomposerPtr_();
148 
149  volScalarField::Internal& icellWeights = cellWeights;
150 
151  // For each cell in the mesh has it been determined if it is fully
152  // inside, outside, or overlaps the surface
153  List<volumeType> volumeStatus
154  (
155  mesh_.nCells(),
157  );
158 
159  // Surface refinement
160  {
161  while (true)
162  {
163  // Determine/update the status of each cell
164  forAll(volumeStatus, celli)
165  {
166  if (volumeStatus[celli] == volumeType::unknown)
167  {
168  treeBoundBox cellBb
169  (
170  mesh_.cells()[celli].points
171  (
172  mesh_.faces(),
173  mesh_.points()
174  )
175  );
176 
177  if (geometry.overlaps(cellBb))
178  {
179  volumeStatus[celli] = volumeType::mixed;
180  }
181  else if (geometry.inside(cellBb.midpoint()))
182  {
183  volumeStatus[celli] = volumeType::inside;
184  }
185  else
186  {
187  volumeStatus[celli] = volumeType::outside;
188  }
189  }
190  }
191 
192  {
193  labelList refCells = selectRefinementCells
194  (
195  volumeStatus,
196  cellWeights
197  );
198 
199  // Maintain 2:1 ratio
200  labelList newCellsToRefine
201  (
202  meshCutter_.consistentRefinement
203  (
204  refCells,
205  true // extend set
206  )
207  );
208 
209  forAll(newCellsToRefine, nCTRI)
210  {
211  label celli = newCellsToRefine[nCTRI];
212 
213  if (volumeStatus[celli] == volumeType::mixed)
214  {
215  volumeStatus[celli] = volumeType::unknown;
216  }
217 
218  icellWeights[celli] = max
219  (
220  1.0,
221  icellWeights[celli]/8.0
222  );
223  }
224 
225  if (returnReduce(newCellsToRefine.size(), sumOp<label>()) == 0)
226  {
227  break;
228  }
229 
230  // Mesh changing engine.
231  polyTopoChange meshMod(mesh_);
232 
233  // Play refinement commands into mesh changer.
234  meshCutter_.setRefinement(newCellsToRefine, meshMod);
235 
236  // Create mesh, return map from old to new mesh.
237  autoPtr<mapPolyMesh> map = meshMod.changeMesh
238  (
239  mesh_,
240  false, // inflate
241  true, // syncParallel
242  true, // orderCells (to reduce cell transfers)
243  false // orderPoints
244  );
245 
246  // Update fields
247  mesh_.updateMesh(map);
248 
249  // Update numbering of cells/vertices.
250  meshCutter_.updateMesh(map);
251 
252  {
253  // Map volumeStatus
254 
255  const labelList& cellMap = map().cellMap();
256 
257  List<volumeType> newVolumeStatus(cellMap.size());
258 
259  forAll(cellMap, newCelli)
260  {
261  label oldCelli = cellMap[newCelli];
262 
263  if (oldCelli == -1)
264  {
265  newVolumeStatus[newCelli] = volumeType::unknown;
266  }
267  else
268  {
269  newVolumeStatus[newCelli] = volumeStatus[oldCelli];
270  }
271  }
272 
273  volumeStatus.transfer(newVolumeStatus);
274  }
275 
276  Info<< " Background mesh refined from "
277  << returnReduce(map().nOldCells(), sumOp<label>())
278  << " to " << mesh_.globalData().nTotalCells()
279  << " cells." << endl;
280  }
281 
282  // Determine/update the status of each cell
283  forAll(volumeStatus, celli)
284  {
285  if (volumeStatus[celli] == volumeType::unknown)
286  {
287  treeBoundBox cellBb
288  (
289  mesh_.cells()[celli].points
290  (
291  mesh_.faces(),
292  mesh_.points()
293  )
294  );
295 
296  if (geometry.overlaps(cellBb))
297  {
298  volumeStatus[celli] = volumeType::mixed;
299  }
300  else if (geometry.inside(cellBb.midpoint()))
301  {
302  volumeStatus[celli] = volumeType::inside;
303  }
304  else
305  {
306  volumeStatus[celli] = volumeType::outside;
307  }
308  }
309  }
310 
311  // Hard code switch for this stage for testing
312  bool removeOutsideCells = false;
313 
314  if (removeOutsideCells)
315  {
316  DynamicList<label> cellsToRemove;
317 
318  forAll(volumeStatus, celli)
319  {
320  if (volumeStatus[celli] == volumeType::outside)
321  {
322  cellsToRemove.append(celli);
323  }
324  }
325 
326  removeCells cellRemover(mesh_);
327 
328  // Mesh changing engine.
329  polyTopoChange meshMod(mesh_);
330 
331  labelList exposedFaces = cellRemover.getExposedFaces
332  (
333  cellsToRemove
334  );
335 
336  // Play refinement commands into mesh changer.
337  cellRemover.setRefinement
338  (
339  cellsToRemove,
340  exposedFaces,
341  labelList(exposedFaces.size(), 0), // patchID dummy
342  meshMod
343  );
344 
345  // Create mesh, return map from old to new mesh.
346  autoPtr<mapPolyMesh> map = meshMod.changeMesh
347  (
348  mesh_,
349  false, // inflate
350  true, // syncParallel
351  true, // orderCells (to reduce cell transfers)
352  false // orderPoints
353  );
354 
355  // Update fields
356  mesh_.updateMesh(map);
357 
358  // Update numbering of cells/vertices.
359  meshCutter_.updateMesh(map);
360  cellRemover.updateMesh(map);
361 
362  {
363  // Map volumeStatus
364 
365  const labelList& cellMap = map().cellMap();
366 
367  List<volumeType> newVolumeStatus(cellMap.size());
368 
369  forAll(cellMap, newCelli)
370  {
371  label oldCelli = cellMap[newCelli];
372 
373  if (oldCelli == -1)
374  {
375  newVolumeStatus[newCelli] = volumeType::unknown;
376  }
377  else
378  {
379  newVolumeStatus[newCelli] = volumeStatus[oldCelli];
380  }
381  }
382 
383  volumeStatus.transfer(newVolumeStatus);
384  }
385 
386  Info<< "Removed "
387  << returnReduce(map().nOldCells(), sumOp<label>())
388  - mesh_.globalData().nTotalCells()
389  << " cells." << endl;
390  }
391 
392  if (debug)
393  {
394  // const_cast<Time&>(mesh_.time())++;
395  // Info<< "Time " << mesh_.time().timeName() << endl;
396  meshCutter_.write();
397  mesh_.write();
398  cellWeights.write();
399  }
400 
401  labelList newDecomp = decomposer.decompose
402  (
403  mesh_,
404  mesh_.cellCentres(),
405  icellWeights
406  );
407 
408  fvMeshDistribute distributor(mesh_, mergeDist_);
409 
410  autoPtr<mapDistributePolyMesh> mapDist = distributor.distribute
411  (
412  newDecomp
413  );
414 
415  meshCutter_.distribute(mapDist);
416 
417  mapDist().distributeCellData(volumeStatus);
418 
419  if (debug)
420  {
421  printMeshData(mesh_);
422 
423  // const_cast<Time&>(mesh_.time())++;
424  // Info<< "Time " << mesh_.time().timeName() << endl;
425  meshCutter_.write();
426  mesh_.write();
427  cellWeights.write();
428  }
429  }
430  }
431 
432  if (debug)
433  {
434  // const_cast<Time&>(mesh_.time())++;
435  // Info<< "Time " << mesh_.time().timeName() << endl;
436  cellWeights.write();
437  mesh_.write();
438  }
439 
440  buildPatchAndTree();
441 }
442 
443 
444 void Foam::backgroundMeshDecomposition::printMeshData
445 (
446  const polyMesh& mesh
447 ) const
448 {
449  // Collect all data on master
450 
451  globalIndex globalCells(mesh.nCells());
452  // labelListList patchNeiProcNo(Pstream::nProcs());
453  // labelListList patchSize(Pstream::nProcs());
454  // const labelList& pPatches = mesh.globalData().processorPatches();
455  // patchNeiProcNo[Pstream::myProcNo()].setSize(pPatches.size());
456  // patchSize[Pstream::myProcNo()].setSize(pPatches.size());
457  // forAll(pPatches, i)
458  // {
459  // const processorPolyPatch& ppp = refCast<const processorPolyPatch>
460  // (
461  // mesh.boundaryMesh()[pPatches[i]]
462  // );
463  // patchNeiProcNo[Pstream::myProcNo()][i] = ppp.neighbProcNo();
464  // patchSize[Pstream::myProcNo()][i] = ppp.size();
465  // }
466  // Pstream::gatherList(patchNeiProcNo);
467  // Pstream::gatherList(patchSize);
468 
469 
470  // // Print stats
471 
472  // globalIndex globalBoundaryFaces(mesh.nFaces()-mesh.nInternalFaces());
473 
474  for (label proci = 0; proci < Pstream::nProcs(); proci++)
475  {
476  Info<< "Processor " << proci << " "
477  << "Number of cells = " << globalCells.localSize(proci)
478  << endl;
479 
480  // label nProcFaces = 0;
481 
482  // const labelList& nei = patchNeiProcNo[proci];
483 
484  // forAll(patchNeiProcNo[proci], i)
485  // {
486  // Info<< " Number of faces shared with processor "
487  // << patchNeiProcNo[proci][i] << " = " << patchSize[proci][i]
488  // << endl;
489 
490  // nProcFaces += patchSize[proci][i];
491  // }
492 
493  // Info<< " Number of processor patches = " << nei.size() << nl
494  // << " Number of processor faces = " << nProcFaces << nl
495  // << " Number of boundary faces = "
496  // << globalBoundaryFaces.localSize(proci) << endl;
497  }
498 }
499 
500 
501 bool Foam::backgroundMeshDecomposition::refineCell
502 (
503  label celli,
504  volumeType volType,
505  scalar& weightEstimate
506 ) const
507 {
508  // Sample the box to find an estimate of the min size, and a volume
509  // estimate when overlapping == true.
510 
511 // const conformationSurfaces& geometry = geometryToConformTo_;
512 
513  treeBoundBox cellBb
514  (
515  mesh_.cells()[celli].points
516  (
517  mesh_.faces(),
518  mesh_.points()
519  )
520  );
521 
522  weightEstimate = 1.0;
523 
524  if (volType == volumeType::mixed)
525  {
526 // // Assess the cell size at the nearest point on the surface for the
527 // // MIXED cells, if the cell is large with respect to the cell size,
528 // // then refine it.
529 //
530 // pointField samplePoints
531 // (
532 // volRes_*volRes_*volRes_,
533 // Zero
534 // );
535 //
536 // // scalar sampleVol = cellBb.volume()/samplePoints.size();
537 //
538 // vector delta = cellBb.span()/volRes_;
539 //
540 // label pI = 0;
541 //
542 // for (label i = 0; i < volRes_; i++)
543 // {
544 // for (label j = 0; j < volRes_; j++)
545 // {
546 // for (label k = 0; k < volRes_; k++)
547 // {
548 // samplePoints[pI++] =
549 // cellBb.min()
550 // + vector
551 // (
552 // delta.x()*(i + 0.5),
553 // delta.y()*(j + 0.5),
554 // delta.z()*(k + 0.5)
555 // );
556 // }
557 // }
558 // }
559 //
560 // List<pointIndexHit> hitInfo;
561 // labelList hitSurfaces;
562 //
563 // geometry.findSurfaceNearest
564 // (
565 // samplePoints,
566 // scalarField(samplePoints.size(), sqr(great)),
567 // hitInfo,
568 // hitSurfaces
569 // );
570 //
571 // // weightEstimate = 0.0;
572 //
573 // scalar minCellSize = great;
574 //
575 // forAll(samplePoints, i)
576 // {
577 // scalar s = cellShapeControl_.cellSize
578 // (
579 // hitInfo[i].hitPoint()
580 // );
581 //
582 // // Info<< "cellBb.midpoint() " << cellBb.midpoint() << nl
583 // // << samplePoints[i] << nl
584 // // << hitInfo[i] << nl
585 // // << "cellBb.span() " << cellBb.span() << nl
586 // // << "cellBb.mag() " << cellBb.mag() << nl
587 // // << s << endl;
588 //
589 // if (s < minCellSize)
590 // {
591 // minCellSize = max(s, minCellSizeLimit_);
592 // }
593 //
594 // // Estimate the number of points in the cell by the surface size,
595 // // this is likely to be too small, so reduce.
596 // // weightEstimate += sampleVol/pow3(s);
597 // }
598 //
599 // if (sqr(spanScale_)*sqr(minCellSize) < magSqr(cellBb.span()))
600 // {
601 // return true;
602 // }
603  }
604  else if (volType == volumeType::inside)
605  {
606  // scalar s =
607  // foamyHexMesh_.cellShapeControl_.cellSize(cellBb.midpoint());
608 
609  // Estimate the number of points in the cell by the size at the cell
610  // midpoint
611  // weightEstimate = cellBb.volume()/pow3(s);
612 
613  return false;
614  }
615  // else
616  // {
617  // weightEstimate = 1.0;
618 
619  // return false;
620  // }
621 
622  return false;
623 }
624 
625 
626 Foam::labelList Foam::backgroundMeshDecomposition::selectRefinementCells
627 (
628  List<volumeType>& volumeStatus,
629  volScalarField& cellWeights
630 ) const
631 {
632  volScalarField::Internal& icellWeights = cellWeights;
633 
634  labelHashSet cellsToRefine;
635 
636  // Determine/update the status of each cell
637  forAll(volumeStatus, celli)
638  {
639  if (volumeStatus[celli] == volumeType::mixed)
640  {
641  if (meshCutter_.cellLevel()[celli] < minLevels_)
642  {
643  cellsToRefine.insert(celli);
644  }
645  }
646 
647  if (volumeStatus[celli] != volumeType::outside)
648  {
649  if
650  (
651  refineCell
652  (
653  celli,
654  volumeStatus[celli],
655  icellWeights[celli]
656  )
657  )
658  {
659  cellsToRefine.insert(celli);
660  }
661  }
662  }
663 
664  return cellsToRefine.toc();
665 }
666 
667 
668 void Foam::backgroundMeshDecomposition::buildPatchAndTree()
669 {
670  primitivePatch tmpBoundaryFaces
671  (
672  SubList<face>
673  (
674  mesh_.faces(),
675  mesh_.nFaces() - mesh_.nInternalFaces(),
676  mesh_.nInternalFaces()
677  ),
678  mesh_.points()
679  );
680 
681  boundaryFacesPtr_.reset
682  (
683  new bPatch
684  (
685  tmpBoundaryFaces.localFaces(),
686  tmpBoundaryFaces.localPoints()
687  )
688  );
689 
690  // Overall bb
691  treeBoundBox overallBb(boundaryFacesPtr_().localPoints());
692 
693  bFTreePtr_.reset
694  (
695  new indexedOctree<treeDataBPatch>
696  (
698  (
699  false,
700  boundaryFacesPtr_(),
702  ),
703  overallBb.extend(1e-4),
704  10, // maxLevel
705  10, // leafSize
706  3.0 // duplicity
707  )
708  );
709 
710  // Give the bounds of every processor to every other processor
711  allBackgroundMeshBounds_[Pstream::myProcNo()] = overallBb;
712 
713  Pstream::gatherList(allBackgroundMeshBounds_);
714  Pstream::scatterList(allBackgroundMeshBounds_);
715 
716  point bbMin(great, great, great);
717  point bbMax(-great, -great, -great);
718 
719  forAll(allBackgroundMeshBounds_, proci)
720  {
721  bbMin = min(bbMin, allBackgroundMeshBounds_[proci].min());
722  bbMax = max(bbMax, allBackgroundMeshBounds_[proci].max());
723  }
724 
725  globalBackgroundBounds_ = treeBoundBox(bbMin, bbMax);
726 
727  if (false)
728  {
729  OFstream fStr
730  (
731  mesh_.time().path()
732  /"backgroundMeshDecomposition_proc_"
734  + "_boundaryFaces.obj"
735  );
736 
737  const faceList& faces = boundaryFacesPtr_().localFaces();
738  const List<point>& points = boundaryFacesPtr_().localPoints();
739 
740  Map<label> foamToObj(points.size());
741 
742  label vertI = 0;
743 
744  forAll(faces, i)
745  {
746  const face& f = faces[i];
747 
748  forAll(f, fPI)
749  {
750  if (foamToObj.insert(f[fPI], vertI))
751  {
752  meshTools::writeOBJ(fStr, points[f[fPI]]);
753  vertI++;
754  }
755  }
756 
757  fStr<< 'f';
758 
759  forAll(f, fPI)
760  {
761  fStr<< ' ' << foamToObj[f[fPI]] + 1;
762  }
763 
764  fStr<< nl;
765  }
766  }
767 }
768 
769 
770 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
771 
773 (
774  const Time& runTime,
775  Random& rndGen,
776  const conformationSurfaces& geometryToConformTo,
777  const dictionary& coeffsDict
778 )
779 :
780  runTime_(runTime),
781  geometryToConformTo_(geometryToConformTo),
782  rndGen_(rndGen),
783  mesh_
784  (
785  IOobject
786  (
787  "backgroundMeshDecomposition",
788  runTime_.timeName(),
789  runTime_,
790  IOobject::MUST_READ,
791  IOobject::AUTO_WRITE,
792  false
793  )
794  ),
795  meshCutter_
796  (
797  mesh_,
798  labelList(mesh_.nCells(), 0),
799  labelList(mesh_.nPoints(), 0)
800  ),
801  boundaryFacesPtr_(),
802  bFTreePtr_(),
803  allBackgroundMeshBounds_(Pstream::nProcs()),
804  globalBackgroundBounds_(),
805  decomposeDict_
806  (
807  IOobject
808  (
809  "decomposeParDict",
810  runTime_.system(),
811  runTime_,
812  IOobject::MUST_READ_IF_MODIFIED,
813  IOobject::NO_WRITE
814  )
815  ),
816  decomposerPtr_(decompositionMethod::New(decomposeDict_)),
817  mergeDist_(1e-6*mesh_.bounds().mag()),
818  spanScale_(readScalar(coeffsDict.lookup("spanScale"))),
819  minCellSizeLimit_
820  (
821  coeffsDict.lookupOrDefault<scalar>("minCellSizeLimit", 0.0)
822  ),
823  minLevels_(readLabel(coeffsDict.lookup("minLevels"))),
824  volRes_(readLabel(coeffsDict.lookup("sampleResolution"))),
825  maxCellWeightCoeff_(readScalar(coeffsDict.lookup("maxCellWeightCoeff")))
826 {
827  if (!Pstream::parRun())
828  {
830  << "This cannot be used when not running in parallel."
831  << exit(FatalError);
832  }
833 
834  if (!decomposerPtr_().parallelAware())
835  {
837  << "You have selected decomposition method "
838  << decomposerPtr_().typeName
839  << " which is not parallel aware." << endl
840  << exit(FatalError);
841  }
842 
843  Info<< nl << "Building initial background mesh decomposition" << endl;
844 
845  initialRefinement();
846 }
847 
848 
849 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
850 
852 {}
853 
854 
855 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
856 
859 (
860  volScalarField& cellWeights
861 )
862 {
863  if (debug)
864  {
865  // const_cast<Time&>(mesh_.time())++;
866  // Info<< "Time " << mesh_.time().timeName() << endl;
867  cellWeights.write();
868  mesh_.write();
869  }
870 
871  volScalarField::Internal& icellWeights = cellWeights;
872 
873  while (true)
874  {
875  // Refine large cells if necessary
876 
877  label nOccupiedCells = 0;
878 
879  forAll(icellWeights, cI)
880  {
881  if (icellWeights[cI] > 1 - small)
882  {
883  nOccupiedCells++;
884  }
885  }
886 
887  // Only look at occupied cells, as there is a possibility of runaway
888  // refinement if the number of cells grows too fast. Also, clip the
889  // minimum cellWeightLimit at maxCellWeightCoeff_
890 
891  scalar cellWeightLimit = max
892  (
893  maxCellWeightCoeff_
894  *sum(cellWeights).value()
895  /returnReduce(nOccupiedCells, sumOp<label>()),
896  maxCellWeightCoeff_
897  );
898 
899  if (debug)
900  {
901  Info<< " cellWeightLimit " << cellWeightLimit << endl;
902 
903  Pout<< " sum(cellWeights) " << sum(cellWeights.primitiveField())
904  << " max(cellWeights) " << max(cellWeights.primitiveField())
905  << endl;
906  }
907 
908  labelHashSet cellsToRefine;
909 
910  forAll(icellWeights, cWI)
911  {
912  if (icellWeights[cWI] > cellWeightLimit)
913  {
914  cellsToRefine.insert(cWI);
915  }
916  }
917 
918  if (returnReduce(cellsToRefine.size(), sumOp<label>()) == 0)
919  {
920  break;
921  }
922 
923  // Maintain 2:1 ratio
924  labelList newCellsToRefine
925  (
926  meshCutter_.consistentRefinement
927  (
928  cellsToRefine.toc(),
929  true // extend set
930  )
931  );
932 
933  if (debug && !cellsToRefine.empty())
934  {
935  Pout<< " cellWeights too large in " << cellsToRefine.size()
936  << " cells" << endl;
937  }
938 
939  forAll(newCellsToRefine, nCTRI)
940  {
941  label celli = newCellsToRefine[nCTRI];
942 
943  icellWeights[celli] /= 8.0;
944  }
945 
946  // Mesh changing engine.
947  polyTopoChange meshMod(mesh_);
948 
949  // Play refinement commands into mesh changer.
950  meshCutter_.setRefinement(newCellsToRefine, meshMod);
951 
952  // Create mesh, return map from old to new mesh.
953  autoPtr<mapPolyMesh> map = meshMod.changeMesh
954  (
955  mesh_,
956  false, // inflate
957  true, // syncParallel
958  true, // orderCells (to reduce cell motion)
959  false // orderPoints
960  );
961 
962  // Update fields
963  mesh_.updateMesh(map);
964 
965  // Update numbering of cells/vertices.
966  meshCutter_.updateMesh(map);
967 
968  Info<< " Background mesh refined from "
969  << returnReduce(map().nOldCells(), sumOp<label>())
970  << " to " << mesh_.globalData().nTotalCells()
971  << " cells." << endl;
972 
973  if (debug)
974  {
975  // const_cast<Time&>(mesh_.time())++;
976  // Info<< "Time " << mesh_.time().timeName() << endl;
977  cellWeights.write();
978  mesh_.write();
979  }
980  }
981 
982  if (debug)
983  {
984  printMeshData(mesh_);
985 
986  Pout<< " Pre distribute sum(cellWeights) "
987  << sum(icellWeights)
988  << " max(cellWeights) "
989  << max(icellWeights)
990  << endl;
991  }
992 
993  labelList newDecomp = decomposerPtr_().decompose
994  (
995  mesh_,
996  mesh_.cellCentres(),
997  icellWeights
998  );
999 
1000  Info<< " Redistributing background mesh cells" << endl;
1001 
1002  fvMeshDistribute distributor(mesh_, mergeDist_);
1003 
1004  autoPtr<mapDistributePolyMesh> mapDist = distributor.distribute(newDecomp);
1005 
1006  meshCutter_.distribute(mapDist);
1007 
1008  if (debug)
1009  {
1010  printMeshData(mesh_);
1011 
1012  Pout<< " Post distribute sum(cellWeights) "
1013  << sum(icellWeights)
1014  << " max(cellWeights) "
1015  << max(icellWeights)
1016  << endl;
1017 
1018  // const_cast<Time&>(mesh_.time())++;
1019  // Info<< "Time " << mesh_.time().timeName() << endl;
1020  mesh_.write();
1021  cellWeights.write();
1022  }
1023 
1024  buildPatchAndTree();
1025 
1026  return mapDist;
1027 }
1028 
1029 
1031 (
1032  const point& pt
1033 ) const
1034 {
1035 // return bFTreePtr_().findAnyOverlap(pt, 0.0);
1036 
1037  return (bFTreePtr_().getVolumeType(pt) == volumeType::inside);
1038 }
1039 
1040 
1042 (
1043  const List<point>& pts
1044 ) const
1045 {
1046  boolList posProc(pts.size(), true);
1047 
1048  forAll(pts, pI)
1049  {
1050  posProc[pI] = positionOnThisProcessor(pts[pI]);
1051  }
1052 
1053  return posProc;
1054 }
1055 
1056 
1058 (
1059  const treeBoundBox& box
1060 ) const
1061 {
1062 // return !procBounds().contains(box);
1063  return !bFTreePtr_().findBox(box).empty();
1064 }
1065 
1066 
1068 (
1069  const point& centre,
1070  const scalar radiusSqr
1071 ) const
1072 {
1073  // return bFTreePtr_().findAnyOverlap(centre, radiusSqr);
1074 
1075  return bFTreePtr_().findNearest(centre, radiusSqr).hit();
1076 }
1077 
1078 
1080 (
1081  const point& start,
1082  const point& end
1083 ) const
1084 {
1085  return bFTreePtr_().findLine(start, end);
1086 }
1087 
1088 
1090 (
1091  const point& start,
1092  const point& end
1093 ) const
1094 {
1095  return bFTreePtr_().findLineAny(start, end);
1096 }
1097 
1098 
1100 (
1101  const List<point>& pts
1102 ) const
1103 {
1104  DynamicList<label> toCandidateProc;
1105  DynamicList<point> testPoints;
1106  labelList ptBlockStart(pts.size(), -1);
1107  labelList ptBlockSize(pts.size(), -1);
1108 
1109  label nTotalCandidates = 0;
1110 
1111  forAll(pts, pI)
1112  {
1113  const point& pt = pts[pI];
1114 
1115  label nCandidates = 0;
1116 
1117  forAll(allBackgroundMeshBounds_, proci)
1118  {
1119  // Candidate points may lie just outside a processor box, increase
1120  // test range by using overlaps rather than contains
1121  if (allBackgroundMeshBounds_[proci].overlaps(pt, sqr(small*100)))
1122  {
1123  toCandidateProc.append(proci);
1124  testPoints.append(pt);
1125 
1126  nCandidates++;
1127  }
1128  }
1129 
1130  ptBlockStart[pI] = nTotalCandidates;
1131  ptBlockSize[pI] = nCandidates;
1132 
1133  nTotalCandidates += nCandidates;
1134  }
1135 
1136  // Needed for reverseDistribute
1137  label preDistributionToCandidateProcSize = toCandidateProc.size();
1138 
1139  autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1140 
1141  map().distribute(testPoints);
1142 
1143  List<scalar> distanceSqrToCandidate(testPoints.size(), sqr(great));
1144 
1145  // Test candidate points on candidate processors
1146  forAll(testPoints, tPI)
1147  {
1148  pointIndexHit info = bFTreePtr_().findNearest
1149  (
1150  testPoints[tPI],
1151  sqr(great)
1152  );
1153 
1154  if (info.hit())
1155  {
1156  distanceSqrToCandidate[tPI] = magSqr
1157  (
1158  testPoints[tPI] - info.hitPoint()
1159  );
1160  }
1161  }
1162 
1163  map().reverseDistribute
1164  (
1165  preDistributionToCandidateProcSize,
1166  distanceSqrToCandidate
1167  );
1168 
1169  labelList ptNearestProc(pts.size(), -1);
1170 
1171  forAll(pts, pI)
1172  {
1173  // Extract the sub list of results for this point
1174 
1175  SubList<scalar> ptNearestProcResults
1176  (
1177  distanceSqrToCandidate,
1178  ptBlockSize[pI],
1179  ptBlockStart[pI]
1180  );
1181 
1182  scalar nearestProcDistSqr = great;
1183 
1184  forAll(ptNearestProcResults, pPRI)
1185  {
1186  if (ptNearestProcResults[pPRI] < nearestProcDistSqr)
1187  {
1188  nearestProcDistSqr = ptNearestProcResults[pPRI];
1189 
1190  ptNearestProc[pI] = toCandidateProc[ptBlockStart[pI] + pPRI];
1191  }
1192  }
1193 
1194  if (debug)
1195  {
1196  Pout<< pts[pI] << " nearestProcDistSqr " << nearestProcDistSqr
1197  << " ptNearestProc[pI] " << ptNearestProc[pI] << endl;
1198  }
1199 
1200  if (ptNearestProc[pI] < 0)
1201  {
1203  << "The position " << pts[pI]
1204  << " did not find a nearest point on the background mesh."
1205  << exit(FatalError);
1206  }
1207  }
1208 
1209  return ptNearestProc;
1210 }
1211 
1212 
1213 
1216 (
1217  const List<point>& starts,
1218  const List<point>& ends,
1219  bool includeOwnProcessor
1220 ) const
1221 {
1222  DynamicList<label> toCandidateProc;
1223  DynamicList<point> testStarts;
1224  DynamicList<point> testEnds;
1225  labelList segmentBlockStart(starts.size(), -1);
1226  labelList segmentBlockSize(starts.size(), -1);
1227 
1228  label nTotalCandidates = 0;
1229 
1230  forAll(starts, sI)
1231  {
1232  const point& s = starts[sI];
1233  const point& e = ends[sI];
1234 
1235  // Dummy point for treeBoundBox::intersects
1236  point p(Zero);
1237 
1238  label nCandidates = 0;
1239 
1240  forAll(allBackgroundMeshBounds_, proci)
1241  {
1242  // It is assumed that the sphere in question overlaps the source
1243  // processor, so don't test it, unless includeOwnProcessor is true
1244  if
1245  (
1246  (includeOwnProcessor || proci != Pstream::myProcNo())
1247  && allBackgroundMeshBounds_[proci].intersects(s, e, p)
1248  )
1249  {
1250  toCandidateProc.append(proci);
1251  testStarts.append(s);
1252  testEnds.append(e);
1253 
1254  nCandidates++;
1255  }
1256  }
1257 
1258  segmentBlockStart[sI] = nTotalCandidates;
1259  segmentBlockSize[sI] = nCandidates;
1260 
1261  nTotalCandidates += nCandidates;
1262  }
1263 
1264  // Needed for reverseDistribute
1265  label preDistributionToCandidateProcSize = toCandidateProc.size();
1266 
1267  autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1268 
1269  map().distribute(testStarts);
1270  map().distribute(testEnds);
1271 
1272  List<pointIndexHit> segmentIntersectsCandidate(testStarts.size());
1273 
1274  // Test candidate segments on candidate processors
1275  forAll(testStarts, sI)
1276  {
1277  const point& s = testStarts[sI];
1278  const point& e = testEnds[sI];
1279 
1280  // If the sphere finds some elements of the patch, then it overlaps
1281  segmentIntersectsCandidate[sI] = bFTreePtr_().findLine(s, e);
1282  }
1283 
1284  map().reverseDistribute
1285  (
1286  preDistributionToCandidateProcSize,
1287  segmentIntersectsCandidate
1288  );
1289 
1290  List<List<pointIndexHit>> segmentHitProcs(starts.size());
1291 
1292  // Working storage for assessing processors
1293  DynamicList<pointIndexHit> tmpProcHits;
1294 
1295  forAll(starts, sI)
1296  {
1297  tmpProcHits.clear();
1298 
1299  // Extract the sub list of results for this point
1300 
1301  SubList<pointIndexHit> segmentProcResults
1302  (
1303  segmentIntersectsCandidate,
1304  segmentBlockSize[sI],
1305  segmentBlockStart[sI]
1306  );
1307 
1308  forAll(segmentProcResults, sPRI)
1309  {
1310  if (segmentProcResults[sPRI].hit())
1311  {
1312  tmpProcHits.append(segmentProcResults[sPRI]);
1313 
1314  tmpProcHits.last().setIndex
1315  (
1316  toCandidateProc[segmentBlockStart[sI] + sPRI]
1317  );
1318  }
1319  }
1320 
1321  segmentHitProcs[sI] = tmpProcHits;
1322  }
1323 
1324  return segmentHitProcs;
1325 }
1326 
1327 
1329 (
1330  const point& centre,
1331  const scalar& radiusSqr
1332 ) const
1333 {
1334  forAll(allBackgroundMeshBounds_, proci)
1335  {
1336  if (bFTreePtr_().findNearest(centre, radiusSqr).hit())
1337  {
1338  return true;
1339  }
1340  }
1341 
1342  return false;
1343 }
1344 
1345 
1347 (
1348  const point& centre,
1349  const scalar radiusSqr
1350 ) const
1351 {
1352  DynamicList<label> toProc(Pstream::nProcs());
1353 
1354  forAll(allBackgroundMeshBounds_, proci)
1355  {
1356  // Test against the bounding box of the processor
1357  if
1358  (
1359  proci != Pstream::myProcNo()
1360  && allBackgroundMeshBounds_[proci].overlaps(centre, radiusSqr)
1361  )
1362  {
1363  // Expensive test
1364 // if (bFTreePtr_().findNearest(centre, radiusSqr).hit())
1365  {
1366  toProc.append(proci);
1367  }
1368  }
1369  }
1370 
1371  return Foam::move(toProc);
1372 }
1373 
1374 
1375 //Foam::labelListList Foam::backgroundMeshDecomposition::overlapsProcessors
1376 //(
1377 // const List<point>& centres,
1378 // const List<scalar>& radiusSqrs,
1379 // const Delaunay& T,
1380 // bool includeOwnProcessor
1381 //) const
1382 //{
1383 // DynamicList<label> toCandidateProc;
1384 // DynamicList<point> testCentres;
1385 // DynamicList<scalar> testRadiusSqrs;
1386 // labelList sphereBlockStart(centres.size(), -1);
1387 // labelList sphereBlockSize(centres.size(), -1);
1388 //
1389 // label nTotalCandidates = 0;
1390 //
1391 // forAll(centres, sI)
1392 // {
1393 // const point& c = centres[sI];
1394 // scalar rSqr = radiusSqrs[sI];
1395 //
1396 // label nCandidates = 0;
1397 //
1398 // forAll(allBackgroundMeshBounds_, proci)
1399 // {
1400 // // It is assumed that the sphere in question overlaps the source
1401 // // processor, so don't test it, unless includeOwnProcessor is true
1402 // if
1403 // (
1404 // (includeOwnProcessor || proci != Pstream::myProcNo())
1405 // && allBackgroundMeshBounds_[proci].overlaps(c, rSqr)
1406 // )
1407 // {
1408 // if (bFTreePtr_().findNearest(c, rSqr).hit())
1409 // {
1410 // toCandidateProc.append(proci);
1411 // testCentres.append(c);
1412 // testRadiusSqrs.append(rSqr);
1413 //
1414 // nCandidates++;
1415 // }
1416 // }
1417 // }
1418 //
1419 // sphereBlockStart[sI] = nTotalCandidates;
1420 // sphereBlockSize[sI] = nCandidates;
1421 //
1422 // nTotalCandidates += nCandidates;
1423 // }
1424 //
1425 // // Needed for reverseDistribute
1432 //
1433 // // TODO: This is faster, but results in more vertices being referred
1434 // boolList sphereOverlapsCandidate(testCentres.size(), true);
1471 //
1477 //
1478 // labelListList sphereProcs(centres.size());
1479 //
1480 // // Working storage for assessing processors
1481 // DynamicList<label> tmpProcs;
1482 //
1483 // forAll(centres, sI)
1484 // {
1485 // tmpProcs.clear();
1486 //
1487 // // Extract the sub list of results for this point
1488 //
1489 // SubList<bool> sphereProcResults
1490 // (
1491 // sphereOverlapsCandidate,
1492 // sphereBlockSize[sI],
1493 // sphereBlockStart[sI]
1494 // );
1495 //
1496 // forAll(sphereProcResults, sPRI)
1497 // {
1498 // if (sphereProcResults[sPRI])
1499 // {
1500 // tmpProcs.append(toCandidateProc[sphereBlockStart[sI] + sPRI]);
1501 // }
1502 // }
1503 //
1504 // sphereProcs[sI] = tmpProcs;
1505 // }
1506 //
1507 // return sphereProcs;
1508 //}
1509 
1510 
1511 //Foam::labelListList Foam::backgroundMeshDecomposition::overlapProcessors
1512 //(
1513 // const point& cc,
1514 // const scalar rSqr
1515 //) const
1516 //{
1517 // DynamicList<label> toCandidateProc;
1518 // label sphereBlockStart(-1);
1519 // label sphereBlockSize(-1);
1520 //
1521 // label nCandidates = 0;
1522 //
1523 // forAll(allBackgroundMeshBounds_, proci)
1524 // {
1525 // // It is assumed that the sphere in question overlaps the source
1526 // // processor, so don't test it, unless includeOwnProcessor is true
1527 // if
1528 // (
1529 // (includeOwnProcessor || proci != Pstream::myProcNo())
1530 // && allBackgroundMeshBounds_[proci].overlaps(cc, rSqr)
1531 // )
1532 // {
1533 // toCandidateProc.append(proci);
1534 //
1535 // nCandidates++;
1536 // }
1537 // }
1538 //
1539 // sphereBlockSize = nCandidates;
1540 // nTotalCandidates += nCandidates;
1541 //
1542 // // Needed for reverseDistribute
1543 // label preDistributionToCandidateProcSize = toCandidateProc.size();
1544 //
1545 // autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1546 //
1547 // map().distribute(testCentres);
1548 // map().distribute(testRadiusSqrs);
1549 //
1550 // // TODO: This is faster, but results in more vertices being referred
1552 // boolList sphereOverlapsCandidate(testCentres.size(), false);
1553 //
1554 // // Test candidate spheres on candidate processors
1555 // forAll(testCentres, sI)
1556 // {
1557 // const point& c = testCentres[sI];
1558 // const scalar rSqr = testRadiusSqrs[sI];
1559 //
1560 // const bool flagOverlap = bFTreePtr_().findNearest(c, rSqr).hit();
1561 //
1562 // if (flagOverlap)
1563 // {
1564 // // if (vertexOctree.findAnyOverlap(c, rSqr))
1569 //
1584 // sphereOverlapsCandidate[sI] = true;
1586 // }
1587 // }
1588 //
1589 // map().reverseDistribute
1590 // (
1591 // preDistributionToCandidateProcSize,
1592 // sphereOverlapsCandidate
1593 // );
1594 //
1595 // labelListList sphereProcs(centres.size());
1596 //
1597 // // Working storage for assessing processors
1598 // DynamicList<label> tmpProcs;
1599 //
1600 // forAll(centres, sI)
1601 // {
1602 // tmpProcs.clear();
1603 //
1604 // // Extract the sub list of results for this point
1605 //
1606 // SubList<bool> sphereProcResults
1607 // (
1608 // sphereOverlapsCandidate,
1609 // sphereBlockSize[sI],
1610 // sphereBlockStart[sI]
1611 // );
1612 //
1613 // forAll(sphereProcResults, sPRI)
1614 // {
1615 // if (sphereProcResults[sPRI])
1616 // {
1617 // tmpProcs.append(toCandidateProc[sphereBlockStart[sI] + sPRI]);
1618 // }
1619 // }
1620 //
1621 // sphereProcs[sI] = tmpProcs;
1622 // }
1623 //
1624 // return sphereProcs;
1625 //}
1626 
1627 
1628 // ************************************************************************* //
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:57
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
bool positionOnThisProcessor(const point &pt) const
Is the given position inside the domain of this decomposition.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
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
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
error FatalError
dimensioned< Type > max(const dimensioned< Type > &, const dimensioned< Type > &)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
dimensionedSymmTensor sqr(const dimensionedVector &dv)
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:429
List< face > faceList
Definition: faceListFwd.H:43
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:256
static scalar & perturbTol()
Get the perturbation tolerance.
labelList identity(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:53
PointIndexHit< point > pointIndexHit
Definition: pointIndexHit.H:42
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of point.
Definition: meshTools.C:203
labelList overlapProcessors(const point &centre, const scalar radiusSqr) const
DimensionedField< scalar, volMesh > Internal
Type of the internal field from which this GeometricField is derived.
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &df)
static autoPtr< mapDistribute > buildMap(const List< label > &toProc)
Build a mapDistribute for the supplied destination processor data.
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tdf1, const word &name, const dimensionSet &dimensions)
List< bool > boolList
Bool container classes.
Definition: boolList.H:50
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:52
HashSet< label, Hash< label > > labelHashSet
A HashSet with label keys.
Definition: HashSet.H:208
treeDataPrimitivePatch< bPatch > treeDataBPatch
static void exchangeSizes(const Container &sendData, labelList &sizes, const label comm=UPstream::worldComm)
Helper: exchange sizes of sendData. sendData is the data per.
Definition: exchange.C:137
labelList processorNearestPosition(const List< point > &pts) const
What is the nearest processor to the given position?
stressControl lookup("compactNormalStress") >> compactNormalStress
bool overlapsOtherProcessors(const point &centre, const scalar &radiusSqr) const
label nPoints
pointIndexHit findLine(const point &start, const point &end) const
Find nearest intersection of line between start and end, (exposing.
List< List< pointIndexHit > > intersectsProcessors(const List< point > &starts, const List< point > &ends, bool includeOwnProcessor=false) const
Which processors are intersected by the line segment, returns all.
word timeName
Definition: getTimeIndex.H:3
List< label > labelList
A List of labels.
Definition: labelList.H:56
static const zero Zero
Definition: zero.H:97
bool readScalar(const char *buf, doubleScalar &s)
Read whole of buf as a scalar. Return true if successful.
Definition: doubleScalar.H:68
label readLabel(Istream &is)
Definition: label.H:64
dimensioned< scalar > magSqr(const dimensioned< Type > &)
static const char nl
Definition: Ostream.H:265
defineTypeNameAndDebug(combustionModel, 0)
labelList f(nPoints)
pointIndexHit findLineAny(const point &start, const point &end) const
Find any intersection of line between start and end, (exposing.
dimensioned< Type > min(const dimensioned< Type > &, const dimensioned< Type > &)
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
Addressing for a faceList slice.
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:399
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:411
const dimensionSet dimless(0, 0, 0, 0, 0, 0, 0)
Definition: dimensionSets.H:47
vector point
Point is a vector.
Definition: point.H:41
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
prefixOSstream Pout(cout, "Pout")
Definition: IOstreams.H:53
messageStream Info
dimensioned< scalar > mag(const dimensioned< Type > &)
PrimitivePatch< faceList, const pointField > bPatch
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
autoPtr< mapDistributePolyMesh > distribute(volScalarField &cellWeights)
Redistribute the background mesh based on a supplied weight field,.
virtual Ostream & write(const token &)=0
Write next token to stream.
volScalarField & p
backgroundMeshDecomposition(const Time &runTime, Random &rndGen, const conformationSurfaces &geometryToConformTo, const dictionary &coeffsDict)
Construct from components in foamyHexMesh operation.
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
~backgroundMeshDecomposition()
Destructor.
Namespace for OpenFOAM.
int system(const std::string &command)
Execute the specified command.
Definition: POSIX.C:1234
bool overlapsThisProcessor(const treeBoundBox &box) const
Does the given box overlap the faces of the boundary of this.