distributedTriSurfaceMesh.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) 2015-2020 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 \*---------------------------------------------------------------------------*/
28 
30 #include "mapDistribute.H"
31 #include "Random.H"
33 #include "triangleFuncs.H"
34 #include "matchPoints.H"
35 #include "globalIndex.H"
36 #include "Time.H"
37 
38 #include "IFstream.H"
39 #include "decompositionMethod.H"
40 #include "geomDecomp.H"
41 #include "vectorList.H"
42 #include "bitSet.H"
43 #include "PatchTools.H"
44 #include "OBJstream.H"
45 #include "labelBits.H"
46 #include "profiling.H"
47 
48 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
49 
50 namespace Foam
51 {
52  defineTypeNameAndDebug(distributedTriSurfaceMesh, 0);
54  (
55  searchableSurface,
56  distributedTriSurfaceMesh,
57  dict
58  );
59 
60 
61  //- Combine operator for volume types
63  {
64  public:
65  void operator()(volumeType& x, const volumeType& y) const
66  {
68  {
69  FatalErrorInFunction << "Illegal volume type "
71  << "," << volumeType::names[y] << exit(FatalError);
72  }
73  else
74  {
75  switch (x)
76  {
78  {
80  {
81  x = y;
82  }
83  }
84  break;
85  case volumeType::INSIDE:
86  {
87  if (y == volumeType::OUTSIDE)
88  {
89  FatalErrorInFunction << "Conflicting volume types "
90  << volumeType::names[x] << ","
92  }
93  }
94  break;
96  {
97  if (y == volumeType::INSIDE)
98  {
99  FatalErrorInFunction << "Conflicting volume types "
100  << volumeType::names[x] << ","
102  }
103  }
104  break;
105  case volumeType::MIXED:
106  break;
107  }
108  }
109  }
110  };
111 
112 
113  //- Combine operator for nearest
115 
117  (
119  (
120  pointIndexHit(),
121  -GREAT
122  )
123  );
125  {
126  public:
128  {
129  if (x.first().hit())
130  {
131  if (y.first().hit() && y.second() < x.second())
132  {
133  x = y;
134  }
135  }
136  else if (y.first().hit())
137  {
138  x = y;
139  }
140  }
141  };
142 }
143 
144 
145 const Foam::Enum
146 <
148 >
150 ({
151  { distributionType::FOLLOW, "follow" },
152  { distributionType::INDEPENDENT, "independent" },
153  { distributionType::DISTRIBUTED, "distributed" },
154  { distributionType::FROZEN, "frozen" }
155 });
156 
157 
158 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
159 
160 Foam::word Foam::distributedTriSurfaceMesh::findLocalInstance
161 (
162  const IOobject& io
163 )
164 {
165  // Modified findInstance which also looks in parent directory
166  word instance
167  (
168  io.time().findInstance
169  (
170  io.local(),
171  word::null,
173  )
174  );
175 
176  if (instance.size())
177  {
178  return instance;
179  }
180 
181 
182  // Replicate findInstance operation but now on parent directory
183 
184  // Search in parent directory
185  fileName parentDir =
186  io.rootPath()/io.time().globalCaseName()
187  /io.instance()/io.db().dbDir()/io.local()/io.name();
188 
189  if (fileHandler().isDir(parentDir))
190  {
191  return io.instance();
192  }
193 
194  instantList ts = io.time().times();
195  label instanceI;
196 
197  const scalar startValue = io.time().timeOutputValue();
198 
199  for (instanceI = ts.size()-1; instanceI >= 0; --instanceI)
200  {
201  if (ts[instanceI].value() <= startValue)
202  {
203  break;
204  }
205  }
206 
207  // continue searching from here
208  for (; instanceI >= 0; --instanceI)
209  {
210  // Shortcut: if actual directory is the timeName we've already tested it
211  if (ts[instanceI].name() == io.instance())
212  {
213  continue;
214  }
215 
216  fileName parentDir =
217  io.rootPath()/io.time().globalCaseName()
218  /ts[instanceI].name()/io.db().dbDir()/io.local()/io.name();
219 
220  if (fileHandler().isDir(parentDir))
221  {
222  return ts[instanceI].name();
223  }
224  }
225 
226  // times() usually already includes the constant() so would have been
227  // checked above. Re-test if
228  // - times() is empty. Sometimes this can happen (e.g. decomposePar with
229  // collated)
230  // - times()[0] is not constant
231  if (!ts.size() || ts[0].name() != io.time().constant())
232  {
233  // Note. This needs to be a hard-coded constant, rather than the
234  // constant function of the time, because the latter points to
235  // the case constant directory in parallel cases
236 
237  fileName parentDir =
238  io.rootPath()/io.time().globalCaseName()
239  /io.time().constant()/io.db().dbDir()/io.local()/io.name();
240 
241  if (fileHandler().isDir(parentDir))
242  {
243  return io.time().constant();
244  }
245  }
246 
248  << "Cannot find directory " << io.local() << " in times " << ts
249  << exit(FatalError);
250 
251  return word::null;
252 }
253 
254 
255 // Read my additional data from the dictionary
256 bool Foam::distributedTriSurfaceMesh::read()
257 {
258  // Get bb of all domains.
259  procBb_.setSize(Pstream::nProcs());
260 
261  if (dict_.empty())
262  {
263  // Did not find the distributed version; assume master has loaded the
264  // triSurfaceMesh version. Make up some settings.
265 
266  const boundBox& localBb = triSurfaceMesh::bounds();
267 
268  procBb_[Pstream::myProcNo()] =
269  treeBoundBoxList(1, treeBoundBox(localBb));
270  Pstream::gatherList(procBb_);
271  Pstream::scatterList(procBb_);
272 
273  dict_.add("bounds", procBb_[Pstream::myProcNo()]);
274 
275  // Wanted distribution type
276  distType_ = DISTRIBUTED; //INDEPENDENT;
277  dict_.add("distributionType", distributionTypeNames_[distType_]);
278 
279  // Merge distance
280  mergeDist_ = SMALL;
281  dict_.add("mergeDistance", mergeDist_);
282 
283  // Force underlying triSurfaceMesh to calculate volume type
284  // (is topological walk; does not construct tree)
285  surfaceClosed_ = triSurfaceMesh::hasVolumeType();
286  Pstream::scatter(surfaceClosed_);
287  dict_.add("closed", surfaceClosed_);
288 
289  // Delay calculating outside vol type since constructs tree. Is ok
290  // after distributing since then local surfaces much smaller
291  //outsideVolType_ = volumeType::UNKNOWN;
292  //if (surfaceClosed_)
293  //{
294  // point outsidePt(localBb.max()+localBb.midpoint());
295  // List<volumeType> outsideVolTypes;
296  // triSurfaceMesh::getVolumeType
297  // (
298  // pointField(1, outsidePt),
299  // outsideVolTypes
300  // );
301  // outsideVolType_ = outsideVolTypes[0];
302  //}
303  //dict_.add("outsideVolumeType", volumeType::names[outsideVolType_]);
304  }
305  else
306  {
307  dict_.readEntry("bounds", procBb_[Pstream::myProcNo()]);
308  Pstream::gatherList(procBb_);
309  Pstream::scatterList(procBb_);
310 
311  // Wanted distribution type
312  distType_ = distributionTypeNames_.get("distributionType", dict_);
313 
314  // Merge distance
315  dict_.readEntry("mergeDistance", mergeDist_);
316 
317  // Distribution type
318  surfaceClosed_ = dict_.getOrDefault<bool>("closed", false);
319 
320  outsideVolType_ = volumeType::names.getOrDefault
321  (
322  "outsideVolumeType",
323  dict_,
325  );
326  }
327 
328  return true;
329 }
330 
331 
332 // Is segment fully local?
333 bool Foam::distributedTriSurfaceMesh::isLocal
334 (
335  const List<treeBoundBox>& myBbs,
336  const point& start,
337  const point& end
338 )
339 {
340  forAll(myBbs, bbi)
341  {
342  if (myBbs[bbi].contains(start) && myBbs[bbi].contains(end))
343  {
344  return true;
345  }
346  }
347  return false;
348 }
349 
350 
351 //void Foam::distributedTriSurfaceMesh::splitSegment
352 //(
353 // const label segmenti,
354 // const point& start,
355 // const point& end,
356 // const treeBoundBox& bb,
357 //
358 // DynamicList<segment>& allSegments,
359 // DynamicList<label>& allSegmentMap,
360 // DynamicList<label> sendMap
361 //) const
362 //{
363 // // Work points
364 // point clipPt0, clipPt1;
365 //
366 // if (bb.contains(start))
367 // {
368 // // start within, trim end to bb
369 // bool clipped = bb.intersects(end, start, clipPt0);
370 //
371 // if (clipped)
372 // {
373 // // segment from start to clippedStart passes
374 // // through proc.
375 // sendMap[proci].append(allSegments.size());
376 // allSegmentMap.append(segmenti);
377 // allSegments.append(segment(start, clipPt0));
378 // }
379 // }
380 // else if (bb.contains(end))
381 // {
382 // // end within, trim start to bb
383 // bool clipped = bb.intersects(start, end, clipPt0);
384 //
385 // if (clipped)
386 // {
387 // sendMap[proci].append(allSegments.size());
388 // allSegmentMap.append(segmenti);
389 // allSegments.append(segment(clipPt0, end));
390 // }
391 // }
392 // else
393 // {
394 // // trim both
395 // bool clippedStart = bb.intersects(start, end, clipPt0);
396 //
397 // if (clippedStart)
398 // {
399 // bool clippedEnd = bb.intersects(end, clipPt0, clipPt1);
400 //
401 // if (clippedEnd)
402 // {
403 // // middle part of segment passes through proc.
404 // sendMap[proci].append(allSegments.size());
405 // allSegmentMap.append(segmenti);
406 // allSegments.append(segment(clipPt0, clipPt1));
407 // }
408 // }
409 // }
410 //}
411 
412 
413 void Foam::distributedTriSurfaceMesh::distributeSegment
414 (
415  const label segmenti,
416  const point& start,
417  const point& end,
418 
419  DynamicList<segment>& allSegments,
420  DynamicList<label>& allSegmentMap,
421  List<DynamicList<label>>& sendMap
422 ) const
423 {
424  // 1. Fully local already handled outside. Note: retest is cheap.
425  if (isLocal(procBb_[Pstream::myProcNo()], start, end))
426  {
427  return;
428  }
429 
430 
431  // 2. If fully inside one other processor, then only need to send
432  // to that one processor even if it intersects another. Rare occurrence
433  // but cheap to test.
434  forAll(procBb_, proci)
435  {
436  if (proci != Pstream::myProcNo())
437  {
438  const List<treeBoundBox>& bbs = procBb_[proci];
439 
440  if (isLocal(bbs, start, end))
441  {
442  sendMap[proci].append(allSegments.size());
443  allSegmentMap.append(segmenti);
444  allSegments.append(segment(start, end));
445  return;
446  }
447  }
448  }
449 
450 
451  // 3. If not contained in single processor send to all intersecting
452  // processors.
453  forAll(procBb_, proci)
454  {
455  const List<treeBoundBox>& bbs = procBb_[proci];
456 
457  forAll(bbs, bbi)
458  {
459  const treeBoundBox& bb = bbs[bbi];
460 
461  // Scheme a: any processor that intersects the segment gets
462  // the whole segment.
463 
464  // Intersection point
465  point clipPt;
466 
467  if (bb.intersects(start, end, clipPt))
468  {
469  sendMap[proci].append(allSegments.size());
470  allSegmentMap.append(segmenti);
471  allSegments.append(segment(start, end));
472  }
473 
474  // Alternative: any processor only gets clipped bit of
475  // segment. This gives small problems with additional
476  // truncation errors.
477  //splitSegment
478  //(
479  // segmenti,
480  // start,
481  // end,
482  // bb,
483  //
484  // allSegments,
485  // allSegmentMap,
486  // sendMap[proci]
487  //);
488  }
489  }
490 }
491 
492 
494 Foam::distributedTriSurfaceMesh::distributeSegments
495 (
496  const pointField& start,
497  const pointField& end,
498 
499  List<segment>& allSegments,
500  labelList& allSegmentMap
501 ) const
502 {
503  // Determine send map
504  // ~~~~~~~~~~~~~~~~~~
505 
506  labelListList sendMap(Pstream::nProcs());
507 
508  {
509  // Since intersection test is quite expensive compared to memory
510  // allocation we use DynamicList to immediately store the segment
511  // in the correct bin.
512 
513  // Segments to test
514  DynamicList<segment> dynAllSegments(start.size());
515  // Original index of segment
516  DynamicList<label> dynAllSegmentMap(start.size());
517  // Per processor indices into allSegments to send
518  List<DynamicList<label>> dynSendMap(Pstream::nProcs());
519 
520  // Pre-size
521  forAll(dynSendMap, proci)
522  {
523  dynSendMap[proci].reserve
524  (
525  (proci == Pstream::myProcNo())
526  ? start.size()
527  : start.size()/Pstream::nProcs()
528  );
529  }
530 
531  forAll(start, segmenti)
532  {
533  distributeSegment
534  (
535  segmenti,
536  start[segmenti],
537  end[segmenti],
538 
539  dynAllSegments,
540  dynAllSegmentMap,
541  dynSendMap
542  );
543  }
544 
545  // Convert dynamicList to labelList
546  sendMap.setSize(Pstream::nProcs());
547  forAll(sendMap, proci)
548  {
549  dynSendMap[proci].shrink();
550  sendMap[proci].transfer(dynSendMap[proci]);
551  }
552 
553  allSegments.transfer(dynAllSegments);
554  allSegmentMap.transfer(dynAllSegmentMap);
555  }
556 
557  return autoPtr<mapDistribute>::New(std::move(sendMap));
558 }
559 
560 
561 void Foam::distributedTriSurfaceMesh::findLine
562 (
563  const bool nearestIntersection,
564  const pointField& start,
565  const pointField& end,
566  List<pointIndexHit>& info
567 ) const
568 {
569  if (debug)
570  {
571  Pout<< "distributedTriSurfaceMesh::findLine :"
572  << " intersecting with "
573  << start.size() << " rays" << endl;
574  }
575  addProfiling(findLine, "distributedTriSurfaceMesh::findLine");
576 
577  const indexedOctree<treeDataTriSurface>& octree = tree();
578 
579  // Initialise
580  info.setSize(start.size());
581  forAll(info, i)
582  {
583  info[i].setMiss();
584  }
585 
586  // Important:force synchronised construction of indexing
587  const globalIndex& triIndexer = globalTris();
588 
589 
590  // Do any local queries
591  // ~~~~~~~~~~~~~~~~~~~~
592 
593  label nLocal = 0;
594 
595  forAll(start, i)
596  {
597  if (isLocal(procBb_[Pstream::myProcNo()], start[i], end[i]))
598  {
599  if (nearestIntersection)
600  {
601  info[i] = octree.findLine(start[i], end[i]);
602  }
603  else
604  {
605  info[i] = octree.findLineAny(start[i], end[i]);
606  }
607 
608  if (info[i].hit())
609  {
610  info[i].setIndex(triIndexer.toGlobal(info[i].index()));
611  }
612  nLocal++;
613  }
614  }
615 
616 
617  if
618  (
619  returnReduce(nLocal, sumOp<label>())
620  < returnReduce(start.size(), sumOp<label>())
621  )
622  {
623  // Not all can be resolved locally. Build segments and map,
624  // send over segments, do intersections, send back and merge.
625 
626 
627  // Construct queries (segments)
628  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
629 
630  // Segments to test
631  List<segment> allSegments(start.size());
632  // Original index of segment
633  labelList allSegmentMap(start.size());
634 
635  const autoPtr<mapDistribute> mapPtr
636  (
637  distributeSegments
638  (
639  start,
640  end,
641  allSegments,
642  allSegmentMap
643  )
644  );
645  const mapDistribute& map = mapPtr();
646 
647  label nOldAllSegments = allSegments.size();
648 
649 
650  // Exchange the segments
651  // ~~~~~~~~~~~~~~~~~~~~~
652 
653  map.distribute(allSegments);
654 
655 
656  // Do tests i need to do
657  // ~~~~~~~~~~~~~~~~~~~~~
658 
659  // Intersections
660  List<pointIndexHit> intersections(allSegments.size());
661 
662  forAll(allSegments, i)
663  {
664  if (nearestIntersection)
665  {
666  intersections[i] = octree.findLine
667  (
668  allSegments[i].first(),
669  allSegments[i].second()
670  );
671  }
672  else
673  {
674  intersections[i] = octree.findLineAny
675  (
676  allSegments[i].first(),
677  allSegments[i].second()
678  );
679  }
680 
681  // Convert triangle index to global numbering
682  if (intersections[i].hit())
683  {
684  intersections[i].setIndex
685  (
686  triIndexer.toGlobal(intersections[i].index())
687  );
688  }
689  }
690 
691 
692  // Exchange the intersections (opposite to segments)
693  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
694 
695  map.reverseDistribute(nOldAllSegments, intersections);
696 
697 
698  // Extract the hits
699  // ~~~~~~~~~~~~~~~~
700 
701  forAll(intersections, i)
702  {
703  const pointIndexHit& allInfo = intersections[i];
704  label segmenti = allSegmentMap[i];
705  pointIndexHit& hitInfo = info[segmenti];
706 
707  if (allInfo.hit())
708  {
709  if (!hitInfo.hit())
710  {
711  // No intersection yet so take this one
712  hitInfo = allInfo;
713  }
714  else if (nearestIntersection)
715  {
716  // Nearest intersection
717  if
718  (
719  magSqr(allInfo.hitPoint()-start[segmenti])
720  < magSqr(hitInfo.hitPoint()-start[segmenti])
721  )
722  {
723  hitInfo = allInfo;
724  }
725  }
726  }
727  }
728  }
729 }
730 
731 
732 void Foam::distributedTriSurfaceMesh::convertTriIndices
733 (
734  List<pointIndexHit>& info
735 ) const
736 {
737  // Important:force synchronised construction of indexing
738  const globalIndex& triIndexer = globalTris();
739 
740  for (pointIndexHit& pi : info)
741  {
742  if (pi.hit())
743  {
744  pi.setIndex(triIndexer.toGlobal(pi.index()));
745  }
746  }
747 }
748 
749 
750 // Exchanges indices to the processor they come from.
751 // - calculates exchange map
752 // - uses map to calculate local triangle index
754 Foam::distributedTriSurfaceMesh::calcLocalQueries
755 (
756  const List<pointIndexHit>& info,
757  labelList& triangleIndex
758 ) const
759 {
760  // Note: does not filter duplicate queries so a triangle that gets requested
761  // from more than one processor also get local queried more than
762  // once.
763 
764  triangleIndex.setSize(info.size());
765 
766  const globalIndex& triIndexer = globalTris();
767 
768 
769  // Determine send map
770  // ~~~~~~~~~~~~~~~~~~
771 
772  // Since determining which processor the query should go to is
773  // cheap we do a multi-pass algorithm to save some memory temporarily.
774 
775  // 1. Count
776  labelList nSend(Pstream::nProcs(), Zero);
777 
778  forAll(info, i)
779  {
780  if (info[i].hit())
781  {
782  label proci = triIndexer.whichProcID(info[i].index());
783  nSend[proci]++;
784  }
785  }
786 
787  // 2. Size sendMap
788  labelListList sendMap(Pstream::nProcs());
789  forAll(nSend, proci)
790  {
791  sendMap[proci].setSize(nSend[proci]);
792  nSend[proci] = 0;
793  }
794 
795  // 3. Fill sendMap
796  forAll(info, i)
797  {
798  if (info[i].hit())
799  {
800  label proci = triIndexer.whichProcID(info[i].index());
801  triangleIndex[i] = triIndexer.toLocal(proci, info[i].index());
802  sendMap[proci][nSend[proci]++] = i;
803  }
804  else
805  {
806  triangleIndex[i] = -1;
807  }
808  }
809 
810 
811  // Pack into distribution map
812  // ~~~~~~~~~~~~~~~~~~~~~~~~~~
813 
814  autoPtr<mapDistribute> mapPtr(new mapDistribute(std::move(sendMap)));
815 
816 
817  // Send over queries
818  // ~~~~~~~~~~~~~~~~~
819 
820  mapPtr().distribute(triangleIndex);
821 
822  return mapPtr;
823 }
824 
825 
826 bool Foam::distributedTriSurfaceMesh::contains
827 (
828  const List<treeBoundBox>& bbs,
829  const point& sample
830 ) const
831 {
832  forAll(bbs, bbi)
833  {
834  if (bbs[bbi].contains(sample))
835  {
836  return true;
837  }
838  }
839  return false;
840 }
841 
842 
844 Foam::distributedTriSurfaceMesh::findBestProcs
845 (
846  const point& centre,
847  const scalar radiusSqr,
848  boolList& procContains,
849  boolList& procOverlaps,
850  label& minProci
851 ) const
852 {
853  // Find processors:
854  // - that contain the centre
855  // - or overlap the search sphere
856 
857  procContains.setSize(Pstream::nProcs());
858  procContains = false;
859 
860  procOverlaps.setSize(Pstream::nProcs());
861  procOverlaps = false;
862 
863  minProci = -1;
864 
865  scalar minDistSqr = radiusSqr;
866 
867  label nContain = 0;
868  forAll(procBb_, proci)
869  {
870  const List<treeBoundBox>& bbs = procBb_[proci];
871 
872  forAll(bbs, bbi)
873  {
874  if (bbs[bbi].contains(centre))
875  {
876  // We found a bb that contains the centre. There must be
877  // a triangle inside (since otherwise the bb would never
878  // have been created).
879  if (!procContains[proci])
880  {
881  procContains[proci] = true;
882  nContain++;
883  }
884  // Minimum search distance to find the triangle
885  point near, far;
886  bbs[bbi].calcExtremities(centre, near, far);
887  minDistSqr = min(minDistSqr, magSqr(centre-far));
888  }
889  }
890  }
891 
892  if (nContain > 0)
893  {
894  return Tuple2<label, scalar>(nContain, minDistSqr);
895  }
896  else
897  {
898  scalar maxDistSqr = radiusSqr;
899 
900  // Pass 1: find box with nearest minimum distance. Store its maximum
901  // extent as well. Since box will always contain a triangle
902  // this guarantees at least one hit.
903  forAll(procBb_, proci)
904  {
905  const List<treeBoundBox>& bbs = procBb_[proci];
906 
907  forAll(bbs, bbi)
908  {
909  if (bbs[bbi].overlaps(centre, radiusSqr))
910  {
911  point near, far;
912  bbs[bbi].calcExtremities(centre, near, far);
913 
914  scalar d2 = magSqr(centre-near);
915  if (d2 < minDistSqr)
916  {
917  minDistSqr = d2;
918  maxDistSqr = min(radiusSqr, magSqr(centre-far));
919  minProci = proci;
920  }
921  }
922  }
923  }
924 
925  label nOverlap = 0;
926  if (minProci >= 0)
927  {
928  // Pass 2. Find all bb in range minDistSqr..maxDistSqr
929 
930  procOverlaps[minProci] = true;
931  nOverlap++;
932 
933  forAll(procBb_, proci)
934  {
935  if (proci != minProci)
936  {
937  const List<treeBoundBox>& bbs = procBb_[proci];
938  forAll(bbs, bbi)
939  {
940  if (bbs[bbi].overlaps(centre, maxDistSqr))
941  {
942  procOverlaps[proci] = true;
943  nOverlap++;
944  break;
945  }
946  }
947  }
948  }
949  }
950  return Tuple2<label, scalar>(nOverlap, maxDistSqr);
951  }
952 }
953 
954 
955 Foam::label Foam::distributedTriSurfaceMesh::calcOverlappingProcs
956 (
957  const point& centre,
958  const scalar radiusSqr,
959  boolList& overlaps
960 ) const
961 {
962  overlaps = false;
963  label nOverlaps = 0;
964 
965  forAll(procBb_, proci)
966  {
967  const List<treeBoundBox>& bbs = procBb_[proci];
968 
969  forAll(bbs, bbi)
970  {
971  if (bbs[bbi].overlaps(centre, radiusSqr))
972  {
973  overlaps[proci] = true;
974  nOverlaps++;
975  break;
976  }
977  }
978  }
979  return nOverlaps;
980 }
981 
982 
983 // Generate queries for parallel distance calculation
984 // - calculates exchange map
985 // - uses map to exchange points and radius
987 Foam::distributedTriSurfaceMesh::calcLocalQueries
988 (
989  const bool includeLocalProcessor,
990  const pointField& centres,
991  const scalarField& radiusSqr,
992 
993  pointField& allCentres,
994  scalarField& allRadiusSqr,
995  labelList& allSegmentMap
996 ) const
997 {
998  // Determine queries
999  // ~~~~~~~~~~~~~~~~~
1000 
1001  labelListList sendMap(Pstream::nProcs());
1002 
1003  {
1004  // Queries
1005  DynamicList<point> dynAllCentres(centres.size());
1006  DynamicList<scalar> dynAllRadiusSqr(centres.size());
1007  // Original index of segment
1008  DynamicList<label> dynAllSegmentMap(centres.size());
1009  // Per processor indices into allSegments to send
1010  List<DynamicList<label>> dynSendMap(Pstream::nProcs());
1011 
1012  // Pre-size
1013  forAll(dynSendMap, proci)
1014  {
1015  dynSendMap[proci].reserve
1016  (
1017  (proci == Pstream::myProcNo())
1018  ? centres.size()
1019  : centres.size()/Pstream::nProcs()
1020  );
1021  }
1022 
1023  // Work array - whether processor bb overlaps the bounding sphere.
1024  boolList procBbOverlaps(Pstream::nProcs());
1025 
1026  forAll(centres, centrei)
1027  {
1028  // Find the processor this sample+radius overlaps.
1029  calcOverlappingProcs
1030  (
1031  centres[centrei],
1032  radiusSqr[centrei],
1033  procBbOverlaps
1034  );
1035 
1036  forAll(procBbOverlaps, proci)
1037  {
1038  if
1039  (
1040  procBbOverlaps[proci]
1041  && (
1042  includeLocalProcessor
1043  || proci != Pstream::myProcNo()
1044  )
1045  )
1046  {
1047  dynSendMap[proci].append(dynAllCentres.size());
1048  dynAllSegmentMap.append(centrei);
1049  dynAllCentres.append(centres[centrei]);
1050  dynAllRadiusSqr.append(radiusSqr[centrei]);
1051  }
1052  }
1053  }
1054 
1055  // Convert dynamicList to labelList
1056  sendMap.setSize(Pstream::nProcs());
1057  forAll(sendMap, proci)
1058  {
1059  dynSendMap[proci].shrink();
1060  sendMap[proci].transfer(dynSendMap[proci]);
1061  }
1062 
1063  allCentres.transfer(dynAllCentres);
1064  allRadiusSqr.transfer(dynAllRadiusSqr);
1065  allSegmentMap.transfer(dynAllSegmentMap);
1066  }
1067 
1068  return autoPtr<mapDistribute>::New(std::move(sendMap));
1069 }
1070 
1071 
1072 Foam::volumeType Foam::distributedTriSurfaceMesh::edgeSide
1073 (
1074  const point& sample,
1075  const point& nearestPoint,
1076  const label face0,
1077  const label face1
1078 ) const
1079 {
1080  const triSurface& surf = static_cast<const triSurface&>(*this);
1081  const pointField& points = surf.points();
1082 
1083  // Compare to bisector. This is actually correct since edge is
1084  // nearest so there is a knife-edge.
1085 
1086  //const vectorField& faceNormals = surf.faceNormals();
1087  //vector n = faceNormals[face0] + faceNormals[face1];
1088  vector n = surf[face0].unitNormal(points)+surf[face1].unitNormal(points);
1089 
1090  if (((sample - nearestPoint) & n) > 0)
1091  {
1092  return volumeType::OUTSIDE;
1093  }
1094  else
1095  {
1096  return volumeType::INSIDE;
1097  }
1098 }
1099 
1100 
1101 Foam::label Foam::distributedTriSurfaceMesh::findOtherFace
1102 (
1103  const labelListList& pointFaces,
1104  const label nearFacei,
1105  const label nearLabel
1106 ) const
1107 {
1108  const triSurface& surf = static_cast<const triSurface&>(*this);
1109  const triSurface::face_type& nearF = surf[nearFacei];
1110 
1111  const edge e(nearF[nearLabel], nearF[nearF.fcIndex(nearLabel)]);
1112 
1113  const labelList& pFaces = pointFaces[e[0]];
1114  for (const label facei : pFaces)
1115  {
1116  if (facei != nearFacei)
1117  {
1118  int dir = surf[facei].edgeDirection(e);
1119  if (dir != 0)
1120  {
1121  return facei;
1122  }
1123  }
1124  }
1125  return -1;
1126 }
1127 
1128 
1129 void Foam::distributedTriSurfaceMesh::calcFaceFaces
1130 (
1131  const triSurface& s,
1132  const labelListList& pointFaces,
1133  labelListList& faceFaces
1134 )
1135 {
1136  faceFaces.setSize(s.size());
1137 
1138  DynamicList<label> nbrs;
1139 
1140  forAll(faceFaces, facei)
1141  {
1142  const labelledTri& f = s[facei];
1143 
1144  nbrs.reserve(f.size());
1145  nbrs.clear();
1146 
1147  forAll(f, fp)
1148  {
1149  const edge e(f[fp], f[f.fcIndex(fp)]);
1150  const labelList& pFaces = pointFaces[f[fp]];
1151  for (const label otherFacei : pFaces)
1152  {
1153  if (otherFacei != facei)
1154  {
1155  if (s[otherFacei].edgeDirection(e) != 0)
1156  {
1157  if (!nbrs.find(otherFacei))
1158  {
1159  nbrs.append(otherFacei);
1160  }
1161  }
1162  }
1163  }
1164  }
1165  faceFaces[facei] = std::move(nbrs);
1166  }
1167 }
1168 
1169 
1170 void Foam::distributedTriSurfaceMesh::surfaceSide
1171 (
1172  const pointField& samples,
1173  const List<pointIndexHit>& nearestInfo,
1174  List<volumeType>& volType
1175 ) const
1176 {
1177  if (debug)
1178  {
1179  Pout<< "distributedTriSurfaceMesh::surfaceSide :"
1180  << " finding surface side given points on surface for "
1181  << samples.size() << " samples" << endl;
1182  }
1183 
1184  // Use global index to send local tri and nearest back to originating
1185  // processor
1186 
1187  labelList triangleIndex(nearestInfo.size());
1188  autoPtr<mapDistribute> mapPtr
1189  (
1190  calcLocalQueries
1191  (
1192  nearestInfo,
1193  triangleIndex
1194  )
1195  );
1196  const mapDistribute& map = mapPtr();
1197 
1198  // Send over samples
1199  pointField localSamples(samples);
1200  map.distribute(localSamples);
1201 
1202 
1203  // Do my tests
1204  // ~~~~~~~~~~~
1205 
1206  volType.setSize(triangleIndex.size());
1207  volType = volumeType::UNKNOWN;
1208 
1209  const triSurface& surf = static_cast<const triSurface&>(*this);
1210  const pointField& points = surf.points();
1211  {
1212  //const labelListList& pointFaces = surf.pointFaces();
1213  // Construct pointFaces. Let's hope surface has compact point
1214  // numbering ...
1215  labelListList pointFaces;
1216  invertManyToMany(points.size(), surf, pointFaces);
1217 
1218  EdgeMap<labelPair> edgeToFaces;
1219 
1220  forAll(triangleIndex, i)
1221  {
1222  const label facei = triangleIndex[i];
1223  const triSurface::face_type& f = surf[facei];
1224  const point& sample = localSamples[i];
1225 
1226  // Find where point is on face
1227  label nearType, nearLabel;
1228  pointHit pHit =
1229  f.nearestPointClassify(sample, points, nearType, nearLabel);
1230 
1231  const point& nearestPoint(pHit.rawPoint());
1232 
1233  if (nearType == triPointRef::NONE)
1234  {
1235  const vector sampleNearestVec = (sample - nearestPoint);
1236 
1237  // Nearest to face interior. Use faceNormal to determine side
1238  //scalar c = sampleNearestVec & surf.faceNormals()[facei];
1239  scalar c = sampleNearestVec & surf[facei].areaNormal(points);
1240 
1241  if (c > 0)
1242  {
1243  volType[i] = volumeType::OUTSIDE;
1244  }
1245  else
1246  {
1247  volType[i] = volumeType::INSIDE;
1248  }
1249  }
1250  else if (nearType == triPointRef::EDGE)
1251  {
1252  // Nearest to edge nearLabel. Note that this can only be a
1253  // knife-edge
1254  // situation since otherwise the nearest point could never be
1255  // the edge.
1256 
1257  label otherFacei = findOtherFace(pointFaces, facei, nearLabel);
1258  if (otherFacei != -1)
1259  {
1260  volType[i] =
1261  edgeSide(sample, nearestPoint, facei, otherFacei);
1262  }
1263  else
1264  {
1265  // Open edge. Leave volume type unknown
1266  }
1267  }
1268  else
1269  {
1270  // Nearest to point. Could use pointNormal here but is not
1271  // correct.
1272  // Instead determine which edge using point is nearest and
1273  // use test above (nearType == triPointRef::EDGE).
1274 
1275  const label pointi = f[nearLabel];
1276  const labelList& pFaces = pointFaces[pointi];
1277  const vector sampleNearestVec = (sample - nearestPoint);
1278 
1279  // Loop over all faces. Check if have both edge faces. Do
1280  // test.
1281  edgeToFaces.clear();
1282 
1283  scalar maxCosAngle = -GREAT;
1284  labelPair maxEdgeFaces(-1, -1);
1285 
1286  for (const label facei : pFaces)
1287  {
1288  const triSurface::face_type& f = surf[facei];
1289 
1290  label fp = f.find(pointi);
1291  label p1 = f[f.fcIndex(fp)];
1292  label pMin1 = f[f.rcIndex(fp)];
1293 
1294  Pair<edge> edges
1295  (
1296  edge(pointi, p1),
1297  edge(pointi, pMin1)
1298  );
1299 
1300  // Check edge fp-to-fp+1 and fp-1
1301  // determine distance/angle to nearPoint
1302  for (const edge& e : edges)
1303  {
1304  auto iter = edgeToFaces.find(e);
1305  if (iter.found())
1306  {
1307  if (iter().second() == -1)
1308  {
1309  // Found second face. Now we have edge+faces
1310  iter().second() = facei;
1311 
1312  vector eVec(e.vec(points));
1313  scalar magEVec = mag(eVec);
1314 
1315  if (magEVec > VSMALL)
1316  {
1317  eVec /= magEVec;
1318 
1319  // Determine edge most in direction of
1320  // sample
1321  scalar cosAngle(sampleNearestVec&eVec);
1322  if (cosAngle > maxCosAngle)
1323  {
1324  maxCosAngle = cosAngle;
1325  maxEdgeFaces = iter();
1326  }
1327  }
1328  }
1329  else
1330  {
1331  FatalErrorInFunction << "Not closed"
1332  << exit(FatalError);
1333  }
1334  }
1335  else
1336  {
1337  edgeToFaces.insert(e, labelPair(facei, -1));
1338  }
1339  }
1340  }
1341 
1342 
1343  // Check that surface is closed
1344  bool closed = true;
1345  for (auto iter : edgeToFaces)
1346  {
1347  if (iter[0] == -1 || iter[1] == -1)
1348  {
1349  closed = false;
1350  break;
1351  }
1352  }
1353  if (closed)
1354  {
1355  volType[i] = edgeSide
1356  (
1357  sample,
1358  nearestPoint,
1359  maxEdgeFaces[0],
1360  maxEdgeFaces[1]
1361  );
1362  }
1363  }
1364  }
1365  }
1366 
1367 
1368  // Send back results
1369  // ~~~~~~~~~~~~~~~~~
1370 
1371  // Note that we make sure that if multiple processors hold data only
1372  // the one with inside/outside wins. (though this should already be
1373  // handled by the fact we have a unique nearest triangle so we only
1374  // send the volume-query to a single processor)
1375 
1376 
1377  //forAll(localSamples, i)
1378  //{
1379  // Pout<< "surfaceSide : for localSample:" << localSamples[i]
1380  // << " found volType:" << volumeType::names[volType[i]]
1381  // << endl;
1382  //}
1383 
1384  const volumeType zero(volumeType::UNKNOWN);
1386  (
1388  List<labelPair>(0),
1389  nearestInfo.size(),
1390  map.constructMap(),
1391  map.constructHasFlip(),
1392  map.subMap(),
1393  map.subHasFlip(),
1394  volType,
1395  zero,
1396  volumeCombineOp(),
1397  noOp(), // no flipping
1399  map.comm()
1400  );
1401 
1402  if (debug)
1403  {
1404  Pout<< "distributedTriSurfaceMesh::surfaceSide :"
1405  << " finished finding surface side given points on surface for "
1406  << samples.size() << " samples" << endl;
1407  }
1408 }
1409 
1410 
1411 void Foam::distributedTriSurfaceMesh::collectLeafMids
1412 (
1413  const label nodeI,
1414  DynamicField<point>& midPoints
1415 ) const
1416 {
1417  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1418 
1419  for (direction octant = 0; octant < nod.subNodes_.size(); octant++)
1420  {
1421  const labelBits index = nod.subNodes_[octant];
1422 
1424  {
1425  // tree node. Recurse.
1426  collectLeafMids
1427  (
1429  midPoints
1430  );
1431  }
1433  {}
1434  else
1435  {
1436  // No data in this octant. Set type for octant acc. to the mid
1437  // of its bounding box.
1438  const treeBoundBox subBb = nod.bb_.subBbox(octant);
1439  midPoints.append(subBb.midpoint());
1440  }
1441  }
1442 }
1443 
1444 
1445 Foam::volumeType Foam::distributedTriSurfaceMesh::calcVolumeType
1446 (
1447  const List<volumeType>& midPointTypes,
1448  label& midPointi,
1449  PackedList<2>& nodeTypes,
1450  const label nodeI
1451 ) const
1452 {
1453  // Pre-calculates wherever possible the volume status per node/subnode.
1454  // Recurses to determine status of lowest level boxes. Level above is
1455  // combination of octants below.
1456 
1457  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1458 
1459  volumeType myType = volumeType::UNKNOWN;
1460 
1461  for (direction octant = 0; octant < nod.subNodes_.size(); octant++)
1462  {
1463  volumeType subType;
1464 
1465  const labelBits index = nod.subNodes_[octant];
1466 
1468  {
1469  // tree node. Recurse.
1470  subType = calcVolumeType
1471  (
1472  midPointTypes,
1473  midPointi,
1474  nodeTypes,
1476  );
1477  }
1479  {
1480  // Contents. Depending on position in box might be on either
1481  // side.
1482  subType = volumeType::MIXED;
1483  }
1484  else
1485  {
1486  // No data in this octant. Set type for octant acc. to the mid
1487  // of its bounding box.
1488  //Pout<< " for leaf at bb:" << nod.bb_.subBbox(octant)
1489  // << " node:" << nodeI
1490  // << " octant:" << octant
1491  // << " caching volType to:" << midPointTypes[midPointi] << endl;
1492  subType = midPointTypes[midPointi++];
1493  }
1494 
1495  // Store octant type
1496  nodeTypes.set((nodeI<<3)+octant, subType);
1497 
1498  // Combine sub node types into type for treeNode. Result is 'mixed' if
1499  // types differ among subnodes.
1500  if (myType == volumeType::UNKNOWN)
1501  {
1502  myType = subType;
1503  }
1504  else if (subType != myType)
1505  {
1506  myType = volumeType::MIXED;
1507  }
1508  }
1509  return myType;
1510 }
1511 
1512 
1513 Foam::volumeType Foam::distributedTriSurfaceMesh::cachedVolumeType
1514 (
1515  const label nodeI,
1516  const point& sample
1517 ) const
1518 {
1519  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1520 
1521  direction octant = nod.bb_.subOctant(sample);
1522 
1523  volumeType octantType = volumeType::type
1524  (
1525  tree().nodeTypes().get((nodeI<<3)+octant)
1526  );
1527 
1528  if (octantType == volumeType::INSIDE)
1529  {
1530  return octantType;
1531  }
1532  else if (octantType == volumeType::OUTSIDE)
1533  {
1534  return octantType;
1535  }
1536  else if (octantType == volumeType::UNKNOWN)
1537  {
1538  // Can happen for e.g. non-manifold surfaces.
1539  return octantType;
1540  }
1541  else if (octantType == volumeType::MIXED)
1542  {
1543  labelBits index = nod.subNodes_[octant];
1544 
1546  {
1547  // Recurse
1548  volumeType subType = cachedVolumeType
1549  (
1551  sample
1552  );
1553 
1554  return subType;
1555  }
1557  {
1558  // Content.
1559  return volumeType::UNKNOWN;
1560  }
1561  else
1562  {
1563  // Empty node. Cannot have 'mixed' as its type since not divided
1564  // up and has no items inside it.
1566  << "Sample:" << sample << " node:" << nodeI
1567  << " with bb:" << nod.bb_ << nl
1568  << "Empty subnode has invalid volume type MIXED."
1569  << abort(FatalError);
1570 
1571  return volumeType::UNKNOWN;
1572  }
1573  }
1574  else
1575  {
1577  << "Sample:" << sample << " at node:" << nodeI
1578  << " octant:" << octant
1579  << " with bb:" << nod.bb_.subBbox(octant) << nl
1580  << "Node has invalid volume type " << octantType
1581  << abort(FatalError);
1582 
1583  return volumeType::UNKNOWN;
1584  }
1585 }
1586 
1587 
1588 // Find bounding boxes that guarantee a more or less uniform distribution
1589 // of triangles. Decomposition in here is only used to get the bounding
1590 // boxes, actual decomposition is done later on.
1591 // Returns a per processor a list of bounding boxes that most accurately
1592 // describe the shape. For now just a single bounding box per processor but
1593 // optimisation might be to determine a better fitting shape.
1595 Foam::distributedTriSurfaceMesh::independentlyDistributedBbs
1596 (
1597  const triSurface& s
1598 )
1599 {
1600  addProfiling
1601  (
1602  distribute,
1603  "distributedTriSurfaceMesh::independentlyDistributedBbs"
1604  );
1605 
1606  if (!decomposer_)
1607  {
1608  // Use singleton decomposeParDict. Cannot use decompositionModel
1609  // here since we've only got Time and not a mesh.
1610 
1611  const auto* dictPtr =
1612  searchableSurface::time().findObject<IOdictionary>
1613  (
1614  // == decompositionModel::canonicalName
1615  "decomposeParDict"
1616  );
1617 
1618  if (dictPtr)
1619  {
1620  decomposer_ = decompositionMethod::New(*dictPtr);
1621  }
1622  else
1623  {
1624  if (!decomposeParDict_)
1625  {
1626  decomposeParDict_.reset
1627  (
1628  new IOdictionary
1629  (
1630  IOobject
1631  (
1632  // == decompositionModel::canonicalName
1633  "decomposeParDict",
1638  )
1639  )
1640  );
1641  }
1642  decomposer_ = decompositionMethod::New(*decomposeParDict_);
1643  }
1644  }
1645 
1646 
1647  // Initialise to inverted box
1648  List<List<treeBoundBox>> bbs(Pstream::nProcs());
1649  forAll(bbs, proci)
1650  {
1651  bbs[proci].setSize(1, treeBoundBox(boundBox::invertedBox));
1652  }
1653 
1654 
1655  const globalIndex& triIndexer = globalTris();
1656 
1657  bool masterOnly;
1658  {
1659  masterOnly = true;
1660  for (const int proci : Pstream::subProcs())
1661  {
1662  if (triIndexer.localSize(proci) != 0)
1663  {
1664  masterOnly = false;
1665  break;
1666  }
1667  }
1668  }
1669 
1670  if (masterOnly)
1671  {
1672  if (debug)
1673  {
1674  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1675  << " determining master-only decomposition for " << s.size()
1676  << " centroids for " << searchableSurface::name() << endl;
1677  }
1678 
1679  // Triangle centres
1680  pointField triCentres(s.size());
1681  forAll(s, trii)
1682  {
1683  triCentres[trii] = s[trii].centre(s.points());
1684  }
1685 
1686  labelList distribution;
1687  if (!isA<geomDecomp>(decomposer_()))
1688  {
1689  // Use connectivity
1690  labelListList pointFaces;
1691  invertManyToMany(s.points().size(), s, pointFaces);
1692  labelListList faceFaces(s.size());
1693  calcFaceFaces(s, pointFaces, faceFaces);
1694 
1695  // Do the actual decomposition
1696  const bool oldParRun = UPstream::parRun(false);
1697  distribution = decomposer_().decompose(faceFaces, triCentres);
1698  UPstream::parRun(oldParRun); // Restore parallel state
1699  }
1700  else
1701  {
1702  // Do the actual decomposition
1703  distribution = decomposer_().decompose(triCentres);
1704  }
1705 
1706  if (debug)
1707  {
1708  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1709  << " determining processor bounding boxes" << endl;
1710  }
1711 
1712  // Find bounding box for all triangles on new distribution.
1713  forAll(s, trii)
1714  {
1715  treeBoundBox& bb = bbs[distribution[trii]][0];
1716  bb.add(s.points(), s[trii]);
1717  }
1718 
1719  // Now combine for all processors and convert to correct format.
1720  forAll(bbs, proci)
1721  {
1722  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1723  Pstream::listCombineScatter(bbs[proci]);
1724  }
1725  }
1726  else if (distType_ == DISTRIBUTED)
1727  {
1728  // Fully distributed decomposition. Does not filter duplicate
1729  // triangles.
1730  if (!decomposer_().parallelAware())
1731  {
1733  << "The decomposition method " << decomposer_().typeName
1734  << " does not decompose in parallel."
1735  << " Please choose one that does." << exit(FatalError);
1736  }
1737 
1738  if (debug)
1739  {
1740  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1741  << " determining decomposition for " << s.size()
1742  << " centroids" << endl;
1743  }
1744 
1745  // Triangle centres
1746  pointField triCentres(s.size());
1747  forAll(s, trii)
1748  {
1749  triCentres[trii] = s[trii].centre(s.points());
1750  }
1751 
1752  labelList distribution = decomposer_().decompose(triCentres);
1753 
1754  if (debug)
1755  {
1756  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1757  << " determining processor bounding boxes for "
1758  << searchableSurface::name() << endl;
1759  }
1760 
1761  // Find bounding box for all triangles on new distribution.
1762  forAll(s, trii)
1763  {
1764  treeBoundBox& bb = bbs[distribution[trii]][0];
1765  bb.add(s.points(), s[trii]);
1766  }
1767 
1768  // Now combine for all processors and convert to correct format.
1769  forAll(bbs, proci)
1770  {
1771  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1772  Pstream::listCombineScatter(bbs[proci]);
1773  }
1774  }
1775 // //// Tbd. initial duplicate filtering of border points only
1776 // if (distType_ == DISTRIBUTED)
1777 // {
1778 // // Fully distributed decomposition. Does not filter duplicate
1779 // // triangles.
1780 // if (!decomposer_().parallelAware())
1781 // {
1782 // FatalErrorInFunction
1783 // << "The decomposition method " << decomposer_().typeName
1784 // << " does not decompose in parallel."
1785 // << " Please choose one that does." << exit(FatalError);
1786 // }
1787 //
1788 // if (debug)
1789 // {
1790 // Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1791 // << " determining decomposition for " << s.size()
1792 // << " centroids" << endl;
1793 // }
1794 // const pointField& points = s.points();
1795 //
1796 // pointField triCentres(s.size());
1797 // forAll(s, trii)
1798 // {
1799 // triCentres[trii] = s[trii].centre(points);
1800 // }
1801 //
1802 // // Collect all triangles not fully inside the current bb
1803 // DynamicList<label> borderTris(s.size()/Pstream::nProcs());
1804 //
1805 // const List<treeBoundBox>& myBbs = procBb_[Pstream::myProcNo];
1806 //
1807 // boolList includedFace;
1808 // overlappingSurface(s, myBbs, includedFace);
1809 // boolList internalOrBorderFace(includedFace);
1810 // forAll(s, trii)
1811 // {
1812 // if (includedFace[trii])
1813 // {
1814 // // Triangle is either inside or part-inside. Exclude fully
1815 // // inside triangles.
1816 // const labelledTri& f = s[trii];
1817 // const point& p0 = points[f[0]];
1818 // const point& p1 = points[f[1]];
1819 // const point& p2 = points[f[2]];
1820 // if
1821 // (
1822 // !contains(myBbs, p0)
1823 // || !contains(myBbs, p1)
1824 // || !contains(myBbs, p2)
1825 // )
1826 // {
1827 // borderTris.append(trii);
1828 // }
1829 // }
1830 // }
1831 //
1832 // const label nBorderTris = borderTris.size();
1833 //
1834 // Pout<< "Have " << borderTris.size() << " border triangles out of "
1835 // << s.size() << endl;
1836 //
1837 // labelListList sendMap(Pstream::nProcs());
1838 // sendMap[0] = std::move(borderTris);
1839 //
1840 // const mapDistribute map(std::move(sendMap));
1841 //
1842 // // Gather all borderTris
1843 // //globalIndex globalBorderTris(borderTris.size());
1844 // //pointField globalBorderCentres(allCentres, borderTris);
1845 // //globalBorderTris.gather
1846 // //(
1847 // // UPstream::worldComm,
1848 // // UPstream::procID(Pstream::worldComm),
1849 // // globalBorderCentres
1850 // //);
1851 // pointField globalBorderCentres(allCentres);
1852 // map.distribute(globalBorderCentres);
1853 //
1854 // // Merge on master
1855 // labelList masterBorder(borderTris.size(), -1);
1856 // if (Pstream::master())
1857 // {
1858 // labelList allToMerged;
1859 // label nMerged = mergePoints
1860 // (
1861 // globalBorderCentres,
1862 // mergeDist_,
1863 // false, //const bool verbose,
1864 // allToMerged
1865 // // maybe bounds().mid() ?
1866 // );
1867 //
1868 // if (debug)
1869 // {
1870 // Pout<< "distributedTriSurfaceMesh::"
1871 // << "independentlyDistributedBbs :"
1872 // << " merged " << globalBorderCentres.size()
1873 // << " border centroids down to " << nMerged << endl;
1874 // }
1875 //
1876 // labelList mergedMaster(nMerged, -1);
1877 // isMaster.setSize(globalBorderCentres.size(), false);
1878 // forAll(allToMerged, i)
1879 // {
1880 // label mergedi = allToMerged[i];
1881 // if (mergedMaster[mergedi] == -1)
1882 // {
1883 // mergedMaster[mergedi] = i;
1884 // isMaster[i] = true;
1885 // }
1886 // }
1887 // forAll(allToMerged, i)
1888 // {
1889 // label mergedi = allToMerged[i];
1890 // masterBorder[i] = mergedMaster[mergedi];
1891 // }
1892 // }
1893 // //globalBorderTris.scatter
1894 // //(
1895 // // UPstream::worldComm,
1896 // // UPstream::procID(Pstream::worldComm),
1897 // // isMasterPoint
1898 // //);
1899 // //boolList isMasterBorder(s.size(), false);
1900 // //forAll(borderTris, i)
1901 // //{
1902 // // isMasterBorder[borderTris[i]] = isMasterPoint[i];
1903 // //}
1904 // map.reverseDistribute(s.size(), isMaster);
1905 //
1906 // // Collect all non-border or master-border points
1907 // DynamicList<label> triMap(s.size());
1908 // forAll(s, trii)
1909 // {
1910 // if (includedFace[trii])
1911 // {
1912 // // Triangle is either inside or part-inside. Exclude fully
1913 // // inside triangles.
1914 // const labelledTri& f = s[trii];
1915 // const point& p0 = points[f[0]];
1916 // const point& p1 = points[f[1]];
1917 // const point& p2 = points[f[2]];
1918 //
1919 // if
1920 // (
1921 // contains(myBbs, p0)
1922 // && contains(myBbs, p1)
1923 // && contains(myBbs, p2)
1924 // )
1925 // {
1926 // // Internal
1927 // triMap.append(trii);
1928 // }
1929 // else if (isMasterBorder[trii])
1930 // {
1931 // // Part overlapping and master of overlap
1932 // triMap.append(trii);
1933 // }
1934 // }
1935 // }
1936 //
1937 // pointField masterCentres(allCentres, triMap);
1938 //
1939 // // Do the actual decomposition
1940 // labelList masterDistribution(decomposer_().decompose(masterCentres));
1941 //
1942 // // Make map to get the decomposition from the master of each border
1943 // labelList borderGlobalMaster(borderTris.size());
1944 // forAll(borderTris, borderi)
1945 // {
1946 // borderGlobalMaster[borderi] = ..masterTri
1947 // }
1948 // mapDistribute map(globalBorderTris, borderGlobalMaster
1949 //
1950 // // Send decomposition
1951 //
1952 //
1953 // if (debug)
1954 // {
1955 // Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1956 // << " determining processor bounding boxes" << endl;
1957 // }
1958 //
1959 // // Find bounding box for all triangles on new distribution.
1960 // forAll(s, trii)
1961 // {
1962 // treeBoundBox& bb = bbs[distribution[trii]][0];
1963 // bb.add(s.points(), s[trii]);
1964 // }
1965 //
1966 // // Now combine for all processors and convert to correct format.
1967 // forAll(bbs, proci)
1968 // {
1969 // Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1970 // Pstream::listCombineScatter(bbs[proci]);
1971 // }
1972 // }
1973  else
1974  {
1975  // Master-only decomposition. Filters duplicate triangles so repeatable.
1976 
1977  if (debug)
1978  {
1979  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1980  << " collecting all centroids" << endl;
1981  }
1982 
1983  // Collect all triangle centres
1984  pointField allCentres(s.size());
1985  {
1986  forAll(s, trii)
1987  {
1988  allCentres[trii] = s[trii].centre(s.points());
1989  }
1990  globalTris().gather
1991  (
1994  allCentres
1995  );
1996  }
1997 
1998  // Determine local decomposition
1999  labelList distribution(s.size());
2000  {
2001  labelList allDistribution;
2002  if (Pstream::master())
2003  {
2004  labelList allToMerged;
2005  label nMerged = mergePoints
2006  (
2007  allCentres,
2008  mergeDist_,
2009  false, //const bool verbose,
2010  allToMerged
2011  // maybe bounds().mid() ?
2012  );
2013 
2014  if (debug)
2015  {
2016  Pout<< "distributedTriSurfaceMesh::"
2017  << "independentlyDistributedBbs :"
2018  << " merged " << allCentres.size()
2019  << " centroids down to " << nMerged << endl;
2020  }
2021 
2022  // Collect merged points
2023  pointField mergedPoints(nMerged);
2024  UIndirectList<point>(mergedPoints, allToMerged) = allCentres;
2025 
2026  // Decompose merged centres
2027  const bool oldParRun = UPstream::parRun(false);
2028  labelList mergedDist(decomposer_().decompose(mergedPoints));
2029  UPstream::parRun(oldParRun); // Restore parallel state
2030 
2031  // Convert to all
2032  allDistribution = UIndirectList<label>
2033  (
2034  mergedDist,
2035  allToMerged
2036  );
2037  }
2038 
2039  // Scatter back to processors
2040  globalTris().scatter
2041  (
2044  allDistribution,
2045  distribution
2046  );
2047  if (debug)
2048  {
2049  Pout<< "distributedTriSurfaceMesh::"
2050  << "independentlyDistributedBbs :"
2051  << " determined decomposition" << endl;
2052  }
2053  }
2054 
2055  // Find bounding box for all triangles on new distribution.
2056  if (debug)
2057  {
2058  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
2059  << " determining processor bounding boxes for "
2060  << searchableSurface::name() << endl;
2061  }
2062 
2063  forAll(s, trii)
2064  {
2065  treeBoundBox& bb = bbs[distribution[trii]][0];
2066  bb.add(s.points(), s[trii]);
2067  }
2068 
2069  // Now combine for all processors and convert to correct format.
2070  forAll(bbs, proci)
2071  {
2072  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
2073  Pstream::listCombineScatter(bbs[proci]);
2074  }
2075  }
2076  return bbs;
2077 }
2078 
2079 
2080 // Does any part of triangle overlap bb.
2081 bool Foam::distributedTriSurfaceMesh::overlaps
2082 (
2083  const List<treeBoundBox>& bbs,
2084  const point& p0,
2085  const point& p1,
2086  const point& p2
2087 )
2088 {
2089  treeBoundBox triBb(p0);
2090  triBb.add(p1);
2091  triBb.add(p2);
2092 
2093  forAll(bbs, bbi)
2094  {
2095  const treeBoundBox& bb = bbs[bbi];
2096 
2097  // Exact test of triangle intersecting bb
2098 
2099  // Quick rejection. If whole bounding box of tri is outside cubeBb then
2100  // there will be no intersection.
2101  if (bb.overlaps(triBb))
2102  {
2103  // Check if one or more triangle point inside
2104  if (bb.contains(p0) || bb.contains(p1) || bb.contains(p2))
2105  {
2106  // One or more points inside
2107  return true;
2108  }
2109 
2110  // Now we have the difficult case: all points are outside but
2111  // connecting edges might go through cube. Use fast intersection
2112  // of bounding box.
2113  bool intersect = triangleFuncs::intersectBb(p0, p1, p2, bb);
2114 
2115  if (intersect)
2116  {
2117  return true;
2118  }
2119  }
2120  }
2121  return false;
2122 }
2123 
2124 
2125 void Foam::distributedTriSurfaceMesh::subsetMeshMap
2126 (
2127  const triSurface& s,
2128  const boolList& include,
2129  const label nIncluded,
2130  labelList& newToOldPoints,
2131  labelList& oldToNewPoints,
2132  labelList& newToOldFaces
2133 )
2134 {
2135  newToOldFaces.setSize(nIncluded);
2136  newToOldPoints.setSize(s.points().size());
2137  oldToNewPoints.setSize(s.points().size());
2138  oldToNewPoints = -1;
2139  {
2140  label facei = 0;
2141  label pointi = 0;
2142 
2143  forAll(include, oldFacei)
2144  {
2145  if (include[oldFacei])
2146  {
2147  // Store new faces compact
2148  newToOldFaces[facei++] = oldFacei;
2149 
2150  // Renumber labels for face
2151  for (const label oldPointi : s[oldFacei])
2152  {
2153  if (oldToNewPoints[oldPointi] == -1)
2154  {
2155  oldToNewPoints[oldPointi] = pointi;
2156  newToOldPoints[pointi++] = oldPointi;
2157  }
2158  }
2159  }
2160  }
2161  newToOldPoints.setSize(pointi);
2162  }
2163 }
2164 
2165 
2166 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2167 (
2168  const triSurface& s,
2169  const labelList& newToOldPoints,
2170  const labelList& oldToNewPoints,
2171  const labelList& newToOldFaces
2172 )
2173 {
2174  // Extract points
2175  pointField newPoints(newToOldPoints.size());
2176  forAll(newToOldPoints, i)
2177  {
2178  newPoints[i] = s.points()[newToOldPoints[i]];
2179  }
2180  // Extract faces
2181  List<labelledTri> newTriangles(newToOldFaces.size());
2182 
2183  forAll(newToOldFaces, i)
2184  {
2185  // Get old vertex labels
2186  const labelledTri& tri = s[newToOldFaces[i]];
2187 
2188  newTriangles[i][0] = oldToNewPoints[tri[0]];
2189  newTriangles[i][1] = oldToNewPoints[tri[1]];
2190  newTriangles[i][2] = oldToNewPoints[tri[2]];
2191  newTriangles[i].region() = tri.region();
2192  }
2193 
2194  // Reuse storage.
2195  return triSurface(newTriangles, s.patches(), newPoints, true);
2196 }
2197 
2198 
2199 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2200 (
2201  const triSurface& s,
2202  const boolList& include,
2203  labelList& newToOldPoints,
2204  labelList& newToOldFaces
2205 )
2206 {
2207  label n = 0;
2208 
2209  forAll(include, i)
2210  {
2211  if (include[i])
2212  {
2213  n++;
2214  }
2215  }
2216 
2217  labelList oldToNewPoints;
2218  subsetMeshMap
2219  (
2220  s,
2221  include,
2222  n,
2223  newToOldPoints,
2224  oldToNewPoints,
2225  newToOldFaces
2226  );
2227 
2228  return subsetMesh
2229  (
2230  s,
2231  newToOldPoints,
2232  oldToNewPoints,
2233  newToOldFaces
2234  );
2235 }
2236 
2237 
2238 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2239 (
2240  const triSurface& s,
2241  const labelList& newToOldFaces,
2242  labelList& newToOldPoints
2243 )
2244 {
2245  const boolList include
2246  (
2247  ListOps::createWithValue<bool>(s.size(), newToOldFaces, true, false)
2248  );
2249 
2250  newToOldPoints.setSize(s.points().size());
2251  labelList oldToNewPoints(s.points().size(), -1);
2252  {
2253  label pointi = 0;
2254 
2255  forAll(include, oldFacei)
2256  {
2257  if (include[oldFacei])
2258  {
2259  // Renumber labels for face
2260  for (const label oldPointi : s[oldFacei])
2261  {
2262  if (oldToNewPoints[oldPointi] == -1)
2263  {
2264  oldToNewPoints[oldPointi] = pointi;
2265  newToOldPoints[pointi++] = oldPointi;
2266  }
2267  }
2268  }
2269  }
2270  newToOldPoints.setSize(pointi);
2271  }
2272 
2273  return subsetMesh
2274  (
2275  s,
2276  newToOldPoints,
2277  oldToNewPoints,
2278  newToOldFaces
2279  );
2280 }
2281 
2282 
2283 Foam::label Foam::distributedTriSurfaceMesh::findTriangle
2284 (
2285  const List<labelledTri>& allFaces,
2286  const labelListList& allPointFaces,
2287  const labelledTri& otherF
2288 )
2289 {
2290  // allFaces connected to otherF[0]
2291  const labelList& pFaces = allPointFaces[otherF[0]];
2292 
2293  forAll(pFaces, i)
2294  {
2295  const labelledTri& f = allFaces[pFaces[i]];
2296 
2297  if (f.region() == otherF.region())
2298  {
2299  // Find index of otherF[0]
2300  label fp0 = f.find(otherF[0]);
2301  // Check rest of triangle in same order
2302  label fp1 = f.fcIndex(fp0);
2303  label fp2 = f.fcIndex(fp1);
2304 
2305  if (f[fp1] == otherF[1] && f[fp2] == otherF[2])
2306  {
2307  return pFaces[i];
2308  }
2309  }
2310  }
2311  return -1;
2312 }
2313 
2314 
2315 // Merge into allSurf.
2316 void Foam::distributedTriSurfaceMesh::merge
2317 (
2318  const scalar mergeDist,
2319  const List<labelledTri>& subTris,
2320  const pointField& subPoints,
2321 
2322  List<labelledTri>& allTris,
2324 
2325  labelList& faceConstructMap,
2326  labelList& pointConstructMap
2327 )
2328 {
2329  labelList subToAll;
2330  matchPoints
2331  (
2332  subPoints,
2333  allPoints,
2334  scalarField(subPoints.size(), mergeDist), // match distance
2335  false, // verbose
2336  pointConstructMap
2337  );
2338 
2339  label nOldAllPoints = allPoints.size();
2340 
2341 
2342  // Add all unmatched points
2343  // ~~~~~~~~~~~~~~~~~~~~~~~~
2344 
2345  label allPointi = nOldAllPoints;
2346  forAll(pointConstructMap, pointi)
2347  {
2348  if (pointConstructMap[pointi] == -1)
2349  {
2350  pointConstructMap[pointi] = allPointi++;
2351  }
2352  }
2353 
2354  if (allPointi > nOldAllPoints)
2355  {
2356  allPoints.setSize(allPointi);
2357 
2358  forAll(pointConstructMap, pointi)
2359  {
2360  if (pointConstructMap[pointi] >= nOldAllPoints)
2361  {
2362  allPoints[pointConstructMap[pointi]] = subPoints[pointi];
2363  }
2364  }
2365  }
2366 
2367 
2368  // To check whether triangles are same we use pointFaces.
2369  labelListList allPointFaces;
2370  invertManyToMany(nOldAllPoints, allTris, allPointFaces);
2371 
2372 
2373  // Add all unmatched triangles
2374  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2375 
2376  label allTrii = allTris.size();
2377  allTris.setSize(allTrii + subTris.size());
2378 
2379  faceConstructMap.setSize(subTris.size());
2380 
2381  forAll(subTris, trii)
2382  {
2383  const labelledTri& subTri = subTris[trii];
2384 
2385  // Get triangle in new numbering
2386  labelledTri mappedTri
2387  (
2388  pointConstructMap[subTri[0]],
2389  pointConstructMap[subTri[1]],
2390  pointConstructMap[subTri[2]],
2391  subTri.region()
2392  );
2393 
2394 
2395  // Check if all points were already in surface
2396  bool fullMatch = true;
2397 
2398  forAll(mappedTri, fp)
2399  {
2400  if (mappedTri[fp] >= nOldAllPoints)
2401  {
2402  fullMatch = false;
2403  break;
2404  }
2405  }
2406 
2407  if (fullMatch)
2408  {
2409  // All three points are mapped to old points. See if same
2410  // triangle.
2411  label i = findTriangle
2412  (
2413  allTris,
2414  allPointFaces,
2415  mappedTri
2416  );
2417 
2418  if (i == -1)
2419  {
2420  // Add
2421  faceConstructMap[trii] = allTrii;
2422  allTris[allTrii] = mappedTri;
2423  allTrii++;
2424  }
2425  else
2426  {
2427  faceConstructMap[trii] = i;
2428  }
2429  }
2430  else
2431  {
2432  // Add
2433  faceConstructMap[trii] = allTrii;
2434  allTris[allTrii] = mappedTri;
2435  allTrii++;
2436  }
2437  }
2438  allTris.setSize(allTrii);
2439 }
2440 
2441 
2442 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
2443 
2444 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh
2446  const IOobject& io,
2447  const triSurface& s,
2448  const dictionary& dict
2449 )
2450 :
2451  triSurfaceMesh(io, s),
2452  dict_
2453  (
2454  IOobject
2455  (
2456  searchableSurface::name() + "Dict",
2463  ),
2464  dict
2465  ),
2466  currentDistType_(FROZEN) // only used to trigger re-distribution
2467 {
2468  // Read from the provided dictionary
2469  read();
2470 
2471  bounds().reduce();
2472 
2473  if (debug)
2474  {
2475  InfoInFunction << "Constructed from triSurface:" << endl;
2476  writeStats(Info);
2477 
2478  labelList nTris(Pstream::nProcs());
2479  nTris[Pstream::myProcNo()] = triSurface::size();
2480  Pstream::gatherList(nTris);
2481  Pstream::scatterList(nTris);
2482 
2483  Info<< endl<< "\tproc\ttris\tbb" << endl;
2484  forAll(nTris, proci)
2485  {
2486  Info<< '\t' << proci << '\t' << nTris[proci]
2487  << '\t' << procBb_[proci] << endl;
2488  }
2489  Info<< endl;
2490  }
2491 }
2492 
2493 
2494 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh(const IOobject& io)
2495 :
2497  (
2498  IOobject
2499  (
2500  io.name(),
2501  findLocalInstance(io), // findInstance with parent searching
2502  io.local(),
2503  io.db(),
2504  io.readOpt(),
2505  io.writeOpt(),
2506  io.registerObject()
2507  ),
2508  triSurfaceMesh::masterOnly // allow parent searching
2509  ),
2510  dict_
2511  (
2512  IOobject
2513  (
2514  searchableSurface::name() + "Dict",
2515  searchableSurface::instance(),
2516  searchableSurface::local(),
2517  searchableSurface::db(),
2518  (
2519  (
2520  searchableSurface::readOpt()
2521  == IOobject::MUST_READ
2522  || searchableSurface::readOpt()
2523  == IOobject::MUST_READ_IF_MODIFIED
2524  )
2525  ? IOobject::READ_IF_PRESENT
2526  : searchableSurface::readOpt()
2527  ),
2528  searchableSurface::writeOpt(),
2529  searchableSurface::registerObject()
2530  ),
2531  dictionary()
2532  ),
2533  currentDistType_(FROZEN) // only used to trigger re-distribution
2534 {
2535  // Read from the local, decomposed dictionary
2536  read();
2537 
2538  bounds().reduce();
2539 
2540  const fileName actualFile(triSurfaceMesh::checkFile(io, true));
2541 
2542  if
2543  (
2544  actualFile != io.localFilePath(triSurfaceMesh::typeName)
2545  && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED)
2546  )
2547  {
2549  << "Read distributedTriSurface " << io.name()
2550  << " from parent path " << actualFile << endl;
2551 
2552  if (Pstream::parRun())
2553  {
2554  // Distribute (checks that distType != currentDistType_ so should
2555  // always trigger re-distribution)
2556  List<treeBoundBox> bbs;
2558  autoPtr<mapDistribute> pointMap;
2559  distribute
2560  (
2561  bbs,
2562  true, // keep unmapped triangles
2563  faceMap,
2564  pointMap
2565  );
2566  }
2567  }
2568  else
2569  {
2570  if (debug)
2571  {
2573  << "Read distributedTriSurface " << io.name()
2574  << " from actual path " << actualFile << ':' << endl;
2575 
2576  labelList nTris(Pstream::nProcs());
2577  nTris[Pstream::myProcNo()] = triSurface::size();
2578  Pstream::gatherList(nTris);
2579  Pstream::scatterList(nTris);
2580 
2581  Info<< endl<< "\tproc\ttris\tbb" << endl;
2582  forAll(nTris, proci)
2583  {
2584  Info<< '\t' << proci << '\t' << nTris[proci]
2585  << '\t' << procBb_[proci] << endl;
2586  }
2587  Info<< endl;
2588  }
2589  }
2590  if (debug)
2591  {
2593  << "Read distributedTriSurface " << io.name() << ':' << endl;
2594  writeStats(Info);
2595  }
2596 }
2597 
2598 
2599 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh
2601  const IOobject& io,
2602  const dictionary& dict
2603 )
2604 :
2606  (
2607  IOobject
2608  (
2609  io.name(),
2610  findLocalInstance(io),
2611  io.local(),
2612  io.db(),
2613  io.readOpt(),
2614  io.writeOpt(),
2615  io.registerObject()
2616  ),
2617  dict,
2619  ),
2620  dict_
2621  (
2622  IOobject
2623  (
2624  searchableSurface::name() + "Dict",
2628  (
2629  (
2634  )
2637  ),
2640  ),
2641  dictionary()
2642  ),
2643  currentDistType_(FROZEN) // only used to trigger re-distribution
2644 {
2645  // Read from the local, decomposed dictionary
2646  read();
2647 
2648  // Optionally override settings from provided dictionary
2649  {
2650  // Wanted distribution type
2651  distributionTypeNames_.readIfPresent
2652  (
2653  "distributionType",
2654  dict_,
2655  distType_
2656  );
2657 
2658  // Merge distance
2659  dict_.readIfPresent("mergeDistance", mergeDist_);
2660 
2661  // Distribution type
2662  bool closed;
2663  if (dict_.readIfPresent<bool>("closed", closed))
2664  {
2665  surfaceClosed_ = closed;
2666  }
2667 
2668  outsideVolType_ = volumeType::names.getOrDefault
2669  (
2670  "outsideVolumeType",
2671  dict_,
2672  outsideVolType_
2673  );
2674  }
2675 
2676 
2677  bounds().reduce();
2678 
2679  const fileName actualFile(triSurfaceMesh::checkFile(io, dict, true));
2680 
2681  if
2682  (
2683  actualFile != io.localFilePath(triSurfaceMesh::typeName)
2684  && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED)
2685  )
2686  {
2688  << "Read distributedTriSurface " << io.name()
2689  << " from parent path " << actualFile
2690  << " and dictionary" << endl;
2691 
2692  if (Pstream::parRun())
2693  {
2694  // Distribute (checks that distType != currentDistType_ so should
2695  // always trigger re-distribution)
2696  List<treeBoundBox> bbs;
2698  autoPtr<mapDistribute> pointMap;
2699  distribute
2700  (
2701  bbs,
2702  true, // keep unmapped triangles
2703  faceMap,
2704  pointMap
2705  );
2706  }
2707  }
2708  else
2709  {
2710  if (debug)
2711  {
2713  << "Read distributedTriSurface " << io.name()
2714  << " from actual path " << actualFile
2715  << " and dictionary:" << endl;
2716 
2717  labelList nTris(Pstream::nProcs());
2718  nTris[Pstream::myProcNo()] = triSurface::size();
2719  Pstream::gatherList(nTris);
2720  Pstream::scatterList(nTris);
2721 
2722  Info<< endl<< "\tproc\ttris\tbb" << endl;
2723  forAll(nTris, proci)
2724  {
2725  Info<< '\t' << proci << '\t' << nTris[proci]
2726  << '\t' << procBb_[proci] << endl;
2727  }
2728  Info<< endl;
2729  }
2730  }
2731  if (debug)
2732  {
2734  << "Read distributedTriSurface " << io.name() << ':' << endl;
2735  writeStats(Info);
2736  }
2737 }
2738 
2739 
2740 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
2741 
2743 {
2744  clearOut();
2745 }
2746 
2747 
2749 {
2750  globalTris_.clear();
2752 }
2753 
2754 
2755 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2756 
2758 {
2759  if (!globalTris_)
2760  {
2761  globalTris_.reset(new globalIndex(triSurface::size()));
2762  }
2763  return *globalTris_;
2764 }
2765 
2766 
2767 //void Foam::distributedTriSurfaceMesh::findNearest
2768 //(
2769 // const pointField& samples,
2770 // const scalarField& nearestDistSqr,
2771 // List<pointIndexHit>& info
2772 //) const
2773 //{
2774 // if (!Pstream::parRun())
2775 // {
2776 // triSurfaceMesh::findNearest(samples, nearestDistSqr, info);
2777 // return;
2778 // }
2779 //
2780 // addProfiling
2781 // (
2782 // findNearest,
2783 // "distributedTriSurfaceMesh::findNearest"
2784 // );
2785 //
2786 // if (debug)
2787 // {
2788 // Pout<< "distributedTriSurfaceMesh::findNearest :"
2789 // << " trying to find nearest for " << samples.size()
2790 // << " samples with max sphere "
2791 // << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
2792 // << endl;
2793 // }
2794 //
2795 //
2796 // const indexedOctree<treeDataTriSurface>& octree = tree();
2797 //
2798 // // Important:force synchronised construction of indexing
2799 // const globalIndex& triIndexer = globalTris();
2800 //
2801 //
2802 // // Initialise
2803 // // ~~~~~~~~~~
2804 //
2805 // info.setSize(samples.size());
2806 // forAll(info, i)
2807 // {
2808 // info[i].setMiss();
2809 // }
2810 //
2811 //
2812 //
2813 // // Do any local queries
2814 // // ~~~~~~~~~~~~~~~~~~~~
2815 //
2816 // label nLocal = 0;
2817 //
2818 // {
2819 // // Work array - whether processor bb overlaps the bounding sphere.
2820 // boolList procBbOverlaps(Pstream::nProcs());
2821 //
2822 // forAll(samples, i)
2823 // {
2824 // // Find the processor this sample+radius overlaps.
2825 // label nProcs = calcOverlappingProcs
2826 // (
2827 // samples[i],
2828 // nearestDistSqr[i],
2829 // procBbOverlaps
2830 // );
2831 //
2832 // // Overlaps local processor?
2833 // if (procBbOverlaps[Pstream::myProcNo()])
2834 // {
2835 // info[i] = octree.findNearest(samples[i], nearestDistSqr[i]);
2836 // if (info[i].hit())
2837 // {
2838 // if
2839 // (
2840 // surfaceClosed_
2841 // && !contains(procBb_[proci], info[i].hitPoint())
2842 // )
2843 // {
2844 // // Nearest point is not on local processor so the
2845 // // the triangle is only there because some other bit
2846 // // of it
2847 // // is on it. Assume there is another processor that
2848 // // holds
2849 // // the full surrounding of the triangle so we can
2850 // // clear this particular nearest.
2851 // info[i].setMiss();
2852 // info[i].setIndex(-1);
2853 // }
2854 // else
2855 // {
2856 // info[i].setIndex
2857 // (triIndexer.toGlobal(info[i].index()));
2858 // }
2859 // }
2860 // if (nProcs == 1)
2861 // {
2862 // // Fully local
2863 // nLocal++;
2864 // }
2865 // }
2866 // }
2867 // }
2868 //
2869 //
2870 // if
2871 // (
2872 // Pstream::parRun()
2873 // && (
2874 // returnReduce(nLocal, sumOp<label>())
2875 // < returnReduce(samples.size(), sumOp<label>())
2876 // )
2877 // )
2878 // {
2879 // // Not all can be resolved locally. Build queries and map, send over
2880 // // queries, do intersections, send back and merge.
2881 //
2882 // // Calculate queries and exchange map
2883 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2884 //
2885 // pointField allCentres;
2886 // scalarField allRadiusSqr;
2887 // labelList allSegmentMap;
2888 // autoPtr<mapDistribute> mapPtr
2889 // (
2890 // calcLocalQueries
2891 // (
2892 // false, // exclude local processor since already done above
2893 // samples,
2894 // nearestDistSqr,
2895 //
2896 // allCentres,
2897 // allRadiusSqr,
2898 // allSegmentMap
2899 // )
2900 // );
2901 // const mapDistribute& map = mapPtr();
2902 //
2903 //
2904 // // swap samples to local processor
2905 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2906 //
2907 // map.distribute(allCentres);
2908 // map.distribute(allRadiusSqr);
2909 //
2910 //
2911 // // Do my tests
2912 // // ~~~~~~~~~~~
2913 //
2914 // List<pointIndexHit> allInfo(allCentres.size());
2915 // forAll(allInfo, i)
2916 // {
2917 // allInfo[i] = octree.findNearest
2918 // (
2919 // allCentres[i],
2920 // allRadiusSqr[i]
2921 // );
2922 // if (allInfo[i].hit())
2923 // {
2924 // // We don't know if the nearest is on an edge/point. If
2925 // // this is the case we preferentially want to return the
2926 // // index on the processor that holds all surrounding triangles
2927 // // so we can do e.g. follow-on inside/outside tests
2928 // if
2929 // (
2930 // surfaceClosed_
2931 // && !contains
2932 // (
2933 // procBb_[Pstream::myProcNo()],
2934 // allInfo[i].hitPoint()
2935 // )
2936 // )
2937 // {
2938 // // Nearest point is not on local processor so the
2939 // // the triangle is only there because some other bit of it
2940 // // is on it. Assume there is another processor that holds
2941 // // the full surrounding of the triangle so we can clear
2942 // // this particular nearest.
2943 // allInfo[i].setMiss();
2944 // allInfo[i].setIndex(-1);
2945 // }
2946 // else
2947 // {
2948 // allInfo[i].setIndex
2949 // (
2950 // triIndexer.toGlobal(allInfo[i].index())
2951 // );
2952 // }
2953 // }
2954 // }
2955 //
2956 //
2957 // // Send back results
2958 // // ~~~~~~~~~~~~~~~~~
2959 //
2960 // map.reverseDistribute(allSegmentMap.size(), allInfo);
2961 //
2962 //
2963 // // Extract information
2964 // // ~~~~~~~~~~~~~~~~~~~
2965 //
2966 // forAll(allInfo, i)
2967 // {
2968 // if (allInfo[i].hit())
2969 // {
2970 // label pointi = allSegmentMap[i];
2971 //
2972 // if (!info[pointi].hit())
2973 // {
2974 // // No intersection yet so take this one
2975 // info[pointi] = allInfo[i];
2976 // }
2977 // else
2978 // {
2979 // // Nearest intersection
2980 // if
2981 // (
2982 // magSqr(allInfo[i].hitPoint()-samples[pointi])
2983 // < magSqr(info[pointi].hitPoint()-samples[pointi])
2984 // )
2985 // {
2986 // info[pointi] = allInfo[i];
2987 // }
2988 // }
2989 // }
2990 // }
2991 // }
2992 //}
2993 
2994 
2997  const pointField& samples,
2998  const scalarField& nearestDistSqr,
2999  List<pointIndexHit>& info
3000 ) const
3001 {
3002  if (!Pstream::parRun())
3003  {
3004  triSurfaceMesh::findNearest(samples, nearestDistSqr, info);
3005  return;
3006  }
3007 
3008  addProfiling
3009  (
3010  findNearest,
3011  "distributedTriSurfaceMesh::findNearest"
3012  );
3013 
3014  if (debug)
3015  {
3016  Pout<< "distributedTriSurfaceMesh::findNearest :"
3017  << " trying to find nearest for " << samples.size()
3018  << " samples with max sphere "
3019  << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
3020  << endl;
3021  }
3022 
3023  const globalIndex& triIndexer = globalTris();
3024 
3025  // Two-pass searching:
3026  // 1. send the sample to the processor whose bb contains it. This is
3027  // most likely also the one that holds the nearest triangle. (In case
3028  // there is no containing processor send to nearest processors. Note
3029  // that this might cause a lot of traffic if this is likely)
3030  // Send the resulting nearest point back.
3031  // 2. with the find from 1 look at which other processors might have a
3032  // better triangle. Since hopefully step 1) will have produced a tight
3033  // bounding box this should limit the amount of points to be retested
3034 
3035 
3036  // 1. Test samples on processor(s) that contains them
3037  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3038 
3039  autoPtr<mapDistribute> map1Ptr;
3040  scalarField distSqr(nearestDistSqr);
3041  boolList procContains(Pstream::nProcs(), false);
3042  boolList procOverlaps(Pstream::nProcs(), false);
3043 
3044  label nOutside = 0;
3045  {
3047  // Pre-size. Assume samples are uniformly distributed
3048  forAll(dynSendMap, proci)
3049  {
3050  dynSendMap[proci].reserve(samples.size()/Pstream::nProcs());
3051  }
3052 
3053  forAll(samples, samplei)
3054  {
3055  label minProci = -1;
3056  Tuple2<label, scalar> best = findBestProcs
3057  (
3058  samples[samplei],
3059  distSqr[samplei],
3060  procContains,
3061  procOverlaps,
3062  minProci
3063  );
3064 
3065  label nContains = 0;
3066  forAll(procBb_, proci)
3067  {
3068  if (procContains[proci])
3069  {
3070  nContains++;
3071  dynSendMap[proci].append(samplei);
3072  distSqr[samplei] = best.second();
3073  }
3074  }
3075  if (nContains == 0)
3076  {
3077  nOutside++;
3078  // Sample is outside all bb. Choices:
3079  // - send to all processors
3080  // - send to single processor
3081 
3082  //forAll(procOverlaps[proci])
3083  //{
3084  // if (procOverlaps[proci])
3085  // {
3086  // dynSendMap[proci].append(samplei);
3087  // distSqr[samplei] = best.second();
3088  // }
3089  //}
3090  if (minProci != -1)
3091  {
3092  dynSendMap[minProci].append(samplei);
3093  distSqr[samplei] = best.second();
3094  }
3095  }
3096  }
3097 
3098  labelListList sendMap(Pstream::nProcs());
3099  forAll(sendMap, proci)
3100  {
3101  sendMap[proci].transfer(dynSendMap[proci]);
3102  }
3103  map1Ptr.reset(new mapDistribute(std::move(sendMap)));
3104  }
3105  const mapDistribute& map1 = *map1Ptr;
3106 
3107 
3108  if (debug)
3109  {
3110  Pout<< "Pass1:"
3111  << " of " << samples.size() << " samples sending to" << endl;
3112  label nSend = 0;
3113  forAll(map1.subMap(), proci)
3114  {
3115  Pout<< " " << proci << "\t" << map1.subMap()[proci].size()
3116  << endl;
3117  nSend += map1.subMap()[proci].size();
3118  }
3119  Pout<< " sum\t" << nSend << endl
3120  << " outside\t" << nOutside << endl;
3121  }
3122 
3123 
3124  List<nearestAndDist> nearestInfo;
3125  {
3126  // Get the points I need to test and test locally
3127  pointField localPoints(samples);
3128  map1.distribute(localPoints);
3129  scalarField localDistSqr(distSqr);
3130  map1.distribute(localDistSqr);
3131  List<pointIndexHit> localInfo;
3132  triSurfaceMesh::findNearest(localPoints, localDistSqr, localInfo);
3133  convertTriIndices(localInfo);
3134 
3135  // Pack into structure for combining information from multiple
3136  // processors
3137  nearestInfo.setSize(localInfo.size());
3138  nearestInfo = nearestAndDist(pointIndexHit(), Foam::sqr(GREAT));
3139 
3140  label nHit = 0;
3141  label nIgnoredHit = 0;
3142 
3143  forAll(nearestInfo, i)
3144  {
3145  const pointIndexHit& info = localInfo[i];
3146  if (info.hit())
3147  {
3148  nHit++;
3149 
3150  if
3151  (
3152  surfaceClosed_
3153  && !contains(procBb_[Pstream::myProcNo()], info.hitPoint())
3154  )
3155  {
3156  // Nearest point is not on local processor so the
3157  // the triangle is only there because some other bit
3158  // of it is on it. Assume there is another processor that
3159  // holds the full surrounding of the triangle so we can
3160  // ignore this particular nearest.
3161  nIgnoredHit++;
3162  }
3163  else
3164  {
3165  nearestAndDist& ni = nearestInfo[i];
3166  ni.first() = info;
3167  ni.second() = magSqr(localPoints[i]-info.hitPoint());
3168  }
3169  }
3170  }
3171 
3172  if (debug)
3173  {
3174  Pout<< "distributedTriSurfaceMesh::findNearest :"
3175  << " searched locally for " << localPoints.size()
3176  << " samples with max sphere "
3177  << (localDistSqr.size() ? Foam::sqrt(max(localDistSqr)) : Zero)
3178  << " found hits:" << nHit
3179  << " of which outside local bb:" << nIgnoredHit
3180  << endl;
3181  }
3182  }
3183 
3184  // Send back to originating processor. Choose best if sent to multiple
3185  // processors. Note that afterwards all unused entries have the unique
3186  // value nearestZero (distance < 0). This is used later on to see if
3187  // the sample was sent to any processor.
3189  (
3191  List<labelPair>(0),
3192  samples.size(),
3193  map1.constructMap(),
3194  map1.constructHasFlip(),
3195  map1.subMap(),
3196  map1.subHasFlip(),
3197  nearestInfo,
3198  nearestZero,
3199  nearestEqOp(),
3200  noOp(), // no flipping
3202  map1.comm()
3203  );
3204 
3205 
3206  // 2. Test samples on other processor(s) that overlap
3207  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3208 
3209  // Now we have (in nearestInfo) for every input sample the current best
3210  // hit (on the processor that originates the sample). See if we can
3211  // improve it by sending the queries to any other processors
3212 
3213  autoPtr<mapDistribute> map2Ptr;
3214  {
3216 
3217  // Work array - whether processor bb overlaps the bounding sphere.
3218  boolList procBbOverlaps(Pstream::nProcs());
3219 
3220  label nFound = 0;
3221 
3222  forAll(nearestInfo, samplei)
3223  {
3224  const point& sample = samples[samplei];
3225  const nearestAndDist& ni = nearestInfo[samplei];
3226  const pointIndexHit& info = ni.first();
3227 
3228  if (info.hit())
3229  {
3230  nFound++;
3231  }
3232 
3233  scalar d2 =
3234  (
3235  info.hit()
3236  ? ni.second()
3237  : distSqr[samplei]
3238  );
3239 
3240  label hitProci =
3241  (
3242  info.hit()
3243  ? triIndexer.whichProcID(info.index())
3244  : -1
3245  );
3246 
3247  // Find the processors this sample+radius overlaps.
3248  calcOverlappingProcs(sample, d2, procBbOverlaps);
3249 
3250  forAll(procBbOverlaps, proci)
3251  {
3252  if (procBbOverlaps[proci])
3253  {
3254  // Check this sample wasn't already handled above. This
3255  // could be improved since the sample might have been
3256  // searched on multiple processors. We now only exclude the
3257  // processor where the point was inside.
3258  if (proci != hitProci)
3259  {
3260  dynSendMap[proci].append(samplei);
3261  }
3262  }
3263  }
3264  }
3265 
3266  labelListList sendMap(Pstream::nProcs());
3267  forAll(sendMap, proci)
3268  {
3269  sendMap[proci].transfer(dynSendMap[proci]);
3270  }
3271  map2Ptr.reset(new mapDistribute(std::move(sendMap)));
3272  }
3273 
3274  const mapDistribute& map2 = map2Ptr();
3275 
3276  if (debug)
3277  {
3278  Pout<< "Pass2:"
3279  << " of " << samples.size() << " samples sending to" << endl;
3280  label nSend = 0;
3281  forAll(map2.subMap(), proci)
3282  {
3283  Pout<< " " << proci << "\t" << map2.subMap()[proci].size()
3284  << endl;
3285  nSend += map2.subMap()[proci].size();
3286  }
3287  Pout<< " sum\t" << nSend << endl;
3288  }
3289 
3290  // Send samples and current best distance
3291  pointField localSamples(samples);
3292  map2.distribute(localSamples);
3293  scalarField localDistSqr(distSqr);
3294  forAll(nearestInfo, samplei)
3295  {
3296  const nearestAndDist& ni = nearestInfo[samplei];
3297  if (ni.first().hit())
3298  {
3299  localDistSqr[samplei] = ni.second();
3300  }
3301  }
3302  map2.distribute(localDistSqr);
3303 
3304  // Do local test
3305  List<pointIndexHit> localInfo;
3306  triSurfaceMesh::findNearest(localSamples, localDistSqr, localInfo);
3307  convertTriIndices(localInfo);
3308 
3309  // Pack and send back
3310  List<nearestAndDist> localBest(localSamples.size());
3311  label nHit = 0;
3312  label nIgnoredHit = 0;
3313  forAll(localInfo, i)
3314  {
3315  const pointIndexHit& info = localInfo[i];
3316  if (info.hit())
3317  {
3318  nHit++;
3319  if
3320  (
3321  surfaceClosed_
3322  && !contains(procBb_[Pstream::myProcNo()], info.hitPoint())
3323  )
3324  {
3325  // See above
3326  nIgnoredHit++;
3327  }
3328  else
3329  {
3330  nearestAndDist& ni = localBest[i];
3331  ni.first() = info;
3332  ni.second() = magSqr(info.hitPoint()-localSamples[i]);
3333  }
3334  }
3335  }
3336 
3337  if (debug)
3338  {
3339  Pout<< "distributedTriSurfaceMesh::findNearest :"
3340  << " searched locally for " << localSamples.size()
3341  << " samples with max sphere "
3342  << (localDistSqr.size() ? Foam::sqrt(max(localDistSqr)) : Zero)
3343  << " found hits:" << nHit
3344  << " of which outside local bb:" << nIgnoredHit
3345  << endl;
3346  }
3347 
3349  (
3351  List<labelPair>(0),
3352  samples.size(),
3353  map2.constructMap(),
3354  map2.constructHasFlip(),
3355  map2.subMap(),
3356  map2.subHasFlip(),
3357  localBest,
3358  nearestZero,
3359  nearestEqOp(),
3360  noOp(), // no flipping
3362  map2.comm()
3363  );
3364 
3365  // Combine with nearestInfo
3366  info.setSize(samples.size());
3367  forAll(samples, samplei)
3368  {
3369  nearestAndDist ni(nearestInfo[samplei]);
3370  nearestEqOp()(ni, localBest[samplei]);
3371 
3372  info[samplei] = ni.first();
3373  }
3374 }
3375 
3376 
3379  const pointField& samples,
3380  const scalarField& nearestDistSqr,
3381  const labelList& regionIndices,
3382  List<pointIndexHit>& info
3383 ) const
3384 {
3385  if (!Pstream::parRun())
3386  {
3388  (
3389  samples,
3390  nearestDistSqr,
3391  regionIndices,
3392  info
3393  );
3394  return;
3395  }
3396 
3397  addProfiling
3398  (
3399  findNearestRegion,
3400  "distributedTriSurfaceMesh::findNearestRegion"
3401  );
3402 
3403  if (debug)
3404  {
3405  Pout<< "distributedTriSurfaceMesh::findNearest :"
3406  << " trying to find nearest and region for " << samples.size()
3407  << " samples with max sphere "
3408  << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
3409  << endl;
3410  }
3411 
3412  if (regionIndices.empty())
3413  {
3414  findNearest(samples, nearestDistSqr, info);
3415  }
3416  else
3417  {
3418  // Calculate queries and exchange map
3419  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3420 
3421  pointField allCentres;
3422  scalarField allRadiusSqr;
3423  labelList allSegmentMap;
3424  autoPtr<mapDistribute> mapPtr
3425  (
3426  calcLocalQueries
3427  (
3428  true, // also send to local processor
3429  samples,
3430  nearestDistSqr,
3431 
3432  allCentres,
3433  allRadiusSqr,
3434  allSegmentMap
3435  )
3436  );
3437  const mapDistribute& map = mapPtr();
3438 
3439 
3440  // swap samples to local processor
3441  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3442 
3443  map.distribute(allCentres);
3444  map.distribute(allRadiusSqr);
3445 
3446 
3447  // Do my tests
3448  // ~~~~~~~~~~~
3449 
3450  List<pointIndexHit> allInfo(allCentres.size());
3452  (
3453  allCentres,
3454  allRadiusSqr,
3455  regionIndices,
3456  allInfo
3457  );
3458  convertTriIndices(allInfo);
3459 
3460  forAll(allInfo, i)
3461  {
3462  if (allInfo[i].hit())
3463  {
3464  if
3465  (
3466  surfaceClosed_
3467  && !contains
3468  (
3469  procBb_[Pstream::myProcNo()],
3470  allInfo[i].hitPoint()
3471  )
3472  )
3473  {
3474  // Nearest point is not on local processor so the
3475  // the triangle is only there because some other bit of it
3476  // is on it. Assume there is another processor that holds
3477  // the full surrounding of the triangle so we can clear
3478  // this particular nearest.
3479  allInfo[i].setMiss();
3480  allInfo[i].setIndex(-1);
3481  }
3482  }
3483  }
3484 
3485 
3486  // Send back results
3487  // ~~~~~~~~~~~~~~~~~
3488 
3489  map.reverseDistribute(allSegmentMap.size(), allInfo);
3490 
3491 
3492  // Extract information
3493  // ~~~~~~~~~~~~~~~~~~~
3494 
3495  forAll(allInfo, i)
3496  {
3497  if (allInfo[i].hit())
3498  {
3499  label pointi = allSegmentMap[i];
3500 
3501  if (!info[pointi].hit())
3502  {
3503  // No intersection yet so take this one
3504  info[pointi] = allInfo[i];
3505  }
3506  else
3507  {
3508  // Nearest intersection
3509  if
3510  (
3511  magSqr(allInfo[i].hitPoint()-samples[pointi])
3512  < magSqr(info[pointi].hitPoint()-samples[pointi])
3513  )
3514  {
3515  info[pointi] = allInfo[i];
3516  }
3517  }
3518  }
3519  }
3520  }
3521 }
3522 
3523 
3524 void Foam::distributedTriSurfaceMesh::findLine
3526  const pointField& start,
3527  const pointField& end,
3528  List<pointIndexHit>& info
3529 ) const
3530 {
3531  if (!Pstream::parRun())
3532  {
3533  triSurfaceMesh::findLine(start, end, info);
3534  }
3535  else
3536  {
3537  findLine
3538  (
3539  true, // nearestIntersection
3540  start,
3541  end,
3542  info
3543  );
3544  }
3545 }
3546 
3547 
3550  const pointField& start,
3551  const pointField& end,
3552  List<pointIndexHit>& info
3553 ) const
3554 {
3555  if (!Pstream::parRun())
3556  {
3557  triSurfaceMesh::findLineAny(start, end, info);
3558  }
3559  else
3560  {
3561  findLine
3562  (
3563  true, // nearestIntersection
3564  start,
3565  end,
3566  info
3567  );
3568  }
3569 }
3570 
3571 
3574  const pointField& start,
3575  const pointField& end,
3576  List<List<pointIndexHit>>& info
3577 ) const
3578 {
3579  if (!Pstream::parRun())
3580  {
3581  triSurfaceMesh::findLineAll(start, end, info);
3582  return;
3583  }
3584 
3585  if (debug)
3586  {
3587  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3588  << " intersecting with "
3589  << start.size() << " rays" << endl;
3590  }
3591 
3592  addProfiling
3593  (
3594  findLineAll,
3595  "distributedTriSurfaceMesh::findLineAll"
3596  );
3597 
3598  // Reuse fineLine. We could modify all of findLine to do multiple
3599  // intersections but this would mean a lot of data transferred so
3600  // for now we just find nearest intersection and retest from that
3601  // intersection onwards.
3602 
3603  // Work array.
3604  List<pointIndexHit> hitInfo(start.size());
3605 
3606  findLine
3607  (
3608  true, // nearestIntersection
3609  start,
3610  end,
3611  hitInfo
3612  );
3613 
3614  // Tolerances:
3615  // To find all intersections we add a small vector to the last intersection
3616  // This is chosen such that
3617  // - it is significant (SMALL is smallest representative relative tolerance;
3618  // we need something bigger since we're doing calculations)
3619  // - if the start-end vector is zero we still progress
3620  const vectorField dirVec(end-start);
3621  const scalarField magSqrDirVec(magSqr(dirVec));
3622  const vectorField smallVec
3623  (
3624  ROOTSMALL*dirVec
3625  + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
3626  );
3627 
3628  // Copy to input and compact any hits
3629  labelList pointMap(start.size());
3630  pointField e0(start.size());
3631  pointField e1(start.size());
3632  label compacti = 0;
3633 
3634  info.setSize(hitInfo.size());
3635  forAll(hitInfo, pointi)
3636  {
3637  if (hitInfo[pointi].hit())
3638  {
3639  info[pointi].setSize(1);
3640  info[pointi][0] = hitInfo[pointi];
3641 
3642  point pt = hitInfo[pointi].hitPoint() + smallVec[pointi];
3643 
3644  if (((pt-start[pointi])&dirVec[pointi]) <= magSqrDirVec[pointi])
3645  {
3646  e0[compacti] = pt;
3647  e1[compacti] = end[pointi];
3648  pointMap[compacti] = pointi;
3649  compacti++;
3650  }
3651  }
3652  else
3653  {
3654  info[pointi].clear();
3655  }
3656  }
3657 
3658  e0.setSize(compacti);
3659  e1.setSize(compacti);
3660  pointMap.setSize(compacti);
3661 
3662 
3663  label iter = 0;
3664  while (returnReduce(e0.size(), sumOp<label>()) > 0)
3665  {
3666  findLine
3667  (
3668  true, // nearestIntersection
3669  e0,
3670  e1,
3671  hitInfo
3672  );
3673 
3674 
3675  // Extract
3676  label compacti = 0;
3677  forAll(hitInfo, i)
3678  {
3679  if (hitInfo[i].hit())
3680  {
3681  label pointi = pointMap[i];
3682 
3683  label sz = info[pointi].size();
3684  info[pointi].setSize(sz+1);
3685  info[pointi][sz] = hitInfo[i];
3686 
3687  point pt = hitInfo[i].hitPoint() + smallVec[pointi];
3688 
3689  // Check current coordinate along ray
3690  scalar d = ((pt-start[pointi])&dirVec[pointi]);
3691 
3692  // Note check for d>0. Very occasionally the octree will find
3693  // an intersection to the left of the ray due to tolerances.
3694  if (d > 0 && d <= magSqrDirVec[pointi])
3695  {
3696  e0[compacti] = pt;
3697  e1[compacti] = end[pointi];
3698  pointMap[compacti] = pointi;
3699  compacti++;
3700  }
3701  }
3702  }
3703 
3704  // Trim
3705  e0.setSize(compacti);
3706  e1.setSize(compacti);
3707  pointMap.setSize(compacti);
3708 
3709  iter++;
3710 
3711  if (iter == 1000)
3712  {
3713  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3714  << " Exiting loop due to excessive number of"
3715  << " intersections along ray"
3716  << " start:" << UIndirectList<point>(start, pointMap)
3717  << " end:" << UIndirectList<point>(end, pointMap)
3718  << " e0:" << UIndirectList<point>(e0, pointMap)
3719  << " e1:" << UIndirectList<point>(e1, pointMap)
3720  << endl;
3721  break;
3722  }
3723  }
3724  if (debug)
3725  {
3726  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3727  << " finished intersecting with "
3728  << start.size() << " rays" << endl;
3729  }
3730 }
3731 
3732 
3735  const List<pointIndexHit>& info,
3736  labelList& region
3737 ) const
3738 {
3739  if (debug)
3740  {
3741  Pout<< "distributedTriSurfaceMesh::getRegion :"
3742  << " getting region for "
3743  << info.size() << " triangles" << endl;
3744  }
3745 
3746  addProfiling(getRegion, "distributedTriSurfaceMesh::getRegion");
3747 
3748  if (!Pstream::parRun())
3749  {
3750  region.setSize(info.size());
3751  forAll(info, i)
3752  {
3753  if (info[i].hit())
3754  {
3755  region[i] = triSurface::operator[](info[i].index()).region();
3756  }
3757  else
3758  {
3759  region[i] = -1;
3760  }
3761  }
3762 
3763  if (debug)
3764  {
3765  Pout<< "distributedTriSurfaceMesh::getRegion :"
3766  << " finished getting region for "
3767  << info.size() << " triangles" << endl;
3768  }
3769 
3770  return;
3771  }
3772 
3773  // Get query data (= local index of triangle)
3774  // ~~~~~~~~~~~~~~
3775 
3776  labelList triangleIndex(info.size());
3777  autoPtr<mapDistribute> mapPtr
3778  (
3779  localQueries
3780  (
3781  info,
3782  triangleIndex
3783  )
3784  );
3785  const mapDistribute& map = mapPtr();
3786 
3787 
3788  // Do my tests
3789  // ~~~~~~~~~~~
3790 
3791  const triSurface& s = static_cast<const triSurface&>(*this);
3792 
3793  region.setSize(triangleIndex.size());
3794 
3795  forAll(triangleIndex, i)
3796  {
3797  label trii = triangleIndex[i];
3798  region[i] = s[trii].region();
3799  }
3800 
3801 
3802  // Send back results
3803  // ~~~~~~~~~~~~~~~~~
3804 
3805  map.reverseDistribute(info.size(), region);
3806 
3807  if (debug)
3808  {
3809  Pout<< "distributedTriSurfaceMesh::getRegion :"
3810  << " finished getting region for "
3811  << info.size() << " triangles" << endl;
3812  }
3813 }
3814 
3815 
3818  const List<pointIndexHit>& info,
3819  vectorField& normal
3820 ) const
3821 {
3822  if (!Pstream::parRun())
3823  {
3824  triSurfaceMesh::getNormal(info, normal);
3825  return;
3826  }
3827 
3828  if (debug)
3829  {
3830  Pout<< "distributedTriSurfaceMesh::getNormal :"
3831  << " getting normal for "
3832  << info.size() << " triangles" << endl;
3833  }
3834 
3835  addProfiling(getNormal, "distributedTriSurfaceMesh::getNormal");
3836 
3837 
3838  // Get query data (= local index of triangle)
3839  // ~~~~~~~~~~~~~~
3840 
3841  labelList triangleIndex(info.size());
3842  autoPtr<mapDistribute> mapPtr
3843  (
3844  localQueries
3845  (
3846  info,
3847  triangleIndex
3848  )
3849  );
3850  const mapDistribute& map = mapPtr();
3851 
3852 
3853  // Do my tests
3854  // ~~~~~~~~~~~
3855 
3856  const triSurface& s = static_cast<const triSurface&>(*this);
3857 
3858  normal.setSize(triangleIndex.size());
3859 
3860  forAll(triangleIndex, i)
3861  {
3862  label trii = triangleIndex[i];
3863  normal[i] = s[trii].unitNormal(s.points());
3864  }
3865 
3866 
3867  // Send back results
3868  // ~~~~~~~~~~~~~~~~~
3869 
3870  map.reverseDistribute(info.size(), normal);
3871 
3872  if (debug)
3873  {
3874  Pout<< "distributedTriSurfaceMesh::getNormal :"
3875  << " finished getting normal for "
3876  << info.size() << " triangles" << endl;
3877  }
3878 }
3879 
3880 
3881 //void Foam::distributedTriSurfaceMesh::getVolumeTypeUncached
3882 //(
3883 // const pointField& samples,
3884 // List<volumeType>& volType
3885 //) const
3886 //{
3887 // if (!Pstream::parRun())
3888 // {
3889 // triSurfaceMesh::getVolumeType(samples, volType);
3890 // return;
3891 // }
3892 //
3893 //
3894 // if (!hasVolumeType())
3895 // {
3896 // FatalErrorInFunction
3897 // << "Volume type only supported for closed distributed surfaces."
3898 // << exit(FatalError);
3899 // }
3900 //
3901 // // Trigger (so parallel synchronised) construction of outside type.
3902 // // Normally this would get triggered from inside individual searches
3903 // // so would not be parallel synchronised
3904 // if (outsideVolType_ == volumeType::UNKNOWN)
3905 // {
3906 // // Determine nearest (in parallel)
3907 // const point outsidePt(bounds().max() + 0.5*bounds().span());
3908 // if (debug)
3909 // {
3910 // Pout<< "distributedTriSurfaceMesh::outsideVolumeType :"
3911 // << " triggering outsidePoint" << outsidePt
3912 // << " orientation" << endl;
3913 // }
3914 //
3915 // const pointField outsidePts(1, outsidePt);
3916 // List<pointIndexHit> nearestInfo;
3917 // findNearest
3918 // (
3919 // outsidePts,
3920 // scalarField(1, Foam::sqr(GREAT)),
3921 // nearestInfo
3922 // );
3923 //
3924 // List<volumeType> outsideVolTypes;
3925 // surfaceSide(outsidePts, nearestInfo, outsideVolTypes);
3926 // outsideVolType_ = outsideVolTypes[0];
3927 //
3928 // if (debug)
3929 // {
3930 // Pout<< "distributedTriSurfaceMesh::outsideVolumeType :"
3931 // << " determined outsidePoint" << outsidePt
3932 // << " to be " << volumeType::names[outsideVolType_] << endl;
3933 // }
3934 // }
3935 //
3936 // // Determine nearest (in parallel)
3937 // List<pointIndexHit> nearestInfo(samples.size());
3938 // findNearest
3939 // (
3940 // samples,
3941 // scalarField(samples.size(), Foam::sqr(GREAT)),
3942 // nearestInfo
3943 // );
3944 //
3945 // // Determine orientation (in parallel)
3946 // surfaceSide(samples, nearestInfo, volType);
3947 //}
3948 
3949 
3952  const pointField& samples,
3953  List<volumeType>& volType
3954 ) const
3955 {
3956  if (!Pstream::parRun())
3957  {
3959  return;
3960  }
3961 
3962 
3963  if (!hasVolumeType())
3964  {
3966  << "Volume type only supported for closed distributed surfaces."
3967  << exit(FatalError);
3968  }
3969 
3970  // Trigger (so parallel synchronised) construction of outside type.
3971  // Normally this would get triggered from inside individual searches
3972  // so would not be parallel synchronised
3973  if (outsideVolType_ == volumeType::UNKNOWN)
3974  {
3975  addProfiling
3976  (
3977  getVolumeType,
3978  "distributedTriSurfaceMesh::getCachedVolumeType"
3979  );
3980 
3981  // Determine nearest (in parallel)
3982  const point outsidePt(bounds().max() + 0.5*bounds().span());
3983  if (debug)
3984  {
3985  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
3986  << " triggering outsidePoint" << outsidePt
3987  << " orientation" << endl;
3988  }
3989 
3990  const pointField outsidePts(1, outsidePt);
3991  List<pointIndexHit> nearestInfo;
3992  findNearest
3993  (
3994  outsidePts,
3995  scalarField(1, Foam::sqr(GREAT)),
3996  nearestInfo
3997  );
3998 
3999  List<volumeType> outsideVolTypes;
4000  surfaceSide(outsidePts, nearestInfo, outsideVolTypes);
4001  outsideVolType_ = outsideVolTypes[0];
4002 
4003  if (debug)
4004  {
4005  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4006  << " determined outsidePoint" << outsidePt
4007  << " to be " << volumeType::names[outsideVolType_] << endl;
4008  }
4009 
4010  if
4011  (
4012  outsideVolType_ == volumeType::INSIDE
4013  || outsideVolType_ == volumeType::OUTSIDE
4014  )
4015  {
4016  // Get local tree
4017  const indexedOctree<treeDataTriSurface>& t = tree();
4018  PackedList<2>& nt = t.nodeTypes();
4020  t.nodes();
4021  nt.setSize(nodes.size());
4022  nt = volumeType::UNKNOWN;
4023 
4024  // Collect midpoints
4025  DynamicField<point> midPoints(label(0.5*nodes.size()));
4026  collectLeafMids(0, midPoints);
4027 
4028  if (debug)
4029  {
4030  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4031  << " triggering orientation caching for "
4032  << midPoints.size() << " leaf mids" << endl;
4033  }
4034 
4035  // Get volume type of mid points
4036  List<volumeType> midVolTypes;
4037  getVolumeType(midPoints, midVolTypes);
4038 
4039  // Cache on local tree
4040  label index = 0;
4041  calcVolumeType
4042  (
4043  midVolTypes,
4044  index,
4045  nt,
4046  0 // nodeI
4047  );
4048  if (debug)
4049  {
4050  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4051  << " done orientation caching for "
4052  << midPoints.size() << " leaf mids" << endl;
4053  }
4054  }
4055  }
4056 
4057 
4058  if (debug)
4059  {
4060  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4061  << " finding orientation for " << samples.size()
4062  << " samples" << endl;
4063  }
4064 
4065  addProfiling
4066  (
4067  getVolumeType,
4068  "distributedTriSurfaceMesh::getVolumeType"
4069  );
4070 
4071 
4072  DynamicList<label> outsideSamples;
4073 
4074  // Distribute samples to relevant processors
4075  autoPtr<mapDistribute> mapPtr;
4076  {
4077  labelListList sendMap(Pstream::nProcs());
4078  {
4079  // 1. Count
4080  labelList nSend(Pstream::nProcs(), 0);
4081  forAll(samples, samplei)
4082  {
4083  // Find the processors this sample overlaps.
4084  label nOverlap = 0;
4085  forAll(procBb_, proci)
4086  {
4087  if (contains(procBb_[proci], samples[samplei]))
4088  {
4089  nSend[proci]++;
4090  nOverlap++;
4091  }
4092  }
4093 
4094  // Special case: point is outside all bbs. These would not
4095  // get sent to anyone so handle locally. Note that is the
4096  // equivalent of the test in triSurfaceMesh against the local
4097  // tree bb
4098  if (nOverlap == 0)
4099  {
4100  outsideSamples.append(samplei);
4101  }
4102  }
4103 
4104  forAll(nSend, proci)
4105  {
4106  sendMap[proci].setSize(nSend[proci]);
4107  }
4108  nSend = 0;
4109 
4110  // 2. Fill
4111  forAll(samples, samplei)
4112  {
4113  // Find the processors this sample overlaps.
4114  forAll(procBb_, proci)
4115  {
4116  if (contains(procBb_[proci], samples[samplei]))
4117  {
4118  labelList& procSend = sendMap[proci];
4119  procSend[nSend[proci]++] = samplei;
4120  }
4121  }
4122  }
4123  }
4124 
4125  mapPtr.reset(new mapDistribute(std::move(sendMap)));
4126  }
4127  const mapDistribute& map = *mapPtr;
4128 
4129  // Get the points I need to test
4130  pointField localPoints(samples);
4131  map.distribute(localPoints);
4132 
4133  volType.setSize(localPoints.size());
4134  volType = volumeType::UNKNOWN;
4135 
4136  // Split the local queries into those that I can look up on the tree and
4137  // those I need to search the nearest for
4138  DynamicField<point> fullSearchPoints(localPoints.size());
4139  DynamicList<label> fullSearchMap(localPoints.size());
4140  forAll(localPoints, i)
4141  {
4142  volType[i] = cachedVolumeType(0, localPoints[i]);
4143  if (volType[i] == volumeType::UNKNOWN)
4144  {
4145  fullSearchMap.append(i);
4146  fullSearchPoints.append(localPoints[i]);
4147  }
4148  }
4149 
4150  if (debug)
4151  {
4152  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4153  << " original samples:" << samples.size()
4154  << " resulting in local queries:"
4155  << localPoints.size()
4156  << " of which cached:" << localPoints.size()-fullSearchPoints.size()
4157  << endl;
4158  }
4159 
4160  // Determine nearest (in parallel)
4161  List<pointIndexHit> nearestInfo;
4162  findNearest
4163  (
4164  fullSearchPoints,
4165  scalarField(fullSearchPoints.size(), Foam::sqr(GREAT)),
4166  nearestInfo
4167  );
4168 
4169  // Determine orientation (in parallel)
4170  List<volumeType> fullSearchType;
4171  surfaceSide(fullSearchPoints, nearestInfo, fullSearchType);
4172 
4173  // Combine
4174  forAll(fullSearchMap, i)
4175  {
4176  volType[fullSearchMap[i]] = fullSearchType[i];
4177  }
4178 
4179  // Send back to originator. In case of multiple answers choose inside or
4180  // outside
4183  (
4185  List<labelPair>(0),
4186  samples.size(),
4187  map.constructMap(),
4188  map.constructHasFlip(),
4189  map.subMap(),
4190  map.subHasFlip(),
4191  volType,
4192  zero,
4193  volumeCombineOp(),
4194  noOp(), // no flipping
4196  map.comm()
4197  );
4198 
4199 
4200  // Add the points outside the bounding box
4201  for (label samplei : outsideSamples)
4202  {
4203  volType[samplei] = outsideVolType_;
4204  }
4205 
4206  if (debug)
4207  {
4208  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4209  << " finished finding orientation for " << samples.size()
4210  << " samples" << endl;
4211  }
4212 }
4213 
4214 
4217  const List<pointIndexHit>& info,
4218  labelList& values
4219 ) const
4220 {
4221  if (!Pstream::parRun())
4222  {
4224  return;
4225  }
4226 
4227  if (debug)
4228  {
4229  Pout<< "distributedTriSurfaceMesh::getField :"
4230  << " retrieving field for "
4231  << info.size() << " triangles" << endl;
4232  }
4233 
4234  addProfiling(getField, "distributedTriSurfaceMesh::getField");
4235 
4236  const auto* fldPtr = findObject<triSurfaceLabelField>("values");
4237 
4238  if (fldPtr)
4239  {
4240  const triSurfaceLabelField& fld = *fldPtr;
4241 
4242  // Get query data (= local index of triangle)
4243  // ~~~~~~~~~~~~~~
4244 
4245  labelList triangleIndex(info.size());
4246  autoPtr<mapDistribute> mapPtr
4247  (
4248  localQueries
4249  (
4250  info,
4251  triangleIndex
4252  )
4253  );
4254  const mapDistribute& map = mapPtr();
4255 
4256 
4257  // Do my tests
4258  // ~~~~~~~~~~~
4259 
4260  values.setSize(triangleIndex.size());
4261 
4262  forAll(triangleIndex, i)
4263  {
4264  label trii = triangleIndex[i];
4265  values[i] = fld[trii];
4266  }
4267 
4268 
4269  // Send back results
4270  // ~~~~~~~~~~~~~~~~~
4271 
4272  map.reverseDistribute(info.size(), values);
4273  }
4274 
4275  if (debug)
4276  {
4277  Pout<< "distributedTriSurfaceMesh::getField :"
4278  << " finished retrieving field for "
4279  << info.size() << " triangles" << endl;
4280  }
4281 }
4282 
4283 
4286  const triSurface& s,
4287  const List<treeBoundBox>& bbs,
4288  boolList& includedFace
4289 )
4290 {
4291  // Determine what triangles to keep.
4292  includedFace.setSize(s.size());
4293  includedFace = false;
4294 
4295  // Create a slightly larger bounding box.
4296  List<treeBoundBox> bbsX(bbs.size());
4297  const scalar eps = 1.0e-4;
4298  forAll(bbs, i)
4299  {
4300  const point mid = bbs[i].centre();
4301  const vector halfSpan = (1.0+eps)*(bbs[i].max() - mid);
4302 
4303  bbsX[i].min() = mid - halfSpan;
4304  bbsX[i].max() = mid + halfSpan;
4305  }
4306 
4307  forAll(s, trii)
4308  {
4309  const labelledTri& f = s[trii];
4310  const point& p0 = s.points()[f[0]];
4311  const point& p1 = s.points()[f[1]];
4312  const point& p2 = s.points()[f[2]];
4313 
4314  if (overlaps(bbsX, p0, p1, p2))
4315  {
4316  includedFace[trii] = true;
4317  }
4318  }
4319 }
4320 
4321 
4322 // Subset the part of surface that is overlapping bb.
4325  const triSurface& s,
4326  const List<treeBoundBox>& bbs,
4327 
4328  labelList& subPointMap,
4329  labelList& subFaceMap
4330 )
4331 {
4332  // Determine what triangles to keep.
4333  boolList includedFace;
4334  overlappingSurface(s, bbs, includedFace);
4335  return subsetMesh(s, includedFace, subPointMap, subFaceMap);
4336 }
4337 
4338 
4339 // Exchanges indices to the processor they come from.
4340 // - calculates exchange map
4341 // - uses map to calculate local triangle index
4345  const List<pointIndexHit>& info,
4346  labelList& triangleIndex
4347 ) const
4348 {
4349  triangleIndex.setSize(info.size());
4350 
4351  const globalIndex& triIndexer = globalTris();
4352 
4353 
4354  // Determine send map
4355  // ~~~~~~~~~~~~~~~~~~
4356 
4357  // Since determining which processor the query should go to is
4358  // cheap we do a multi-pass algorithm to save some memory temporarily.
4359 
4360  // 1. Count
4361  labelList nSend(Pstream::nProcs(), 0);
4362 
4363  forAll(info, i)
4364  {
4365  if (info[i].hit())
4366  {
4367  label proci = triIndexer.whichProcID(info[i].index());
4368  nSend[proci]++;
4369  }
4370  }
4371 
4372  // 2. Size sendMap
4373  labelListList sendMap(Pstream::nProcs());
4374  forAll(nSend, proci)
4375  {
4376  sendMap[proci].setSize(nSend[proci]);
4377  nSend[proci] = 0;
4378  }
4379 
4380  // 3. Fill sendMap
4381  forAll(info, i)
4382  {
4383  if (info[i].hit())
4384  {
4385  label proci = triIndexer.whichProcID(info[i].index());
4386  triangleIndex[i] = triIndexer.toLocal(proci, info[i].index());
4387  sendMap[proci][nSend[proci]++] = i;
4388  }
4389  else
4390  {
4391  triangleIndex[i] = -1;
4392  }
4393  }
4394 
4395 
4396  // Send over how many i need to receive
4397  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4398 
4399  labelListList sendSizes(Pstream::nProcs());
4400  sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
4401  forAll(sendMap, proci)
4402  {
4403  sendSizes[Pstream::myProcNo()][proci] = sendMap[proci].size();
4404  }
4405  Pstream::gatherList(sendSizes);
4406  Pstream::scatterList(sendSizes);
4407 
4408 
4409  // Determine receive map
4410  // ~~~~~~~~~~~~~~~~~~~~~
4411 
4412  labelListList constructMap(Pstream::nProcs());
4413 
4414  // My local segments first
4415  constructMap[Pstream::myProcNo()] = identity
4416  (
4417  sendMap[Pstream::myProcNo()].size()
4418  );
4419 
4420  label segmenti = constructMap[Pstream::myProcNo()].size();
4421  forAll(constructMap, proci)
4422  {
4423  if (proci != Pstream::myProcNo())
4424  {
4425  // What i need to receive is what other processor is sending to me.
4426  label nRecv = sendSizes[proci][Pstream::myProcNo()];
4427  constructMap[proci].setSize(nRecv);
4428 
4429  for (label i = 0; i < nRecv; i++)
4430  {
4431  constructMap[proci][i] = segmenti++;
4432  }
4433  }
4434  }
4435 
4436 
4437  // Pack into distribution map
4438  // ~~~~~~~~~~~~~~~~~~~~~~~~~~
4439 
4440  autoPtr<mapDistribute> mapPtr
4441  (
4442  new mapDistribute
4443  (
4444  segmenti, // size after construction
4445  std::move(sendMap),
4446  std::move(constructMap)
4447  )
4448  );
4449  const mapDistribute& map = mapPtr();
4450 
4451 
4452  // Send over queries
4453  // ~~~~~~~~~~~~~~~~~
4454 
4455  map.distribute(triangleIndex);
4456 
4457  return mapPtr;
4458 }
4459 
4460 
4463  const List<treeBoundBox>& bbs,
4464  const bool keepNonLocal,
4466  autoPtr<mapDistribute>& pointMap
4467 )
4468 {
4469  if (!Pstream::parRun())
4470  {
4471  return;
4472  }
4473 
4474  if (debug)
4475  {
4476  Pout<< "distributedTriSurfaceMesh::distribute :"
4477  << " distributing surface according to method:"
4478  << distributionTypeNames_[distType_]
4479  << " follow bbs:" << flatOutput(bbs) << endl;
4480  }
4481 
4482  addProfiling(distribute, "distributedTriSurfaceMesh::distribute");
4483 
4484 
4485  // Get bbs of all domains
4486  // ~~~~~~~~~~~~~~~~~~~~~~
4487 
4488  {
4490 
4491  switch(distType_)
4492  {
4493  case FOLLOW:
4494  newProcBb[Pstream::myProcNo()].setSize(bbs.size());
4495  forAll(bbs, i)
4496  {
4497  newProcBb[Pstream::myProcNo()][i] = bbs[i];
4498  }
4499  Pstream::gatherList(newProcBb);
4500  Pstream::scatterList(newProcBb);
4501  break;
4502 
4503  case INDEPENDENT:
4504  case DISTRIBUTED:
4505  if (currentDistType_ == distType_)
4506  {
4507  return;
4508  }
4509  newProcBb = independentlyDistributedBbs(*this);
4510  break;
4511 
4512  case FROZEN:
4513  return;
4514  break;
4515 
4516  default:
4518  << "Unsupported distribution type." << exit(FatalError);
4519  break;
4520  }
4521 
4522  if (newProcBb == procBb_)
4523  {
4524  return;
4525  }
4526  else
4527  {
4528  procBb_.transfer(newProcBb);
4529  dict_.set("bounds", procBb_[Pstream::myProcNo()]);
4530  }
4531  }
4532 
4533 
4534  // Debug information
4535  if (debug)
4536  {
4537  labelList nTris(Pstream::nProcs());
4538  nTris[Pstream::myProcNo()] = triSurface::size();
4539  Pstream::gatherList(nTris);
4540  Pstream::scatterList(nTris);
4541 
4543  << "before distribution:" << endl << "\tproc\ttris" << endl;
4544 
4545  forAll(nTris, proci)
4546  {
4547  Info<< '\t' << proci << '\t' << nTris[proci] << endl;
4548  }
4549  Info<< endl;
4550  }
4551 
4552 
4553  // Use procBbs to determine which faces go where
4554  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4555 
4556  labelListList faceSendMap(Pstream::nProcs());
4557  labelListList pointSendMap(Pstream::nProcs());
4558 
4559  forAll(procBb_, proci)
4560  {
4561  overlappingSurface
4562  (
4563  *this,
4564  procBb_[proci],
4565  pointSendMap[proci],
4566  faceSendMap[proci]
4567  );
4568  }
4569 
4570  if (keepNonLocal)
4571  {
4572  // Include in faceSendMap/pointSendMap the triangles that are
4573  // not mapped to any processor so they stay local.
4574 
4575  const triSurface& s = static_cast<const triSurface&>(*this);
4576 
4577  boolList includedFace(s.size(), true);
4578 
4579  forAll(faceSendMap, proci)
4580  {
4581  if (proci != Pstream::myProcNo())
4582  {
4583  forAll(faceSendMap[proci], i)
4584  {
4585  includedFace[faceSendMap[proci][i]] = false;
4586  }
4587  }
4588  }
4589 
4590  // Combine includedFace (all triangles that are not on any neighbour)
4591  // with overlap.
4592 
4593  forAll(faceSendMap[Pstream::myProcNo()], i)
4594  {
4595  includedFace[faceSendMap[Pstream::myProcNo()][i]] = true;
4596  }
4597 
4598  subsetMesh
4599  (
4600  s,
4601  includedFace,
4602  pointSendMap[Pstream::myProcNo()],
4603  faceSendMap[Pstream::myProcNo()]
4604  );
4605  }
4606 
4607 
4608  // Send over how many faces/points i need to receive
4609  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4610 
4611  labelList faceRecvSizes;
4612  Pstream::exchangeSizes(faceSendMap, faceRecvSizes);
4613 
4614 
4615  // Exchange surfaces
4616  // ~~~~~~~~~~~~~~~~~
4617 
4618  // Storage for resulting surface
4619  List<labelledTri> allTris;
4621 
4622  labelListList faceConstructMap(Pstream::nProcs());
4623  labelListList pointConstructMap(Pstream::nProcs());
4624 
4625 
4626  // My own surface first
4627  // ~~~~~~~~~~~~~~~~~~~~
4628 
4629  {
4630  labelList pointMap;
4631  triSurface subSurface
4632  (
4633  subsetMesh
4634  (
4635  *this,
4636  faceSendMap[Pstream::myProcNo()],
4637  pointMap
4638  )
4639  );
4640 
4641  allTris = subSurface;
4642  allPoints = subSurface.points();
4643 
4644  faceConstructMap[Pstream::myProcNo()] = identity
4645  (
4646  faceSendMap[Pstream::myProcNo()].size()
4647  );
4648  pointConstructMap[Pstream::myProcNo()] = identity
4649  (
4650  pointSendMap[Pstream::myProcNo()].size()
4651  );
4652  }
4653 
4654 
4655 
4656  // Send all
4657  // ~~~~~~~~
4658 
4660 
4661  forAll(faceSendMap, proci)
4662  {
4663  if (proci != Pstream::myProcNo())
4664  {
4665  if (faceSendMap[proci].size() > 0)
4666  {
4667  UOPstream str(proci, pBufs);
4668 
4669  labelList pointMap;
4670  triSurface subSurface
4671  (
4672  subsetMesh
4673  (
4674  *this,
4675  faceSendMap[proci],
4676  pointMap
4677  )
4678  );
4679  str << subSurface;
4680  }
4681  }
4682  }
4683 
4684  pBufs.finishedSends(); // no-op for blocking
4685 
4686 
4687  // Receive and merge all
4688  // ~~~~~~~~~~~~~~~~~~~~~
4689 
4690  forAll(faceRecvSizes, proci)
4691  {
4692  if (proci != Pstream::myProcNo())
4693  {
4694  if (faceRecvSizes[proci] > 0)
4695  {
4696  UIPstream str(proci, pBufs);
4697 
4698  // Receive
4699  triSurface subSurface(str);
4700 
4701  // Merge into allSurf
4702  merge
4703  (
4704  mergeDist_,
4705  subSurface,
4706  subSurface.points(),
4707 
4708  allTris,
4709  allPoints,
4710  faceConstructMap[proci],
4711  pointConstructMap[proci]
4712  );
4713  }
4714  }
4715  }
4716 
4717 
4718  faceMap.reset
4719  (
4720  new mapDistribute
4721  (
4722  allTris.size(),
4723  std::move(faceSendMap),
4724  std::move(faceConstructMap)
4725  )
4726  );
4727  pointMap.reset
4728  (
4729  new mapDistribute
4730  (
4731  allPoints.size(),
4732  std::move(pointSendMap),
4733  std::move(pointConstructMap)
4734  )
4735  );
4736 
4737  // Construct triSurface. Reuse storage.
4738  triSurface::operator=(triSurface(allTris, patches(), allPoints, true));
4739 
4740  // Clear trees, preserve topological info (surfaceClosed, outsidePointType)
4741  clearOut();
4742 
4743  // Set the bounds() value to the boundBox of the undecomposed surface
4744  bounds() = boundBox(points(), true);
4745 
4746  currentDistType_ = distType_;
4747 
4748  // Regions stays same
4749  // Volume type stays same.
4750 
4751  distributeFields<label>(faceMap());
4752  distributeFields<scalar>(faceMap());
4753  distributeFields<vector>(faceMap());
4754  distributeFields<sphericalTensor>(faceMap());
4755  distributeFields<symmTensor>(faceMap());
4756  distributeFields<tensor>(faceMap());
4757 
4758  if (debug)
4759  {
4760  labelList nTris(Pstream::nProcs());
4761  nTris[Pstream::myProcNo()] = triSurface::size();
4762  Pstream::gatherList(nTris);
4763  Pstream::scatterList(nTris);
4764 
4766  << "after distribution:" << endl << "\tproc\ttris" << endl;
4767 
4768  forAll(nTris, proci)
4769  {
4770  Info<< '\t' << proci << '\t' << nTris[proci] << endl;
4771  }
4772  Info<< endl;
4773 
4774  if (debug & 2)
4775  {
4776  OBJstream str(searchableSurface::time().path()/"after.obj");
4777  Info<< "Writing local bounding box to " << str.name() << endl;
4778  const List<treeBoundBox>& myBbs = procBb_[Pstream::myProcNo()];
4779  forAll(myBbs, i)
4780  {
4781  pointField pts(myBbs[i].points());
4782  const edgeList& es = treeBoundBox::edges;
4783  forAll(es, ei)
4784  {
4785  const edge& e = es[ei];
4786  str.write(linePointRef(pts[e[0]], pts[e[1]]));
4787  }
4788  }
4789  }
4790  if (debug & 2)
4791  {
4792  OBJstream str(searchableSurface::time().path()/"after_all.obj");
4793  Info<< "Writing all bounding boxes to " << str.name() << endl;
4794  for (auto myBbs : procBb_)
4795  {
4796  forAll(myBbs, i)
4797  {
4798  pointField pts(myBbs[i].points());
4799  const edgeList& es = treeBoundBox::edges;
4800  forAll(es, ei)
4801  {
4802  const edge& e = es[ei];
4803  str.write(linePointRef(pts[e[0]], pts[e[1]]));
4804  }
4805  }
4806  }
4807  }
4808  }
4809 
4810  if (debug)
4811  {
4812  Pout<< "distributedTriSurfaceMesh::distribute :"
4813  << " done distributing surface according to method:"
4814  << distributionTypeNames_[distType_]
4815  << " follow bbs:" << flatOutput(bbs) << endl;
4816  }
4817 }
4818 
4819 
4822  IOstreamOption streamOpt,
4823  const bool valid
4824 ) const
4825 {
4826  if (debug)
4827  {
4828  Pout<< "distributedTriSurfaceMesh::writeObject :"
4829  << " writing surface valid:" << valid << endl;
4830  }
4831 
4832  // Make sure dictionary goes to same directory as surface
4833  const_cast<fileName&>(dict_.instance()) = searchableSurface::instance();
4834 
4835  // Copy of triSurfaceMesh::writeObject except for the sorting of
4836  // triangles by region. This is done so we preserve region names,
4837  // even if locally we have zero triangles.
4838  {
4840 
4841  if (!mkDir(fullPath.path()))
4842  {
4843  return false;
4844  }
4845 
4846  // Important: preserve any zero-sized patches
4847  triSurface::write(fullPath, true);
4848 
4849  if (!isFile(fullPath))
4850  {
4851  return false;
4852  }
4853  }
4854 
4855  // Dictionary needs to be written in ascii - binary output not supported.
4856  streamOpt.format(IOstream::ASCII);
4857  bool ok = dict_.writeObject(streamOpt, true);
4858 
4859  if (debug)
4860  {
4861  Pout<< "distributedTriSurfaceMesh::writeObject :"
4862  << " done writing surface" << endl;
4863  }
4864 
4865  return ok;
4866 }
4867 
4868 
4870 {
4871  boundBox bb;
4872  label nPoints;
4873  PatchTools::calcBounds(static_cast<const triSurface&>(*this), bb, nPoints);
4874  bb.reduce();
4875 
4876  os << "Triangles : " << returnReduce(triSurface::size(), sumOp<label>())
4877  << endl
4878  << "Vertices : " << returnReduce(nPoints, sumOp<label>()) << endl
4879  << "Bounding Box : " << bb << endl
4880  << "Closed : " << surfaceClosed_ << endl
4881  << "Outside point: " << volumeType::names[outsideVolType_] << endl
4882  << "Distribution : " << distributionTypeNames_[distType_] << endl;
4883 }
4884 
4885 
4886 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::PrimitivePatch::points
const Field< point_type > & points() const noexcept
Return reference to global points.
Definition: PrimitivePatch.H:299
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
Foam::IOobject::NO_WRITE
Definition: IOobject.H:195
Foam::autoPtr::New
static autoPtr< T > New(Args &&... args)
Construct autoPtr of T with forwarding arguments.
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:67
profiling.H
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
Foam::mapDistributeBase::subMap
const labelListList & subMap() const
From subsetted data back to original data.
Definition: mapDistributeBase.H:289
Foam::scalarField
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
Definition: primitiveFieldsFwd.H:52
Foam::mapDistributeBase::constructHasFlip
bool constructHasFlip() const
Does constructMap include a sign.
Definition: mapDistributeBase.H:325
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:169
distributedTriSurfaceMesh.H
Foam::boundBox::reduce
void reduce()
Parallel reduction of min/max values.
Definition: boundBox.C:184
InfoInFunction
#define InfoInFunction
Report an information message using Foam::Info.
Definition: messageStream.H:350
geomDecomp.H
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:57
Foam::volumeType::type
type
Volume classification types.
Definition: volumeType.H:65
Foam::distributedTriSurfaceMesh::distributionTypeNames_
static const Enum< distributionType > distributionTypeNames_
Definition: distributedTriSurfaceMesh.H:101
Foam::faceMap
Pair< int > faceMap(const label facePi, const face &faceP, const label faceNi, const face &faceN)
Definition: blockMeshMergeTopological.C:94
labelBits.H
vectorList.H
Foam::IOobject::localFilePath
fileName localFilePath(const word &typeName, const bool search=true) const
Helper for filePath that searches locally.
Definition: IOobject.C:569
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:65
Foam::nearestEqOp
Definition: distributedTriSurfaceMesh.C:124
Foam::UOPstream
Output inter-processor communications stream operating on external buffer.
Definition: UOPstream.H:57
Foam::fileName
A class for handling file names.
Definition: fileName.H:73
Foam::IOobject::registerObject
bool registerObject() const noexcept
Should object created with this IOobject be registered?
Definition: IOobjectI.H:107
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
s
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))
Definition: gmvOutputSpray.H:25
Foam::OBJstream
OFstream that keeps track of vertices.
Definition: OBJstream.H:58
Foam::nearestAndDist
Tuple2< pointIndexHit, scalar > nearestAndDist
Combine operator for nearest.
Definition: distributedTriSurfaceMesh.C:114
Foam::OFstream::name
virtual const fileName & name() const
Read/write access to the name of the stream.
Definition: OSstream.H:107
Foam::IOobject::instance
const fileName & instance() const noexcept
Definition: IOobjectI.H:196
Foam::fileName::path
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:176
Foam::nearestEqOp::operator()
void operator()(nearestAndDist &x, const nearestAndDist &y) const
Definition: distributedTriSurfaceMesh.C:127
Foam::Zero
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
PatchTools.H
Foam::indexedOctree::isContent
static bool isContent(const labelBits i)
Definition: indexedOctree.H:482
Foam::DynamicList< label >
Foam::triSurfaceMesh::getNormal
virtual void getNormal(const List< pointIndexHit > &, vectorField &normal) const
From a set of points and indices get the normal.
Definition: triSurfaceMesh.C:954
Foam::HashTableOps::values
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:149
Foam::Pstream::scatterList
static void scatterList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Scatter data. Reverse of gatherList.
Definition: gatherScatterList.C:215
globalIndex.H
Foam::boundBox::invertedBox
static const boundBox invertedBox
A large inverted boundBox: min/max == +/- ROOTVGREAT.
Definition: boundBox.H:86
Foam::distributedTriSurfaceMesh::clearOut
void clearOut()
Clear storage.
Definition: distributedTriSurfaceMesh.C:2748
Foam::fileName::name
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:199
Foam::read
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:108
Foam::PstreamBuffers
Buffers for inter-processor communications streams (UOPstream, UIPstream).
Definition: PstreamBuffers.H:88
Foam::indexedOctree::isNode
static bool isNode(const labelBits i)
Definition: indexedOctree.H:492
Foam::system
int system(const std::string &command, const bool bg=false)
Execute the specified command via the shell.
Definition: MSwindows.C:1150
Foam::Pstream::exchangeSizes
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:349
Foam::edge
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:63
Foam::indexedOctree::nodes
const List< node > & nodes() const
List of all nodes.
Definition: indexedOctree.H:450
Foam::IOstreamOption::format
streamFormat format() const noexcept
Get the current stream format.
Definition: IOstreamOption.H:286
Foam::PatchTools::calcBounds
static void calcBounds(const PrimitivePatch< FaceList, PointField > &p, boundBox &bb, label &nPoints)
Definition: PatchToolsSearch.C:178
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Am I the master process.
Definition: UPstream.H:457
Foam::Pstream::scatter
static void scatter(const List< commsStruct > &comms, T &Value, const int tag, const label comm)
Scatter data. Distribute without modification. Reverse of gather.
Definition: gatherScatter.C:150
Foam::boolList
List< bool > boolList
A List of bools.
Definition: List.H:65
Foam::isFile
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: MSwindows.C:658
Foam::fileHandler
const fileOperation & fileHandler()
Get current file handler.
Definition: fileOperation.C:1485
Foam::List::append
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:175
Foam::triSurfaceMesh
IOoject and searching on triSurface.
Definition: triSurfaceMesh.H:106
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:369
Foam::treeBoundBox::edges
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:154
Foam::PointIndexHit::hitPoint
const point_type & hitPoint() const
Return hit point. Fatal if not hit.
Definition: PointIndexHit.H:154
Foam::UPstream::defaultCommsType
static commsTypes defaultCommsType
Default commsType.
Definition: UPstream.H:281
Foam::distributedTriSurfaceMesh::findLineAll
virtual void findLineAll(const pointField &start, const pointField &end, List< List< pointIndexHit >> &) const
Get all intersections in order from start to end.
Definition: distributedTriSurfaceMesh.C:3573
decompositionMethod.H
Foam::distributedTriSurfaceMesh::writeStats
void writeStats(Ostream &os) const
Print some stats. Parallel aware version of.
Definition: distributedTriSurfaceMesh.C:4869
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::IOobject::writeOpt
writeOption writeOpt() const noexcept
The write option.
Definition: IOobjectI.H:179
Foam::OBJstream::write
virtual Ostream & write(const char c)
Write character.
Definition: OBJstream.C:78
bitSet.H
Foam::indexedOctree::getNode
static label getNode(const labelBits i)
Definition: indexedOctree.H:507
Foam::IOobject::time
const Time & time() const
Return Time associated with the objectRegistry.
Definition: IOobject.C:493
Foam::instantList
List< instant > instantList
List of instants.
Definition: instantList.H:44
Foam::distributedTriSurfaceMesh::overlappingSurface
static void overlappingSurface(const triSurface &, const List< treeBoundBox > &, boolList &includedFace)
Calculate the triangles that are overlapping bounds.
Definition: distributedTriSurfaceMesh.C:4285
Foam::distributedTriSurfaceMesh::getRegion
virtual void getRegion(const List< pointIndexHit > &, labelList &region) const
From a set of points and indices get the region.
Definition: distributedTriSurfaceMesh.C:3734
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
Foam::triSurfaceMesh::findLineAny
virtual void findLineAny(const pointField &start, const pointField &end, List< pointIndexHit > &) const
Return any intersection on segment from start to end.
Definition: triSurfaceMesh.C:875
Foam::sumOp
Definition: ops.H:213
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::distributedTriSurfaceMesh::findNearest
virtual void findNearest(const pointField &sample, const scalarField &nearestDistSqr, List< pointIndexHit > &) const
Definition: distributedTriSurfaceMesh.C:2996
Foam::magSqr
dimensioned< typename typeOfMag< Type >::type > magSqr(const dimensioned< Type > &dt)
Foam::invertManyToMany
void invertManyToMany(const label len, const UList< InputIntListType > &input, List< OutputIntListType > &output)
Invert many-to-many.
Definition: ListOpsTemplates.C:720
Foam::distributedTriSurfaceMesh::distributionType
distributionType
Definition: distributedTriSurfaceMesh.H:93
Foam::DynamicField
Dynamically sized Field.
Definition: DynamicField.H:49
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
Foam::labelPair
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:54
Foam::triangleFuncs::intersectBb
static bool intersectBb(const point &p0, const point &p1, const point &p2, const treeBoundBox &cubeBb)
Intersect triangle with bounding box.
Definition: triangleFuncs.C:152
Foam::distributedTriSurfaceMesh::globalTris
const globalIndex & globalTris() const
Triangle indexing (demand driven)
Definition: distributedTriSurfaceMesh.C:2757
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::triSurface::checkFile
static fileName checkFile(const IOobject &io, const bool isGlobal=true)
Return fileName to load IOobject from.
Definition: triSurfaceIO.C:122
Foam::volumeCombineOp::operator()
void operator()(volumeType &x, const volumeType &y) const
Definition: distributedTriSurfaceMesh.C:65
Foam::distributedTriSurfaceMesh::localQueries
virtual autoPtr< mapDistribute > localQueries(const List< pointIndexHit > &, labelList &triangleIndex) const
Obtains global indices from pointIndexHit and swaps them back.
Definition: distributedTriSurfaceMesh.C:4344
Foam::distributedTriSurfaceMesh::getField
virtual void getField(const List< pointIndexHit > &, labelList &) const
WIP. From a set of hits (points and.
Definition: distributedTriSurfaceMesh.C:4216
Foam::VectorSpace::min
static const Form min
Definition: VectorSpace.H:118
matchPoints.H
Determine correspondence between points. See below.
Foam::PointIndexHit
This class describes the interaction of (usually) a face and a point. It carries the info of a succes...
Definition: PointIndexHit.H:52
Foam::UPstream::subProcs
static rangeType subProcs(const label communicator=worldComm)
Range of process indices for sub-processes.
Definition: UPstream.H:515
Foam::PointIndexHit::hit
bool hit() const noexcept
Is there a hit?
Definition: PointIndexHit.H:130
Foam::volumeType
An enumeration wrapper for classification of a location as being inside/outside of a volume.
Definition: volumeType.H:60
Foam::Field< vector >
Foam::triSurface::write
void write(Ostream &os) const
Write to Ostream in simple OpenFOAM format.
Definition: triSurfaceIO.C:336
Foam::triSurface
Triangulated surface description with patch information.
Definition: triSurface.H:76
Foam::distributedTriSurfaceMesh::DISTRIBUTED
Definition: distributedTriSurfaceMesh.H:97
Foam::Info
messageStream Info
Information stream (stdout output on master, null elsewhere)
DebugInFunction
#define DebugInFunction
Report an information message using Foam::Info.
Definition: messageStream.H:388
Foam::IOobject::local
const fileName & local() const noexcept
Definition: IOobjectI.H:208
Foam::mapDistributeBase::distribute
static void distribute(const Pstream::commsTypes commsType, const List< labelPair > &schedule, const label constructSize, const labelListList &subMap, const bool subHasFlip, const labelListList &constructMap, const bool constructHasFlip, List< T > &, const negateOp &negOp, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Distribute data. Note:schedule only used for.
Definition: mapDistributeBaseTemplates.C:122
Foam::volumeType::UNKNOWN
Unknown state.
Definition: volumeType.H:67
Foam::volumeType::names
static const Enum< volumeType::type > names
Names for the classification enumeration.
Definition: volumeType.H:76
Foam::List::setSize
void setSize(const label n)
Alias for resize()
Definition: List.H:222
Foam::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:511
Foam::Enum::getOrDefault
EnumType getOrDefault(const word &key, const dictionary &dict, const EnumType deflt, const bool failsafe=false) const
Definition: Enum.C:179
Foam::searchableSurface
Base class of (analytical or triangulated) surface. Encapsulates all the search routines....
Definition: searchableSurface.H:69
Foam::mapDistribute
Class containing processor-to-processor mapping information.
Definition: mapDistribute.H:163
Foam::distributedTriSurfaceMesh::getVolumeType
virtual void getVolumeType(const pointField &, List< volumeType > &) const
Determine type (inside/outside/mixed) for point. unknown if.
Definition: distributedTriSurfaceMesh.C:3951
Foam::volumeType::MIXED
A location that is partly inside and outside.
Definition: volumeType.H:70
Foam::List::transfer
void transfer(List< T > &list)
Definition: List.C:456
Foam::IOobject::READ_IF_PRESENT
Definition: IOobject.H:187
Foam::triangle::NONE
Unknown proximity.
Definition: triangle.H:95
Foam::IOstreamOption
The IOstreamOption is a simple container for options an IOstream can normally have.
Definition: IOstreamOption.H:63
Foam::triSurfaceMesh::findLine
virtual void findLine(const pointField &start, const pointField &end, List< pointIndexHit > &) const
Find first intersection on segment from start to end.
Definition: triSurfaceMesh.C:852
Foam::indexedOctree
Non-pointer based hierarchical recursive searching.
Definition: treeDataEdge.H:50
samples
scalarField samples(nIntervals, Zero)
Foam::UPstream::procID
static List< int > & procID(label communicator)
Process ID of given process index.
Definition: UPstream.H:474
Foam::triSurfaceMesh::getField
virtual void getField(const List< pointIndexHit > &, labelList &) const
WIP. From a set of hits (points and.
Definition: triSurfaceMesh.C:1076
Foam::distributedTriSurfaceMesh::INDEPENDENT
Definition: distributedTriSurfaceMesh.H:96
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
IFstream.H
fld
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;for(const word &name :lagrangianScalarNames){ IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
Definition: gmvOutputLagrangian.H:23
Foam::edgeMeshTools::writeStats
void writeStats(Ostream &os, const extendedFeatureEdgeMesh &emesh)
Write some information.
Definition: edgeMeshTools.C:36
Foam::mapDistribute::distribute
void distribute(List< T > &fld, const bool dummyTransform=true, const int tag=UPstream::msgType()) const
Distribute data using default commsType.
Definition: mapDistributeTemplates.C:152
Foam::PstreamBuffers::finishedSends
void finishedSends(const bool block=true)
Mark all sends as having been done.
Definition: PstreamBuffers.C:73
Foam::PackedList::setSize
void setSize(const label n, unsigned int val=0u)
Alias for resize()
Definition: PackedList.H:558
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::triSurfaceMesh::clearOut
void clearOut()
Clear storage.
Definition: triSurfaceMesh.C:530
addProfiling
#define addProfiling(name, descr)
Define profiling trigger with specified name and description string.
Definition: profilingTrigger.H:113
Foam::segment
Pair< point > segment
Definition: distributedTriSurfaceMesh.H:75
Foam::mapDistributeBase::comm
label comm() const
Definition: mapDistributeBase.H:336
Foam::FatalError
error FatalError
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:123
Foam::pointHit
PointHit< point > pointHit
A PointIndexHit for 3D points.
Definition: pointHit.H:44
os
OBJstream os(runTime.globalPath()/outputName)
addToRunTimeSelectionTable.H
Macros for easy insertion into run-time selection tables.
stdFoam::end
constexpr auto end(C &c) -> decltype(c.end())
Return iterator to the end of the container c.
Definition: stdFoam.H:121
Foam::mapDistributeBase::subHasFlip
bool subHasFlip() const
Does subMap include a sign.
Definition: mapDistributeBase.H:313
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::flatOutput
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:216
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:144
triangleFuncs.H
Foam::globalIndex
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:68
Foam::nearestZero
const nearestAndDist nearestZero(nearestAndDist(pointIndexHit(), -GREAT))
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
Foam::globalIndex::reset
void reset(const label localSize)
Reset from local size.
Definition: globalIndexI.H:157
Foam::distributedTriSurfaceMesh::getNormal
virtual void getNormal(const List< pointIndexHit > &, vectorField &normal) const
From a set of points and indices get the normal.
Definition: distributedTriSurfaceMesh.C:3817
Foam::mergePoints
label mergePoints(const PointList &points, const scalar mergeTol, const bool verbose, labelList &pointMap, typename PointList::const_reference origin=PointList::value_type::zero)
Sorts and merges points. All points closer than/equal mergeTol get merged.
Random.H
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
Foam::IOobject::readOpt
readOption readOpt() const noexcept
The read option.
Definition: IOobjectI.H:164
Foam::Pstream::listCombineGather
static void listCombineGather(const List< commsStruct > &comms, List< T > &Value, const CombineOp &cop, const int tag, const label comm)
Definition: combineGatherScatter.C:290
Foam::autoPtr::reset
void reset(autoPtr< T > &&other) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:117
Foam::IOobject::name
const word & name() const noexcept
Return name.
Definition: IOobjectI.H:65
Foam::distributedTriSurfaceMesh::distribute
virtual void distribute(const List< treeBoundBox > &, const bool keepNonLocal, autoPtr< mapDistribute > &faceMap, autoPtr< mapDistribute > &pointMap)
Set bounds of surface. Bounds currently set as list of.
Definition: distributedTriSurfaceMesh.C:4462
Time.H
Foam::globalIndex::toLocal
label toLocal(const label i) const
From global to local on current processor.
Definition: globalIndexI.H:305
Foam::autoPtr< Foam::mapDistribute >
Foam::labelListList
List< labelList > labelListList
A List of labelList.
Definition: labelList.H:56
Foam::distributedTriSurfaceMesh::~distributedTriSurfaceMesh
virtual ~distributedTriSurfaceMesh()
Destructor.
Definition: distributedTriSurfaceMesh.C:2742
Foam::linePointRef
line< point, const point & > linePointRef
A line using referred points.
Definition: linePointRef.H:47
Foam::IOstreamOption::ASCII
"ascii" (normal default)
Definition: IOstreamOption.H:72
Foam::getField
tmp< GeoField > getField(const IOobject *io, const typename GeoField::Mesh &mesh)
Get the field or return nullptr.
Definition: readFields.H:53
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
Foam::Pstream::gatherList
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
Definition: gatherScatterList.C:52
Foam::sqr
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Definition: dimensionedSymmTensor.C:51
Foam::pointIndexHit
PointIndexHit< point > pointIndexHit
A PointIndexHit for 3D points.
Definition: pointIndexHit.H:46
Foam::UPstream::commsTypes::nonBlocking
Foam::triangle::EDGE
Close to edge.
Definition: triangle.H:97
Foam::UPstream::msgType
static int & msgType() noexcept
Message tag of standard messages.
Definition: UPstream.H:540
Foam::objectRegistry::findObject
const Type * findObject(const word &name, const bool recursive=false) const
Return const pointer to the object of the given Type.
Definition: objectRegistryTemplates.C:401
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:463
Foam::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::nl
constexpr char nl
Definition: Ostream.H:404
Foam::VectorSpace::max
static const Form max
Definition: VectorSpace.H:117
mapDistribute.H
f
labelList f(nPoints)
Foam::triSurfaceMesh::findNearest
virtual void findNearest(const pointField &sample, const scalarField &nearestDistSqr, List< pointIndexHit > &) const
Definition: triSurfaceMesh.C:795
Foam::Vector< scalar >
Foam::UPstream::worldComm
static label worldComm
Default communicator (all processors)
Definition: UPstream.H:293
Foam::UPstream::parRun
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:433
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:63
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::PointIndexHit::index
label index() const noexcept
Return the hit index.
Definition: PointIndexHit.H:136
Foam::PackedList< 2 >
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::globalIndex::whichProcID
label whichProcID(const label i) const
Which processor does global come from? Binary search.
Definition: globalIndexI.H:311
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
Foam::List::clear
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:116
Foam::decompositionMethod::New
static autoPtr< decompositionMethod > New(const dictionary &decompDict, const word &regionName="")
Definition: decompositionMethod.C:344
path
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
Foam::direction
uint8_t direction
Definition: direction.H:52
Foam::word::null
static const word null
An empty word.
Definition: word.H:80
Foam::IOobject::MUST_READ_IF_MODIFIED
Definition: IOobject.H:186
patches
const polyBoundaryMesh & patches
Definition: convertProcessorPatches.H:65
x
x
Definition: LISASMDCalcMethod2.H:52
Foam::volumeType::INSIDE
A location inside the volume.
Definition: volumeType.H:68
Foam::labelledTri
A triFace with additional (region) index.
Definition: labelledTri.H:57
Foam::Tuple2::second
const T2 & second() const noexcept
Return second.
Definition: Tuple2.H:130
Foam::boundBox
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
Foam::constant::universal::c
const dimensionedScalar c
Speed of light in a vacuum.
Foam::PtrListOps::get
List< ReturnType > get(const UPtrList< T > &list, const AccessOp &aop)
Foam::name
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:59
Foam::mapDistribute::reverseDistribute
void reverseDistribute(const label constructSize, List< T > &, const bool dummyTransform=true, const int tag=UPstream::msgType()) const
Reverse distribute data using default commsType.
Definition: mapDistributeTemplates.C:182
Foam::UIndirectList
A List with indirect addressing.
Definition: faMatrix.H:60
Foam::distributedTriSurfaceMesh::findLineAny
virtual void findLineAny(const pointField &start, const pointField &end, List< pointIndexHit > &) const
Return any intersection on segment from start to end.
Definition: distributedTriSurfaceMesh.C:3549
Foam::DelaunayMeshTools::allPoints
tmp< pointField > allPoints(const Triangulation &t)
Extract all points in vertex-index order.
Foam::triSurface::face_type
labelledTri face_type
The face type (same as the underlying PrimitivePatch)
Definition: triSurface.H:209
Foam::searchableSurface::bounds
virtual const boundBox & bounds() const
Return const reference to boundBox.
Definition: searchableSurface.H:179
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::UIPstream
Input inter-processor communications stream operating on external buffer.
Definition: UIPstream.H:56
p0
const volScalarField & p0
Definition: EEqn.H:36
Foam::triSurfaceMesh::getVolumeType
virtual void getVolumeType(const pointField &points, List< volumeType > &volType) const
Determine type (inside/outside/mixed) for point.
Definition: triSurfaceMesh.C:1107
Foam::Tuple2
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: stringOps.H:60
Foam::Pstream::listCombineScatter
static void listCombineScatter(const List< commsStruct > &comms, List< T > &Value, const int tag, const label comm)
Scatter data. Reverse of combineGather.
Definition: combineGatherScatter.C:432
Foam::triSurface::operator=
void operator=(const triSurface &surf)
Copy assignment.
Definition: triSurface.C:999
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::IOobject::objectPath
fileName objectPath() const
The complete path + object name.
Definition: IOobjectI.H:214
Foam::distributedTriSurfaceMesh::writeObject
virtual bool writeObject(IOstreamOption streamOpt, const bool valid) const
Write using stream options.
Definition: distributedTriSurfaceMesh.C:4821
Foam::triSurfaceMesh::masterOnly
Definition: triSurfaceMesh.H:201
Foam::mapDistributeBase::constructMap
const labelListList & constructMap() const
From subsetted data to new reconstructed data.
Definition: mapDistributeBase.H:301
Foam::IOobject::NO_READ
Definition: IOobject.H:188
Foam::treeBoundBoxList
List< treeBoundBox > treeBoundBoxList
List of bounding boxes.
Definition: treeBoundBoxList.H:46
Foam::mkDir
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: MSwindows.C:507
Foam::indexedOctree::nodeTypes
PackedList< 2 > & nodeTypes() const
Per node, per octant whether is fully inside/outside/mixed.
Definition: indexedOctree.H:474
Foam::defineTypeNameAndDebug
defineTypeNameAndDebug(combustionModel, 0)
Foam::Tuple2::first
const T1 & first() const noexcept
Return first.
Definition: Tuple2.H:118
Foam::matchPoints
bool matchPoints(const UList< point > &pts0, const UList< point > &pts1, const UList< scalar > &matchDistance, const bool verbose, labelList &from0To1, const point &origin=point::zero)
Determine correspondence between pointFields. Gets passed.
Definition: matchPoints.C:36
Foam::triSurfaceMesh::hasVolumeType
virtual bool hasVolumeType() const
Whether supports volume type (below) - i.e. whether is closed.
Definition: triSurfaceMesh.C:742
pFaces
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
OBJstream.H
Foam::triSurfaceMesh::findLineAll
virtual void findLineAll(const pointField &start, const pointField &end, List< List< pointIndexHit >> &) const
Get all intersections in order from start to end.
Definition: triSurfaceMesh.C:898
Foam::UPstream::nProcs
static label nProcs(const label communicator=worldComm)
Number of processes in parallel run, and 1 for serial run.
Definition: UPstream.H:445
Foam::volumeType::OUTSIDE
A location outside the volume.
Definition: volumeType.H:69
Foam::DimensionedField
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Definition: DimensionedField.H:54
Foam::isDir
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: MSwindows.C:643
Foam::noOp
Pass through value. Should never be specialized.
Definition: flipOp.H:64
y
scalar y
Definition: LISASMDCalcMethod1.H:14
sample
Minimal example by using system/controlDict.functions:
Foam::zero
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Definition: zero.H:62
Foam::IOobject::MUST_READ
Definition: IOobject.H:185
Foam::IOobject::db
const objectRegistry & db() const noexcept
Return the local objectRegistry.
Definition: IOobject.C:487
Foam::volumeCombineOp
Combine operator for volume types.
Definition: distributedTriSurfaceMesh.C:62