diff --git a/applications/utilities/preProcessing/PDR/Allwclean b/applications/utilities/preProcessing/PDR/Allwclean index 9f1e7393322b44da5d6fd2253a86c4817c61fbff..4478b88ce6123d565bd97050c1a5a6bd764a67c4 100755 --- a/applications/utilities/preProcessing/PDR/Allwclean +++ b/applications/utilities/preProcessing/PDR/Allwclean @@ -1,7 +1,11 @@ #!/bin/sh cd "${0%/*}" || exit # Run from this directory +#------------------------------------------------------------------------------ wclean libso pdrFields + +wclean PDRfitMesh + wclean PDRsetFields #------------------------------------------------------------------------------ diff --git a/applications/utilities/preProcessing/PDR/Allwmake b/applications/utilities/preProcessing/PDR/Allwmake index 2131eed2f8dc738a95ed2155ade3a4bf27955b29..10618728d884d7d57f5cc1a00644a55979ccf96c 100755 --- a/applications/utilities/preProcessing/PDR/Allwmake +++ b/applications/utilities/preProcessing/PDR/Allwmake @@ -5,6 +5,9 @@ cd "${0%/*}" || exit # Run from this directory #------------------------------------------------------------------------------ wmake $targetType pdrFields + +wmake $targetType PDRfitMesh + wmake $targetType PDRsetFields #------------------------------------------------------------------------------ diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/files b/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..ccf95e298a1f7e8f1fbf02b86d5f782464c9b7f9 --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/files @@ -0,0 +1,7 @@ +PDRfitMeshParams.C +PDRfitMeshPlane.C +PDRfitMeshPlanes.C + +PDRfitMesh.C + +EXE = $(FOAM_APPBIN)/PDRfitMesh diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/options b/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/options new file mode 100644 index 0000000000000000000000000000000000000000..64d6ba56258273f7da3b85ec0dfa36f3afa5f51d --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/Make/options @@ -0,0 +1,15 @@ +EXE_INC = \ + -I../pdrFields/lnInclude \ + -I$(LIB_SRC)/finiteVolume/lnInclude \ + -I$(LIB_SRC)/fileFormats/lnInclude \ + -I$(LIB_SRC)/surfMesh/lnInclude \ + -I$(LIB_SRC)/meshTools/lnInclude \ + -I$(LIB_SRC)/mesh/blockMesh/lnInclude + +EXE_LIBS = \ + -lfiniteVolume \ + -lfileFormats \ + -lsurfMesh \ + -lmeshTools \ + -lblockMesh \ + -lpdrFields diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMesh.C b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMesh.C new file mode 100644 index 0000000000000000000000000000000000000000..2ef5dc2d92b77050c9924dde21119439793945cd --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMesh.C @@ -0,0 +1,346 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2016 Shell Research Ltd. + 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/>. + +Applications + PDRfitMesh + +Description + Scans extends of obstacles to establish reasonable estimates + for generating a PDRblockMeshDict. + +SourceFiles + PDRfitMesh.C + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "Time.H" +#include "IOdictionary.H" + +#include "PDRfitMeshParams.H" +#include "PDRfitMeshPlanes.H" +#include "PDRlegacy.H" +#include "PDRsetFields.H" + +using namespace Foam; + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Main program: + +int main(int argc, char* argv[]) +{ + argList::addNote + ( + "Processes a set of geometrical obstructions to determine" + " reasonable estimates for generating a PDRblockMeshDict" + ); + argList::noParallel(); + argList::noFunctionObjects(); + + argList::addOption("dict", "file", "Alternative PDRsetFieldsDict"); + argList::addOption("params", "file", "Alternative PDRfitMeshDict"); + argList::addBoolOption + ( + "overwrite", + "Overwrite existing system/PDRblockMeshDict" + ); + + argList::addBoolOption("verbose", "Increase verbosity"); + + argList::addBoolOption + ( + "legacy", + "Force use of legacy obstacles table" + ); + + argList::addBoolOption + ( + "dry-run", + "Read obstacles, write VTK and display output" + ); + + argList::addBoolOption + ( + "print-dict", + "Print PDRblockMeshDict equivalent and exit" + ); + + #include "setRootCase.H" + #include "createTime.H" + + const bool dryrun = args.found("dry-run"); + const bool printDict = args.found("print-dict"); + + const word dictName("PDRsetFieldsDict"); + #include "setSystemRunTimeDictionaryIO.H" + + Info<< "Reading " << dictIO.name() << nl << endl; + + IOdictionary setFieldsDict(dictIO); + + const fileName& casepath = runTime.globalPath(); + + // Program parameters (globals) + pars.read(setFieldsDict); + + if (args.found("legacy")) + { + pars.legacyObsSpec = true; + } + + + // Params for fitMesh + // - like setSystemRunTimeDictionaryIO + + IOobject paramIO = IOobject::selectIO + ( + IOobject + ( + "PDRfitMeshDict", + runTime.system(), + runTime, + IOobject::MUST_READ_IF_MODIFIED, + IOobject::NO_WRITE + ), + args.getOrDefault<fileName>("params", "") + ); + + if (paramIO.typeHeaderOk<IOdictionary>(true)) + { + Info<< "Using PDRfitMesh parameters from " + << runTime.relativePath(paramIO.objectPath()) << nl + << endl; + } + else + { + paramIO.readOpt() = IOobject::NO_READ; + Info<< "No PDRfitMeshDict found, using defaults" << nl + << endl; + } + + + IOdictionary fitParamsDict(paramIO, dictionary()); + + PDRfitMeshParams fitParams(fitParamsDict); + + + // Essential parameters + + scalar cellWidth = 0; + + if + ( + !setFieldsDict.readIfPresent("cellWidth", cellWidth) + && !fitParamsDict.readIfPresent("cellWidth", cellWidth) + ) + { + FatalErrorInFunction + << "No cellWidth specified in any dictionary" << nl + << exit(FatalError); + } + + + // Storage for obstacles and cylinder-like obstacles + DynamicList<PDRobstacle> obstacles, cylinders; + + // Read in obstacles + if (pars.legacyObsSpec) + { + PDRobstacle::legacyReadFiles + ( + pars.obsfile_dir, pars.obsfile_names, + boundBox::invertedBox, + obstacles, + cylinders + ); + } + else + { + PDRobstacle::readFiles + ( + pars.obsfile_dir, pars.obsfile_names, + boundBox::invertedBox, + obstacles, + cylinders + ); + } + + + // + // Output names + // + + IOobject outputIO + ( + "PDRblockMeshDict", + runTime.system(), + runTime, + IOobject::NO_READ, + IOobject::NO_WRITE, + false // unregistered + ); + + + enum class writeType { DRY_RUN, OKAY, OVERWRITE, NO_CLOBBER }; + + writeType writable = writeType::OKAY; + + if (dryrun || printDict) + { + writable = writeType::DRY_RUN; + } + else if (isFile(outputIO.objectPath())) + { + if (args.found("overwrite")) + { + writable = writeType::OVERWRITE; + } + else + { + writable = writeType::NO_CLOBBER; + InfoErr + << nl + << "File exists: " + << runTime.relativePath(outputIO.objectPath()) << nl + << "Move out of the way or specify -overwrite" << nl + << nl + << "Exiting" << nl + << nl; + + return 1; + } + } + else + { + writable = writeType::OKAY; + } + + + if (dryrun) + { + PDRobstacle::generateVtk(casepath/"VTK", obstacles, cylinders); + } + + + // + // The fitting routines + // + + PDRfitMeshPlane::verbose_ = args.found("verbose"); + + Vector<PDRblock::gridControl> griding = + PDRfitMeshPlanes().calcGriding + ( + {obstacles, cylinders}, + fitParams, + cellWidth + ); + + + // + // Output + // + + if (writable == writeType::DRY_RUN) + { + InfoErr + << nl + << "dry-run: displaying equivalent PDRblockMeshDict" << nl + << nl; + } + else if (writable == writeType::OKAY) + { + InfoErr + << nl + << "Write " + << runTime.relativePath(outputIO.objectPath()) << nl; + } + else if (writable == writeType::OVERWRITE) + { + InfoErr + << nl + << "Overwrite existing " + << runTime.relativePath(outputIO.objectPath()) << nl; + } + // NO_CLOBBER already handled + + + { + autoPtr<Ostream> outputFilePtr; + + if + ( + writable == writeType::OKAY + || writable == writeType::OVERWRITE + ) + { + outputFilePtr.reset(new OFstream(outputIO.objectPath())); + } + + Ostream& os = bool(outputFilePtr) ? *outputFilePtr : Info(); + + outputIO.writeHeader(os, IOdictionary::typeName_()); + + os.writeEntry + ( + "expansion", + PDRblock::expansionNames_[PDRblock::EXPAND_RELATIVE] + ); + os << nl; + + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + griding[cmpt].writeDict(os, cmpt); + } + + const dictionary* outerDict; + + if + ( + (outerDict = setFieldsDict.findDict("outer")) != nullptr + || (outerDict = fitParamsDict.findDict("outer")) != nullptr + ) + { + outerDict->writeEntry(os); + } + else + { + // Define our own "outer" + os.beginBlock("outer"); + os.writeEntry("type", "none"); + os.endBlock(); + } + + IOobject::writeEndDivider(os); + } + + Info<< nl << "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshDict b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshDict new file mode 100644 index 0000000000000000000000000000000000000000..b29f67f2cb034b7cefa667a223abd37c328c576c --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshDict @@ -0,0 +1,126 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object PDRfitMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Tuning parameters (defaults) for PDRfitMesh +// - these normally do not need much changing + + +// Note the following are normally read from PDRsetFieldsDict +// +// - cellWidth +// - cellWidthFactor +// - outer (contains optional zmin) +// + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +// ---------------------------------------------------------------------- +// Finding planes +// ---------------------------------------------------------------------- + +// User defined bounds +bounds +{ + // xmin -1e16; + // xmax 1e16; + + // ymin -1e16; + // ymax 1e16; + + // zmin -1e16; + // zmax 1e16; +} + + +outer +{ + //- Ground datum (hard zmin) + // zmin -1e16; +} + + +// ---------------------------------------------------------------------- +// Finding planes +// ---------------------------------------------------------------------- + + +// Ignore faces smaller than this multiple of cell face area +minFaceArea 5.0; + +// Only fit a plane if the face area at the coordinate is at least +// this times the cell face area +minAreaRatio 20.0; + +// Size of search zones for face areas will be this * the cell width. +// So faces closer than this zone size might be grouped together +areaWidthFactor 0.7; + + +// Very long zones produce bad outer boundary shape from +// makePDRmeshBlocks, so we subdivide a zone if its length is greater +// than this * the height of the area of cuboid vells +maxZoneToHeight 2.0; + + + +// ---------------------------------------------------------------------- +// Choosing cell width +// +// <cellWidth> and <cellWidthFactor> read from PDRsetFieldsDict +// ---------------------------------------------------------------------- + +// Minimum number of cells in any direction +nCellsMin 10; + +// Width of obstacles must be less than this * cell width to added +// into subgrid length +widthFactor 1.0; + +// Optimal average number of obstacles per cell +obsPerCell 2.0; + +//- Maximum cellWidth when auto-sizing +maxCellWidth 1e16; + +// Do not use a cellWidth more than this times the initial estimate +maxWidthEstimate 5.0; + +// Assume converged if optimised width changes by less than this +maxWidthRatio 1.2; + +// Maximum iterations in optimising cellWidth +maxIterations 5; + + +// ---------------------------------------------------------------------- +// Defining outer region +// +// ---------------------------------------------------------------------- + +// Fraction of rectangular cell layers each side of the central region. +edgeLayers 5.0; + + +// FUTURE? +// +// // Ratio of the outer extension distance to the average radius of the +// // congested region +// ratioOuterToCongRad 20.0; +// +// // Cell size ratio - should be same as that hard-wired in makePDRmeshBlocks +// outerRatio 1.2; + + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.C b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.C new file mode 100644 index 0000000000000000000000000000000000000000..a83577764d233a02d373f08bffbe27b503d9ec4d --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.C @@ -0,0 +1,123 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / 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 "PDRfitMeshParams.H" + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::PDRfitMeshParams::readBounds(const dictionary& dict) +{ + scalar value; + + if (dict.readIfPresent("xmin", value)) + { + minBounds.x() = optionalData<scalar>(value); + } + if (dict.readIfPresent("ymin", value)) + { + minBounds.y() = optionalData<scalar>(value); + } + if (dict.readIfPresent("zmin", value)) + { + minBounds.z() = optionalData<scalar>(value); + } + + if (dict.readIfPresent("xmax", value)) + { + maxBounds.x() = optionalData<scalar>(value); + } + if (dict.readIfPresent("ymax", value)) + { + maxBounds.y() = optionalData<scalar>(value); + } + if (dict.readIfPresent("zmax", value)) + { + maxBounds.z() = optionalData<scalar>(value); + } +} + + +// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // + +Foam::PDRfitMeshParams::PDRfitMeshParams(const dictionary& dict) +{ + read(dict); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::PDRfitMeshParams::read(const dictionary& dict) +{ + const dictionary* dictptr; + + // Geometric limits + + if ((dictptr = dict.findDict("bounds")) != nullptr) + { + readBounds(*dictptr); + } + + if ((dictptr = dict.findDict("outer")) != nullptr) + { + const dictionary& d = *dictptr; + + d.readIfPresent("zmin", ground); + } + + + // Finding planes + + dict.readIfPresent("minFaceArea", minFaceArea); + dict.readIfPresent("minAreaRatio", minAreaRatio); + dict.readIfPresent("areaWidthFactor", areaWidthFactor); + dict.readIfPresent("maxZoneToHeight", maxZoneToHeight); + + + // Choosing cell width + + dict.readIfPresent("nCellsMin", nCellsMin); + dict.readIfPresent("widthFactor", widthFactor); + dict.readIfPresent("obsPerCell", obsPerCell); + dict.readIfPresent("maxCellWidth", maxCellWidth); + dict.readIfPresent("maxWidthEstimate", maxWidthEstimate); + dict.readIfPresent("maxWidthRatio", maxWidthRatio); + dict.readIfPresent("maxIterations", maxIterations); + + + // outer region + + dict.readIfPresent("edgeLayers", edgeLayers); + + dict.readIfPresent("outerRatio", outerRatio); + + // dict.readIfPresent("outerRadius", outerRadius); +}; + + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.H b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.H new file mode 100644 index 0000000000000000000000000000000000000000..b27846e27d6092772e544efd7b4e8db2510add5b --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshParams.H @@ -0,0 +1,158 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / 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::PDRfitMeshParams + +Description + Parameters for PDRfitMesh + +SourceFiles + PDRfitMeshParams.C + +\*---------------------------------------------------------------------------*/ + +#ifndef PDRfitMeshParams_H +#define PDRfitMeshParams_H + +#include "labelList.H" +#include "scalarList.H" +#include "dictionary.H" +#include "optionalData.H" +#include "MinMax.H" +#include "Vector.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class PDRfitMeshParams Declaration +\*---------------------------------------------------------------------------*/ + +class PDRfitMeshParams +{ + // Private Member Functions + + //- Read optional bounds + void readBounds(const dictionary& dict); + + +public: + + // Data Members + + //- Optional user bounds (xmin, xmax, ymin, ymax, zmin, zmax) + Vector<optionalData<scalar>> minBounds, maxBounds; + + //- Ground datum (hard zmin) + scalar ground = -1e16; + + + // Finding planes + + //- Ignore faces smaller than this multiple of cell face area + scalar minFaceArea = 5.0; + + //- Only fit a plane if the face area at the coordinate is at + //- least this times the cell face area + scalar minAreaRatio = 20.0; + + //- Size of search zones for face areas will be this * the + //- cell width. + // Faces closer than this zone size may be grouped together + scalar areaWidthFactor = 0.7; + + // Very long zones produce bad outer boundary shape from + // makePDRmeshBlocks, so we subdivide a zone if its length is + // greater than this * the height of the area of cuboid vells + scalar maxZoneToHeight = 2.0; + + + // Choosing cell width + + // Minimum number of cells in any direction + label nCellsMin = 10; + + // Width of obstacles must be less than this * cell width to + // added into subgrid length + scalar widthFactor = 1.0; + + // Optimal average number of obstacles per cell + scalar obsPerCell = 2.0; + + //- Maximum cellWidth when auto-sizing + scalar maxCellWidth = 1e16; + + //- Do not use a cellWidth more than this times the initial estimate + scalar maxWidthEstimate = 5.0; + + //- Converged if optimised width changes by less than this amount + scalar maxWidthRatio = 1.2; + + //- Maximum iterations in optimising cellWidth + label maxIterations = 5; + + + // Outer region + + //- Fraction of rectangular cell layers on each side of the + //- central region + scalar edgeLayers = 5; + + //- Cell size ratio - should be same as used for PDRblockMesh + scalar outerRatio = 1.2; + +//FUTURE? // Ratio of the outer extension distance to the average +//FUTURE? // radius of the congested region +//FUTURE? scalar outerRadius = 20.0; + + + // Constructors + + //- Default construct + PDRfitMeshParams() = default; + + //- Construct and read dictionary + explicit PDRfitMeshParams(const dictionary& dict); + + + // Member Functions + + //- Read program parameters from dictionary + void read(const dictionary& dict); +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.C b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.C new file mode 100644 index 0000000000000000000000000000000000000000..740a50b062f5e34be0bdf51c5104b76a26618051 --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.C @@ -0,0 +1,831 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2016 Shell Research Ltd. + 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 "PDRfitMeshPlane.H" +#include "PDRfitMeshParams.H" +#include "PDRobstacle.H" + +// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // + +bool Foam::PDRfitMeshPlane::verbose_ = false; + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::PDRfitMeshPlane::addCoord +( + const scalar coord, + const scalar weight +) +{ + if (!limits.contains(coord)) + { + return; + } + + const scalar coordOffset = (coord - limits.min()); + + if (coordOffset < 0) + { + FatalErrorInFunction + << "Negative coordinate! " << coordOffset << nl + << exit(FatalError); + } + + // We divide the range into a series of steps, and decide + // which step this coord is in + + + // Note: would be simpler to record the totals only in the + // half steps in this routine then get the full-step values + // at the end by adding half steps + + + // Add the area to the total already found in this step + // and update the weighted average position of these obstacle faces + + const label stepi1 = 2*floor(coordOffset / stepWidth_); + + + // Also do ranges half a step offset in case several faces + // are around the step boundary. + // + // Stored in odd-numbered elements + + const label stepi2 = + Foam::max + ( + 0, + 1 + 2*floor((coordOffset - 0.5*stepWidth_) / stepWidth_) + ); + + + // Last, keep track of totals in half-step ranges + + const label stepih = floor(coordOffset / stepWidth_); + + + const label maxSize = Foam::max(stepi1, stepi2); + + if (weightedPos_.size() <= maxSize) + { + weightedPos_.resize(maxSize+1, Zero); + totalArea_.resize(maxSize+1, Zero); + + weightedPos2_.resize(maxSize+1, Zero); + totalArea2_.resize(maxSize+1, Zero); + } + + weightedPos_[stepi1] += (coord * weight); + totalArea_[stepi1] += weight; + + weightedPos_[stepi2] += (coord * weight); + totalArea_[stepi2] += weight; + + weightedPos2_[stepih] += (coord * weight); + totalArea2_[stepih] += weight; +} + + +// void Foam::PDRfitMeshPlane::clear() +// { +// weightedPos_.clear(); totalArea_.clear(); +// weightedPos2_.clear(); totalArea2_.clear(); +// } + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +void Foam::PDRfitMeshPlane::reset() +{ + limits.min() = GREAT; + limits.max() = -GREAT; + + hard_min = -GREAT; + + nsteps_ = 0; + stepWidth_ = 0; + + weightedPos_.clear(); + totalArea_.clear(); + + weightedPos2_.clear(); + totalArea2_.clear(); +} + + +void Foam::PDRfitMeshPlane::resize +( + const scalar cellWidth, + const PDRfitMeshParams& pars +) +{ + if (limits.valid()) + { + nsteps_ = + ( + limits.mag() / (mag(cellWidth) * pars.areaWidthFactor) + + 0.5 + ); + + nsteps_ = max(nsteps_, pars.nCellsMin); + + stepWidth_ = limits.mag() / nsteps_; + + minFaceArea = pars.minFaceArea * sqr(stepWidth_); + } + else + { + nsteps_ = 0; + stepWidth_ = 0; + minFaceArea = 0; + + WarningInFunction + << "No valid limits defined" << endl; + } + + weightedPos_.clear(); + totalArea_.clear(); + + weightedPos2_.clear(); + totalArea2_.clear(); + + // resize + weightedPos_.resize(2*nsteps_+1, Zero); + totalArea_.resize(2*nsteps_+1, Zero); + + weightedPos2_.resize(2*nsteps_+1, Zero); + totalArea2_.resize(2*nsteps_+1, Zero); +} + + +void Foam::PDRfitMeshPlane::adjustLimits +( + const scalar point0, + const scalar point1 +) +{ + limits.add(point0); + limits.add(point1); +} + + +void Foam::PDRfitMeshPlane::addArea +( + const scalar area, + const scalar point0, + const scalar point1 +) +{ + if (!nsteps_) + { + FatalErrorInFunction + << "No step-size defined" << nl + << exit(FatalError); + } + + if (area < minFaceArea * sqr(stepWidth_)) + { + return; + } + + addCoord(point0, area); + addCoord(point1, area); +} + + +void Foam::PDRfitMeshPlane::print(Ostream& os) const +{ + if (nsteps_) + { + os << "steps:" << nsteps_; + } + + if (limits.valid()) + { + os << " limits:" << limits; + } + + scalar totalArea = 0; + for (const scalar areaValue : totalArea_) + { + totalArea += areaValue; + } + + os << " area:" << totalArea; + os << nl; +} + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Evaluates ( r**n - 1) / ( r - 1 ) including in the limit r=1 +inline static scalar rn1r1(scalar r, label n) +{ + if (mag(r-1.0) < SMALL) + { + return n; + } + + return (pow(r, n) - 1.0)/(r - 1.0); +} + + +inline static scalar end_val(scalar width, scalar ratio, label n) +{ + return width / rn1r1(ratio, n); +} + + +static scalar fit_slope(scalar left, scalar width, label n, scalar& r) +{ + const scalar wol = width / left; + + // Initial value + if (left < width / n) + { + r = 1.01; + } + else + { + r = 0.99; + } + + for (int nIter = 0; nIter < 25; ++nIter) + { + scalar rm1 = r - 1.0; + scalar rn = pow(r, n ); + scalar f = (rn - 1.0 ) * r / rm1 - wol; + scalar fprime = ((n * rm1 - 1) * rn + 1) / (rm1 * rm1); + + scalar new_r = r - f / fprime; + scalar delta = mag(new_r - r); + + r = 0.5 * (r + new_r ); + + // InfoErr + // << "New r: " << new_r << ' ' + // << f << ' ' << fprime << ' ' << delta << ' ' << nIter << nl; + + if (delta <= 1e-3) + { + break; + } + } + + return end_val(width, 1.0/r, n); +} + +} // End namespace Foam + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::PDRblock::gridControl +Foam::PDRfitMeshPlane::calcGridControl +( + const scalar cellWidth, + const scalar max_zone, + const PDRfitMeshParams& pars +) +{ + // Prune out small areas + { + const scalar minArea = sqr(cellWidth) * pars.minAreaRatio; + if (verbose_) + { + Info<< "Checking planes. Min-area: " << minArea << nl; + } + + for (scalar& areaValue : totalArea_) + { + if (areaValue < minArea) + { + areaValue = 0; + } + } + + for (scalar& areaValue : totalArea2_) + { + if (areaValue < minArea) + { + areaValue = 0; + } + } + } + + + // Initialy only the two outer boundaries and absolute limits + + DynamicList<scalar> plane(nsteps_+1); + + plane.resize(4); + plane[0] = -GREAT; + plane[1] = Foam::max(limits.min(), hard_min); + plane[2] = limits.max(); + plane[3] = GREAT; + + + // Select largest areas for fitting. + // - done manually (instead of via sort) to handle full/half-steps + // - find the largest area, add that to the list and remove its + // area (and immediate surroundings) from the input. + + while (plane.size() < totalArea_.size()) + { + scalar largest = 0; + label n_big = 0; + + for (label ii=0; ii < 2*nsteps_; ++ii) + { + if (largest < totalArea_[ii]) + { + largest = totalArea_[ii]; + n_big = ii; + } + if (largest < totalArea2_[ii]) + { + largest = totalArea2_[ii]; + n_big = -ii-1; + } + } + + if (mag(largest) < SMALL) + { + break; + } + + // We have the "full steps overlapping so that we do not miss + // a group of adjacent faces that are split over a boundary. + // When we have identified a plane, we need to zero that + // step, and also the overlapping ones to avoid + // double-counting. when we have done that twice, we might + // have completely removed the half-step that was between + // them. That is why we also have the areas stored in + // half-steps, so that is not lost. + + if (n_big >= 0) + { + // Full step + + const scalar pos = averagePosition(n_big); + + // Remove from future checks + totalArea_[n_big] = 0; + + if (pos > hard_min + 0.4 * cellWidth) + { + // Only accept if not close to the ground + + plane.append(pos); + + // Remove this and adjacent areas, which overlap this one + if (n_big > 1) + { + totalArea_[n_big-1] = 0; + } + if (n_big < 2*nsteps_ - 1) + { + totalArea_[n_big+1] = 0; + } + + // Remove half-steps contained by this step + totalArea2_[n_big] = 0; + totalArea2_[n_big+1] = 0; + } + } + else + { + // Half step + n_big = -n_big-1; + + const scalar pos = averageHalfPosition(n_big); + + // Remove from future checks + totalArea2_[n_big] = 0; + + // On the first passes, this area will be included in full steps, + // but it may eventually only remain in the half-step, + // which could be the next largest + + if (pos > hard_min + 0.4 * cellWidth) + { + // Only accept if not close to the ground + + plane.append(pos); + } + } + } + + + // Now sort the positions + Foam::sort(plane); + + if ((plane[2] - plane[1]) < 0.4 * cellWidth) + { + // Remove [1] if too close to [2] + plane.remove(1); + } + + if ((plane[plane.size()-2] - plane[plane.size()-3] ) < 0.4 * cellWidth) + { + // Remove [N-3] if too close to [N-2] + plane.remove(plane.size()-3); + } + + + label n_planes = plane.size(); + + + // This is somewhat questionable (horrible mix of C and C++ addressing). + // + // If we have specified a max_zone (max number of cells per segment) + // we will actually generate more divisions that originally + // anticipated. + + // So over-dimension the arrays by a larger factor (eg, 100) + // which is generally enough. + + // FIXME: Needs revisiting (2020-12-16) + + scalarList width(20*n_planes); + + labelList i_steps(width.size()); + + // + // Set up initially uniform zones + // + for (label ii=1; ii < (n_planes-2); ++ii) + { + width[ii] = plane[ii+1] - plane[ii]; + i_steps[ii] = width[ii] / stepWidth_ * pars.areaWidthFactor + 0.5; + + while (i_steps[ii] < 1) + { + // Planes too close - merge them + --n_planes; + + plane[ii] = 0.5 * (plane[ii+1] + plane[ii]); + width[ii-1] = plane[ii] - plane[ii-1]; + + i_steps[ii-1] = + width[ii-1] / stepWidth_ * pars.areaWidthFactor + 0.5; + + if (ii < (n_planes - 2)) + { + for (label iii=ii+1; iii < n_planes-1; ++iii) + { + plane[iii] = plane[iii+1]; + } + width[ii] = plane[ii+1] - plane[ii]; + + i_steps[ii] = + width[ii] / stepWidth_ * pars.areaWidthFactor + 0.5; + } + else + { + i_steps[ii] = 1; // Dummy value to terminate while + } + } + } + + + // Initially all steps have equal width (ratio == 1). + + scalarList first(width.size()); + + for (label ii=1; ii < (n_planes-2); ++ii) + { + first[ii] = width[ii] / i_steps[ii]; + } + scalarList last(first); + + // + // Now adjust the inter-cell ratios to reduce cell-size changes + // between zones + // + + scalarList ratio(width.size(), scalar(1)); + + // For diagnostics + charList marker(width.size(), char('x')); + marker[0] = '0'; + + if (n_planes > 5) + { + // The outer zones can be adjusted to fit the adjacent steps + + last[1] = first[2]; + first[n_planes-3] = last[n_planes-4]; + + // We adjust the cell size ratio in each zone so that the + // cell sizes at the ends of the zones fit as well as + // possible with their neighbours. Work forward through the + // zones and then repeat a few times so that the adjustment + // settles down. + + for (int nIter = 0; nIter < 2; ++nIter) + { + for (label ii=1; ii < (n_planes-2); ++ii) + { + if (i_steps[ii] == 1) + { + // If only one step, no grading is possible + + marker[ii] = '1'; // For Diagnostics + continue; + } + marker[ii] = 'L'; // For Diagnostics + + // Try making the in-zone steps equal to the step at + // the left end to the last of the previous zone. + + // If the step at the right is less than this, then + // good... (fF this is the penultimate zone, we do + // not need to worry about the step on the right + // because the extent of the last zone can be + // adjusted later to fit.) + + scalar r_fit = 1; + + if + ( + ( + (ii > 1) + && mag + ( + log + ( + fit_slope + ( + last[ii-1], + width[ii], + i_steps[ii], + r_fit + ) + / first[ii+1] + ) + ) + < mag(log(r_fit)) + ) + || (ii == n_planes-3) + ) + { + ratio[ii] = r_fit; + } + else + { + marker[ii] = 'R'; + + // otherwise try making the in-zone steps equal to + // the step at the right end to the last of the + // previous zone. If the step at the right is less + // thhan this, then good... (If this is the second + // zone, we do not need to worry about the step on + // the left because the extent of the first zone + // can be adjusted later to fit.) + + if + ( + mag + ( + log + ( + fit_slope + ( + first[ii+1], + width[ii], + i_steps[ii], + r_fit + ) + / last[ii-1] + ) + ) + < mag(log(r_fit)) + + || (ii == 1) + ) + { + ratio[ii] = 1.0 / r_fit; + } + else + { + ratio[ii] = + pow(first[ii+1] / last[ii-1], 1.0/(i_steps[ii]-1)); + + marker[ii] = 'M'; + } + } + + first[ii] = end_val(width[ii], ratio[ii], i_steps[ii] ); + last[ii] = end_val(width[ii], 1.0/ratio[ii], i_steps[ii] ); + } + }; + + + // Adjust the ratio and the width in the first zone to + // grade from cellWidth at the outside to the first cell of + // the next zone. Increase the number of steps to keep edge + // at least two (edgeLayers) cell widths from min. + } + + + if (plane[1] > hard_min + first[1] / pars.outerRatio) + { + i_steps[0] = pars.edgeLayers; + ratio[0] = 1.0 / pars.outerRatio; + plane[0] = plane[1] - first[1] * rn1r1( pars.outerRatio, i_steps[0]); + + // Reduce the number of steps if we have extended below hard_min + while (plane[0] < hard_min && i_steps[0] > 1) + { + --i_steps[0]; + plane[0] = plane[1] - first[1] * rn1r1(pars.outerRatio, i_steps[0]); + } + + if (i_steps[0] < pars.edgeLayers) + { + // We had to adjust for hard_min, so now fit exaxtly + plane[0] = hard_min; + } + } + else + { + // No room for outer zone. Remove it. + --n_planes; + + for (label ii = 0; ii < n_planes; ++ii) + { + plane[ii] = plane[ii+1]; + i_steps[ii] = i_steps[ii+1]; + ratio[ii] = ratio[ii+1]; + first[ii] = first[ii+1]; + last[ii] = last[ii+1]; + } + plane[0] = hard_min; + } + + width[0] = plane[1] - plane[0]; + first[0] = end_val(width[0],ratio[0],i_steps[0]); + last[0] = end_val(width[0],1.0/ratio[0],i_steps[0]); + + + // Now do the upper edge zone + { + const label np2 = n_planes - 2; + + ratio[np2] = pars.outerRatio; + i_steps[np2] = pars.edgeLayers; + + plane[np2+1] = + plane[np2] + last[np2-1] * rn1r1(pars.outerRatio, i_steps[np2] ); + + width[np2] = plane[np2+1] - plane[np2]; + + first[np2] = last[np2-1]; + last[np2] = first[np2] * pow(pars.outerRatio, i_steps[np2] - 1); + } + + + // If we have a zone along the length of the plant that is much + // longer than the width or height, then makePDRMeshBlocks does + // not produce a very good outer boundary shape. so here we + // divide the zone into several zones of roughly equal width. A + // bit commplicated because of the increasing or decreasing step + // sizes across the zone. + + if (max_zone > 0) + { + for (label ii = 0; ii < (n_planes-1); ++ii) + { + if (width[ii] > max_zone && i_steps[ii] > 1) + { + // No. of extra zones + const label n_extra = width[ii] / max_zone - 1; + + // Make space for extra zones + for (label ij = n_planes-1; ij > ii; --ij) + { + width[ij+n_extra] = width[ij]; + ratio[ij+n_extra] = ratio[ij]; + first[ij+n_extra] = first[ij]; + last[ij+n_extra] = last[ij]; + i_steps[ij+n_extra] = i_steps[ij]; + plane[ij+n_extra] = plane[ij]; + } + n_planes += n_extra; + + const scalar sub_w = width[ii] / (n_extra + 1); + scalar bdy = plane[ii]; + + last[ii+n_extra] = last[ii]; + i_steps[ii+n_extra] = i_steps[ii]; // will be decremented + width[ii+n_extra] = width[ii]; // will be decremented + + // Current position + scalar coord = plane[ii]; + scalar step = first[ii]; + + // Look for cell boundaries close to where we want + // to put the zone boundaries + for (label ij=ii; ij < ii+n_extra; ++ij) + { + label ist = 0; + bdy += sub_w; + do + { + coord += step; + step *= ratio[ii]; + ++ist; + } while ((coord + 0.5 * step) < bdy); + + // Found a cell boundary at coord that is close to bdy + + // Create this sub-zone + last[ij] = step / ratio[ii]; + first[ij+1] = step; + plane[ij+1] = coord; + width[ij] = plane[ij+1] - plane[ij]; + i_steps[ij] = ist; + ratio[ij+1] = ratio[ij]; + + // Decrement what remaons for the last zub-zone + i_steps[ii+n_extra] -= ist; + width[ii+n_extra] -= width[ij]; + } + + ii += n_extra; + } + } + } + + + if (verbose_) + { + printf("Zone Cell widths Ratios\n"); + printf("start first last left mid right\n"); + for (label ii = 0; ii < (n_planes-1); ++ii) + { + printf + ( + "%6.2f %6.2f %6.2f %c %6.2f %6.2f %6.2f\n", + plane[ii], first[ii], last[ii], + marker[ii], + first[ii]/last[ii-1], ratio[ii], first[ii+1]/last[ii] + ); + } + printf("%6.2f\n", plane[n_planes-1]); + } + + + // Copy back into a gridControl form + + PDRblock::gridControl grid; + + grid.resize(n_planes); + for (label i = 0; i < n_planes; ++i) + { + grid[i] = plane[i]; + } + for (label i = 0; i < n_planes-1; ++i) + { + grid.divisions_[i] = i_steps[i]; + } + for (label i = 0; i < n_planes-1; ++i) + { + grid.expansion_[i] = ratio[i]; + } + + return grid; +} + + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.H b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.H new file mode 100644 index 0000000000000000000000000000000000000000..35cdfba0bc408852e5ac3bef2fba3216d5d578f7 --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlane.H @@ -0,0 +1,172 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / 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::PDRfitMeshPlane + +Description + Handling for PDRfitMesh + +SourceFiles + PDRfitMeshPlane.C + +\*---------------------------------------------------------------------------*/ + +#ifndef PDRfitMeshPlane_H +#define PDRfitMeshPlane_H + +#include "scalarList.H" +#include "DynamicList.H" +#include "MinMax.H" +#include "PDRblock.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +// Forward Declarations +class PDRfitMeshParams; +class PDRobstacle; + +/*---------------------------------------------------------------------------*\ + Class PDRfitMeshPlane Declaration +\*---------------------------------------------------------------------------*/ + +//- A single mesh plane +class PDRfitMeshPlane +{ + // Private Data + + DynamicList<scalar> weightedPos_, totalArea_; + DynamicList<scalar> weightedPos2_, totalArea2_; + + + // Private Member Functions + + //- Area-averaged position at index + scalar averagePosition(const label i) const + { + return + ( + totalArea_[i] < VSMALL + ? 0 + : (weightedPos_[i] / totalArea_[i]) + ); + } + + //- Area-averaged half-position at index + scalar averageHalfPosition(const label i) const + { + return + ( + totalArea2_[i] < VSMALL + ? 0 + : (weightedPos2_[i] / totalArea2_[i]) + ); + } + + + //- Add coordinate and weight. Already sanity checked + void addCoord(const scalar coord, const scalar weight); + + +public: + + // Public Data + + //- Output verbosity + static bool verbose_; + + //- Reuse gridControl + typedef PDRblock::gridControl gridControl; + + + // Data Members + + //- Lower and upper limits + scalarMinMax limits; + + scalar hard_min; + + scalar minFaceArea = 0; + + label nsteps_; + scalar stepWidth_; + + + // Constructors + + //- Default construct + PDRfitMeshPlane() + : + limits(GREAT, -GREAT), + hard_min(-GREAT), + nsteps_(0), + stepWidth_(0) + {} + + + // Member Functions + + //- Reset to initial state + void reset(); + + //- Adjust limits to accommodate the specified point coordinates + void adjustLimits(const scalar point0, const scalar point1); + + //- Define number of steps (and step-size) according to the + //- current limits and the specified parameters + void resize(const scalar cellWidth, const PDRfitMeshParams& pars); + + void addArea + ( + const scalar weight, + const scalar point0, + const scalar point1 + ); + + //- Calculate equivalent grid control from the scanned planes + PDRblock::gridControl + calcGridControl + ( + const scalar cellWidth, + const scalar max_zone, + const PDRfitMeshParams& pars + ); + + void print(Ostream& os) const; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.C b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.C new file mode 100644 index 0000000000000000000000000000000000000000..e6a7cec08bdb4f0d9d83004ae0a6bb7dba7ea9aa --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.C @@ -0,0 +1,466 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | www.openfoam.com + \\/ M anipulation | +------------------------------------------------------------------------------- + Copyright (C) 2016 Shell Research Ltd. + 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 "PDRfitMeshPlanes.H" +#include "PDRfitMeshParams.H" +#include "PDRobstacle.H" + + +// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // + +void Foam::PDRfitMeshPlanes::prepare +( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth +) +{ + this->reset(); + + // Scan for obstacle limits + for (const UList<PDRobstacle>& list : obstacles) + { + this->scanLimits(list); + } + + // Clip with optional user bounds + + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + scalarMinMax& limits = (*this)[cmpt].limits; + + if (fitParams.minBounds[cmpt].has_value()) + { + limits.min() = fitParams.minBounds[cmpt].value(); + } + if (fitParams.maxBounds[cmpt].has_value()) + { + limits.max() = fitParams.maxBounds[cmpt].value(); + } + } + + // Get obstacle locations and subgrid length (if any) + scanAreas(obstacles, fitParams, cellWidth); +} + + +// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // + +Foam::scalar Foam::PDRfitMeshPlanes::volume() const +{ + scalar vol = 1; + + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + const scalarMinMax& limits = (*this)[cmpt].limits; + + if (limits.valid()) + { + vol *= limits.mag(); + } + else + { + return 0; + } + } + + return vol; +} + + +Foam::scalar Foam::PDRfitMeshPlanes::cellVolume() const +{ + scalar vol = 1; + + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + vol *= (*this)[cmpt].stepWidth_; + } + + return vol; +} + + +void Foam::PDRfitMeshPlanes::reset() +{ + subgridLen_ = 0; + + for (PDRfitMeshPlane& pln : *this) + { + pln.reset(); + } +} + + +void Foam::PDRfitMeshPlanes::resize +( + const scalar cellWidth, + const PDRfitMeshParams& pars +) +{ + for (PDRfitMeshPlane& pln : *this) + { + pln.resize(cellWidth, pars); + } +} + + + + +void Foam::PDRfitMeshPlanes::print(Ostream& os) const +{ + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + os << vector::componentNames[cmpt] << ' '; + (*this)[cmpt].print(os); + } +} + + +void Foam::PDRfitMeshPlanes::scanLimits +( + const UList<PDRobstacle>& obstacles +) +{ + for (const PDRobstacle& obs : obstacles) + { + switch (obs.typeId) + { + case PDRobstacle::CYLINDER: + case PDRobstacle::DIAG_BEAM: + { + (*this)[obs.orient].adjustLimits + ( + obs.pt[obs.orient], + obs.pt[obs.orient] + obs.len() + ); + break; + } + + case PDRobstacle::CUBOID_1: + case PDRobstacle::LOUVRE_BLOWOFF: + case PDRobstacle::CUBOID: + case PDRobstacle::WALL_BEAM: + case PDRobstacle::GRATING: + case PDRobstacle::RECT_PATCH: + { + (*this).x().adjustLimits(obs.x(), obs.x() + obs.span.x()); + (*this).y().adjustLimits(obs.y(), obs.y() + obs.span.y()); + (*this).z().adjustLimits(obs.z(), obs.z() + obs.span.z()); + break; + } + } + } +} + + +Foam::scalar +Foam::PDRfitMeshPlanes::scanAreas +( + const UList<PDRobstacle>& obstacles, + const scalar minSubgridLen +) +{ + scalar subgridLen = 0; + + const bool checkSubgrid = (minSubgridLen > 0); + + if (verbose()) + { + Info<< "Scan " << obstacles.size() + << " obstacles. Check for subgrid < " << minSubgridLen + << nl; + } + + // When sorting flat elements, convert vector -> List + List<scalar> gridSpan(3); + + for (const PDRobstacle& obs : obstacles) + { + switch (obs.typeId) + { + case PDRobstacle::CYLINDER: + { + // #ifdef FULLDEBUG + // Info<< "Add " << obs.info() << nl; + // #endif + + (*this)[obs.orient].addArea + ( + sqr(0.5*obs.dia()), + obs.pt[obs.orient], + obs.pt[obs.orient] + obs.len() + ); + + // Store total length of subgrid obstcales + if (checkSubgrid && obs.dia() < minSubgridLen) + { + subgridLen += obs.len(); + } + break; + } + + case PDRobstacle::DIAG_BEAM: + { + // #ifdef FULLDEBUG + // Info<< "Add " << obs.info() << nl; + // #endif + + (*this)[obs.orient].addArea + ( + (obs.wa * obs.wb), + obs.pt[obs.orient], + obs.pt[obs.orient] + obs.len() + ); + + // Store total length of subgrid obstcales + if (checkSubgrid && Foam::max(obs.wa, obs.wb) < minSubgridLen) + { + subgridLen += obs.len(); + } + break; + } + + case PDRobstacle::CUBOID_1: + case PDRobstacle::LOUVRE_BLOWOFF: + case PDRobstacle::CUBOID: + case PDRobstacle::WALL_BEAM: + case PDRobstacle::GRATING: + case PDRobstacle::RECT_PATCH: + { + // #ifdef FULLDEBUG + // Info<< "Add " << obs.info() << nl; + // #endif + + // Can be lazy here, addArea() filters out zero areas + + (*this)[vector::X].addArea + ( + (obs.span.y() * obs.span.z()), + obs.x(), + obs.x() + obs.span.x() + ); + + (*this)[vector::Y].addArea + ( + (obs.span.z() * obs.span.x()), + obs.y(), + obs.y() + obs.span.y() + ); + + (*this)[vector::Z].addArea + ( + (obs.span.x() * obs.span.y()), + obs.z(), + obs.z() + obs.span.z() + ); + + if (checkSubgrid) + { + gridSpan[0] = obs.span.x(); + gridSpan[1] = obs.span.y(); + gridSpan[2] = obs.span.z(); + Foam::sort(gridSpan); + + // Ignore zero dimension when considering subgrid + + // Store total length of subgrid obstcales + if (gridSpan[1] < minSubgridLen) + { + subgridLen += gridSpan.last(); + } + } + break; + } + } + } + + return subgridLen; +} + + +void Foam::PDRfitMeshPlanes::scanAreas +( + const UList<PDRobstacle>& obstacles +) +{ + (void)scanAreas(obstacles, -GREAT); +} + + +void Foam::PDRfitMeshPlanes::scanAreas +( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth +) +{ + resize(mag(cellWidth), fitParams); + + const scalar minSubgridLen = + (cellWidth < 0 ? (mag(cellWidth) * fitParams.widthFactor) : 0); + + subgridLen_ = 0; + + for (const UList<PDRobstacle>& list : obstacles) + { + subgridLen_ += scanAreas(list, minSubgridLen); + } +} + + +Foam::Vector<Foam::PDRblock::gridControl> +Foam::PDRfitMeshPlanes::calcGriding +( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth +) +{ + prepare(obstacles, fitParams, cellWidth); + + scalar innerVol = this->volume(); + + // Hard limit at the ground + z().hard_min = fitParams.ground; + + if (z().limits.min() < fitParams.ground) + { + z().limits.min() = fitParams.ground; + } + + if (verbose()) + { + this->print(Info); + Info<< "cellWidth = " << cellWidth << nl; + Info<< "inner volume: " << innerVol << nl; + Info<< "subgrid length: " << subgridLen_ << nl; + } + + + scalar cw = 0; + scalar prev_cw = 0; + + for (label nIter = 0; nIter < fitParams.maxIterations; ++nIter) + { + scanAreas(obstacles, fitParams, cellWidth); + + if (cellWidth < 0) + { + constexpr scalar relax = 0.7; + + prev_cw = cw; + + if (subgridLen_ < cellWidth) + { + FatalErrorInFunction + << "No sub-grid obstacles found" << endl + << exit(FatalError); + } + + // Optimise to average OBS_PER_CELL subgrid obstacles per cell + cw = sqrt(fitParams.obsPerCell * innerVol / subgridLen_); + cw = Foam::min(cw, fitParams.maxCellWidth); + + if (cw > cellWidth * fitParams.maxWidthEstimate) + { + Warning + << "Calculated cell width more than" + " maxWidthEstimate x estimate." << nl + << "Too few sub-grid obstacles?" + << nl; + } + + Info<< "Current cellwidth: " << cw << nl; + + scalar ratio = cw / cellWidth; + + if (ratio < 1) + { + ratio = 1/ratio; + } + + if (ratio < fitParams.maxWidthRatio) + { + cellWidth = -cellWidth; + } + else + { + cellWidth = relax * (-cw) + (1.0 - relax) * cellWidth; + } + Info<< "Cell width: " << cellWidth << nl; + } + + if (cellWidth > 0) + { + break; + } + } + + + // Read in obstacles and record face planes Also record length of + // subgrid obstacles so that we can optimise cell size to get + // desired average no, of obstacles oper cell. Determinuing which + // obstacles are subgrid is iself dependent on the cell size. + // Therefore we iterate (if cell_width is -ve) If cell_width is + // +ve just use the user-supplied value. + + if (cellWidth < 0) + { + cellWidth = 0.5 * (cw + prev_cw); + } + Info<< nl << "Final cell width: " << cellWidth << nl << nl; + + + // Now we fit the mesh to the planes and write out the result + + this->resize(cellWidth, fitParams); + cellWidth = cbrt(this->cellVolume()) / fitParams.areaWidthFactor; + + scanAreas(obstacles, fitParams, cellWidth); + + const scalar max_zone = + ( + (this->z().limits.mag() + fitParams.edgeLayers * cellWidth) + * fitParams.maxZoneToHeight + ); + + + Vector<PDRblock::gridControl> griding; + + for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt) + { + griding[cmpt] = + (*this)[cmpt].calcGridControl(cellWidth, max_zone, fitParams); + } + + return griding; +} + + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.H b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.H new file mode 100644 index 0000000000000000000000000000000000000000..26b7e8ff8e6a8906aacf5b2a76187ff882df3c59 --- /dev/null +++ b/applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMeshPlanes.H @@ -0,0 +1,151 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / 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::PDRfitMeshPlanes + +Description + Handling for PDRfitMesh + +SourceFiles + PDRfitMeshPlanes.C + +\*---------------------------------------------------------------------------*/ + +#ifndef PDRfitMeshPlanes_H +#define PDRfitMeshPlanes_H + +#include "PDRfitMeshPlane.H" +#include "vector.H" + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +namespace Foam +{ + +/*---------------------------------------------------------------------------*\ + Class PDRfitMeshPlanes Declaration +\*---------------------------------------------------------------------------*/ + +class PDRfitMeshPlanes +: + public Vector<PDRfitMeshPlane> +{ + // Private Data + + //- The evaluated sub-grid lengths + scalar subgridLen_ = 0; + + + // Private Member Functions + + //- Use verbosity + static inline bool verbose() + { + return PDRfitMeshPlane::verbose_; + } + + //- Prepare limits, scan areas + void prepare + ( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth + ); + +public: + + // Default Constructors + + + // Member Functions + + //- Calculate the grid controls for the given obstacles + //- and parameters + Vector<PDRblock::gridControl> + calcGriding + ( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth + ); + + + // Low-level functions + + //- The volume of the limits + scalar volume() const; + + //- The volume of single cell of the respective step-width + scalar cellVolume() const; + + //- Reset to initial state + void reset(); + + //- Define number of steps (and step-size) according to the + //- current limits and the specified parameters + void resize(const scalar cellWidth, const PDRfitMeshParams& pars); + + //- Adjust directional limits to accommodate obstacles + void scanLimits(const UList<PDRobstacle>& obstacles); + + //- Populate with areas/positions of the obstacles, + //- and check for sub-grid obstacles + // Sets the internal + void scanAreas + ( + std::initializer_list<UList<PDRobstacle>> obstacles, + const PDRfitMeshParams& fitParams, + scalar cellWidth + ); + + //- Populate with areas/positions of the obstacles, + //- and check for sub-grid obstacles + // + // \return total subgrid lengths + scalar scanAreas + ( + const UList<PDRobstacle>& obstacles, + const scalar minSubgridLen + ); + + //- Populate with areas/positions of the obstacles, + //- without checks for sub-grid obstacles + void scanAreas(const UList<PDRobstacle>& obstacles); + + //- Print information + void print(Ostream& os) const; +}; + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +} // End namespace Foam + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +#endif + +// ************************************************************************* // diff --git a/applications/utilities/preProcessing/PDR/PDRsetFields/PDRsetFields.C b/applications/utilities/preProcessing/PDR/PDRsetFields/PDRsetFields.C index 4ba931c358b451fb1cfd1f65aca348f73eed2c90..cd1922f2b35736211f4380615bd2b9c1f7393483 100644 --- a/applications/utilities/preProcessing/PDR/PDRsetFields/PDRsetFields.C +++ b/applications/utilities/preProcessing/PDR/PDRsetFields/PDRsetFields.C @@ -83,6 +83,8 @@ int main(int argc, char* argv[]) #include "setRootCase.H" #include "createTime.H" + const bool dryrun = args.found("dry-run"); + const word dictName("PDRsetFieldsDict"); #include "setSystemRunTimeDictionaryIO.H" @@ -90,8 +92,6 @@ int main(int argc, char* argv[]) IOdictionary setFieldsDict(dictIO); - const bool dryrun = args.found("dry-run"); - const fileName& casepath = runTime.globalPath(); pars.timeName = "0"; diff --git a/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRfitMeshDict b/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRfitMeshDict new file mode 100644 index 0000000000000000000000000000000000000000..fb634d2b92c030b9b683a0513571444a9de8a182 --- /dev/null +++ b/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRfitMeshDict @@ -0,0 +1,105 @@ +/*--------------------------------*- C++ -*----------------------------------*\ +| ========= | | +| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | +| \\ / O peration | Version: v2012 | +| \\ / A nd | Website: www.openfoam.com | +| \\/ M anipulation | | +\*---------------------------------------------------------------------------*/ +FoamFile +{ + version 2.0; + format ascii; + class dictionary; + object PDRfitMeshDict; +} +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // +// Settings for PDRfitMesh + +cellWidth 0.22; + +// Tuning parameters (defaults) for PDRfitMesh + +bounds +{ +// xmin -1e16; +// xmax 1e16; +// +// ymin -1e16; +// ymax 1e16; +// +// zmin -1e16; +// zmax 1e16; +} + + +findPlanes +{ + // A face smaller than this multiple of cell face area will not be + // recorded + minSigFaceArea 5; + + // Only fit a plane if the face area at the coordinate is at least + // this times the cell face area + minAreaRatio 20.0; + + // Size of search zones for face areas will be this * the cell width. + // So faces closer than this zone size might be grouped together + areasetWidthFactor 0.7; + + // Very long zones produce bad outer boundary shape from + // makePDRmeshBlocks, so we subdivide a zone if its length is greater + // than this * the height of the area of cuboid vells + // + maxRatioZoneToH 2.0 ; +} + + +/****************** Choosing cell width ***************/ + +// Note "cellWidth" and "cellWidthFactor" are read from PDRsetFieldsDict +// cellWidth +// { +// } + +// Note "cellWidth" and "cellWidthFactor" are read from PDRsetFieldsDict +grid +{ + auto true; + width 0; + max great; +} + + +// Width of obstacles must be less than this * cell width to added +// into subgrid length +widthFactor 1.0; + +// Optimal average number of obstacles per cell +obsPerCell 2.0; + +// Minimum number of cells in any direction +minStepsPerDim 10; + +// Do not try a cell width more than this times the initial estimate +maxCwToEst 5.0; + +// Assume converged if optimised width changes by less than this +maxWidthRatio 1.2; + +// Maximum iterations in optimizing cell width +maxPasses 5; + + +/****************** Defining outer expanding region ***************/ + +// Number of rectangular cell layers each side of the congested region. (double, not int) +nEdgeLayers 5.0; + +// Ratio of the outer extension distance to the average radius of the congested region +ratioOuterToCongRad 20.0; + +// Cell size ratio - should be same as that hard-wired in makePDRmeshBlocks +outerRatio 1.2; + + +// ************************************************************************* // diff --git a/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRsetFieldsDict b/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRsetFieldsDict index 3609db612c76d61df431682f2b360ec8079ef3d6..239dd273c4384edbac0a716a85162655a80a420c 100644 --- a/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRsetFieldsDict +++ b/tutorials/preProcessing/PDRsetFields/simplePipeCage/system/PDRsetFieldsDict @@ -35,6 +35,24 @@ cellWidth 0.22; // Optional cellWidthFactor 1.0; +// Here or in PDRfitMeshDict. Largely pass-through to PDRblockMesh + +// Copy for PDRblockMeshDict +outer +{ + type sphere; + onGround true; + expansion relative; + + ratios 1.1; + + size 3; + nCells 10; + + // For PDRfitMesh only: + zmin 0; +} + // ------------------ // Advanced