mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
Merge branch 'feature-PDRfitMesh' into 'develop'
Draft: Integration of PDRfitMesh See merge request Development/openfoam!415
This commit is contained in:
@ -1,7 +1,11 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
cd "${0%/*}" || exit # Run from this directory
|
cd "${0%/*}" || exit # Run from this directory
|
||||||
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
wclean libso pdrFields
|
wclean libso pdrFields
|
||||||
|
|
||||||
|
wclean PDRfitMesh
|
||||||
|
|
||||||
wclean PDRsetFields
|
wclean PDRsetFields
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|||||||
@ -5,6 +5,9 @@ cd "${0%/*}" || exit # Run from this directory
|
|||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|
||||||
wmake $targetType pdrFields
|
wmake $targetType pdrFields
|
||||||
|
|
||||||
|
wmake $targetType PDRfitMesh
|
||||||
|
|
||||||
wmake $targetType PDRsetFields
|
wmake $targetType PDRsetFields
|
||||||
|
|
||||||
#------------------------------------------------------------------------------
|
#------------------------------------------------------------------------------
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
PDRfitMeshParams.C
|
||||||
|
PDRfitMeshScan.C
|
||||||
|
PDRfitMeshScans.C
|
||||||
|
|
||||||
|
PDRfitMesh.C
|
||||||
|
|
||||||
|
EXE = $(FOAM_APPBIN)/PDRfitMesh
|
||||||
@ -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
|
||||||
339
applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMesh.C
Normal file
339
applications/utilities/preProcessing/PDR/PDRfitMesh/PDRfitMesh.C
Normal file
@ -0,0 +1,339 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / 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/>.
|
||||||
|
|
||||||
|
Applications
|
||||||
|
PDRfitMesh
|
||||||
|
|
||||||
|
Description
|
||||||
|
Scans extents of obstacles to establish reasonable estimates
|
||||||
|
for generating a PDRblockMeshDict.
|
||||||
|
|
||||||
|
SourceFiles
|
||||||
|
PDRfitMesh.C
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#include "argList.H"
|
||||||
|
#include "Time.H"
|
||||||
|
#include "Fstream.H"
|
||||||
|
#include "IOdictionary.H"
|
||||||
|
|
||||||
|
#include "PDRfitMeshScans.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
|
||||||
|
(
|
||||||
|
"dry-run",
|
||||||
|
"Equivalent to -print-dict"
|
||||||
|
);
|
||||||
|
|
||||||
|
argList::addBoolOption
|
||||||
|
(
|
||||||
|
"print-dict",
|
||||||
|
"Print PDRblockMeshDict equivalent and exit"
|
||||||
|
);
|
||||||
|
|
||||||
|
argList::addBoolOption
|
||||||
|
(
|
||||||
|
"write-vtk",
|
||||||
|
"Write obstacles as VTK files"
|
||||||
|
);
|
||||||
|
|
||||||
|
#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);
|
||||||
|
|
||||||
|
// 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, // ie, no bounds
|
||||||
|
obstacles,
|
||||||
|
cylinders
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PDRobstacle::readFiles
|
||||||
|
(
|
||||||
|
pars.obsfile_dir, pars.obsfile_names,
|
||||||
|
boundBox::invertedBox, // ie, no bounds
|
||||||
|
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 (args.found("write-vtk"))
|
||||||
|
{
|
||||||
|
PDRobstacle::generateVtk(casepath/"VTK", obstacles, cylinders);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// The fitting routines
|
||||||
|
//
|
||||||
|
|
||||||
|
// Collapse into a single list of obstacles
|
||||||
|
obstacles.append(std::move(cylinders));
|
||||||
|
|
||||||
|
PDRfitMeshScan::verbose(args.found("verbose"));
|
||||||
|
|
||||||
|
Vector<PDRblock::gridControl> griding =
|
||||||
|
PDRfitMeshScans().calcGriding(obstacles, fitParams, cellWidth);
|
||||||
|
|
||||||
|
|
||||||
|
//
|
||||||
|
// Output
|
||||||
|
//
|
||||||
|
|
||||||
|
if (writable == writeType::DRY_RUN)
|
||||||
|
{
|
||||||
|
InfoErr << nl;
|
||||||
|
if (!printDict)
|
||||||
|
{
|
||||||
|
InfoErr
|
||||||
|
<< "dry-run: ";
|
||||||
|
}
|
||||||
|
InfoErr
|
||||||
|
<< "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",
|
||||||
|
PDRfitMeshScan::expansionName()
|
||||||
|
);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -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.
|
||||||
|
nEdgeLayers 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;
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -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("nEdgeLayers", nEdgeLayers);
|
||||||
|
|
||||||
|
dict.readIfPresent("outerRatio", outerRatio);
|
||||||
|
|
||||||
|
// dict.readIfPresent("outerRadius", outerRadius);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -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 nEdgeLayers = 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
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,859 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / 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 "PDRfitMeshScan.H"
|
||||||
|
#include "PDRfitMeshParams.H"
|
||||||
|
#include "PDRblock.H"
|
||||||
|
#include "PDRobstacle.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
bool Foam::PDRfitMeshScan::verbose_ = false;
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
Foam::word Foam::PDRfitMeshScan::expansionName()
|
||||||
|
{
|
||||||
|
return PDRblock::expansionNames_[PDRblock::EXPAND_RELATIVE];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScan::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 half-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::PDRfitMeshScan::clear()
|
||||||
|
// {
|
||||||
|
// weightedPos_.clear(); totalArea_.clear();
|
||||||
|
// weightedPos2_.clear(); totalArea2_.clear();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScan::reset()
|
||||||
|
{
|
||||||
|
limits_.min() = GREAT;
|
||||||
|
limits_.max() = -GREAT;
|
||||||
|
|
||||||
|
hard_min_ = -GREAT;
|
||||||
|
|
||||||
|
nsteps_ = 0;
|
||||||
|
stepWidth_ = 0;
|
||||||
|
minFaceArea_ = 0;
|
||||||
|
|
||||||
|
weightedPos_.clear();
|
||||||
|
totalArea_.clear();
|
||||||
|
|
||||||
|
weightedPos2_.clear();
|
||||||
|
totalArea2_.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScan::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;
|
||||||
|
}
|
||||||
|
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::PDRfitMeshScan::adjustLimits
|
||||||
|
(
|
||||||
|
const scalar point0,
|
||||||
|
const scalar point1
|
||||||
|
)
|
||||||
|
{
|
||||||
|
limits_.add(point0);
|
||||||
|
limits_.add(point1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScan::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::PDRfitMeshScan::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(const scalar r, const label n)
|
||||||
|
{
|
||||||
|
if (mag(r - 1.0) < SMALL)
|
||||||
|
{
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (pow(r, n) - 1.0)/(r - 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
inline static scalar end_val
|
||||||
|
(
|
||||||
|
const scalar width,
|
||||||
|
const scalar ratio,
|
||||||
|
const label n
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return width / rn1r1(ratio, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static scalar fit_slope
|
||||||
|
(
|
||||||
|
const scalar left,
|
||||||
|
const scalar width,
|
||||||
|
const label n,
|
||||||
|
scalar& r
|
||||||
|
)
|
||||||
|
{
|
||||||
|
const scalar wol = width / left;
|
||||||
|
|
||||||
|
// Initial value
|
||||||
|
r = (left < width / n) ? 1.01 : 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) / sqr(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::PDRfitMeshScan::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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initially only the two outer boundaries and absolute limits
|
||||||
|
|
||||||
|
DynamicList<scalar> initialCoord(nsteps_+1);
|
||||||
|
|
||||||
|
initialCoord.resize(4);
|
||||||
|
initialCoord[0] = -GREAT;
|
||||||
|
initialCoord[1] = Foam::max(limits_.min(), hard_min_);
|
||||||
|
initialCoord[2] = limits_.max();
|
||||||
|
initialCoord[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.
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
while (initialCoord.size()-4 < totalArea_.size())
|
||||||
|
{
|
||||||
|
label n_big = 0;
|
||||||
|
scalar largest = 0;
|
||||||
|
|
||||||
|
// Find largest area, using "real" scanned areas
|
||||||
|
// thus checking the stored even locations.
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
initialCoord.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
|
||||||
|
|
||||||
|
initialCoord.append(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now sort the positions
|
||||||
|
Foam::sort(initialCoord);
|
||||||
|
|
||||||
|
// Avoid small zones near the begin/end positions
|
||||||
|
|
||||||
|
if
|
||||||
|
(
|
||||||
|
(initialCoord[2] - initialCoord[1])
|
||||||
|
< (0.4 * cellWidth)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Remove [1] if too close to [2]
|
||||||
|
initialCoord.remove(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if
|
||||||
|
(
|
||||||
|
(
|
||||||
|
initialCoord[initialCoord.size()-2]
|
||||||
|
- initialCoord[initialCoord.size()-3]
|
||||||
|
)
|
||||||
|
< (0.4 * cellWidth)
|
||||||
|
)
|
||||||
|
{
|
||||||
|
// Remove [N-3] if too close to [N-2]
|
||||||
|
initialCoord.remove(initialCoord.size()-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 than originally
|
||||||
|
// anticipated.
|
||||||
|
|
||||||
|
// So over-dimension arrays by a larger factor (eg, 20-100)
|
||||||
|
// which is presumably good enough.
|
||||||
|
|
||||||
|
// FIXME: Needs revisiting (2020-12-16)
|
||||||
|
|
||||||
|
label nCoords = initialCoord.size();
|
||||||
|
|
||||||
|
scalarList coord(50*initialCoord.size(), Zero);
|
||||||
|
SubList<scalar>(coord, initialCoord.size()) = initialCoord;
|
||||||
|
|
||||||
|
scalarList width(coord.size(), Zero);
|
||||||
|
labelList nDivs(width.size(), Zero);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Set up initially uniform zones
|
||||||
|
//
|
||||||
|
for (label ii=1; ii < (nCoords-2); ++ii)
|
||||||
|
{
|
||||||
|
width[ii] = coord[ii+1] - coord[ii];
|
||||||
|
nDivs[ii] = width[ii] / stepWidth_ * pars.areaWidthFactor + 0.5;
|
||||||
|
|
||||||
|
while (nDivs[ii] < 1)
|
||||||
|
{
|
||||||
|
// Positions too close - merge them
|
||||||
|
--nCoords;
|
||||||
|
|
||||||
|
coord[ii] = 0.5 * (coord[ii+1] + coord[ii]);
|
||||||
|
width[ii-1] = coord[ii] - coord[ii-1];
|
||||||
|
nDivs[ii-1] =
|
||||||
|
width[ii-1] / stepWidth_ * pars.areaWidthFactor + 0.5;
|
||||||
|
|
||||||
|
if (ii < (nCoords - 2))
|
||||||
|
{
|
||||||
|
for (label iii=ii+1; iii < nCoords-1; ++iii)
|
||||||
|
{
|
||||||
|
coord[iii] = coord[iii+1];
|
||||||
|
}
|
||||||
|
width[ii] = coord[ii+1] - coord[ii];
|
||||||
|
|
||||||
|
nDivs[ii] =
|
||||||
|
width[ii] / stepWidth_ * pars.areaWidthFactor + 0.5;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nDivs[ii] = 1; // Dummy value to terminate while
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Initially all steps have equal width (ratio == 1)
|
||||||
|
|
||||||
|
scalarList first(width.size(), Zero);
|
||||||
|
|
||||||
|
for (label ii=1; ii < (nCoords-2); ++ii)
|
||||||
|
{
|
||||||
|
first[ii] = width[ii] / nDivs[ii];
|
||||||
|
}
|
||||||
|
|
||||||
|
scalarList last(first);
|
||||||
|
scalarList ratio(width.size(), scalar(1));
|
||||||
|
|
||||||
|
// For diagnostics
|
||||||
|
charList marker(width.size(), char('x'));
|
||||||
|
marker[0] = '0';
|
||||||
|
|
||||||
|
|
||||||
|
// Now adjust the inter-cell ratios to reduce cell-size changes
|
||||||
|
// between zones
|
||||||
|
|
||||||
|
if (nCoords > 5)
|
||||||
|
{
|
||||||
|
// The outer zones can be adjusted to fit the adjacent steps
|
||||||
|
|
||||||
|
last[1] = first[2];
|
||||||
|
first[nCoords-3] = last[nCoords-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 < (nCoords-2); ++ii)
|
||||||
|
{
|
||||||
|
if (nDivs[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],
|
||||||
|
nDivs[ii],
|
||||||
|
r_fit
|
||||||
|
)
|
||||||
|
/ first[ii+1]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
< mag(log(r_fit))
|
||||||
|
)
|
||||||
|
|| (ii == nCoords-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],
|
||||||
|
nDivs[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/(nDivs[ii]-1));
|
||||||
|
|
||||||
|
marker[ii] = 'M';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
first[ii] = end_val(width[ii], ratio[ii], nDivs[ii]);
|
||||||
|
last[ii] = end_val(width[ii], 1.0/ratio[ii], nDivs[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 (nEdgeLayers) cell widths from min.
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (coord[1] > hard_min_ + first[1] / pars.outerRatio)
|
||||||
|
{
|
||||||
|
nDivs[0] = pars.nEdgeLayers;
|
||||||
|
ratio[0] = 1.0 / pars.outerRatio;
|
||||||
|
coord[0] = coord[1] - first[1] * rn1r1(pars.outerRatio, nDivs[0]);
|
||||||
|
|
||||||
|
// Reduce the number of steps if we have extended below hard_min_
|
||||||
|
while (coord[0] < hard_min_ && nDivs[0] > 1)
|
||||||
|
{
|
||||||
|
--nDivs[0];
|
||||||
|
coord[0] = coord[1] - first[1] * rn1r1(pars.outerRatio, nDivs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nDivs[0] < label(pars.nEdgeLayers))
|
||||||
|
{
|
||||||
|
// We had to adjust for hard_min_, so now fit exaxtly
|
||||||
|
coord[0] = hard_min_;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No room for outer zone. Remove it.
|
||||||
|
--nCoords;
|
||||||
|
|
||||||
|
for (label ii = 0; ii < nCoords; ++ii)
|
||||||
|
{
|
||||||
|
coord[ii] = coord[ii+1];
|
||||||
|
nDivs[ii] = nDivs[ii+1];
|
||||||
|
ratio[ii] = ratio[ii+1];
|
||||||
|
first[ii] = first[ii+1];
|
||||||
|
last[ii] = last[ii+1];
|
||||||
|
}
|
||||||
|
coord[0] = hard_min_;
|
||||||
|
}
|
||||||
|
|
||||||
|
width[0] = coord[1] - coord[0];
|
||||||
|
first[0] = end_val(width[0],ratio[0],nDivs[0]);
|
||||||
|
last[0] = end_val(width[0],1.0/ratio[0],nDivs[0]);
|
||||||
|
|
||||||
|
|
||||||
|
// Now do the upper edge zone
|
||||||
|
{
|
||||||
|
const label np2 = nCoords - 2;
|
||||||
|
|
||||||
|
ratio[np2] = pars.outerRatio;
|
||||||
|
nDivs[np2] = pars.nEdgeLayers;
|
||||||
|
|
||||||
|
coord[np2+1] =
|
||||||
|
coord[np2] + last[np2-1] * rn1r1(pars.outerRatio, nDivs[np2]);
|
||||||
|
|
||||||
|
width[np2] = coord[np2+1] - coord[np2];
|
||||||
|
|
||||||
|
first[np2] = last[np2-1];
|
||||||
|
last[np2] = first[np2] * pow(pars.outerRatio, nDivs[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 < (nCoords-1); ++ii)
|
||||||
|
{
|
||||||
|
if (width[ii] > max_zone && nDivs[ii] > 1)
|
||||||
|
{
|
||||||
|
// No. of extra zones
|
||||||
|
const label nExtra = width[ii] / max_zone - 1;
|
||||||
|
|
||||||
|
// Make space for extra zones
|
||||||
|
for (label ij = nCoords-1; ij > ii; --ij)
|
||||||
|
{
|
||||||
|
width[ij+nExtra] = width[ij];
|
||||||
|
ratio[ij+nExtra] = ratio[ij];
|
||||||
|
first[ij+nExtra] = first[ij];
|
||||||
|
last[ij+nExtra] = last[ij];
|
||||||
|
nDivs[ij+nExtra] = nDivs[ij];
|
||||||
|
coord[ij+nExtra] = coord[ij];
|
||||||
|
}
|
||||||
|
nCoords += nExtra;
|
||||||
|
|
||||||
|
const scalar subWidth = width[ii] / (nExtra + 1);
|
||||||
|
scalar bdy = coord[ii];
|
||||||
|
|
||||||
|
last[ii+nExtra] = last[ii];
|
||||||
|
nDivs[ii+nExtra] = nDivs[ii]; // will be decremented
|
||||||
|
width[ii+nExtra] = width[ii]; // will be decremented
|
||||||
|
|
||||||
|
// Current position
|
||||||
|
scalar here = coord[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+nExtra; ++ij)
|
||||||
|
{
|
||||||
|
label ist = 0;
|
||||||
|
bdy += subWidth;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
here += step;
|
||||||
|
step *= ratio[ii];
|
||||||
|
++ist;
|
||||||
|
} while ((here + 0.5 * step) < bdy);
|
||||||
|
|
||||||
|
// Found a cell boundary at 'here' that is close to bdy
|
||||||
|
|
||||||
|
// Create this sub-zone
|
||||||
|
last[ij] = step / ratio[ii];
|
||||||
|
first[ij+1] = step;
|
||||||
|
coord[ij+1] = here;
|
||||||
|
width[ij] = coord[ij+1] - coord[ij];
|
||||||
|
nDivs[ij] = ist;
|
||||||
|
ratio[ij+1] = ratio[ij];
|
||||||
|
|
||||||
|
// Decrement what remains for the last sub-zone
|
||||||
|
nDivs[ii+nExtra] -= ist;
|
||||||
|
width[ii+nExtra] -= width[ij];
|
||||||
|
}
|
||||||
|
|
||||||
|
ii += nExtra;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (verbose())
|
||||||
|
{
|
||||||
|
printf("Zone Cell widths Ratios\n");
|
||||||
|
printf("start first last left mid right\n");
|
||||||
|
for (label ii = 0; ii < (nCoords-1); ++ii)
|
||||||
|
{
|
||||||
|
printf
|
||||||
|
(
|
||||||
|
"%6.2f %6.2f %6.2f %c %6.2f %6.2f %6.2f\n",
|
||||||
|
coord[ii], first[ii], last[ii],
|
||||||
|
marker[ii],
|
||||||
|
first[ii]/last[ii-1], ratio[ii], first[ii+1]/last[ii]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
printf("%6.2f\n", coord[nCoords-1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Copy back into a gridControl form
|
||||||
|
|
||||||
|
PDRblock::gridControl grid;
|
||||||
|
|
||||||
|
grid.resize(nCoords);
|
||||||
|
for (label i = 0; i < nCoords; ++i)
|
||||||
|
{
|
||||||
|
grid[i] = coord[i];
|
||||||
|
}
|
||||||
|
for (label i = 0; i < nCoords-1; ++i)
|
||||||
|
{
|
||||||
|
grid.divisions_[i] = nDivs[i];
|
||||||
|
}
|
||||||
|
for (label i = 0; i < nCoords-1; ++i)
|
||||||
|
{
|
||||||
|
grid.expansion_[i] = ratio[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return grid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,224 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / 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::PDRfitMeshScan
|
||||||
|
|
||||||
|
Description
|
||||||
|
Scanning of obstacles in a single direction for PDRfitMesh
|
||||||
|
|
||||||
|
SourceFiles
|
||||||
|
PDRfitMeshScan.C
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef PDRfitMeshScan_H
|
||||||
|
#define PDRfitMeshScan_H
|
||||||
|
|
||||||
|
#include "scalarList.H"
|
||||||
|
#include "DynamicList.H"
|
||||||
|
#include "MinMax.H"
|
||||||
|
#include "PDRblock.H"
|
||||||
|
#include "PDRfitMeshParams.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
Class PDRfitMeshScan Declaration
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class PDRfitMeshScan
|
||||||
|
{
|
||||||
|
// Private Data
|
||||||
|
|
||||||
|
//- Lower and upper limits
|
||||||
|
scalarMinMax limits_;
|
||||||
|
|
||||||
|
//- Absolute lower limit (eg, for the ground plane)
|
||||||
|
scalar hard_min_ = -GREAT;
|
||||||
|
|
||||||
|
//- Number of steps
|
||||||
|
label nsteps_ = 0;
|
||||||
|
|
||||||
|
//- Step width = limits / nsteps
|
||||||
|
scalar stepWidth_ = 0;
|
||||||
|
|
||||||
|
//- Minimum per cell face area. Defined via PDRfitMeshParams
|
||||||
|
scalar minFaceArea_ = 0;
|
||||||
|
|
||||||
|
//- Area-weighted position
|
||||||
|
DynamicList<scalar> weightedPos_;
|
||||||
|
|
||||||
|
//- Area-weights for position
|
||||||
|
DynamicList<scalar> totalArea_;
|
||||||
|
|
||||||
|
//- Area-weighted half-cell position
|
||||||
|
DynamicList<scalar> weightedPos2_;
|
||||||
|
|
||||||
|
//- Area-weights for half-cell position
|
||||||
|
DynamicList<scalar> totalArea2_;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Functions
|
||||||
|
|
||||||
|
//- Area-averaged position at index
|
||||||
|
inline scalar averagePosition(const label i) const
|
||||||
|
{
|
||||||
|
return
|
||||||
|
(
|
||||||
|
totalArea_[i] < VSMALL
|
||||||
|
? 0
|
||||||
|
: (weightedPos_[i] / totalArea_[i])
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Area-averaged half-position at index
|
||||||
|
inline 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_;
|
||||||
|
|
||||||
|
//- Use gridControl as per PDRblock, with EXPAND_RELATIVE
|
||||||
|
typedef PDRblock::gridControl gridControl;
|
||||||
|
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
|
||||||
|
//- Default construct
|
||||||
|
PDRfitMeshScan()
|
||||||
|
:
|
||||||
|
limits_(GREAT, -GREAT),
|
||||||
|
hard_min_(-GREAT),
|
||||||
|
nsteps_(0),
|
||||||
|
stepWidth_(0)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
|
// Static Member Functions
|
||||||
|
|
||||||
|
//- Get verbosity
|
||||||
|
static inline bool verbose() noexcept
|
||||||
|
{
|
||||||
|
return verbose_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Set verbosity
|
||||||
|
static inline bool verbose(const bool val) noexcept
|
||||||
|
{
|
||||||
|
bool old(verbose_);
|
||||||
|
verbose_ = val;
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- The expansion name
|
||||||
|
static word expansionName();
|
||||||
|
|
||||||
|
|
||||||
|
// Member Functions
|
||||||
|
|
||||||
|
//- The min/max limits
|
||||||
|
const scalarMinMax& limits() const noexcept
|
||||||
|
{
|
||||||
|
return limits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- The min/max limits
|
||||||
|
scalarMinMax& limits() noexcept
|
||||||
|
{
|
||||||
|
return limits_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- The step-width
|
||||||
|
scalar stepWidth() const noexcept
|
||||||
|
{
|
||||||
|
return stepWidth_;
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Set the hard-min
|
||||||
|
void hard_min(const scalar val) noexcept
|
||||||
|
{
|
||||||
|
hard_min_ = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//- 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
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,464 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / 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 "PDRfitMeshScans.H"
|
||||||
|
#include "PDRobstacle.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::prepare
|
||||||
|
(
|
||||||
|
const UList<PDRobstacle>& obstacles,
|
||||||
|
const PDRfitMeshParams& fitParams,
|
||||||
|
scalar cellWidth
|
||||||
|
)
|
||||||
|
{
|
||||||
|
reset();
|
||||||
|
|
||||||
|
// Scan for obstacle limits
|
||||||
|
scanLimits(obstacles);
|
||||||
|
|
||||||
|
// 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::PDRfitMeshScans::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::PDRfitMeshScans::cellVolume() const
|
||||||
|
{
|
||||||
|
scalar vol = 1;
|
||||||
|
|
||||||
|
for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt)
|
||||||
|
{
|
||||||
|
vol *= (*this)[cmpt].stepWidth();
|
||||||
|
}
|
||||||
|
|
||||||
|
return vol;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::reset()
|
||||||
|
{
|
||||||
|
subgridLen_ = 0;
|
||||||
|
|
||||||
|
for (PDRfitMeshScan& pln : *this)
|
||||||
|
{
|
||||||
|
pln.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::resize
|
||||||
|
(
|
||||||
|
const scalar cellWidth,
|
||||||
|
const PDRfitMeshParams& pars
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (PDRfitMeshScan& pln : *this)
|
||||||
|
{
|
||||||
|
pln.resize(cellWidth, pars);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::print(Ostream& os) const
|
||||||
|
{
|
||||||
|
for (direction cmpt = 0; cmpt < vector::nComponents; ++cmpt)
|
||||||
|
{
|
||||||
|
os << vector::componentNames[cmpt] << ' ';
|
||||||
|
(*this)[cmpt].print(os);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::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::PDRfitMeshScans::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::PDRfitMeshScans::scanAreas
|
||||||
|
(
|
||||||
|
const UList<PDRobstacle>& obstacles
|
||||||
|
)
|
||||||
|
{
|
||||||
|
(void)scanAreas(obstacles, -GREAT);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Foam::PDRfitMeshScans::scanAreas
|
||||||
|
(
|
||||||
|
const UList<PDRobstacle>& obstacles,
|
||||||
|
const PDRfitMeshParams& fitParams,
|
||||||
|
scalar cellWidth
|
||||||
|
)
|
||||||
|
{
|
||||||
|
resize(mag(cellWidth), fitParams);
|
||||||
|
|
||||||
|
const scalar minSubgridLen =
|
||||||
|
(cellWidth < 0 ? (mag(cellWidth) * fitParams.widthFactor) : 0);
|
||||||
|
|
||||||
|
subgridLen_ = scanAreas(obstacles, minSubgridLen);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Foam::Vector<Foam::PDRblock::gridControl>
|
||||||
|
Foam::PDRfitMeshScans::calcGriding
|
||||||
|
(
|
||||||
|
const UList<PDRobstacle>& obstacles,
|
||||||
|
const PDRfitMeshParams& fitParams,
|
||||||
|
scalar cellWidth
|
||||||
|
)
|
||||||
|
{
|
||||||
|
prepare(obstacles, fitParams, cellWidth);
|
||||||
|
|
||||||
|
const scalar innerVol = volume();
|
||||||
|
|
||||||
|
// Set hard lower limit at the ground
|
||||||
|
|
||||||
|
z().hard_min(fitParams.ground);
|
||||||
|
|
||||||
|
if (z().limits().min() < fitParams.ground)
|
||||||
|
{
|
||||||
|
z().limits().min() = fitParams.ground;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (verbose())
|
||||||
|
{
|
||||||
|
print(Info);
|
||||||
|
Info<< "cellWidth = " << cellWidth << nl;
|
||||||
|
Info<< "inner volume: " << innerVol << nl;
|
||||||
|
Info<< "subgrid length: " << subgridLen_ << nl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
{
|
||||||
|
// The cell-widths during optimisation
|
||||||
|
scalar prev_cw = 0, cw = 0;
|
||||||
|
|
||||||
|
const bool optimiseWidth = (cellWidth < 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 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cellWidth < 0)
|
||||||
|
{
|
||||||
|
cellWidth = 0.5 * (cw + prev_cw);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (optimiseWidth)
|
||||||
|
{
|
||||||
|
Info<< nl << "Final cell width: " << cellWidth << nl << nl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Now we fit the mesh to the planes and write out the result
|
||||||
|
|
||||||
|
resize(cellWidth, fitParams);
|
||||||
|
cellWidth = cbrt(cellVolume()) / fitParams.areaWidthFactor;
|
||||||
|
|
||||||
|
scanAreas(obstacles, fitParams, cellWidth);
|
||||||
|
|
||||||
|
const scalar max_zone =
|
||||||
|
(
|
||||||
|
(z().limits().mag() + fitParams.nEdgeLayers * 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
========= |
|
||||||
|
\\ / 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::PDRfitMeshScans
|
||||||
|
|
||||||
|
Description
|
||||||
|
Scanning of obstacles in a multiple directions for PDRfitMesh
|
||||||
|
|
||||||
|
SourceFiles
|
||||||
|
PDRfitMeshScans.C
|
||||||
|
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
#ifndef PDRfitMeshScans_H
|
||||||
|
#define PDRfitMeshScans_H
|
||||||
|
|
||||||
|
#include "PDRfitMeshScan.H"
|
||||||
|
#include "vector.H"
|
||||||
|
|
||||||
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
||||||
|
|
||||||
|
namespace Foam
|
||||||
|
{
|
||||||
|
|
||||||
|
// Forward Declarations
|
||||||
|
class PDRobstacle;
|
||||||
|
|
||||||
|
/*---------------------------------------------------------------------------*\
|
||||||
|
Class PDRfitMeshScans Declaration
|
||||||
|
\*---------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
class PDRfitMeshScans
|
||||||
|
:
|
||||||
|
public Vector<PDRfitMeshScan>
|
||||||
|
{
|
||||||
|
// Private Data
|
||||||
|
|
||||||
|
//- The evaluated sub-grid length
|
||||||
|
scalar subgridLen_ = 0;
|
||||||
|
|
||||||
|
|
||||||
|
// Private Member Functions
|
||||||
|
|
||||||
|
//- Prepare limits, scan areas
|
||||||
|
void prepare
|
||||||
|
(
|
||||||
|
const UList<PDRobstacle>& obstacles,
|
||||||
|
const PDRfitMeshParams& fitParams,
|
||||||
|
scalar cellWidth
|
||||||
|
);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// Default Constructors
|
||||||
|
|
||||||
|
|
||||||
|
// Member Functions
|
||||||
|
|
||||||
|
//- Verbosity
|
||||||
|
static inline bool verbose() noexcept
|
||||||
|
{
|
||||||
|
return PDRfitMeshScan::verbose();
|
||||||
|
}
|
||||||
|
|
||||||
|
//- Calculate the grid controls for the given obstacles
|
||||||
|
//- and parameters
|
||||||
|
Vector<PDRblock::gridControl>
|
||||||
|
calcGriding
|
||||||
|
(
|
||||||
|
const 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
|
||||||
|
(
|
||||||
|
const 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
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -82,6 +82,8 @@ int main(int argc, char* argv[])
|
|||||||
#include "setRootCase.H"
|
#include "setRootCase.H"
|
||||||
#include "createTime.H"
|
#include "createTime.H"
|
||||||
|
|
||||||
|
const bool dryrun = args.found("dry-run");
|
||||||
|
|
||||||
const word dictName("PDRsetFieldsDict");
|
const word dictName("PDRsetFieldsDict");
|
||||||
#include "setSystemRunTimeDictionaryIO.H"
|
#include "setSystemRunTimeDictionaryIO.H"
|
||||||
|
|
||||||
|
|||||||
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
// ************************************************************************* //
|
||||||
@ -35,6 +35,24 @@ cellWidth 0.22;
|
|||||||
// Optional
|
// Optional
|
||||||
cellWidthFactor 1.0;
|
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
|
// Advanced
|
||||||
|
|||||||
Reference in New Issue
Block a user