From b312f0ba4024d3935fa09dedbfbb693781c91049 Mon Sep 17 00:00:00 2001 From: Mark Olesen <Mark.Olesen@esi-group.com> Date: Thu, 1 Jun 2017 15:46:42 +0200 Subject: [PATCH] ENH: documentation and input simplification for surfaceFeatureExtract --- .../extractionMethod/extractFromFile.H | 11 +- .../extractionMethod/extractFromNone.C | 7 +- .../extractionMethod/extractFromNone.H | 9 +- .../extractionMethod/extractFromSurface.H | 13 +- .../surfaceFeaturesExtraction.C | 12 +- .../surfaceFeaturesExtraction.H | 9 +- .../surfaceFeatureExtract.C | 271 ++++++++++++++---- .../surfaceFeatureExtractDict | 163 ++++------- 8 files changed, 290 insertions(+), 205 deletions(-) diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromFile.H b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromFile.H index 83853fab2de..8cd5cf4a97d 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromFile.H +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromFile.H @@ -27,6 +27,7 @@ Class Description Run-time selectable surface feature extraction. + Selectable as "extractFromFile". Mandatory dictionary entries: "featureEdgeFile". Optional dictionary entries: "geometricTestOnly". @@ -60,17 +61,13 @@ class extractFromFile public: - // Constructors - - //- Construct from dictionary - extractFromFile(const dictionary& dict); - + //- Construct from dictionary + extractFromFile(const dictionary& dict); //- Destructor virtual ~extractFromFile(); - - //- Extracted features from surface + //- Features loaded (extracted) from featureEdgeFile virtual autoPtr<surfaceFeatures> features ( const triSurface& surf diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.C b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.C index c49db760a98..45061472c46 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.C +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.C @@ -52,10 +52,10 @@ Foam::surfaceFeaturesExtraction::extractFromNone::extractFromNone : method() { - const dictionary& coeffDict = dict.optionalSubDict("noneCoeffs"); + // A "noneCoeffs" sub-dictionary doesn't make much sense. - coeffDict.readIfPresent("includedAngle", includedAngle_); - coeffDict.readIfPresent("geometricTestOnly", geometricTestOnly_); + dict.readIfPresent("includedAngle", includedAngle_); + dict.readIfPresent("geometricTestOnly", geometricTestOnly_); } @@ -67,7 +67,6 @@ Foam::surfaceFeaturesExtraction::extractFromNone::~extractFromNone() // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // - Foam::autoPtr<Foam::surfaceFeatures> Foam::surfaceFeaturesExtraction::extractFromNone::features ( diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.H b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.H index 9f9b94410f4..9cc9ebae601 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.H +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromNone.H @@ -28,6 +28,7 @@ Description Run-time selectable surface feature extraction - no extraction. Primarily useful with self-intersection methods. + Selectable as "none". Optional dictionary entries: "includedAngle", "geometricTestOnly". SourceFiles @@ -58,16 +59,12 @@ class extractFromNone public: - // Constructors - - //- Construct from dictionary - extractFromNone(const dictionary& dict); - + //- Construct from dictionary + extractFromNone(const dictionary& dict); //- Destructor virtual ~extractFromNone(); - //- Extracted features from surface (no-op) virtual autoPtr<surfaceFeatures> features ( diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromSurface.H b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromSurface.H index 3ad3412819a..83c2888d02f 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromSurface.H +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/extractFromSurface.H @@ -27,6 +27,8 @@ Class Description Run-time selectable surface feature extraction - extract from surface. + Selectable as "extractFromSurface". + Mandatory dictionary entries: "includedAngle". Optional dictionary entries: "geometricTestOnly". @@ -57,23 +59,18 @@ class extractFromSurface { public: - // Constructors - - //- Construct from dictionary - extractFromSurface(const dictionary& dict); - + //- Construct from dictionary + extractFromSurface(const dictionary& dict); //- Destructor virtual ~extractFromSurface(); - - //- Extracted features from surface + //- Features extracted from surface virtual autoPtr<surfaceFeatures> features ( const triSurface& surf ) const override; - }; diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.C b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.C index 28bd8f092d7..2108e73a9df 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.C +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.C @@ -2,7 +2,7 @@ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | - \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. + \\ / A nd | Copyright (C) 2017 OpenCFD Ltd. \\/ M anipulation | ------------------------------------------------------------------------------- License @@ -70,22 +70,20 @@ Foam::surfaceFeaturesExtraction::method::New { const word methodName = dict.lookup("extractionMethod"); - dictionaryConstructorTable::iterator cstrIter = - dictionaryConstructorTablePtr_->find(methodName); + auto cstrIter = dictionaryConstructorTablePtr_->cfind(methodName); if (!cstrIter.found()) { FatalIOErrorInFunction ( dict - ) << "Unknown extractionMethod " - << methodName << nl << nl - << "Valid extraction methods: :" << nl + ) << "Unknown extractionMethod " << methodName << nl << nl + << "Valid extraction methods:" << nl << flatOutput(dictionaryConstructorTablePtr_->sortedToc()) << exit(FatalIOError); } - return autoPtr<method>(cstrIter()(dict)); + return autoPtr<method>(cstrIter.object()(dict)); } diff --git a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.H b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.H index 6f9482144fe..71c5ba5bc5a 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.H +++ b/applications/utilities/surface/surfaceFeatureExtract/extractionMethod/surfaceFeaturesExtraction.H @@ -21,11 +21,17 @@ License You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. +Namespace + Foam::surfaceFeaturesExtraction + +Description + Namespace for run-time selectable surface feature extraction methods. + Class Foam::surfaceFeaturesExtraction::method Description - Run-time selectable surface feature extraction methods. + Abstract base for run-time selectable surface feature extraction methods. SourceFiles surfaceFeaturesExtraction.C @@ -123,7 +129,6 @@ public: }; - // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // } // End namespace surfaceFeaturesExtraction diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C index 7cd22735a72..bcf8358a105 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C +++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtract.C @@ -31,6 +31,153 @@ Description Extracts and writes surface features to file. All but the basic feature extraction is a work-in-progress. + The extraction process is driven by the \a system/surfaceFeatureExtractDict + dictionary, but the \a -dict option can be used to define an alternative + location. + + The \a system/surfaceFeatureExtractDict dictionary contains entries + for each extraction process. + The name of the individual dictionary is used to load the input surface + (found under \a constant/triSurface) and also as the basename for the + output. + + If the \c surfaces entry is present in a sub-dictionary, it has absolute + precedence over a surface name deduced from the dictionary name. + If the dictionary name itself does not have an extension, the \c surfaces + entry becomes mandatory since in this case the dictionary name cannot + represent an input surface file (ie, there is no file extension). + The \c surfaces entry is a wordRe list, which allows loading and + combining of multiple surfaces. Any exactly specified surface names must + exist, but surfaces selected via regular expressions need not exist. + The selection mechanism preserves order and is without duplicates. + For example, + \verbatim + dictName + { + surfaces (surface1.stl "other.*" othersurf.obj); + ... + } + \endverbatim + + When loading surfaces, the points/faces/regions of each surface are + normally offset to create an aggregated surface. No merging of points + or faces is done. The optional entry \c loadingOption can be used to + adjust the treatment of the regions when loading single or multiple files, + with selections according to the Foam::triSurfaceLoader::loadingOption + enumeration. + \verbatim + dictName + { + // Optional treatment of surface regions when loading + // (single, file, offset, merge) + loadingOption file; + ... + } + \endverbatim + The \c loadingOption is primarily used in combination with the + \c intersectionMethod (specifically its \c region option). + The default \c loadingOption is normally \c offset, + but this changes to \c file if the \c intersectionMethod + \c region is being used. + + Once surfaces have been loaded, the first stage is to extract + features according to the specified \c extractionMethod with values + as per the following table: + \table + extractionMethod | Description + none | No feature extraction + extractFromFile | Load features from the file named in featureEdgeFile + extractFromSurface | Extract features from surface geometry + \endtable + + There are a few entries that influence the extraction behaviour: + \verbatim + // File to use for extractFromFile input + featureEdgeFile "FileName" + + // Mark edges whose adjacent surface normals are at an angle less + // than includedAngle as features + // - 0 : selects no edges + // - 180: selects all edges + includedAngle 120; + + // Do not mark region edges + geometricTestOnly yes; + \endverbatim + + This initial set of edges can be trimmed: + \verbatim + trimFeatures + { + // Remove features with fewer than the specified number of edges + minElem 0; + + // Remove features shorter than the specified cumulative length + minLen 0.0; + } + \endverbatim + + and subsetted + \verbatim + subsetFeatures + { + // Use a plane to select feature edges (normal)(basePoint) + // Only keep edges that intersect the plane + plane (1 0 0)(0 0 0); + + // Select feature edges using a box // (minPt)(maxPt) + // Only keep edges inside the box: + insideBox (0 0 0)(1 1 1); + + // Only keep edges outside the box: + outsideBox (0 0 0)(1 1 1); + + // Keep nonManifold edges (edges with >2 connected faces where + // the faces form more than two different normal planes) + nonManifoldEdges yes; + + // Keep open edges (edges with 1 connected face) + openEdges yes; + } + \endverbatim + + Subsequently, additional features can be added from another file: + \verbatim + addFeatures + { + // Add (without merging) another extendedFeatureEdgeMesh + name axZ.extendedFeatureEdgeMesh; + } + \endverbatim + + The intersectionMethod provides a final means of adding additional + features. These are loosely termed "self-intersection", since it + detects the face/face intersections of the loaded surface or surfaces. + + \table + intersectionMethod | Description + none | Do nothing + self | All face/face intersections + region | Limit face/face intersections to those between different regions. + \endtable + The optional \c tolerance tuning parameter is available for handling + the face/face intersections, but should normally not be touched. + + As well as the normal extendedFeatureEdgeMesh written, + other items can be selected with boolean switches: + + \table + Output option | Description + closeness | Output the closeness of surface elements to other surface elements. + curvature | Output surface curvature + featureProximity | Output the proximity of feature points and edges to another + writeObj | Write features to OBJ format for postprocessing + writeVTK | Write closeness/curvature/proximity fields as VTK for postprocessing + \endtable + +Note + Although surfaceFeatureExtract can do many things, there are still a fair + number of corner cases where it may not produce the desired result. \*---------------------------------------------------------------------------*/ #include "argList.H" @@ -60,16 +207,26 @@ int main(int argc, char *argv[]) { argList::addNote ( - "extract and write surface features to file" + "Extract and write surface features to file" ); argList::noParallel(); argList::noFunctionObjects(); - #include "addDictOption.H" + argList::addOption + ( + "dict", + "file", + "read surfaceFeatureExtractDict from specified location" + ); #include "setRootCase.H" #include "createTime.H" + Info<< nl + << "Note: " + << "Feature line extraction only valid on closed manifold surfaces" + << nl << nl; + const word dictName("surfaceFeatureExtractDict"); #include "setSystemRunTimeDictionaryIO.H" @@ -82,44 +239,58 @@ int main(int argc, char *argv[]) // Where to write VTK output files const fileName vtkOutputDir = runTime.constantPath()/"triSurface"; - forAllConstIter(dictionary, dict, iter) + forAllConstIters(dict, iter) { - const word& dictName = iter().keyword(); - - if (!iter().isDict()) + if (!iter().isDict() || iter().keyword().isPattern()) { continue; } + const dictionary& surfaceDict = iter().dict(); if (!surfaceDict.found("extractionMethod")) { + // Insist on an extractionMethod continue; } + // The output name based in dictionary name (without extensions) + const word& dictName = iter().keyword(); + const word outputName = dictName.lessExt(); + autoPtr<surfaceFeaturesExtraction::method> extractor = surfaceFeaturesExtraction::method::New ( surfaceDict ); - // The output name, cleansed of extensions - // Optional "output" entry, or the dictionary name. - const word outputName = - fileName + // We don't needs the intersectionMethod yet, but can use it + // for setting a reasonable loading option + const surfaceIntersection::intersectionType selfIntersect = + surfaceIntersection::selfIntersectionNames.lookupOrDefault ( - surfaceDict.lookupOrDefault<word>("output", dictName) - ).lessExt(); - - // The "surfaces" entry is normally optional, but if the sub-dictionary - // is itself called "surfaces", then this becomes mandatory. - // This provides a simple means of handling both situations without an - // additional switch. - if + "intersectionMethod", + surfaceDict, + surfaceIntersection::NONE + ); + + const Switch writeObj = surfaceDict.lookupOrDefault<Switch> ( - dictName == "surfaces" // mandatory - || surfaceDict.found("surfaces") // or optional - ) + "writeObj", + Switch::OFF + ); + const Switch writeVTK = surfaceDict.lookupOrDefault<Switch> + ( + "writeVTK", + Switch::OFF + ); + + // The "surfaces" entry is normally optional, but make it mandatory + // if the dictionary name doesn't have an extension + // (ie, probably not a surface filename at all). + // If it is missing, this will fail nicely with an appropriate error + // message. + if (surfaceDict.found("surfaces") || !dictName.hasExt()) { loader.select(wordReList(surfaceDict.lookup("surfaces"))); } @@ -128,39 +299,45 @@ int main(int argc, char *argv[]) loader.select(dictName); } + // DebugVar(loader.available()); + // DebugVar(outputName); + if (loader.selected().empty()) { FatalErrorInFunction << "No surfaces specified/found for entry: " << dictName << exit(FatalError); } - // DebugVar(loader.available()); - // DebugVar(outputName); - - Info<< "Surfaces : "; + Info<< "Surfaces : "; if (loader.selected().size() == 1) { - Info<< loader.selected()[0] << nl; + Info<< loader.selected().first() << nl; } else { Info<< flatOutput(loader.selected()) << nl; } - Info<< "Output : " << outputName << nl; + Info<< "Output : " << outputName << nl; + // Loading option - default depends on context triSurfaceLoader::loadingOption loadingOption = triSurfaceLoader::loadingOptionNames.lookupOrDefault ( "loadingOption", surfaceDict, - triSurfaceLoader::loadingOption::OFFSET_REGION + ( + selfIntersect == surfaceIntersection::SELF_REGION + ? triSurfaceLoader::FILE_REGION + : triSurfaceLoader::OFFSET_REGION + ) ); - Info<<"loading with " - << triSurfaceLoader::loadingOptionNames[loadingOption] - << endl; - + Info<<"Load options : " + << triSurfaceLoader::loadingOptionNames[loadingOption] << nl + << "Write options:" + << " writeObj=" << writeObj + << " writeVTK=" << writeVTK << nl; // Load a single file, or load and combine multiple selected files autoPtr<triSurface> surfPtr = loader.load(loadingOption); @@ -173,25 +350,12 @@ int main(int argc, char *argv[]) triSurface surf = surfPtr(); - const Switch writeVTK = surfaceDict.lookupOrDefault<Switch> - ( - "writeVTK", - Switch::OFF - ); - const Switch writeObj = surfaceDict.lookupOrDefault<Switch> - ( - "writeObj", - Switch::OFF - ); - - Info<< "write VTK: " << writeVTK << nl; - - Info<< "Feature line extraction is only valid on closed manifold " + Info<< "NB: Feature line extraction is only valid on closed manifold " << "surfaces." << nl; - Info<< nl << "Statistics:" << nl; + Info<< nl + << "Statistics:" << nl; surf.writeStats(Info); - Info<< nl; // Need a copy as plain faces if outputting VTK format faceList faces; @@ -403,14 +567,6 @@ int main(int argc, char *argv[]) feMesh.add(addFeMesh); } - const surfaceIntersection::intersectionType selfIntersect = - surfaceIntersection::selfIntersectionNames.lookupOrDefault - ( - "intersectionMethod", - surfaceDict, - surfaceIntersection::NONE - ); - if (selfIntersect != surfaceIntersection::NONE) { triSurfaceSearch query(surf); @@ -461,7 +617,8 @@ int main(int argc, char *argv[]) feMesh.write(); - // Write a featureEdgeMesh for backwards compatibility + // Write a featureEdgeMesh (.eMesh) for backwards compatibility + // Used by snappyHexMesh (JUN-2017) if (true) { featureEdgeMesh bfeMesh diff --git a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict index 89f5b9c6b78..176a0e19384 100644 --- a/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict +++ b/applications/utilities/surface/surfaceFeatureExtract/surfaceFeatureExtractDict @@ -16,7 +16,7 @@ FoamFile surface1.stl { - // How to obtain raw features (none | extractFromFile | extractFromSurface) + // Extract raw features (none | extractFromFile | extractFromSurface) extractionMethod extractFromSurface; // Mark edges whose adjacent surface normals are at an angle less @@ -28,37 +28,48 @@ surface1.stl // Do not mark region edges geometricTestOnly yes; - /* alternative specification as coeff dictionary - extractFromSurfaceCoeffs - { - includedAngle 120; + // Generate additional intersection features (none | self | region) + intersectionMethod none; + + // Tolerance for surface intersections + // tolerance 1e-3; + + // Output options: + + // Write features to obj format for postprocessing + writeObj yes; +} + + +// Self intersection (single or multiple surfaces). +// - Use 'surfaces' entry (a wordRe list) if it exists. +// - If the dictionary name does not have an extension, 'surfaces' is mandatory. +outputName1 +{ + extractionMethod none; - geometricTestOnly yes; - } */ + surfaces (surface1.stl surface2.nas); // Generate additional intersection features (none | self | region) - intersectionMethod none; + intersectionMethod self; // Tolerance for surface intersections - tolerance 1e-3; + // tolerance 1e-3; - // Write options + // Output options: - // Write features to obj format for postprocessing - writeObj yes; + // Write features to OBJ format for postprocessing + writeObj yes; } surface2.nas { - // How to obtain raw features (none | extractFromFile | extractFromSurface) + // Extract raw features (none | extractFromFile | extractFromSurface) extractionMethod extractFromFile; - extractFromFileCoeffs - { - // Load from an existing feature edge file - featureEdgeFile "constant/triSurface/featureEdges.nas"; - } + // Load from an existing feature edge file + featureEdgeFile "constant/triSurface/featureEdges.nas"; trimFeatures { @@ -71,16 +82,15 @@ surface2.nas subsetFeatures { - // Use a plane to select feature edges - // (normal)(basePoint) - // Keep only edges that intersect the plane will be included + // Use a plane to select feature edges (normal)(basePoint) + // Only keep edges that intersect the plane plane (1 0 0)(0 0 0); - // Select feature edges using a box - // (minPt)(maxPt) - // Keep edges inside the box: + // Select feature edges using a box // (minPt)(maxPt) + // Only keep edges inside the box: insideBox (0 0 0)(1 1 1); - // Keep edges outside the box: + + // Only keep edges outside the box: outsideBox (0 0 0)(1 1 1); // Keep nonManifold edges (edges with >2 connected faces where @@ -95,110 +105,35 @@ surface2.nas { // Add (without merging) another extendedFeatureEdgeMesh name axZ.extendedFeatureEdgeMesh; - - // Optionally flip features (invert all normals, making - // convex<->concave etc) - //flip false; } - // Output the curvature of the surface - curvature no; - - // Output the proximity of feature points and edges to each other - featureProximity no; - - // The maximum search distance to use when looking for other feature - // points and edges - maxFeatureProximity 1; - - // Out put the closeness of surface elements to other surface elements. - closeness no; // Generate additional intersection features (none | self | region) intersectionMethod none; // Tolerance for surface intersections - tolerance 1e-3; - - // Write options - - // Write features to obj format for postprocessing - writeObj yes; - - // Write surface proximity and curvature fields to vtk format - // for postprocessing - writeVTK no; -} - + // tolerance 1e-3; -// Handle single or multiple surfaces -// -// - If the dictionary is named 'surfaces', it must also contain a 'surfaces' -// entry (wordRe list). -// -// - If other dictionaries contain a 'surfaces' entry, -// it will be taken for the input. -// -dummyName -{ - extractionMethod extractFromSurface; - - surfaces (surface1.stl surface2.nas); + // Output options: - // Base output name (optional) - // output surfaces; - - // Generate additional intersection features (none | self | region) - intersectionMethod self; + // Output the closeness of surface elements to other surface elements. + closeness no; - // Tolerance for surface intersections - tolerance 1e-3; + // Output surface curvature + curvature no; - includedAngle 120; + // Output the proximity of feature points and edges to another + featureProximity no; - // Do not mark region edges - geometricTestOnly yes; + // The maximum search distance when checking feature proximity + maxFeatureProximity 1; - // Write options + // Write features to OBJ format for postprocessing + writeObj no; - // Write features to obj format for postprocessing - writeObj yes; + // Write closeness/curvature/proximity fields as VTK for postprocessing + writeVTK no; } -// Handle single or multiple surfaces -// -// - If the dictionary is named 'surfaces', it must also contain a 'surfaces' -// entry (wordRe list). -// -// - If other dictionaries contain a 'surfaces' entry, -// it will be taken for the input. -// -surfaces -{ - extractionMethod none; - - surfaces (surface1.stl surface2.nas); - - // Base output name (optional) - // output surfaces; - - // Generate additional intersection features (none | self | region) - intersectionMethod self; - - // Tolerance for surface intersections - tolerance 1e-3; - - /* alternative specification as coeff dictionary - noneCoeffs - { - includedAngle 0; - } */ - - // Write options - - // Write features to obj format for postprocessing - writeObj yes; -} - // ************************************************************************* // -- GitLab