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