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