diff --git a/src/sampling/Make/files b/src/sampling/Make/files index 71007d65bd8a19dedc6c41d640d652e63a89db62..22c32f0eeb11f19d3686dc45e44ecb79d113178e 100644 --- a/src/sampling/Make/files +++ b/src/sampling/Make/files @@ -28,7 +28,9 @@ surface/cutting/cuttingSurfaceCuts.C surface/cutting/cuttingSurfaceBase.C surface/cutting/cuttingSurfaceBaseSelection.C surface/distanceSurface/distanceSurface.C +surface/distanceSurface/distanceSurfaceFilter.C surface/isoSurface/isoSurfaceBase.C +surface/isoSurface/isoSurfaceBaseNew.C surface/isoSurface/isoSurfaceParams.C surface/isoSurface/isoSurfaceCell.C surface/isoSurface/isoSurfacePoint.C @@ -42,6 +44,7 @@ sampledSurface/sampledPatchInternalField/sampledPatchInternalField.C sampledSurface/sampledPlane/sampledPlane.C sampledSurface/isoSurface/sampledIsoSurface.C sampledSurface/isoSurface/sampledIsoSurfaceCell.C +sampledSurface/isoSurface/sampledIsoSurfacePoint.C sampledSurface/isoSurface/sampledIsoSurfaceTopo.C sampledSurface/distanceSurface/sampledDistanceSurface.C sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C diff --git a/src/sampling/sampledSurface/distanceSurface/sampledDistanceSurface.H b/src/sampling/sampledSurface/distanceSurface/sampledDistanceSurface.H index e3d5df2509e4e0eb9294a4fc79f72c9ee756801a..ce208cb2591ea72c4c7944c8def26bbd69949ee7 100644 --- a/src/sampling/sampledSurface/distanceSurface/sampledDistanceSurface.H +++ b/src/sampling/sampledSurface/distanceSurface/sampledDistanceSurface.H @@ -40,6 +40,9 @@ Usage surface1 { type distanceSurface; + surfaceType triSurfaceMesh; + surfaceName something.obj; + topology proximity; } } \endverbatim @@ -48,18 +51,23 @@ Usage \table Property | Description | Required | Default type | distanceSurface | yes | - distance | Distance from surface | yes | - signed | Use sign when distance is positive | partly | - isoMethod | Iso-algorithm (cell/topo/point) | no | topo - regularise | Point snapping for iso-surface | no | true + distance | distance from surface | no | 0 + signed | Use sign when distance is positive | no | true + isoMethod | Iso-algorithm (cell/topo/point) | no | default + regularise | Face simplification (enum or bool) | no | true average | Cell values from averaged point values | no | false bounds | Limit with bounding box | no | surfaceType | Type of surface | yes | surfaceName | Name of surface in \c triSurface/ | no | dict name + topology | Topology filter (none/largestRegion/nearestPoints/proximity) | no | none + nearestPoints | Points for point-based segmentation | no | + maxDistance | Max search distance for nearestPoints | no | GREAT + relProximity | Max limit of absolute vs normal distance | no | 1e3 \endtable SourceFiles sampledDistanceSurface.C + sampledDistanceSurfaceTemplates.C \*---------------------------------------------------------------------------*/ diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.C index 19479bd0fbc7bfdea68063111d3d39bad275f139..bd12eed92cb0417e32568ee31e364437dda92eaa 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.C +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.C @@ -28,9 +28,11 @@ License #include "sampledIsoSurface.H" #include "dictionary.H" +#include "fvMesh.H" #include "volFields.H" #include "volPointInterpolation.H" #include "addToRunTimeSelectionTable.H" +#include "PtrList.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -44,13 +46,6 @@ namespace Foam word, isoSurface ); - addNamedToRunTimeSelectionTable - ( - sampledSurface, - sampledIsoSurface, - word, - isoSurfacePoint - ); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // @@ -310,6 +305,111 @@ void Foam::sampledIsoSurface::getIsoFields() const } +void Foam::sampledIsoSurface::combineSurfaces +( + PtrList<isoSurfaceBase>& isoSurfPtrs +) +{ + isoSurfacePtr_.reset(nullptr); + + // Already checked previously for ALGO_POINT, but do it again + // - ALGO_POINT still needs fields (for interpolate) + // The others can do straight transfer + if + ( + isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT + && isoSurfPtrs.size() == 1 + ) + { + // Shift from list to autoPtr + isoSurfacePtr_.reset(isoSurfPtrs.release(0)); + } + else if (isoSurfPtrs.size() == 1) + { + autoPtr<isoSurfaceBase> ptr(isoSurfPtrs.release(0)); + auto& surf = *ptr; + + surface_.transfer(static_cast<meshedSurface&>(surf)); + meshCells_.transfer(surf.meshCells()); + } + else + { + // Combine faces with point offsets + // + // Note: use points().size() from surface, not nPoints() + // since there may be uncompacted dangling nodes + + label nFaces = 0, nPoints = 0; + + for (const auto& surf : isoSurfPtrs) + { + nFaces += surf.size(); + nPoints += surf.points().size(); + } + + faceList newFaces(nFaces); + pointField newPoints(nPoints); + meshCells_.resize(nFaces); + + surfZoneList newZones(isoSurfPtrs.size()); + + nFaces = 0; + nPoints = 0; + forAll(isoSurfPtrs, surfi) + { + autoPtr<isoSurfaceBase> ptr(isoSurfPtrs.release(surfi)); + auto& surf = *ptr; + + SubList<face> subFaces(newFaces, surf.size(), nFaces); + SubList<point> subPoints(newPoints, surf.points().size(), nPoints); + SubList<label> subCells(meshCells_, surf.size(), nFaces); + + newZones[surfi] = surfZone + ( + surfZoneIdentifier::defaultName(surfi), + subFaces.size(), // size + nFaces, // start + surfi // index + ); + + subFaces = surf.surfFaces(); + subPoints = surf.points(); + subCells = surf.meshCells(); + + if (nPoints) + { + for (face& f : subFaces) + { + for (label& pointi : f) + { + pointi += nPoints; + } + } + } + + nFaces += subFaces.size(); + nPoints += subPoints.size(); + } + + meshedSurface combined + ( + std::move(newPoints), + std::move(newFaces), + newZones + ); + + surface_.transfer(combined); + } + + // Addressing into the full mesh + if (subMeshPtr_ && meshCells_.size()) + { + meshCells_ = + UIndirectList<label>(subMeshPtr_->cellMap(), meshCells_); + } +} + + bool Foam::sampledIsoSurface::updateGeometry() const { const fvMesh& fvm = static_cast<const fvMesh&>(mesh()); @@ -330,34 +430,76 @@ bool Foam::sampledIsoSurface::updateGeometry() const // Clear derived data sampledSurface::clearGeom(); + const bool hasCellZones = + (-1 != mesh().cellZones().findIndex(zoneNames_)); - // Get sub-mesh if any + // Geometry if ( - !subMeshPtr_ - && (-1 != mesh().cellZones().findIndex(zoneNames_)) + simpleSubMesh_ + && isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT ) { - const label exposedPatchi = - mesh().boundaryMesh().findPatchID(exposedPatchName_); + subMeshPtr_.reset(nullptr); - DebugInfo - << "Allocating subset of size " - << mesh().cellZones().selection(zoneNames_).count() - << " with exposed faces into patch " - << exposedPatchi << endl; + // Handle cell zones as inverse (blocked) selection + if (!ignoreCellsPtr_) + { + ignoreCellsPtr_.reset(new bitSet); - subMeshPtr_.reset - ( - new fvMeshSubset + if (hasCellZones) + { + bitSet select(mesh().cellZones().selection(zoneNames_)); + + if (select.any() && !select.all()) + { + // From selection to blocking + select.flip(); + + *ignoreCellsPtr_ = std::move(select); + } + } + } + } + else + { + // A standard subMesh treatment + + if (ignoreCellsPtr_) + { + ignoreCellsPtr_->clearStorage(); + } + else + { + ignoreCellsPtr_.reset(new bitSet); + } + + // Get sub-mesh if any + if (!subMeshPtr_ && hasCellZones) + { + const label exposedPatchi = + mesh().boundaryMesh().findPatchID(exposedPatchName_); + + DebugInfo + << "Allocating subset of size " + << mesh().cellZones().selection(zoneNames_).count() + << " with exposed faces into patch " + << exposedPatchi << endl; + + subMeshPtr_.reset ( - fvm, - mesh().cellZones().selection(zoneNames_), - exposedPatchi - ) - ); + new fvMeshSubset + ( + fvm, + mesh().cellZones().selection(zoneNames_), + exposedPatchi + ) + ); + } } + + // The fields getIsoFields(); refPtr<volScalarField> tvolFld(*volFieldPtr_); @@ -369,27 +511,57 @@ bool Foam::sampledIsoSurface::updateGeometry() const tpointFld.cref(*pointSubFieldPtr_); } - isoSurfacePtr_.reset - ( - new isoSurfacePoint + + // Create surfaces for each iso level + + PtrList<isoSurfaceBase> isoSurfPtrs(isoValues_.size()); + + forAll(isoValues_, surfi) + { + isoSurfPtrs.set ( - tvolFld(), - tpointFld(), - isoVal_, - isoParams_ - ) - ); + surfi, + isoSurfaceBase::New + ( + isoParams_, + tvolFld(), + tpointFld().primitiveField(), + isoValues_[surfi], + *ignoreCellsPtr_ + ) + ); + } + + // And flatten + const_cast<sampledIsoSurface&>(*this) + .combineSurfaces(isoSurfPtrs); + // triangulate uses remapFaces() + // - this is somewhat less efficient since it recopies the faces + // that we just created, but we probably don't want to do this + // too often anyhow. + if + ( + triangulate_ + && surface_.size() + && (isoParams_.algorithm() == isoSurfaceParams::ALGO_TOPO) + ) + { + labelList faceMap; + surface_.triangulate(faceMap); + meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); + } + if (debug) { - Pout<< "isoSurfacePoint::updateGeometry() : constructed iso:" - << nl - << " isoField : " << isoField_ << nl - << " isoValue : " << isoVal_ << nl + Pout<< "isoSurface::updateGeometry() : constructed iso:" << nl + << " field : " << isoField_ << nl + << " value : " << flatOutput(isoValues_) << nl << " average : " << Switch(average_) << nl << " filter : " - << Switch(bool(isoParams_.filter())) << nl; + << Switch(bool(isoParams_.filter())) << nl + << " bounds : " << isoParams_.getClipBounds() << nl; if (subMeshPtr_) { Pout<< " zone size : " @@ -409,6 +581,7 @@ bool Foam::sampledIsoSurface::updateGeometry() const Foam::sampledIsoSurface::sampledIsoSurface ( + const isoSurfaceParams& params, const word& name, const polyMesh& mesh, const dictionary& dict @@ -416,32 +589,102 @@ Foam::sampledIsoSurface::sampledIsoSurface : sampledSurface(name, mesh, dict), isoField_(dict.get<word>("isoField")), - isoVal_(dict.get<scalar>("isoValue")), - isoParams_(dict), + isoValues_(), + isoParams_(dict, params), average_(dict.getOrDefault("average", false)), + triangulate_(dict.getOrDefault("triangulate", false)), + simpleSubMesh_(dict.getOrDefault("simpleSubMesh", false)), zoneNames_(), exposedPatchName_(), prevTimeIndex_(-1), + surface_(), meshCells_(), isoSurfacePtr_(nullptr), + + subMeshPtr_(nullptr), + ignoreCellsPtr_(nullptr), + storedVolFieldPtr_(nullptr), volFieldPtr_(nullptr), pointFieldPtr_(nullptr), - subMeshPtr_(nullptr), + storedVolSubFieldPtr_(nullptr), volSubFieldPtr_(nullptr), pointSubFieldPtr_(nullptr) { - isoParams_.algorithm(isoSurfaceParams::ALGO_POINT); // Force + if (params.algorithm() != isoSurfaceParams::ALGO_DEFAULT) + { + // Forced use of specified algorithm (ignore dictionary entry) + isoParams_.algorithm(params.algorithm()); + } + + // The isoValues or isoValue + + if (!dict.readIfPresent("isoValues", isoValues_)) + { + isoValues_.resize(1); + dict.readEntry("isoValue", isoValues_.first()); + } - if (!sampledSurface::interpolate()) + if (isoValues_.empty()) { FatalIOErrorInFunction(dict) - << "Non-interpolated iso surface not supported since triangles" - << " span across cells." << exit(FatalIOError); + << "No isoValue or isoValues specified." << nl + << exit(FatalIOError); } + if (isoValues_.size() > 1) + { + const label nOrig = isoValues_.size(); + + inplaceUniqueSort(isoValues_); + + if (nOrig != isoValues_.size()) + { + IOWarningInFunction(dict) + << "Removed non-unique isoValues" << nl; + } + } + + if (isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT) + { + // Not possible for ALGO_POINT + simpleSubMesh_ = false; + + if (!sampledSurface::interpolate()) + { + FatalIOErrorInFunction(dict) + << "Non-interpolated iso-surface (point) not supported" + << " since triangles span across cells." << nl + << exit(FatalIOError); + } + + if (isoValues_.size() > 1) + { + FatalIOErrorInFunction(dict) + << "Multiple values on iso-surface (point) not supported" + << " since needs original interpolators." << nl + << exit(FatalIOError); + } + } + + if (isoParams_.algorithm() == isoSurfaceParams::ALGO_TOPO) + { + if + ( + triangulate_ + && (isoParams_.filter() == isoSurfaceParams::filterType::NONE) + ) + { + FatalIOErrorInFunction(dict) + << "Cannot triangulate without a regularise filter" << nl + << exit(FatalIOError); + } + } + + + // Zones if (!dict.readIfPresent("zones", zoneNames_) && dict.found("zone")) { @@ -461,6 +704,17 @@ Foam::sampledIsoSurface::sampledIsoSurface } +Foam::sampledIsoSurface::sampledIsoSurface +( + const word& name, + const polyMesh& mesh, + const dictionary& dict +) +: + sampledIsoSurface(isoSurfaceParams(), name, mesh, dict) +{} + + // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // Foam::sampledIsoSurface::~sampledIsoSurface() @@ -608,7 +862,7 @@ void Foam::sampledIsoSurface::print(Ostream& os) const { os << "isoSurfacePoint: " << name() << " :" << " field :" << isoField_ - << " value :" << isoVal_; + << " value :" << flatOutput(isoValues_); } diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.H b/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.H index 24990814693d61ec0c89fbb5fee2892fddc32cd1..b78aa1dcbbdaadd0ee2f1fd978623f2a06fc1267 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.H +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurface.H @@ -28,8 +28,7 @@ Class Foam::sampledIsoSurface Description - A sampledSurface defined by a surface of iso value using a - \em point algorithm (always triangulated!). + A sampledSurface defined by a surface of iso value. It only recalculates the iso-surface if time changes. To be used in sampleSurfaces / functionObjects. @@ -40,9 +39,10 @@ Usage { surface1 { - type isoSurfacePoint; + type isoSurface; isoField T; isoValue 373; + isoMethod topo; } } \endverbatim @@ -50,18 +50,26 @@ Usage Where the sub-entries comprise: \table Property | Description | Required | Default - type | isoSurfacePoint / isoSurface | yes | + type | isoSurface | yes | isoField | field name for obtaining iso-surface | yes | isoValue | value of iso-surface | yes | + isoValues| values for iso-surfaces | yes | + isoMethod | Iso-algorithm (cell/topo/point) | no | topo average | cell values from averaged point values | no | false bounds | limit with bounding box | no | zone | limit to cell zone (name or regex) | no | zones | limit to cell zones (names, regexs) | no | + simpleSubMesh | Simple sub-meshing in algorithm itself | no | false exposedPatchName | name for zone subset | optional | regularise | point snapping (bool or enum) | no | true + triangulate | triangulate faces (if regularise) | no | false mergeTol | tolerance for merging points | no | 1e-6 \endtable + Some options are limited to particular algorithms. + - triangulate is topo-only + - simpleSubMesh and multiple isoValues are not available for point. + SourceFiles sampledIsoSurface.C sampledIsoSurfaceTemplates.C @@ -71,9 +79,9 @@ SourceFiles #ifndef sampledIsoSurface_H #define sampledIsoSurface_H -#include "isoSurfacePoint.H" #include "sampledSurface.H" #include "fvMeshSubset.H" +#include "isoSurfaceBase.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -93,8 +101,8 @@ class sampledIsoSurface //- Field to get isoSurface of const word isoField_; - //- Iso value - const scalar isoVal_; + //- The iso-value(s) + List<scalar> isoValues_; //- Parameters (filtering etc) for iso-surface isoSurfaceParams isoParams_; @@ -102,6 +110,12 @@ class sampledIsoSurface //- Whether to recalculate cell values as average of point values bool average_; + //- Whether to triangulate ALGO_TOPO (after filtering) + bool triangulate_; + + //- Use simple sub-meshing in algorithm itself + bool simpleSubMesh_; + //- The zone or zones for the iso-surface wordRes zoneNames_; @@ -121,7 +135,16 @@ class sampledIsoSurface mutable labelList meshCells_; //- Extracted iso-surface, for interpolators - mutable autoPtr<isoSurfacePoint> isoSurfacePtr_; + mutable autoPtr<isoSurfaceBase> isoSurfacePtr_; + + + // Mesh Subsetting + + //- Cached subMesh for (pre-)subset of cell zones + mutable autoPtr<fvMeshSubset> subMeshPtr_; + + //- Cached ignore cells for (post-)subset of cell zones + mutable autoPtr<bitSet> ignoreCellsPtr_; // Fields @@ -133,10 +156,8 @@ class sampledIsoSurface //- Cached pointfield mutable const pointScalarField* pointFieldPtr_; - // And on subsetted mesh - //- Cached submesh - mutable autoPtr<fvMeshSubset> subMeshPtr_; + // And on (pre-)subsetted mesh //- Cached volfield mutable autoPtr<volScalarField> storedVolSubFieldPtr_; @@ -151,6 +172,9 @@ class sampledIsoSurface //- Get fields needed to recreate iso surface. void getIsoFields() const; + //- Collect iso-surfaces into a single surface (No point merging) + void combineSurfaces(PtrList<isoSurfaceBase>& isoSurfPtrs); + //- Create iso surface (if time has changed) // Do nothing (and return false) if no update was needed bool updateGeometry() const; @@ -196,6 +220,15 @@ public: // Constructors + //- Construct from dictionary + sampledIsoSurface + ( + const isoSurfaceParams& params, + const word& name, + const polyMesh& mesh, + const dictionary& dict + ); + //- Construct from dictionary sampledIsoSurface ( diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.C index cc83106d317365c6bc473455055a1667d36f795a..d28cd2997208fce674efbb61926257678b51c105 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.C +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.C @@ -5,8 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2016-2020 OpenCFD Ltd. + Copyright (C) 2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,19 +26,13 @@ License \*---------------------------------------------------------------------------*/ #include "sampledIsoSurfaceCell.H" -#include "isoSurfaceCell.H" -#include "dictionary.H" -#include "fvMesh.H" -#include "volFields.H" -#include "volPointInterpolation.H" #include "addToRunTimeSelectionTable.H" -#include "fvMesh.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { - defineTypeNameAndDebug(sampledIsoSurfaceCell, 0); + defineTypeName(sampledIsoSurfaceCell); addNamedToRunTimeSelectionTable ( sampledSurface, @@ -49,161 +42,6 @@ namespace Foam ); } -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -bool Foam::sampledIsoSurfaceCell::updateGeometry() const -{ - const fvMesh& fvm = static_cast<const fvMesh&>(mesh()); - - // No update needed - if (fvm.time().timeIndex() == prevTimeIndex_) - { - return false; - } - - prevTimeIndex_ = fvm.time().timeIndex(); - - // Clear any previously stored topologies - surface_.clear(); - meshCells_.clear(); - isoSurfacePtr_.reset(nullptr); - - // Clear derived data - sampledSurface::clearGeom(); - - - // Handle cell zones as inverse (blocked) selection - if (!ignoreCellsPtr_) - { - ignoreCellsPtr_.reset(new bitSet); - - if (-1 != mesh().cellZones().findIndex(zoneNames_)) - { - bitSet select(mesh().cellZones().selection(zoneNames_)); - - if (select.any() && !select.all()) - { - // From selection to blocking - select.flip(); - - *ignoreCellsPtr_ = std::move(select); - } - } - } - - - // Use field from database, or try to read it in - const auto* cellFldPtr = fvm.findObject<volScalarField>(isoField_); - - if (debug) - { - if (cellFldPtr) - { - InfoInFunction << "Lookup " << isoField_ << endl; - } - else - { - InfoInFunction - << "Reading " << isoField_ - << " from time " << fvm.time().timeName() - << endl; - } - } - - // For holding the volScalarField read in. - autoPtr<volScalarField> fieldReadPtr; - - if (!cellFldPtr) - { - // Bit of a hack. Read field and store. - - fieldReadPtr = autoPtr<volScalarField>::New - ( - IOobject - ( - isoField_, - fvm.time().timeName(), - fvm, - IOobject::MUST_READ, - IOobject::NO_WRITE, - false - ), - fvm - ); - } - - const volScalarField& cellFld = - (fieldReadPtr ? *fieldReadPtr : *cellFldPtr); - - auto tpointFld = volPointInterpolation::New(fvm).interpolate(cellFld); - - // Field reference (assuming non-averaged) - tmp<scalarField> tcellValues(cellFld.primitiveField()); - - if (average_) - { - // From point field and interpolated cell. - tcellValues = tmp<scalarField>::New(fvm.nCells(), Zero); - auto& cellAvg = tcellValues.ref(); - - labelField nPointCells(fvm.nCells(), Zero); - - for (label pointi = 0; pointi < fvm.nPoints(); ++pointi) - { - const scalar& val = tpointFld().primitiveField()[pointi]; - const labelList& pCells = fvm.pointCells(pointi); - - for (const label celli : pCells) - { - cellAvg[celli] += val; - ++nPointCells[celli]; - } - } - forAll(cellAvg, celli) - { - cellAvg[celli] /= nPointCells[celli]; - } - } - - { - isoSurfaceCell surf - ( - fvm, - tcellValues(), // A primitiveField - tpointFld().primitiveField(), - isoVal_, - isoParams_, - *ignoreCellsPtr_ - ); - - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_.transfer(surf.meshCells()); - } - - // if (subMeshPtr_ && meshCells_.size()) - // { - // // With the correct addressing into the full mesh - // meshCells_ = - // UIndirectList<label>(subMeshPtr_->cellMap(), meshCells_); - // } - - if (debug) - { - Pout<< "isoSurfaceCell::updateGeometry() : constructed iso:" << nl - << " isoField : " << isoField_ << nl - << " isoValue : " << isoVal_ << nl - << " average : " << Switch(average_) << nl - << " filter : " - << Switch(bool(isoParams_.filter())) << nl - << " bounds : " << isoParams_.getClipBounds() << nl - << " points : " << points().size() << nl - << " faces : " << surface().size() << nl - << " cut cells : " << meshCells().size() << endl; - } - - return true; -} - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // @@ -214,185 +52,14 @@ Foam::sampledIsoSurfaceCell::sampledIsoSurfaceCell const dictionary& dict ) : - sampledSurface(name, mesh, dict), - isoField_(dict.get<word>("isoField")), - isoVal_(dict.get<scalar>("isoValue")), - isoParams_(dict), - average_(dict.getOrDefault("average", true)), - zoneNames_(), - prevTimeIndex_(-1), - surface_(), - meshCells_(), - isoSurfacePtr_(nullptr) -{ - isoParams_.algorithm(isoSurfaceParams::ALGO_CELL); // Force - - if (!dict.readIfPresent("zones", zoneNames_) && dict.found("zone")) - { - zoneNames_.resize(1); - dict.readEntry("zone", zoneNames_.first()); - } - - if (-1 != mesh.cellZones().findIndex(zoneNames_)) - { - DebugInfo - << "Restricting to cellZone(s) " << flatOutput(zoneNames_) << endl; - } -} - - -// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // - -Foam::sampledIsoSurfaceCell::~sampledIsoSurfaceCell() + sampledIsoSurface + ( + isoSurfaceParams(isoSurfaceParams::ALGO_CELL), + name, + mesh, + dict + ) {} -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - -bool Foam::sampledIsoSurfaceCell::needsUpdate() const -{ - const fvMesh& fvm = static_cast<const fvMesh&>(mesh()); - - return fvm.time().timeIndex() != prevTimeIndex_; -} - - -bool Foam::sampledIsoSurfaceCell::expire() -{ - surface_.clear(); - meshCells_.clear(); - isoSurfacePtr_.reset(nullptr); - - // Clear derived data - sampledSurface::clearGeom(); - - ignoreCellsPtr_.reset(nullptr); - - // Already marked as expired - if (prevTimeIndex_ == -1) - { - return false; - } - - // Force update - prevTimeIndex_ = -1; - return true; -} - - -bool Foam::sampledIsoSurfaceCell::update() -{ - return updateGeometry(); -} - - -Foam::tmp<Foam::scalarField> -Foam::sampledIsoSurfaceCell::sample -( - const interpolation<scalar>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::vectorField> -Foam::sampledIsoSurfaceCell::sample -( - const interpolation<vector>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::sphericalTensorField> -Foam::sampledIsoSurfaceCell::sample -( - const interpolation<sphericalTensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::symmTensorField> -Foam::sampledIsoSurfaceCell::sample -( - const interpolation<symmTensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::tensorField> -Foam::sampledIsoSurfaceCell::sample -( - const interpolation<tensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::scalarField> -Foam::sampledIsoSurfaceCell::interpolate -( - const interpolation<scalar>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::vectorField> -Foam::sampledIsoSurfaceCell::interpolate -( - const interpolation<vector>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - -Foam::tmp<Foam::sphericalTensorField> -Foam::sampledIsoSurfaceCell::interpolate -( - const interpolation<sphericalTensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::symmTensorField> -Foam::sampledIsoSurfaceCell::interpolate -( - const interpolation<symmTensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::tensorField> -Foam::sampledIsoSurfaceCell::interpolate -( - const interpolation<tensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -void Foam::sampledIsoSurfaceCell::print(Ostream& os) const -{ - os << "isoSurfaceCell: " << name() << " :" - << " field:" << isoField_ - << " value:" << isoVal_; - //<< " faces:" << faces().size() // possibly no geom yet - //<< " points:" << points().size(); -} - - // ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.H b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.H index a71aa0fee3da9f174984a546008138fc9b26144a..2841566f4c43c7a0937f52e16b0bd2e664ee0c3f 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.H +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCell.H @@ -63,17 +63,13 @@ Usage SourceFiles sampledIsoSurfaceCell.C - sampledIsoSurfaceCellTemplates.C \*---------------------------------------------------------------------------*/ #ifndef sampledIsoSurfaceCell_H #define sampledIsoSurfaceCell_H -#include "sampledSurface.H" -#include "MeshedSurface.H" -#include "MeshedSurfacesFwd.H" -#include "isoSurfaceCell.H" +#include "sampledIsoSurface.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -86,94 +82,12 @@ namespace Foam class sampledIsoSurfaceCell : - public sampledSurface + public sampledIsoSurface { - // Private typedefs for convenience - typedef meshedSurface Mesh; - - - // Private Data - - //- Field to get isoSurface of - const word isoField_; - - //- Iso value - const scalar isoVal_; - - //- Parameters (filtering etc) for iso-surface - isoSurfaceParams isoParams_; - - //- Recalculate cell values as average of point values - bool average_; - - //- The zone or zones for the iso-surface - wordRes zoneNames_; - - - // Sampling geometry. Directly stored or via an iso-surface (ALGO_POINT) - - //- Time at last call, also track if surface needs an update - mutable label prevTimeIndex_; - - //- The extracted surface (direct storage) - mutable meshedSurface surface_; - - //- For every face the original cell in mesh (direct storage) - mutable labelList meshCells_; - - //- Extracted iso-surface, for interpolators - mutable autoPtr<isoSurfaceCell> isoSurfacePtr_; - - - // Mesh subsetting - - //- Cached ignore cells for sub-mesh (zoned) - mutable autoPtr<bitSet> ignoreCellsPtr_; - - - // Private Member Functions - - //- Create iso surface (if time has changed) - // Do nothing (and return false) if no update was needed - bool updateGeometry() const; - - //- Sample volume field onto surface faces - template<class Type> - tmp<Field<Type>> sampleOnFaces - ( - const interpolation<Type>& sampler - ) const; - - //- Interpolate volume field onto surface points - template<class Type> - tmp<Field<Type>> sampleOnPoints - ( - const interpolation<Type>& interpolator - ) const; - - //- Use isoSurfacePtr_ for point interpolation - template<class Type> - tmp<Field<Type>> sampleOnIsoSurfacePoints - ( - const interpolation<Type>& interpolator - ) const; - - -protected: - - // Protected Member Functions - - //- Is currently backed by an isoSurfacePtr_ - bool hasIsoSurface() const - { - return bool(isoSurfacePtr_); - } - - public: //- Runtime type information - TypeName("sampledIsoSurfaceCell"); + TypeNameNoDebug("sampledIsoSurfaceCell"); // Constructors @@ -188,147 +102,7 @@ public: //- Destructor - virtual ~sampledIsoSurfaceCell(); - - - // Member Functions - - //- Does the surface need an update? - virtual bool needsUpdate() const; - - //- Mark the surface as needing an update. - // May also free up unneeded data. - // Return false if surface was already marked as expired. - virtual bool expire(); - - //- Update the surface as required. - // Do nothing (and return false) if no update was needed - virtual bool update(); - - //- The currently created surface geometry - const meshedSurface& surface() const - { - if (isoSurfacePtr_) - { - return *isoSurfacePtr_; - } - return surface_; - } - - //- For each face, the original cell in mesh - const labelList& meshCells() const - { - if (isoSurfacePtr_) - { - return isoSurfacePtr_->meshCells(); - } - return meshCells_; - } - - //- Points of surface - virtual const pointField& points() const - { - return surface().points(); - } - - //- Faces of surface - virtual const faceList& faces() const - { - return surface().surfFaces(); - } - - //- Per-face zone/region information - virtual const labelList& zoneIds() const - { - return labelList::null(); - } - - //- Face area magnitudes - virtual const vectorField& Sf() const - { - return surface().Sf(); - } - - //- Face area magnitudes - virtual const scalarField& magSf() const - { - return surface().magSf(); - } - - //- Face centres - virtual const vectorField& Cf() const - { - return surface().Cf(); - } - - - // Sample - - //- Sample volume field onto surface faces - virtual tmp<scalarField> sample - ( - const interpolation<scalar>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<vectorField> sample - ( - const interpolation<vector>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<sphericalTensorField> sample - ( - const interpolation<sphericalTensor>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<symmTensorField> sample - ( - const interpolation<symmTensor>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<tensorField> sample - ( - const interpolation<tensor>& sampler - ) const; - - - // Interpolate - - //- Interpolate volume field onto surface points - virtual tmp<scalarField> interpolate - ( - const interpolation<scalar>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<vectorField> interpolate - ( - const interpolation<vector>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<sphericalTensorField> interpolate - ( - const interpolation<sphericalTensor>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<symmTensorField> interpolate - ( - const interpolation<symmTensor>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<tensorField> interpolate - ( - const interpolation<tensor>& interpolator - ) const; - - //- Write - virtual void print(Ostream& os) const; + virtual ~sampledIsoSurfaceCell() = default; }; @@ -338,12 +112,6 @@ public: // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -#ifdef NoRepository - #include "sampledIsoSurfaceCellTemplates.C" -#endif - -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - #endif // ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCellTemplates.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCellTemplates.C index 70a5910e37cbc1954ed5900e62ac056d6f8bf558..7cec1b4541d1553571d30afa2aac3b841f6c3d51 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCellTemplates.C +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceCellTemplates.C @@ -1,120 +1 @@ -/*---------------------------------------------------------------------------*\ - ========= | - \\ / F ield | OpenFOAM: The Open Source CFD Toolbox - \\ / O peration | - \\ / A nd | www.openfoam.com - \\/ M anipulation | -------------------------------------------------------------------------------- - Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2018-2020 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 "sampledIsoSurfaceCell.H" -#include "volFieldsFwd.H" -#include "pointFields.H" -#include "volPointInterpolation.H" - -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceCell::sampleOnFaces -( - const interpolation<Type>& sampler -) const -{ - updateGeometry(); // Recreate geometry if time has changed - - return sampledSurface::sampleOnFaces - ( - sampler, - meshCells(), - surface(), - points() - ); -} - - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceCell::sampleOnPoints -( - const interpolation<Type>& interpolator -) const -{ - updateGeometry(); // Recreate geometry if time has changed - - if (isoSurfacePtr_) - { - return this->sampleOnIsoSurfacePoints(interpolator); - } - - return sampledSurface::sampleOnPoints - ( - interpolator, - meshCells(), - faces(), - points() - ); -} - - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceCell::sampleOnIsoSurfacePoints -( - const interpolation<Type>& interpolator -) const -{ - if (!isoSurfacePtr_) - { - FatalErrorInFunction - << "cannot call without an iso-surface" << nl - << exit(FatalError); - } - - // Assume volPointInterpolation for the point field! - const auto& volFld = interpolator.psi(); - - tmp<GeometricField<Type, fvPatchField, volMesh>> tvolFld(volFld); - tmp<GeometricField<Type, pointPatchField, pointMesh>> tpointFld; - - // if (subMeshPtr_) - // { - // // Replace with subset - // tvolFld.reset(subMeshPtr_->interpolate(volFld)); - // } - - // Interpolated point field - tpointFld.reset - ( - volPointInterpolation::New(tvolFld().mesh()).interpolate(tvolFld()) - ); - - if (average_) - { - tvolFld.reset(pointAverage(tpointFld())); - } - - return isoSurfacePtr_->interpolate(tvolFld(), tpointFld()); -} - - -// ************************************************************************* // +#warning File removed - left for old dependency check only diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.C new file mode 100644 index 0000000000000000000000000000000000000000..a96ce68e17d16e1f3d1871e5ec411eca8fcf904f --- /dev/null +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.C @@ -0,0 +1,64 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 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 "sampledIsoSurfacePoint.H" +#include "addToRunTimeSelectionTable.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +namespace Foam +{ + defineTypeName(sampledIsoSurfacePoint); + addNamedToRunTimeSelectionTable + ( + sampledSurface, + sampledIsoSurfacePoint, + word, + isoSurfacePoint + ); +} + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::sampledIsoSurfacePoint::sampledIsoSurfacePoint +( + const word& name, + const polyMesh& mesh, + const dictionary& dict +) +: + sampledIsoSurface + ( + isoSurfaceParams(isoSurfaceParams::ALGO_POINT), + name, + mesh, + dict + ) +{} + + +// ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.H b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.H new file mode 100644 index 0000000000000000000000000000000000000000..e1ec6f3e29af46e637358df330434da76209b70d --- /dev/null +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfacePoint.H @@ -0,0 +1,116 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 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/>. + +Class + Foam::sampledIsoSurfacePoint + +Description + A sampledSurface defined by a surface of iso value using a + \em point algorithm (always triangulated!). + It only recalculates the iso-surface if time changes. + To be used in sampleSurfaces / functionObjects. + +Usage + Example of function object partial specification: + \verbatim + surfaces + { + surface1 + { + type isoSurfacePoint; + isoField T; + isoValue 373; + } + } + \endverbatim + + Where the sub-entries comprise: + \table + Property | Description | Required | Default + type | isoSurfacePoint | yes | + isoField | field name for obtaining iso-surface | yes | + isoValue | value of iso-surface | yes | + average | cell values from averaged point values | no | false + bounds | limit with bounding box | no | + zone | limit to cell zone (name or regex) | no | + zones | limit to cell zones (names, regexs) | no | + regularise | point snapping | yes | + mergeTol | tolerance for merging points | no | 1e-6 + \endtable + +SourceFiles + sampledIsoSurfacePoint.C + +\*---------------------------------------------------------------------------*/ + +#ifndef sampledIsoSurfacePoint_H +#define sampledIsoSurfacePoint_H + +#include "sampledIsoSurface.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class sampledIsoSurfacePoint Declaration +\*---------------------------------------------------------------------------*/ + +class sampledIsoSurfacePoint +: + public sampledIsoSurface +{ +public: + + //- Runtime type information + TypeNameNoDebug("sampledIsoSurfacePoint"); + + + // Constructors + + //- Construct from dictionary + sampledIsoSurfacePoint + ( + const word& name, + const polyMesh& mesh, + const dictionary& dict + ); + + + //- Destructor + virtual ~sampledIsoSurfacePoint() = default; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.C index 610ceede0c05449c455fcee37c69cc22cedde01c..eb02955bca8ec0478a2fab72c84fd07834eaae29 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.C +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.C @@ -5,8 +5,7 @@ \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- - Copyright (C) 2018 OpenFOAM Foundation - Copyright (C) 2018-2020 OpenCFD Ltd. + Copyright (C) 2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -27,18 +26,13 @@ License \*---------------------------------------------------------------------------*/ #include "sampledIsoSurfaceTopo.H" -#include "isoSurfaceTopo.H" -#include "dictionary.H" -#include "fvMesh.H" -#include "volFields.H" -#include "volPointInterpolation.H" #include "addToRunTimeSelectionTable.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { - defineTypeNameAndDebug(sampledIsoSurfaceTopo, 0); + defineTypeName(sampledIsoSurfaceTopo); addNamedToRunTimeSelectionTable ( sampledSurface, @@ -48,174 +42,6 @@ namespace Foam ); } -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -bool Foam::sampledIsoSurfaceTopo::updateGeometry() const -{ - const fvMesh& fvm = static_cast<const fvMesh&>(mesh()); - - // No update needed - if (fvm.time().timeIndex() == prevTimeIndex_) - { - return false; - } - - prevTimeIndex_ = fvm.time().timeIndex(); - - // Clear any previously stored topologies - surface_.clear(); - meshCells_.clear(); - isoSurfacePtr_.reset(nullptr); - - // Clear derived data - sampledSurface::clearGeom(); - - - // Handle cell zones as inverse (blocked) selection - if (!ignoreCellsPtr_) - { - ignoreCellsPtr_.reset(new bitSet); - - if (-1 != mesh().cellZones().findIndex(zoneNames_)) - { - bitSet select(mesh().cellZones().selection(zoneNames_)); - - if (select.any() && !select.all()) - { - // From selection to blocking - select.flip(); - - *ignoreCellsPtr_ = std::move(select); - } - } - } - - - // Use field from database, or try to read it in - const auto* cellFldPtr = fvm.findObject<volScalarField>(isoField_); - - if (debug) - { - if (cellFldPtr) - { - InfoInFunction << "Lookup " << isoField_ << endl; - } - else - { - InfoInFunction - << "Reading " << isoField_ - << " from time " << fvm.time().timeName() - << endl; - } - } - - // For holding the volScalarField read in. - autoPtr<volScalarField> fieldReadPtr; - - if (!cellFldPtr) - { - // Bit of a hack. Read field and store. - - fieldReadPtr = autoPtr<volScalarField>::New - ( - IOobject - ( - isoField_, - fvm.time().timeName(), - fvm, - IOobject::MUST_READ, - IOobject::NO_WRITE, - false - ), - fvm - ); - } - - const volScalarField& cellFld = - (fieldReadPtr ? *fieldReadPtr : *cellFldPtr); - - auto tpointFld = volPointInterpolation::New(fvm).interpolate(cellFld); - - // Field reference (assuming non-averaged) - tmp<scalarField> tcellValues(cellFld.primitiveField()); - - if (average_) - { - // From point field and interpolated cell. - tcellValues = tmp<scalarField>::New(fvm.nCells(), Zero); - auto& cellAvg = tcellValues.ref(); - - labelField nPointCells(fvm.nCells(), Zero); - - for (label pointi = 0; pointi < fvm.nPoints(); ++pointi) - { - const scalar& val = tpointFld().primitiveField()[pointi]; - const labelList& pCells = fvm.pointCells(pointi); - - for (const label celli : pCells) - { - cellAvg[celli] += val; - ++nPointCells[celli]; - } - } - forAll(cellAvg, celli) - { - cellAvg[celli] /= nPointCells[celli]; - } - } - - { - isoSurfaceTopo surf - ( - fvm, - cellFld.primitiveField(), - tpointFld().primitiveField(), - isoVal_, - isoParams_, - *ignoreCellsPtr_ - ); - - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_ = std::move(surf.meshCells()); - } - - // if (subMeshPtr_ && meshCells_.size()) - // { - // // With the correct addressing into the full mesh - // meshCells_ = - // UIndirectList<label>(subMeshPtr_->cellMap(), meshCells_); - // } - - // triangulate uses remapFaces() - // - this is somewhat less efficient since it recopies the faces - // that we just created, but we probably don't want to do this - // too often anyhow. - if (triangulate_ && surface_.size()) - { - labelList faceMap; - surface_.triangulate(faceMap); - meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); - } - - if (debug) - { - Pout<< "isoSurfaceTopo::updateGeometry() : constructed iso:" << nl - << " isoField : " << isoField_ << nl - << " isoValue : " << isoVal_ << nl - << " average : " << Switch(average_) << nl - << " filter : " - << isoSurfaceParams::filterNames[isoParams_.filter()] << nl - << " triangulate : " << Switch(triangulate_) << nl - << " bounds : " << isoParams_.getClipBounds() << nl - << " points : " << points().size() << nl - << " faces : " << surface().size() << nl - << " cut cells : " << meshCells().size() << endl; - } - - return true; -} - - // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::sampledIsoSurfaceTopo::sampledIsoSurfaceTopo @@ -225,197 +51,14 @@ Foam::sampledIsoSurfaceTopo::sampledIsoSurfaceTopo const dictionary& dict ) : - sampledSurface(name, mesh, dict), - isoField_(dict.get<word>("isoField")), - isoVal_(dict.get<scalar>("isoValue")), - isoParams_(dict), - average_(dict.getOrDefault("average", false)), - triangulate_(dict.getOrDefault("triangulate", false)), - zoneNames_(), - prevTimeIndex_(-1), - surface_(), - meshCells_(), - isoSurfacePtr_(nullptr) -{ - isoParams_.algorithm(isoSurfaceParams::ALGO_TOPO); // Force - - if + sampledIsoSurface ( - triangulate_ - && (isoParams_.filter() == isoSurfaceParams::filterType::NONE) + isoSurfaceParams(isoSurfaceParams::ALGO_TOPO), + name, + mesh, + dict ) - { - FatalIOErrorInFunction(dict) - << "Cannot triangulate without a regularise filter" << nl - << exit(FatalIOError); - } - - if (!dict.readIfPresent("zones", zoneNames_) && dict.found("zone")) - { - zoneNames_.resize(1); - dict.readEntry("zone", zoneNames_.first()); - } - - if (-1 != mesh.cellZones().findIndex(zoneNames_)) - { - DebugInfo - << "Restricting to cellZone(s) " << flatOutput(zoneNames_) << endl; - } -} - - -// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * // - -Foam::sampledIsoSurfaceTopo::~sampledIsoSurfaceTopo() {} -// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - -bool Foam::sampledIsoSurfaceTopo::needsUpdate() const -{ - const fvMesh& fvm = static_cast<const fvMesh&>(mesh()); - - return fvm.time().timeIndex() != prevTimeIndex_; -} - - -bool Foam::sampledIsoSurfaceTopo::expire() -{ - surface_.clear(); - meshCells_.clear(); - isoSurfacePtr_.reset(nullptr); - - // Clear derived data - sampledSurface::clearGeom(); - - ignoreCellsPtr_.reset(nullptr); - - // Already marked as expired - if (prevTimeIndex_ == -1) - { - return false; - } - - // Force update - prevTimeIndex_ = -1; - return true; -} - - -bool Foam::sampledIsoSurfaceTopo::update() -{ - return updateGeometry(); -} - - -Foam::tmp<Foam::scalarField> -Foam::sampledIsoSurfaceTopo::sample -( - const interpolation<scalar>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::vectorField> -Foam::sampledIsoSurfaceTopo::sample -( - const interpolation<vector>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::sphericalTensorField> -Foam::sampledIsoSurfaceTopo::sample -( - const interpolation<sphericalTensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::symmTensorField> -Foam::sampledIsoSurfaceTopo::sample -( - const interpolation<symmTensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::tensorField> -Foam::sampledIsoSurfaceTopo::sample -( - const interpolation<tensor>& sampler -) const -{ - return sampleOnFaces(sampler); -} - - -Foam::tmp<Foam::scalarField> -Foam::sampledIsoSurfaceTopo::interpolate -( - const interpolation<scalar>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::vectorField> -Foam::sampledIsoSurfaceTopo::interpolate -( - const interpolation<vector>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - -Foam::tmp<Foam::sphericalTensorField> -Foam::sampledIsoSurfaceTopo::interpolate -( - const interpolation<sphericalTensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::symmTensorField> -Foam::sampledIsoSurfaceTopo::interpolate -( - const interpolation<symmTensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -Foam::tmp<Foam::tensorField> -Foam::sampledIsoSurfaceTopo::interpolate -( - const interpolation<tensor>& interpolator -) const -{ - return sampleOnPoints(interpolator); -} - - -void Foam::sampledIsoSurfaceTopo::print(Ostream& os) const -{ - os << "isoSurfaceTopo: " << name() << " :" - << " field:" << isoField_ - << " value:" << isoVal_; - //<< " faces:" << faces().size() // possibly no geom yet - //<< " points:" << points().size(); -} - - // ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.H b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.H index 71d10aa7cf050ab8c7c935a39dfd5e0275e8226a..5eca51e7ad9fe4716600f1b710da78d785ad4b63 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.H +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopo.H @@ -63,17 +63,13 @@ Usage SourceFiles sampledIsoSurfaceTopo.C - sampledIsoSurfaceTopoTemplates.C \*---------------------------------------------------------------------------*/ #ifndef sampledIsoSurfaceTopo_H #define sampledIsoSurfaceTopo_H -#include "sampledSurface.H" -#include "MeshedSurface.H" -#include "MeshedSurfacesFwd.H" -#include "isoSurfaceTopo.H" +#include "sampledIsoSurface.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -86,97 +82,12 @@ namespace Foam class sampledIsoSurfaceTopo : - public sampledSurface + public sampledIsoSurface { - // Private typedefs for convenience - typedef meshedSurface Mesh; - - - // Private Data - - //- Field to get isoSurface of - const word isoField_; - - //- Iso value - const scalar isoVal_; - - //- Parameters (filtering etc) for iso-surface - isoSurfaceParams isoParams_; - - //- Recalculate cell values as average of point values? - bool average_; - - //- Whether to triangulate (after filtering) - bool triangulate_; - - //- The zone or zones for the iso-surface - wordRes zoneNames_; - - - // Sampling geometry. Directly stored or via an iso-surface (ALGO_POINT) - - //- Time at last call, also track it surface needs an update - mutable label prevTimeIndex_; - - //- The extracted surface (direct storage) - mutable meshedSurface surface_; - - //- For every face the original cell in mesh (direct storage) - mutable labelList meshCells_; - - //- Extracted iso-surface, for interpolators - mutable autoPtr<isoSurfaceTopo> isoSurfacePtr_; - - - // Mesh subsetting - - //- Cached ignore cells for sub-mesh (zoned) - mutable autoPtr<bitSet> ignoreCellsPtr_; - - - // Private Member Functions - - //- Create iso surface (if time has changed) - // Do nothing (and return false) if no update was needed - bool updateGeometry() const; - - //- Sample volume field onto surface faces - template<class Type> - tmp<Field<Type>> sampleOnFaces - ( - const interpolation<Type>& sampler - ) const; - - //- Interpolate volume field onto surface points - template<class Type> - tmp<Field<Type>> sampleOnPoints - ( - const interpolation<Type>& interpolator - ) const; - - //- Use isoSurfacePtr_ for point interpolation - template<class Type> - tmp<Field<Type>> sampleOnIsoSurfacePoints - ( - const interpolation<Type>& interpolator - ) const; - - -protected: - - // Protected Member Functions - - //- Is currently backed by an isoSurfacePtr_ - bool hasIsoSurface() const - { - return bool(isoSurfacePtr_); - } - - public: //- Runtime type information - TypeName("sampledIsoSurfaceTopo"); + TypeNameNoDebug("sampledIsoSurfaceTopo"); // Constructors @@ -191,147 +102,7 @@ public: //- Destructor - virtual ~sampledIsoSurfaceTopo(); - - - // Member Functions - - //- Does the surface need an update? - virtual bool needsUpdate() const; - - //- Mark the surface as needing an update. - // May also free up unneeded data. - // Return false if surface was already marked as expired. - virtual bool expire(); - - //- Update the surface as required. - // Do nothing (and return false) if no update was needed - virtual bool update(); - - //- The currently created surface geometry - const meshedSurface& surface() const - { - if (isoSurfacePtr_) - { - return *isoSurfacePtr_; - } - return surface_; - } - - //- For each face, the original cell in mesh - const labelList& meshCells() const - { - if (isoSurfacePtr_) - { - return isoSurfacePtr_->meshCells(); - } - return meshCells_; - } - - //- Points of surface - virtual const pointField& points() const - { - return surface().points(); - } - - //- Faces of surface - virtual const faceList& faces() const - { - return surface().surfFaces(); - } - - //- Per-face zone/region information - virtual const labelList& zoneIds() const - { - return labelList::null(); - } - - //- Face area magnitudes - virtual const vectorField& Sf() const - { - return surface().Sf(); - } - - //- Face area magnitudes - virtual const scalarField& magSf() const - { - return surface().magSf(); - } - - //- Face centres - virtual const vectorField& Cf() const - { - return surface().Cf(); - } - - - // Sample - - //- Sample volume field onto surface faces - virtual tmp<scalarField> sample - ( - const interpolation<scalar>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<vectorField> sample - ( - const interpolation<vector>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<sphericalTensorField> sample - ( - const interpolation<sphericalTensor>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<symmTensorField> sample - ( - const interpolation<symmTensor>& sampler - ) const; - - //- Sample volume field onto surface faces - virtual tmp<tensorField> sample - ( - const interpolation<tensor>& sampler - ) const; - - - // Interpolate - - //- Interpolate volume field onto surface points - virtual tmp<scalarField> interpolate - ( - const interpolation<scalar>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<vectorField> interpolate - ( - const interpolation<vector>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<sphericalTensorField> interpolate - ( - const interpolation<sphericalTensor>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<symmTensorField> interpolate - ( - const interpolation<symmTensor>& interpolator - ) const; - - //- Interpolate volume field onto surface points - virtual tmp<tensorField> interpolate - ( - const interpolation<tensor>& interpolator - ) const; - - //- Write - virtual void print(Ostream& os) const; + virtual ~sampledIsoSurfaceTopo() = default; }; @@ -341,12 +112,6 @@ public: // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -#ifdef NoRepository - #include "sampledIsoSurfaceTopoTemplates.C" -#endif - -// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // - #endif // ************************************************************************* // diff --git a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopoTemplates.C b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopoTemplates.C index c797acc319e69347a4e66be728fc96d780c177b9..7cec1b4541d1553571d30afa2aac3b841f6c3d51 100644 --- a/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopoTemplates.C +++ b/src/sampling/sampledSurface/isoSurface/sampledIsoSurfaceTopoTemplates.C @@ -1,120 +1 @@ -/*---------------------------------------------------------------------------*\ - ========= | - \\ / F ield | OpenFOAM: The Open Source CFD Toolbox - \\ / O peration | - \\ / A nd | www.openfoam.com - \\/ M anipulation | -------------------------------------------------------------------------------- - Copyright (C) 2018 OpenFOAM Foundation - Copyright (C) 2018-2020 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 "sampledIsoSurfaceTopo.H" -#include "volFieldsFwd.H" -#include "pointFields.H" -#include "volPointInterpolation.H" - -// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceTopo::sampleOnFaces -( - const interpolation<Type>& sampler -) const -{ - updateGeometry(); // Recreate geometry if time has changed - - return sampledSurface::sampleOnFaces - ( - sampler, - meshCells(), - faces(), - points() - ); -} - - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceTopo::sampleOnPoints -( - const interpolation<Type>& interpolator -) const -{ - updateGeometry(); // Recreate geometry if time has changed - - if (isoSurfacePtr_) - { - return this->sampleOnIsoSurfacePoints(interpolator); - } - - return sampledSurface::sampleOnPoints - ( - interpolator, - meshCells(), - faces(), - points() - ); -} - - -template<class Type> -Foam::tmp<Foam::Field<Type>> -Foam::sampledIsoSurfaceTopo::sampleOnIsoSurfacePoints -( - const interpolation<Type>& interpolator -) const -{ - if (!isoSurfacePtr_) - { - FatalErrorInFunction - << "cannot call without an iso-surface" << nl - << exit(FatalError); - } - - // Assume volPointInterpolation for the point field! - const auto& volFld = interpolator.psi(); - - tmp<GeometricField<Type, fvPatchField, volMesh>> tvolFld(volFld); - tmp<GeometricField<Type, pointPatchField, pointMesh>> tpointFld; - - // if (subMeshPtr_) - // { - // // Replace with subset - // tvolFld.reset(subMeshPtr_->interpolate(volFld)); - // } - - // Interpolated point field - tpointFld.reset - ( - volPointInterpolation::New(tvolFld().mesh()).interpolate(tvolFld()) - ); - - if (average_) - { - tvolFld.reset(pointAverage(tpointFld())); - } - - return isoSurfacePtr_->interpolate(tvolFld(), tpointFld()); -} - - -// ************************************************************************* // +#warning File removed - left for old dependency check only diff --git a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C index cd379ca0ee656c562655b43efa5ae8e6ccb1f80a..b3d0982fe2e2ec73efaf7718097a75f5188954eb 100644 --- a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C +++ b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.C @@ -28,13 +28,11 @@ License #include "sampledCuttingPlane.H" #include "dictionary.H" +#include "fvMesh.H" #include "volFields.H" #include "volPointInterpolation.H" #include "addToRunTimeSelectionTable.H" -#include "fvMesh.H" -#include "isoSurfaceCell.H" -#include "isoSurfacePoint.H" -#include "isoSurfaceTopo.H" +#include "PtrList.H" // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // @@ -189,6 +187,111 @@ void Foam::sampledCuttingPlane::setDistanceFields(const plane& pln) } +void Foam::sampledCuttingPlane::combineSurfaces +( + PtrList<isoSurfaceBase>& isoSurfPtrs +) +{ + isoSurfacePtr_.reset(nullptr); + + // Already checked previously for ALGO_POINT, but do it again + // - ALGO_POINT still needs fields (for interpolate) + // The others can do straight transfer + if + ( + isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT + && isoSurfPtrs.size() == 1 + ) + { + // Shift ownership from list to autoPtr + isoSurfacePtr_.reset(isoSurfPtrs.release(0)); + } + else if (isoSurfPtrs.size() == 1) + { + autoPtr<isoSurfaceBase> ptr(isoSurfPtrs.release(0)); + auto& surf = *ptr; + + surface_.transfer(static_cast<meshedSurface&>(surf)); + meshCells_.transfer(surf.meshCells()); + } + else + { + // Combine faces with point offsets + // + // Note: use points().size() from surface, not nPoints() + // since there may be uncompacted dangling nodes + + label nFaces = 0, nPoints = 0; + + for (const auto& surf : isoSurfPtrs) + { + nFaces += surf.size(); + nPoints += surf.points().size(); + } + + faceList newFaces(nFaces); + pointField newPoints(nPoints); + meshCells_.resize(nFaces); + + surfZoneList newZones(isoSurfPtrs.size()); + + nFaces = 0; + nPoints = 0; + forAll(isoSurfPtrs, surfi) + { + autoPtr<isoSurfaceBase> ptr(isoSurfPtrs.release(surfi)); + auto& surf = *ptr; + + SubList<face> subFaces(newFaces, surf.size(), nFaces); + SubList<point> subPoints(newPoints, surf.points().size(), nPoints); + SubList<label> subCells(meshCells_, surf.size(), nFaces); + + newZones[surfi] = surfZone + ( + surfZoneIdentifier::defaultName(surfi), + subFaces.size(), // size + nFaces, // start + surfi // index + ); + + subFaces = surf.surfFaces(); + subPoints = surf.points(); + subCells = surf.meshCells(); + + if (nPoints) + { + for (face& f : subFaces) + { + for (label& pointi : f) + { + pointi += nPoints; + } + } + } + + nFaces += subFaces.size(); + nPoints += subPoints.size(); + } + + meshedSurface combined + ( + std::move(newPoints), + std::move(newFaces), + newZones + ); + + surface_.transfer(combined); + } + + // Addressing into the full mesh + if (subMeshPtr_ && meshCells_.size()) + { + meshCells_ = + UIndirectList<label>(subMeshPtr_->cellMap(), meshCells_); + } +} + + void Foam::sampledCuttingPlane::createGeometry() { if (debug) @@ -198,62 +301,105 @@ void Foam::sampledCuttingPlane::createGeometry() } // Clear any previously stored topologies - isoSurfacePtr_.reset(nullptr); surface_.clear(); meshCells_.clear(); + isoSurfacePtr_.reset(nullptr); + + // Clear derived data + sampledSurface::clearGeom(); // Clear any stored fields pointDistance_.clear(); cellDistancePtr_.clear(); + const bool hasCellZones = + (-1 != mesh().cellZones().findIndex(zoneNames_)); + const fvMesh& fvm = static_cast<const fvMesh&>(this->mesh()); - // Get sub-mesh if any + // Geometry if ( - !subMeshPtr_ - && (-1 != mesh().cellZones().findIndex(zoneNames_)) + simpleSubMesh_ + && isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT ) { - const label exposedPatchi = - mesh().boundaryMesh().findPatchID(exposedPatchName_); + subMeshPtr_.reset(nullptr); - bitSet cellsToSelect(mesh().cellZones().selection(zoneNames_)); + // Handle cell zones as inverse (blocked) selection + if (!ignoreCellsPtr_) + { + ignoreCellsPtr_.reset(new bitSet); - DebugInfo - << "Allocating subset of size " - << cellsToSelect.count() - << " with exposed faces into patch " - << exposedPatchi << endl; + if (hasCellZones) + { + bitSet select(mesh().cellZones().selection(zoneNames_)); + if (select.any() && !select.all()) + { + // From selection to blocking + select.flip(); - // If we will use a fvMeshSubset so can apply bounds as well to make - // the initial selection smaller. + *ignoreCellsPtr_ = std::move(select); + } + } + } + } + else + { + // A standard subMesh treatment - const boundBox& clipBb = isoParams_.getClipBounds(); - if (clipBb.valid() && cellsToSelect.any()) + if (ignoreCellsPtr_) + { + ignoreCellsPtr_->clearStorage(); + } + else { - const auto& cellCentres = fvm.C(); + ignoreCellsPtr_.reset(new bitSet); + } + + // Get sub-mesh if any + if (!subMeshPtr_ && hasCellZones) + { + const label exposedPatchi = + mesh().boundaryMesh().findPatchID(exposedPatchName_); + + bitSet cellsToSelect(mesh().cellZones().selection(zoneNames_)); + + DebugInfo + << "Allocating subset of size " + << cellsToSelect.count() << " with exposed faces into patch " + << exposedPatchi << endl; - for (const label celli : cellsToSelect) + + // If we will use a fvMeshSubset so can apply bounds as well to make + // the initial selection smaller. + + const boundBox& clipBb = isoParams_.getClipBounds(); + if (clipBb.valid() && cellsToSelect.any()) { - const point& cc = cellCentres[celli]; + const auto& cellCentres = fvm.C(); - if (!clipBb.contains(cc)) + for (const label celli : cellsToSelect) { - cellsToSelect.unset(celli); + const point& cc = cellCentres[celli]; + + if (!clipBb.contains(cc)) + { + cellsToSelect.unset(celli); + } } + + DebugInfo + << "Bounded subset of size " + << cellsToSelect.count() << endl; } - DebugInfo - << "Bounded subset of size " - << cellsToSelect.count() << endl; + subMeshPtr_.reset + ( + new fvMeshSubset(fvm, cellsToSelect, exposedPatchi) + ); } - - subMeshPtr_.reset - ( - new fvMeshSubset(fvm, cellsToSelect, exposedPatchi) - ); } @@ -288,7 +434,6 @@ void Foam::sampledCuttingPlane::createGeometry() dimensionedScalar(dimLength, Zero) ) ); - const volScalarField& cellDistance = cellDistancePtr_(); setDistanceFields(plane_); @@ -318,66 +463,37 @@ void Foam::sampledCuttingPlane::createGeometry() } - // This will soon improve (reduced clutter) + // Create surfaces for each offset - // Direct from cell field and point field. - if (isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT) + PtrList<isoSurfaceBase> isoSurfPtrs(offsets_.size()); + + forAll(offsets_, surfi) { - isoSurfacePtr_.reset + isoSurfPtrs.set ( - new isoSurfacePoint + surfi, + isoSurfaceBase::New ( + isoParams_, cellDistance, pointDistance_, - scalar(0), // distance - isoParams_ + offsets_[surfi], + *ignoreCellsPtr_ ) ); } - else if (isoParams_.algorithm() == isoSurfaceParams::ALGO_CELL) - { - isoSurfaceCell surf - ( - fvm, - cellDistance, - pointDistance_, - scalar(0), // distance - isoParams_ - ); - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_.transfer(surf.meshCells()); - } - else - { - // ALGO_TOPO - isoSurfaceTopo surf - ( - fvm, - cellDistance, - pointDistance_, - scalar(0), // distance - isoParams_ - ); + // And flatten + combineSurfaces(isoSurfPtrs); - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_.transfer(surf.meshCells()); - } - // Only retain for iso-surface + // Discard fields if not required by an iso-surface if (!isoSurfacePtr_) { cellDistancePtr_.reset(nullptr); pointDistance_.clear(); } - if (subMeshPtr_ && meshCells_.size()) - { - // With the correct addressing into the full mesh - meshCells_ = - UIndirectList<label>(subMeshPtr_->cellMap(), meshCells_); - } - if (debug) { print(Pout); @@ -397,6 +513,7 @@ Foam::sampledCuttingPlane::sampledCuttingPlane : sampledSurface(name, mesh, dict), plane_(dict), + offsets_(), isoParams_ ( dict, @@ -404,15 +521,59 @@ Foam::sampledCuttingPlane::sampledCuttingPlane isoSurfaceParams::filterType::DIAGCELL ), average_(dict.getOrDefault("average", false)), + simpleSubMesh_(dict.getOrDefault("simpleSubMesh", false)), zoneNames_(), exposedPatchName_(), needsUpdate_(true), - subMeshPtr_(nullptr), - cellDistancePtr_(nullptr), + surface_(), meshCells_(), - isoSurfacePtr_(nullptr) + isoSurfacePtr_(nullptr), + + subMeshPtr_(nullptr), + ignoreCellsPtr_(nullptr), + cellDistancePtr_(nullptr), + pointDistance_() { + dict.readIfPresent("offsets", offsets_); + + if (offsets_.empty()) + { + offsets_.resize(1); + offsets_.first() = Zero; + } + + if (offsets_.size() > 1) + { + const label nOrig = offsets_.size(); + + inplaceUniqueSort(offsets_); + + if (nOrig != offsets_.size()) + { + IOWarningInFunction(dict) + << "Removed non-unique offsets" << nl; + } + } + + if (isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT) + { + // Not possible for ALGO_POINT + simpleSubMesh_ = false; + + // Not possible for ALGO_POINT + if (offsets_.size() > 1) + { + FatalIOErrorInFunction(dict) + << "Multiple offsets with iso-surface (point) not supported" + << " since needs original interpolators." << nl + << exit(FatalIOError); + } + } + + + // Zones + if (!dict.readIfPresent("zones", zoneNames_) && dict.found("zone")) { zoneNames_.resize(1); @@ -589,6 +750,7 @@ void Foam::sampledCuttingPlane::print(Ostream& os) const { os << "sampledCuttingPlane: " << name() << " :" << " plane:" << plane_ + << " offsets:" << flatOutput(offsets_) << " faces:" << faces().size() << " points:" << points().size(); } diff --git a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.H b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.H index 0c78c2e52cb1d10d673397a38080386848905ab2..c35d5a74726cad9f688fa40c92b05245f1480e68 100644 --- a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.H +++ b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlane.H @@ -55,6 +55,7 @@ Usage Property | Description | Required | Default type | cuttingPlane | yes | planeType | plane description (pointAndNormal etc) | yes | + offsets | Offsets of the origin in the normal direction | no | (0) isoMethod | Iso-algorithm (cell/topo/point) | no | topo bounds | limit with bounding box | no | zone | limit to cell zone (name or regex) | no | @@ -81,7 +82,7 @@ SourceFiles #include "sampledSurface.H" #include "plane.H" #include "fvMeshSubset.H" -#include "isoSurfacePoint.H" +#include "isoSurfaceBase.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -101,12 +102,18 @@ class sampledCuttingPlane //- Plane const plane plane_; + //- The offsets to the plane - defaults to (0). + List<scalar> offsets_; + //- Parameters (filtering etc) for iso-surface isoSurfaceParams isoParams_; //- Whether to recalculate cell values as average of point values bool average_; + //- Use simple sub-meshing in algorithm itself + bool simpleSubMesh_; + //- The zone or zones in which cutting is to occur wordRes zoneNames_; @@ -117,16 +124,6 @@ class sampledCuttingPlane mutable bool needsUpdate_; - //- Mesh subset (optional: only used with zones) - autoPtr<fvMeshSubset> subMeshPtr_; - - //- Distance to cell centres - autoPtr<volScalarField> cellDistancePtr_; - - //- Distance to points - scalarField pointDistance_; - - // Sampling geometry. Directly stored or via an iso-surface (ALGO_POINT) //- The extracted surface (direct storage) @@ -136,7 +133,25 @@ class sampledCuttingPlane mutable labelList meshCells_; //- Constructed iso-surface (ALGO_POINT), for interpolators - autoPtr<isoSurfacePoint> isoSurfacePtr_; + autoPtr<isoSurfaceBase> isoSurfacePtr_; + + + // Mesh Subsetting + + //- Cached subMesh for (pre-)subset of cell zones + mutable autoPtr<fvMeshSubset> subMeshPtr_; + + //- Cached ignore cells for (post-)subset of cell zones + mutable autoPtr<bitSet> ignoreCellsPtr_; + + + // Fields + + //- Distance to cell centres + autoPtr<volScalarField> cellDistancePtr_; + + //- Distance to points + scalarField pointDistance_; // Private Member Functions @@ -151,6 +166,9 @@ class sampledCuttingPlane //- Fill cellDistance, pointDistance fields for the specified plane void setDistanceFields(const plane& pln); + //- Collect iso-surfaces into a single surface (No point merging) + void combineSurfaces(PtrList<isoSurfaceBase>& isoSurfPtrs); + //- Create iso surface void createGeometry(); diff --git a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlaneTemplates.C b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlaneTemplates.C index 239855994f6dd96c7f4e983782557aef44d3c922..cc030b9e01725e17c7fee46c18c81f6cf98a3f69 100644 --- a/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlaneTemplates.C +++ b/src/sampling/sampledSurface/sampledCuttingPlane/sampledCuttingPlaneTemplates.C @@ -91,7 +91,6 @@ Foam::sampledCuttingPlane::sampleOnIsoSurfacePoints } - template<class Type> Foam::tmp<Foam::Field<Type>> Foam::sampledCuttingPlane::sampleOnPoints diff --git a/src/sampling/surface/distanceSurface/distanceSurface.C b/src/sampling/surface/distanceSurface/distanceSurface.C index 5eb7b4dccc80c6ae8e0f091c6c9911504f9a06c2..be57d3b122b6aa294f6f73a6bc65c580d2f07265 100644 --- a/src/sampling/surface/distanceSurface/distanceSurface.C +++ b/src/sampling/surface/distanceSurface/distanceSurface.C @@ -41,6 +41,205 @@ namespace Foam } +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +const Foam::Enum +< + Foam::distanceSurface::topologyFilterType +> +Foam::distanceSurface::topoFilterNames_ +({ + { topologyFilterType::NONE, "none" }, + { topologyFilterType::LARGEST_REGION, "largestRegion" }, + { topologyFilterType::NEAREST_POINTS, "nearestPoints" }, + { topologyFilterType::PROXIMITY, "proximity" }, +}); + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Check that all point hits are valid +static inline void checkAllHits(const UList<pointIndexHit>& nearest) +{ + label notHit = 0; + for (const pointIndexHit& pHit : nearest) + { + if (!pHit.hit()) + { + ++notHit; + } + } + + if (notHit) + { + FatalErrorInFunction + << "Had " << notHit << " from " << nearest.size() + << " without a point hit" << endl + << abort(FatalError); + } +} + + +// Normal distance from surface hit point to a point in the mesh +static inline scalar normalDistance_zero +( + const point& pt, + const pointIndexHit& pHit, + const vector& norm +) +{ + const vector diff(pt - pHit.point()); + + return (diff & norm); +} + + +// Signed distance from surface hit point to a point in the mesh, +// the sign is dictated by the normal +static inline scalar normalDistance_nonzero +( + const point& pt, + const pointIndexHit& pHit, + const vector& norm +) +{ + const vector diff(pt - pHit.point()); + const scalar normDist = (diff & norm); + + return Foam::sign(normDist) * Foam::mag(diff); +} + + +// Normal distance from surface hit point to a point in the mesh +static inline void calcNormalDistance_zero +( + scalarField& distance, + const pointField& points, + const List<pointIndexHit>& nearest, + const vectorField& normals +) +{ + forAll(nearest, i) + { + distance[i] = + normalDistance_zero(points[i], nearest[i], normals[i]); + } +} + + +// Signed distance from surface hit point -> point in the mesh, +// the sign is dictated by the normal +static inline void calcNormalDistance_nonzero +( + scalarField& distance, + const pointField& points, + const List<pointIndexHit>& nearest, + const vectorField& normals +) +{ + forAll(nearest, i) + { + distance[i] = + normalDistance_nonzero(points[i], nearest[i], normals[i]); + } +} + + +// Close to the surface: normal distance from surface hit point +// Far from surface: distance from surface hit point +// +// Note +// This switch may be helpful when working directly with +// distance/gradient fields. Has low overhead otherwise. +// May be replaced in the future (2020-11) +static inline void calcNormalDistance_filtered +( + scalarField& distance, + const bitSet& ignoreLocation, + const pointField& points, + const List<pointIndexHit>& nearest, + const vectorField& normals +) +{ + forAll(nearest, i) + { + if (ignoreLocation.test(i)) + { + distance[i] = + normalDistance_nonzero(points[i], nearest[i], normals[i]); + } + else + { + distance[i] = + normalDistance_zero(points[i], nearest[i], normals[i]); + } + } +} + + +// Flat surfaces (eg, a plane) have an extreme change in +// the normal at the edge, which creates a zero-crossing +// extending to infinity. +// +// Ad hoc treatment: require that the surface hit +// point is within a somewhat generous bounding box +// for the cell +template<bool WantPointFilter = false> +static bitSet simpleGeometricFilter +( + bitSet& ignoreCells, + const List<pointIndexHit>& nearest, + const polyMesh& mesh +) +{ + // A deny filter. Initially false (accept everything) + ignoreCells.resize(mesh.nCells()); + + bitSet pointFilter; + if (WantPointFilter) + { + // Create as accept filter. Initially false (deny everything) + pointFilter.resize(mesh.nPoints()); + } + + boundBox cellBb; + + forAll(nearest, celli) + { + const point& pt = nearest[celli].point(); + + const labelList& cPoints = mesh.cellPoints(celli); + + cellBb.clear(); + cellBb.add(mesh.points(), cPoints); + + // Expand slightly to catch corners + cellBb.inflate(0.1); + + if (!cellBb.contains(pt)) + { + ignoreCells.set(celli); + } + else if (WantPointFilter) + { + // Good cell candidate, accept its points + pointFilter.set(cPoints); + } + } + + // Flip from accept to deny filter + pointFilter.flip(); + + return pointFilter; +} + + +} // End namespace Foam + + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::distanceSurface::distanceSurface @@ -51,7 +250,7 @@ Foam::distanceSurface::distanceSurface ) : mesh_(mesh), - surfPtr_ + geometryPtr_ ( searchableSurface::New ( @@ -68,19 +267,69 @@ Foam::distanceSurface::distanceSurface dict ) ), - distance_(dict.get<scalar>("distance")), - signed_ + distance_(dict.getOrDefault<scalar>("distance", 0)), + withZeroDistance_(equal(distance_, 0)), + withSignDistance_ ( - distance_ < 0 || equal(distance_, Zero) || dict.get<bool>("signed") + withZeroDistance_ + || (distance_ < 0) + || dict.getOrDefault<bool>("signed", true) ), isoParams_ ( dict, - isoSurfaceBase::ALGO_TOPO + isoSurfaceParams::ALGO_TOPO, + isoSurfaceParams::filterType::DIAGCELL ), + topoFilter_ + ( + topoFilterNames_.getOrDefault + ( + "topology", + dict, + topologyFilterType::NONE + ) + ), + nearestPoints_(), + maxDistanceSqr_(Foam::sqr(GREAT)), + absProximity_(dict.getOrDefault<scalar>("absProximity", 1e-5)), + cellDistancePtr_(nullptr), + pointDistance_(), surface_(), meshCells_(), isoSurfacePtr_(nullptr) +{ + if (topologyFilterType::NEAREST_POINTS == topoFilter_) + { + dict.readEntry("nearestPoints", nearestPoints_); + } + + if (dict.readIfPresent("maxDistance", maxDistanceSqr_)) + { + maxDistanceSqr_ = Foam::sqr(maxDistanceSqr_); + } +} + + +Foam::distanceSurface::distanceSurface +( + const polyMesh& mesh, + const word& surfaceType, + const word& surfaceName, + const isoSurfaceParams& params, + const bool interpolate +) +: + distanceSurface + ( + mesh, + interpolate, + surfaceType, + surfaceName, + scalar(0), + true, // redundant - must be signed + params + ) {} @@ -96,7 +345,7 @@ Foam::distanceSurface::distanceSurface ) : mesh_(mesh), - surfPtr_ + geometryPtr_ ( searchableSurface::New ( @@ -114,11 +363,20 @@ Foam::distanceSurface::distanceSurface ) ), distance_(distance), - signed_ + withZeroDistance_(equal(distance_, 0)), + withSignDistance_ ( - useSignedDistance || distance_ < 0 || equal(distance_, Zero) + withZeroDistance_ + || (distance_ < 0) + || useSignedDistance ), isoParams_(params), + topoFilter_(topologyFilterType::NONE), + nearestPoints_(), + maxDistanceSqr_(Foam::sqr(GREAT)), + absProximity_(1e-5), + cellDistancePtr_(nullptr), + pointDistance_(), surface_(), meshCells_(), isoSurfacePtr_(nullptr) @@ -139,7 +397,10 @@ void Foam::distanceSurface::createGeometry() surface_.clear(); meshCells_.clear(); - const fvMesh& fvm = static_cast<const fvMesh&>(mesh_); + // Doing searches on this surface + const searchableSurface& geom = geometryPtr_(); + + const fvMesh& fvmesh = static_cast<const fvMesh&>(mesh_); // Distance to cell centres // ~~~~~~~~~~~~~~~~~~~~~~~~ @@ -151,132 +412,126 @@ void Foam::distanceSurface::createGeometry() IOobject ( "distanceSurface.cellDistance", - fvm.time().timeName(), - fvm.time(), + fvmesh.time().timeName(), + fvmesh.time(), IOobject::NO_READ, IOobject::NO_WRITE, false ), - fvm, - dimensionedScalar(dimLength, Zero) + fvmesh, + dimensionedScalar(dimLength, GREAT) ) ); - volScalarField& cellDistance = *cellDistancePtr_; + auto& cellDistance = *cellDistancePtr_; + - // For distance = 0 (and isoSurfaceCell) we apply additional filtering + // For distance = 0 we apply additional geometric filtering // to limit the extent of open edges. + // + // Does not work with ALGO_POINT + + bitSet ignoreCells, ignoreCellPoints; - const bool isZeroDist = equal(distance_, Zero); const bool filterCells = ( - isZeroDist + withZeroDistance_ && isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT ); - bitSet ignoreCells; - if (filterCells) - { - ignoreCells.resize(fvm.nCells()); - } // Internal field { - const pointField& cc = fvm.C(); + const pointField& cc = fvmesh.C(); scalarField& fld = cellDistance.primitiveFieldRef(); List<pointIndexHit> nearest; - surfPtr_().findNearest + geom.findNearest ( cc, - scalarField(cc.size(), GREAT), + // Use initialized field (GREAT) to limit search too + fld, nearest ); + checkAllHits(nearest); - if (signed_ || isZeroDist) + // Geometric pre-filtering when distance == 0 + if (filterCells) { - vectorField norms; - surfPtr_().getNormal(nearest, norms); + ignoreCellPoints = + simpleGeometricFilter<false>(ignoreCells, nearest, fvmesh); + } - boundBox cellBb; + if (withSignDistance_) + { + vectorField norms; + geom.getNormal(nearest, norms); - forAll(norms, i) + if (filterCells) { - const point diff(cc[i] - nearest[i].hitPoint()); - - fld[i] = + // With inside/outside switching (see note above) + calcNormalDistance_filtered ( - isZeroDist // Use normal distance - ? (diff & norms[i]) - : Foam::sign(diff & norms[i]) * Foam::mag(diff) + fld, + ignoreCells, + cc, + nearest, + norms ); - - if (filterCells) - { - cellBb.clear(); - cellBb.add(fvm.points(), fvm.cellPoints(i)); - - // Expand slightly to catch corners - cellBb.inflate(0.1); - - if (!cellBb.contains(nearest[i].hitPoint())) - { - ignoreCells.set(i); - } - } + } + else if (withZeroDistance_) + { + calcNormalDistance_zero(fld, cc, nearest, norms); + } + else + { + calcNormalDistance_nonzero(fld, cc, nearest, norms); } } else { - forAll(nearest, i) - { - fld[i] = Foam::mag(cc[i] - nearest[i].hitPoint()); - - // No filtering for unsigned or distance != 0. - } + calcAbsoluteDistance(fld, cc, nearest); } } + // Patch fields { - volScalarField::Boundary& cellDistanceBf = - cellDistance.boundaryFieldRef(); - - forAll(fvm.C().boundaryField(), patchi) + forAll(fvmesh.C().boundaryField(), patchi) { - const pointField& cc = fvm.C().boundaryField()[patchi]; - fvPatchScalarField& fld = cellDistanceBf[patchi]; + const pointField& cc = fvmesh.C().boundaryField()[patchi]; + scalarField& fld = cellDistance.boundaryFieldRef()[patchi]; List<pointIndexHit> nearest; - surfPtr_().findNearest + geom.findNearest ( cc, scalarField(cc.size(), GREAT), nearest ); + checkAllHits(nearest); - if (signed_) + if (withSignDistance_) { vectorField norms; - surfPtr_().getNormal(nearest, norms); + geom.getNormal(nearest, norms); + + if (withZeroDistance_) + { + // Slight inconsistency in boundary vs interior when + // cells are filtered, but the patch fields are only + // used by isoSurfacePoint, and filtering is disabled + // for that anyhow. - forAll(norms, i) + calcNormalDistance_zero(fld, cc, nearest, norms); + } + else { - const point diff(cc[i] - nearest[i].hitPoint()); - - fld[i] = - ( - isZeroDist // Use normal distance - ? (diff & norms[i]) - : Foam::sign(diff & norms[i]) * Foam::mag(diff) - ); + calcNormalDistance_nonzero(fld, cc, nearest, norms); } } else { - forAll(nearest, i) - { - fld[i] = Foam::mag(cc[i] - nearest[i].hitPoint()); - } + calcAbsoluteDistance(fld, cc, nearest); } } } @@ -287,45 +542,87 @@ void Foam::distanceSurface::createGeometry() // Distance to points - pointDistance_.resize(fvm.nPoints()); + pointDistance_.resize(fvmesh.nPoints()); + pointDistance_ = GREAT; { - const pointField& pts = fvm.points(); + const pointField& pts = fvmesh.points(); + scalarField& fld = pointDistance_; List<pointIndexHit> nearest; - surfPtr_().findNearest + geom.findNearest ( pts, - scalarField(pts.size(), GREAT), + // Use initialized field (GREAT) to limit search too + pointDistance_, nearest ); + checkAllHits(nearest); - if (signed_) + if (withSignDistance_) { vectorField norms; - surfPtr_().getNormal(nearest, norms); + geom.getNormal(nearest, norms); - forAll(norms, i) + if (filterCells) { - const point diff(pts[i] - nearest[i].hitPoint()); - - pointDistance_[i] = + // With inside/outside switching (see note above) + calcNormalDistance_filtered ( - isZeroDist // Use normal distance - ? (diff & norms[i]) - : Foam::sign(diff & norms[i]) * Foam::mag(diff) + fld, + ignoreCellPoints, + pts, + nearest, + norms ); } + else if (withZeroDistance_) + { + calcNormalDistance_zero(fld, pts, nearest, norms); + } + else + { + calcNormalDistance_nonzero(fld, pts, nearest, norms); + } } else { - forAll(nearest, i) - { - pointDistance_[i] = Foam::mag(pts[i] - nearest[i].hitPoint()); - } + calcAbsoluteDistance(fld, pts, nearest); } } + // Don't need ignoreCells if there is nothing to ignore. + if (ignoreCells.none()) + { + ignoreCells.clearStorage(); + } + else if (filterCells && topologyFilterType::NONE != topoFilter_) + { + // For refine blocked cells (eg, checking actual cells cut) + isoSurfaceBase isoCutter + ( + mesh_, + cellDistance, + pointDistance_, + distance_ + ); + + if (topologyFilterType::LARGEST_REGION == topoFilter_) + { + refineBlockedCells(ignoreCells, isoCutter); + filterKeepLargestRegion(ignoreCells); + } + else if (topologyFilterType::NEAREST_POINTS == topoFilter_) + { + refineBlockedCells(ignoreCells, isoCutter); + filterKeepNearestRegions(ignoreCells); + } + } + + // Don't need point filter beyond this point + ignoreCellPoints.clearStorage(); + + if (debug) { Pout<< "Writing cell distance:" << cellDistance.objectPath() << endl; @@ -335,13 +632,13 @@ void Foam::distanceSurface::createGeometry() IOobject ( "distanceSurface.pointDistance", - fvm.time().timeName(), - fvm.time(), + fvmesh.time().timeName(), + fvmesh.time(), IOobject::NO_READ, IOobject::NO_WRITE, false ), - pointMesh::New(fvm), + pointMesh::New(fvmesh), dimensionedScalar(dimLength, Zero) ); pDist.primitiveFieldRef() = pointDistance_; @@ -350,60 +647,40 @@ void Foam::distanceSurface::createGeometry() pDist.write(); } - // Don't need ignoreCells if there is nothing to ignore. - if (!ignoreCells.any()) - { - ignoreCells.clear(); - } - - - // This will soon improve (reduced clutter) - - // Direct from cell field and point field. - if (isoParams_.algorithm() == isoSurfaceParams::ALGO_POINT) - { - isoSurfacePtr_.reset - ( - new isoSurfacePoint - ( - cellDistance, - pointDistance_, - distance_, - isoParams_, - ignoreCells - ) - ); - } - else if (isoParams_.algorithm() == isoSurfaceParams::ALGO_CELL) - { - isoSurfaceCell surf + isoSurfacePtr_.reset + ( + isoSurfaceBase::New ( - fvm, + isoParams_, cellDistance, pointDistance_, distance_, - isoParams_, ignoreCells - ); + ) + ); - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_.transfer(surf.meshCells()); - } - else + + // ALGO_POINT still needs cell, point fields (for interpolate) + // The others can do straight transfer + + // But also flatten into a straight transfer for proximity filtering + if + ( + isoParams_.algorithm() != isoSurfaceParams::ALGO_POINT + || topologyFilterType::PROXIMITY == topoFilter_ + ) { - // ALGO_TOPO - isoSurfaceTopo surf - ( - fvm, - cellDistance, - pointDistance_, - distance_, - isoParams_, - ignoreCells - ); + surface_.transfer(static_cast<meshedSurface&>(*isoSurfacePtr_)); + meshCells_.transfer(isoSurfacePtr_->meshCells()); - surface_.transfer(static_cast<meshedSurface&>(surf)); - meshCells_.transfer(surf.meshCells()); + isoSurfacePtr_.reset(nullptr); + cellDistancePtr_.reset(nullptr); + pointDistance_.clear(); + } + + if (topologyFilterType::PROXIMITY == topoFilter_) + { + filterByProximity(); } if (debug) diff --git a/src/sampling/surface/distanceSurface/distanceSurface.H b/src/sampling/surface/distanceSurface/distanceSurface.H index df01b77c6674e27a7cb481a406de9db9aa1bd9bb..22738393291b3783e2b36059adca04144f461a9c 100644 --- a/src/sampling/surface/distanceSurface/distanceSurface.H +++ b/src/sampling/surface/distanceSurface/distanceSurface.H @@ -32,27 +32,82 @@ Description Uses an iso-surface algorithm (cell, topo, point) for constructing the distance surface. + For a zero-distance surface, it performs additional checks and supports + filtering to handle the surface boundaries. + Usage + Example of function object partial specification: + \verbatim + surfaces + { + surface1 + { + type distanceSurface; + surfaceType triSurfaceMesh; + surfaceName something.obj; + topology proximity; + } + + surface2 + { + type distanceSurface; + surfaceType triSurfaceMesh; + surfaceName other.obj; + + topology nearestPoints; + nearestPoints + ( + (0 0 0) + (10 10 0) + ); + + // Max search distance for nearestPoints + maxDistance 0.005; + } + } + \endverbatim + Dictionary controls: \table Property | Description | Required | Default - distance | distance from surface | yes | - signed | Use sign when distance is positive | partly | - isoMethod | Iso-algorithm (cell/topo/point) | no | topo + distance | distance from surface | no | 0 + signed | Use sign when distance is positive | no | true + isoMethod | Iso-algorithm (cell/topo/point) | no | default regularise | Face simplification (enum or bool) | no | true bounds | Limit with bounding box | no | surfaceType | Type of surface | yes | surfaceName | Name of surface in \c triSurface/ | no | dict name + topology | Topology filter (none/largestRegion/nearestPoints/proximity) | no | none + nearestPoints | Points for point-based segmentation | no | + maxDistance | Max search distance for nearestPoints | no | GREAT + absProximity | Max proximity of face centres | no | 1e-5 \endtable + Filtering (for zero-distance only) + + - \c largestRegion (pre-filter): + The cut cells are checked for topological connectivity and the + region with the most number of cut cells is retained. + This handles the "ragged" edge problem. + + - \c nearestPoints (pre-filter): + The cut cells split into regions, the regions closest to the + user-defined points are retained. + Uses \c maxDistance for additional control. + + - \c proximity (post-filter): + Checks the resulting faces against the original search surface + and rejects faces with a distance greater than \c absProximity. + . + Note For distance = 0, some special adjustments. - Always signed (ignoring the input value). - Use normal distance from surface (for better treatment of open edges). - - When the isoSurfaceCell algorithm is used, additional checks for open - surfaces edges are used to limit the extend of resulting distance - surface. The resulting surface elements will not, however, contain - partial cell coverage. + - Additional checks for open surfaces edges are used to limit the extend + of resulting distance surface. + The resulting surface elements will, however, contain partial cell + coverage. NB: Not applicable if the \c point isoMethod is used. The keyword \c cell (bool value) which was use in 1906 and earlier to switch between point/cell algorithms is now ignored (2020-12). @@ -69,9 +124,8 @@ SourceFiles #include "sampledSurface.H" #include "searchableSurface.H" -#include "isoSurfaceCell.H" -#include "isoSurfacePoint.H" -#include "isoSurfaceTopo.H" +#include "isoSurfaceBase.H" +#include "pointIndexHit.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -84,23 +138,53 @@ namespace Foam class distanceSurface { + // Data Types + + //- The type of pre/post face-filtering + enum class topologyFilterType : uint8_t + { + NONE, //!< No additional filtering + LARGEST_REGION, //!< Retain largest region + NEAREST_POINTS, //!< Retain regions nearest to the points + PROXIMITY //!< Post-filter for surface proximity + }; + + //- Names for the topology filter + static const Enum<topologyFilterType> topoFilterNames_; + + // Private Data //- Reference to mesh const polyMesh& mesh_; - //- Surface - const autoPtr<searchableSurface> surfPtr_; + //- Searchable surface + const autoPtr<searchableSurface> geometryPtr_; //- Distance value const scalar distance_; - //- Signed distance - const bool signed_; + //- Distance is zero. Implies signed and additional optimizations + const bool withZeroDistance_; + + //- Use signed distance + const bool withSignDistance_; //- Parameters for iso-surface (algorithm, filter, mergeTol, etc) isoSurfaceParams isoParams_; + //- Optional topology face-filtering + topologyFilterType topoFilter_; + + //- Points for nearest-points segmentation + pointField nearestPoints_; + + //- Max search distance squared (for nearestPoints) + scalar maxDistanceSqr_; + + //- Max distance for proximity check (post-filtering) + scalar absProximity_; + //- Distance to cell centres autoPtr<volScalarField> cellDistancePtr_; @@ -117,7 +201,25 @@ class distanceSurface mutable labelList meshCells_; //- Extracted iso-surface, for interpolators - mutable autoPtr<isoSurfacePoint> isoSurfacePtr_; + mutable autoPtr<isoSurfaceBase> isoSurfacePtr_; + + + // Private Member Functions + + //- Absolute distances from hit points + // Hit/miss checks have been done elsewhere. + static inline void calcAbsoluteDistance + ( + scalarField& distance, + const pointField& points, + const List<pointIndexHit>& nearest + ) + { + forAll(nearest, i) + { + distance[i] = Foam::mag(points[i] - nearest[i].point()); + } + } protected: @@ -147,6 +249,29 @@ protected: } + // Private Member Functions + + //- Re-filter the blocked cells based on the anticipated cuts + // Uses a lightweight variant of cutting. + bool refineBlockedCells + ( + bitSet& ignoreCells, + const isoSurfaceBase& isoContext + ) const; + + //- Prepare blockedFaces for region split + bitSet filterPrepareRegionSplit(const bitSet& ignoreCells) const; + + //- Adjust extracted iso-surface to remove far faces + void filterByProximity(); + + //- Keep region with the most cuts (after region split) + void filterKeepLargestRegion(bitSet& ignoreCells) const; + + //- Keep region(s) closest to the nearest points + void filterKeepNearestRegions(bitSet& ignoreCells) const; + + public: //- Runtime type information @@ -163,6 +288,16 @@ public: const dictionary& dict ); + //- Construct from components with zero-distanced + distanceSurface + ( + const polyMesh& mesh, + const word& surfaceType, + const word& surfaceName, + const isoSurfaceParams& params = isoSurfaceParams(), + const bool interpolate = false + ); + //- Construct from components distanceSurface ( @@ -188,11 +323,11 @@ public: //- The name of the underlying searchableSurface const word& surfaceName() const { - return surfPtr_->name(); + return geometryPtr_->name(); } //- The distance to the underlying searchableSurface - scalar distance() const + scalar distance() const noexcept { return distance_; } diff --git a/src/sampling/surface/distanceSurface/distanceSurfaceFilter.C b/src/sampling/surface/distanceSurface/distanceSurfaceFilter.C new file mode 100644 index 0000000000000000000000000000000000000000..9599058302400ac6dc8ee707ca9b6996880841fd --- /dev/null +++ b/src/sampling/surface/distanceSurface/distanceSurfaceFilter.C @@ -0,0 +1,421 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 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 "distanceSurface.H" +#include "regionSplit.H" +#include "syncTools.H" +#include "vtkSurfaceWriter.H" + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +bool Foam::distanceSurface::refineBlockedCells +( + bitSet& ignoreCells, + const isoSurfaceBase& isoCutter +) const +{ + // With the cell/point distance fields we can take a second pass at + // pre-filtering. + // This duplicates how cut detection is determined in the cell/topo + // algorithms but is fairly inexpensive (creates no geometry) + + bool changed = false; + + for (label celli = 0; celli < mesh_.nCells(); ++celli) + { + if (ignoreCells.test(celli)) + { + continue; + } + + auto cut = isoCutter.getCellCutType(celli); + if (!(cut & isoSurfaceBase::ANYCUT)) + { + ignoreCells.set(celli); + changed = true; + } + } + + return changed; +} + + +Foam::bitSet Foam::distanceSurface::filterPrepareRegionSplit +( + const bitSet& ignoreCells +) const +{ + // Prepare for region split + + bitSet blockedFaces(mesh_.nFaces()); + + const labelList& faceOwn = mesh_.faceOwner(); + const labelList& faceNei = mesh_.faceNeighbour(); + + // Could be more efficient + for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei) + { + // If only one cell is blocked, the face corresponds + // to an exposed subMesh face + + if + ( + ignoreCells.test(faceOwn[facei]) + != ignoreCells.test(faceNei[facei]) + ) + { + blockedFaces.set(facei); + } + } + + for (const polyPatch& patch : mesh_.boundaryMesh()) + { + if (!patch.coupled()) + { + continue; + } + + forAll(patch, patchFacei) + { + const label facei = patch.start() + patchFacei; + if (ignoreCells.test(faceOwn[facei])) + { + blockedFaces.set(facei); + } + } + } + + syncTools::syncFaceList(mesh_, blockedFaces, xorEqOp<unsigned int>()); + + return blockedFaces; +} + + +void Foam::distanceSurface::filterKeepLargestRegion +( + bitSet& ignoreCells +) const +{ + // For region split + bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells)); + + // Split region + regionSplit rs(mesh_, blockedFaces); + blockedFaces.clearStorage(); + + const labelList& regionColour = rs; + + // Identical number of regions on all processors + labelList nCutsPerRegion(rs.nRegions(), Zero); + + // Count cut cells (ie, unblocked) + forAll(regionColour, celli) + { + if (!ignoreCells.test(celli)) + { + ++nCutsPerRegion[regionColour[celli]]; + } + } + + // Sum totals from all processors + Pstream::listCombineGather(nCutsPerRegion, plusEqOp<label>()); + + + // Define which regions to keep + boolList keepRegion(rs.nRegions(), false); + + if (Pstream::master()) + { + const label largest = findMax(nCutsPerRegion); + + if (largest == -1) + { + // Should not happen + keepRegion = true; + } + else + { + keepRegion[largest] = true; + } + + if (debug) + { + Info<< "Had " << sum(nCutsPerRegion) << " cuts, in " + << nCutsPerRegion.size() << " regions, largest is " + << largest << ": " << flatOutput(nCutsPerRegion) << nl; + } + } + + Pstream::scatter(keepRegion); + + forAll(regionColour, celli) + { + if (!keepRegion.test(regionColour[celli])) + { + ignoreCells.set(celli); + } + } +} + + +void Foam::distanceSurface::filterKeepNearestRegions +( + bitSet& ignoreCells +) const +{ + if (nearestPoints_.empty()) + { + WarningInFunction + << "Ignoring nearestPoints - no points provided" << nl + << endl; + return; + } + + // For region split + bitSet blockedFaces(filterPrepareRegionSplit(ignoreCells)); + + // Split region + regionSplit rs(mesh_, blockedFaces); + blockedFaces.clearStorage(); + + const labelList& regionColour = rs; + + const pointField& cc = mesh_.cellCentres(); + const pointField& nearPts = nearestPoints_; + + // The magSqr distance and region + typedef Tuple2<scalar, label> nearInfo; + List<nearInfo> nearest(nearPts.size(), nearInfo(GREAT, -1)); + + // Also collect cuts per region, may be useful for rejecting + // small regions. Code as per filterKeepLargestRegion + labelList nCutsPerRegion(rs.nRegions(), Zero); + + forAll(cc, celli) + { + if (ignoreCells.test(celli)) + { + continue; + } + + const point& pt = cc[celli]; + const label regioni = regionColour[celli]; + + ++nCutsPerRegion[regioni]; + + label pointi = 0; + for (nearInfo& near : nearest) + { + const scalar distSqr = magSqr(nearPts[pointi] - pt); + ++pointi; + + if (distSqr < near.first()) + { + near.first() = distSqr; + near.second() = regioni; + } + } + } + + // Sum totals from all processors + Pstream::listCombineGather(nCutsPerRegion, plusEqOp<label>()); + + // Get nearest + Pstream::listCombineGather(nearest, minFirstEqOp<scalar>()); + + + // Define which regions to keep + + boolList keepRegion(rs.nRegions(), false); + + if (Pstream::master()) + { + const label largest = findMax(nCutsPerRegion); + + for (const nearInfo& near : nearest) + { + const scalar distSqr = near.first(); + const label regioni = near.second(); + + if (regioni != -1 && distSqr < maxDistanceSqr_) + { + keepRegion[regioni] = true; + } + } + + if (debug) + { + Info<< "Had " << sum(nCutsPerRegion) << " cuts, in " + << nCutsPerRegion.size() << " regions, largest is " + << largest << ": " << flatOutput(nCutsPerRegion) << nl; + + Info<< "nearestPoints (max distance = " + << sqrt(maxDistanceSqr_) << ")" << nl; + + forAll(nearPts, pointi) + { + const scalar dist = sqrt(nearest[pointi].first()); + const label regioni = nearest[pointi].second(); + + Info<< " " << nearPts[pointi] << " region " + << regioni << " distance " + << dist; + + if (!keepRegion.test(regioni)) + { + Info<< " too far"; + } + Info<< nl; + } + } + } + + Pstream::scatter(keepRegion); + + forAll(regionColour, celli) + { + if (!keepRegion.test(regionColour[celli])) + { + ignoreCells.set(celli); + } + } +} + + +void Foam::distanceSurface::filterByProximity() +{ + const searchableSurface& geom = geometryPtr_(); + + // Filtering for faces + const pointField& fc = surface_.faceCentres(); + + bitSet faceSelection(fc.size()); + label nTrimmed = 0; + + + // For each face + scalarField faceDistance(fc.size(), GREAT); + scalarField faceNormalDistance; // Debugging only + { + List<pointIndexHit> nearest; + geom.findNearest + ( + fc, + // Use initialized field (GREAT) to limit search too + faceDistance, + nearest + ); + calcAbsoluteDistance(faceDistance, fc, nearest); + + // Heavier debugging + if (debug & 4) + { + vectorField norms; + geom.getNormal(nearest, norms); + + faceNormalDistance.resize(fc.size()); + + forAll(nearest, i) + { + const vector diff(fc[i] - nearest[i].point()); + + faceNormalDistance[i] = Foam::mag((diff & norms[i])); + } + } + } + + // Note + // Proximity checks using the face points (nearest hit) to establish + // a length scale are too fragile. Can easily have stretched faces + // where the centre is less than say 0.3-0.5 of the centre-point distance + // but they are still outside. + + // Using the absolute proximity of the face centres is more robust. + + + // Consider the absolute proximity of the face centres + forAll(faceDistance, facei) + { + if (faceDistance[facei] <= absProximity_) + { + faceSelection.set(facei); + } + else + { + ++nTrimmed; + + if (debug & 2) + { + Pout<< "trim reject: " + << faceDistance[facei] << nl; + } + } + } + + + // Heavier debugging + if (debug & 4) + { + labelField faceFilterStatus(faceSelection.size(), Zero); + + for (const label facei : faceSelection) + { + faceFilterStatus[facei] = 1; + } + + const fileName outputName(surfaceName() + "-proximity-filter"); + + Info<< "Writing debug surface: " << outputName << nl; + + surfaceWriters::vtkWriter writer + ( + surface_.points(), + surface_, // faces + outputName + ); + + writer.write("absolute-distance", faceDistance); + writer.write("normal-distance", faceNormalDistance); + writer.write("filter-state", faceFilterStatus); + } + + + if (returnReduce(nTrimmed, sumOp<label>()) != 0) + { + labelList pointMap, faceMap; + meshedSurface filtered + ( + surface_.subsetMesh(faceSelection, pointMap, faceMap) + ); + surface_.transfer(filtered); + + meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); + } +} + + +// ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceBase.C b/src/sampling/surface/isoSurface/isoSurfaceBase.C index b8546926e8161e3e0b90ce49c85f65af88cd4a74..d317bc74ddd76285f3e2d73264f12e5578a45dc0 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceBase.C +++ b/src/sampling/surface/isoSurface/isoSurfaceBase.C @@ -26,19 +26,292 @@ License \*---------------------------------------------------------------------------*/ #include "isoSurfaceBase.H" +#include "polyMesh.H" +#include "tetMatcher.H" +#include "cyclicACMIPolyPatch.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "isoSurfaceBaseMethods.H" +defineIsoSurfaceInterpolateMethods(Foam::isoSurfaceBase); + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Test face for edge cuts +inline static bool isFaceCut +( + const scalar isoval, + const scalarField& pointValues, + const labelUList& indices +) +{ + auto iter = indices.cbegin(); + const auto last = indices.cend(); + + // if (iter == last) return false; // ie, indices.empty() + + const bool aLower = (pointValues[*iter] < isoval); + ++iter; + + while (iter != last) + { + if (aLower != (pointValues[*iter] < isoval)) + { + return true; + } + ++iter; + } + + return false; +} + +} // End namespace Foam + // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // Foam::isoSurfaceBase::isoSurfaceBase ( + const polyMesh& mesh, + const scalarField& cellValues, + const scalarField& pointValues, const scalar iso, const isoSurfaceParams& params ) : meshedSurface(), isoSurfaceParams(params), - iso_(iso) + mesh_(mesh), + cVals_(cellValues), + pVals_(pointValues), + iso_(iso), + ignoreBoundaryFaces_(), + meshCells_() {} +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::isoSurfaceBase::ignoreCyclics() +{ + // Determine boundary pyramids to ignore (originating from ACMI faces) + // Maybe needs to be optional argument or more general detect colocated + // faces. + + for (const polyPatch& pp : mesh_.boundaryMesh()) + { + if (isA<cyclicACMIPolyPatch>(pp)) + { + ignoreBoundaryFaces_.set(labelRange(pp.offset(), pp.size())); + } + } +} + + +Foam::label Foam::isoSurfaceBase::countCutType +( + const UList<cutType>& cuts, + const uint8_t maskValue +) +{ + label count = 0; + + for (const cutType cut : cuts) + { + if (maskValue ? (cut & maskValue) != 0 : !cut) + { + ++count; + } + } + + return count; +} + + +void Foam::isoSurfaceBase::resetCuts(UList<cutType>& cuts) +{ + for (cutType& cut : cuts) + { + if (cut != cutType::BLOCKED) + { + cut = cutType::UNVISITED; + } + } +} + + +Foam::label Foam::isoSurfaceBase::blockCells +( + UList<cutType>& cuts, + const bitSet& ignoreCells +) const +{ + label count = 0; + + for (const label celli : ignoreCells) + { + if (celli >= cuts.size()) + { + break; + } + + cuts[celli] = cutType::BLOCKED; + ++count; + } + + return count; +} + + +Foam::label Foam::isoSurfaceBase::blockCells +( + UList<cutType>& cuts, + const boundBox& bb, + const volumeType::type volType +) const +{ + label count = 0; + + // Mark cells inside/outside bounding box as blocked + const bool keepInside = (volType == volumeType::INSIDE); + + if (!keepInside && volType != volumeType::OUTSIDE) + { + // Could warn about invalid... + } + else if (bb.valid()) + { + const pointField& cc = mesh_.cellCentres(); + + forAll(cuts, celli) + { + if + ( + cuts[celli] == cutType::UNVISITED + && (bb.contains(cc[celli]) ? keepInside : !keepInside) + ) + { + cuts[celli] = cutType::BLOCKED; + ++count; + } + } + } + + return count; +} + + +Foam::label Foam::isoSurfaceBase::calcCellCuts(List<cutType>& cuts) const +{ + // Don't consider SPHERE cuts in the total number of cells cut + constexpr uint8_t realCut(cutType::CUT | cutType::TETCUT); + + cuts.resize(mesh_.nCells(), cutType::UNVISITED); + + label nCuts = 0; + forAll(cuts, celli) + { + if (cuts[celli] == cutType::UNVISITED) + { + cuts[celli] = getCellCutType(celli); + + if ((cuts[celli] & realCut) != 0) + { + ++nCuts; + } + } + } + + return nCuts; +} + + +Foam::isoSurfaceBase::cutType +Foam::isoSurfaceBase::getFaceCutType(const label facei) const +{ + return + ( + ( + mesh_.isInternalFace(facei) + || !ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces()) + ) + && isFaceCut(iso_, pVals_, mesh_.faces()[facei]) + ) ? cutType::CUT : cutType::NOTCUT; +} + + +Foam::isoSurfaceBase::cutType +Foam::isoSurfaceBase::getCellCutType(const label celli) const +{ + // Tet version + if (tetMatcher::test(mesh_, celli)) + { + for (const label facei : mesh_.cells()[celli]) + { + if + ( + !mesh_.isInternalFace(facei) + && ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces()) + ) + { + continue; + } + + if (isFaceCut(iso_, pVals_, mesh_.faces()[facei])) + { + return cutType::TETCUT; + } + } + + return cutType::NOTCUT; + } + + + // Regular cell + label nPyrEdges = 0; + label nPyrCuts = 0; + + const bool cellLower = (cVals_[celli] < iso_); + + for (const label facei : mesh_.cells()[celli]) + { + if + ( + !mesh_.isInternalFace(facei) + && ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces()) + ) + { + continue; + } + + const face& f = mesh_.faces()[facei]; + + // Count pyramid edges cut + for (const label pointi : f) + { + ++nPyrEdges; + + if (cellLower != (pVals_[pointi] < iso_)) + { + ++nPyrCuts; + } + } + } + + if (nPyrCuts == 0) + { + return cutType::NOTCUT; + } + + // If all pyramid edges are cut (no outside faces), + // it is a sphere cut + + return (nPyrCuts == nPyrEdges) ? cutType::SPHERE : cutType::CUT; +} + + // ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceBase.H b/src/sampling/surface/isoSurface/isoSurfaceBase.H index 3229f002f578f19671e7c85df1d1c8c0a86703c4..3e29657bd111202d18d4016a15835396a8515874 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceBase.H +++ b/src/sampling/surface/isoSurface/isoSurfaceBase.H @@ -29,15 +29,14 @@ Class Description Low-level components common to various iso-surface algorithms. - Some common dictionary properties: - \table - Property | Description | Required | Default - isoAlgorithm | (cell/topo/point) | no | topo - regularise | point snapping (bool or enum) | no | true - \endtable +Note + The interpolation samplers currently require a volField for the cell + values. This is largely a restriction imposed by the point algorithm + and may be revised in the future. SourceFiles isoSurfaceBase.C + isoSurfaceBaseNew.C \*---------------------------------------------------------------------------*/ @@ -45,7 +44,10 @@ SourceFiles #define isoSurfaceBase_H #include "isoSurfaceParams.H" -#include "scalar.H" +#include "bitSet.H" +#include "scalarField.H" +#include "volumeType.H" +#include "volFieldsFwd.H" #include "MeshedSurface.H" #include "MeshedSurfacesFwd.H" @@ -55,6 +57,7 @@ namespace Foam { // Forward Declarations +class polyMesh; class tetCell; /*---------------------------------------------------------------------------*\ @@ -66,16 +69,58 @@ class isoSurfaceBase public meshedSurface, public isoSurfaceParams { +public: + + // Data Types + + //- The type of cell/face cuts + enum cutType : uint8_t + { + NOTCUT = 0, //!< Not cut + CUT = 0x1, //!< Normal cut + TETCUT = 0x2, //!< Cell cut is a tet + SPHERE = 0x4, //!< All edges to cell centre cut + ANYCUT = 0xF, //!< Any cut type (bitmask) + + UNVISITED = 0x10, //!< Unvisited + BLOCKED = 0x20, //!< Blocked (never cut) + SPECIAL = 0xF0 //!< Bitmask for specials + }; + + protected: // Protected typedefs for convenience typedef meshedSurface Mesh; + // Typedef for code transition + typedef cutType cellCutType; + + // Protected Data + //- Reference to mesh + const polyMesh& mesh_; + + //- Cell values + const scalarField& cVals_; + + //- Point values + const scalarField& pVals_; + //- Iso value const scalar iso_; + + // Controls, restrictions + + //- Optional boundary faces to ignore. + // Eg, Used to exclude cyclicACMI (since duplicate faces) + bitSet ignoreBoundaryFaces_; + + + // Sampling information + //- For every face, the original cell in mesh labelList meshCells_; @@ -102,21 +147,88 @@ protected: ); } + //- Count the number of cuts matching the mask type + // Checks as bitmask or as zero. + static label countCutType + ( + const UList<cutType>& cuts, + const uint8_t maskValue + ); + + //- Dummy templated interpolate method + template<class Type> + tmp<Field<Type>> interpolateTemplate + ( + const GeometricField<Type, fvPatchField, volMesh>& cellValues, + const Field<Type>& pointValues + ) const + { + return nullptr; + } + + //- No copy construct + isoSurfaceBase(const isoSurfaceBase&) = delete; + + //- No copy assignment + void operator=(const isoSurfaceBase&) = delete; + public: + // Typedefs for code transition + using isoSurfaceParams::algorithmType; + using isoSurfaceParams::filterType; + + // Constructors - //- Create for specified algorithm type/filter and iso-value + //- Construct with mesh, cell/point values and iso-value isoSurfaceBase ( + const polyMesh& mesh, + const scalarField& cellValues, + const scalarField& pointValues, const scalar iso, const isoSurfaceParams& params = isoSurfaceParams() ); + // Selector + + //- Create for specified algorithm type + // Currently uses hard-code lookups based in isoSurfaceParams + static autoPtr<isoSurfaceBase> New + ( + const isoSurfaceParams& params, + const volScalarField& cellValues, + const scalarField& pointValues, + const scalar iso, + const bitSet& ignoreCells = bitSet() + ); + + // Member Functions + // Access, Edit + + //- The mesh for which the iso-surface is associated + const polyMesh& mesh() const noexcept + { + return mesh_; + } + + //- The mesh cell values used for creating the iso-surface + const scalarField& cellValues() const noexcept + { + return cVals_; + } + + //- The mesh point values used for creating the iso-surface + const scalarField& pointValues() const noexcept + { + return pVals_; + } + //- The iso-value associated with the surface scalar isoValue() const noexcept { @@ -134,6 +246,63 @@ public: { return meshCells_; } + + + // Helpers + + //- Restore non-BLOCKED state to an UNVISITED state + static void resetCuts(UList<cutType>& cuts); + + //- Mark ignoreCells as BLOCKED + label blockCells + ( + UList<cutType>& cuts, + const bitSet& ignoreCells + ) const; + + //- Mark cells inside/outside a (valid) bound box as BLOCKED + // The volType is INSIDE or OUTSIDE only + label blockCells + ( + UList<cutType>& cuts, + const boundBox& bb, + const volumeType::type volType + ) const; + + + // Cutting + + //- Set ignoreBoundaryFaces to ignore cyclics (cyclicACMI) + void ignoreCyclics(); + + //- Populate a list of candidate cell cuts using getCellCutType() + label calcCellCuts(List<cutType>& cuts) const; + + //- Determine face cut for an individual face + cutType getFaceCutType(const label facei) const; + + //- Cell cut for an individual cell, with special handling + //- for TETCUT and SPHERE cuts + cutType getCellCutType(const label celli) const; + + + // Sampling + +#undef declareIsoSurfaceInterpolateMethod +#define declareIsoSurfaceInterpolateMethod(Type) \ + /*! \brief interpolate Type cellValues, pointValues on iso-surface */ \ + virtual tmp<Field<Type>> \ + interpolate \ + ( \ + const GeometricField<Type, fvPatchField, volMesh>& cellValues, \ + const Field<Type>& pointValues \ + ) const; + + declareIsoSurfaceInterpolateMethod(scalar); + declareIsoSurfaceInterpolateMethod(vector); + declareIsoSurfaceInterpolateMethod(sphericalTensor); + declareIsoSurfaceInterpolateMethod(symmTensor); + declareIsoSurfaceInterpolateMethod(tensor); }; diff --git a/src/sampling/surface/isoSurface/isoSurfaceBaseMethods.H b/src/sampling/surface/isoSurface/isoSurfaceBaseMethods.H new file mode 100644 index 0000000000000000000000000000000000000000..9b14913866b115821ee0b20264400eae5ffd8425 --- /dev/null +++ b/src/sampling/surface/isoSurface/isoSurfaceBaseMethods.H @@ -0,0 +1,66 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 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/>. + +InClass + Foam::isoSurfaceBaseMethods + +Description + Convenience macros for instantiating iso-surface interpolate methods. + +\*---------------------------------------------------------------------------*/ + +#ifndef isoSurfaceBaseMethods_H +#define isoSurfaceBaseMethods_H + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// Instantiate templated method for standard types +// See note in isoSurfaceBase explaining why these are volume fields + +#undef defineIsoSurfaceInterpolateMethod +#define defineIsoSurfaceInterpolateMethod(ThisClass, Type) \ + Foam::tmp<Foam::Field<Type>> ThisClass::interpolate \ + ( \ + const GeometricField<Type, fvPatchField, volMesh>& cellValues, \ + const Field<Type>& pointValues \ + ) const \ + { \ + return interpolateTemplate(cellValues, pointValues); \ + } + + +#define defineIsoSurfaceInterpolateMethods(ThisClass) \ + defineIsoSurfaceInterpolateMethod(ThisClass, Foam::scalar); \ + defineIsoSurfaceInterpolateMethod(ThisClass, Foam::vector); \ + defineIsoSurfaceInterpolateMethod(ThisClass, Foam::sphericalTensor); \ + defineIsoSurfaceInterpolateMethod(ThisClass, Foam::symmTensor); \ + defineIsoSurfaceInterpolateMethod(ThisClass, Foam::tensor) + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceBaseNew.C b/src/sampling/surface/isoSurface/isoSurfaceBaseNew.C new file mode 100644 index 0000000000000000000000000000000000000000..b9b60c3f0a0d86d5b1fe57995d8ddecf9b57d8a4 --- /dev/null +++ b/src/sampling/surface/isoSurface/isoSurfaceBaseNew.C @@ -0,0 +1,107 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2020 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 "isoSurfaceBase.H" +#include "isoSurfaceCell.H" +#include "isoSurfacePoint.H" +#include "isoSurfaceTopo.H" + +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + +Foam::autoPtr<Foam::isoSurfaceBase> +Foam::isoSurfaceBase::New +( + const isoSurfaceParams& params, + const volScalarField& cellValues, + const scalarField& pointValues, + const scalar iso, + const bitSet& ignoreCells +) +{ + autoPtr<isoSurfaceBase> ptr; + + if (params.algorithm() == isoSurfaceParams::ALGO_POINT) + { + ptr.reset + ( + new isoSurfacePoint + ( + /* fvMesh implicit from cellValues */ + cellValues, + pointValues, + iso, + params, + ignoreCells // unused + ) + ); + } + else if (params.algorithm() == isoSurfaceParams::ALGO_CELL) + { + ptr.reset + ( + new isoSurfaceCell + ( + cellValues.mesh(), // polyMesh + cellValues.primitiveField(), + pointValues, + iso, + params, + ignoreCells + ) + ); + } + else + { + // ALGO_TOPO, ALGO_DEFAULT + ptr.reset + ( + new isoSurfaceTopo + ( + cellValues.mesh(), // polyMesh + cellValues.primitiveField(), + pointValues, + iso, + params, + ignoreCells + ) + ); + } + + // Cannot really fail (with current logic) + // if (!ptr) + // { + // FatalErrorInFunction + // << "Unknown iso-surface algorithm (" + // << int(params.algorithm()) << ")\n" + // << exit(FatalError); + // } + + return ptr; +} + + +// ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceCell.C b/src/sampling/surface/isoSurface/isoSurfaceCell.C index b1ee8ca0b266c07fd8a980ae5fd4ca25f0bcf51b..bc637cc6949ee9c90e0ae5be823ce1567d93d277 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceCell.C +++ b/src/sampling/surface/isoSurface/isoSurfaceCell.C @@ -38,34 +38,17 @@ License #include "Time.H" #include "triPoints.H" -// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -namespace Foam -{ - defineTypeNameAndDebug(isoSurfaceCell, 0); -} +#include "isoSurfaceBaseMethods.H" +defineIsoSurfaceInterpolateMethods(Foam::isoSurfaceCell); -// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { - // Does any edge of triangle cross iso value? - inline static bool isTriCut - ( - const label a, - const label b, - const label c, - const scalarField& pointValues, - const scalar isoValue - ) - { - const bool aLower = (pointValues[a] < isoValue); - const bool bLower = (pointValues[b] < isoValue); - const bool cLower = (pointValues[c] < isoValue); - - return !(aLower == bLower && aLower == cLower); - } + defineTypeNameAndDebug(isoSurfaceCell, 0); } @@ -88,154 +71,6 @@ Foam::scalar Foam::isoSurfaceCell::isoFraction } -Foam::isoSurfaceCell::cellCutType Foam::isoSurfaceCell::calcCutType -( - const bitSet& isTet, - const scalarField& cellValues, - const scalarField& pointValues, - const label celli -) const -{ - if (ignoreCells_.test(celli)) - { - return NOTCUT; - } - - const cell& cFaces = mesh_.cells()[celli]; - - if (isTet.test(celli)) - { - for (const label facei : cFaces) - { - const face& f = mesh_.faces()[facei]; - - for (label fp = 1; fp < f.size() - 1; ++fp) - { - if (isTriCut(f[0], f[fp], f[f.fcIndex(fp)], pointValues, iso_)) - { - return CUT; - } - } - } - return NOTCUT; - } - - - const bool cellLower = (cellValues[celli] < iso_); - - // First check if there is any cut in cell - bool edgeCut = false; - - for (const label facei : cFaces) - { - const face& f = mesh_.faces()[facei]; - - // Check pyramid edges (corner point to cell centre) - for (const label pointi : f) - { - if (cellLower != (pointValues[pointi] < iso_)) - { - edgeCut = true; - break; - } - } - - if (edgeCut) - { - break; - } - - // Check (triangulated) face edges - // With fallback for problem decompositions - const label fp0 = - (mesh_.tetBasePtIs()[facei] < 0 ? 0 : mesh_.tetBasePtIs()[facei]); - - label fp = f.fcIndex(fp0); - for (label i = 2; i < f.size(); ++i) - { - const label nextFp = f.fcIndex(fp); - - if (isTriCut(f[fp0], f[fp], f[nextFp], pointValues, iso_)) - { - edgeCut = true; - break; - } - - fp = nextFp; - } - - if (edgeCut) - { - break; - } - } - - - if (edgeCut) - { - // Count actual cuts (expensive since addressing needed) - // Note: not needed if you don't want to preserve maxima/minima - // centred around cellcentre. In that case just always return CUT - - const labelList& cPoints = mesh_.cellPoints(celli); - - label nPyrCuts = 0; - - for (const label pointi : cPoints) - { - if ((pointValues[pointi] < iso_) != cellLower) - { - ++nPyrCuts; - } - } - - if (nPyrCuts == cPoints.size()) - { - return SPHERE; - } - else if (nPyrCuts) - { - // There is a pyramid edge cut. E.g. lopping off a tet from a corner - return CUT; - } - } - - return NOTCUT; -} - - -void Foam::isoSurfaceCell::calcCutTypes -( - const bitSet& isTet, - const scalarField& cVals, - const scalarField& pVals -) -{ - cellCutType_.setSize(mesh_.nCells()); - nCutCells_ = 0; - - // Some processor domains may require tetBasePtIs and others do not. - // Do now to ensure things stay synchronized. - (void)mesh_.tetBasePtIs(); - - forAll(cellCutType_, celli) - { - cellCutType_[celli] = calcCutType(isTet, cVals, pVals, celli); - - if (cellCutType_[celli] == CUT) - { - ++nCutCells_; - } - } - - if (debug) - { - Pout<< "isoSurfaceCell : candidate cut cells " - << nCutCells_ << " / " << mesh_.nCells() << endl; - } -} - - Foam::labelPair Foam::isoSurfaceCell::findCommonPoints ( const labelledTri& tri0, @@ -402,7 +237,7 @@ void Foam::isoSurfaceCell::calcSnappedCc forAll(mesh_.cells(), celli) { - if (cellCutType_[celli] == CUT && !isTet.test(celli)) + if (cellCutType_[celli] == cutType::CUT) // No tet cuts { const scalar cVal = cVals[celli]; @@ -709,18 +544,21 @@ void Foam::isoSurfaceCell::calcSnappedPoint labelList& snappedPoint ) const { + const labelList& faceOwn = mesh_.faceOwner(); + const labelList& faceNei = mesh_.faceNeighbour(); + // Determine if point is on boundary. Points on boundaries are never // snapped. Coupled boundaries are handled explicitly so not marked here. bitSet isBoundaryPoint(mesh_.nPoints()); const polyBoundaryMesh& patches = mesh_.boundaryMesh(); + for (const polyPatch& pp : patches) { if (!pp.coupled()) { - label facei = pp.start(); - forAll(pp, i) + for (const label facei : pp.range()) { - const face& f = mesh_.faces()[facei++]; + const face& f = mesh_.faces()[facei]; isBoundaryPoint.set(f); } @@ -739,6 +577,8 @@ void Foam::isoSurfaceCell::calcSnappedPoint forAll(mesh_.pointFaces(), pointi) { + constexpr uint8_t realCut(cutType::CUT | cutType::TETCUT); + if (isBoundaryPoint.test(pointi)) { continue; @@ -752,10 +592,10 @@ void Foam::isoSurfaceCell::calcSnappedPoint { if ( - cellCutType_[mesh_.faceOwner()[facei]] == CUT + (cellCutType_[faceOwn[facei]] & realCut) != 0 || ( mesh_.isInternalFace(facei) - && cellCutType_[mesh_.faceNeighbour()[facei]] == CUT + && (cellCutType_[faceNei[facei]] & realCut) != 0 ) ) { @@ -777,7 +617,7 @@ void Foam::isoSurfaceCell::calcSnappedPoint for (const label facei : pFaces) { - const label own = mesh_.faceOwner()[facei]; + const label own = faceOwn[facei]; if (isTet.test(own)) { @@ -803,7 +643,7 @@ void Foam::isoSurfaceCell::calcSnappedPoint if (mesh_.isInternalFace(facei)) { - const label nei = mesh_.faceNeighbour()[facei]; + const label nei = faceNei[facei]; if (isTet.test(nei)) { @@ -1298,12 +1138,9 @@ Foam::isoSurfaceCell::isoSurfaceCell const bitSet& ignoreCells ) : - isoSurfaceBase(iso, params), - mesh_(mesh), - cVals_(cellValues), - pVals_(pointValues), - ignoreCells_(ignoreCells), - mergeDistance_(params.mergeTol()*mesh.bounds().mag()) + isoSurfaceBase(mesh, cellValues, pointValues, iso, params), + mergeDistance_(params.mergeTol()*mesh.bounds().mag()), + cellCutType_(mesh.nCells(), cutType::UNVISITED) { const bool regularise = (params.filter() != filterType::NONE); @@ -1317,15 +1154,27 @@ Foam::isoSurfaceCell::isoSurfaceCell << " mergeTol : " << params.mergeTol() << nl << " mesh span : " << mesh.bounds().mag() << nl << " mergeDistance : " << mergeDistance_ << nl - << " ignoreCells : " << ignoreCells_.count() + << " ignoreCells : " << ignoreCells.count() << " / " << cVals_.size() << nl << endl; } - // Determine if cell is tet + + label nBlockedCells = 0; + + // Mark ignoreCells as blocked + nBlockedCells += blockCells(cellCutType_, ignoreCells); + + + // Some processor domains may require tetBasePtIs and others do not. + // Do now to ensure things stay synchronized. + (void)mesh_.tetBasePtIs(); + + + // Calculate a tet/non-tet filter bitSet isTet(mesh_.nCells()); { - forAll(isTet, celli) + for (label celli = 0; celli < mesh_.nCells(); ++celli) { if (tetMatcher::test(mesh_, celli)) { @@ -1334,26 +1183,33 @@ Foam::isoSurfaceCell::isoSurfaceCell } } + // Determine cell cuts + nCutCells_ = calcCellCuts(cellCutType_); - // Determine if any cut through cell - calcCutTypes(isTet, cellValues, pointValues); + if (debug) + { + Pout<< "isoSurfaceCell : candidate cells cut " + << nCutCells_ + << " blocked " << nBlockedCells + << " total " << mesh_.nCells() << endl; + } if (debug && isA<fvMesh>(mesh)) { - const fvMesh& fvm = dynamicCast<const fvMesh&>(mesh); + const fvMesh& fvmesh = dynamicCast<const fvMesh&>(mesh); volScalarField debugField ( IOobject ( "isoSurfaceCell.cutType", - fvm.time().timeName(), - fvm.time(), + fvmesh.time().timeName(), + fvmesh.time(), IOobject::NO_READ, IOobject::NO_WRITE, false ), - fvm, + fvmesh, dimensionedScalar(dimless, Zero) ); @@ -1614,4 +1470,5 @@ Foam::isoSurfaceCell::isoSurfaceCell } } + // ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceCell.H b/src/sampling/surface/isoSurface/isoSurfaceCell.H index 592cb7aaf1f4e0db0ebad2639bd00b810121b42a..56ef23d34bf011bc27b05337605d3dda8b9a35ed 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceCell.H +++ b/src/sampling/surface/isoSurface/isoSurfaceCell.H @@ -50,7 +50,6 @@ SourceFiles #include "labelPair.H" #include "pointIndexHit.H" -#include "bitSet.H" #include "isoSurfaceBase.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -59,8 +58,6 @@ namespace Foam { // Forward Declarations - -class polyMesh; class triSurface; /*---------------------------------------------------------------------------*\ @@ -71,38 +68,13 @@ class isoSurfaceCell : public isoSurfaceBase { - // Private data - - enum segmentCutType - { - NEARFIRST, //!< Intersection close to e.first() - NEARSECOND, //!< Intersection close to e.second() - NOTNEAR //!< No intersection - }; - - enum cellCutType - { - NOTCUT, //!< Cell not cut - SPHERE, //!< All edges to cell centre cut - CUT //!< Normal cut - }; - - - //- Reference to mesh - const polyMesh& mesh_; - - const scalarField& cVals_; - - const scalarField& pVals_; - - //- Optional cells to ignore - const bitSet& ignoreCells_; + // Private Data //- When to merge points const scalar mergeDistance_; - //- Whether cell might be cut - List<cellCutType> cellCutType_; + //- The cell cut type + List<cutType> cellCutType_; //- Estimated number of cut cells label nCutCells_; @@ -123,28 +95,7 @@ class isoSurfaceCell // Private Member Functions //- Get location of iso value as fraction inbetween s0,s1 - scalar isoFraction - ( - const scalar s0, - const scalar s1 - ) const; - - - //- Determine whether cell is cut - cellCutType calcCutType - ( - const bitSet&, - const scalarField& cellValues, - const scalarField& pointValues, - const label - ) const; - - void calcCutTypes - ( - const bitSet&, - const scalarField& cellValues, - const scalarField& pointValues - ); + scalar isoFraction(const scalar s0, const scalar s1) const; //- Return the two common points between two triangles static labelPair findCommonPoints @@ -166,7 +117,7 @@ class isoSurfaceCell ) const; //- Determine per cc whether all near cuts can be snapped to single - // point. + //- point. void calcSnappedCc ( const bitSet& isTet, @@ -301,8 +252,17 @@ class isoSurfaceCell ); -public: + // Sampling + //- Interpolates cCoords, pCoords. + template<class Type> + tmp<Field<Type>> interpolateTemplate + ( + const Field<Type>& cCoords, + const Field<Type>& pCoords + ) const; + +public: //- Runtime type information TypeName("isoSurfaceCell"); @@ -328,15 +288,17 @@ public: ); - // Member Functions + //- Destructor + virtual ~isoSurfaceCell() = default; - //- Interpolates cCoords, pCoords. - template<class Type> - tmp<Field<Type>> interpolate - ( - const Field<Type>& cCoords, - const Field<Type>& pCoords - ) const; + + // Sampling + + declareIsoSurfaceInterpolateMethod(scalar); + declareIsoSurfaceInterpolateMethod(vector); + declareIsoSurfaceInterpolateMethod(sphericalTensor); + declareIsoSurfaceInterpolateMethod(symmTensor); + declareIsoSurfaceInterpolateMethod(tensor); }; diff --git a/src/sampling/surface/isoSurface/isoSurfaceCellTemplates.C b/src/sampling/surface/isoSurface/isoSurfaceCellTemplates.C index 71f033cd767b6aa08146f0bfac46cfa6e427d2db..a1db602648e902ea0d851eb00d7bfe269be9fe4a 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceCellTemplates.C +++ b/src/sampling/surface/isoSurface/isoSurfaceCellTemplates.C @@ -175,7 +175,7 @@ void Foam::isoSurfaceCell::generateTriPoints if (triIndex == 0x0C) { // Flip normals - label sz = pts.size(); + const label sz = pts.size(); Swap(pts[sz-5], pts[sz-4]); Swap(pts[sz-2], pts[sz-1]); } @@ -285,7 +285,7 @@ void Foam::isoSurfaceCell::generateTriPoints forAll(mesh_.cells(), celli) { - if (cellCutType_[celli] != NOTCUT) + if ((cellCutType_[celli] & cutType::ANYCUT) != 0) { label oldNPoints = triPoints.size(); @@ -469,7 +469,7 @@ void Foam::isoSurfaceCell::generateTriPoints template<class Type> Foam::tmp<Foam::Field<Type>> -Foam::isoSurfaceCell::interpolate +Foam::isoSurfaceCell::interpolateTemplate ( const Field<Type>& cCoords, const Field<Type>& pCoords diff --git a/src/sampling/surface/isoSurface/isoSurfacePoint.C b/src/sampling/surface/isoSurface/isoSurfacePoint.C index 9cc8eada00ef11e573650ec8a94c6fa0394a7251..64be6e5e966209e37ce3d2ed2a1ee61c1f2a488c 100644 --- a/src/sampling/surface/isoSurface/isoSurfacePoint.C +++ b/src/sampling/surface/isoSurface/isoSurfacePoint.C @@ -34,6 +34,12 @@ License #include "triSurface.H" #include "triPoints.H" +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#include "isoSurfaceBaseMethods.H" +defineIsoSurfaceInterpolateMethods(Foam::isoSurfacePoint); + + // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam @@ -384,8 +390,8 @@ void Foam::isoSurfacePoint::calcCutTypes const labelList& own = mesh_.faceOwner(); const labelList& nei = mesh_.faceNeighbour(); - faceCutType_.setSize(mesh_.nFaces()); - faceCutType_ = NOTCUT; + faceCutType_.resize(mesh_.nFaces()); + faceCutType_ = cutType::NOTCUT; for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei) { @@ -409,7 +415,7 @@ void Foam::isoSurfacePoint::calcCutTypes if (ownLower != neiLower) { - faceCutType_[facei] = CUT; + faceCutType_[facei] = cutType::CUT; } else { @@ -419,7 +425,7 @@ void Foam::isoSurfacePoint::calcCutTypes if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower)) { - faceCutType_[facei] = CUT; + faceCutType_[facei] = cutType::CUT; } } } @@ -448,7 +454,7 @@ void Foam::isoSurfacePoint::calcCutTypes if (ownLower != neiLower) { - faceCutType_[facei] = CUT; + faceCutType_[facei] = cutType::CUT; } else { @@ -458,34 +464,34 @@ void Foam::isoSurfacePoint::calcCutTypes if (isEdgeOfFaceCut(pVals, f, ownLower, neiLower)) { - faceCutType_[facei] = CUT; + faceCutType_[facei] = cutType::CUT; } } } } nCutCells_ = 0; - cellCutType_.setSize(mesh_.nCells()); - cellCutType_ = NOTCUT; + cellCutType_.resize(mesh_.nCells()); + cellCutType_ = cutType::NOTCUT; // Propagate internal face cuts into the cells. for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei) { - if (faceCutType_[facei] == NOTCUT) + if (faceCutType_[facei] == cutType::NOTCUT) { continue; } - if (cellCutType_[own[facei]] == NOTCUT) + if (cellCutType_[own[facei]] == cutType::NOTCUT) { - cellCutType_[own[facei]] = CUT; + cellCutType_[own[facei]] = cutType::CUT; ++nCutCells_; } - if (cellCutType_[nei[facei]] == NOTCUT) + if (cellCutType_[nei[facei]] == cutType::NOTCUT) { - cellCutType_[nei[facei]] = CUT; + cellCutType_[nei[facei]] = cutType::CUT; ++nCutCells_; } } @@ -495,14 +501,14 @@ void Foam::isoSurfacePoint::calcCutTypes for (label facei = mesh_.nInternalFaces(); facei < mesh_.nFaces(); ++facei) { - if (faceCutType_[facei] == NOTCUT) + if (faceCutType_[facei] == cutType::NOTCUT) { continue; } - if (cellCutType_[own[facei]] == NOTCUT) + if (cellCutType_[own[facei]] == cutType::NOTCUT) { - cellCutType_[own[facei]] = CUT; + cellCutType_[own[facei]] = cutType::CUT; ++nCutCells_; } } @@ -551,7 +557,7 @@ void Foam::isoSurfacePoint::calcSnappedCc forAll(mesh_.cells(), celli) { - if (cellCutType_[celli] == CUT) + if (cellCutType_[celli] == cutType::CUT) { const scalar cVal = cVals[celli]; @@ -722,7 +728,7 @@ void Foam::isoSurfacePoint::calcSnappedPoint for (const label facei : pFaces) { - if (faceCutType_[facei] == CUT) + if (faceCutType_[facei] == cutType::CUT) { anyCut = true; break; @@ -1335,19 +1341,25 @@ Foam::isoSurfacePoint::isoSurfacePoint const bitSet& /*unused*/ ) : - isoSurfaceBase(iso, params), - mesh_(cellValues.mesh()), - pVals_(pointValues), + isoSurfaceBase + ( + cellValues.mesh(), + cellValues.primitiveField(), + pointValues, + iso, + params + ), + cValsPtr_(nullptr), mergeDistance_(params.mergeTol()*mesh_.bounds().mag()) { const bool regularise = (params.filter() != filterType::NONE); + const fvMesh& fvmesh = cellValues.mesh(); if (debug) { Pout<< "isoSurfacePoint:" << nl << " isoField : " << cellValues.name() << nl - << " cell min/max : " - << minMax(cellValues.primitiveField()) << nl + << " cell min/max : " << minMax(cVals_) << nl << " point min/max : " << minMax(pVals_) << nl << " isoValue : " << iso << nl << " filter : " << Switch(regularise) << nl @@ -1357,7 +1369,6 @@ Foam::isoSurfacePoint::isoSurfacePoint const polyBoundaryMesh& patches = mesh_.boundaryMesh(); - // Rewrite input field // ~~~~~~~~~~~~~~~~~~~ // Rewrite input volScalarField to have interpolated values @@ -1376,17 +1387,17 @@ Foam::isoSurfacePoint::isoSurfacePoint IOobject ( "C", - mesh_.pointsInstance(), - mesh_.meshSubDir, - mesh_, + fvmesh.pointsInstance(), + fvmesh.meshSubDir, + fvmesh, IOobject::NO_READ, IOobject::NO_WRITE, false ), - mesh_, + fvmesh, dimLength, - mesh_.cellCentres(), - mesh_.faceCentres() + fvmesh.cellCentres(), + fvmesh.faceCentres() ); forAll(patches, patchi) { @@ -1429,7 +1440,7 @@ Foam::isoSurfacePoint::isoSurfacePoint patchi, new calculatedFvPatchField<vector> ( - mesh_.boundary()[patchi], + fvmesh.boundary()[patchi], meshC ) ); @@ -1454,20 +1465,18 @@ Foam::isoSurfacePoint::isoSurfacePoint if (debug) { - const fvMesh& fvm = mesh_; - volScalarField debugField ( IOobject ( "isoSurfacePoint.cutType", - fvm.time().timeName(), - fvm.time(), + fvmesh.time().timeName(), + fvmesh.time(), IOobject::NO_READ, IOobject::NO_WRITE, false ), - fvm, + fvmesh, dimensionedScalar(dimless, Zero) ); diff --git a/src/sampling/surface/isoSurface/isoSurfacePoint.H b/src/sampling/surface/isoSurface/isoSurfacePoint.H index 3e046558ae08a675a5b9189194f234dae8b9dc9a..a4bb33faae33a1986ed0aeb82ac6ff1ec08c5a38 100644 --- a/src/sampling/surface/isoSurface/isoSurfacePoint.H +++ b/src/sampling/surface/isoSurface/isoSurfacePoint.H @@ -76,9 +76,7 @@ SourceFiles namespace Foam { -// Forward declarations - -class fvMesh; +// Forward Declarations class plane; class treeBoundBox; class triSurface; @@ -93,26 +91,7 @@ class isoSurfacePoint { // Private Data - enum segmentCutType - { - NEARFIRST, // intersection close to e.first() - NEARSECOND, // ,, e.second() - NOTNEAR // no intersection - }; - - enum cellCutType - { - NOTCUT, // not cut - SPHERE, // all edges to cell centre cut - CUT // normal cut - }; - - - //- Reference to mesh - const fvMesh& mesh_; - - const scalarField& pVals_; - + //- Cell values. //- Input volScalarField with separated coupled patches rewritten autoPtr<slicedVolScalarField> cValsPtr_; @@ -120,10 +99,10 @@ class isoSurfacePoint const scalar mergeDistance_; //- Whether face might be cut - List<cellCutType> faceCutType_; + List<cutType> faceCutType_; //- Whether cell might be cut - List<cellCutType> cellCutType_; + List<cutType> cellCutType_; //- Estimated number of cut cells label nCutCells_; @@ -374,6 +353,18 @@ class isoSurfacePoint labelList& newToOldPoints ); + + //- Interpolates cCoords, pCoords. + // Uses the references to the original fields used to create the + // iso surface. + template<class Type> + tmp<Field<Type>> interpolateTemplate + ( + const GeometricField<Type, fvPatchField, volMesh>& cCoords, + const Field<Type>& pCoords + ) const; + + public: //- Declare friendship to share some functionality @@ -404,18 +395,19 @@ public: ); + //- Destructor + virtual ~isoSurfacePoint() = default; + + // Member Functions - //- Interpolates cCoords, pCoords. - // Uses the references to the original fields used to create the - // iso surface. - template<class Type> - tmp<Field<Type>> interpolate - ( - const GeometricField<Type, fvPatchField, volMesh>& cCoords, - const Field<Type>& pCoords - ) const; + // Sampling + declareIsoSurfaceInterpolateMethod(scalar); + declareIsoSurfaceInterpolateMethod(vector); + declareIsoSurfaceInterpolateMethod(sphericalTensor); + declareIsoSurfaceInterpolateMethod(symmTensor); + declareIsoSurfaceInterpolateMethod(tensor); }; diff --git a/src/sampling/surface/isoSurface/isoSurfacePointTemplates.C b/src/sampling/surface/isoSurface/isoSurfacePointTemplates.C index 0f2dd0c1aebe83b5aafd9027b464abd9afe04814..0323bf473978f6c3b8083c43856472bf23c8105a 100644 --- a/src/sampling/surface/isoSurface/isoSurfacePointTemplates.C +++ b/src/sampling/surface/isoSurface/isoSurfacePointTemplates.C @@ -6,7 +6,7 @@ \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2011-2016 OpenFOAM Foundation - Copyright (C) 2018 OpenCFD Ltd. + Copyright (C) 2018-2020 OpenCFD Ltd. ------------------------------------------------------------------------------- License This file is part of OpenFOAM. @@ -407,7 +407,7 @@ void Foam::isoSurfacePoint::generateTriPoints if (triIndex == 0x09) { // Flip normals - label sz = pts.size(); + const label sz = pts.size(); Swap(pts[sz-5], pts[sz-4]); Swap(pts[sz-2], pts[sz-1]); } @@ -433,7 +433,7 @@ void Foam::isoSurfacePoint::generateTriPoints if (triIndex == 0x07) { // Flip normals - label sz = pts.size(); + const label sz = pts.size(); Swap(pts[sz-2], pts[sz-1]); } } @@ -465,7 +465,7 @@ Foam::label Foam::isoSurfacePoint::generateFaceTriPoints DynamicList<label>& triMeshCells ) const { - label own = mesh_.faceOwner()[facei]; + const label own = mesh_.faceOwner()[facei]; label oldNPoints = triPoints.size(); @@ -577,7 +577,7 @@ void Foam::isoSurfacePoint::generateTriPoints for (label facei = 0; facei < mesh_.nInternalFaces(); ++facei) { - if (faceCutType_[facei] != NOTCUT) + if ((faceCutType_[facei] & cutType::ANYCUT) != 0) { generateFaceTriPoints ( @@ -647,9 +647,9 @@ void Foam::isoSurfacePoint::generateTriPoints forAll(isCollocated, i) { - label facei = pp.start()+i; + const label facei = pp.start()+i; - if (faceCutType_[facei] != NOTCUT) + if ((faceCutType_[facei] & cutType::ANYCUT) != 0) { if (isCollocated[i]) { @@ -708,7 +708,7 @@ void Foam::isoSurfacePoint::generateTriPoints forAll(pp, i) { - if (faceCutType_[facei] != NOTCUT) + if ((faceCutType_[facei] & cutType::ANYCUT) != 0) { generateFaceTriPoints ( @@ -807,7 +807,7 @@ Foam::isoSurfacePoint::interpolate template<class Type> Foam::tmp<Foam::Field<Type>> -Foam::isoSurfacePoint::interpolate +Foam::isoSurfacePoint::interpolateTemplate ( const GeometricField<Type, fvPatchField, volMesh>& cCoords, const Field<Type>& pCoords diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.C b/src/sampling/surface/isoSurface/isoSurfaceTopo.C index 681226a59be778ee34e69e250adecdabab890839..5cdd41329442cb57beab933364d1e3667c71de87 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.C @@ -34,217 +34,44 @@ License #include "tetPointRef.H" #include "DynamicField.H" #include "syncTools.H" +#include "uindirectPrimitivePatch.H" #include "polyMeshTetDecomposition.H" -#include "cyclicACMIPolyPatch.H" -// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // -namespace Foam -{ - defineTypeNameAndDebug(isoSurfaceTopo, 0); -} +#include "isoSurfaceBaseMethods.H" +defineIsoSurfaceInterpolateMethods(Foam::isoSurfaceTopo); -// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // namespace Foam { - // Does any edge of triangle cross iso value? - inline static bool isTriCut - ( - const label a, - const label b, - const label c, - const scalarField& pointValues, - const scalar isoValue - ) - { - const bool aLower = (pointValues[a] < isoValue); - const bool bLower = (pointValues[b] < isoValue); - const bool cLower = (pointValues[c] < isoValue); - - return !(aLower == bLower && aLower == cLower); - } + defineTypeNameAndDebug(isoSurfaceTopo, 0); } // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // -Foam::isoSurfaceTopo::cellCutType Foam::isoSurfaceTopo::calcCutType -( - const label celli -) const -{ - if (ignoreCells_.test(celli)) - { - return NOTCUT; - } - - const cell& cFaces = mesh_.cells()[celli]; - - if (tetMatcher::test(mesh_, celli)) - { - for (const label facei : cFaces) - { - if - ( - !mesh_.isInternalFace(facei) - && ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces()) - ) - { - continue; - } - - const face& f = mesh_.faces()[facei]; - - for (label fp = 1; fp < f.size() - 1; ++fp) - { - if (isTriCut(f[0], f[fp], f[f.fcIndex(fp)], pVals_, iso_)) - { - return CUT; - } - } - } - return NOTCUT; - } - - - const bool cellLower = (cVals_[celli] < iso_); - - // First check if there is any cut in cell - bool edgeCut = false; - - for (const label facei : cFaces) - { - if - ( - !mesh_.isInternalFace(facei) - && ignoreBoundaryFaces_.test(facei-mesh_.nInternalFaces()) - ) - { - continue; - } - - const face& f = mesh_.faces()[facei]; - - // Check pyramids cut - for (const label pointi : f) - { - if ((pVals_[pointi] < iso_) != cellLower) - { - edgeCut = true; - break; - } - } - - if (edgeCut) - { - break; - } - - // Check (triangulated) face edges - // With fallback for problem decompositions - const label fp0 = (tetBasePtIs_[facei] < 0 ? 0 : tetBasePtIs_[facei]); - - label fp = f.fcIndex(fp0); - for (label i = 2; i < f.size(); ++i) - { - const label nextFp = f.fcIndex(fp); - - if (isTriCut(f[fp0], f[fp], f[nextFp], pVals_, iso_)) - { - edgeCut = true; - break; - } - - fp = nextFp; - } - - if (edgeCut) - { - break; - } - } - - if (edgeCut) - { - // Count actual cuts (expensive since addressing needed) - // Note: not needed if you don't want to preserve maxima/minima - // centred around cellcentre. In that case just always return CUT - - const labelList& cPoints = mesh_.cellPoints(celli); - - label nPyrCuts = 0; - - for (const label pointi : cPoints) - { - if ((pVals_[pointi] < iso_) != cellLower) - { - ++nPyrCuts; - } - } - - if (nPyrCuts == cPoints.size()) - { - return SPHERE; - } - else if (nPyrCuts) - { - return CUT; - } - } - - return NOTCUT; -} - - -Foam::label Foam::isoSurfaceTopo::calcCutTypes -( - List<cellCutType>& cellCutTypes -) -{ - cellCutTypes.setSize(mesh_.nCells()); - - label nCutCells = 0; - forAll(cellCutTypes, celli) - { - cellCutTypes[celli] = calcCutType(celli); - - if (cellCutTypes[celli] == CUT) - { - ++nCutCells; - } - } - - if (debug) - { - Pout<< "isoSurfaceTopo : candidate cut cells " - << nCutCells << " / " << mesh_.nCells() << endl; - } - return nCutCells; -} - - Foam::scalar Foam::isoSurfaceTopo::minTetQ ( const label facei, const label faceBasePtI ) const { - scalar q = polyMeshTetDecomposition::minQuality - ( - mesh_, - mesh_.cellCentres()[mesh_.faceOwner()[facei]], - facei, - true, - faceBasePtI - ); + const scalar ownQuality = + polyMeshTetDecomposition::minQuality + ( + mesh_, + mesh_.cellCentres()[mesh_.faceOwner()[facei]], + facei, + true, + faceBasePtI + ); if (mesh_.isInternalFace(facei)) { - q = min - ( - q, + const scalar neiQuality = polyMeshTetDecomposition::minQuality ( mesh_, @@ -252,10 +79,15 @@ Foam::scalar Foam::isoSurfaceTopo::minTetQ facei, false, faceBasePtI - ) - ); + ); + + if (neiQuality < ownQuality) + { + return neiQuality; + } } - return q; + + return ownQuality; } @@ -264,8 +96,8 @@ void Foam::isoSurfaceTopo::fixTetBasePtIs() // Determine points used by two faces on the same cell const cellList& cells = mesh_.cells(); const faceList& faces = mesh_.faces(); - const labelList& faceOwner = mesh_.faceOwner(); - const labelList& faceNeighbour = mesh_.faceNeighbour(); + const labelList& faceOwn = mesh_.faceOwner(); + const labelList& faceNei = mesh_.faceNeighbour(); // Get face triangulation base point @@ -279,11 +111,11 @@ void Foam::isoSurfaceTopo::fixTetBasePtIs() { if (tetBasePtIs_[facei] == -1) { - problemCells.set(faceOwner[facei]); + problemCells.set(faceOwn[facei]); if (mesh_.isInternalFace(facei)) { - problemCells.set(faceNeighbour[facei]); + problemCells.set(faceNei[facei]); } } } @@ -338,8 +170,8 @@ void Foam::isoSurfaceTopo::fixTetBasePtIs() { if ( - problemCells[faceOwner[facei]] - || (mesh_.isInternalFace(facei) && problemCells[faceNeighbour[facei]]) + problemCells.test(faceOwn[facei]) + || (mesh_.isInternalFace(facei) && problemCells.test(faceNei[facei])) ) { const face& f = faces[facei]; @@ -350,8 +182,8 @@ void Foam::isoSurfaceTopo::fixTetBasePtIs() if ( - !problemPoints[f.rcValue(fp0)] - && !problemPoints[f.fcValue(fp0)] + !problemPoints.test(f.rcValue(fp0)) + && !problemPoints.test(f.fcValue(fp0)) ) { continue; @@ -365,8 +197,8 @@ void Foam::isoSurfaceTopo::fixTetBasePtIs() { if ( - !problemPoints[f.rcValue(fp)] - && !problemPoints[f.fcValue(fp)] + !problemPoints.test(f.rcValue(fp)) + && !problemPoints.test(f.fcValue(fp)) ) { const scalar q = minTetQ(facei, fp); @@ -832,7 +664,9 @@ void Foam::isoSurfaceTopo::generateTriPoints // For tets don't do cell-centre decomposition, just use the // tet points and values - label facei = cFaces[0]; + const label startTrii = verts.size(); + + const label facei = cFaces[0]; const face& f0 = faces[facei]; // Get the other point from f1. Tbd: check if not duplicate face @@ -848,7 +682,6 @@ void Foam::isoSurfaceTopo::generateTriPoints } } - label p0 = f0[0]; label p1 = f0[1]; label p2 = f0[2]; @@ -858,7 +691,6 @@ void Foam::isoSurfaceTopo::generateTriPoints Swap(p1, p2); } - label startTrii = verts.size(); generateTriPoints ( facei, @@ -880,8 +712,7 @@ void Foam::isoSurfaceTopo::generateTriPoints verts // Every three verts is new triangle ); - label nTris = (verts.size()-startTrii)/3; - for (label i = 0; i < nTris; ++i) + for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) { faceLabels.append(facei); } @@ -899,12 +730,12 @@ void Foam::isoSurfaceTopo::generateTriPoints continue; } + const label startTrii = verts.size(); + const face& f = faces[facei]; label fp0 = tetBasePtIs_[facei]; - label startTrii = verts.size(); - // Fallback if (fp0 < 0) { @@ -957,8 +788,7 @@ void Foam::isoSurfaceTopo::generateTriPoints fp = nextFp; } - label nTris = (verts.size()-startTrii)/3; - for (label i = 0; i < nTris; ++i) + for (label nTris = (verts.size()-startTrii)/3; nTris; --nTris) { faceLabels.append(facei); } @@ -967,6 +797,8 @@ void Foam::isoSurfaceTopo::generateTriPoints } +// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * // + void Foam::isoSurfaceTopo::triangulateOutside ( const bool filterDiag, @@ -978,7 +810,7 @@ void Foam::isoSurfaceTopo::triangulateOutside // outputs DynamicList<face>& compactFaces, DynamicList<label>& compactCellIDs -) const +) { // We can form pockets: // - 1. triangle on face @@ -1000,10 +832,10 @@ void Foam::isoSurfaceTopo::triangulateOutside label fpi = 0; forAll(f, i) { - label pointi = mp[loop[i]]; + const label pointi = mp[loop[i]]; if (filterDiag && pointFromDiag[pointi]) { - label prevPointi = mp[loop[loop.fcIndex(i)]]; + const label prevPointi = mp[loop[loop.fcIndex(i)]]; if ( pointFromDiag[prevPointi] @@ -1042,10 +874,10 @@ void Foam::isoSurfaceTopo::triangulateOutside } -Foam::meshedSurface Foam::isoSurfaceTopo::removeInsidePoints +void Foam::isoSurfaceTopo::removeInsidePoints ( + Mesh& s, const bool filterDiag, - const Mesh& s, // inputs const boolUList& pointFromDiag, @@ -1055,13 +887,13 @@ Foam::meshedSurface Foam::isoSurfaceTopo::removeInsidePoints // outputs DynamicList<label>& pointCompactMap, // Per returned point the original DynamicList<label>& compactCellIDs // Per returned tri the cellID -) const +) { - const pointField& points = s.points(); - pointCompactMap.clear(); compactCellIDs.clear(); + const pointField& points = s.points(); + DynamicList<face> compactFaces(s.size()/8); for (label celli = 0; celli < start.size()-1; ++celli) @@ -1119,14 +951,17 @@ Foam::meshedSurface Foam::isoSurfaceTopo::removeInsidePoints UIndirectList<point>(s.points(), pointCompactMap) ); - Mesh cpSurface + surfZoneList newZones(s.surfZones()); + + s.clear(); + Mesh updated ( std::move(compactPoints), std::move(compactFaces), s.surfZones() ); - return cpSurface; + s.transfer(updated); } @@ -1135,18 +970,15 @@ Foam::meshedSurface Foam::isoSurfaceTopo::removeInsidePoints Foam::isoSurfaceTopo::isoSurfaceTopo ( const polyMesh& mesh, - const scalarField& cVals, - const scalarField& pVals, + const scalarField& cellValues, + const scalarField& pointValues, const scalar iso, const isoSurfaceParams& params, const bitSet& ignoreCells ) : - isoSurfaceBase(iso, params), - mesh_(mesh), - cVals_(cVals), - pVals_(pVals), - ignoreCells_(ignoreCells) + isoSurfaceBase(mesh, cellValues, pointValues, iso, params), + cellCutType_(mesh.nCells(), cutType::UNVISITED) { if (debug) { @@ -1157,31 +989,35 @@ Foam::isoSurfaceTopo::isoSurfaceTopo << " filter : " << isoSurfaceParams::filterNames[params.filter()] << nl << " mesh span : " << mesh.bounds().mag() << nl - << " ignoreCells : " << ignoreCells_.count() + << " ignoreCells : " << ignoreCells.count() << " / " << cVals_.size() << nl << endl; } - // Determine boundary pyramids to ignore (since originating from ACMI faces) - // Maybe needs to be optional argument or more general detect colocated - // faces. - for (const polyPatch& pp : mesh_.boundaryMesh()) - { - if (isA<cyclicACMIPolyPatch>(pp)) - { - ignoreBoundaryFaces_.set - ( - labelRange(pp.offset(), pp.size()) - ); - } - } + this->ignoreCyclics(); + + label nBlockedCells = 0; + + // Mark ignoreCells as blocked + nBlockedCells += blockCells(cellCutType_, ignoreCells); + + // Mark cells outside bounding box as blocked + nBlockedCells += + blockCells(cellCutType_, params.getClipBounds(), volumeType::OUTSIDE); fixTetBasePtIs(); - // Determine if any cut through cell - List<cellCutType> cellCutTypes; - const label nCutCells = calcCutTypes(cellCutTypes); + // Determine cell cuts + const label nCutCells = calcCellCuts(cellCutType_); + + if (debug) + { + Pout<< "isoSurfaceTopo : candidate cells cut " + << nCutCells + << " blocked " << nBlockedCells + << " total " << mesh_.nCells() << endl; + } if (debug && isA<fvMesh>(mesh)) { @@ -1204,9 +1040,9 @@ Foam::isoSurfaceTopo::isoSurfaceTopo auto& debugFld = debugField.primitiveFieldRef(); - forAll(cellCutTypes, celli) + forAll(cellCutType_, celli) { - debugFld[celli] = cellCutTypes[celli]; + debugFld[celli] = cellCutType_[celli]; } Pout<< "Writing cut types:" @@ -1221,7 +1057,7 @@ Foam::isoSurfaceTopo::isoSurfaceTopo DynamicList<edge> pointToVerts(10*nCutCells); // - pointToFace : from generated iso point to originating mesh face DynamicList<label> pointToFace(10*nCutCells); - // - pointToFace : from generated iso point whether is on face diagonal + // - pointFromDiag : if generated iso point is on face diagonal DynamicList<bool> pointFromDiag(10*nCutCells); // Per cell: number of intersected edges: @@ -1239,12 +1075,14 @@ Foam::isoSurfaceTopo::isoSurfaceTopo for (label celli = 0; celli < mesh_.nCells(); ++celli) { startTri[celli] = faceLabels.size(); - if (cellCutTypes[celli] != NOTCUT) + if ((cellCutType_[celli] & cutType::ANYCUT) != 0) { generateTriPoints ( celli, - tetMatcher::test(mesh_, celli), + + // Same as tetMatcher::test(mesh_, celli), + bool(cellCutType_[celli] & cutType::TETCUT), // isTet pointToVerts, pointToFace, @@ -1268,9 +1106,9 @@ Foam::isoSurfaceTopo::isoSurfaceTopo meshCells_.transfer(cellLabels); pointToFace_.transfer(pointToFace); - tmp<pointField> allPoints + pointField allPoints ( - interpolate + this->interpolateTemplate ( mesh_.cellCentres(), mesh_.points() @@ -1312,7 +1150,9 @@ Foam::isoSurfaceTopo::isoSurfaceTopo if (debug) { - Pout<< "isoSurfaceTopo : generated " << size() << " faces." << endl; + Pout<< "isoSurfaceTopo : generated " + << Mesh::size() << " faces " + << Mesh::points().size() << " points" << endl; } @@ -1322,159 +1162,190 @@ Foam::isoSurfaceTopo::isoSurfaceTopo // face diagonals) DynamicList<label> pointCompactMap(size()); // Back to original point DynamicList<label> compactCellIDs(size()); // Per tri the cell - Mesh::operator= + + removeInsidePoints ( - removeInsidePoints - ( - (params.filter() == filterType::DIAGCELL ? true : false), - *this, - pointFromDiag, - pointToFace_, - startTri, - pointCompactMap, - compactCellIDs - ) + *this, + (params.filter() == filterType::DIAGCELL ? true : false), + + pointFromDiag, + pointToFace_, + startTri, + pointCompactMap, + compactCellIDs ); pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointCompactMap)(); pointToFace_ = UIndirectList<label>(pointToFace_, pointCompactMap)(); - pointFromDiag = UIndirectList<bool>(pointFromDiag, pointCompactMap)(); meshCells_.transfer(compactCellIDs); + pointCompactMap.clearStorage(); + compactCellIDs.clearStorage(); + if (debug) { Pout<< "isoSurfaceTopo :" - << " after removing cell centre and face-diag triangles : " - << size() << endl; + " after removing cell centre and face-diag triangles " + << Mesh::size() << " faces " + << Mesh::points().size() << " points" + << endl; } + } + // Not required after this stage + pointFromDiag.clearStorage(); - if (params.filter() == filterType::DIAGCELL) - { - // We remove verts on face diagonals. This is in fact just - // straightening the edges of the face through the cell. This can - // close off 'pockets' of triangles and create open or - // multiply-connected triangles - // Solved by eroding open-edges - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if (params.filter() == filterType::DIAGCELL) + { + // We remove verts on face diagonals. This is in fact just + // straightening the edges of the face through the cell. This can + // close off 'pockets' of triangles and create open or + // multiply-connected triangles - // Mark points on mesh outside. - bitSet isBoundaryPoint(mesh.nPoints()); - for - ( - label facei = mesh.nInternalFaces(); - facei < mesh.nFaces(); - ++facei - ) - { - isBoundaryPoint.set(mesh.faces()[facei]); - } + // Solved by eroding open-edges + // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + // Mark points on mesh outside. + bitSet isBoundaryPoint(mesh.nPoints()); + for + ( + label facei = mesh.nInternalFaces(); + facei < mesh.nFaces(); + ++facei + ) + { + isBoundaryPoint.set(mesh.faces()[facei]); + } - // Include faces that would be exposed from mesh subset - if (!ignoreCells_.empty()) - { - const labelList& faceOwner = mesh_.faceOwner(); - const labelList& faceNeighbour = mesh_.faceNeighbour(); + // Include faces that would be exposed from mesh subset + if (nBlockedCells) + { + const labelList& faceOwn = mesh_.faceOwner(); + const labelList& faceNei = mesh_.faceNeighbour(); - for + for (label facei = 0; facei < mesh.nInternalFaces(); ++facei) + { + // If only one cell is blocked, the face corresponds + // to an exposed subMesh face + if ( - label facei = 0; - facei < mesh.nInternalFaces(); - ++facei + (cellCutType_[faceOwn[facei]] == cutType::BLOCKED) + != (cellCutType_[faceNei[facei]] == cutType::BLOCKED) ) { - // Only one of the cells is being ignored. - // That means it is an exposed subMesh face. - if - ( - ignoreCells_.test(faceOwner[facei]) - != ignoreCells_.test(faceNeighbour[facei]) - ) - { - isBoundaryPoint.set(mesh.faces()[facei]); - } + isBoundaryPoint.set(mesh.faces()[facei]); } } + } - bitSet faceSelection; + // The list of surface faces that should be retained after erosion + Mesh& surf = *this; + labelList faceAddr(identity(surf.size())); - while (true) - { - faceSelection.clear(); - faceSelection.resize(this->size()); + bitSet faceSelection; - const labelList& mp = meshPoints(); + while (true) + { + // Shadow the surface for the purposes of erosion + uindirectPrimitivePatch erosion + ( + UIndirectList<face>(surf, faceAddr), + surf.points() + ); + + faceSelection.clear(); + faceSelection.resize(erosion.size()); - const labelListList& edgeFaces = Mesh::edgeFaces(); + const labelList& mp = erosion.meshPoints(); + const edgeList& surfEdges = erosion.edges(); + const labelListList& edgeFaces = erosion.edgeFaces(); - forAll(edgeFaces, edgei) + label nEdgeRemove = 0; + + forAll(edgeFaces, edgei) + { + const labelList& eFaces = edgeFaces[edgei]; + if (eFaces.size() == 1) { - const labelList& eFaces = edgeFaces[edgei]; - if (eFaces.size() == 1) + // Open edge. Check that vertices do not originate + // from a boundary face + const edge& e = surfEdges[edgei]; + + const edge& verts0 = pointToVerts_[mp[e.first()]]; + const edge& verts1 = pointToVerts_[mp[e.second()]]; + + if + ( + isBoundaryPoint.test(verts0.first()) + && isBoundaryPoint.test(verts0.second()) + && isBoundaryPoint.test(verts1.first()) + && isBoundaryPoint.test(verts1.second()) + ) + { + // Open edge on boundary face. Keep + } + else { - // Open edge. Check that vertices do not originate - // from a boundary face - const edge& e = edges()[edgei]; - const edge& verts0 = pointToVerts_[mp[e[0]]]; - const edge& verts1 = pointToVerts_[mp[e[1]]]; - if - ( - isBoundaryPoint.test(verts0[0]) - && isBoundaryPoint.test(verts0[1]) - && isBoundaryPoint.test(verts1[0]) - && isBoundaryPoint.test(verts1[1]) - ) - { - // Open edge on boundary face. Keep - } - else - { - // Open edge. Mark for erosion - faceSelection.set(eFaces[0]); - } + // Open edge. Mark for erosion + faceSelection.set(eFaces[0]); + ++nEdgeRemove; } } + } - if (debug) - { - Pout<< "isoSurfaceTopo :" - << " removing " << faceSelection.count() - << " faces on open edges" << endl; - } + if (debug) + { + Pout<< "isoSurfaceTopo :" + << " removing " << faceSelection.count() + << " / " << faceSelection.size() + << " faces on " << nEdgeRemove << " open edges" << endl; + } - if (returnReduce(faceSelection.none(), andOp<bool>())) - { - break; - } + if (returnReduce(faceSelection.none(), andOp<bool>())) + { + break; + } + // Remove the faces from the addressing + inplaceSubset(faceSelection, faceAddr, true); // True = remove + } - // Flip from remove face -> keep face - faceSelection.flip(); - labelList pointMap; - labelList faceMap; - Mesh filteredSurf - ( - Mesh::subsetMesh - ( - faceSelection, - pointMap, - faceMap - ) - ); - Mesh::transfer(filteredSurf); + // Finished erosion (if any) + // - retain the faces listed in the updated addressing - pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)(); - pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)(); - pointFromDiag = UIndirectList<bool>(pointFromDiag, pointMap)(); - meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); - } + if (surf.size() != faceAddr.size()) + { + faceSelection.clear(); + faceSelection.resize(surf.size()); + faceSelection.set(faceAddr); + + inplaceSubsetMesh(faceSelection); } } } +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::isoSurfaceTopo::inplaceSubsetMesh(const bitSet& include) +{ + labelList pointMap; + labelList faceMap; + Mesh filtered + ( + Mesh::subsetMesh(include, pointMap, faceMap) + ); + Mesh::transfer(filtered); + + meshCells_ = UIndirectList<label>(meshCells_, faceMap)(); + + pointToVerts_ = UIndirectList<edge>(pointToVerts_, pointMap)(); + pointToFace_ = UIndirectList<label>(pointToFace_, pointMap)(); +} + + // ************************************************************************* // diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopo.H b/src/sampling/surface/isoSurface/isoSurfaceTopo.H index 65284d6b7bb0fb815206e86f0b16847cc504d0fa..1a5bba527cdb4031cfd9406cd5b1f9b4ea523d2d 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopo.H +++ b/src/sampling/surface/isoSurface/isoSurfaceTopo.H @@ -62,28 +62,6 @@ class isoSurfaceTopo { // Private Data - enum cellCutType - { - NOTCUT, //!< Not cut - SPHERE, //!< All edges to cell centre cut - CUT //!< Normal cut - }; - - - //- Reference to mesh - const polyMesh& mesh_; - - const scalarField& cVals_; - - const scalarField& pVals_; - - //- Optional cells to ignore - const bitSet& ignoreCells_; - - //- Optional boundary faces to ignore. Used to exclude cyclicACMI - // (since duplicate faces) - bitSet ignoreBoundaryFaces_; - //- Corrected version of tetBasePtIs labelList tetBasePtIs_; @@ -93,6 +71,9 @@ class isoSurfaceTopo //- For every point the originating face in mesh labelList pointToFace_; + //- The cell cut type + List<cutType> cellCutType_; + // Private Member Functions @@ -104,13 +85,6 @@ class isoSurfaceTopo void fixTetBasePtIs(); - //- Determine whether cell is cut - cellCutType calcCutType(const label celli) const; - - //- Determine for all mesh whether cell is cut - // \return number of cells cut - label calcCutTypes(List<cellCutType>& cellCutTypes); - //- Generate single point on edge label generatePoint ( @@ -158,7 +132,7 @@ class isoSurfaceTopo // Simplification - void triangulateOutside + static void triangulateOutside ( const bool filterDiag, const primitivePatch& pp, @@ -169,12 +143,12 @@ class isoSurfaceTopo DynamicList<face>& compactFaces, DynamicList<label>& compactCellIDs - ) const; + ); - Mesh removeInsidePoints + static void removeInsidePoints ( + Mesh& s, // Modify inplace const bool filterDiag, - const Mesh& s, // Inputs const boolUList& pointFromDiag, @@ -184,11 +158,30 @@ class isoSurfaceTopo // Outputs DynamicList<label>& pointCompactMap, // Per point the original DynamicList<label>& compactCellIDs // Per face the cellID - ) const; + ); -public: +protected: + // Editing + + //- Subset the surface using the selected faces. + // + // \param[in] include the faces to select + void inplaceSubsetMesh(const bitSet& include); + + + // Sampling + + //- Interpolates cCoords,pCoords. + template<class Type> + tmp<Field<Type>> interpolateTemplate + ( + const Field<Type>& cCoords, + const Field<Type>& pCoords + ) const; + +public: //- Runtime type information TypeName("isoSurfaceTopo"); @@ -214,6 +207,10 @@ public: ); + //- Destructor + virtual ~isoSurfaceTopo() = default; + + // Member Functions //- For every point originating face (pyramid) in mesh @@ -228,13 +225,14 @@ public: return pointToVerts_; } - //- Interpolates cCoords,pCoords. - template<class Type> - tmp<Field<Type>> interpolate - ( - const Field<Type>& cCoords, - const Field<Type>& pCoords - ) const; + + // Sampling + + declareIsoSurfaceInterpolateMethod(scalar); + declareIsoSurfaceInterpolateMethod(vector); + declareIsoSurfaceInterpolateMethod(sphericalTensor); + declareIsoSurfaceInterpolateMethod(symmTensor); + declareIsoSurfaceInterpolateMethod(tensor); }; diff --git a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C index 2fa71be0f595fad2d99ff0fd5533e0e33a8939a9..8ffec74e8db56b714118824d188edb711456ef95 100644 --- a/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C +++ b/src/sampling/surface/isoSurface/isoSurfaceTopoTemplates.C @@ -30,7 +30,7 @@ License template<class Type> Foam::tmp<Foam::Field<Type>> -Foam::isoSurfaceTopo::interpolate +Foam::isoSurfaceTopo::interpolateTemplate ( const Field<Type>& cellCoords, const Field<Type>& pointCoords