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