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