diff --git a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C index 198be01bcf71f133a77380de31ef7b6ba5ec5ff6..a6547fa1142cc9ce4ce14ffe8a302042d99090c7 100644 --- a/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C +++ b/src/mesh/snappyHexMesh/shellSurfaces/shellSurfaces.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2015 OpenFOAM Foundation - Copyright (C) 2015-2022 OpenCFD Ltd. + Copyright (C) 2015-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -168,12 +168,7 @@ void Foam::shellSurfaces::orient() { const searchableSurface& s = allGeometry_[shells_[shellI]]; - if - ( - modes_[shellI] != DISTANCE - && isA<triSurfaceMesh>(s) - && !isA<distributedTriSurfaceMesh>(s) - ) + if (modes_[shellI] != DISTANCE && isA<triSurfaceMesh>(s)) { hasSurface = true; @@ -196,35 +191,39 @@ void Foam::shellSurfaces::orient() { const searchableSurface& s = allGeometry_[shells_[shellI]]; - if - ( - modes_[shellI] != DISTANCE - && isA<triSurfaceMesh>(s) - && !isA<distributedTriSurfaceMesh>(s) - ) + if (modes_[shellI] != DISTANCE && isA<triSurfaceMesh>(s)) { - triSurfaceMesh& shell = const_cast<triSurfaceMesh&> + List<pointIndexHit> info; + vectorField normal; + labelList region; + s.findNearest ( - refCast<const triSurfaceMesh>(s) + pointField(1, outsidePt), + scalarField(1, GREAT), + info, + normal, + region ); - // Flip surface so outsidePt is outside. - bool anyFlipped = orientedSurface::orient - ( - shell, - outsidePt, - true - ); + //Pout<< "outsidePt:" << outsidePt << endl; + //Pout<< "info :" << info[0] << endl; + //Pout<< "normal :" << normal[0] << endl; + //Pout<< "region :" << region[0] << endl; - if (anyFlipped && !dryRun_) + bool anyFlipped = false; + if ((normal[0] & (info[0].point()-outsidePt)) > 0) { - // orientedSurface will have done a clearOut of the surface. - // we could do a clearout of the triSurfaceMeshes::trees() - // but these aren't affected by orientation - // (except for cached - // sideness which should not be set at this point. - // !!Should check!) + triSurfaceMesh& shell = const_cast<triSurfaceMesh&> + ( + refCast<const triSurfaceMesh>(s) + ); + shell.flip(); + anyFlipped = true; + } + + if (anyFlipped && !dryRun_) + { Info<< "shellSurfaces : Flipped orientation of surface " << s.name() << " so point " << outsidePt << " is outside." << endl; diff --git a/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.C b/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.C index 8976887905924d6c8f0fe559955959f44af87b7a..4d13ca4deb75bfa8ff9db1b22e93045bb79a2a45 100644 --- a/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.C +++ b/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2015-2020,2022 OpenCFD Ltd. + Copyright (C) 2015-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -101,6 +101,7 @@ bool Foam::triSurfaceMesh::isSurfaceClosed() const if (debug) { Pout<< "triSurfaceMesh::isSurfaceClosed:" + << " surface:" << searchableSurface::name() << " determining closedness for surface with " << triSurface::size() << " triangles" << endl; } @@ -223,6 +224,7 @@ bool Foam::triSurfaceMesh::isSurfaceClosed() const if (debug) { Pout<< "triSurfaceMesh::isSurfaceClosed :" + << " surface:" << searchableSurface::name() << " surface is non-manifold" << endl; } return false; @@ -240,6 +242,7 @@ bool Foam::triSurfaceMesh::isSurfaceClosed() const if (debug) { Pout<< "triSurfaceMesh::isSurfaceClosed :" + << " surface:" << searchableSurface::name() << " surface is open" << endl; } return false; @@ -274,6 +277,7 @@ bool Foam::triSurfaceMesh::isSurfaceClosed() const if (debug) { Pout<< "triSurfaceMesh::isSurfaceClosed :" + << " surface:" << searchableSurface::name() << " surface is closed" << endl; } return true; @@ -738,6 +742,7 @@ void Foam::triSurfaceMesh::movePoints(const pointField& newPoints) if (debug) { Pout<< "triSurfaceMesh::movePoints :" + << " surface:" << searchableSurface::name() << " moving at time " << objectRegistry::time().timeName() << endl; } @@ -773,6 +778,7 @@ Foam::triSurfaceMesh::edgeTree() const if (debug) { Pout<< "triSurfaceMesh::edgeTree :" + << " surface:" << searchableSurface::name() << " constructing tree for " << nEdges() - nInternalEdges() << " boundary edges" << endl; } @@ -882,6 +888,7 @@ Foam::volumeType Foam::triSurfaceMesh::outsideVolumeType() const if (debug) { Pout<< "triSurfaceMesh::outsideVolumeType :" + << " surface:" << searchableSurface::name() << " triggering outsidePoint:" << outsidePt << " orientation" << endl; } @@ -906,6 +913,49 @@ Foam::volumeType Foam::triSurfaceMesh::outsideVolumeType() const } +void Foam::triSurfaceMesh::flip() +{ + if (debug) + { + Pout<< "triSurfaceMesh::flip :" + << " surface:" << searchableSurface::name() + << " with current orientation " + << volumeType::names[outsideVolType_] + << endl; + } + + // Don't bother getting nearest etc. Just flip the triangles. + + // triSurface + { + triSurface& s = *this; + s.clearOut(); + for (auto& tri : s) + { + tri.flip(); + } + } + + // triSurfaceRegionSearch (if cached volume type) + triSurfaceRegionSearch::flip(); + + // edge tree not relevant + + if (hasVolumeType()) + { + // outsideVolType_ + if (outsideVolType_ == volumeType::INSIDE) + { + outsideVolType_ = volumeType::OUTSIDE; + } + else if (outsideVolType_ == volumeType::OUTSIDE) + { + outsideVolType_ = volumeType::INSIDE; + } + } +} + + void Foam::triSurfaceMesh::findNearest ( const pointField& samples, @@ -916,6 +966,7 @@ void Foam::triSurfaceMesh::findNearest if (debug) { Pout<< "triSurfaceMesh::findNearest :" + << " surface:" << searchableSurface::name() << " trying to find nearest for " << samples.size() << " samples with max sphere " << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero) @@ -942,6 +993,7 @@ void Foam::triSurfaceMesh::findNearest if (debug) { Pout<< "triSurfaceMesh::findNearest :" + << " surface:" << searchableSurface::name() << " trying to find nearest and region for " << samples.size() << " samples with max sphere " << (samples.size() ? Foam::sqrt(max(nearestDistSqr)) : Zero) @@ -973,6 +1025,7 @@ void Foam::triSurfaceMesh::findLine if (debug) { Pout<< "triSurfaceMesh::findLine :" + << " surface:" << searchableSurface::name() << " intersecting with " << start.size() << " rays" << endl; } @@ -996,6 +1049,7 @@ void Foam::triSurfaceMesh::findLineAny if (debug) { Pout<< "triSurfaceMesh::findLineAny :" + << " surface:" << searchableSurface::name() << " intersecting with " << start.size() << " rays" << endl; } @@ -1019,6 +1073,7 @@ void Foam::triSurfaceMesh::findLineAll if (debug) { Pout<< "triSurfaceMesh::findLineAll :" + << " surface:" << searchableSurface::name() << " intersecting with " << start.size() << " rays" << endl; } @@ -1041,6 +1096,7 @@ void Foam::triSurfaceMesh::getRegion if (debug) { Pout<< "triSurfaceMesh::getRegion :" + << " surface:" << searchableSurface::name() << " getting region for " << info.size() << " triangles" << endl; } @@ -1074,6 +1130,7 @@ void Foam::triSurfaceMesh::getNormal if (debug) { Pout<< "triSurfaceMesh::getNormal :" + << " surface:" << searchableSurface::name() << " getting normal for " << info.size() << " triangles" << endl; } @@ -1182,6 +1239,7 @@ void Foam::triSurfaceMesh::setField(const labelList& values) if (debug) { Pout<< "triSurfaceMesh::setField :" + << " surface:" << searchableSurface::name() << " finished setting field for " << values.size() << " triangles" << endl; } @@ -1213,6 +1271,7 @@ void Foam::triSurfaceMesh::getField if (debug) { Pout<< "triSurfaceMesh::setField :" + << " surface:" << searchableSurface::name() << " finished getting field for " << info.size() << " triangles" << endl; } @@ -1231,6 +1290,7 @@ void Foam::triSurfaceMesh::getVolumeType if (debug) { Pout<< "triSurfaceMesh::getVolumeType :" + << " surface:" << searchableSurface::name() << " finding orientation for " << points.size() << " samples" << endl; } diff --git a/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.H b/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.H index e5a18bdeb4bbc417cb1c817a79c28dfb58b64cd8..c833e24d2b5b70cc4cd0362140d4ab6a6fe73abf 100644 --- a/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.H +++ b/src/meshTools/searchableSurfaces/triSurfaceMesh/triSurfaceMesh.H @@ -216,6 +216,9 @@ public: return triSurface::size(); } + //- Flip triangles, outsideVolumeType and all cached inside/outside. + virtual void flip(); + //- Get representative set of element coordinates // Usually the element centres (should be of length size()). virtual tmp<pointField> coordinates() const; diff --git a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.C b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.C index cb8a67db5cc6ae0f4c5970f5a3a56a020b559b3b..eae21fb06e97b34b8b04ec50e7f136d9e5ec8c23 100644 --- a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.C +++ b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.C @@ -251,4 +251,26 @@ void Foam::triSurfaceRegionSearch::findNearest } +void Foam::triSurfaceRegionSearch::flip() +{ + triSurfaceSearch::flip(); + + for (auto& tree : treeByRegion_) + { + PackedList<2>& nodeTypes = tree.nodeTypes(); + forAll(nodeTypes, i) + { + if (nodeTypes[i] == volumeType::INSIDE) + { + nodeTypes[i] = volumeType::OUTSIDE; + } + else if (nodeTypes[i] == volumeType::OUTSIDE) + { + nodeTypes[i] = volumeType::INSIDE; + } + } + } +} + + // ************************************************************************* // diff --git a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.H b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.H index 724617d1321275ddc266ca0f305fc89170da2e6e..54143930eb55c1f451714210884f6ce5e59d7b99 100644 --- a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.H +++ b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceRegionSearch.H @@ -128,6 +128,11 @@ public: const labelList& regionIndices, List<pointIndexHit>& info ) const; + + // Edit + + //- Flip orientation + void flip(); }; diff --git a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.C b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.C index 33fa142a6149cdb8b53c7d8d097a4e7dbe654ea6..b80944a4910ecf014befa9464c2f0c1bcb2889ca 100644 --- a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.C +++ b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.C @@ -255,6 +255,26 @@ Foam::triSurfaceSearch::tree() const } +void Foam::triSurfaceSearch::flip() +{ + if (treePtr_) + { + PackedList<2>& nodeTypes = treePtr_->nodeTypes(); + forAll(nodeTypes, i) + { + if (nodeTypes[i] == volumeType::INSIDE) + { + nodeTypes[i] = volumeType::OUTSIDE; + } + else if (nodeTypes[i] == volumeType::OUTSIDE) + { + nodeTypes[i] = volumeType::INSIDE; + } + } + } +} + + // Determine inside/outside for samples Foam::boolList Foam::triSurfaceSearch::calcInside ( diff --git a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.H b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.H index a4ce7c8c986b716d38b1878f7b76dab7eb27470d..2deb9abf9e2ad5e2a0469a8acf18f8279dfc0222 100644 --- a/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.H +++ b/src/meshTools/triSurface/triSurfaceSearch/triSurfaceSearch.H @@ -126,6 +126,9 @@ public: //- Demand driven construction of the octree const indexedOctree<treeDataTriSurface>& tree() const; + //- Flip orientation (if cached on octree) + void flip(); + //- Return reference to the surface. const triSurface& surface() const { diff --git a/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.C b/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.C index 8c32aface9d13a59d0e1510d601f641950f01d5e..bdb63f152e13184d6d624babf7be5be013612aa8 100644 --- a/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.C +++ b/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.C @@ -110,6 +110,7 @@ namespace Foam }; + ////- Same as above but with some more debugging //typedef Tuple2<Pair<point>, volumeType> NearType; // ////- Combine operator for volume types @@ -337,73 +338,145 @@ Foam::word Foam::distributedTriSurfaceMesh::findLocalInstance } -// Read my additional data from the dictionary -bool Foam::distributedTriSurfaceMesh::read() +bool Foam::distributedTriSurfaceMesh::readSettings(const bool undecomposed) { // Get bb of all domains. procBb_.resize_nocopy(Pstream::nProcs()); - if (dict_.empty()) + if (!dict_.readIfPresent("bounds", procBb_[Pstream::myProcNo()])) { - // Did not find the distributed version; assume master has loaded the - // triSurfaceMesh version. Make up some settings. - + // Not found. Use local triangles' bounding box const boundBox& localBb = triSurfaceMesh::bounds(); procBb_[Pstream::myProcNo()] = treeBoundBoxList(1, treeBoundBox(localBb)); dict_.add("bounds", procBb_[Pstream::myProcNo()]); + } + - // Wanted distribution type + decomposeUsingBbs_ = dict_.getOrDefault<bool>("decomposeUsingBbs", true); + + // Wanted distribution type + if + ( + !distributionTypeNames_.readIfPresent + ( + "distributionType", + dict_, + distType_ + ) + ) + { + // Not found. Make up most sensible data distType_ = DISTRIBUTED; //INDEPENDENT; dict_.add("distributionType", distributionTypeNames_[distType_]); + } - // Merge distance + // Merge distance + if (!dict_.readIfPresent("mergeDistance", mergeDist_)) + { mergeDist_ = SMALL; dict_.add("mergeDistance", mergeDist_); + } - // Force underlying triSurfaceMesh to calculate volume type - // (is topological walk; does not construct tree) - surfaceClosed_ = triSurfaceMesh::hasVolumeType(); - Pstream::broadcast(surfaceClosed_); + // Is closed? + if (!dict_.readIfPresent<label>("closed", surfaceClosed_)) + { + if (undecomposed) + { + surfaceClosed_ = triSurfaceMesh::hasVolumeType(); + Pstream::broadcast(surfaceClosed_); + } + else + { + surfaceClosed_ = false; + } dict_.add("closed", surfaceClosed_); + } - // Delay calculating outside vol type since constructs tree. Is ok - // after distributing since then local surfaces much smaller - //outsideVolType_ = volumeType::UNKNOWN; - //if (surfaceClosed_) - //{ - // point outsidePt(localBb.max()+localBb.centre()); - // List<volumeType> outsideVolTypes; - // triSurfaceMesh::getVolumeType - // ( - // pointField(1, outsidePt), - // outsideVolTypes - // ); - // outsideVolType_ = outsideVolTypes[0]; - //} - //dict_.add("outsideVolumeType", volumeType::names[outsideVolType_]); + // Do we know volume inside/outside? + volumeType::type vt; + if + ( + volumeType::names.readIfPresent + ( + "outsideVolumeType", + dict_, + vt + ) + ) + { + outsideVolType_ = vt; } else { - dict_.readEntry("bounds", procBb_[Pstream::myProcNo()]); + if (undecomposed && surfaceClosed_) + { + if (UPstream::master()) + { + // Determine nearest to do inside/outside testing. + // Do linear search to avoid building tree on this large + // undecomposed surface. See e.g. triSurfaceTools::surfaceSide + + const triSurface& surf = *this; + const pointField& points = surf.points(); + const boundBox& localBb = triSurfaceMesh::bounds(); + const point outsidePt(localBb.max()+localBb.centre()); + + label nearTrii = -1; + pointHit nearInfo; // distance=GREAT + forAll(surf, trii) + { + pointHit info = surf[trii].nearestPoint(outsidePt, points); - // Wanted distribution type - distType_ = distributionTypeNames_.get("distributionType", dict_); + if (info.distance() < nearInfo.distance()) + { + nearTrii = trii; + nearInfo = info; + } + } - // Merge distance - dict_.readEntry("mergeDistance", mergeDist_); + if (nearTrii != -1) + { + const triSurfaceTools::sideType t = + triSurfaceTools::surfaceSide + ( + surf, + outsidePt, + nearTrii + ); - // Distribution type - surfaceClosed_ = dict_.getOrDefault<bool>("closed", false); + if (t == triSurfaceTools::UNKNOWN) + { + outsideVolType_ = volumeType::UNKNOWN; + } + else if (t == triSurfaceTools::INSIDE) + { + outsideVolType_ = volumeType::INSIDE; + } + else if (t == triSurfaceTools::OUTSIDE) + { + outsideVolType_ = volumeType::OUTSIDE; + } + else + { + FatalErrorInFunction << "problem" << abort(FatalError); + } + } + } + // Scatter master + label volType = outsideVolType_; + Pstream::broadcast(volType); + outsideVolType_ = volumeType(volType); + } + else + { + // Mark as to-be-done + outsideVolType_ = volumeType::UNKNOWN; + } - outsideVolType_ = volumeType::names.getOrDefault - ( - "outsideVolumeType", - dict_, - volumeType::UNKNOWN - ); + dict_.add("outsideVolumeType", volumeType::names[outsideVolType_]); } Pstream::allGatherList(procBb_); @@ -412,6 +485,41 @@ bool Foam::distributedTriSurfaceMesh::read() } +void Foam::distributedTriSurfaceMesh::calcVertexNormals +( + const triSurface& surf, + List<FixedList<vector, 3>>& vn +) const +{ + // For now don't do features so just use the point normals + vectorField pointNormals(surf.points().size(), vector::zero); + forAll(surf, facei) + { + const triSurface::face_type& f = surf[facei]; + const vector n = f.unitNormal(surf.points()); + forAll(f, fp) + { + const label pointi = f[fp]; + pointNormals[pointi] += n; + } + } + pointNormals.normalise(); + + + vn.resize_nocopy(surf.size()); + vn = Zero; + + forAll(surf, facei) + { + const triSurface::face_type& f = surf[facei]; + forAll(f, fp) + { + vn[facei][fp] = pointNormals[f[fp]]; + } + } +} + + // Is segment fully local? bool Foam::distributedTriSurfaceMesh::isLocal ( @@ -504,28 +612,31 @@ void Foam::distributedTriSurfaceMesh::distributeSegment List<DynamicList<label>>& sendMap ) const { - // 1. Fully local already handled outside. Note: retest is cheap. - if (isLocal(procBb_[Pstream::myProcNo()], start, end)) + if (decomposeUsingBbs_) { - return; - } + // 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()) + // 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) { - const List<treeBoundBox>& bbs = procBb_[proci]; - - if (isLocal(bbs, start, end)) + if (proci != Pstream::myProcNo()) { - sendMap[proci].append(allSegments.size()); - allSegmentMap.append(segmenti); - allSegments.append(segment(start, end)); - return; + const List<treeBoundBox>& bbs = procBb_[proci]; + + if (isLocal(bbs, start, end)) + { + sendMap[proci].append(allSegments.size()); + allSegmentMap.append(segmenti); + allSegments.append(segment(start, end)); + return; + } } } } @@ -644,7 +755,7 @@ void Foam::distributedTriSurfaceMesh::findLine ( const bool nearestIntersection, const pointField& start, - const pointField& end, + const pointField& initialEnd, List<pointIndexHit>& info ) const { @@ -659,6 +770,9 @@ void Foam::distributedTriSurfaceMesh::findLine const indexedOctree<treeDataTriSurface>& octree = tree(); + // end-point - updated with local hits + pointField end(initialEnd); + // Initialise info.setSize(start.size()); forAll(info, i) @@ -691,6 +805,8 @@ void Foam::distributedTriSurfaceMesh::findLine if (info[i].hit()) { info[i].setIndex(triIndexer.toGlobal(info[i].index())); + // Update endpoint + end[i] = info[i].hitPoint(); } nLocal++; } @@ -699,113 +815,121 @@ void Foam::distributedTriSurfaceMesh::findLine if ( - returnReduce(nLocal, sumOp<label>()) - < returnReduce(start.size(), sumOp<label>()) + decomposeUsingBbs_ + && ( + 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. + // In decomposeUsingBbs_ mode we're guaranteed to have all the geometry + // inside the local bb. Since we've done all tests we're done. + return; + } - // Construct queries (segments) - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Not all can be resolved locally. Build segments and map, + // send over segments, do intersections, send back and merge. - // Segments to test - List<segment> allSegments(start.size()); - // Original index of segment - labelList allSegmentMap(start.size()); - const autoPtr<mapDistribute> mapPtr + // Construct queries (segments) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Segments to test + List<segment> allSegments(start.size()); + // Original index of segment + labelList allSegmentMap(start.size()); + + const autoPtr<mapDistribute> mapPtr + ( + distributeSegments ( - distributeSegments - ( - start, - end, - allSegments, - allSegmentMap - ) - ); - const mapDistribute& map = mapPtr(); + start, + end, + allSegments, + allSegmentMap + ) + ); + const mapDistribute& map = mapPtr(); - label nOldAllSegments = allSegments.size(); + label nOldAllSegments = allSegments.size(); - // Exchange the segments - // ~~~~~~~~~~~~~~~~~~~~~ + // Exchange the segments + // ~~~~~~~~~~~~~~~~~~~~~ - map.distribute(allSegments); + map.distribute(allSegments); - // Do tests i need to do - // ~~~~~~~~~~~~~~~~~~~~~ + // Do tests i need to do + // ~~~~~~~~~~~~~~~~~~~~~ - // Intersections - List<pointIndexHit> intersections(allSegments.size()); + // Intersections + List<pointIndexHit> intersections(allSegments.size()); - forAll(allSegments, i) + forAll(allSegments, i) + { + if (nearestIntersection) { - if (nearestIntersection) - { - intersections[i] = octree.findLine - ( - allSegments[i].first(), - allSegments[i].second() - ); - } - else - { - intersections[i] = octree.findLineAny - ( - allSegments[i].first(), - allSegments[i].second() - ); - } + 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()) - ); - } + // Convert triangle index to global numbering + if (intersections[i].hit()) + { + intersections[i].setIndex + ( + triIndexer.toGlobal(intersections[i].index()) + ); } + } - // Exchange the intersections (opposite to segments) - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + // Exchange the intersections (opposite to segments) + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - map.reverseDistribute(nOldAllSegments, intersections); + map.reverseDistribute(nOldAllSegments, intersections); - // Extract the hits - // ~~~~~~~~~~~~~~~~ + // Extract the hits + // ~~~~~~~~~~~~~~~~ - forAll(intersections, i) - { - const pointIndexHit& allInfo = intersections[i]; - label segmenti = allSegmentMap[i]; - pointIndexHit& hitInfo = info[segmenti]; + forAll(intersections, i) + { + const pointIndexHit& allInfo = intersections[i]; + label segmenti = allSegmentMap[i]; + pointIndexHit& hitInfo = info[segmenti]; - if (allInfo.hit()) + if (allInfo.hit()) + { + if (!hitInfo.hit()) { - if (!hitInfo.hit()) + // No intersection yet so take this one + hitInfo = allInfo; + } + else if (nearestIntersection) + { + // Nearest intersection + if + ( + start[segmenti].distSqr(allInfo.point()) + < start[segmenti].distSqr(hitInfo.point()) + ) { - // No intersection yet so take this one hitInfo = allInfo; } - else if (nearestIntersection) - { - // Nearest intersection - if - ( - start[segmenti].distSqr(allInfo.point()) - < start[segmenti].distSqr(hitInfo.point()) - ) - { - hitInfo = allInfo; - } - } } } } @@ -1154,7 +1278,7 @@ Foam::volumeType Foam::distributedTriSurfaceMesh::edgeSide const label face1 ) const { - const triSurface& surf = static_cast<const triSurface&>(*this); + const triSurface& surf = *this; const pointField& points = surf.points(); // Compare to bisector. This is actually correct since edge is @@ -1182,7 +1306,7 @@ Foam::label Foam::distributedTriSurfaceMesh::findOtherFace const label nearLabel ) const { - const triSurface& surf = static_cast<const triSurface&>(*this); + const triSurface& surf = *this; const triSurface::face_type& nearF = surf[nearFacei]; const edge e(nearF[nearLabel], nearF[nearF.fcIndex(nearLabel)]); @@ -1255,7 +1379,8 @@ void Foam::distributedTriSurfaceMesh::surfaceSide Pout<< "distributedTriSurfaceMesh::surfaceSide :" << " on surface " << searchableSurface::name() << " finding surface side given points on surface for " - << samples.size() << " samples" << endl; + << samples.size() << " samples" + << " cached vertexnormals " << vertexNormals_.valid() << endl; } // Use global index to send local tri and nearest back to originating @@ -1276,6 +1401,10 @@ void Foam::distributedTriSurfaceMesh::surfaceSide pointField localSamples(samples); map.distribute(localSamples); + // Send over nearest point since we have it anyway + List<pointIndexHit> localNearestInfo(nearestInfo); + map.distribute(localNearestInfo); + // Do my tests // ~~~~~~~~~~~ @@ -1283,8 +1412,43 @@ void Foam::distributedTriSurfaceMesh::surfaceSide volType.setSize(triangleIndex.size()); volType = volumeType::UNKNOWN; - const triSurface& surf = static_cast<const triSurface&>(*this); + const triSurface& surf = *this; const pointField& points = surf.points(); + + + if (vertexNormals_) + { + // Use smooth interpolation for the normal + + forAll(triangleIndex, i) + { + const label facei = triangleIndex[i]; + const triSurface::face_type& f = surf[facei]; + const auto& vn = vertexNormals_()[facei]; + + const point& sample = localSamples[i]; + + const point& nearest = localNearestInfo[i].point(); + + const vector sampleNearestVec(sample - nearest); + + const barycentric2D w(f.tri(points).pointToBarycentric(nearest)); + + const vector n = w[0]*vn[0]+w[1]*vn[1]+w[2]*vn[2]; + + const scalar c = (sampleNearestVec & n); + + if (c > 0) + { + volType[i] = volumeType::OUTSIDE; + } + else + { + volType[i] = volumeType::INSIDE; + } + } + } + else { //const labelListList& pointFaces = surf.pointFaces(); // Construct pointFaces. Let's hope surface has compact point @@ -1313,7 +1477,7 @@ void Foam::distributedTriSurfaceMesh::surfaceSide // Nearest to face interior. Use faceNormal to determine side //scalar c = sampleNearestVec & surf.faceNormals()[facei]; - scalar c = sampleNearestVec & surf[facei].areaNormal(points); + const scalar c = (sampleNearestVec & f.areaNormal(points)); if (c > 0) { @@ -1451,13 +1615,45 @@ void Foam::distributedTriSurfaceMesh::surfaceSide // send the volume-query to a single processor) - //forAll(localSamples, i) //{ - // Pout<< "surfaceSide : for localSample:" << localSamples[i] - // << " found volType:" << volumeType::names[volType[i]] - // << endl; + //- Alternative which also sends over source sample so we get better + //- debug messages. + // // Pack sample, nearest and volType so we get nicer error messages + // typedef Tuple2<Pair<point>, volumeType> NearType; + // List<NearType> nearTypes(volType.size()); + // forAll(localSamples, i) + // { + // const point& sample = localSamples[i]; + // const point& near = localNearestInfo[i].point(); + // nearTypes[i] = NearType(Pair<point>(sample, near), volType[i]); + // } + // + // + // const NearType zero(Pair<point>(Zero, Zero), volumeType::UNKNOWN); + // mapDistributeBase::distribute + // ( + // Pstream::commsTypes::nonBlocking, + // List<labelPair>::null(), + // nearestInfo.size(), + // map.constructMap(), + // map.constructHasFlip(), + // map.subMap(), + // map.subHasFlip(), + // nearTypes, + // zero, + // NearTypeCombineOp(), + // noOp(), // no flipping + // UPstream::msgType(), + // map.comm() + // ); + // volType.setSize(nearTypes.size()); + // forAll(nearTypes, i) + // { + // volType[i] = nearTypes[i].second(); + // } //} + const volumeType zero(volumeType::UNKNOWN); mapDistributeBase::distribute ( @@ -1476,44 +1672,10 @@ void Foam::distributedTriSurfaceMesh::surfaceSide map.comm() ); - //// Pack sample, nearest and volType so we get nicer error messages - ////typedef Tuple2<Pair<point>, volumeType>> NearType; - //List<NearType> nearTypes(volType.size()); - //forAll(localSamples, i) - //{ - // const point& sample = localSamples[i]; - // const point& near = nearest[i]; - // nearTypes[i] = NearType(Pair<point>(sample, near), volType[i]); - //} - // - // - //const NearType zero(Pair<point>(Zero, Zero), volumeType::UNKNOWN); - //mapDistributeBase::distribute - //( - // Pstream::commsTypes::nonBlocking, - // List<labelPair>::null(), - // nearestInfo.size(), - // map.constructMap(), - // map.constructHasFlip(), - // map.subMap(), - // map.subHasFlip(), - // nearTypes, - // zero, - // NearTypeCombineOp(), - // noOp(), // no flipping - // UPstream::msgType(), - // map.comm() - //); - //volType.setSize(nearTypes.size()); - //forAll(nearTypes, i) - //{ - // volType[i] = nearTypes[i].second(); - //} - if (debug) { Pout<< "distributedTriSurfaceMesh::surfaceSide :" - << " finished finding surface" << searchableSurface::name() + << " finished finding surface " << searchableSurface::name() << " given points on surface " << searchableSurface::name() << " for " << samples.size() << " samples" << endl; @@ -1521,6 +1683,196 @@ void Foam::distributedTriSurfaceMesh::surfaceSide } +Foam::volumeType Foam::distributedTriSurfaceMesh::markMixed +( + const indexedOctree<treeDataTriSurface>& tree, + const label nodei, + const triPointRef& tri, + PackedList<2>& nodeTypes +) const +{ + const auto& nod = tree.nodes()[nodei]; + //const auto& contents = tree.contents(); + + //Pout<< indent + // << "Entering node:" << nodei + // << " with bb:" << nod.bb_ << endl; + //Pout<< incrIndent; + + volumeType myType = volumeType::UNKNOWN; + + for (direction octant = 0; octant < nod.subNodes_.size(); octant++) + { + const labelBits index = nod.subNodes_[octant]; + const treeBoundBox subBb(nod.bb_.subBbox(octant)); + + if (subBb.intersects(tri)) + { + volumeType subType; + + if (indexedOctree<treeDataTriSurface>::isNode(index)) + { + //Pout<< indent + // << "Testing node octant:" << octant << " with node:" + // << indexedOctree<treeDataTriSurface>::getNode(index) + // << " subBb:" << subBb << endl; + + // tree node. Recurse. + subType = markMixed + ( + tree, + indexedOctree<treeDataTriSurface>::getNode(index), + tri, + nodeTypes + ); + } + else if (indexedOctree<treeDataTriSurface>::isContent(index)) + { + // Content inside so we cannot pre-determine whether + // whole node box is inside or outside + + //const label contenti = + // indexedOctree<treeDataTriSurface>::getContent(index); + //Pout<< indent + // << "Testing content octant:" << octant + // << " with contenti:" << contenti << " subBb:" << subBb + // << " holding shapes:" << flatOutput(contents[contenti]) + // << endl; + + // Store octant type + subType = volumeType::MIXED; + } + else + { + // Nothing in it but intersects triangle. Can't cache + + //Pout<< indent + // << "Testing empty octant:" << octant + // << " subBb:" << subBb << endl; + + // Store octant type + subType = volumeType::MIXED; + } + + // Store octant type + nodeTypes.set(labelBits::pack(nodei, octant), subType); + + // Combine sub node types into type for treeNode. Result is + // 'mixed' if types differ among subnodes. + if (myType == volumeType::UNKNOWN) + { + myType = subType; + } + else if (subType != myType) + { + //Pout<< indent + // << "for octant:" << octant + // << " siwtched from myType:" << myType + // << " and subType:" << subType + // << " switched to MIXED" << endl; + myType = volumeType::MIXED; + } + } + } + + //Pout<< indent + // << "Result for node:" << nodei << " myType:" << myType << endl; + //Pout<< decrIndent; + + return myType; +} + + +void Foam::distributedTriSurfaceMesh::markMixedOverlap +( + const indexedOctree<treeDataTriSurface>& tree, + PackedList<2>& nodeTypes +) const +{ + // At this point each triangle is on a unique processor so we can just + // go and + // check what additional triangles would be sent over based on the + // bounding boxes. Note that the surface at this point is already + // uniquely distributed (every triangle is on a single processor + // only) and the tree is only for those. + + PstreamBuffers pBufs; + + // Test & send + forAll(procBb_, proci) + { + if (proci != UPstream::myProcNo()) + { + labelList pointMap; + labelList faceMap; + overlappingSurface + ( + *this, + procBb_[proci], + pointMap, // not used + faceMap + ); + if (!faceMap.empty()) + { + const triSurface subSurface + ( + subsetMesh + ( + *this, + faceMap, + pointMap + ) + ); + UOPstream os(proci, pBufs); + os << subSurface; + } + } + } + + pBufs.finishedSends(); + + // Receive + for (const int proci : pBufs.allProcs()) + { + if (proci != Pstream::myProcNo() && pBufs.recvDataCount(proci)) + { + UIPstream is(proci, pBufs); + + // Receive + const triSurface subSurface(is); + + //Pout<< "** from proc:" << proci + // << " received:" << subSurface.size() + // << " compared to local:" << this->size() << endl; + //const fileName fName + //( + // searchableSurface::time().path() + // /searchableSurface::name() + // + "_from_proc" + Foam::name(proci) + ".obj" + //); + //Pout<< "fName:" << fName << endl; + //subSurface.write(fName); + + + // Set all tree nodes overlapping buffer triangles to + // mixed. This state will be preserved when caching the + // volume type (see below). This uses the actual local + // triangles. + for (const auto& tri : subSurface) + { + markMixed + ( + tree, + 0, //nodei, + tri.tri(subSurface.points()), + nodeTypes + ); + } + } + } +} + + void Foam::distributedTriSurfaceMesh::collectLeafMids ( const label nodeI, @@ -1616,10 +1968,100 @@ Foam::volumeType Foam::distributedTriSurfaceMesh::calcVolumeType } else if (subType != myType) { - myType = volumeType::MIXED; + myType = volumeType::MIXED; + } + } + return myType; +} + + +void Foam::distributedTriSurfaceMesh::cacheVolumeType(PackedList<2>& nt) const +{ + // Get local tree + const indexedOctree<treeDataTriSurface>& t = tree(); + const auto& nodes = t.nodes(); + + // Collect midpoints + DynamicField<point> midPoints(label(0.5*nodes.size())); + if (nodes.size()) + { + collectLeafMids(0, midPoints); + } + + if (debug) + { + Pout<< "distributedTriSurfaceMesh::cacheVolumeType :" + << " surface " << searchableSurface::name() + << " triggering orientation caching for " + << midPoints.size() << " leaf mids" << endl; + } + + // Get volume type of mid points + List<volumeType> midVolTypes; + + // Note: could recurse here (since now outsideVolType_ is set) + // but this would use the cached mid point data which we're trying + // to calculate. Instead bypass caching and do a full search + { + List<pointIndexHit> nearestInfo; + findNearest + ( + midPoints, + scalarField(midPoints.size(), Foam::sqr(GREAT)), + nearestInfo + ); + surfaceSide(midPoints, nearestInfo, midVolTypes); + + if (debug & 2) + { + OBJstream insideStr + ( + searchableSurface::time().path() + /searchableSurface::name() + "_cacheVolumeType_inside.obj" + ); + OBJstream outsideStr + ( + searchableSurface::time().path() + /searchableSurface::name() + "_cacheVolumeType_outside.obj" + ); + forAll(midVolTypes, i) + { + const linePointRef ln(midPoints[i], nearestInfo[i].hitPoint()); + if (midVolTypes[i] == volumeType::INSIDE) + { + insideStr.write(ln); + } + else if (midVolTypes[i] == volumeType::OUTSIDE) + { + outsideStr.write(ln); + } + } + Pout<< "Whilst caching " << searchableSurface::name() + << " have inside:" << insideStr.nVertices() + << " have outside:" << outsideStr.nVertices() + << endl; } } - return myType; + + // Cache on local tree + if (nodes.size()) + { + label index = 0; + calcVolumeType + ( + midVolTypes, + index, + nt, + 0 // nodeI + ); + } + if (debug) + { + Pout<< "distributedTriSurfaceMesh::cacheVolumeType :" + << " surface " << searchableSurface::name() + << " done orientation caching for " + << midPoints.size() << " leaf mids" << endl; + } } @@ -1698,72 +2140,106 @@ Foam::volumeType Foam::distributedTriSurfaceMesh::cachedVolumeType } -// 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. -Foam::List<Foam::List<Foam::treeBoundBox>> -Foam::distributedTriSurfaceMesh::independentlyDistributedBbs -( - const triSurface& s -) +const Foam::decompositionMethod& +Foam::distributedTriSurfaceMesh::decomposer() const { - addProfiling - ( - distribute, - "distributedTriSurfaceMesh::independentlyDistributedBbs" - ); - if (!decomposer_) { - // Use singleton decomposeParDict. Cannot use decompositionModel - // here since we've only got Time and not a mesh. - - const auto* dictPtr = - searchableSurface::time().findObject<IOdictionary> - ( - // == decompositionModel::canonicalName - "decomposeParDict" - ); - - if (dictPtr) + // Check if local dictionary contains decomposition entries + if (dict_.found("method")) { - decomposer_ = decompositionMethod::New(*dictPtr); + if (debug) + { + Pout<< "distributedTriSurfaceMesh::decomposer() :" + << " constructing decomposer from provided dictionary " + << dict_ << endl; + } + + decomposer_ = decompositionMethod::New(dict_); } else { - if (!decomposeParDict_) - { - decomposeParDict_.reset + // Use singleton decomposeParDict. Cannot use decompositionModel + // here since we've only got Time and not a mesh. + + const auto* dictPtr = + searchableSurface::time().findObject<IOdictionary> ( - new IOdictionary + // == decompositionModel::canonicalName + "decomposeParDict" + ); + + if (dictPtr) + { + if (debug) + { + Pout<< "distributedTriSurfaceMesh::decomposer() :" + << " constructing decomposer from registered" + << " dictionary " << *dictPtr << endl; + } + + decomposer_ = decompositionMethod::New(*dictPtr); + } + else + { + if (!decomposeParDict_) + { + decomposeParDict_.reset ( - IOobject + new IOdictionary ( - // == decompositionModel::canonicalName - "decomposeParDict", - searchableSurface::time().system(), - searchableSurface::time(), - IOobject::MUST_READ, - IOobject::NO_WRITE + IOobject + ( + // == decompositionModel::canonicalName + "decomposeParDict", + searchableSurface::time().system(), + searchableSurface::time(), + IOobject::MUST_READ, + IOobject::NO_WRITE + ) ) - ) - ); + ); + + if (debug) + { + Pout<< "distributedTriSurfaceMesh::decomposer() :" + << " reading " + << decomposeParDict_().objectRelPath() + << endl; + } + } + decomposer_ = decompositionMethod::New(*decomposeParDict_); } - decomposer_ = decompositionMethod::New(*decomposeParDict_); } } + return decomposer_(); +} + - // Initialise to inverted box - List<List<treeBoundBox>> bbs +// 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. +void Foam::distributedTriSurfaceMesh::independentlyDistributedBbs +( + const triSurface& s, + labelList& distribution, // per local triangle the destination processor + List<List<treeBoundBox>>& bbs +) const +{ + addProfiling ( - Pstream::nProcs(), - List<treeBoundBox>(1, treeBoundBox::null()) + distribute, + "distributedTriSurfaceMesh::independentlyDistributedBbs" ); + // Initialise to inverted box + bbs.resize_nocopy(Pstream::nProcs()); + bbs = List<treeBoundBox>(1, treeBoundBox::null()); + const globalIndex& triIndexer = globalTris(); bool masterOnly; @@ -1795,8 +2271,7 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs triCentres[trii] = s[trii].centre(s.points()); } - labelList distribution; - if (!isA<geomDecomp>(decomposer_())) + if (!isA<geomDecomp>(decomposer())) { // Use connectivity labelListList pointFaces; @@ -1806,19 +2281,19 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs // Do the actual decomposition const bool oldParRun = UPstream::parRun(false); - distribution = decomposer_().decompose(faceFaces, triCentres); + distribution = decomposer().decompose(faceFaces, triCentres); UPstream::parRun(oldParRun); // Restore parallel state } else { // Do the actual decomposition - distribution = decomposer_().decompose(triCentres); + distribution = decomposer().decompose(triCentres); } if (debug) { Pout<< "distributedTriSurfaceMesh::independentlyDistributedBbs :" - << " determining processor bounding boxes for surface" + << " determining processor bounding boxes for surface " << searchableSurface::name() << endl; } @@ -1845,10 +2320,10 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs { // Fully distributed decomposition. Does not filter duplicate // triangles. - if (!decomposer_().parallelAware()) + if (!decomposer().parallelAware()) { FatalErrorInFunction - << "The decomposition method " << decomposer_().typeName + << "The decomposition method " << decomposer().typeName << " does not decompose in parallel." << " Please choose one that does." << exit(FatalError); } @@ -1868,7 +2343,7 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs triCentres[trii] = s[trii].centre(s.points()); } - labelList distribution = decomposer_().decompose(triCentres); + distribution = decomposer().decompose(triCentres); if (debug) { @@ -1901,10 +2376,10 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs // { // // Fully distributed decomposition. Does not filter duplicate // // triangles. -// if (!decomposer_().parallelAware()) +// if (!decomposer().parallelAware()) // { // FatalErrorInFunction -// << "The decomposition method " << decomposer_().typeName +// << "The decomposition method " << decomposer().typeName // << " does not decompose in parallel." // << " Please choose one that does." << exit(FatalError); // } @@ -2060,7 +2535,7 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs // pointField masterCentres(allCentres, triMap); // // // Do the actual decomposition -// labelList masterDistribution(decomposer_().decompose(masterCentres)); +// labelList masterDistribution(decomposer().decompose(masterCentres)); // // // Make map to get the decomposition from the master of each border // labelList borderGlobalMaster(borderTris.size()); @@ -2120,7 +2595,6 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs } // Determine local decomposition - labelList distribution(s.size()); { labelList allDistribution; if (Pstream::master()) @@ -2149,7 +2623,7 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs // Decompose merged centres const bool oldParRun = UPstream::parRun(false); - labelList mergedDist(decomposer_().decompose(mergedPoints)); + labelList mergedDist(decomposer().decompose(mergedPoints)); UPstream::parRun(oldParRun); // Restore parallel state // Convert to all @@ -2202,7 +2676,6 @@ Foam::distributedTriSurfaceMesh::independentlyDistributedBbs } Pstream::broadcast(bbs); } - return bbs; } @@ -2576,8 +3049,10 @@ Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh ), currentDistType_(FROZEN) // only used to trigger re-distribution { - // Read from the provided dictionary - read(); + // Construct from components. If empty dictionary get all the info from the + // supplied (undecomposed) triSurface. + + readSettings(dict_.empty()); bounds().reduce(); @@ -2639,21 +3114,34 @@ Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh(const IOobject& io) ), currentDistType_(FROZEN) // only used to trigger re-distribution { - // Read from the local, decomposed dictionary - read(); - - bounds().reduce(); - // Try and find out where the triSurface was loaded from. On slave this // might give empty filename. const fileName actualFile(triSurfaceMesh::findFile(io, true)); + // Was it read undecomposed? + const bool readFromMaster = + ( + actualFile.empty() + || actualFile != io.localFilePath(triSurfaceMesh::typeName) + ); + + readSettings(readFromMaster); + + bounds().reduce(); + + if (readFromMaster && !decomposeUsingBbs_) + { + // No fill-in so store normals + DebugInFunction + << "Determining vertex based normals since undecomposed" << endl; + const triSurface& surf = *this; + vertexNormals_ = autoPtr<List<FixedList<vector, 3>>>::New(); + calcVertexNormals(surf, vertexNormals_()); + } + if ( - ( - actualFile.empty() - || actualFile != io.localFilePath(triSurfaceMesh::typeName) - ) + readFromMaster && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED) ) { @@ -2748,50 +3236,37 @@ Foam::distributedTriSurfaceMesh::distributedTriSurfaceMesh ), currentDistType_(FROZEN) // only used to trigger re-distribution { - // Read from the local, decomposed dictionary - read(); - - // Optionally override settings from provided dictionary - { - // Wanted distribution type - distributionTypeNames_.readIfPresent - ( - "distributionType", - dict, - distType_ - ); - - // Merge distance - dict.readIfPresent("mergeDistance", mergeDist_); + // Try and find out where the triSurface was loaded from. On slave this + // might give empty filename. + const fileName actualFile(triSurfaceMesh::findFile(io, dict, true)); - // Distribution type - bool closed; - if (dict.readIfPresent<bool>("closed", closed)) - { - surfaceClosed_ = closed; - } + // Was it read undecomposed? + const bool readFromMaster = + ( + actualFile.empty() + || actualFile != io.localFilePath(triSurfaceMesh::typeName) + ); - outsideVolType_ = volumeType::names.getOrDefault - ( - "outsideVolumeType", - dict, - outsideVolType_ - ); - } + // Add supplied dictionary entries (override existing) + dict_ <<= dict; + readSettings(readFromMaster); bounds().reduce(); - // Try and find out where the triSurface was loaded from. On slave this - // might give empty filename. - const fileName actualFile(triSurfaceMesh::findFile(io, dict, true)); + if (readFromMaster && !decomposeUsingBbs_) + { + // No fill-in so store enough normals to calculate + DebugInFunction + << "Determining vertex based normals since undecomposed" << endl; + const triSurface& surf = *this; + vertexNormals_ = autoPtr<List<FixedList<vector, 3>>>::New(); + calcVertexNormals(surf, vertexNormals_()); + } if ( - ( - actualFile.empty() - || actualFile != io.localFilePath(triSurfaceMesh::typeName) - ) + readFromMaster && (distType_ == INDEPENDENT || distType_ == DISTRIBUTED) ) { @@ -2878,6 +3353,35 @@ const Foam::globalIndex& Foam::distributedTriSurfaceMesh::globalTris() const } +void Foam::distributedTriSurfaceMesh::flip() +{ + if (debug) + { + Pout<< "distributedTriSurfaceMesh::flip :" + << " surface " << searchableSurface::name() + << " with current orientation " + << volumeType::names[outsideVolType_] + << " vertexNormals_ " << vertexNormals_.good() + << endl; + } + + triSurfaceMesh::flip(); + + if (vertexNormals_) + { + List<FixedList<vector, 3>>& vn = vertexNormals_(); + + for (auto& normals : vn) + { + for (auto& n : normals) + { + n = -n; + } + } + } +} + + //void Foam::distributedTriSurfaceMesh::findNearest //( // const pointField& samples, @@ -3902,7 +4406,7 @@ void Foam::distributedTriSurfaceMesh::getRegion // Do my tests // ~~~~~~~~~~~ - const triSurface& s = static_cast<const triSurface&>(*this); + const triSurface& s = *this; region.setSize(triangleIndex.size()); @@ -3969,14 +4473,36 @@ void Foam::distributedTriSurfaceMesh::getNormal // Do my tests // ~~~~~~~~~~~ - const triSurface& s = static_cast<const triSurface&>(*this); + const triSurface& s = *this; normal.setSize(triangleIndex.size()); - forAll(triangleIndex, i) + if (vertexNormals_) { - label trii = triangleIndex[i]; - normal[i] = s[trii].unitNormal(s.points()); + // Use smooth interpolation for the normal + + // Send over nearest point since we have it anyway + List<pointIndexHit> localNearestInfo(info); + map.distribute(localNearestInfo); + + forAll(triangleIndex, i) + { + const point& nearest = localNearestInfo[i].point(); + + const label trii = triangleIndex[i]; + const triPointRef tri(s[trii].tri(s.points())); + const auto& vn = vertexNormals_()[trii]; + const barycentric2D w(tri.pointToBarycentric(nearest)); + normal[i] = w[0]*vn[0]+w[1]*vn[1]+w[2]*vn[2]; + } + } + else + { + forAll(triangleIndex, i) + { + const label trii = triangleIndex[i]; + normal[i] = s[trii].unitNormal(s.points()); + } } @@ -4138,64 +4664,18 @@ void Foam::distributedTriSurfaceMesh::getVolumeType || outsideVolType_ == volumeType::OUTSIDE ) { - // Get local tree const indexedOctree<treeDataTriSurface>& t = tree(); const auto& nodes = t.nodes(); PackedList<2>& nt = t.nodeTypes(); nt.resize(nodes.size()); nt = volumeType::UNKNOWN; - // Collect midpoints - DynamicField<point> midPoints(label(0.5*nodes.size())); - if (nodes.size()) - { - collectLeafMids(0, midPoints); - } - - if (debug) - { - Pout<< "distributedTriSurfaceMesh::getVolumeType :" - << " surface " << searchableSurface::name() - << " triggering orientation caching for " - << midPoints.size() << " leaf mids" << endl; - } - - // Get volume type of mid points - List<volumeType> midVolTypes; - - // Note: could recurse here (since now outsideVolType_ is set) - // but this would use the cached mid point data which we're trying - // to calculate. Instead bypass caching and do a full search + if (!decomposeUsingBbs_) { - List<pointIndexHit> nearestInfo; - findNearest - ( - midPoints, - scalarField(midPoints.size(), Foam::sqr(GREAT)), - nearestInfo - ); - surfaceSide(midPoints, nearestInfo, midVolTypes); + markMixedOverlap(t, nt); } - // Cache on local tree - if (nodes.size()) - { - label index = 0; - calcVolumeType - ( - midVolTypes, - index, - nt, - 0 // nodeI - ); - } - if (debug) - { - Pout<< "distributedTriSurfaceMesh::getVolumeType :" - << " surface " << searchableSurface::name() - << " done orientation caching for " - << midPoints.size() << " leaf mids" << endl; - } + cacheVolumeType(nt); } } @@ -4282,9 +4762,18 @@ void Foam::distributedTriSurfaceMesh::getVolumeType // those I need to search the nearest for DynamicField<point> fullSearchPoints(localPoints.size()); DynamicList<label> fullSearchMap(localPoints.size()); + forAll(localPoints, i) { - if (tree().nodes().size()) + // If no-fill in: + // - tree nodes on the outside will not include fill-in triangles + // - so might decide the wrong state (might be 'mixed' with fill-in + // triangles) + // - for now do not cache + // - TBD. Note that the cached volume types already include remote + // fill-in triangles. There is still a problem there though. TBD. + + if (decomposeUsingBbs_ && tree().nodes().size()) { volType[i] = cachedVolumeType(0, localPoints[i]); } @@ -4322,9 +4811,68 @@ void Foam::distributedTriSurfaceMesh::getVolumeType // Combine forAll(fullSearchMap, i) { - volType[fullSearchMap[i]] = fullSearchType[i]; + volumeType& vt = volType[fullSearchMap[i]]; + if (vt == volumeType::UNKNOWN) + { + vt = fullSearchType[i]; + } + else if (vt != fullSearchType[i]) + { + Pout<< "At point:" << fullSearchPoints[i] + << " or point:" << localPoints[fullSearchMap[i]] + << " have cached status:" << vt + << " have full-search status:" << fullSearchType[i] + << endl; + } + //volType[fullSearchMap[i]] = fullSearchType[i]; } + //{ + // //- Variant of below that produces better error messages + // typedef Tuple2<Pair<point>, volumeType> NearType; + // const NearType zero(Pair<point>(Zero, Zero), volumeType::UNKNOWN); + // + // // Combine nearest and volType so we can give nicer error messages + // List<NearType> nearTypes(volType.size(), zero); + // + // NearTypeCombineOp cop; + // + // // For all volType we do have the originating point but not the + // // nearest + // forAll(localPoints, i) + // { + // nearTypes[i] = + // NearType(Pair<point>(localPoints[i], Zero), volType[i]); + // } + // // But for all full-search we also have the nearest + // forAll(fullSearchMap, i) + // { + // const NearType nt + // ( + // Pair<point>(fullSearchPoints[i], nearestInfo[i].hitPoint()), + // fullSearchType[i] + // ); + // + // cop(nearTypes[fullSearchMap[i]], nt); + // } + // mapDistributeBase::distribute + // ( + // Pstream::commsTypes::nonBlocking, + // List<labelPair>::null(), + // samples.size(), + // map.constructMap(), + // map.constructHasFlip(), + // map.subMap(), + // map.subHasFlip(), + // nearTypes, + // zero, + // cop, //NearTypeCombineOp(), + // noOp(), // no flipping + // UPstream::msgType(), + // map.comm() + // ); + //} + // Send back to originator. In case of multiple answers choose inside or // outside const volumeType zero(volumeType::UNKNOWN); @@ -4345,47 +4893,6 @@ void Foam::distributedTriSurfaceMesh::getVolumeType map.comm() ); - - //// Combine nearest and volType so we can give nicer error messages - //List<NearType> nearTypes(volType.size()); - //// For all volType we do have the originating point but not the nearest - //forAll(localPoints, i) - //{ - // nearTypes[i] = - // NearType(Pair<point>(localPoints[i], Zero), volType[i]); - //} - //// But for all full-search we also have the nearest - //forAll(fullSearchMap, i) - //{ - // nearTypes[fullSearchMap[i]] = NearType - // ( - // Pair<point>(fullSearchPoints[i], nearestInfo[i].hitPoint()), - // fullSearchType[i] - // ); - //} - //const NearType zero(Pair<point>(Zero, Zero), volumeType::UNKNOWN); - //mapDistributeBase::distribute - //( - // Pstream::commsTypes::nonBlocking, - // List<labelPair>::null(), - // samples.size(), - // map.constructMap(), - // map.constructHasFlip(), - // map.subMap(), - // map.subHasFlip(), - // nearTypes, - // zero, - // NearTypeCombineOp(), - // noOp(), // no flipping - // UPstream::msgType(), - // map.comm() - //); - //volType.setSize(nearTypes.size()); - //forAll(nearTypes, i) - //{ - // volType[i] = nearTypes[i].second(); - //} - // Add the points outside the bounding box for (label samplei : outsideSamples) { @@ -4613,6 +5120,7 @@ void Foam::distributedTriSurfaceMesh::distribute << " surface " << searchableSurface::name() << " distributing surface according to method:" << distributionTypeNames_[distType_] + << " fill-in:" << decomposeUsingBbs_ << " follow bbs:" << flatOutput(bbs) << endl; } @@ -4622,7 +5130,10 @@ void Foam::distributedTriSurfaceMesh::distribute // Get bbs of all domains // ~~~~~~~~~~~~~~~~~~~~~~ + // Per triangle the destination processor + labelList distribution; { + // Per processor the bounding box of all (destination) triangles List<List<treeBoundBox>> newProcBb(Pstream::nProcs()); switch (distType_) @@ -4638,7 +5149,7 @@ void Foam::distributedTriSurfaceMesh::distribute { return; } - newProcBb = independentlyDistributedBbs(*this); + independentlyDistributedBbs(*this, distribution, newProcBb); break; case FROZEN: @@ -4691,15 +5202,37 @@ void Foam::distributedTriSurfaceMesh::distribute labelListList faceSendMap(Pstream::nProcs()); labelListList pointSendMap(Pstream::nProcs()); - forAll(procBb_, proci) + if (decomposeUsingBbs_) { - overlappingSurface - ( - *this, - procBb_[proci], - pointSendMap[proci], - faceSendMap[proci] - ); + forAll(procBb_, proci) + { + overlappingSurface + ( + *this, + procBb_[proci], + pointSendMap[proci], + faceSendMap[proci] + ); + } + } + else + { + const triSurface& s = *this; + forAll(procBb_, proci) + { + boolList includedFace(s.size(), false); + forAll(s, trii) + { + includedFace[trii] = (distribution[trii] == proci); + } + subsetMesh + ( + s, + includedFace, + pointSendMap[proci], + faceSendMap[proci] + ); + } } if (keepNonLocal) @@ -4707,7 +5240,7 @@ void Foam::distributedTriSurfaceMesh::distribute // Include in faceSendMap/pointSendMap the triangles that are // not mapped to any processor so they stay local. - const triSurface& s = static_cast<const triSurface&>(*this); + const triSurface& s = *this; boolList includedFace(s.size(), true); @@ -4819,7 +5352,7 @@ void Foam::distributedTriSurfaceMesh::distribute UIPstream is(proci, pBufs); // Receive - triSurface subSurface(is); + const triSurface subSurface(is); // Merge into allSurf merge @@ -4876,6 +5409,63 @@ void Foam::distributedTriSurfaceMesh::distribute distributeFields<sphericalTensor>(faceMap()); distributeFields<symmTensor>(faceMap()); distributeFields<tensor>(faceMap()); + if (vertexNormals_) + { + List<FixedList<vector, 3>>& vn = vertexNormals_(); + faceMap().distribute(vn); + + if (debug & 2) + { + OBJstream str + ( + searchableSurface::time().path() + /searchableSurface::name() + "_vertex_normals.obj" + ); + const triSurface& s = *this; + forAll(s, trii) + { + const auto& f = s[trii]; + const scalar l = Foam::sqrt(f.mag(s.points())); + forAll(f, fp) + { + const point& pt = s.points()[f[fp]]; + const vector& n = vn[trii][fp]; + str.write(linePointRef(pt, pt+l*n)); + } + } + Pout<< "Dumped local vertex normals to " << str.name() << endl; + } + } + + + if + ( + outsideVolType_ == volumeType::INSIDE + || outsideVolType_ == volumeType::OUTSIDE + ) + { + // Re-build tree & inside/outside for closed surfaces + + const indexedOctree<treeDataTriSurface>& t = tree(); + const auto& nodes = t.nodes(); + PackedList<2>& nt = t.nodeTypes(); + nt.resize(nodes.size()); + nt = volumeType::UNKNOWN; + + // Send over any overlapping but not included triangles + if (!decomposeUsingBbs_) + { + // We do not backfill the bounding box so the local tree might + // not have all the triangles to cache inside/outside correctly. + // Do a pre-pass to mark 'mixed' the tree leaves that intersect + // the triangles of the (would be) fill-in triangles. + markMixedOverlap(t, nt); + } + + cacheVolumeType(nt); + } + + if (debug) { @@ -4995,16 +5585,17 @@ void Foam::distributedTriSurfaceMesh::writeStats(Ostream& os) const { boundBox bb; label nPoints; - PatchTools::calcBounds(static_cast<const triSurface&>(*this), bb, nPoints); + PatchTools::calcBounds(*this, bb, nPoints); bb.reduce(); - os << "Triangles : " << returnReduce(triSurface::size(), sumOp<label>()) - << endl - << "Vertices : " << returnReduce(nPoints, sumOp<label>()) << endl - << "Bounding Box : " << bb << endl - << "Closed : " << surfaceClosed_ << endl - << "Outside point: " << volumeType::names[outsideVolType_] << endl - << "Distribution : " << distributionTypeNames_[distType_] << endl; + os << "Triangles : " + << returnReduce(triSurface::size(), sumOp<label>()) << endl + << "Vertices : " << returnReduce(nPoints, sumOp<label>()) << endl + << "Vertex normals : " << vertexNormals_.valid() << endl + << "Bounding Box : " << bb << endl + << "Closed : " << surfaceClosed_ << endl + << "Outside type : " << volumeType::names[outsideVolType_] << endl + << "Distribution : " << distributionTypeNames_[distType_] << endl; } diff --git a/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.H b/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.H index 05adb82d5c8d08746b7a500b339c410b6cfa4af4..5c4498994b535a82275a2d6dfb16d35242db3f41 100644 --- a/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.H +++ b/src/parallel/distributed/distributedTriSurfaceMesh/distributedTriSurfaceMesh.H @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2017 OpenFOAM Foundation - Copyright (C) 2015-2022 OpenCFD Ltd. + Copyright (C) 2015-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -109,20 +109,28 @@ private: //- Merging distance scalar mergeDist_; - autoPtr<IOdictionary> decomposeParDict_; + mutable autoPtr<IOdictionary> decomposeParDict_; //- Decomposition used when independently decomposing surface. - autoPtr<decompositionMethod> decomposer_; + mutable autoPtr<decompositionMethod> decomposer_; //- Bounding box settings localIOdictionary dict_; + //- Use bounding boxes (default) or unique decomposition of triangles + //- (i.e. do not duplicate triangles) + bool decomposeUsingBbs_; + //- Bounding boxes of all processors List<List<treeBoundBox>> procBb_; //- Global triangle numbering mutable autoPtr<globalIndex> globalTris_; + //- Optional per-vertex normals. TBD: move to triSurface? or have + //- per-triangle 3 normals so we can interpolate and have features + mutable autoPtr<List<FixedList<vector, 3>>> vertexNormals_; + //- The (wanted) distribution type. distributionType distType_; @@ -139,8 +147,17 @@ private: // directory or in parent directory static word findLocalInstance(const IOobject& io); - //- Read my additional data - bool read(); + //- Read my additional data from dictionary. Additional flag to + //- say whether we can use master-only geometric tests. + bool readSettings(const bool isUndecomposed); + + //- Construction helper: generate vertex normals upon reading + //- undecomposed surface + void calcVertexNormals + ( + const triSurface& surf, + List<FixedList<vector, 3>>& vn + ) const; // Line intersection @@ -287,6 +304,23 @@ private: // Caching of volume type (based on indexedOctree) + //- Set node type on any node containing the triangle + volumeType markMixed + ( + const indexedOctree<treeDataTriSurface>& tree, + const label nodei, + const triPointRef& tri, + PackedList<2>& nodeTypes + ) const; + + //- Set node type on any node overlapping any remote triangles. + //- Only valid if using unique decomposition. + void markMixedOverlap + ( + const indexedOctree<treeDataTriSurface>& tree, + PackedList<2>& nodeTypes + ) const; + //- Collect mid points of tree boxes void collectLeafMids ( @@ -303,6 +337,9 @@ private: const label nodeI ) const; + //- Calculate inside/outside of midpoint of tree nodes + void cacheVolumeType(PackedList<2>& nt) const; + //- Look up any cached data. Return unknown if cannot be determined. volumeType cachedVolumeType ( @@ -321,11 +358,16 @@ private: labelListList& faceFaces ); + //- Helper: get decompositionMethod + const decompositionMethod& decomposer() const; + //- Finds new bounds based on an independent decomposition. - List<List<treeBoundBox>> independentlyDistributedBbs + void independentlyDistributedBbs ( - const triSurface& - ); + const triSurface& s, + labelList& distribution, + List<List<treeBoundBox>>& bbs + ) const; //- Does any part of triangle overlap bb. static bool overlaps @@ -413,7 +455,8 @@ public: // Constructors - //- Construct from triSurface + //- Construct from components. Assumes triSurface is already decomposed + //- and dictionary contains corresponding information distributedTriSurfaceMesh ( const IOobject&, @@ -421,11 +464,17 @@ public: const dictionary& dict ); - //- Construct read. Does findInstance to find io.local(). + //- Construct read. Does findInstance to find io.local() + //- - if found local : assume distributed + //- - if found in parent : assume undistributed. Can e.g. check for + //- closedness. distributedTriSurfaceMesh(const IOobject& io); //- Construct from dictionary (used by searchableSurface). // Does read. Does findInstance to find io.local(). + //- - if found local : assume distributed + //- - if found in parent : assume undistributed. Can e.g. check for + //- closedness. distributedTriSurfaceMesh ( const IOobject& io, @@ -454,6 +503,9 @@ public: return globalTris().totalSize(); } + //- Flip triangles, outsideVolumeType and all cached inside/outside. + virtual void flip(); + virtual void findNearest ( const pointField& sample, diff --git a/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.C b/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.C index 73fe2e15fe4d2750c286255c0922fe4d31ff62eb..2fce36be064a19bbf7782dcab39539732edacee0 100644 --- a/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.C +++ b/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.C @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2018-2022 OpenCFD Ltd. + Copyright (C) 2018-2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -61,10 +61,9 @@ Foam::patchDistMethods::exact::patchSurface() const localBb.extend(rndGen, 1E-3) ); - // Dummy bounds dictionary - dictionary dict; - dict.add("bounds", meshBb); - dict.add + // Add any missing properties (but not override existing ones) + dict_.add("bounds", meshBb); + dict_.add ( "distributionType", distributedTriSurfaceMesh::distributionTypeNames_ @@ -74,8 +73,7 @@ Foam::patchDistMethods::exact::patchSurface() const distributedTriSurfaceMesh::DISTRIBUTED // parallel decomp ] ); - dict.add("mergeDistance", 1e-6*localBb.mag()); - + dict_.add("mergeDistance", 1e-6*localBb.mag()); Info<< "Triangulating local patch faces" << nl << endl; @@ -100,7 +98,7 @@ Foam::patchDistMethods::exact::patchSurface() const patchIDs_, mapTriToGlobal ), - dict + dict_ ) ); @@ -131,7 +129,8 @@ Foam::patchDistMethods::exact::exact const labelHashSet& patchIDs ) : - patchDistMethod(mesh, patchIDs) + patchDistMethod(mesh, patchIDs), + dict_(dict.subOrEmptyDict(typeName + "Coeffs")) {} diff --git a/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.H b/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.H index 4fd1f82fb1ac3a1fc3f18a3f56a4565b411a3cfb..3ac84561ce1cbeb27d14b4f14f234bbe5db098a2 100644 --- a/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.H +++ b/src/parallel/distributed/patchDistMethods/exact/exactPatchDistMethod.H @@ -5,7 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2018-2019 OpenCFD Ltd. + Copyright (C) 2018-2019,2024 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -30,9 +30,32 @@ Description Calculation of exact distance to nearest patch for all cells and boundary by constructing a search tree for all patch faces. +Usage + + \begin{verbatim} + wallDist + { + method exactDistance; + + // Optional entries (currently for distributedTriSurfaceMesh) + exactDistanceCoeffs + { + // Optional decomposition method. If not supplied will use the + // system/decomposeParDict. + method hierarchical; + numberOfSubdomains 8; + n (2 2 2); + + // (not)add fill-in triangles + decomposeUsingBbs false; + } + } + \end{verbatim} + See also Foam::patchDistMethod::meshWave Foam::wallDist + Foam::distributedTriSurfaceMesh SourceFiles exactPatchDistMethod.C @@ -55,7 +78,7 @@ namespace patchDistMethods { /*---------------------------------------------------------------------------*\ - Class exact Declaration + Class exact Declaration \*---------------------------------------------------------------------------*/ class exact @@ -64,6 +87,9 @@ class exact { // Private Member Data + //- Dictionary contents from the dictionary constructor + mutable dictionary dict_; + //- Cache surface+searching of patch mutable autoPtr<distributedTriSurfaceMesh> patchSurfPtr_; diff --git a/tutorials/mesh/snappyHexMesh/distributedTriSurfaceMesh/system/snappyHexMeshDict b/tutorials/mesh/snappyHexMesh/distributedTriSurfaceMesh/system/snappyHexMeshDict index 9fb6072860498dd34dfa7daea8d5eeedab832bfe..bb4f915e9e8c3ffe5e49756ed7736c20786dcfb4 100644 --- a/tutorials/mesh/snappyHexMesh/distributedTriSurfaceMesh/system/snappyHexMeshDict +++ b/tutorials/mesh/snappyHexMesh/distributedTriSurfaceMesh/system/snappyHexMeshDict @@ -20,6 +20,18 @@ snap true; addLayers false; +// Define common distributedTriSurface properties +_decomp +{ + numberOfSubdomains 8; + method hierarchical; + n (2 2 2); + + // unique triangle decomposition; no fill-in + decomposeUsingBbs false; +} + + // Geometry. Definition of all surfaces. All surfaces are of class // searchableSurface. // Surfaces are used @@ -32,16 +44,19 @@ geometry { type distributedTriSurfaceMesh; file "box.obj"; //"box_12_2.obj"; + ${_decomp} } box_trans { - file "box_trans.obj"; type distributedTriSurfaceMesh; + file "box_trans.obj"; + ${_decomp} } shell { - file "box_scaled.obj"; type distributedTriSurfaceMesh; + file "box_scaled.obj"; + ${_decomp} } };