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-2019 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::FaceType& nearF = surf[nearFacei];
1110 
1111  const edge e(nearF[nearLabel], nearF[nearF.fcIndex(nearLabel)]);
1112 
1113  const labelList& pFaces = pointFaces[e[0]];
1114  forAll(pFaces, pFacei)
1115  {
1116  const label facei = pFaces[pFacei];
1117  if (facei != nearFacei)
1118  {
1119  const triSurface::FaceType& f = surf[facei];
1120 
1121  int dir = f.edgeDirection(e);
1122  if (dir != 0)
1123  {
1124  return facei;
1125  }
1126  }
1127  }
1128  return -1;
1129 }
1130 
1131 
1132 void Foam::distributedTriSurfaceMesh::calcFaceFaces
1133 (
1134  const triSurface& s,
1135  const labelListList& pointFaces,
1136  labelListList& faceFaces
1137 )
1138 {
1139  faceFaces.setSize(s.size());
1140 
1141  DynamicList<label> nbrs;
1142 
1143  forAll(faceFaces, facei)
1144  {
1145  const labelledTri& f = s[facei];
1146 
1147  nbrs.reserve(f.size());
1148  nbrs.clear();
1149 
1150  forAll(f, fp)
1151  {
1152  const edge e(f[fp], f[f.fcIndex(fp)]);
1153  const labelList& pFaces = pointFaces[f[fp]];
1154  forAll(pFaces, pFacei)
1155  {
1156  const label otherFacei = pFaces[pFacei];
1157  if (otherFacei != facei)
1158  {
1159  if (s[otherFacei].edgeDirection(e) != 0)
1160  {
1161  if (!nbrs.find(otherFacei))
1162  {
1163  nbrs.append(otherFacei);
1164  }
1165  }
1166  }
1167  }
1168  }
1169  faceFaces[facei] = std::move(nbrs);
1170  }
1171 }
1172 
1173 
1174 void Foam::distributedTriSurfaceMesh::surfaceSide
1175 (
1176  const pointField& samples,
1177  const List<pointIndexHit>& nearestInfo,
1178  List<volumeType>& volType
1179 ) const
1180 {
1181  if (debug)
1182  {
1183  Pout<< "distributedTriSurfaceMesh::surfaceSide :"
1184  << " finding surface side given points on surface for "
1185  << samples.size() << " samples" << endl;
1186  }
1187 
1188  // Use global index to send local tri and nearest back to originating
1189  // processor
1190 
1191  labelList triangleIndex(nearestInfo.size());
1192  autoPtr<mapDistribute> mapPtr
1193  (
1194  calcLocalQueries
1195  (
1196  nearestInfo,
1197  triangleIndex
1198  )
1199  );
1200  const mapDistribute& map = mapPtr();
1201 
1202  // Send over samples
1203  pointField localSamples(samples);
1204  map.distribute(localSamples);
1205 
1206 
1207  // Do my tests
1208  // ~~~~~~~~~~~
1209 
1210  volType.setSize(triangleIndex.size());
1211  volType = volumeType::UNKNOWN;
1212 
1213  const triSurface& surf = static_cast<const triSurface&>(*this);
1214  const pointField& points = surf.points();
1215  {
1216  //const labelListList& pointFaces = surf.pointFaces();
1217  // Construct pointFaces. Let's hope surface has compact point
1218  // numbering ...
1219  labelListList pointFaces;
1220  invertManyToMany(points.size(), surf, pointFaces);
1221 
1222  EdgeMap<labelPair> edgeToFaces;
1223 
1224  forAll(triangleIndex, i)
1225  {
1226  label facei = triangleIndex[i];
1227  const triSurface::FaceType& f = surf[facei];
1228  const point& sample = localSamples[i];
1229 
1230  // Find where point is on face
1231  label nearType, nearLabel;
1232  pointHit pHit =
1233  f.nearestPointClassify(sample, points, nearType, nearLabel);
1234 
1235  const point& nearestPoint(pHit.rawPoint());
1236 
1237  if (nearType == triPointRef::NONE)
1238  {
1239  const vector sampleNearestVec = (sample - nearestPoint);
1240 
1241  // Nearest to face interior. Use faceNormal to determine side
1242  //scalar c = sampleNearestVec & surf.faceNormals()[facei];
1243  scalar c = sampleNearestVec & surf[facei].areaNormal(points);
1244 
1245  if (c > 0)
1246  {
1247  volType[i] = volumeType::OUTSIDE;
1248  }
1249  else
1250  {
1251  volType[i] = volumeType::INSIDE;
1252  }
1253  }
1254  else if (nearType == triPointRef::EDGE)
1255  {
1256  // Nearest to edge nearLabel. Note that this can only be a
1257  // knife-edge
1258  // situation since otherwise the nearest point could never be
1259  // the edge.
1260 
1261  label otherFacei = findOtherFace(pointFaces, facei, nearLabel);
1262  if (otherFacei != -1)
1263  {
1264  volType[i] =
1265  edgeSide(sample, nearestPoint, facei, otherFacei);
1266  }
1267  else
1268  {
1269  // Open edge. Leave volume type unknown
1270  }
1271  }
1272  else
1273  {
1274  // Nearest to point. Could use pointNormal here but is not
1275  // correct.
1276  // Instead determine which edge using point is nearest and
1277  // use test above (nearType == triPointRef::EDGE).
1278 
1279  const label pointi = f[nearLabel];
1280  const labelList& pFaces = pointFaces[pointi];
1281  const vector sampleNearestVec = (sample - nearestPoint);
1282 
1283  // Loop over all faces. Check if have both edge faces. Do
1284  // test.
1285  edgeToFaces.clear();
1286 
1287  scalar maxCosAngle = -GREAT;
1288  labelPair maxEdgeFaces(-1, -1);
1289 
1290  forAll(pFaces, pFacei)
1291  {
1292  label facei = pFaces[pFacei];
1293  const triSurface::FaceType& f = surf[facei];
1294 
1295  label fp = f.find(pointi);
1296  label p1 = f[f.fcIndex(fp)];
1297  label pMin1 = f[f.rcIndex(fp)];
1298 
1299  Pair<edge> edges
1300  (
1301  edge(pointi, p1),
1302  edge(pointi, pMin1)
1303  );
1304 
1305  // Check edge fp-to-fp+1 and fp-1
1306  // determine distance/angle to nearPoint
1307  for (const edge& e : edges)
1308  {
1309  auto iter = edgeToFaces.find(e);
1310  if (iter.found())
1311  {
1312  if (iter().second() == -1)
1313  {
1314  // Found second face. Now we have edge+faces
1315  iter().second() = facei;
1316 
1317  vector eVec(e.vec(points));
1318  scalar magEVec = mag(eVec);
1319 
1320  if (magEVec > VSMALL)
1321  {
1322  eVec /= magEVec;
1323 
1324  // Determine edge most in direction of
1325  // sample
1326  scalar cosAngle(sampleNearestVec&eVec);
1327  if (cosAngle > maxCosAngle)
1328  {
1329  maxCosAngle = cosAngle;
1330  maxEdgeFaces = iter();
1331  }
1332  }
1333  }
1334  else
1335  {
1336  FatalErrorInFunction << "Not closed"
1337  << exit(FatalError);
1338  }
1339  }
1340  else
1341  {
1342  edgeToFaces.insert(e, labelPair(facei, -1));
1343  }
1344  }
1345  }
1346 
1347 
1348  // Check that surface is closed
1349  bool closed = true;
1350  for (auto iter : edgeToFaces)
1351  {
1352  if (iter[0] == -1 || iter[1] == -1)
1353  {
1354  closed = false;
1355  break;
1356  }
1357  }
1358  if (closed)
1359  {
1360  volType[i] = edgeSide
1361  (
1362  sample,
1363  nearestPoint,
1364  maxEdgeFaces[0],
1365  maxEdgeFaces[1]
1366  );
1367  }
1368  }
1369  }
1370  }
1371 
1372 
1373  // Send back results
1374  // ~~~~~~~~~~~~~~~~~
1375 
1376  // Note that we make sure that if multiple processors hold data only
1377  // the one with inside/outside wins. (though this should already be
1378  // handled by the fact we have a unique nearest triangle so we only
1379  // send the volume-query to a single processor)
1380 
1381 
1382  //forAll(localSamples, i)
1383  //{
1384  // Pout<< "surfaceSide : for localSample:" << localSamples[i]
1385  // << " found volType:" << volumeType::names[volType[i]]
1386  // << endl;
1387  //}
1388 
1389  const volumeType zero(volumeType::UNKNOWN);
1391  (
1393  List<labelPair>(0),
1394  nearestInfo.size(),
1395  map.constructMap(),
1396  map.constructHasFlip(),
1397  map.subMap(),
1398  map.subHasFlip(),
1399  volType,
1400  volumeCombineOp(),
1401  noOp(), // no flipping
1402  zero
1403  );
1404 
1405  if (debug)
1406  {
1407  Pout<< "distributedTriSurfaceMesh::surfaceSide :"
1408  << " finished finding surface side given points on surface for "
1409  << samples.size() << " samples" << endl;
1410  }
1411 }
1412 
1413 
1414 void Foam::distributedTriSurfaceMesh::collectLeafMids
1415 (
1416  const label nodeI,
1417  DynamicField<point>& midPoints
1418 ) const
1419 {
1420  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1421 
1422  for (direction octant = 0; octant < nod.subNodes_.size(); octant++)
1423  {
1424  const labelBits index = nod.subNodes_[octant];
1425 
1427  {
1428  // tree node. Recurse.
1429  collectLeafMids
1430  (
1432  midPoints
1433  );
1434  }
1436  {}
1437  else
1438  {
1439  // No data in this octant. Set type for octant acc. to the mid
1440  // of its bounding box.
1441  const treeBoundBox subBb = nod.bb_.subBbox(octant);
1442  midPoints.append(subBb.midpoint());
1443  }
1444  }
1445 }
1446 
1447 
1448 Foam::volumeType Foam::distributedTriSurfaceMesh::calcVolumeType
1449 (
1450  const List<volumeType>& midPointTypes,
1451  label& midPointi,
1452  PackedList<2>& nodeTypes,
1453  const label nodeI
1454 ) const
1455 {
1456  // Pre-calculates wherever possible the volume status per node/subnode.
1457  // Recurses to determine status of lowest level boxes. Level above is
1458  // combination of octants below.
1459 
1460  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1461 
1462  volumeType myType = volumeType::UNKNOWN;
1463 
1464  for (direction octant = 0; octant < nod.subNodes_.size(); octant++)
1465  {
1466  volumeType subType;
1467 
1468  const labelBits index = nod.subNodes_[octant];
1469 
1471  {
1472  // tree node. Recurse.
1473  subType = calcVolumeType
1474  (
1475  midPointTypes,
1476  midPointi,
1477  nodeTypes,
1479  );
1480  }
1482  {
1483  // Contents. Depending on position in box might be on either
1484  // side.
1485  subType = volumeType::MIXED;
1486  }
1487  else
1488  {
1489  // No data in this octant. Set type for octant acc. to the mid
1490  // of its bounding box.
1491  //Pout<< " for leaf at bb:" << nod.bb_.subBbox(octant)
1492  // << " node:" << nodeI
1493  // << " octant:" << octant
1494  // << " caching volType to:" << midPointTypes[midPointi] << endl;
1495  subType = midPointTypes[midPointi++];
1496  }
1497 
1498  // Store octant type
1499  nodeTypes.set((nodeI<<3)+octant, subType);
1500 
1501  // Combine sub node types into type for treeNode. Result is 'mixed' if
1502  // types differ among subnodes.
1503  if (myType == volumeType::UNKNOWN)
1504  {
1505  myType = subType;
1506  }
1507  else if (subType != myType)
1508  {
1509  myType = volumeType::MIXED;
1510  }
1511  }
1512  return myType;
1513 }
1514 
1515 
1516 Foam::volumeType Foam::distributedTriSurfaceMesh::cachedVolumeType
1517 (
1518  const label nodeI,
1519  const point& sample
1520 ) const
1521 {
1522  const indexedOctree<treeDataTriSurface>::node& nod = tree().nodes()[nodeI];
1523 
1524  direction octant = nod.bb_.subOctant(sample);
1525 
1526  volumeType octantType = volumeType::type
1527  (
1528  tree().nodeTypes().get((nodeI<<3)+octant)
1529  );
1530 
1531  if (octantType == volumeType::INSIDE)
1532  {
1533  return octantType;
1534  }
1535  else if (octantType == volumeType::OUTSIDE)
1536  {
1537  return octantType;
1538  }
1539  else if (octantType == volumeType::UNKNOWN)
1540  {
1541  // Can happen for e.g. non-manifold surfaces.
1542  return octantType;
1543  }
1544  else if (octantType == volumeType::MIXED)
1545  {
1546  labelBits index = nod.subNodes_[octant];
1547 
1549  {
1550  // Recurse
1551  volumeType subType = cachedVolumeType
1552  (
1554  sample
1555  );
1556 
1557  return subType;
1558  }
1560  {
1561  // Content.
1562  return volumeType::UNKNOWN;
1563  }
1564  else
1565  {
1566  // Empty node. Cannot have 'mixed' as its type since not divided
1567  // up and has no items inside it.
1569  << "Sample:" << sample << " node:" << nodeI
1570  << " with bb:" << nod.bb_ << nl
1571  << "Empty subnode has invalid volume type MIXED."
1572  << abort(FatalError);
1573 
1574  return volumeType::UNKNOWN;
1575  }
1576  }
1577  else
1578  {
1580  << "Sample:" << sample << " at node:" << nodeI
1581  << " octant:" << octant
1582  << " with bb:" << nod.bb_.subBbox(octant) << nl
1583  << "Node has invalid volume type " << octantType
1584  << abort(FatalError);
1585 
1586  return volumeType::UNKNOWN;
1587  }
1588 }
1589 
1590 
1591 // Find bounding boxes that guarantee a more or less uniform distribution
1592 // of triangles. Decomposition in here is only used to get the bounding
1593 // boxes, actual decomposition is done later on.
1594 // Returns a per processor a list of bounding boxes that most accurately
1595 // describe the shape. For now just a single bounding box per processor but
1596 // optimisation might be to determine a better fitting shape.
1598 Foam::distributedTriSurfaceMesh::independentlyDistributedBbs
1599 (
1600  const triSurface& s
1601 )
1602 {
1603  addProfiling
1604  (
1605  distribute,
1606  "distributedTriSurfaceMesh::independentlyDistributedBbs"
1607  );
1608 
1609  if (!decomposer_.valid())
1610  {
1611  // Use singleton decomposeParDict. Cannot use decompositionModel
1612  // here since we've only got Time and not a mesh.
1613 
1614  const auto* dictPtr =
1615  searchableSurface::time().findObject<IOdictionary>
1616  (
1617  // == decompositionModel::canonicalName
1618  "decomposeParDict"
1619  );
1620 
1621  if (dictPtr)
1622  {
1623  decomposer_ = decompositionMethod::New(*dictPtr);
1624  }
1625  else
1626  {
1627  if (!decomposeParDict_.valid())
1628  {
1629  decomposeParDict_.reset
1630  (
1631  new IOdictionary
1632  (
1633  IOobject
1634  (
1635  // == decompositionModel::canonicalName
1636  "decomposeParDict",
1641  )
1642  )
1643  );
1644  }
1645  decomposer_ = decompositionMethod::New(decomposeParDict_());
1646  }
1647  }
1648 
1649 
1650  // Initialise to inverted box
1651  List<List<treeBoundBox>> bbs(Pstream::nProcs());
1652  forAll(bbs, proci)
1653  {
1654  bbs[proci].setSize(1, treeBoundBox(boundBox::invertedBox));
1655  }
1656 
1657 
1658  const globalIndex& triIndexer = globalTris();
1659 
1660  bool masterOnly;
1661  {
1662  masterOnly = true;
1663  for (label proci = 1; proci < Pstream::nProcs(); proci++)
1664  {
1665  if (triIndexer.localSize(proci) != 0)
1666  {
1667  masterOnly = false;
1668  break;
1669  }
1670  }
1671  }
1672 
1673  if (masterOnly)
1674  {
1675  if (debug)
1676  {
1677  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1678  << " determining master-only decomposition for " << s.size()
1679  << " centroids for " << searchableSurface::name() << endl;
1680  }
1681 
1682  // Triangle centres
1683  pointField triCentres(s.size());
1684  forAll(s, trii)
1685  {
1686  triCentres[trii] = s[trii].centre(s.points());
1687  }
1688 
1689  labelList distribution;
1690  if (!isA<geomDecomp>(decomposer_()))
1691  {
1692  // Use connectivity
1693  labelListList pointFaces;
1694  invertManyToMany(s.points().size(), s, pointFaces);
1695  labelListList faceFaces(s.size());
1696  calcFaceFaces(s, pointFaces, faceFaces);
1697 
1698  // Do the actual decomposition
1699  const bool oldParRun = UPstream::parRun();
1700  UPstream::parRun() = false;
1701  distribution = decomposer_().decompose(faceFaces, triCentres);
1702  UPstream::parRun() = oldParRun;
1703  }
1704  else
1705  {
1706  // Do the actual decomposition
1707  distribution = decomposer_().decompose(triCentres);
1708  }
1709 
1710  if (debug)
1711  {
1712  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1713  << " determining processor bounding boxes" << endl;
1714  }
1715 
1716  // Find bounding box for all triangles on new distribution.
1717  forAll(s, trii)
1718  {
1719  const triSurface::FaceType& f = s[trii];
1720 
1721  treeBoundBox& bb = bbs[distribution[trii]][0];
1722  bb.add(s.points(), f);
1723  }
1724 
1725  // Now combine for all processors and convert to correct format.
1726  forAll(bbs, proci)
1727  {
1728  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1729  Pstream::listCombineScatter(bbs[proci]);
1730  }
1731  }
1732  else if (distType_ == DISTRIBUTED)
1733  {
1734  // Fully distributed decomposition. Does not filter duplicate
1735  // triangles.
1736  if (!decomposer_().parallelAware())
1737  {
1739  << "The decomposition method " << decomposer_().typeName
1740  << " does not decompose in parallel."
1741  << " Please choose one that does." << exit(FatalError);
1742  }
1743 
1744  if (debug)
1745  {
1746  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1747  << " determining decomposition for " << s.size()
1748  << " centroids" << endl;
1749  }
1750 
1751  // Triangle centres
1752  pointField triCentres(s.size());
1753  forAll(s, trii)
1754  {
1755  triCentres[trii] = s[trii].centre(s.points());
1756  }
1757 
1758  labelList distribution = decomposer_().decompose(triCentres);
1759 
1760  if (debug)
1761  {
1762  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1763  << " determining processor bounding boxes for "
1764  << searchableSurface::name() << endl;
1765  }
1766 
1767  // Find bounding box for all triangles on new distribution.
1768  forAll(s, trii)
1769  {
1770  const triSurface::FaceType& f = s[trii];
1771 
1772  treeBoundBox& bb = bbs[distribution[trii]][0];
1773  bb.add(s.points(), f);
1774  }
1775 
1776  // Now combine for all processors and convert to correct format.
1777  forAll(bbs, proci)
1778  {
1779  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1780  Pstream::listCombineScatter(bbs[proci]);
1781  }
1782  }
1783 // //// Tbd. initial duplicate filtering of border points only
1784 // if (distType_ == DISTRIBUTED)
1785 // {
1786 // // Fully distributed decomposition. Does not filter duplicate
1787 // // triangles.
1788 // if (!decomposer_().parallelAware())
1789 // {
1790 // FatalErrorInFunction
1791 // << "The decomposition method " << decomposer_().typeName
1792 // << " does not decompose in parallel."
1793 // << " Please choose one that does." << exit(FatalError);
1794 // }
1795 //
1796 // if (debug)
1797 // {
1798 // Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1799 // << " determining decomposition for " << s.size()
1800 // << " centroids" << endl;
1801 // }
1802 // const pointField& points = s.points();
1803 //
1804 // pointField triCentres(s.size());
1805 // forAll(s, trii)
1806 // {
1807 // triCentres[trii] = s[trii].centre(points);
1808 // }
1809 //
1810 // // Collect all triangles not fully inside the current bb
1811 // DynamicList<label> borderTris(s.size()/Pstream::nProcs());
1812 //
1813 // const List<treeBoundBox>& myBbs = procBb_[Pstream::myProcNo];
1814 //
1815 // boolList includedFace;
1816 // overlappingSurface(s, myBbs, includedFace);
1817 // boolList internalOrBorderFace(includedFace);
1818 // forAll(s, trii)
1819 // {
1820 // if (includedFace[trii])
1821 // {
1822 // // Triangle is either inside or part-inside. Exclude fully
1823 // // inside triangles.
1824 // const labelledTri& f = s[trii];
1825 // const point& p0 = points[f[0]];
1826 // const point& p1 = points[f[1]];
1827 // const point& p2 = points[f[2]];
1828 // if
1829 // (
1830 // !contains(myBbs, p0)
1831 // || !contains(myBbs, p1)
1832 // || !contains(myBbs, p2)
1833 // )
1834 // {
1835 // borderTris.append(trii);
1836 // }
1837 // }
1838 // }
1839 //
1840 // const label nBorderTris = borderTris.size();
1841 //
1842 // Pout<< "Have " << borderTris.size() << " border triangles out of "
1843 // << s.size() << endl;
1844 //
1845 // labelListList sendMap(Pstream::nProcs());
1846 // sendMap[0] = std::move(borderTris);
1847 //
1848 // const mapDistribute map(std::move(sendMap));
1849 //
1850 // // Gather all borderTris
1851 // //globalIndex globalBorderTris(borderTris.size());
1852 // //pointField globalBorderCentres(allCentres, borderTris);
1853 // //globalBorderTris.gather
1854 // //(
1855 // // UPstream::worldComm,
1856 // // UPstream::procID(Pstream::worldComm),
1857 // // globalBorderCentres
1858 // //);
1859 // pointField globalBorderCentres(allCentres);
1860 // map.distribute(globalBorderCentres);
1861 //
1862 // // Merge on master
1863 // labelList masterBorder(borderTris.size(), -1);
1864 // if (Pstream::master())
1865 // {
1866 // labelList allToMerged;
1867 // label nMerged = mergePoints
1868 // (
1869 // globalBorderCentres,
1870 // mergeDist_,
1871 // false, //const bool verbose,
1872 // allToMerged
1873 // // maybe bounds().mid() ?
1874 // );
1875 //
1876 // if (debug)
1877 // {
1878 // Pout<< "distributedTriSurfaceMesh::"
1879 // << "independentlyDistributedBbs :"
1880 // << " merged " << globalBorderCentres.size()
1881 // << " border centroids down to " << nMerged << endl;
1882 // }
1883 //
1884 // labelList mergedMaster(nMerged, -1);
1885 // isMaster.setSize(globalBorderCentres.size(), false);
1886 // forAll(allToMerged, i)
1887 // {
1888 // label mergedi = allToMerged[i];
1889 // if (mergedMaster[mergedi] == -1)
1890 // {
1891 // mergedMaster[mergedi] = i;
1892 // isMaster[i] = true;
1893 // }
1894 // }
1895 // forAll(allToMerged, i)
1896 // {
1897 // label mergedi = allToMerged[i];
1898 // masterBorder[i] = mergedMaster[mergedi];
1899 // }
1900 // }
1901 // //globalBorderTris.scatter
1902 // //(
1903 // // UPstream::worldComm,
1904 // // UPstream::procID(Pstream::worldComm),
1905 // // isMasterPoint
1906 // //);
1907 // //boolList isMasterBorder(s.size(), false);
1908 // //forAll(borderTris, i)
1909 // //{
1910 // // isMasterBorder[borderTris[i]] = isMasterPoint[i];
1911 // //}
1912 // map.reverseDistribute(s.size(), isMaster);
1913 //
1914 // // Collect all non-border or master-border points
1915 // DynamicList<label> triMap(s.size());
1916 // forAll(s, trii)
1917 // {
1918 // if (includedFace[trii])
1919 // {
1920 // // Triangle is either inside or part-inside. Exclude fully
1921 // // inside triangles.
1922 // const labelledTri& f = s[trii];
1923 // const point& p0 = points[f[0]];
1924 // const point& p1 = points[f[1]];
1925 // const point& p2 = points[f[2]];
1926 //
1927 // if
1928 // (
1929 // contains(myBbs, p0)
1930 // && contains(myBbs, p1)
1931 // && contains(myBbs, p2)
1932 // )
1933 // {
1934 // // Internal
1935 // triMap.append(trii);
1936 // }
1937 // else if (isMasterBorder[trii])
1938 // {
1939 // // Part overlapping and master of overlap
1940 // triMap.append(trii);
1941 // }
1942 // }
1943 // }
1944 //
1945 // pointField masterCentres(allCentres, triMap);
1946 //
1947 // // Do the actual decomposition
1948 // labelList masterDistribution(decomposer_().decompose(masterCentres));
1949 //
1950 // // Make map to get the decomposition from the master of each border
1951 // labelList borderGlobalMaster(borderTris.size());
1952 // forAll(borderTris, borderi)
1953 // {
1954 // borderGlobalMaster[borderi] = ..masterTri
1955 // }
1956 // mapDistribute map(globalBorderTris, borderGlobalMaster
1957 //
1958 // // Send decomposition
1959 //
1960 //
1961 // if (debug)
1962 // {
1963 // Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1964 // << " determining processor bounding boxes" << endl;
1965 // }
1966 //
1967 // // Find bounding box for all triangles on new distribution.
1968 // forAll(s, trii)
1969 // {
1970 // const triSurface::FaceType& f = s[trii];
1971 //
1972 // treeBoundBox& bb = bbs[distribution[trii]][0];
1973 // bb.add(s.points(), f);
1974 // }
1975 //
1976 // // Now combine for all processors and convert to correct format.
1977 // forAll(bbs, proci)
1978 // {
1979 // Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
1980 // Pstream::listCombineScatter(bbs[proci]);
1981 // }
1982 // }
1983  else
1984  {
1985  // Master-only decomposition. Filters duplicate triangles so repeatable.
1986 
1987  if (debug)
1988  {
1989  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
1990  << " collecting all centroids" << endl;
1991  }
1992 
1993  // Collect all triangle centres
1994  pointField allCentres(s.size());
1995  {
1996  forAll(s, trii)
1997  {
1998  allCentres[trii] = s[trii].centre(s.points());
1999  }
2000  globalTris().gather
2001  (
2004  allCentres
2005  );
2006  }
2007 
2008  // Determine local decomposition
2009  labelList distribution(s.size());
2010  {
2011  labelList allDistribution;
2012  if (Pstream::master())
2013  {
2014  labelList allToMerged;
2015  label nMerged = mergePoints
2016  (
2017  allCentres,
2018  mergeDist_,
2019  false, //const bool verbose,
2020  allToMerged
2021  // maybe bounds().mid() ?
2022  );
2023 
2024  if (debug)
2025  {
2026  Pout<< "distributedTriSurfaceMesh::"
2027  << "independentlyDistributedBbs :"
2028  << " merged " << allCentres.size()
2029  << " centroids down to " << nMerged << endl;
2030  }
2031 
2032  // Collect merged points
2033  pointField mergedPoints(nMerged);
2034  UIndirectList<point>(mergedPoints, allToMerged) = allCentres;
2035 
2036  // Decompose merged centres
2037  const bool oldParRun = UPstream::parRun();
2038  UPstream::parRun() = false;
2039  labelList mergedDist(decomposer_().decompose(mergedPoints));
2040  UPstream::parRun() = oldParRun;
2041 
2042  // Convert to all
2043  allDistribution = UIndirectList<label>
2044  (
2045  mergedDist,
2046  allToMerged
2047  );
2048  }
2049 
2050  // Scatter back to processors
2051  globalTris().scatter
2052  (
2055  allDistribution,
2056  distribution
2057  );
2058  if (debug)
2059  {
2060  Pout<< "distributedTriSurfaceMesh::"
2061  << "independentlyDistributedBbs :"
2062  << " determined decomposition" << endl;
2063  }
2064  }
2065 
2066  // Find bounding box for all triangles on new distribution.
2067  if (debug)
2068  {
2069  Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :"
2070  << " determining processor bounding boxes for "
2071  << searchableSurface::name() << endl;
2072  }
2073 
2074  forAll(s, trii)
2075  {
2076  const triSurface::FaceType& f = s[trii];
2077  treeBoundBox& bb = bbs[distribution[trii]][0];
2078  bb.add(s.points(), f);
2079  }
2080 
2081  // Now combine for all processors and convert to correct format.
2082  forAll(bbs, proci)
2083  {
2084  Pstream::listCombineGather(bbs[proci], plusEqOp<boundBox>());
2085  Pstream::listCombineScatter(bbs[proci]);
2086  }
2087  }
2088  return bbs;
2089 }
2090 
2091 
2092 // Does any part of triangle overlap bb.
2093 bool Foam::distributedTriSurfaceMesh::overlaps
2094 (
2095  const List<treeBoundBox>& bbs,
2096  const point& p0,
2097  const point& p1,
2098  const point& p2
2099 )
2100 {
2101  treeBoundBox triBb(p0);
2102  triBb.add(p1);
2103  triBb.add(p2);
2104 
2105  forAll(bbs, bbi)
2106  {
2107  const treeBoundBox& bb = bbs[bbi];
2108 
2109  // Exact test of triangle intersecting bb
2110 
2111  // Quick rejection. If whole bounding box of tri is outside cubeBb then
2112  // there will be no intersection.
2113  if (bb.overlaps(triBb))
2114  {
2115  // Check if one or more triangle point inside
2116  if (bb.contains(p0) || bb.contains(p1) || bb.contains(p2))
2117  {
2118  // One or more points inside
2119  return true;
2120  }
2121 
2122  // Now we have the difficult case: all points are outside but
2123  // connecting edges might go through cube. Use fast intersection
2124  // of bounding box.
2125  bool intersect = triangleFuncs::intersectBb(p0, p1, p2, bb);
2126 
2127  if (intersect)
2128  {
2129  return true;
2130  }
2131  }
2132  }
2133  return false;
2134 }
2135 
2136 
2137 void Foam::distributedTriSurfaceMesh::subsetMeshMap
2138 (
2139  const triSurface& s,
2140  const boolList& include,
2141  const label nIncluded,
2142  labelList& newToOldPoints,
2143  labelList& oldToNewPoints,
2144  labelList& newToOldFaces
2145 )
2146 {
2147  newToOldFaces.setSize(nIncluded);
2148  newToOldPoints.setSize(s.points().size());
2149  oldToNewPoints.setSize(s.points().size());
2150  oldToNewPoints = -1;
2151  {
2152  label facei = 0;
2153  label pointi = 0;
2154 
2155  forAll(include, oldFacei)
2156  {
2157  if (include[oldFacei])
2158  {
2159  // Store new faces compact
2160  newToOldFaces[facei++] = oldFacei;
2161 
2162  // Renumber labels for face
2163  const triSurface::FaceType& f = s[oldFacei];
2164 
2165  forAll(f, fp)
2166  {
2167  label oldPointi = f[fp];
2168 
2169  if (oldToNewPoints[oldPointi] == -1)
2170  {
2171  oldToNewPoints[oldPointi] = pointi;
2172  newToOldPoints[pointi++] = oldPointi;
2173  }
2174  }
2175  }
2176  }
2177  newToOldPoints.setSize(pointi);
2178  }
2179 }
2180 
2181 
2182 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2183 (
2184  const triSurface& s,
2185  const labelList& newToOldPoints,
2186  const labelList& oldToNewPoints,
2187  const labelList& newToOldFaces
2188 )
2189 {
2190  // Extract points
2191  pointField newPoints(newToOldPoints.size());
2192  forAll(newToOldPoints, i)
2193  {
2194  newPoints[i] = s.points()[newToOldPoints[i]];
2195  }
2196  // Extract faces
2197  List<labelledTri> newTriangles(newToOldFaces.size());
2198 
2199  forAll(newToOldFaces, i)
2200  {
2201  // Get old vertex labels
2202  const labelledTri& tri = s[newToOldFaces[i]];
2203 
2204  newTriangles[i][0] = oldToNewPoints[tri[0]];
2205  newTriangles[i][1] = oldToNewPoints[tri[1]];
2206  newTriangles[i][2] = oldToNewPoints[tri[2]];
2207  newTriangles[i].region() = tri.region();
2208  }
2209 
2210  // Reuse storage.
2211  return triSurface(newTriangles, s.patches(), newPoints, true);
2212 }
2213 
2214 
2215 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2216 (
2217  const triSurface& s,
2218  const boolList& include,
2219  labelList& newToOldPoints,
2220  labelList& newToOldFaces
2221 )
2222 {
2223  label n = 0;
2224 
2225  forAll(include, i)
2226  {
2227  if (include[i])
2228  {
2229  n++;
2230  }
2231  }
2232 
2233  labelList oldToNewPoints;
2234  subsetMeshMap
2235  (
2236  s,
2237  include,
2238  n,
2239  newToOldPoints,
2240  oldToNewPoints,
2241  newToOldFaces
2242  );
2243 
2244  return subsetMesh
2245  (
2246  s,
2247  newToOldPoints,
2248  oldToNewPoints,
2249  newToOldFaces
2250  );
2251 }
2252 
2253 
2254 Foam::triSurface Foam::distributedTriSurfaceMesh::subsetMesh
2255 (
2256  const triSurface& s,
2257  const labelList& newToOldFaces,
2258  labelList& newToOldPoints
2259 )
2260 {
2261  const boolList include
2262  (
2263  ListOps::createWithValue<bool>(s.size(), newToOldFaces, true, false)
2264  );
2265 
2266  newToOldPoints.setSize(s.points().size());
2267  labelList oldToNewPoints(s.points().size(), -1);
2268  {
2269  label pointi = 0;
2270 
2271  forAll(include, oldFacei)
2272  {
2273  if (include[oldFacei])
2274  {
2275  // Renumber labels for face
2276  const triSurface::FaceType& f = s[oldFacei];
2277 
2278  forAll(f, fp)
2279  {
2280  label oldPointi = f[fp];
2281 
2282  if (oldToNewPoints[oldPointi] == -1)
2283  {
2284  oldToNewPoints[oldPointi] = pointi;
2285  newToOldPoints[pointi++] = oldPointi;
2286  }
2287  }
2288  }
2289  }
2290  newToOldPoints.setSize(pointi);
2291  }
2292 
2293  return subsetMesh
2294  (
2295  s,
2296  newToOldPoints,
2297  oldToNewPoints,
2298  newToOldFaces
2299  );
2300 }
2301 
2302 
2303 Foam::label Foam::distributedTriSurfaceMesh::findTriangle
2304 (
2305  const List<labelledTri>& allFaces,
2306  const labelListList& allPointFaces,
2307  const labelledTri& otherF
2308 )
2309 {
2310  // allFaces connected to otherF[0]
2311  const labelList& pFaces = allPointFaces[otherF[0]];
2312 
2313  forAll(pFaces, i)
2314  {
2315  const labelledTri& f = allFaces[pFaces[i]];
2316 
2317  if (f.region() == otherF.region())
2318  {
2319  // Find index of otherF[0]
2320  label fp0 = f.find(otherF[0]);
2321  // Check rest of triangle in same order
2322  label fp1 = f.fcIndex(fp0);
2323  label fp2 = f.fcIndex(fp1);
2324 
2325  if (f[fp1] == otherF[1] && f[fp2] == otherF[2])
2326  {
2327  return pFaces[i];
2328  }
2329  }
2330  }
2331  return -1;
2332 }
2333 
2334 
2335 // Merge into allSurf.
2336 void Foam::distributedTriSurfaceMesh::merge
2337 (
2338  const scalar mergeDist,
2339  const List<labelledTri>& subTris,
2340  const pointField& subPoints,
2341 
2342  List<labelledTri>& allTris,
2344 
2345  labelList& faceConstructMap,
2346  labelList& pointConstructMap
2347 )
2348 {
2349  labelList subToAll;
2350  matchPoints
2351  (
2352  subPoints,
2353  allPoints,
2354  scalarField(subPoints.size(), mergeDist), // match distance
2355  false, // verbose
2356  pointConstructMap
2357  );
2358 
2359  label nOldAllPoints = allPoints.size();
2360 
2361 
2362  // Add all unmatched points
2363  // ~~~~~~~~~~~~~~~~~~~~~~~~
2364 
2365  label allPointi = nOldAllPoints;
2366  forAll(pointConstructMap, pointi)
2367  {
2368  if (pointConstructMap[pointi] == -1)
2369  {
2370  pointConstructMap[pointi] = allPointi++;
2371  }
2372  }
2373 
2374  if (allPointi > nOldAllPoints)
2375  {
2376  allPoints.setSize(allPointi);
2377 
2378  forAll(pointConstructMap, pointi)
2379  {
2380  if (pointConstructMap[pointi] >= nOldAllPoints)
2381  {
2382  allPoints[pointConstructMap[pointi]] = subPoints[pointi];
2383  }
2384  }
2385  }
2386 
2387 
2388  // To check whether triangles are same we use pointFaces.
2389  labelListList allPointFaces;
2390  invertManyToMany(nOldAllPoints, allTris, allPointFaces);
2391 
2392 
2393  // Add all unmatched triangles
2394  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~
2395 
2396  label allTrii = allTris.size();
2397  allTris.setSize(allTrii + subTris.size());
2398 
2399  faceConstructMap.setSize(subTris.size());
2400 
2401  forAll(subTris, trii)
2402  {
2403  const labelledTri& subTri = subTris[trii];
2404 
2405  // Get triangle in new numbering
2406  labelledTri mappedTri
2407  (
2408  pointConstructMap[subTri[0]],
2409  pointConstructMap[subTri[1]],
2410  pointConstructMap[subTri[2]],
2411  subTri.region()
2412  );
2413 
2414 
2415  // Check if all points were already in surface
2416  bool fullMatch = true;
2417 
2418  forAll(mappedTri, fp)
2419  {
2420  if (mappedTri[fp] >= nOldAllPoints)
2421  {
2422  fullMatch = false;
2423  break;
2424  }
2425  }
2426 
2427  if (fullMatch)
2428  {
2429  // All three points are mapped to old points. See if same
2430  // triangle.
2431  label i = findTriangle
2432  (
2433  allTris,
2434  allPointFaces,
2435  mappedTri
2436  );
2437 
2438  if (i == -1)
2439  {
2440  // Add
2441  faceConstructMap[trii] = allTrii;
2442  allTris[allTrii] = mappedTri;
2443  allTrii++;
2444  }
2445  else
2446  {
2447  faceConstructMap[trii] = i;
2448  }
2449  }
2450  else
2451  {
2452  // Add
2453  faceConstructMap[trii] = allTrii;
2454  allTris[allTrii] = mappedTri;
2455  allTrii++;
2456  }
2457  }
2458  allTris.setSize(allTrii);
2459 }
2460 
2461 
2462 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
2463 
2464 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh
2466  const IOobject& io,
2467  const triSurface& s,
2468  const dictionary& dict
2469 )
2470 :
2471  triSurfaceMesh(io, s),
2472  dict_
2473  (
2474  IOobject
2475  (
2476  searchableSurface::name() + "Dict",
2483  ),
2484  dict
2485  ),
2486  currentDistType_(FROZEN) // only used to trigger re-distribution
2487 {
2488  // Read from the provided dictionary
2489  read();
2490 
2491  bounds().reduce();
2492 
2493  if (debug)
2494  {
2495  InfoInFunction << "Constructed from triSurface:" << endl;
2496  writeStats(Info);
2497 
2498  labelList nTris(Pstream::nProcs());
2499  nTris[Pstream::myProcNo()] = triSurface::size();
2500  Pstream::gatherList(nTris);
2501  Pstream::scatterList(nTris);
2502 
2503  Info<< endl<< "\tproc\ttris\tbb" << endl;
2504  forAll(nTris, proci)
2505  {
2506  Info<< '\t' << proci << '\t' << nTris[proci]
2507  << '\t' << procBb_[proci] << endl;
2508  }
2509  Info<< endl;
2510  }
2511 }
2512 
2513 
2514 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh(const IOobject& io)
2515 :
2517  (
2518  IOobject
2519  (
2520  io.name(),
2521  findLocalInstance(io), // findInstance with parent searching
2522  io.local(),
2523  io.db(),
2524  io.readOpt(),
2525  io.writeOpt(),
2526  io.registerObject()
2527  ),
2528  triSurfaceMesh::masterOnly // allow parent searching
2529  ),
2530  dict_
2531  (
2532  IOobject
2533  (
2534  searchableSurface::name() + "Dict",
2535  searchableSurface::instance(),
2536  searchableSurface::local(),
2537  searchableSurface::db(),
2538  (
2539  (
2540  searchableSurface::readOpt()
2541  == IOobject::MUST_READ
2542  || searchableSurface::readOpt()
2543  == IOobject::MUST_READ_IF_MODIFIED
2544  )
2545  ? IOobject::READ_IF_PRESENT
2546  : searchableSurface::readOpt()
2547  ),
2548  searchableSurface::writeOpt(),
2549  searchableSurface::registerObject()
2550  ),
2551  dictionary()
2552  ),
2553  currentDistType_(FROZEN) // only used to trigger re-distribution
2554 {
2555  // Read from the local, decomposed dictionary
2556  read();
2557 
2558  bounds().reduce();
2559 
2560  const fileName actualFile(checkFile(io, true));
2561 
2562  if
2563  (
2564  actualFile != io.localFilePath(triSurfaceMesh::typeName)
2565  && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED)
2566  )
2567  {
2568  if (debug)
2569  {
2570  InfoInFunction << "Read distributedTriSurface " << io.name()
2571  << " from parent path " << actualFile << endl;
2572  }
2573 
2574  if (Pstream::parRun())
2575  {
2576  // Distribute (checks that distType != currentDistType_ so should
2577  // always trigger re-distribution)
2578  List<treeBoundBox> bbs;
2580  autoPtr<mapDistribute> pointMap;
2581  distribute
2582  (
2583  bbs,
2584  true, // keep unmapped triangles
2585  faceMap,
2586  pointMap
2587  );
2588  }
2589  }
2590  else
2591  {
2592  if (debug)
2593  {
2594  InfoInFunction << "Read distributedTriSurface " << io.name()
2595  << " from actual path " << actualFile << ':' << endl;
2596 
2597  labelList nTris(Pstream::nProcs());
2598  nTris[Pstream::myProcNo()] = triSurface::size();
2599  Pstream::gatherList(nTris);
2600  Pstream::scatterList(nTris);
2601 
2602  Info<< endl<< "\tproc\ttris\tbb" << endl;
2603  forAll(nTris, proci)
2604  {
2605  Info<< '\t' << proci << '\t' << nTris[proci]
2606  << '\t' << procBb_[proci] << endl;
2607  }
2608  Info<< endl;
2609  }
2610  }
2611  if (debug)
2612  {
2613  InfoInFunction << "Read distributedTriSurface " << io.name() << ':'
2614  << endl;
2615  writeStats(Info);
2616  }
2617 }
2618 
2619 
2620 Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh
2622  const IOobject& io,
2623  const dictionary& dict
2624 )
2625 :
2627  (
2628  IOobject
2629  (
2630  io.name(),
2631  findLocalInstance(io),
2632  io.local(),
2633  io.db(),
2634  io.readOpt(),
2635  io.writeOpt(),
2636  io.registerObject()
2637  ),
2638  dict,
2640  ),
2641  dict_
2642  (
2643  IOobject
2644  (
2645  searchableSurface::name() + "Dict",
2649  (
2650  (
2655  )
2658  ),
2661  ),
2662  dictionary()
2663  ),
2664  currentDistType_(FROZEN) // only used to trigger re-distribution
2665 {
2666  // Read from the local, decomposed dictionary
2667  read();
2668 
2669  // Optionally override settings from provided dictionary
2670  {
2671  // Wanted distribution type
2672  distributionTypeNames_.readIfPresent
2673  (
2674  "distributionType",
2675  dict_,
2676  distType_
2677  );
2678 
2679  // Merge distance
2680  dict_.readIfPresent("mergeDistance", mergeDist_);
2681 
2682  // Distribution type
2683  bool closed;
2684  if (dict_.readIfPresent<bool>("closed", closed))
2685  {
2686  surfaceClosed_ = closed;
2687  }
2688 
2689  outsideVolType_ = volumeType::names.getOrDefault
2690  (
2691  "outsideVolumeType",
2692  dict_,
2693  outsideVolType_
2694  );
2695  }
2696 
2697 
2698  bounds().reduce();
2699 
2700  const fileName actualFile(checkFile(io, dict, true));
2701 
2702  if
2703  (
2704  actualFile != io.localFilePath(triSurfaceMesh::typeName)
2705  && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED)
2706  )
2707  {
2708  if (debug)
2709  {
2710  InfoInFunction << "Read distributedTriSurface " << io.name()
2711  << " from parent path " << actualFile
2712  << " and dictionary" << endl;
2713  }
2714 
2715  if (Pstream::parRun())
2716  {
2717  // Distribute (checks that distType != currentDistType_ so should
2718  // always trigger re-distribution)
2719  List<treeBoundBox> bbs;
2721  autoPtr<mapDistribute> pointMap;
2722  distribute
2723  (
2724  bbs,
2725  true, // keep unmapped triangles
2726  faceMap,
2727  pointMap
2728  );
2729  }
2730  }
2731  else
2732  {
2733  if (debug)
2734  {
2735  InfoInFunction << "Read distributedTriSurface " << io.name()
2736  << " from actual path " << actualFile
2737  << " and dictionary:" << endl;
2738 
2739  labelList nTris(Pstream::nProcs());
2740  nTris[Pstream::myProcNo()] = triSurface::size();
2741  Pstream::gatherList(nTris);
2742  Pstream::scatterList(nTris);
2743 
2744  Info<< endl<< "\tproc\ttris\tbb" << endl;
2745  forAll(nTris, proci)
2746  {
2747  Info<< '\t' << proci << '\t' << nTris[proci]
2748  << '\t' << procBb_[proci] << endl;
2749  }
2750  Info<< endl;
2751  }
2752  }
2753  if (debug)
2754  {
2755  InfoInFunction << "Read distributedTriSurface " << io.name() << ':'
2756  << endl;
2757  writeStats(Info);
2758  }
2759 }
2760 
2761 
2762 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
2763 
2765 {
2766  clearOut();
2767 }
2768 
2769 
2771 {
2772  globalTris_.clear();
2774 }
2775 
2776 
2777 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
2778 
2780 {
2781  if (!globalTris_.valid())
2782  {
2783  globalTris_.reset(new globalIndex(triSurface::size()));
2784  }
2785  return *globalTris_;
2786 }
2787 
2788 
2789 //void Foam::distributedTriSurfaceMesh::findNearest
2790 //(
2791 // const pointField& samples,
2792 // const scalarField& nearestDistSqr,
2793 // List<pointIndexHit>& info
2794 //) const
2795 //{
2796 // if (!Pstream::parRun())
2797 // {
2798 // triSurfaceMesh::findNearest(samples, nearestDistSqr, info);
2799 // return;
2800 // }
2801 //
2802 // addProfiling
2803 // (
2804 // findNearest,
2805 // "distributedTriSurfaceMesh::findNearest"
2806 // );
2807 //
2808 // if (debug)
2809 // {
2810 // Pout<< "distributedTriSurfaceMesh::findNearest :"
2811 // << " trying to find nearest for " << samples.size()
2812 // << " samples with max sphere "
2813 // << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
2814 // << endl;
2815 // }
2816 //
2817 //
2818 // const indexedOctree<treeDataTriSurface>& octree = tree();
2819 //
2820 // // Important:force synchronised construction of indexing
2821 // const globalIndex& triIndexer = globalTris();
2822 //
2823 //
2824 // // Initialise
2825 // // ~~~~~~~~~~
2826 //
2827 // info.setSize(samples.size());
2828 // forAll(info, i)
2829 // {
2830 // info[i].setMiss();
2831 // }
2832 //
2833 //
2834 //
2835 // // Do any local queries
2836 // // ~~~~~~~~~~~~~~~~~~~~
2837 //
2838 // label nLocal = 0;
2839 //
2840 // {
2841 // // Work array - whether processor bb overlaps the bounding sphere.
2842 // boolList procBbOverlaps(Pstream::nProcs());
2843 //
2844 // forAll(samples, i)
2845 // {
2846 // // Find the processor this sample+radius overlaps.
2847 // label nProcs = calcOverlappingProcs
2848 // (
2849 // samples[i],
2850 // nearestDistSqr[i],
2851 // procBbOverlaps
2852 // );
2853 //
2854 // // Overlaps local processor?
2855 // if (procBbOverlaps[Pstream::myProcNo()])
2856 // {
2857 // info[i] = octree.findNearest(samples[i], nearestDistSqr[i]);
2858 // if (info[i].hit())
2859 // {
2860 // if
2861 // (
2862 // surfaceClosed_
2863 // && !contains(procBb_[proci], info[i].hitPoint())
2864 // )
2865 // {
2866 // // Nearest point is not on local processor so the
2867 // // the triangle is only there because some other bit
2868 // // of it
2869 // // is on it. Assume there is another processor that
2870 // // holds
2871 // // the full surrounding of the triangle so we can
2872 // // clear this particular nearest.
2873 // info[i].setMiss();
2874 // info[i].setIndex(-1);
2875 // }
2876 // else
2877 // {
2878 // info[i].setIndex
2879 // (triIndexer.toGlobal(info[i].index()));
2880 // }
2881 // }
2882 // if (nProcs == 1)
2883 // {
2884 // // Fully local
2885 // nLocal++;
2886 // }
2887 // }
2888 // }
2889 // }
2890 //
2891 //
2892 // if
2893 // (
2894 // Pstream::parRun()
2895 // && (
2896 // returnReduce(nLocal, sumOp<label>())
2897 // < returnReduce(samples.size(), sumOp<label>())
2898 // )
2899 // )
2900 // {
2901 // // Not all can be resolved locally. Build queries and map, send over
2902 // // queries, do intersections, send back and merge.
2903 //
2904 // // Calculate queries and exchange map
2905 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2906 //
2907 // pointField allCentres;
2908 // scalarField allRadiusSqr;
2909 // labelList allSegmentMap;
2910 // autoPtr<mapDistribute> mapPtr
2911 // (
2912 // calcLocalQueries
2913 // (
2914 // false, // exclude local processor since already done above
2915 // samples,
2916 // nearestDistSqr,
2917 //
2918 // allCentres,
2919 // allRadiusSqr,
2920 // allSegmentMap
2921 // )
2922 // );
2923 // const mapDistribute& map = mapPtr();
2924 //
2925 //
2926 // // swap samples to local processor
2927 // // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2928 //
2929 // map.distribute(allCentres);
2930 // map.distribute(allRadiusSqr);
2931 //
2932 //
2933 // // Do my tests
2934 // // ~~~~~~~~~~~
2935 //
2936 // List<pointIndexHit> allInfo(allCentres.size());
2937 // forAll(allInfo, i)
2938 // {
2939 // allInfo[i] = octree.findNearest
2940 // (
2941 // allCentres[i],
2942 // allRadiusSqr[i]
2943 // );
2944 // if (allInfo[i].hit())
2945 // {
2946 // // We don't know if the nearest is on an edge/point. If
2947 // // this is the case we preferentially want to return the
2948 // // index on the processor that holds all surrounding triangles
2949 // // so we can do e.g. follow-on inside/outside tests
2950 // if
2951 // (
2952 // surfaceClosed_
2953 // && !contains
2954 // (
2955 // procBb_[Pstream::myProcNo()],
2956 // allInfo[i].hitPoint()
2957 // )
2958 // )
2959 // {
2960 // // Nearest point is not on local processor so the
2961 // // the triangle is only there because some other bit of it
2962 // // is on it. Assume there is another processor that holds
2963 // // the full surrounding of the triangle so we can clear
2964 // // this particular nearest.
2965 // allInfo[i].setMiss();
2966 // allInfo[i].setIndex(-1);
2967 // }
2968 // else
2969 // {
2970 // allInfo[i].setIndex
2971 // (
2972 // triIndexer.toGlobal(allInfo[i].index())
2973 // );
2974 // }
2975 // }
2976 // }
2977 //
2978 //
2979 // // Send back results
2980 // // ~~~~~~~~~~~~~~~~~
2981 //
2982 // map.reverseDistribute(allSegmentMap.size(), allInfo);
2983 //
2984 //
2985 // // Extract information
2986 // // ~~~~~~~~~~~~~~~~~~~
2987 //
2988 // forAll(allInfo, i)
2989 // {
2990 // if (allInfo[i].hit())
2991 // {
2992 // label pointi = allSegmentMap[i];
2993 //
2994 // if (!info[pointi].hit())
2995 // {
2996 // // No intersection yet so take this one
2997 // info[pointi] = allInfo[i];
2998 // }
2999 // else
3000 // {
3001 // // Nearest intersection
3002 // if
3003 // (
3004 // magSqr(allInfo[i].hitPoint()-samples[pointi])
3005 // < magSqr(info[pointi].hitPoint()-samples[pointi])
3006 // )
3007 // {
3008 // info[pointi] = allInfo[i];
3009 // }
3010 // }
3011 // }
3012 // }
3013 // }
3014 //}
3015 
3016 
3019  const pointField& samples,
3020  const scalarField& nearestDistSqr,
3021  List<pointIndexHit>& info
3022 ) const
3023 {
3024  if (!Pstream::parRun())
3025  {
3026  triSurfaceMesh::findNearest(samples, nearestDistSqr, info);
3027  return;
3028  }
3029 
3030  addProfiling
3031  (
3032  findNearest,
3033  "distributedTriSurfaceMesh::findNearest"
3034  );
3035 
3036  if (debug)
3037  {
3038  Pout<< "distributedTriSurfaceMesh::findNearest :"
3039  << " trying to find nearest for " << samples.size()
3040  << " samples with max sphere "
3041  << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
3042  << endl;
3043  }
3044 
3045  const globalIndex& triIndexer = globalTris();
3046 
3047  // Two-pass searching:
3048  // 1. send the sample to the processor whose bb contains it. This is
3049  // most likely also the one that holds the nearest triangle. (In case
3050  // there is no containing processor send to nearest processors. Note
3051  // that this might cause a lot of traffic if this is likely)
3052  // Send the resulting nearest point back.
3053  // 2. with the find from 1 look at which other processors might have a
3054  // better triangle. Since hopefully step 1) will have produced a tight
3055  // bounding box this should limit the amount of points to be retested
3056 
3057 
3058  // 1. Test samples on processor(s) that contains them
3059  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3060 
3061  autoPtr<mapDistribute> map1Ptr;
3062  scalarField distSqr(nearestDistSqr);
3063  boolList procContains(Pstream::nProcs(), false);
3064  boolList procOverlaps(Pstream::nProcs(), false);
3065 
3066  label nOutside = 0;
3067  {
3069  // Pre-size. Assume samples are uniformly distributed
3070  forAll(dynSendMap, proci)
3071  {
3072  dynSendMap[proci].reserve(samples.size()/Pstream::nProcs());
3073  }
3074 
3075  forAll(samples, samplei)
3076  {
3077  label minProci = -1;
3078  Tuple2<label, scalar> best = findBestProcs
3079  (
3080  samples[samplei],
3081  distSqr[samplei],
3082  procContains,
3083  procOverlaps,
3084  minProci
3085  );
3086 
3087  label nContains = 0;
3088  forAll(procBb_, proci)
3089  {
3090  if (procContains[proci])
3091  {
3092  nContains++;
3093  dynSendMap[proci].append(samplei);
3094  distSqr[samplei] = best.second();
3095  }
3096  }
3097  if (nContains == 0)
3098  {
3099  nOutside++;
3100  // Sample is outside all bb. Choices:
3101  // - send to all processors
3102  // - send to single processor
3103 
3104  //forAll(procOverlaps[proci])
3105  //{
3106  // if (procOverlaps[proci])
3107  // {
3108  // dynSendMap[proci].append(samplei);
3109  // distSqr[samplei] = best.second();
3110  // }
3111  //}
3112  if (minProci != -1)
3113  {
3114  dynSendMap[minProci].append(samplei);
3115  distSqr[samplei] = best.second();
3116  }
3117  }
3118  }
3119 
3120  labelListList sendMap(Pstream::nProcs());
3121  forAll(sendMap, proci)
3122  {
3123  sendMap[proci].transfer(dynSendMap[proci]);
3124  }
3125  map1Ptr.set(new mapDistribute(std::move(sendMap)));
3126  }
3127  const mapDistribute& map1 = map1Ptr();
3128 
3129 
3130  if (debug)
3131  {
3132  Pout<< "Pass1:"
3133  << " of " << samples.size() << " samples sending to" << endl;
3134  label nSend = 0;
3135  forAll(map1.subMap(), proci)
3136  {
3137  Pout<< " " << proci << "\t" << map1.subMap()[proci].size()
3138  << endl;
3139  nSend += map1.subMap()[proci].size();
3140  }
3141  Pout<< " sum\t" << nSend << endl
3142  << " outside\t" << nOutside << endl;
3143  }
3144 
3145 
3146  List<nearestAndDist> nearestInfo;
3147  {
3148  // Get the points I need to test and test locally
3149  pointField localPoints(samples);
3150  map1.distribute(localPoints);
3151  scalarField localDistSqr(distSqr);
3152  map1.distribute(localDistSqr);
3153  List<pointIndexHit> localInfo;
3154  triSurfaceMesh::findNearest(localPoints, localDistSqr, localInfo);
3155  convertTriIndices(localInfo);
3156 
3157  // Pack into structure for combining information from multiple
3158  // processors
3159  nearestInfo.setSize(localInfo.size());
3160  nearestInfo = nearestAndDist(pointIndexHit(), Foam::sqr(GREAT));
3161 
3162  label nHit = 0;
3163  label nIgnoredHit = 0;
3164 
3165  forAll(nearestInfo, i)
3166  {
3167  const pointIndexHit& info = localInfo[i];
3168  if (info.hit())
3169  {
3170  nHit++;
3171 
3172  if
3173  (
3174  surfaceClosed_
3175  && !contains(procBb_[Pstream::myProcNo()], info.hitPoint())
3176  )
3177  {
3178  // Nearest point is not on local processor so the
3179  // the triangle is only there because some other bit
3180  // of it is on it. Assume there is another processor that
3181  // holds the full surrounding of the triangle so we can
3182  // ignore this particular nearest.
3183  nIgnoredHit++;
3184  }
3185  else
3186  {
3187  nearestAndDist& ni = nearestInfo[i];
3188  ni.first() = info;
3189  ni.second() = magSqr(localPoints[i]-info.hitPoint());
3190  }
3191  }
3192  }
3193 
3194  if (debug)
3195  {
3196  Pout<< "distributedTriSurfaceMesh::findNearest :"
3197  << " searched locally for " << localPoints.size()
3198  << " samples with max sphere "
3199  << (localDistSqr.size() ? Foam::sqrt(max(localDistSqr)) : Zero)
3200  << " found hits:" << nHit
3201  << " of which outside local bb:" << nIgnoredHit
3202  << endl;
3203  }
3204  }
3205 
3206  // Send back to originating processor. Choose best if sent to multiple
3207  // processors. Note that afterwards all unused entries have the unique
3208  // value nearestZero (distance < 0). This is used later on to see if
3209  // the sample was sent to any processor.
3211  (
3213  List<labelPair>(0),
3214  samples.size(),
3215  map1.constructMap(),
3216  map1.constructHasFlip(),
3217  map1.subMap(),
3218  map1.subHasFlip(),
3219  nearestInfo,
3220  nearestEqOp(),
3221  noOp(), // no flipping
3222  nearestZero
3223  );
3224 
3225 
3226  // 2. Test samples on other processor(s) that overlap
3227  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3228 
3229  // Now we have (in nearestInfo) for every input sample the current best
3230  // hit (on the processor that originates the sample). See if we can
3231  // improve it by sending the queries to any other processors
3232 
3233  autoPtr<mapDistribute> map2Ptr;
3234  {
3236 
3237  // Work array - whether processor bb overlaps the bounding sphere.
3238  boolList procBbOverlaps(Pstream::nProcs());
3239 
3240  label nFound = 0;
3241 
3242  forAll(nearestInfo, samplei)
3243  {
3244  const point& sample = samples[samplei];
3245  const nearestAndDist& ni = nearestInfo[samplei];
3246  const pointIndexHit& info = ni.first();
3247 
3248  if (info.hit())
3249  {
3250  nFound++;
3251  }
3252 
3253  scalar d2 =
3254  (
3255  info.hit()
3256  ? ni.second()
3257  : distSqr[samplei]
3258  );
3259 
3260  label hitProci =
3261  (
3262  info.hit()
3263  ? triIndexer.whichProcID(info.index())
3264  : -1
3265  );
3266 
3267  // Find the processors this sample+radius overlaps.
3268  calcOverlappingProcs(sample, d2, procBbOverlaps);
3269 
3270  forAll(procBbOverlaps, proci)
3271  {
3272  if (procBbOverlaps[proci])
3273  {
3274  // Check this sample wasn't already handled above. This
3275  // could be improved since the sample might have been
3276  // searched on multiple processors. We now only exclude the
3277  // processor where the point was inside.
3278  if (proci != hitProci)
3279  {
3280  dynSendMap[proci].append(samplei);
3281  }
3282  }
3283  }
3284  }
3285 
3286  labelListList sendMap(Pstream::nProcs());
3287  forAll(sendMap, proci)
3288  {
3289  sendMap[proci].transfer(dynSendMap[proci]);
3290  }
3291  map2Ptr.reset(new mapDistribute(std::move(sendMap)));
3292  }
3293 
3294  const mapDistribute& map2 = map2Ptr();
3295 
3296  if (debug)
3297  {
3298  Pout<< "Pass2:"
3299  << " of " << samples.size() << " samples sending to" << endl;
3300  label nSend = 0;
3301  forAll(map2.subMap(), proci)
3302  {
3303  Pout<< " " << proci << "\t" << map2.subMap()[proci].size()
3304  << endl;
3305  nSend += map2.subMap()[proci].size();
3306  }
3307  Pout<< " sum\t" << nSend << endl;
3308  }
3309 
3310  // Send samples and current best distance
3311  pointField localSamples(samples);
3312  map2.distribute(localSamples);
3313  scalarField localDistSqr(distSqr);
3314  forAll(nearestInfo, samplei)
3315  {
3316  const nearestAndDist& ni = nearestInfo[samplei];
3317  if (ni.first().hit())
3318  {
3319  localDistSqr[samplei] = ni.second();
3320  }
3321  }
3322  map2.distribute(localDistSqr);
3323 
3324  // Do local test
3325  List<pointIndexHit> localInfo;
3326  triSurfaceMesh::findNearest(localSamples, localDistSqr, localInfo);
3327  convertTriIndices(localInfo);
3328 
3329  // Pack and send back
3330  List<nearestAndDist> localBest(localSamples.size());
3331  label nHit = 0;
3332  label nIgnoredHit = 0;
3333  forAll(localInfo, i)
3334  {
3335  const pointIndexHit& info = localInfo[i];
3336  if (info.hit())
3337  {
3338  nHit++;
3339  if
3340  (
3341  surfaceClosed_
3342  && !contains(procBb_[Pstream::myProcNo()], info.hitPoint())
3343  )
3344  {
3345  // See above
3346  nIgnoredHit++;
3347  }
3348  else
3349  {
3350  nearestAndDist& ni = localBest[i];
3351  ni.first() = info;
3352  ni.second() = magSqr(info.hitPoint()-localSamples[i]);
3353  }
3354  }
3355  }
3356 
3357  if (debug)
3358  {
3359  Pout<< "distributedTriSurfaceMesh::findNearest :"
3360  << " searched locally for " << localSamples.size()
3361  << " samples with max sphere "
3362  << (localDistSqr.size() ? Foam::sqrt(max(localDistSqr)) : Zero)
3363  << " found hits:" << nHit
3364  << " of which outside local bb:" << nIgnoredHit
3365  << endl;
3366  }
3367 
3369  (
3371  List<labelPair>(0),
3372  samples.size(),
3373  map2.constructMap(),
3374  map2.constructHasFlip(),
3375  map2.subMap(),
3376  map2.subHasFlip(),
3377  localBest,
3378  nearestEqOp(),
3379  noOp(), // no flipping
3380  nearestZero
3381  );
3382 
3383  // Combine with nearestInfo
3384  info.setSize(samples.size());
3385  forAll(samples, samplei)
3386  {
3387  nearestAndDist ni(nearestInfo[samplei]);
3388  nearestEqOp()(ni, localBest[samplei]);
3389 
3390  info[samplei] = ni.first();
3391  }
3392 }
3393 
3394 
3397  const pointField& samples,
3398  const scalarField& nearestDistSqr,
3399  const labelList& regionIndices,
3400  List<pointIndexHit>& info
3401 ) const
3402 {
3403  if (!Pstream::parRun())
3404  {
3406  (
3407  samples,
3408  nearestDistSqr,
3409  regionIndices,
3410  info
3411  );
3412  return;
3413  }
3414 
3415  addProfiling
3416  (
3417  findNearestRegion,
3418  "distributedTriSurfaceMesh::findNearestRegion"
3419  );
3420 
3421  if (debug)
3422  {
3423  Pout<< "distributedTriSurfaceMesh::findNearest :"
3424  << " trying to find nearest and region for " << samples.size()
3425  << " samples with max sphere "
3426  << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero)
3427  << endl;
3428  }
3429 
3430  if (regionIndices.empty())
3431  {
3432  findNearest(samples, nearestDistSqr, info);
3433  }
3434  else
3435  {
3436  // Calculate queries and exchange map
3437  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3438 
3439  pointField allCentres;
3440  scalarField allRadiusSqr;
3441  labelList allSegmentMap;
3442  autoPtr<mapDistribute> mapPtr
3443  (
3444  calcLocalQueries
3445  (
3446  true, // also send to local processor
3447  samples,
3448  nearestDistSqr,
3449 
3450  allCentres,
3451  allRadiusSqr,
3452  allSegmentMap
3453  )
3454  );
3455  const mapDistribute& map = mapPtr();
3456 
3457 
3458  // swap samples to local processor
3459  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3460 
3461  map.distribute(allCentres);
3462  map.distribute(allRadiusSqr);
3463 
3464 
3465  // Do my tests
3466  // ~~~~~~~~~~~
3467 
3468  List<pointIndexHit> allInfo(allCentres.size());
3470  (
3471  allCentres,
3472  allRadiusSqr,
3473  regionIndices,
3474  allInfo
3475  );
3476  convertTriIndices(allInfo);
3477 
3478  forAll(allInfo, i)
3479  {
3480  if (allInfo[i].hit())
3481  {
3482  if
3483  (
3484  surfaceClosed_
3485  && !contains
3486  (
3487  procBb_[Pstream::myProcNo()],
3488  allInfo[i].hitPoint()
3489  )
3490  )
3491  {
3492  // Nearest point is not on local processor so the
3493  // the triangle is only there because some other bit of it
3494  // is on it. Assume there is another processor that holds
3495  // the full surrounding of the triangle so we can clear
3496  // this particular nearest.
3497  allInfo[i].setMiss();
3498  allInfo[i].setIndex(-1);
3499  }
3500  }
3501  }
3502 
3503 
3504  // Send back results
3505  // ~~~~~~~~~~~~~~~~~
3506 
3507  map.reverseDistribute(allSegmentMap.size(), allInfo);
3508 
3509 
3510  // Extract information
3511  // ~~~~~~~~~~~~~~~~~~~
3512 
3513  forAll(allInfo, i)
3514  {
3515  if (allInfo[i].hit())
3516  {
3517  label pointi = allSegmentMap[i];
3518 
3519  if (!info[pointi].hit())
3520  {
3521  // No intersection yet so take this one
3522  info[pointi] = allInfo[i];
3523  }
3524  else
3525  {
3526  // Nearest intersection
3527  if
3528  (
3529  magSqr(allInfo[i].hitPoint()-samples[pointi])
3530  < magSqr(info[pointi].hitPoint()-samples[pointi])
3531  )
3532  {
3533  info[pointi] = allInfo[i];
3534  }
3535  }
3536  }
3537  }
3538  }
3539 }
3540 
3541 
3542 void Foam::distributedTriSurfaceMesh::findLine
3544  const pointField& start,
3545  const pointField& end,
3546  List<pointIndexHit>& info
3547 ) const
3548 {
3549  if (!Pstream::parRun())
3550  {
3552  }
3553  else
3554  {
3555  findLine
3556  (
3557  true, // nearestIntersection
3558  start,
3559  end,
3560  info
3561  );
3562  }
3563 }
3564 
3565 
3568  const pointField& start,
3569  const pointField& end,
3570  List<pointIndexHit>& info
3571 ) const
3572 {
3573  if (!Pstream::parRun())
3574  {
3576  }
3577  else
3578  {
3579  findLine
3580  (
3581  true, // nearestIntersection
3582  start,
3583  end,
3584  info
3585  );
3586  }
3587 }
3588 
3589 
3592  const pointField& start,
3593  const pointField& end,
3594  List<List<pointIndexHit>>& info
3595 ) const
3596 {
3597  if (!Pstream::parRun())
3598  {
3600  return;
3601  }
3602 
3603  if (debug)
3604  {
3605  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3606  << " intersecting with "
3607  << start.size() << " rays" << endl;
3608  }
3609 
3610  addProfiling
3611  (
3612  findLineAll,
3613  "distributedTriSurfaceMesh::findLineAll"
3614  );
3615 
3616  // Reuse fineLine. We could modify all of findLine to do multiple
3617  // intersections but this would mean a lot of data transferred so
3618  // for now we just find nearest intersection and retest from that
3619  // intersection onwards.
3620 
3621  // Work array.
3622  List<pointIndexHit> hitInfo(start.size());
3623 
3624  findLine
3625  (
3626  true, // nearestIntersection
3627  start,
3628  end,
3629  hitInfo
3630  );
3631 
3632  // Tolerances:
3633  // To find all intersections we add a small vector to the last intersection
3634  // This is chosen such that
3635  // - it is significant (SMALL is smallest representative relative tolerance;
3636  // we need something bigger since we're doing calculations)
3637  // - if the start-end vector is zero we still progress
3638  const vectorField dirVec(end-start);
3639  const scalarField magSqrDirVec(magSqr(dirVec));
3640  const vectorField smallVec
3641  (
3642  ROOTSMALL*dirVec
3643  + vector(ROOTVSMALL,ROOTVSMALL,ROOTVSMALL)
3644  );
3645 
3646  // Copy to input and compact any hits
3647  labelList pointMap(start.size());
3648  pointField e0(start.size());
3649  pointField e1(start.size());
3650  label compacti = 0;
3651 
3652  info.setSize(hitInfo.size());
3653  forAll(hitInfo, pointi)
3654  {
3655  if (hitInfo[pointi].hit())
3656  {
3657  info[pointi].setSize(1);
3658  info[pointi][0] = hitInfo[pointi];
3659 
3660  point pt = hitInfo[pointi].hitPoint() + smallVec[pointi];
3661 
3662  if (((pt-start[pointi])&dirVec[pointi]) <= magSqrDirVec[pointi])
3663  {
3664  e0[compacti] = pt;
3665  e1[compacti] = end[pointi];
3666  pointMap[compacti] = pointi;
3667  compacti++;
3668  }
3669  }
3670  else
3671  {
3672  info[pointi].clear();
3673  }
3674  }
3675 
3676  e0.setSize(compacti);
3677  e1.setSize(compacti);
3678  pointMap.setSize(compacti);
3679 
3680 
3681  label iter = 0;
3682  while (returnReduce(e0.size(), sumOp<label>()) > 0)
3683  {
3684  findLine
3685  (
3686  true, // nearestIntersection
3687  e0,
3688  e1,
3689  hitInfo
3690  );
3691 
3692 
3693  // Extract
3694  label compacti = 0;
3695  forAll(hitInfo, i)
3696  {
3697  if (hitInfo[i].hit())
3698  {
3699  label pointi = pointMap[i];
3700 
3701  label sz = info[pointi].size();
3702  info[pointi].setSize(sz+1);
3703  info[pointi][sz] = hitInfo[i];
3704 
3705  point pt = hitInfo[i].hitPoint() + smallVec[pointi];
3706 
3707  // Check current coordinate along ray
3708  scalar d = ((pt-start[pointi])&dirVec[pointi]);
3709 
3710  // Note check for d>0. Very occasionally the octree will find
3711  // an intersection to the left of the ray due to tolerances.
3712  if (d > 0 && d <= magSqrDirVec[pointi])
3713  {
3714  e0[compacti] = pt;
3715  e1[compacti] = end[pointi];
3716  pointMap[compacti] = pointi;
3717  compacti++;
3718  }
3719  }
3720  }
3721 
3722  // Trim
3723  e0.setSize(compacti);
3724  e1.setSize(compacti);
3725  pointMap.setSize(compacti);
3726 
3727  iter++;
3728 
3729  if (iter == 1000)
3730  {
3731  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3732  << " Exiting loop due to excessive number of"
3733  << " intersections along ray"
3734  << " start:" << UIndirectList<point>(start, pointMap)
3735  << " end:" << UIndirectList<point>(end, pointMap)
3736  << " e0:" << UIndirectList<point>(e0, pointMap)
3737  << " e1:" << UIndirectList<point>(e1, pointMap)
3738  << endl;
3739  break;
3740  }
3741  }
3742  if (debug)
3743  {
3744  Pout<< "distributedTriSurfaceMesh::findLineAll :"
3745  << " finished intersecting with "
3746  << start.size() << " rays" << endl;
3747  }
3748 }
3749 
3750 
3753  const List<pointIndexHit>& info,
3754  labelList& region
3755 ) const
3756 {
3757  if (debug)
3758  {
3759  Pout<< "distributedTriSurfaceMesh::getRegion :"
3760  << " getting region for "
3761  << info.size() << " triangles" << endl;
3762  }
3763 
3764  addProfiling(getRegion, "distributedTriSurfaceMesh::getRegion");
3765 
3766  if (!Pstream::parRun())
3767  {
3768  region.setSize(info.size());
3769  forAll(info, i)
3770  {
3771  if (info[i].hit())
3772  {
3773  region[i] = triSurface::operator[](info[i].index()).region();
3774  }
3775  else
3776  {
3777  region[i] = -1;
3778  }
3779  }
3780 
3781  if (debug)
3782  {
3783  Pout<< "distributedTriSurfaceMesh::getRegion :"
3784  << " finished getting region for "
3785  << info.size() << " triangles" << endl;
3786  }
3787 
3788  return;
3789  }
3790 
3791  // Get query data (= local index of triangle)
3792  // ~~~~~~~~~~~~~~
3793 
3794  labelList triangleIndex(info.size());
3795  autoPtr<mapDistribute> mapPtr
3796  (
3797  localQueries
3798  (
3799  info,
3800  triangleIndex
3801  )
3802  );
3803  const mapDistribute& map = mapPtr();
3804 
3805 
3806  // Do my tests
3807  // ~~~~~~~~~~~
3808 
3809  const triSurface& s = static_cast<const triSurface&>(*this);
3810 
3811  region.setSize(triangleIndex.size());
3812 
3813  forAll(triangleIndex, i)
3814  {
3815  label trii = triangleIndex[i];
3816  region[i] = s[trii].region();
3817  }
3818 
3819 
3820  // Send back results
3821  // ~~~~~~~~~~~~~~~~~
3822 
3823  map.reverseDistribute(info.size(), region);
3824 
3825  if (debug)
3826  {
3827  Pout<< "distributedTriSurfaceMesh::getRegion :"
3828  << " finished getting region for "
3829  << info.size() << " triangles" << endl;
3830  }
3831 }
3832 
3833 
3836  const List<pointIndexHit>& info,
3837  vectorField& normal
3838 ) const
3839 {
3840  if (!Pstream::parRun())
3841  {
3842  triSurfaceMesh::getNormal(info, normal);
3843  return;
3844  }
3845 
3846  if (debug)
3847  {
3848  Pout<< "distributedTriSurfaceMesh::getNormal :"
3849  << " getting normal for "
3850  << info.size() << " triangles" << endl;
3851  }
3852 
3853  addProfiling(getNormal, "distributedTriSurfaceMesh::getNormal");
3854 
3855 
3856  // Get query data (= local index of triangle)
3857  // ~~~~~~~~~~~~~~
3858 
3859  labelList triangleIndex(info.size());
3860  autoPtr<mapDistribute> mapPtr
3861  (
3862  localQueries
3863  (
3864  info,
3865  triangleIndex
3866  )
3867  );
3868  const mapDistribute& map = mapPtr();
3869 
3870 
3871  // Do my tests
3872  // ~~~~~~~~~~~
3873 
3874  const triSurface& s = static_cast<const triSurface&>(*this);
3875 
3876  normal.setSize(triangleIndex.size());
3877 
3878  forAll(triangleIndex, i)
3879  {
3880  label trii = triangleIndex[i];
3881  normal[i] = s[trii].unitNormal(s.points());
3882  }
3883 
3884 
3885  // Send back results
3886  // ~~~~~~~~~~~~~~~~~
3887 
3888  map.reverseDistribute(info.size(), normal);
3889 
3890  if (debug)
3891  {
3892  Pout<< "distributedTriSurfaceMesh::getNormal :"
3893  << " finished getting normal for "
3894  << info.size() << " triangles" << endl;
3895  }
3896 }
3897 
3898 
3899 //void Foam::distributedTriSurfaceMesh::getVolumeTypeUncached
3900 //(
3901 // const pointField& samples,
3902 // List<volumeType>& volType
3903 //) const
3904 //{
3905 // if (!Pstream::parRun())
3906 // {
3907 // triSurfaceMesh::getVolumeType(samples, volType);
3908 // return;
3909 // }
3910 //
3911 //
3912 // if (!hasVolumeType())
3913 // {
3914 // FatalErrorInFunction
3915 // << "Volume type only supported for closed distributed surfaces."
3916 // << exit(FatalError);
3917 // }
3918 //
3919 // // Trigger (so parallel synchronised) construction of outside type.
3920 // // Normally this would get triggered from inside individual searches
3921 // // so would not be parallel synchronised
3922 // if (outsideVolType_ == volumeType::UNKNOWN)
3923 // {
3924 // // Determine nearest (in parallel)
3925 // const point outsidePt(bounds().max() + 0.5*bounds().span());
3926 // if (debug)
3927 // {
3928 // Pout<< "distributedTriSurfaceMesh::outsideVolumeType :"
3929 // << " triggering outsidePoint" << outsidePt
3930 // << " orientation" << endl;
3931 // }
3932 //
3933 // const pointField outsidePts(1, outsidePt);
3934 // List<pointIndexHit> nearestInfo;
3935 // findNearest
3936 // (
3937 // outsidePts,
3938 // scalarField(1, Foam::sqr(GREAT)),
3939 // nearestInfo
3940 // );
3941 //
3942 // List<volumeType> outsideVolTypes;
3943 // surfaceSide(outsidePts, nearestInfo, outsideVolTypes);
3944 // outsideVolType_ = outsideVolTypes[0];
3945 //
3946 // if (debug)
3947 // {
3948 // Pout<< "distributedTriSurfaceMesh::outsideVolumeType :"
3949 // << " determined outsidePoint" << outsidePt
3950 // << " to be " << volumeType::names[outsideVolType_] << endl;
3951 // }
3952 // }
3953 //
3954 // // Determine nearest (in parallel)
3955 // List<pointIndexHit> nearestInfo(samples.size());
3956 // findNearest
3957 // (
3958 // samples,
3959 // scalarField(samples.size(), Foam::sqr(GREAT)),
3960 // nearestInfo
3961 // );
3962 //
3963 // // Determine orientation (in parallel)
3964 // surfaceSide(samples, nearestInfo, volType);
3965 //}
3966 
3967 
3970  const pointField& samples,
3971  List<volumeType>& volType
3972 ) const
3973 {
3974  if (!Pstream::parRun())
3975  {
3977  return;
3978  }
3979 
3980 
3981  if (!hasVolumeType())
3982  {
3984  << "Volume type only supported for closed distributed surfaces."
3985  << exit(FatalError);
3986  }
3987 
3988  // Trigger (so parallel synchronised) construction of outside type.
3989  // Normally this would get triggered from inside individual searches
3990  // so would not be parallel synchronised
3991  if (outsideVolType_ == volumeType::UNKNOWN)
3992  {
3993  addProfiling
3994  (
3995  getVolumeType,
3996  "distributedTriSurfaceMesh::getCachedVolumeType"
3997  );
3998 
3999  // Determine nearest (in parallel)
4000  const point outsidePt(bounds().max() + 0.5*bounds().span());
4001  if (debug)
4002  {
4003  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4004  << " triggering outsidePoint" << outsidePt
4005  << " orientation" << endl;
4006  }
4007 
4008  const pointField outsidePts(1, outsidePt);
4009  List<pointIndexHit> nearestInfo;
4010  findNearest
4011  (
4012  outsidePts,
4013  scalarField(1, Foam::sqr(GREAT)),
4014  nearestInfo
4015  );
4016 
4017  List<volumeType> outsideVolTypes;
4018  surfaceSide(outsidePts, nearestInfo, outsideVolTypes);
4019  outsideVolType_ = outsideVolTypes[0];
4020 
4021  if (debug)
4022  {
4023  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4024  << " determined outsidePoint" << outsidePt
4025  << " to be " << volumeType::names[outsideVolType_] << endl;
4026  }
4027 
4028  if
4029  (
4030  outsideVolType_ == volumeType::INSIDE
4031  || outsideVolType_ == volumeType::OUTSIDE
4032  )
4033  {
4034  // Get local tree
4035  const indexedOctree<treeDataTriSurface>& t = tree();
4036  PackedList<2>& nt = t.nodeTypes();
4038  t.nodes();
4039  nt.setSize(nodes.size());
4040  nt = volumeType::UNKNOWN;
4041 
4042  // Collect midpoints
4043  DynamicField<point> midPoints(label(0.5*nodes.size()));
4044  collectLeafMids(0, midPoints);
4045 
4046  if (debug)
4047  {
4048  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4049  << " triggering orientation caching for "
4050  << midPoints.size() << " leaf mids" << endl;
4051  }
4052 
4053  // Get volume type of mid points
4054  List<volumeType> midVolTypes;
4055  getVolumeType(midPoints, midVolTypes);
4056 
4057  // Cache on local tree
4058  label index = 0;
4059  calcVolumeType
4060  (
4061  midVolTypes,
4062  index,
4063  nt,
4064  0 // nodeI
4065  );
4066  if (debug)
4067  {
4068  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4069  << " done orientation caching for "
4070  << midPoints.size() << " leaf mids" << endl;
4071  }
4072  }
4073  }
4074 
4075 
4076  if (debug)
4077  {
4078  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4079  << " finding orientation for " << samples.size()
4080  << " samples" << endl;
4081  }
4082 
4083  addProfiling
4084  (
4085  getVolumeType,
4086  "distributedTriSurfaceMesh::getVolumeType"
4087  );
4088 
4089 
4090  DynamicList<label> outsideSamples;
4091 
4092  // Distribute samples to relevant processors
4093  autoPtr<mapDistribute> mapPtr;
4094  {
4095  labelListList sendMap(Pstream::nProcs());
4096  {
4097  // 1. Count
4098  labelList nSend(Pstream::nProcs(), 0);
4099  forAll(samples, samplei)
4100  {
4101  // Find the processors this sample overlaps.
4102  label nOverlap = 0;
4103  forAll(procBb_, proci)
4104  {
4105  if (contains(procBb_[proci], samples[samplei]))
4106  {
4107  nSend[proci]++;
4108  nOverlap++;
4109  }
4110  }
4111 
4112  // Special case: point is outside all bbs. These would not
4113  // get sent to anyone so handle locally. Note that is the
4114  // equivalent of the test in triSurfaceMesh against the local
4115  // tree bb
4116  if (nOverlap == 0)
4117  {
4118  outsideSamples.append(samplei);
4119  }
4120  }
4121 
4122  forAll(nSend, proci)
4123  {
4124  sendMap[proci].setSize(nSend[proci]);
4125  }
4126  nSend = 0;
4127 
4128  // 2. Fill
4129  forAll(samples, samplei)
4130  {
4131  // Find the processors this sample overlaps.
4132  forAll(procBb_, proci)
4133  {
4134  if (contains(procBb_[proci], samples[samplei]))
4135  {
4136  labelList& procSend = sendMap[proci];
4137  procSend[nSend[proci]++] = samplei;
4138  }
4139  }
4140  }
4141  }
4142 
4143  mapPtr.set(new mapDistribute(std::move(sendMap)));
4144  }
4145  const mapDistribute& map = mapPtr();
4146 
4147  // Get the points I need to test
4148  pointField localPoints(samples);
4149  map.distribute(localPoints);
4150 
4151  volType.setSize(localPoints.size());
4152  volType = volumeType::UNKNOWN;
4153 
4154  // Split the local queries into those that I can look up on the tree and
4155  // those I need to search the nearest for
4156  DynamicField<point> fullSearchPoints(localPoints.size());
4157  DynamicList<label> fullSearchMap(localPoints.size());
4158  forAll(localPoints, i)
4159  {
4160  volType[i] = cachedVolumeType(0, localPoints[i]);
4161  if (volType[i] == volumeType::UNKNOWN)
4162  {
4163  fullSearchMap.append(i);
4164  fullSearchPoints.append(localPoints[i]);
4165  }
4166  }
4167 
4168  if (debug)
4169  {
4170  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4171  << " original samples:" << samples.size()
4172  << " resulting in local queries:"
4173  << localPoints.size()
4174  << " of which cached:" << localPoints.size()-fullSearchPoints.size()
4175  << endl;
4176  }
4177 
4178  // Determine nearest (in parallel)
4179  List<pointIndexHit> nearestInfo;
4180  findNearest
4181  (
4182  fullSearchPoints,
4183  scalarField(fullSearchPoints.size(), Foam::sqr(GREAT)),
4184  nearestInfo
4185  );
4186 
4187  // Determine orientation (in parallel)
4188  List<volumeType> fullSearchType;
4189  surfaceSide(fullSearchPoints, nearestInfo, fullSearchType);
4190 
4191  // Combine
4192  forAll(fullSearchMap, i)
4193  {
4194  volType[fullSearchMap[i]] = fullSearchType[i];
4195  }
4196 
4197  // Send back to originator. In case of multiple answers choose inside or
4198  // outside
4201  (
4203  List<labelPair>(0),
4204  samples.size(),
4205  map.constructMap(),
4206  map.constructHasFlip(),
4207  map.subMap(),
4208  map.subHasFlip(),
4209  volType,
4210  volumeCombineOp(),
4211  noOp(), // no flipping
4212  zero
4213  );
4214 
4215 
4216  // Add the points outside the bounding box
4217  for (label samplei : outsideSamples)
4218  {
4219  volType[samplei] = outsideVolType_;
4220  }
4221 
4222  if (debug)
4223  {
4224  Pout<< "distributedTriSurfaceMesh::getVolumeType :"
4225  << " finished finding orientation for " << samples.size()
4226  << " samples" << endl;
4227  }
4228 }
4229 
4230 
4233  const List<pointIndexHit>& info,
4234  labelList& values
4235 ) const
4236 {
4237  if (!Pstream::parRun())
4238  {
4240  return;
4241  }
4242 
4243  if (debug)
4244  {
4245  Pout<< "distributedTriSurfaceMesh::getField :"
4246  << " retrieving field for "
4247  << info.size() << " triangles" << endl;
4248  }
4249 
4250  addProfiling(getField, "distributedTriSurfaceMesh::getField");
4251 
4252  const auto* fldPtr = findObject<triSurfaceLabelField>("values");
4253 
4254  if (fldPtr)
4255  {
4256  const triSurfaceLabelField& fld = *fldPtr;
4257 
4258  // Get query data (= local index of triangle)
4259  // ~~~~~~~~~~~~~~
4260 
4261  labelList triangleIndex(info.size());
4262  autoPtr<mapDistribute> mapPtr
4263  (
4264  localQueries
4265  (
4266  info,
4267  triangleIndex
4268  )
4269  );
4270  const mapDistribute& map = mapPtr();
4271 
4272 
4273  // Do my tests
4274  // ~~~~~~~~~~~
4275 
4276  values.setSize(triangleIndex.size());
4277 
4278  forAll(triangleIndex, i)
4279  {
4280  label trii = triangleIndex[i];
4281  values[i] = fld[trii];
4282  }
4283 
4284 
4285  // Send back results
4286  // ~~~~~~~~~~~~~~~~~
4287 
4288  map.reverseDistribute(info.size(), values);
4289  }
4290 
4291  if (debug)
4292  {
4293  Pout<< "distributedTriSurfaceMesh::getField :"
4294  << " finished retrieving field for "
4295  << info.size() << " triangles" << endl;
4296  }
4297 }
4298 
4299 
4302  const triSurface& s,
4303  const List<treeBoundBox>& bbs,
4304  boolList& includedFace
4305 )
4306 {
4307  // Determine what triangles to keep.
4308  includedFace.setSize(s.size());
4309  includedFace = false;
4310 
4311  // Create a slightly larger bounding box.
4312  List<treeBoundBox> bbsX(bbs.size());
4313  const scalar eps = 1.0e-4;
4314  forAll(bbs, i)
4315  {
4316  const point mid = bbs[i].centre();
4317  const vector halfSpan = (1.0+eps)*(bbs[i].max() - mid);
4318 
4319  bbsX[i].min() = mid - halfSpan;
4320  bbsX[i].max() = mid + halfSpan;
4321  }
4322 
4323  forAll(s, trii)
4324  {
4325  const labelledTri& f = s[trii];
4326  const point& p0 = s.points()[f[0]];
4327  const point& p1 = s.points()[f[1]];
4328  const point& p2 = s.points()[f[2]];
4329 
4330  if (overlaps(bbsX, p0, p1, p2))
4331  {
4332  includedFace[trii] = true;
4333  }
4334  }
4335 }
4336 
4337 
4338 // Subset the part of surface that is overlapping bb.
4341  const triSurface& s,
4342  const List<treeBoundBox>& bbs,
4343 
4344  labelList& subPointMap,
4345  labelList& subFaceMap
4346 )
4347 {
4348  // Determine what triangles to keep.
4349  boolList includedFace;
4350  overlappingSurface(s, bbs, includedFace);
4351  return subsetMesh(s, includedFace, subPointMap, subFaceMap);
4352 }
4353 
4354 
4355 // Exchanges indices to the processor they come from.
4356 // - calculates exchange map
4357 // - uses map to calculate local triangle index
4361  const List<pointIndexHit>& info,
4362  labelList& triangleIndex
4363 ) const
4364 {
4365  triangleIndex.setSize(info.size());
4366 
4367  const globalIndex& triIndexer = globalTris();
4368 
4369 
4370  // Determine send map
4371  // ~~~~~~~~~~~~~~~~~~
4372 
4373  // Since determining which processor the query should go to is
4374  // cheap we do a multi-pass algorithm to save some memory temporarily.
4375 
4376  // 1. Count
4377  labelList nSend(Pstream::nProcs(), 0);
4378 
4379  forAll(info, i)
4380  {
4381  if (info[i].hit())
4382  {
4383  label proci = triIndexer.whichProcID(info[i].index());
4384  nSend[proci]++;
4385  }
4386  }
4387 
4388  // 2. Size sendMap
4389  labelListList sendMap(Pstream::nProcs());
4390  forAll(nSend, proci)
4391  {
4392  sendMap[proci].setSize(nSend[proci]);
4393  nSend[proci] = 0;
4394  }
4395 
4396  // 3. Fill sendMap
4397  forAll(info, i)
4398  {
4399  if (info[i].hit())
4400  {
4401  label proci = triIndexer.whichProcID(info[i].index());
4402  triangleIndex[i] = triIndexer.toLocal(proci, info[i].index());
4403  sendMap[proci][nSend[proci]++] = i;
4404  }
4405  else
4406  {
4407  triangleIndex[i] = -1;
4408  }
4409  }
4410 
4411 
4412  // Send over how many i need to receive
4413  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4414 
4415  labelListList sendSizes(Pstream::nProcs());
4416  sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
4417  forAll(sendMap, proci)
4418  {
4419  sendSizes[Pstream::myProcNo()][proci] = sendMap[proci].size();
4420  }
4421  Pstream::gatherList(sendSizes);
4422  Pstream::scatterList(sendSizes);
4423 
4424 
4425  // Determine receive map
4426  // ~~~~~~~~~~~~~~~~~~~~~
4427 
4428  labelListList constructMap(Pstream::nProcs());
4429 
4430  // My local segments first
4431  constructMap[Pstream::myProcNo()] = identity
4432  (
4433  sendMap[Pstream::myProcNo()].size()
4434  );
4435 
4436  label segmenti = constructMap[Pstream::myProcNo()].size();
4437  forAll(constructMap, proci)
4438  {
4439  if (proci != Pstream::myProcNo())
4440  {
4441  // What i need to receive is what other processor is sending to me.
4442  label nRecv = sendSizes[proci][Pstream::myProcNo()];
4443  constructMap[proci].setSize(nRecv);
4444 
4445  for (label i = 0; i < nRecv; i++)
4446  {
4447  constructMap[proci][i] = segmenti++;
4448  }
4449  }
4450  }
4451 
4452 
4453  // Pack into distribution map
4454  // ~~~~~~~~~~~~~~~~~~~~~~~~~~
4455 
4456  autoPtr<mapDistribute> mapPtr
4457  (
4458  new mapDistribute
4459  (
4460  segmenti, // size after construction
4461  std::move(sendMap),
4462  std::move(constructMap)
4463  )
4464  );
4465  const mapDistribute& map = mapPtr();
4466 
4467 
4468  // Send over queries
4469  // ~~~~~~~~~~~~~~~~~
4470 
4471  map.distribute(triangleIndex);
4472 
4473  return mapPtr;
4474 }
4475 
4476 
4479  const List<treeBoundBox>& bbs,
4480  const bool keepNonLocal,
4482  autoPtr<mapDistribute>& pointMap
4483 )
4484 {
4485  if (!Pstream::parRun())
4486  {
4487  return;
4488  }
4489 
4490  if (debug)
4491  {
4492  Pout<< "distributedTriSurfaceMesh::distribute :"
4493  << " distributing surface according to method:"
4494  << distributionTypeNames_[distType_]
4495  << " follow bbs:" << flatOutput(bbs) << endl;
4496  }
4497 
4498  addProfiling(distribute, "distributedTriSurfaceMesh::distribute");
4499 
4500 
4501  // Get bbs of all domains
4502  // ~~~~~~~~~~~~~~~~~~~~~~
4503 
4504  {
4506 
4507  switch(distType_)
4508  {
4509  case FOLLOW:
4510  newProcBb[Pstream::myProcNo()].setSize(bbs.size());
4511  forAll(bbs, i)
4512  {
4513  newProcBb[Pstream::myProcNo()][i] = bbs[i];
4514  }
4515  Pstream::gatherList(newProcBb);
4516  Pstream::scatterList(newProcBb);
4517  break;
4518 
4519  case INDEPENDENT:
4520  case DISTRIBUTED:
4521  if (currentDistType_ == distType_)
4522  {
4523  return;
4524  }
4525  newProcBb = independentlyDistributedBbs(*this);
4526  break;
4527 
4528  case FROZEN:
4529  return;
4530  break;
4531 
4532  default:
4534  << "Unsupported distribution type." << exit(FatalError);
4535  break;
4536  }
4537 
4538  if (newProcBb == procBb_)
4539  {
4540  return;
4541  }
4542  else
4543  {
4544  procBb_.transfer(newProcBb);
4545  dict_.set("bounds", procBb_[Pstream::myProcNo()]);
4546  }
4547  }
4548 
4549 
4550  // Debug information
4551  if (debug)
4552  {
4553  labelList nTris(Pstream::nProcs());
4554  nTris[Pstream::myProcNo()] = triSurface::size();
4555  Pstream::gatherList(nTris);
4556  Pstream::scatterList(nTris);
4557 
4559  << "before distribution:" << endl << "\tproc\ttris" << endl;
4560 
4561  forAll(nTris, proci)
4562  {
4563  Info<< '\t' << proci << '\t' << nTris[proci] << endl;
4564  }
4565  Info<< endl;
4566  }
4567 
4568 
4569  // Use procBbs to determine which faces go where
4570  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4571 
4572  labelListList faceSendMap(Pstream::nProcs());
4573  labelListList pointSendMap(Pstream::nProcs());
4574 
4575  forAll(procBb_, proci)
4576  {
4577  overlappingSurface
4578  (
4579  *this,
4580  procBb_[proci],
4581  pointSendMap[proci],
4582  faceSendMap[proci]
4583  );
4584  }
4585 
4586  if (keepNonLocal)
4587  {
4588  // Include in faceSendMap/pointSendMap the triangles that are
4589  // not mapped to any processor so they stay local.
4590 
4591  const triSurface& s = static_cast<const triSurface&>(*this);
4592 
4593  boolList includedFace(s.size(), true);
4594 
4595  forAll(faceSendMap, proci)
4596  {
4597  if (proci != Pstream::myProcNo())
4598  {
4599  forAll(faceSendMap[proci], i)
4600  {
4601  includedFace[faceSendMap[proci][i]] = false;
4602  }
4603  }
4604  }
4605 
4606  // Combine includedFace (all triangles that are not on any neighbour)
4607  // with overlap.
4608 
4609  forAll(faceSendMap[Pstream::myProcNo()], i)
4610  {
4611  includedFace[faceSendMap[Pstream::myProcNo()][i]] = true;
4612  }
4613 
4614  subsetMesh
4615  (
4616  s,
4617  includedFace,
4618  pointSendMap[Pstream::myProcNo()],
4619  faceSendMap[Pstream::myProcNo()]
4620  );
4621  }
4622 
4623 
4624  // Send over how many faces/points i need to receive
4625  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4626 
4627  labelList faceRecvSizes;
4628  Pstream::exchangeSizes(faceSendMap, faceRecvSizes);
4629 
4630 
4631  // Exchange surfaces
4632  // ~~~~~~~~~~~~~~~~~
4633 
4634  // Storage for resulting surface
4635  List<labelledTri> allTris;
4637 
4638  labelListList faceConstructMap(Pstream::nProcs());
4639  labelListList pointConstructMap(Pstream::nProcs());
4640 
4641 
4642  // My own surface first
4643  // ~~~~~~~~~~~~~~~~~~~~
4644 
4645  {
4646  labelList pointMap;
4647  triSurface subSurface
4648  (
4649  subsetMesh
4650  (
4651  *this,
4652  faceSendMap[Pstream::myProcNo()],
4653  pointMap
4654  )
4655  );
4656 
4657  allTris = subSurface;
4658  allPoints = subSurface.points();
4659 
4660  faceConstructMap[Pstream::myProcNo()] = identity
4661  (
4662  faceSendMap[Pstream::myProcNo()].size()
4663  );
4664  pointConstructMap[Pstream::myProcNo()] = identity
4665  (
4666  pointSendMap[Pstream::myProcNo()].size()
4667  );
4668  }
4669 
4670 
4671 
4672  // Send all
4673  // ~~~~~~~~
4674 
4676 
4677  forAll(faceSendMap, proci)
4678  {
4679  if (proci != Pstream::myProcNo())
4680  {
4681  if (faceSendMap[proci].size() > 0)
4682  {
4683  UOPstream str(proci, pBufs);
4684 
4685  labelList pointMap;
4686  triSurface subSurface
4687  (
4688  subsetMesh
4689  (
4690  *this,
4691  faceSendMap[proci],
4692  pointMap
4693  )
4694  );
4695  str << subSurface;
4696  }
4697  }
4698  }
4699 
4700  pBufs.finishedSends(); // no-op for blocking
4701 
4702 
4703  // Receive and merge all
4704  // ~~~~~~~~~~~~~~~~~~~~~
4705 
4706  forAll(faceRecvSizes, proci)
4707  {
4708  if (proci != Pstream::myProcNo())
4709  {
4710  if (faceRecvSizes[proci] > 0)
4711  {
4712  UIPstream str(proci, pBufs);
4713 
4714  // Receive
4715  triSurface subSurface(str);
4716 
4717  // Merge into allSurf
4718  merge
4719  (
4720  mergeDist_,
4721  subSurface,
4722  subSurface.points(),
4723 
4724  allTris,
4725  allPoints,
4726  faceConstructMap[proci],
4727  pointConstructMap[proci]
4728  );
4729  }
4730  }
4731  }
4732 
4733 
4734  faceMap.reset
4735  (
4736  new mapDistribute
4737  (
4738  allTris.size(),
4739  std::move(faceSendMap),
4740  std::move(faceConstructMap)
4741  )
4742  );
4743  pointMap.reset
4744  (
4745  new mapDistribute
4746  (
4747  allPoints.size(),
4748  std::move(pointSendMap),
4749  std::move(pointConstructMap)
4750  )
4751  );
4752 
4753  // Construct triSurface. Reuse storage.
4754  triSurface::operator=(triSurface(allTris, patches(), allPoints, true));
4755 
4756  // Clear trees, preserve topological info (surfaceClosed, outsidePointType)
4757  clearOut();
4758 
4759  // Set the bounds() value to the boundBox of the undecomposed surface
4760  bounds() = boundBox(points(), true);
4761 
4762  currentDistType_ = distType_;
4763 
4764  // Regions stays same
4765  // Volume type stays same.
4766 
4767  distributeFields<label>(faceMap());
4768  distributeFields<scalar>(faceMap());
4769  distributeFields<vector>(faceMap());
4770  distributeFields<sphericalTensor>(faceMap());
4771  distributeFields<symmTensor>(faceMap());
4772  distributeFields<tensor>(faceMap());
4773 
4774  if (debug)
4775  {
4776  labelList nTris(Pstream::nProcs());
4777  nTris[Pstream::myProcNo()] = triSurface::size();
4778  Pstream::gatherList(nTris);
4779  Pstream::scatterList(nTris);
4780 
4782  << "after distribution:" << endl << "\tproc\ttris" << endl;
4783 
4784  forAll(nTris, proci)
4785  {
4786  Info<< '\t' << proci << '\t' << nTris[proci] << endl;
4787  }
4788  Info<< endl;
4789 
4790  if (debug & 2)
4791  {
4792  OBJstream str(searchableSurface::time().path()/"after.obj");
4793  Info<< "Writing local bounding box to " << str.name() << endl;
4794  const List<treeBoundBox>& myBbs = procBb_[Pstream::myProcNo()];
4795  forAll(myBbs, i)
4796  {
4797  pointField pts(myBbs[i].points());
4798  const edgeList& es = treeBoundBox::edges;
4799  forAll(es, ei)
4800  {
4801  const edge& e = es[ei];
4802  str.write(linePointRef(pts[e[0]], pts[e[1]]));
4803  }
4804  }
4805  }
4806  if (debug & 2)
4807  {
4808  OBJstream str(searchableSurface::time().path()/"after_all.obj");
4809  Info<< "Writing all bounding boxes to " << str.name() << endl;
4810  for (auto myBbs : procBb_)
4811  {
4812  forAll(myBbs, i)
4813  {
4814  pointField pts(myBbs[i].points());
4815  const edgeList& es = treeBoundBox::edges;
4816  forAll(es, ei)
4817  {
4818  const edge& e = es[ei];
4819  str.write(linePointRef(pts[e[0]], pts[e[1]]));
4820  }
4821  }
4822  }
4823  }
4824  }
4825 
4826  if (debug)
4827  {
4828  Pout<< "distributedTriSurfaceMesh::distribute :"
4829  << " done distributing surface according to method:"
4830  << distributionTypeNames_[distType_]
4831  << " follow bbs:" << flatOutput(bbs) << endl;
4832  }
4833 }
4834 
4835 
4841  const bool valid
4842 ) const
4843 {
4844  if (debug)
4845  {
4846  Pout<< "distributedTriSurfaceMesh::writeObject :"
4847  << " writing surface valid:" << valid << endl;
4848  }
4849 
4850  // Make sure dictionary goes to same directory as surface
4851  const_cast<fileName&>(dict_.instance()) = searchableSurface::instance();
4852 
4853  // Copy of triSurfaceMesh::writeObject except for the sorting of
4854  // triangles by region. This is done so we preserve region names,
4855  // even if locally we have zero triangles.
4856  {
4858 
4859  if (!mkDir(fullPath.path()))
4860  {
4861  return false;
4862  }
4863 
4864  // Important: preserve any zero-sized patches
4865  triSurface::write(fullPath, true);
4866 
4867  if (!isFile(fullPath))
4868  {
4869  return false;
4870  }
4871  }
4872 
4873  // Dictionary needs to be written in ascii - binary output not supported.
4874  bool ok = dict_.writeObject(IOstream::ASCII, ver, cmp, true);
4875 
4876  if (debug)
4877  {
4878  Pout<< "distributedTriSurfaceMesh::writeObject :"
4879  << " done writing surface" << endl;
4880  }
4881 
4882  return ok;
4883 }
4884 
4885 
4887 {
4888  boundBox bb;
4889  label nPoints;
4890  PatchTools::calcBounds(static_cast<const triSurface&>(*this), bb, nPoints);
4891  bb.reduce();
4892 
4893  os << "Triangles : " << returnReduce(triSurface::size(), sumOp<label>())
4894  << endl
4895  << "Vertices : " << returnReduce(nPoints, sumOp<label>()) << endl
4896  << "Bounding Box : " << bb << endl
4897  << "Closed : " << surfaceClosed_ << endl
4898  << "Outside point: " << volumeType::names[outsideVolType_] << endl
4899  << "Distribution : " << distributionTypeNames_[distType_] << endl;
4900 }
4901 
4902 
4903 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::addToRunTimeSelectionTable
addToRunTimeSelectionTable(decompositionMethod, kahipDecomp, dictionary)
Foam::IOobject::NO_WRITE
Definition: IOobject.H:130
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:74
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:282
Foam::autoPtr::reset
void reset(T *p=nullptr) noexcept
Delete managed object and set to new given pointer.
Definition: autoPtrI.H:158
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:318
Foam::IOobject
Defines the attributes of an object for which implicit objectRegistry management is supported,...
Definition: IOobject.H:104
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:316
geomDecomp.H
Foam::UPstream::commsTypes::nonBlocking
Foam::PointIndexHit::index
label index() const
Return index.
Definition: PointIndexHit.H:113
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:51
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: blockMeshMergeFast.C:94
Foam::IOobject::name
const word & name() const
Return name.
Definition: IOobjectI.H:46
Foam::PrimitivePatch::points
const Field< PointType > & points() const
Return reference to global points.
Definition: PrimitivePatch.H:300
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:482
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
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:69
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:56
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:91
Foam::fileName::path
static std::string path(const std::string &str)
Return directory path name (part before last /)
Definition: fileNameI.H:186
Foam::PatchTools::calcBounds
static void calcBounds(const PrimitivePatch< Face, FaceList, PointField, PointType > &p, boundBox &bb, label &nPoints)
Definition: PatchToolsSearch.C:207
Foam::nearestEqOp::operator()
void operator()(nearestAndDist &x, const nearestAndDist &y) const
Definition: distributedTriSurfaceMesh.C:127
Foam::Zero
static constexpr const zero Zero
Global zero.
Definition: zero.H:128
PatchTools.H
Foam::indexedOctree::isContent
static bool isContent(const labelBits i)
Definition: indexedOctree.H:482
Foam::getField
tmp< GeoField > getField(const IOobject *io, const fvMeshSubsetProxy &proxy)
Get the field and subset it, or return nullptr.
Definition: readFields.H:51
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:1035
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::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())
Distribute data. Note:schedule only used for.
Definition: mapDistributeBaseTemplates.C:122
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::IOobject::instance
const fileName & instance() const
Definition: IOobjectI.H:167
Foam::distributedTriSurfaceMesh::clearOut
void clearOut()
Clear storage.
Definition: distributedTriSurfaceMesh.C:2770
Foam::UPstream::nProcs
static label nProcs(const label communicator=0)
Number of processes in parallel run.
Definition: UPstream.H:426
Foam::fileName::name
static std::string name(const std::string &str)
Return basename (part beyond last /), including its extension.
Definition: fileNameI.H:209
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:1140
Foam::UPstream::parRun
static bool & parRun()
Is this a parallel run?
Definition: UPstream.H:414
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::PointIndexHit::hit
bool hit() const
Is there a hit.
Definition: PointIndexHit.H:107
Foam::indexedOctree::nodes
const List< node > & nodes() const
List of all nodes.
Definition: indexedOctree.H:450
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:72
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:1181
Foam::List::append
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:182
Foam::triSurfaceMesh
IOoject and searching on triSurface.
Definition: triSurfaceMesh.H:100
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:337
Foam::treeBoundBox::edges
static const edgeList edges
Edge to point addressing.
Definition: treeBoundBox.H:153
Foam::UPstream::defaultCommsType
static commsTypes defaultCommsType
Default commsType.
Definition: UPstream.H:273
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:3591
decompositionMethod.H
Foam::distributedTriSurfaceMesh::writeStats
void writeStats(Ostream &os) const
Print some stats. Parallel aware version of.
Definition: distributedTriSurfaceMesh.C:4886
Foam::Pout
prefixOSstream Pout
An Ostream wrapper for parallel output to std::cout.
Foam::OBJstream::write
virtual Ostream & write(const char c)
Write character.
Definition: OBJstream.C:79
bitSet.H
Foam::indexedOctree::getNode
static label getNode(const labelBits i)
Definition: indexedOctree.H:507
Foam::IOobject::time
const Time & time() const
Return time.
Definition: IOobject.C:438
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:4301
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:3752
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:956
Foam::sumOp
Definition: ops.H:213
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:290
Foam::distributedTriSurfaceMesh::findNearest
virtual void findNearest(const pointField &sample, const scalarField &nearestDistSqr, List< pointIndexHit > &) const
Definition: distributedTriSurfaceMesh.C:3018
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:727
Foam::IOobject::db
const objectRegistry & db() const
Return the local objectRegistry.
Definition: IOobject.C:432
Foam::distributedTriSurfaceMesh::distributionType
distributionType
Definition: distributedTriSurfaceMesh.H:93
Foam::DynamicField
Dynamically sized Field.
Definition: DynamicField.H:51
Foam::triSurfaceMesh::checkFile
static fileName checkFile(const IOobject &io, const bool isGlobal)
Return fileName to load IOobject from.
Definition: triSurfaceMesh.C:51
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)
Does triangle intersect bounding box.
Definition: triangleFuncs.C:176
Foam::distributedTriSurfaceMesh::globalTris
const globalIndex & globalTris() const
Triangle indexing (demand driven)
Definition: distributedTriSurfaceMesh.C:2779
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::IOobject::registerObject
bool registerObject() const
Should object created with this IOobject be registered?
Definition: IOobjectI.H:88
Foam::volumeCombineOp::operator()
void operator()(volumeType &x, const volumeType &y) const
Definition: distributedTriSurfaceMesh.C:65
Foam::IOobject::local
const fileName & local() const
Definition: IOobjectI.H:179
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:4360
Foam::distributedTriSurfaceMesh::getField
virtual void getField(const List< pointIndexHit > &, labelList &) const
WIP. From a set of hits (points and.
Definition: distributedTriSurfaceMesh.C:4232
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:55
Foam::autoPtr::set
void set(T *p) noexcept
Deprecated(2018-02) Identical to reset().
Definition: autoPtr.H:275
Foam::label
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:62
Foam::volumeType
An enumeration wrapper for classification of a location as being inside/outside of a volume.
Definition: volumeType.H:60
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]=cellShape(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
Foam::IOstreamOption::versionNumber
Representation of a major/minor version number.
Definition: IOstreamOption.H:79
Foam::Field< vector >
Foam::IOobject::writeOpt
writeOption writeOpt() const
The write option.
Definition: IOobjectI.H:153
Foam::triSurface
Triangulated surface description with patch information.
Definition: triSurface.H:70
Foam::distributedTriSurfaceMesh::DISTRIBUTED
Definition: distributedTriSurfaceMesh.H:97
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::PointIndexHit::hitPoint
const Point & hitPoint() const
Return hit point.
Definition: PointIndexHit.H:119
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
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::DynamicList::append
DynamicList< T, SizeMin > & append(const T &val)
Append an element to the end of this list.
Definition: DynamicListI.H:472
Foam::Enum::getOrDefault
EnumType getOrDefault(const word &key, const dictionary &dict, const EnumType deflt, const bool failsafe=false) const
Definition: Enum.C:190
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:3969
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:436
Foam::IOobject::READ_IF_PRESENT
Definition: IOobject.H:122
Foam::triangle::NONE
Definition: triangle.H:99
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:933
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:455
Foam::triSurfaceMesh::getField
virtual void getField(const List< pointIndexHit > &, labelList &) const
WIP. From a set of hits (points and.
Definition: triSurfaceMesh.C:1157
Foam::distributedTriSurfaceMesh::INDEPENDENT
Definition: distributedTriSurfaceMesh.H:96
Foam::PrimitivePatch< labelledTri, ::Foam::List, pointField, point >::FaceType
labelledTri FaceType
Definition: PrimitivePatch.H:100
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. This will start receives.
Definition: PstreamBuffers.C:80
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::triSurfaceMesh::clearOut
void clearOut()
Clear storage.
Definition: triSurfaceMesh.C:622
addProfiling
#define addProfiling(name, descr)
Define profiling trigger with specified name and description string.
Definition: profilingTrigger.H:114
Foam::segment
Pair< point > segment
Definition: distributedTriSurfaceMesh.H:75
Foam::IOstreamOption::streamFormat
streamFormat
Data format (ascii | binary)
Definition: IOstreamOption.H:64
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:121
Foam::pointHit
PointHit< point > pointHit
Definition: pointHit.H:43
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:115
Foam::mapDistributeBase::subHasFlip
bool subHasFlip() const
Does subMap include a sign.
Definition: mapDistributeBase.H:306
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:137
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:94
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:3835
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::UPstream::myProcNo
static int myProcNo(const label communicator=0)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:444
Foam::UPstream::master
static bool master(const label communicator=0)
Am I the master process.
Definition: UPstream.H:438
Foam::distributedTriSurfaceMesh::writeObject
virtual bool writeObject(IOstream::streamFormat fmt, IOstream::versionNumber ver, IOstream::compressionType cmp, const bool valid) const
Write using given format, version and compression.
Definition: distributedTriSurfaceMesh.C:4837
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::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:4478
Time.H
Foam::globalIndex::toLocal
label toLocal(const label i) const
From global to local on current processor.
Definition: globalIndexI.H:229
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:2764
Foam::linePointRef
line< point, const point & > linePointRef
Line using referred points.
Definition: linePointRef.H:47
Foam::IOstreamOption::ASCII
"ascii"
Definition: IOstreamOption.H:66
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:355
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
Definition: pointIndexHit.H:45
Foam::triangle::EDGE
Definition: triangle.H:101
Foam::PackedList::setSize
void setSize(const label nElem, const unsigned int val=0u)
Alias for resize()
Definition: PackedListI.H:451
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::constant::mathematical::pi
constexpr scalar pi(M_PI)
Foam::nl
constexpr char nl
Definition: Ostream.H:372
Foam::flatOutput
FlatOutput< Container > flatOutput(const Container &obj, label len=0)
Global flatOutput function.
Definition: FlatOutput.H:85
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:876
Foam::Vector< scalar >
Foam::UPstream::worldComm
static label worldComm
Default communicator (all processors)
Definition: UPstream.H:285
Foam::List
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: HashTable.H:102
Foam::sqrt
dimensionedScalar sqrt(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:144
Foam::PackedList< 2 >
Foam::IOobject::readOpt
readOption readOpt() const
The read option.
Definition: IOobjectI.H:141
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::start
label ListType::const_reference const label start
Definition: ListOps.H:408
Foam::globalIndex::whichProcID
label whichProcID(const label i) const
Which processor does global come from? Binary search.
Definition: globalIndexI.H:235
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:115
path
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
Foam::direction
uint8_t direction
Definition: direction.H:47
Foam::word::null
static const word null
An empty word.
Definition: word.H:77
Foam::IOobject::MUST_READ_IF_MODIFIED
Definition: IOobject.H:121
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
Triangle with additional region number.
Definition: labelledTri.H:58
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::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::IOstreamOption::compressionType
compressionType
Compression treatment (UNCOMPRESSED | COMPRESSED)
Definition: IOstreamOption.H:71
Foam::UIndirectList
A List with indirect addressing.
Definition: fvMatrix.H:109
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:3567
Foam::DelaunayMeshTools::allPoints
tmp< pointField > allPoints(const Triangulation &t)
Extract all points in vertex-index order.
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:1188
Foam::Tuple2
A 2-tuple for storing two objects of dissimilar types. The container is similar in purpose to std::pa...
Definition: Tuple2.H:57
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:916
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:185
Foam::triSurfaceMesh::masterOnly
Definition: triSurfaceMesh.H:215
Foam::List::setSize
void setSize(const label newSize)
Alias for resize(const label)
Definition: ListI.H:146
Foam::mapDistributeBase::constructMap
const labelListList & constructMap() const
From subsetted data to new reconstructed data.
Definition: mapDistributeBase.H:294
Foam::IOobject::NO_READ
Definition: IOobject.H:123
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:35
Foam::triSurfaceMesh::hasVolumeType
virtual bool hasVolumeType() const
Whether supports volume type (below) - i.e. whether is closed.
Definition: triSurfaceMesh.C:823
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:979
Foam::decompositionMethod::New
static autoPtr< decompositionMethod > New(const dictionary &decompDict)
Return a reference to the selected decomposition method.
Definition: decompositionMethod.C:359
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
Foam::zero
A class representing the concept of 0 (zero), which can be used to avoid manipulating objects that ar...
Definition: zero.H:61
Foam::IOobject::MUST_READ
Definition: IOobject.H:120
Foam::volumeCombineOp
Combine operator for volume types.
Definition: distributedTriSurfaceMesh.C:62