Newer
Older
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
Henry Weller
committed
\\ / A nd | Copyright (C) 2011-2016 OpenFOAM Foundation
\\/ M anipulation | Copyright (C) 2015-2016 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "distributedTriSurfaceMesh.H"
#include "mapDistribute.H"
#include "Random.H"
#include "addToRunTimeSelectionTable.H"
#include "triangleFuncs.H"
#include "matchPoints.H"
#include "globalIndex.H"
#include "Time.H"
#include "IFstream.H"
#include "decompositionMethod.H"
#include "PackedBoolList.H"
#include "PatchTools.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(distributedTriSurfaceMesh, 0);
addToRunTimeSelectionTable
(
searchableSurface,
distributedTriSurfaceMesh,
dict
);
template<>
const char* Foam::NamedEnum
<
Foam::distributedTriSurfaceMesh::distributionType,
3
>::names[] =
{
"follow",
"independent",
"frozen"
};
}
const Foam::NamedEnum<Foam::distributedTriSurfaceMesh::distributionType, 3>
Foam::distributedTriSurfaceMesh::distributionTypeNames_;
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
// Read my additional data from the dictionary
bool Foam::distributedTriSurfaceMesh::read()
{
procBb_.setSize(Pstream::nProcs());
procBb_[Pstream::myProcNo()] = List<treeBoundBox>(dict_.lookup("bounds"));
Pstream::gatherList(procBb_);
Pstream::scatterList(procBb_);
// Distribution type
distType_ = distributionTypeNames_.read(dict_.lookup("distributionType"));
// Merge distance
mergeDist_ = readScalar(dict_.lookup("mergeDistance"));
return true;
}
// Is segment fully local?
bool Foam::distributedTriSurfaceMesh::isLocal
(
const List<treeBoundBox>& myBbs,
const point& start,
const point& end
)
{
forAll(myBbs, bbI)
{
if (myBbs[bbI].contains(start) && myBbs[bbI].contains(end))
{
return true;
}
}
return false;
}
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
//void Foam::distributedTriSurfaceMesh::splitSegment
//(
// const label segmentI,
// const point& start,
// const point& end,
// const treeBoundBox& bb,
//
// DynamicList<segment>& allSegments,
// DynamicList<label>& allSegmentMap,
// DynamicList<label> sendMap
//) const
//{
// // Work points
// point clipPt0, clipPt1;
//
// if (bb.contains(start))
// {
// // start within, trim end to bb
// bool clipped = bb.intersects(end, start, clipPt0);
//
// if (clipped)
// {
// // segment from start to clippedStart passes
// // through proc.
// sendMap[procI].append(allSegments.size());
// allSegmentMap.append(segmentI);
// allSegments.append(segment(start, clipPt0));
// }
// }
// else if (bb.contains(end))
// {
// // end within, trim start to bb
// bool clipped = bb.intersects(start, end, clipPt0);
//
// if (clipped)
// {
// sendMap[procI].append(allSegments.size());
// allSegmentMap.append(segmentI);
// allSegments.append(segment(clipPt0, end));
// }
// }
// else
// {
// // trim both
// bool clippedStart = bb.intersects(start, end, clipPt0);
//
// if (clippedStart)
// {
// bool clippedEnd = bb.intersects(end, clipPt0, clipPt1);
//
// if (clippedEnd)
// {
// // middle part of segment passes through proc.
// sendMap[procI].append(allSegments.size());
// allSegmentMap.append(segmentI);
// allSegments.append(segment(clipPt0, clipPt1));
// }
// }
// }
//}
void Foam::distributedTriSurfaceMesh::distributeSegment
(
const label segmentI,
const point& start,
const point& end,
Henry Weller
committed
List<DynamicList<label>>& sendMap
// 1. Fully local already handled outside. Note: retest is cheap.
if (isLocal(procBb_[Pstream::myProcNo()], start, end))
{
return;
}
// 2. If fully inside one other processor, then only need to send
// to that one processor even if it intersects another. Rare occurrence
// but cheap to test.
forAll(procBb_, procI)
{
if (procI != Pstream::myProcNo())
{
const List<treeBoundBox>& bbs = procBb_[procI];
sendMap[procI].append(allSegments.size());
allSegmentMap.append(segmentI);
allSegments.append(segment(start, end));
return;
// 3. If not contained in single processor send to all intersecting
// processors.
forAll(procBb_, procI)
{
const List<treeBoundBox>& bbs = procBb_[procI];
forAll(bbs, bbI)
{
const treeBoundBox& bb = bbs[bbI];
// Scheme a: any processor that intersects the segment gets
// the segment.
// Intersection point
point clipPt;
sendMap[procI].append(allSegments.size());
allSegmentMap.append(segmentI);
allSegments.append(segment(start, end));
// Alternative: any processor only gets clipped bit of
// segment. This gives small problems with additional
// truncation errors.
//splitSegment
//(
// segmentI,
// start,
// end,
// bb,
//
// allSegments,
// allSegmentMap,
// sendMap[procI]
//);
}
}
}
Foam::autoPtr<Foam::mapDistribute>
Foam::distributedTriSurfaceMesh::distributeSegments
(
const pointField& start,
const pointField& end,
labelList& allSegmentMap
) const
{
// Determine send map
// ~~~~~~~~~~~~~~~~~~
labelListList sendMap(Pstream::nProcs());
{
// Since intersection test is quite expensive compared to memory
// allocation we use DynamicList to immediately store the segment
// in the correct bin.
// Segments to test
// Original index of segment
DynamicList<label> dynAllSegmentMap(start.size());
// Per processor indices into allSegments to send
Henry Weller
committed
List<DynamicList<label>> dynSendMap(Pstream::nProcs());
(
segmentI,
start[segmentI],
end[segmentI],
dynAllSegments,
dynAllSegmentMap,
dynSendMap
);
}
// Convert dynamicList to labelList
sendMap.setSize(Pstream::nProcs());
forAll(sendMap, procI)
{
allSegments.transfer(dynAllSegments.shrink());
allSegmentMap.transfer(dynAllSegmentMap.shrink());
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
}
// Send over how many I need to receive.
labelListList sendSizes(Pstream::nProcs());
sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(sendMap, procI)
{
sendSizes[Pstream::myProcNo()][procI] = sendMap[procI].size();
}
Pstream::gatherList(sendSizes);
Pstream::scatterList(sendSizes);
// Determine order of receiving
labelListList constructMap(Pstream::nProcs());
// My local segments first
constructMap[Pstream::myProcNo()] = identity
(
sendMap[Pstream::myProcNo()].size()
);
label segmentI = constructMap[Pstream::myProcNo()].size();
forAll(constructMap, procI)
{
if (procI != Pstream::myProcNo())
{
// What I need to receive is what other processor is sending to me.
label nRecv = sendSizes[procI][Pstream::myProcNo()];
constructMap[procI].setSize(nRecv);
for (label i = 0; i < nRecv; i++)
{
constructMap[procI][i] = segmentI++;
}
}
}
return autoPtr<mapDistribute>
(
new mapDistribute
(
segmentI, // size after construction
sendMap.xfer(),
constructMap.xfer()
)
);
}
void Foam::distributedTriSurfaceMesh::findLine
(
const bool nearestIntersection,
const pointField& start,
const pointField& end,
List<pointIndexHit>& info
) const
{
const indexedOctree<treeDataTriSurface>& octree = tree();
// Initialise
info.setSize(start.size());
forAll(info, i)
{
info[i].setMiss();
}
if (!Pstream::parRun())
forAll(start, i)
{
if (nearestIntersection)
{
info[i] = octree.findLine(start[i], end[i]);
}
else
{
info[i] = octree.findLineAny(start[i], end[i]);
}
}
}
else
{
// Important:force synchronised construction of indexing
const globalIndex& triIndexer = globalTris();
// Do any local queries
// ~~~~~~~~~~~~~~~~~~~~
label nLocal = 0;
forAll(start, i)
{
if (isLocal(procBb_[Pstream::myProcNo()], start[i], end[i]))
if (nearestIntersection)
{
info[i] = octree.findLine(start[i], end[i]);
}
else
{
info[i] = octree.findLineAny(start[i], end[i]);
}
if (info[i].hit())
{
info[i].setIndex(triIndexer.toGlobal(info[i].index()));
}
nLocal++;
if
(
returnReduce(nLocal, sumOp<label>())
< returnReduce(start.size(), sumOp<label>())
)
{
// Not all can be resolved locally. Build segments and map,
// send over segments, do intersections, send back and merge.
// Construct queries (segments)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Segments to test
List<segment> allSegments(start.size());
// Original index of segment
labelList allSegmentMap(start.size());
const autoPtr<mapDistribute> mapPtr
distributeSegments
(
start,
end,
allSegments,
allSegmentMap
)
);
const mapDistribute& map = mapPtr();
label nOldAllSegments = allSegments.size();
// Exchange the segments
// ~~~~~~~~~~~~~~~~~~~~~
map.distribute(allSegments);
// Do tests I need to do
// ~~~~~~~~~~~~~~~~~~~~~
// Intersections
List<pointIndexHit> intersections(allSegments.size());
forAll(allSegments, i)
if (nearestIntersection)
{
intersections[i] = octree.findLine
(
allSegments[i].first(),
allSegments[i].second()
);
}
else
{
intersections[i] = octree.findLineAny
(
allSegments[i].first(),
allSegments[i].second()
);
}
// Convert triangle index to global numbering
if (intersections[i].hit())
{
intersections[i].setIndex
(
triIndexer.toGlobal(intersections[i].index())
);
}
// Exchange the intersections (opposite to segments)
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
map.reverseDistribute(nOldAllSegments, intersections);
// Extract the hits
// ~~~~~~~~~~~~~~~~
forAll(intersections, i)
const pointIndexHit& allInfo = intersections[i];
label segmentI = allSegmentMap[i];
pointIndexHit& hitInfo = info[segmentI];
if (allInfo.hit())
if (!hitInfo.hit())
// No intersection yet so take this one
else if (nearestIntersection)
{
// Nearest intersection
if
(
magSqr(allInfo.hitPoint()-start[segmentI])
< magSqr(hitInfo.hitPoint()-start[segmentI])
)
{
hitInfo = allInfo;
}
}
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
}
}
}
}
}
// Exchanges indices to the processor they come from.
// - calculates exchange map
// - uses map to calculate local triangle index
Foam::autoPtr<Foam::mapDistribute>
Foam::distributedTriSurfaceMesh::calcLocalQueries
(
const List<pointIndexHit>& info,
labelList& triangleIndex
) const
{
triangleIndex.setSize(info.size());
const globalIndex& triIndexer = globalTris();
// Determine send map
// ~~~~~~~~~~~~~~~~~~
// Since determining which processor the query should go to is
// cheap we do a multi-pass algorithm to save some memory temporarily.
// 1. Count
labelList nSend(Pstream::nProcs(), 0);
forAll(info, i)
{
if (info[i].hit())
{
label procI = triIndexer.whichProcID(info[i].index());
nSend[procI]++;
}
}
// 2. Size sendMap
labelListList sendMap(Pstream::nProcs());
forAll(nSend, procI)
{
sendMap[procI].setSize(nSend[procI]);
nSend[procI] = 0;
}
// 3. Fill sendMap
forAll(info, i)
{
if (info[i].hit())
{
label procI = triIndexer.whichProcID(info[i].index());
triangleIndex[i] = triIndexer.toLocal(procI, info[i].index());
sendMap[procI][nSend[procI]++] = i;
}
else
{
triangleIndex[i] = -1;
}
}
// Send over how many I need to receive
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
labelListList sendSizes(Pstream::nProcs());
sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(sendMap, procI)
{
sendSizes[Pstream::myProcNo()][procI] = sendMap[procI].size();
}
Pstream::gatherList(sendSizes);
Pstream::scatterList(sendSizes);
// Determine receive map
// ~~~~~~~~~~~~~~~~~~~~~
labelListList constructMap(Pstream::nProcs());
// My local segments first
constructMap[Pstream::myProcNo()] = identity
(
sendMap[Pstream::myProcNo()].size()
);
label segmentI = constructMap[Pstream::myProcNo()].size();
forAll(constructMap, procI)
{
if (procI != Pstream::myProcNo())
{
// What I need to receive is what other processor is sending to me.
label nRecv = sendSizes[procI][Pstream::myProcNo()];
constructMap[procI].setSize(nRecv);
for (label i = 0; i < nRecv; i++)
{
constructMap[procI][i] = segmentI++;
}
}
}
// Pack into distribution map
// ~~~~~~~~~~~~~~~~~~~~~~~~~~
autoPtr<mapDistribute> mapPtr
(
new mapDistribute
(
segmentI, // size after construction
sendMap.xfer(),
constructMap.xfer()
)
);
const mapDistribute& map = mapPtr();
// Send over queries
// ~~~~~~~~~~~~~~~~~
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
return mapPtr;
}
Foam::label Foam::distributedTriSurfaceMesh::calcOverlappingProcs
(
const point& centre,
const scalar radiusSqr,
boolList& overlaps
) const
{
overlaps = false;
label nOverlaps = 0;
forAll(procBb_, procI)
{
const List<treeBoundBox>& bbs = procBb_[procI];
forAll(bbs, bbI)
{
if (bbs[bbI].overlaps(centre, radiusSqr))
{
overlaps[procI] = true;
nOverlaps++;
break;
}
}
}
return nOverlaps;
}
// Generate queries for parallel distance calculation
// - calculates exchange map
// - uses map to exchange points and radius
Foam::autoPtr<Foam::mapDistribute>
Foam::distributedTriSurfaceMesh::calcLocalQueries
(
const pointField& centres,
const scalarField& radiusSqr,
pointField& allCentres,
scalarField& allRadiusSqr,
labelList& allSegmentMap
) const
{
// Determine queries
// ~~~~~~~~~~~~~~~~~
labelListList sendMap(Pstream::nProcs());
{
// Queries
DynamicList<point> dynAllCentres(centres.size());
DynamicList<scalar> dynAllRadiusSqr(centres.size());
// Original index of segment
DynamicList<label> dynAllSegmentMap(centres.size());
// Per processor indices into allSegments to send
Henry Weller
committed
List<DynamicList<label>> dynSendMap(Pstream::nProcs());
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
// Work array - whether processor bb overlaps the bounding sphere.
boolList procBbOverlaps(Pstream::nProcs());
forAll(centres, centreI)
{
// Find the processor this sample+radius overlaps.
calcOverlappingProcs
(
centres[centreI],
radiusSqr[centreI],
procBbOverlaps
);
forAll(procBbOverlaps, procI)
{
if (procI != Pstream::myProcNo() && procBbOverlaps[procI])
{
dynSendMap[procI].append(dynAllCentres.size());
dynAllSegmentMap.append(centreI);
dynAllCentres.append(centres[centreI]);
dynAllRadiusSqr.append(radiusSqr[centreI]);
}
}
}
// Convert dynamicList to labelList
sendMap.setSize(Pstream::nProcs());
forAll(sendMap, procI)
{
allCentres.transfer(dynAllCentres.shrink());
allRadiusSqr.transfer(dynAllRadiusSqr.shrink());
allSegmentMap.transfer(dynAllSegmentMap.shrink());
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
}
// Send over how many I need to receive.
labelListList sendSizes(Pstream::nProcs());
sendSizes[Pstream::myProcNo()].setSize(Pstream::nProcs());
forAll(sendMap, procI)
{
sendSizes[Pstream::myProcNo()][procI] = sendMap[procI].size();
}
Pstream::gatherList(sendSizes);
Pstream::scatterList(sendSizes);
// Determine order of receiving
labelListList constructMap(Pstream::nProcs());
// My local segments first
constructMap[Pstream::myProcNo()] = identity
(
sendMap[Pstream::myProcNo()].size()
);
label segmentI = constructMap[Pstream::myProcNo()].size();
forAll(constructMap, procI)
{
if (procI != Pstream::myProcNo())
{
// What I need to receive is what other processor is sending to me.
label nRecv = sendSizes[procI][Pstream::myProcNo()];
constructMap[procI].setSize(nRecv);
for (label i = 0; i < nRecv; i++)
{
constructMap[procI][i] = segmentI++;
}
}
}
autoPtr<mapDistribute> mapPtr
(
new mapDistribute
(
segmentI, // size after construction
sendMap.xfer(),
constructMap.xfer()
// Find bounding boxes that guarantee a more or less uniform distribution
// of triangles. Decomposition in here is only used to get the bounding
// boxes, actual decomposition is done later on.
// Returns a per processor a list of bounding boxes that most accurately
// describe the shape. For now just a single bounding box per processor but
// optimisation might be to determine a better fitting shape.
Henry Weller
committed
Foam::List<Foam::List<Foam::treeBoundBox>>
Foam::distributedTriSurfaceMesh::independentlyDistributedBbs
(
const triSurface& s
)
{
if (!decomposer_.valid())
{
// Use singleton decomposeParDict. Cannot use decompositionModel
// here since we've only got Time and not a mesh.
if
searchableSurface::time().foundObject<IOdictionary>
)
{
decomposer_ = decompositionMethod::New
(
searchableSurface::time().lookupObject<IOdictionary>
(
"decomposeParDict"
)
);
}
else
{
if (!decomposeParDict_.valid())
{
decomposeParDict_.reset
(
new IOdictionary
(
IOobject
(
"decomposeParDict",
searchableSurface::time().system(),
searchableSurface::time(),
IOobject::MUST_READ,
IOobject::NO_WRITE
)
)
);
}
decomposer_ = decompositionMethod::New(decomposeParDict_());
}
FatalErrorInFunction
<< "The decomposition method " << decomposer_().typeName
<< " does not decompose in parallel."
<< " Please choose one that does." << exit(FatalError);
}
if (!isA<geomDecomp>(decomposer_()))
{
FatalErrorInFunction
<< "The decomposition method " << decomposer_().typeName
<< " is not a geometric decomposition method." << endl
<< "Only geometric decomposition methods are currently"
<< " supported."
<< exit(FatalError);
}
}
// Do decomposition according to triangle centre
pointField triCentres(s.size());
forAll(s, triI)
{
triCentres[triI] = s[triI].centre(s.points());
}
geomDecomp& decomposer = refCast<geomDecomp>(decomposer_());
labelList distribution(decomposer.decompose(triCentres));
// Find bounding box for all triangles on new distribution.
// Initialise to inverted box (VGREAT, -VGREAT)
Henry Weller
committed
List<List<treeBoundBox>> bbs(Pstream::nProcs());
forAll(bbs, procI)
{
bbs[procI].setSize(1);
//bbs[procI][0] = boundBox::invertedBox;
bbs[procI][0].min() = point( VGREAT, VGREAT, VGREAT);
bbs[procI][0].max() = point(-VGREAT, -VGREAT, -VGREAT);
forAll(s, triI)
{
point& bbMin = bbs[distribution[triI]][0].min();
point& bbMax = bbs[distribution[triI]][0].max();
const triSurface::FaceType& f = s[triI];
forAll(f, fp)
{
const point& pt = s.points()[f[fp]];
bbMin = ::Foam::min(bbMin, pt);
bbMax = ::Foam::max(bbMax, pt);
}
}
// Now combine for all processors and convert to correct format.
forAll(bbs, procI)
{
forAll(bbs[procI], i)
{
reduce(bbs[procI][i].min(), minOp<point>());
reduce(bbs[procI][i].max(), maxOp<point>());
}
}
return bbs;
}
// Does any part of triangle overlap bb.
bool Foam::distributedTriSurfaceMesh::overlaps
(
const List<treeBoundBox>& bbs,
const point& p0,
const point& p1,
const point& p2
)
{
forAll(bbs, bbI)
{
const treeBoundBox& bb = bbs[bbI];
triBb.min() = min(triBb.min(), p1);
triBb.min() = min(triBb.min(), p2);
triBb.max() = max(triBb.max(), p1);
triBb.max() = max(triBb.max(), p2);
// Exact test of triangle intersecting bb
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// Quick rejection. If whole bounding box of tri is outside cubeBb then
// there will be no intersection.
if (bb.overlaps(triBb))
{
// Check if one or more triangle point inside
if (bb.contains(p0) || bb.contains(p1) || bb.contains(p2))
{
// One or more points inside
return true;
}
// Now we have the difficult case: all points are outside but
// connecting edges might go through cube. Use fast intersection
// of bounding box.
bool intersect = triangleFuncs::intersectBb(p0, p1, p2, bb);
if (intersect)
{
return true;
}
}
}
return false;
}
void Foam::distributedTriSurfaceMesh::subsetMeshMap
(
const triSurface& s,
const boolList& include,
const label nIncluded,
labelList& newToOldPoints,
labelList& oldToNewPoints,
labelList& newToOldFaces
)
{
newToOldFaces.setSize(nIncluded);
newToOldPoints.setSize(s.points().size());
oldToNewPoints.setSize(s.points().size());
oldToNewPoints = -1;
{
label faceI = 0;
label pointI = 0;
forAll(include, oldFacei)
{
if (include[oldFacei])
{