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