Files
OpenFOAM-12/applications/utilities/preProcessing/snappyHexMeshConfig/meshingSurfaceList.C
Chris Greenshields c2552978a3 snappyHexMeshConfig: utility to preconfigure input files for snappyHexMesh,
including blockMeshDict, surfaceFeaturesDict and snappyHexMeshDict, based on the
case surface geometry.

Description
    Preconfigures blockMeshDict, surfaceFeaturesDict and snappyHexMeshDict
    files based on the case surface geometry files.

    Starting from a standard OpenFOAM case, this utility locates surface
    geometry files, e.g. OBJ, STL format, in the constant/geometry directory.
    It writes out the configuration files for mesh generation with
    snappyHexMesh based on assumptions which can be overridden by options on
    the command line.

    The utility processes the surface geometry files, attempting to anticipate
    their intended purpose, trying in particular to recognise whether the
    domain represents an external or internal flow. If there is a surface
    which is closed, and is either single or surrounds all other surfaces,
    then it is assumed that it forms the external boundary of an internal
    flow. This assumption is overridden if the bounds of the background mesh
    are specified using the '-bounds' option and they are more than 50% larger
    than the surface bounds.

    Surfaces which form boundaries of the domain may contain named regions
    that are intended to become patches in the final mesh. Any surface region
    whose name begins with 'inlet' or 'outlet' will become a patch of the same
    name in the final mesh. On an external surface (for an internal flow),
    regions can be identified as inlets and outlets using the '-inletRegions'
    and '-outletRegions' options, respectively. When either option specifies a
    single region, the resulting patch name will be specifically 'inlet' or
    'outlet', respectively. Surfaces which are contained within the domain,
    which do not surround or intersect other surfaces, are assumed by default
    to be wall patches. Any closed surface which surrounds another (but not an
    external surface) is used to form a cellZone within the mesh. Any surface
    can be specifically identified as a cellZone with the '-cellZones' option,
    with the additional '-baffles' and '-rotatingZones' options available to
    assign a surface to a more specific use.

    The background mesh for snappyHexMesh is a single block generated by
    blockMesh, configured using a blockMeshDict file. The block bounds are
    automatically calculated, but can be overridden by the '-bounds'
    option. The number of cells is calculated to produce a fairly small
    prototype mesh. The cell density can be overridden by the '-nCells' option
    or can be scaled up by an integer factor using the '-refineBackground'
    option. When the background mesh is required to form patches in the final
    mesh, e.g. for an external flow, the user can specify the names and types
    of the patches corresponding to the six block faces using options such as
    '-xMinPatch', '-xMaxPatch', etc. The name and type of the default patch,
    formed from block faces which are not configured, can also be specified
    with the '-defaultPatch' option. The utility provides placeholder entries
    for all block faces unless the '-clearBoundary' option is used. A special
    '-cylindricalBackground' option generates a cylindrical background mesh,
    oriented along the z-axis along x = y = 0.

    The snappyHexMesh configuration is generated automatically, applying a set
    of defaults to the main configuration parameters. By default, explicit
    feature capturing is configured, for which a surfaceFeaturesDict file is
    written for the user to generate the features files with the
    surfaceFeatures utility. Implicit feature capturing can alternatively be
    selected with the '-implicitFeatures' option. Refinement levels can be
    controlled with a range of options including: '-refinementLevel' for the
    baseline refinement level; '-refinementSurfaces' for levels on specific
    surfaces; '-refinementRegions' for levels inside specific surfaces;
    '-refinementBoxes' for quick, box-shaped refinement regions specified by
    min and max bounds; '-refinementDists' for distance-based refinement; and
    '-nCellsBetweenLevels' to control the transition between refinement
    levels. A '-layers' option specifies additional layers of cells at wall
    boundaries. The insidePoint parameter is set to '(0 0 0)' by default but
    can be overridden using the '-insidePoint' option.
2023-07-07 12:32:14 +01:00

427 lines
11 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2023 OpenFOAM Foundation
\\/ M anipulation |
-------------------------------------------------------------------------------
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 "meshingSurfaceList.H"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
void Foam::meshingSurfaceList::mergeBoundingBoxes
(
boundBox& bb1,
const boundBox& bb2
)
{
if (bb1.volume() == 0)
{
bb1 = bb2;
return;
}
point& min1 = bb1.min();
point& max1 = bb1.max();
const point& min2 = bb2.min();
const point& max2 = bb2.max();
forAll(min1, i)
{
min1[i] = Foam::min(min1[i], min2[i]);
max1[i] = Foam::max(max1[i], max2[i]);
}
}
void Foam::meshingSurfaceList::swapExternalIndexZero(const label index)
{
if (index == 0)
{
return;
}
autoPtr<meshingSurface> s0Ptr(set(0, nullptr));
autoPtr<meshingSurface> sIndexPtr(set(index, nullptr));
set(index, s0Ptr.ptr());
set(0, sIndexPtr.ptr());
}
bool Foam::meshingSurfaceList::regionsValid
(
const wordList& specifiedRegions,
const wordList& regions,
const word& opt
)
{
if (specifiedRegions.empty())
{
return false;
}
forAll(specifiedRegions, s)
{
bool match(false);
forAll(regions, r)
{
match = (specifiedRegions[s] == regions[r]) || match;
if (match)
{
break;
}
}
if (!match)
{
FatalErrorInFunction
<< "Region '"<< specifiedRegions[s]
<< "' specified with the '" << opt
<< "' option" << nl
<< "does not match any regions in the external surface"
<< exit(FatalError);
}
}
return true;
}
void Foam::meshingSurfaceList::setSurfaceTypes
(
const List<word>& surfaces,
const surfaceType& type
)
{
forAll(surfaces, i)
{
const word surface = fileName(surfaces[i]).lessExt();
const word surfaceType = meshingSurface::surfaceTypeNames[type];
bool match = false;
// Check if the surface name matches a surface name
forAll(*this, surfi)
{
if (surface == operator[](surfi).name())
{
// For cellZone and rotatingZone, ensure surface is closed
if
(
!operator[](surfi).closed()
&& (
type == surfaceType::rotatingZone
|| type == surfaceType::cellZone
)
)
{
FatalErrorInFunction
<< "Argument to '-" << surfaceType
<< "' contains the " << surfaceType << " '"
<< surface << "'" << nl
<< "which is not a closed surface."
<< exit(FatalError);
}
operator[](surfi).type() = type;
match = true;
break;
}
}
if (!match)
{
FatalErrorInFunction
<< "Argument to '-" << surfaceType
<< "' contains the " << surfaceType << " '"
<< surface << "'" << nl
<< "which does not correspond to any surfaces."
<< exit(FatalError);
}
}
}
void Foam::meshingSurfaceList::setRotatingZoneBounds()
{
forAll(*this, surfi)
{
if (operator[](surfi).type() == surfaceType::rotatingZone)
{
mergeBoundingBoxes(rzbb_, operator[](surfi).bb());
}
}
}
void Foam::meshingSurfaceList::identifyCellZones()
{
forAll(*this, i)
{
if
(
operator[](i).type() == surfaceType::external
|| operator[](i).type() == surfaceType::rotatingZone
|| operator[](i).nParts() != 1
|| !operator[](i).closed()
)
{
continue;
}
forAll(*this, j)
{
if (i == j)
{
continue;
}
if (operator[](i).bb().contains(operator[](j).bb()))
{
operator[](i).type() = surfaceType::cellZone;
continue;
}
}
}
}
void Foam::meshingSurfaceList::reportWordList(const wordList& wl)
{
forAll(wl, i)
{
Info<< wl[i];
if (i != wl.size() - 1)
{
Info<< ", ";
}
}
Info<< endl;
}
void Foam::meshingSurfaceList::reportSurfaces()
{
Info<< "Case contains the following surface geometry files:"
<< endl;
forAll(*this, i)
{
Info<< "+ " << operator[](i).name() << nl
<< " + File: " << operator[](i).file() << nl
<< " + Bounding box: " << operator[](i).bb() << nl
<< " + " << (operator[](i).closed() ? "Closed" : "Open")
<< " surface" << endl;
switch (operator[](i).type())
{
case surfaceType::external:
{
Info << " + External boundary surface" << endl;
if (!operator[](i).inletRegions().empty())
{
Info<< " + Inlet regions: ";
reportWordList(operator[](i).inletRegions());
}
if (!operator[](i).outletRegions().empty())
{
Info << " + Outlet regions: ";
reportWordList(operator[](i).outletRegions());
}
break;
}
case surfaceType::wall:
{
Info << " + Wall boundary surface" << endl;
break;
}
case surfaceType::cellZone:
{
Info << " + Cell zone surface" << endl;
break;
}
case surfaceType::rotatingZone:
{
Info << " + Rotating zone surface" << endl;
break;
}
case surfaceType::baffle:
{
Info << " + Baffle wall surface" << endl;
break;
}
}
}
Info<< endl;
}
void Foam::meshingSurfaceList::setBounds(const boundBox& bb)
{
if (bb.contains(bb_))
{
Info<< "Specifed bounding box contains the overall bounding box"
<< endl;
}
else
{
WarningInFunction
<< "Specified bounding box does not contain the overall "
<< "bounding box"
<< endl;
}
if (bb.mag() > 1.5*bb_.mag())
{
Info<< "Specified bounding box is > (1.5 * overall "
<< "bounding box)" << nl
<< "**Assuming this is an external flow**"
<< endl;
operator[](0).type() = surfaceType::wall;
}
Info<< endl;
bb_ = bb;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::meshingSurfaceList::meshingSurfaceList
(
const Time& time,
const fileNameList& surfaces,
const wordList& cellZones,
const wordList& rotatingZones,
const wordList& baffles,
const boundBox& bb,
const wordList& specifiedInletRegions,
const wordList& specifiedOutletRegions
)
:
PtrList<meshingSurface>(),
bb_(),
rzbb_()
{
// Load all the surfaces and construct the bounding box
forAll(surfaces, i)
{
append(new meshingSurface(surfaces[i], time));
mergeBoundingBoxes(bb_, last().bb());
}
label externalID = 0;
forAll(surfaces, i)
{
// Test for external surface inflates the bounding box by a small factor
// to account for imprecise surface geometry files
boundBox bbInflate = operator[](i).bb();
bbInflate.inflate(1e-4);
if
(
operator[](i).closed()
&& operator[](i).nParts() == 1
&& bbInflate.contains(bb_)
)
{
externalID = i;
operator[](i).type() = surfaceType::external;
// Override any inlet and outlet regions on external boundary
// specified by the '-inletRegions' and '-outletRegions' options
const wordList& regions = operator[](i).regions();
wordList& inletRegions = operator[](i).inletRegions();
if (regionsValid(specifiedInletRegions, regions, "-inletRegions"))
{
inletRegions = specifiedInletRegions;
}
wordList& outletRegions = operator[](i).outletRegions();
if (regionsValid(specifiedOutletRegions, regions, "-outletRegions"))
{
outletRegions = specifiedOutletRegions;
}
// If inletRegions and outletRegions are both empty, set "template"
// names
if (inletRegions.empty() && outletRegions.empty())
{
inletRegions.append("<inletRegion>");
outletRegions.append("<outletRegion>");
}
}
}
swapExternalIndexZero(externalID);
if (!rotatingZones.empty())
{
setSurfaceTypes(rotatingZones, surfaceType::rotatingZone);
}
if (!baffles.empty())
{
setSurfaceTypes(baffles, surfaceType::baffle);
}
setRotatingZoneBounds();
if (cellZones.empty())
{
identifyCellZones();
}
else
{
setSurfaceTypes(cellZones, surfaceType::cellZone);
}
reportSurfaces();
if (bb.volume() != 0)
{
setBounds(bb);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::meshingSurfaceList::~meshingSurfaceList()
{}
// ************************************************************************* //