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