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