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