Loading...
Searching...
No Matches
backgroundMeshDecomposition.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-2016 OpenFOAM Foundation
9 Copyright (C) 2017-2022 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
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
30#include "meshSearch.H"
33#include "Time.H"
34#include "Random.H"
35#include "pointConversion.H"
36#include "decompositionModel.H"
37
38// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
39
40namespace Foam
41{
43}
44
45
46// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
47
49(
50 const labelUList& toProc
51)
52{
53 // Determine send map
54 // ~~~~~~~~~~~~~~~~~~
55
56 // 1. Count
58
59 forAll(toProc, i)
60 {
61 label proci = toProc[i];
62 nSend[proci]++;
63 }
64
65
66 // 2. Size sendMap
68
69 forAll(nSend, proci)
70 {
71 sendMap[proci].resize_nocopy(nSend[proci]);
72 nSend[proci] = 0;
73 }
74
75 // 3. Fill sendMap
76 forAll(toProc, i)
77 {
78 label proci = toProc[i];
79 sendMap[proci][nSend[proci]++] = i;
80 }
81
82 return autoPtr<mapDistribute>::New(std::move(sendMap));
83}
84
85
86// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
87
88void Foam::backgroundMeshDecomposition::initialRefinement()
89{
90 volScalarField cellWeights
91 (
93 (
94 "cellWeights",
95 mesh_.time().timeName(),
96 mesh_,
99 ),
100 mesh_,
103 );
104
105 const conformationSurfaces& geometry = geometryToConformTo_;
106
107 decompositionMethod& decomposer =
109
110 volScalarField::Internal& icellWeights = cellWeights;
111
112 // For each cell in the mesh has it been determined if it is fully
113 // inside, outside, or overlaps the surface
114 List<volumeType> volumeStatus
115 (
116 mesh_.nCells(),
118 );
119
120 // Surface refinement
121 {
122 while (true)
123 {
124 // Determine/update the status of each cell
125 forAll(volumeStatus, celli)
126 {
127 if (volumeStatus[celli] == volumeType::UNKNOWN)
128 {
129 treeBoundBox cellBb(mesh_.cellBb(celli));
130
131 if (geometry.overlaps(cellBb))
132 {
133 volumeStatus[celli] = volumeType::MIXED;
134 }
135 else if (geometry.inside(cellBb.centre()))
136 {
137 volumeStatus[celli] = volumeType::INSIDE;
138 }
139 else
140 {
141 volumeStatus[celli] = volumeType::OUTSIDE;
142 }
143 }
144 }
145
146 {
147 labelList refCells = selectRefinementCells
148 (
149 volumeStatus,
150 cellWeights
151 );
152
153 // Maintain 2:1 ratio
154 labelList newCellsToRefine
155 (
156 meshCutter_.consistentRefinement
157 (
158 refCells,
159 true // extend set
160 )
161 );
162
163 forAll(newCellsToRefine, nCTRI)
164 {
165 label celli = newCellsToRefine[nCTRI];
166
167 if (volumeStatus[celli] == volumeType::MIXED)
168 {
169 volumeStatus[celli] = volumeType::UNKNOWN;
170 }
171
172 icellWeights[celli] = max
173 (
174 1.0,
175 icellWeights[celli]/8.0
176 );
177 }
178
179 if (returnReduceAnd(newCellsToRefine.empty()))
180 {
181 break;
182 }
183
184 // Mesh changing engine.
185 polyTopoChange meshMod(mesh_);
186
187 // Play refinement commands into mesh changer.
188 meshCutter_.setRefinement(newCellsToRefine, meshMod);
189
190 // Create mesh, return map from old to new mesh.
191 autoPtr<mapPolyMesh> map = meshMod.changeMesh
192 (
193 mesh_,
194 false, // inflate
195 true, // syncParallel
196 true, // orderCells (to reduce cell transfers)
197 false // orderPoints
198 );
199
200 // Update fields
201 mesh_.updateMesh(map());
202
203 // Update numbering of cells/vertices.
204 meshCutter_.updateMesh(map());
205
206 {
207 // Map volumeStatus
208
209 const labelList& cellMap = map().cellMap();
210
211 List<volumeType> newVolumeStatus(cellMap.size());
212
213 forAll(cellMap, newCelli)
214 {
215 label oldCelli = cellMap[newCelli];
216
217 if (oldCelli == -1)
218 {
219 newVolumeStatus[newCelli] = volumeType::UNKNOWN;
220 }
221 else
222 {
223 newVolumeStatus[newCelli] = volumeStatus[oldCelli];
224 }
225 }
226
227 volumeStatus.transfer(newVolumeStatus);
228 }
229
230 Info<< " Background mesh refined from "
231 << returnReduce(map().nOldCells(), sumOp<label>())
232 << " to " << mesh_.globalData().nTotalCells()
233 << " cells." << endl;
234 }
235
236 // Determine/update the status of each cell
237 forAll(volumeStatus, celli)
238 {
239 if (volumeStatus[celli] == volumeType::UNKNOWN)
240 {
241 treeBoundBox cellBb(mesh_.cellBb(celli));
242
243 if (geometry.overlaps(cellBb))
244 {
245 volumeStatus[celli] = volumeType::MIXED;
246 }
247 else if (geometry.inside(cellBb.centre()))
248 {
249 volumeStatus[celli] = volumeType::INSIDE;
250 }
251 else
252 {
253 volumeStatus[celli] = volumeType::OUTSIDE;
254 }
255 }
256 }
257
258 // Hard code switch for this stage for testing
259 bool removeOutsideCells = false;
260
261 if (removeOutsideCells)
262 {
263 DynamicList<label> cellsToRemove;
264
265 forAll(volumeStatus, celli)
266 {
267 if (volumeStatus[celli] == volumeType::OUTSIDE)
268 {
269 cellsToRemove.append(celli);
270 }
271 }
272
273 removeCells cellRemover(mesh_);
274
275 // Mesh changing engine.
276 polyTopoChange meshMod(mesh_);
277
278 labelList exposedFaces = cellRemover.getExposedFaces
279 (
280 cellsToRemove
281 );
282
283 // Play refinement commands into mesh changer.
284 cellRemover.setRefinement
285 (
286 cellsToRemove,
287 exposedFaces,
288 labelList(exposedFaces.size(), Zero), // patchID dummy
289 meshMod
290 );
291
292 // Create mesh, return map from old to new mesh.
293 autoPtr<mapPolyMesh> map = meshMod.changeMesh
294 (
295 mesh_,
296 false, // inflate
297 true, // syncParallel
298 true, // orderCells (to reduce cell transfers)
299 false // orderPoints
300 );
301
302 // Update fields
303 mesh_.updateMesh(map());
304
305 // Update numbering of cells/vertices.
306 meshCutter_.updateMesh(map());
307 cellRemover.updateMesh(map());
308
309 {
310 // Map volumeStatus
311
312 const labelList& cellMap = map().cellMap();
313
314 List<volumeType> newVolumeStatus(cellMap.size());
315
316 forAll(cellMap, newCelli)
317 {
318 label oldCelli = cellMap[newCelli];
319
320 if (oldCelli == -1)
321 {
322 newVolumeStatus[newCelli] = volumeType::UNKNOWN;
323 }
324 else
325 {
326 newVolumeStatus[newCelli] = volumeStatus[oldCelli];
327 }
328 }
329
330 volumeStatus.transfer(newVolumeStatus);
331 }
332
333 Info<< "Removed "
334 << returnReduce(map().nOldCells(), sumOp<label>())
335 - mesh_.globalData().nTotalCells()
336 << " cells." << endl;
337 }
338
339 if (debug)
340 {
341 // const_cast<Time&>(mesh_.time())++;
342 // Info<< "Time " << mesh_.time().timeName() << endl;
343 meshCutter_.write();
344 mesh_.write();
345 cellWeights.write();
346 }
347
348 labelList newDecomp = decomposer.decompose
349 (
350 mesh_,
351 mesh_.cellCentres(),
352 icellWeights
353 );
354
355 fvMeshDistribute distributor(mesh_);
356
357 autoPtr<mapDistributePolyMesh> mapDist = distributor.distribute
358 (
359 newDecomp
360 );
361
362 meshCutter_.distribute(mapDist());
363
364 mapDist().distributeCellData(volumeStatus);
365
366 if (debug)
367 {
368 printMeshData(mesh_);
369
370 // const_cast<Time&>(mesh_.time())++;
371 // Info<< "Time " << mesh_.time().timeName() << endl;
372 meshCutter_.write();
373 mesh_.write();
374 cellWeights.write();
375 }
376 }
377 }
378
379 if (debug)
380 {
381 // const_cast<Time&>(mesh_.time())++;
382 // Info<< "Time " << mesh_.time().timeName() << endl;
383 cellWeights.write();
384 mesh_.write();
385 }
386
387 buildPatchAndTree();
388}
389
390
391void Foam::backgroundMeshDecomposition::printMeshData
392(
393 const polyMesh& mesh
394) const
395{
396 // Collect all data on master
397
398 globalIndex globalCells(mesh.nCells());
399 // labelListList patchNeiProcNo(Pstream::nProcs());
400 // labelListList patchSize(Pstream::nProcs());
401 // const labelList& pPatches = mesh.globalData().processorPatches();
402 // patchNeiProcNo[Pstream::myProcNo()].setSize(pPatches.size());
403 // patchSize[Pstream::myProcNo()].setSize(pPatches.size());
404 // forAll(pPatches, i)
405 // {
406 // const processorPolyPatch& ppp = refCast<const processorPolyPatch>
407 // (
408 // mesh.boundaryMesh()[pPatches[i]]
409 // );
410 // patchNeiProcNo[Pstream::myProcNo()][i] = ppp.neighbProcNo();
411 // patchSize[Pstream::myProcNo()][i] = ppp.size();
412 // }
413 // Pstream::gatherList(patchNeiProcNo);
414 // Pstream::gatherList(patchSize);
415
416
417 // // Print stats
418
419 // globalIndex globalBoundaryFaces(mesh.nBoundaryFaces());
420
421 for (const int proci : Pstream::allProcs())
422 {
423 Info<< "Processor " << proci << " "
424 << "Number of cells = " << globalCells.localSize(proci)
425 << endl;
426
427 // label nProcFaces = 0;
428
429 // const labelList& nei = patchNeiProcNo[proci];
430
431 // forAll(patchNeiProcNo[proci], i)
432 // {
433 // Info<< " Number of faces shared with processor "
434 // << patchNeiProcNo[proci][i] << " = " << patchSize[proci][i]
435 // << endl;
436
437 // nProcFaces += patchSize[proci][i];
438 // }
439
440 // Info<< " Number of processor patches = " << nei.size() << nl
441 // << " Number of processor faces = " << nProcFaces << nl
442 // << " Number of boundary faces = "
443 // << globalBoundaryFaces.localSize(proci) << endl;
444 }
445}
446
447
448bool Foam::backgroundMeshDecomposition::refineCell
449(
450 label celli,
451 volumeType volType,
452 scalar& weightEstimate
453) const
454{
455 // Sample the box to find an estimate of the min size, and a volume
456 // estimate when overlapping == true.
457
458// const conformationSurfaces& geometry = geometryToConformTo_;
459
460 treeBoundBox cellBb(mesh_.cellBb(celli));
461
462 weightEstimate = 1.0;
463
464 if (volType == volumeType::MIXED)
465 {
466// // Assess the cell size at the nearest point on the surface for the
467// // MIXED cells, if the cell is large with respect to the cell size,
468// // then refine it.
469//
470// pointField samplePoints
471// (
472// volRes_*volRes_*volRes_,
473// Zero
474// );
475//
476// // scalar sampleVol = cellBb.volume()/samplePoints.size();
477//
478// vector delta = cellBb.span()/volRes_;
479//
480// label pI = 0;
481//
482// for (label i = 0; i < volRes_; i++)
483// {
484// for (label j = 0; j < volRes_; j++)
485// {
486// for (label k = 0; k < volRes_; k++)
487// {
488// samplePoints[pI++] =
489// cellBb.min()
490// + vector
491// (
492// delta.x()*(i + 0.5),
493// delta.y()*(j + 0.5),
494// delta.z()*(k + 0.5)
495// );
496// }
497// }
498// }
499//
500// List<pointIndexHit> hitInfo;
501// labelList hitSurfaces;
502//
503// geometry.findSurfaceNearest
504// (
505// samplePoints,
506// scalarField(samplePoints.size(), sqr(GREAT)),
507// hitInfo,
508// hitSurfaces
509// );
510//
511// // weightEstimate = 0.0;
512//
513// scalar minCellSize = GREAT;
514//
515// forAll(samplePoints, i)
516// {
517// scalar s = cellShapeControl_.cellSize
518// (
519// hitInfo[i].hitPoint()
520// );
521//
522// // Info<< "cellBb.centre() " << cellBb.centre() << nl
523// // << samplePoints[i] << nl
524// // << hitInfo[i] << nl
525// // << "cellBb.span() " << cellBb.span() << nl
526// // << "cellBb.mag() " << cellBb.mag() << nl
527// // << s << endl;
528//
529// if (s < minCellSize)
530// {
531// minCellSize = max(s, minCellSizeLimit_);
532// }
533//
534// // Estimate the number of points in the cell by the surface size,
535// // this is likely to be too small, so reduce.
536// // weightEstimate += sampleVol/pow3(s);
537// }
538//
539// if (sqr(spanScale_)*sqr(minCellSize) < cellBb.magSqr())
540// {
541// return true;
542// }
543 }
544 else if (volType == volumeType::INSIDE)
545 {
546 // scalar s =
547 // foamyHexMesh_.cellShapeControl_.cellSize(cellBb.centre());
548
549 // Estimate number of points in cell by the size at the cell centre
550 // weightEstimate = cellBb.volume()/pow3(s);
551
552 return false;
553 }
554 // else
555 // {
556 // weightEstimate = 1.0;
557
558 // return false;
559 // }
560
561 return false;
562}
563
564
565Foam::labelList Foam::backgroundMeshDecomposition::selectRefinementCells
566(
567 List<volumeType>& volumeStatus,
568 volScalarField& cellWeights
569) const
570{
571 volScalarField::Internal& icellWeights = cellWeights;
572
573 labelHashSet cellsToRefine;
574
575 // Determine/update the status of each cell
576 forAll(volumeStatus, celli)
577 {
578 if (volumeStatus[celli] == volumeType::MIXED)
579 {
580 if (meshCutter_.cellLevel()[celli] < minLevels_)
581 {
582 cellsToRefine.insert(celli);
583 }
584 }
585
586 if (volumeStatus[celli] != volumeType::OUTSIDE)
587 {
588 if
589 (
591 (
592 celli,
593 volumeStatus[celli],
594 icellWeights[celli]
595 )
596 )
597 {
598 cellsToRefine.insert(celli);
599 }
600 }
601 }
602
603 return cellsToRefine.toc();
604}
605
606
607void Foam::backgroundMeshDecomposition::buildPatchAndTree()
608{
609 primitivePatch tmpBoundaryFaces
610 (
611 mesh_.boundaryMesh().faces(),
612 mesh_.points()
613 );
614
615 boundaryFacesPtr_.reset
616 (
617 new bPatch
618 (
619 tmpBoundaryFaces.localFaces(),
620 tmpBoundaryFaces.localPoints()
621 )
622 );
623
624 // Overall bb
625 treeBoundBox overallBb(boundaryFacesPtr_().localPoints());
626
627 Random& rnd = rndGen_;
628
629 bFTreePtr_.reset
630 (
632 (
634 (
635 false,
636 boundaryFacesPtr_(),
638 ),
639 overallBb.extend(rnd, 1e-4),
640 10, // maxLevel
641 10, // leafSize
642 3.0 // duplicity
643 )
644 );
645
646 // Give the bounds of every processor to every other processor
647 allBackgroundMeshBounds_[Pstream::myProcNo()] = overallBb;
648
649 Pstream::allGatherList(allBackgroundMeshBounds_);
650
651 // find global bounding box
652 globalBackgroundBounds_.reset();
653 forAll(allBackgroundMeshBounds_, proci)
654 {
655 globalBackgroundBounds_.add(allBackgroundMeshBounds_[proci]);
656 }
657
658 if (false)
659 {
660 OFstream fStr
661 (
662 mesh_.time().path()
663 /"backgroundMeshDecomposition_proc_"
665 + "_boundaryFaces.obj"
666 );
667
668 const faceList& faces = boundaryFacesPtr_().localFaces();
669 const List<point>& points = boundaryFacesPtr_().localPoints();
670
671 Map<label> foamToObj(points.size());
672
673 label vertI = 0;
674
675 forAll(faces, i)
676 {
677 const face& f = faces[i];
678
679 forAll(f, fPI)
680 {
681 if (foamToObj.insert(f[fPI], vertI))
682 {
683 meshTools::writeOBJ(fStr, points[f[fPI]]);
684 vertI++;
685 }
686 }
687
688 fStr<< 'f';
689
690 forAll(f, fPI)
691 {
692 fStr<< ' ' << foamToObj[f[fPI]] + 1;
693 }
694
695 fStr<< nl;
696 }
697 }
698}
699
700
701// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
702
703Foam::backgroundMeshDecomposition::backgroundMeshDecomposition
704(
705 const Time& runTime,
706 Random& rndGen,
707 const conformationSurfaces& geometryToConformTo,
708 const dictionary& coeffsDict,
709 const fileName& decompDictFile
710)
711:
712 runTime_(runTime),
713 geometryToConformTo_(geometryToConformTo),
714 rndGen_(rndGen),
715 mesh_
716 (
718 (
719 "backgroundMeshDecomposition",
720 runTime_.timeName(),
721 runTime_,
722 IOobject::MUST_READ,
723 IOobject::AUTO_WRITE,
724 IOobject::NO_REGISTER
725 )
726 ),
727 meshCutter_
728 (
729 mesh_,
730 labelList(mesh_.nCells(), Zero),
731 labelList(mesh_.nPoints(), Zero)
732 ),
733 boundaryFacesPtr_(),
734 bFTreePtr_(),
735 allBackgroundMeshBounds_(Pstream::nProcs()),
736 globalBackgroundBounds_(),
737 mergeDist_(1e-6*mesh_.bounds().mag()),
738 spanScale_(coeffsDict.get<scalar>("spanScale")),
739 minCellSizeLimit_
740 (
741 coeffsDict.getOrDefault<scalar>("minCellSizeLimit", 0)
742 ),
743 minLevels_(coeffsDict.get<label>("minLevels")),
744 volRes_(coeffsDict.get<label>("sampleResolution")),
745 maxCellWeightCoeff_(coeffsDict.get<scalar>("maxCellWeightCoeff"))
746{
747 if (!Pstream::parRun())
748 {
750 << "This cannot be used when not running in parallel."
751 << exit(FatalError);
752 }
753
754 const decompositionMethod& decomposer =
755 decompositionModel::New(mesh_, decompDictFile).decomposer();
756
757 if (!decomposer.parallelAware())
758 {
760 << "You have selected decomposition method "
761 << decomposer.typeName
762 << " which is not parallel aware." << endl
763 << exit(FatalError);
764 }
765
766 Info<< nl << "Building initial background mesh decomposition" << endl;
767
768 initialRefinement();
769}
770
771
772// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
773
776(
777 volScalarField& cellWeights
778)
779{
780 if (debug)
781 {
782 // const_cast<Time&>(mesh_.time())++;
783 // Info<< "Time " << mesh_.time().timeName() << endl;
784 cellWeights.write();
785 mesh_.write();
786 }
787
788 volScalarField::Internal& icellWeights = cellWeights;
789
790 while (true)
791 {
792 // Refine large cells if necessary
793
794 label nOccupiedCells = 0;
795
796 forAll(icellWeights, cI)
797 {
798 if (icellWeights[cI] > 1 - SMALL)
799 {
800 nOccupiedCells++;
801 }
802 }
803
804 // Only look at occupied cells, as there is a possibility of runaway
805 // refinement if the number of cells grows too fast. Also, clip the
806 // minimum cellWeightLimit at maxCellWeightCoeff_
807
808 scalar cellWeightLimit = max
809 (
810 maxCellWeightCoeff_
811 *sum(cellWeights).value()
812 /returnReduce(nOccupiedCells, sumOp<label>()),
813 maxCellWeightCoeff_
814 );
815
816 if (debug)
817 {
818 Info<< " cellWeightLimit " << cellWeightLimit << endl;
819
820 Pout<< " sum(cellWeights) " << sum(cellWeights.primitiveField())
821 << " max(cellWeights) " << max(cellWeights.primitiveField())
822 << endl;
823 }
824
825 labelHashSet cellsToRefine;
826
827 forAll(icellWeights, cWI)
828 {
829 if (icellWeights[cWI] > cellWeightLimit)
830 {
831 cellsToRefine.insert(cWI);
832 }
833 }
834
835 if (returnReduceAnd(cellsToRefine.empty()))
836 {
837 break;
838 }
839
840 // Maintain 2:1 ratio
841 labelList newCellsToRefine
842 (
843 meshCutter_.consistentRefinement
844 (
845 cellsToRefine.toc(),
846 true // extend set
847 )
848 );
849
850 if (debug && !cellsToRefine.empty())
851 {
852 Pout<< " cellWeights too large in " << cellsToRefine.size()
853 << " cells" << endl;
854 }
855
856 forAll(newCellsToRefine, nCTRI)
857 {
858 label celli = newCellsToRefine[nCTRI];
859
860 icellWeights[celli] /= 8.0;
861 }
862
863 // Mesh changing engine.
864 polyTopoChange meshMod(mesh_);
865
866 // Play refinement commands into mesh changer.
867 meshCutter_.setRefinement(newCellsToRefine, meshMod);
868
869 // Create mesh, return map from old to new mesh.
870 autoPtr<mapPolyMesh> map = meshMod.changeMesh
871 (
872 mesh_,
873 false, // inflate
874 true, // syncParallel
875 true, // orderCells (to reduce cell motion)
876 false // orderPoints
877 );
878
879 // Update fields
880 mesh_.updateMesh(map());
881
882 // Update numbering of cells/vertices.
883 meshCutter_.updateMesh(map());
884
885 Info<< " Background mesh refined from "
886 << returnReduce(map().nOldCells(), sumOp<label>())
887 << " to " << mesh_.globalData().nTotalCells()
888 << " cells." << endl;
889
890 if (debug)
891 {
892 // const_cast<Time&>(mesh_.time())++;
893 // Info<< "Time " << mesh_.time().timeName() << endl;
894 cellWeights.write();
895 mesh_.write();
896 }
897 }
898
899 if (debug)
900 {
901 printMeshData(mesh_);
902
903 Pout<< " Pre distribute sum(cellWeights) "
904 << sum(icellWeights)
905 << " max(cellWeights) "
906 << max(icellWeights)
907 << endl;
908 }
909
910 decompositionMethod& decomposer =
912
913 labelList newDecomp = decomposer.decompose
914 (
915 mesh_,
916 mesh_.cellCentres(),
917 icellWeights
918 );
919
920 Info<< " Redistributing background mesh cells" << endl;
921
922 fvMeshDistribute distributor(mesh_);
923
924 autoPtr<mapDistributePolyMesh> mapDist = distributor.distribute(newDecomp);
925
926 meshCutter_.distribute(mapDist());
927
928 if (debug)
929 {
930 printMeshData(mesh_);
931
932 Pout<< " Post distribute sum(cellWeights) "
933 << sum(icellWeights)
934 << " max(cellWeights) "
935 << max(icellWeights)
936 << endl;
937
938 // const_cast<Time&>(mesh_.time())++;
939 // Info<< "Time " << mesh_.time().timeName() << endl;
940 mesh_.write();
941 cellWeights.write();
942 }
943
944 buildPatchAndTree();
945
946 return mapDist;
947}
948
949
951(
952 const point& pt
953) const
954{
955// return bFTreePtr_().findAnyOverlap(pt, 0.0);
956
957 return (bFTreePtr_().getVolumeType(pt) == volumeType::INSIDE);
958}
959
960
962(
963 const List<point>& pts
964) const
965{
966 boolList posProc(pts.size(), true);
967
968 forAll(pts, pI)
969 {
970 posProc[pI] = positionOnThisProcessor(pts[pI]);
971 }
972
973 return posProc;
974}
975
976
978(
979 const treeBoundBox& box
980) const
981{
982// return !procBounds().contains(box);
983 return !bFTreePtr_().findBox(box).empty();
984}
985
986
988(
989 const point& centre,
990 const scalar radiusSqr
991) const
992{
993 //return bFTreePtr_().findAnyOverlap(centre, radiusSqr);
994
995 return bFTreePtr_().findNearest(centre, radiusSqr).hit();
996}
997
998
1000(
1001 const point& start,
1002 const point& end
1003) const
1004{
1005 return bFTreePtr_().findLine(start, end);
1006}
1007
1008
1010(
1011 const point& start,
1012 const point& end
1013) const
1014{
1015 return bFTreePtr_().findLineAny(start, end);
1016}
1017
1018
1020(
1021 const List<point>& pts
1022) const
1023{
1024 DynamicList<label> toCandidateProc;
1025 DynamicList<point> testPoints;
1026 labelList ptBlockStart(pts.size(), -1);
1027 labelList ptBlockSize(pts.size(), -1);
1028
1029 label nTotalCandidates = 0;
1030
1031 forAll(pts, pI)
1032 {
1033 const point& pt = pts[pI];
1034
1035 label nCandidates = 0;
1036
1037 forAll(allBackgroundMeshBounds_, proci)
1038 {
1039 // Candidate points may lie just outside a processor box, increase
1040 // test range by using overlaps rather than contains
1041 if (allBackgroundMeshBounds_[proci].overlaps(pt, sqr(SMALL*100)))
1042 {
1043 toCandidateProc.append(proci);
1044 testPoints.append(pt);
1045
1046 nCandidates++;
1047 }
1048 }
1049
1050 ptBlockStart[pI] = nTotalCandidates;
1051 ptBlockSize[pI] = nCandidates;
1052
1053 nTotalCandidates += nCandidates;
1054 }
1055
1056 // Needed for reverseDistribute
1057 label preDistributionToCandidateProcSize = toCandidateProc.size();
1058
1059 autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1060
1061 map().distribute(testPoints);
1062
1063 List<scalar> distanceSqrToCandidate(testPoints.size(), sqr(GREAT));
1064
1065 // Test candidate points on candidate processors
1066 forAll(testPoints, tPI)
1067 {
1068 pointIndexHit info = bFTreePtr_().findNearest
1069 (
1070 testPoints[tPI],
1071 sqr(GREAT)
1072 );
1073
1074 if (info.hit())
1075 {
1076 distanceSqrToCandidate[tPI] = info.point().distSqr(testPoints[tPI]);
1077 }
1078 }
1079
1080 map().reverseDistribute
1081 (
1082 preDistributionToCandidateProcSize,
1083 distanceSqrToCandidate
1084 );
1085
1086 labelList ptNearestProc(pts.size(), -1);
1087
1088 forAll(pts, pI)
1089 {
1090 // Extract the sub list of results for this point
1091
1092 SubList<scalar> ptNearestProcResults
1093 (
1094 distanceSqrToCandidate,
1095 ptBlockSize[pI],
1096 ptBlockStart[pI]
1097 );
1098
1099 scalar nearestProcDistSqr = GREAT;
1100
1101 forAll(ptNearestProcResults, pPRI)
1102 {
1103 if (ptNearestProcResults[pPRI] < nearestProcDistSqr)
1104 {
1105 nearestProcDistSqr = ptNearestProcResults[pPRI];
1106
1107 ptNearestProc[pI] = toCandidateProc[ptBlockStart[pI] + pPRI];
1108 }
1109 }
1110
1111 if (debug)
1112 {
1113 Pout<< pts[pI] << " nearestProcDistSqr " << nearestProcDistSqr
1114 << " ptNearestProc[pI] " << ptNearestProc[pI] << endl;
1115 }
1116
1117 if (ptNearestProc[pI] < 0)
1118 {
1120 << "The position " << pts[pI]
1121 << " did not find a nearest point on the background mesh."
1122 << exit(FatalError);
1123 }
1124 }
1125
1126 return ptNearestProc;
1127}
1128
1129
1130
1131Foam::List<Foam::List<Foam::pointIndexHit>>
1133(
1134 const List<point>& starts,
1135 const List<point>& ends,
1136 bool includeOwnProcessor
1137) const
1138{
1139 DynamicList<label> toCandidateProc;
1140 DynamicList<point> testStarts;
1141 DynamicList<point> testEnds;
1142 labelList segmentBlockStart(starts.size(), -1);
1143 labelList segmentBlockSize(starts.size(), -1);
1144
1145 label nTotalCandidates = 0;
1146
1147 forAll(starts, sI)
1148 {
1149 const point& s = starts[sI];
1150 const point& e = ends[sI];
1151
1152 // Dummy point for treeBoundBox::intersects
1153 point p(Zero);
1154
1155 label nCandidates = 0;
1156
1157 forAll(allBackgroundMeshBounds_, proci)
1158 {
1159 // It is assumed that the sphere in question overlaps the source
1160 // processor, so don't test it, unless includeOwnProcessor is true
1161 if
1162 (
1163 (includeOwnProcessor || proci != Pstream::myProcNo())
1164 && allBackgroundMeshBounds_[proci].intersects(s, e, p)
1165 )
1166 {
1167 toCandidateProc.append(proci);
1168 testStarts.append(s);
1169 testEnds.append(e);
1170
1171 nCandidates++;
1172 }
1173 }
1174
1175 segmentBlockStart[sI] = nTotalCandidates;
1176 segmentBlockSize[sI] = nCandidates;
1177
1178 nTotalCandidates += nCandidates;
1179 }
1180
1181 // Needed for reverseDistribute
1182 label preDistributionToCandidateProcSize = toCandidateProc.size();
1183
1184 autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1185
1186 map().distribute(testStarts);
1187 map().distribute(testEnds);
1188
1189 List<pointIndexHit> segmentIntersectsCandidate(testStarts.size());
1190
1191 // Test candidate segments on candidate processors
1192 forAll(testStarts, sI)
1193 {
1194 const point& s = testStarts[sI];
1195 const point& e = testEnds[sI];
1196
1197 // If the sphere finds some elements of the patch, then it overlaps
1198 segmentIntersectsCandidate[sI] = bFTreePtr_().findLine(s, e);
1199 }
1200
1201 map().reverseDistribute
1202 (
1203 preDistributionToCandidateProcSize,
1204 segmentIntersectsCandidate
1205 );
1206
1207 List<List<pointIndexHit>> segmentHitProcs(starts.size());
1208
1209 // Working storage for assessing processors
1210 DynamicList<pointIndexHit> tmpProcHits;
1211
1212 forAll(starts, sI)
1213 {
1214 tmpProcHits.clear();
1215
1216 // Extract the sub list of results for this point
1217
1218 SubList<pointIndexHit> segmentProcResults
1219 (
1220 segmentIntersectsCandidate,
1221 segmentBlockSize[sI],
1222 segmentBlockStart[sI]
1223 );
1224
1225 forAll(segmentProcResults, sPRI)
1226 {
1227 if (segmentProcResults[sPRI].hit())
1228 {
1229 tmpProcHits.append(segmentProcResults[sPRI]);
1230
1231 tmpProcHits.last().setIndex
1232 (
1233 toCandidateProc[segmentBlockStart[sI] + sPRI]
1234 );
1235 }
1236 }
1237
1238 segmentHitProcs[sI] = tmpProcHits;
1239 }
1240
1241 return segmentHitProcs;
1242}
1243
1244
1246(
1247 const point& centre,
1248 const scalar& radiusSqr
1249) const
1250{
1251 forAll(allBackgroundMeshBounds_, proci)
1252 {
1253 if (bFTreePtr_().findNearest(centre, radiusSqr).hit())
1254 {
1255 return true;
1256 }
1257 }
1258
1259 return false;
1260}
1261
1262
1264(
1265 const point& centre,
1266 const scalar radiusSqr
1267) const
1268{
1270
1271 forAll(allBackgroundMeshBounds_, proci)
1272 {
1273 // Test against the bounding box of the processor
1274 if
1275 (
1276 proci != Pstream::myProcNo()
1277 && allBackgroundMeshBounds_[proci].overlaps(centre, radiusSqr)
1278 )
1279 {
1280 // Expensive test
1281// if (bFTreePtr_().findNearest(centre, radiusSqr).hit())
1282 {
1283 toProc.append(proci);
1284 }
1285 }
1286 }
1287
1288 return toProc;
1289}
1290
1291
1292//Foam::labelListList Foam::backgroundMeshDecomposition::overlapsProcessors
1293//(
1294// const List<point>& centres,
1295// const List<scalar>& radiusSqrs,
1296// const Delaunay& T,
1297// bool includeOwnProcessor
1298//) const
1299//{
1300// DynamicList<label> toCandidateProc;
1301// DynamicList<point> testCentres;
1302// DynamicList<scalar> testRadiusSqrs;
1303// labelList sphereBlockStart(centres.size(), -1);
1304// labelList sphereBlockSize(centres.size(), -1);
1305//
1306// label nTotalCandidates = 0;
1307//
1308// forAll(centres, sI)
1309// {
1310// const point& c = centres[sI];
1311// scalar rSqr = radiusSqrs[sI];
1312//
1313// label nCandidates = 0;
1314//
1315// forAll(allBackgroundMeshBounds_, proci)
1316// {
1317// // It is assumed that the sphere in question overlaps the source
1318// // processor, so don't test it, unless includeOwnProcessor is true
1319// if
1320// (
1321// (includeOwnProcessor || proci != Pstream::myProcNo())
1322// && allBackgroundMeshBounds_[proci].overlaps(c, rSqr)
1323// )
1324// {
1325// if (bFTreePtr_().findNearest(c, rSqr).hit())
1326// {
1327// toCandidateProc.append(proci);
1328// testCentres.append(c);
1329// testRadiusSqrs.append(rSqr);
1330//
1331// nCandidates++;
1332// }
1333// }
1334// }
1335//
1336// sphereBlockStart[sI] = nTotalCandidates;
1337// sphereBlockSize[sI] = nCandidates;
1338//
1339// nTotalCandidates += nCandidates;
1340// }
1341//
1342// // Needed for reverseDistribute
1349//
1350// // TODO: This is faster, but results in more vertices being referred
1351// boolList sphereOverlapsCandidate(testCentres.size(), true);
1388//
1394//
1395// labelListList sphereProcs(centres.size());
1396//
1397// // Working storage for assessing processors
1398// DynamicList<label> tmpProcs;
1399//
1400// forAll(centres, sI)
1401// {
1402// tmpProcs.clear();
1403//
1404// // Extract the sub list of results for this point
1405//
1406// SubList<bool> sphereProcResults
1407// (
1408// sphereOverlapsCandidate,
1409// sphereBlockSize[sI],
1410// sphereBlockStart[sI]
1411// );
1412//
1413// forAll(sphereProcResults, sPRI)
1414// {
1415// if (sphereProcResults[sPRI])
1416// {
1417// tmpProcs.append(toCandidateProc[sphereBlockStart[sI] + sPRI]);
1418// }
1419// }
1420//
1421// sphereProcs[sI] = tmpProcs;
1422// }
1423//
1424// return sphereProcs;
1425//}
1426
1427
1428//Foam::labelListList Foam::backgroundMeshDecomposition::overlapProcessors
1429//(
1430// const point& cc,
1431// const scalar rSqr
1432//) const
1433//{
1434// DynamicList<label> toCandidateProc;
1435// label sphereBlockStart(-1);
1436// label sphereBlockSize(-1);
1437//
1438// label nCandidates = 0;
1439//
1440// forAll(allBackgroundMeshBounds_, proci)
1441// {
1442// // It is assumed that the sphere in question overlaps the source
1443// // processor, so don't test it, unless includeOwnProcessor is true
1444// if
1445// (
1446// (includeOwnProcessor || proci != Pstream::myProcNo())
1447// && allBackgroundMeshBounds_[proci].overlaps(cc, rSqr)
1448// )
1449// {
1450// toCandidateProc.append(proci);
1451//
1452// nCandidates++;
1453// }
1454// }
1455//
1456// sphereBlockSize = nCandidates;
1457// nTotalCandidates += nCandidates;
1458//
1459// // Needed for reverseDistribute
1460// label preDistributionToCandidateProcSize = toCandidateProc.size();
1461//
1462// autoPtr<mapDistribute> map(buildMap(toCandidateProc));
1463//
1464// map().distribute(testCentres);
1465// map().distribute(testRadiusSqrs);
1466//
1467// // TODO: This is faster, but results in more vertices being referred
1469// boolList sphereOverlapsCandidate(testCentres.size(), false);
1470//
1471// // Test candidate spheres on candidate processors
1472// forAll(testCentres, sI)
1473// {
1474// const point& c = testCentres[sI];
1475// const scalar rSqr = testRadiusSqrs[sI];
1476//
1477// const bool flagOverlap = bFTreePtr_().findNearest(c, rSqr).hit();
1478//
1479// if (flagOverlap)
1480// {
1481// //if (vertexOctree.findAnyOverlap(c, rSqr))
1486//
1501// sphereOverlapsCandidate[sI] = true;
1503// }
1504// }
1505//
1506// map().reverseDistribute
1507// (
1508// preDistributionToCandidateProcSize,
1509// sphereOverlapsCandidate
1510// );
1511//
1512// labelListList sphereProcs(centres.size());
1513//
1514// // Working storage for assessing processors
1515// DynamicList<label> tmpProcs;
1516//
1517// forAll(centres, sI)
1518// {
1519// tmpProcs.clear();
1520//
1521// // Extract the sub list of results for this point
1522//
1523// SubList<bool> sphereProcResults
1524// (
1525// sphereOverlapsCandidate,
1526// sphereBlockSize[sI],
1527// sphereBlockStart[sI]
1528// );
1529//
1530// forAll(sphereProcResults, sPRI)
1531// {
1532// if (sphereProcResults[sPRI])
1533// {
1534// tmpProcs.append(toCandidateProc[sphereBlockStart[sI] + sPRI]);
1535// }
1536// }
1537//
1538// sphereProcs[sI] = tmpProcs;
1539// }
1540//
1541// return sphereProcs;
1542//}
1543
1544
1545// ************************************************************************* //
A 1D vector of objects of type <T> that resizes itself as necessary to accept the new objects.
Definition DynamicList.H:68
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
DimensionedField< scalar, volMesh > Internal
@ NO_READ
Nothing to be read.
@ NO_WRITE
Ignore writing from objectRegistry::writeObject().
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition IOobject.H:191
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition List.H:72
A HashTable to objects of type <T> with a label key.
Definition Map.H:54
Output to file stream as an OSstream, normally using std::ofstream for the actual output.
Definition OFstream.H:75
virtual bool write(const token &tok)=0
Write token to stream or otherwise handle it.
Inter-processor communications stream.
Definition Pstream.H:59
static void allGatherList(UList< T > &values, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Gather data, but keep individual values separate. Uses MPI_Allgather or manual communication.
Random number generator.
Definition Random.H:56
void reset(const label seedValue)
Reset the random number generator seed.
Definition RandomI.H:43
A non-owning sub-view of a List (allocated or unallocated storage).
Definition SubList.H:61
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition Time.H:75
void size(const label n)
Older name for setAddressableSize.
Definition UList.H:118
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Negative if the process is not a...
Definition UPstream.H:1706
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:1697
static rangeType allProcs(const label communicator=worldComm)
Range of process indices for all processes.
Definition UPstream.H:1857
Pointer management similar to std::unique_ptr, with some additional methods and type checking.
Definition autoPtr.H:65
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition autoPtr.H:178
Store a background polyMesh to use for the decomposition of space and queries for parallel conformalV...
labelList processorNearestPosition(const List< point > &pts) const
What is the nearest processor to the given position?
pointIndexHit findLineAny(const point &start, const point &end) const
Find any intersection of line between start and end, (exposing.
bool overlapsThisProcessor(const treeBoundBox &box) const
Does the given box overlap the faces of the boundary of this.
bool positionOnThisProcessor(const point &pt) const
Is the given position inside the domain of this decomposition.
autoPtr< mapDistributePolyMesh > distribute(volScalarField &cellWeights)
Redistribute the background mesh based on a supplied weight field,.
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.
static autoPtr< mapDistribute > buildMap(const labelUList &toProc)
Build a mapDistribute for the supplied destination processor data.
pointIndexHit findLine(const point &start, const point &end) const
Find nearest intersection of line between start and end, (exposing.
bool overlapsOtherProcessors(const point &centre, const scalar &radiusSqr) const
labelList overlapProcessors(const point &centre, const scalar radiusSqr) const
Abstract base class for domain decomposition.
static const decompositionModel & New(const polyMesh &mesh, const fileName &decompDictFile="", const dictionary *fallback=nullptr)
Read and register on mesh, optionally with alternative decomposeParDict path/name or with fallback co...
decompositionMethod & decomposer() const
Return demand-driven decomposition method.
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition dictionary.H:133
A face is a list of labels corresponding to mesh vertices.
Definition face.H:71
A class for handling file names.
Definition fileName.H:75
Sends/receives parts of mesh+fvfields to neighbouring processors. Used in load balancing.
static const word & zeroGradientType() noexcept
The type name for zeroGradient patch fields.
Calculates a non-overlapping list of offsets based on an input size (eg, number of cells) from differ...
Definition globalIndex.H:77
static scalar & perturbTol() noexcept
Get the perturbation tolerance.
Non-pointer based hierarchical recursive searching.
Mesh consisting of general polyhedral cells.
Definition polyMesh.H:79
Direct mesh changes based on v1.3 polyTopoChange syntax.
label nCells() const noexcept
Number of mesh cells.
Container with cells to refine. Refinement given as single direction.
Definition refineCell.H:53
Given list of cells to remove, insert all the topology changes.
Definition removeCells.H:60
Standard boundBox with extra functionality for use in octree.
An enumeration wrapper for classification of a location as being inside/outside of a volume.
Definition volumeType.H:56
@ OUTSIDE
A location outside the volume.
Definition volumeType.H:66
@ MIXED
A location that is partly inside and outside.
Definition volumeType.H:67
@ UNKNOWN
Unknown state.
Definition volumeType.H:64
@ INSIDE
A location inside the volume.
Definition volumeType.H:65
static const word null
An empty word.
Definition word.H:84
#define defineTypeNameAndDebug(Type, DebugSwitch)
Define the typeName and debug information.
Definition className.H:142
volScalarField & p
dynamicFvMesh & mesh
engineTime & runTime
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition error.H:600
const pointField & points
label nPoints
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
word timeName
Definition getTimeIndex.H:3
List< ReturnType > get(const UPtrList< T > &list, const AccessOp &aop)
List of values generated by applying the access operation to each list item.
Namespace for bounding specifications. At the moment, mostly for tables.
Namespace for handling debugging switches.
Definition debug.C:45
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of a point.
Definition meshTools.C:196
Namespace for OpenFOAM.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition hashSets.C:40
const dimensionSet dimless
Dimensionless.
List< labelList > labelListList
List of labelList.
Definition labelList.H:38
List< label > labelList
A List of labels.
Definition List.H:62
dimensionedSymmTensor sqr(const dimensionedVector &dv)
HashSet< label, Hash< label > > labelHashSet
A HashSet of labels, uses label hasher.
Definition HashSet.H:85
GeometricField< scalar, fvPatchField, volMesh > volScalarField
messageStream Info
Information stream (stdout output on master, null elsewhere).
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
List< face > faceList
List of faces.
Definition faceListFwd.H:41
treeDataPrimitivePatch< bPatch > treeDataBPatch
T returnReduce(const T &value, BinaryOp bop, const int tag=UPstream::msgType(), const int communicator=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition Ostream.H:519
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
bool returnReduceAnd(const bool value, const int communicator=UPstream::worldComm)
Perform logical (and) MPI Allreduce on a copy. Uses UPstream::reduceAnd.
vector point
Point is a vector.
Definition point.H:37
List< bool > boolList
A List of bools.
Definition List.H:60
dimensioned< Type > sum(const DimensionedField< Type, GeoMesh > &f1, const label comm)
static constexpr const zero Zero
Global zero (0).
Definition zero.H:127
PrimitivePatch<::Foam::List< face >, const pointField > bPatch
error FatalError
Error stream (stdout output on all processes), with additional 'FOAM FATAL ERROR' header text and sta...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition exprTraits.C:127
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
PointIndexHit< point > pointIndexHit
A PointIndexHit with a 3D point.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition errorManip.H:125
constexpr char nl
The newline '\n' character (0x0a).
Definition Ostream.H:50
labelList f(nPoints)
const pointField & pts
volScalarField & e
#define forAll(list, i)
Loop across all elements in list.
Definition stdFoam.H:299
Random rndGen