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