/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2021 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 .
Application
extrudeMesh
Group
grpMeshGenerationUtilities
Description
Extrude mesh from existing patch (by default outwards facing normals;
optional flips faces) or from patch read from file.
Note: Merges close points so be careful.
Type of extrusion prescribed by run-time selectable model.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "polyTopoChange.H"
#include "polyTopoChanger.H"
#include "edgeCollapser.H"
#include "perfectInterface.H"
#include "addPatchCellLayer.H"
#include "fvMesh.H"
#include "MeshedSurfaces.H"
#include "globalIndex.H"
#include "cellSet.H"
#include "fvMeshTools.H"
#include "extrudedMesh.H"
#include "extrudeModel.H"
#include "wedge.H"
#include "wedgePolyPatch.H"
#include "planeExtrusion.H"
#include "emptyPolyPatch.H"
#include "processorMeshes.H"
#include "hexRef8Data.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
enum ExtrudeMode
{
MESH,
PATCH,
SURFACE
};
static const Enum ExtrudeModeNames
{
{ ExtrudeMode::MESH, "mesh" },
{ ExtrudeMode::PATCH, "patch" },
{ ExtrudeMode::SURFACE, "surface" },
};
label findPatchID(const polyBoundaryMesh& patches, const word& name)
{
const label patchID = patches.findPatchID(name);
if (patchID == -1)
{
FatalErrorInFunction
<< "Cannot find patch " << name
<< " in the source mesh.\n"
<< "Valid patch names are " << patches.names()
<< exit(FatalError);
}
return patchID;
}
labelList patchFaces(const polyBoundaryMesh& patches, const wordList& names)
{
label n = 0;
forAll(names, i)
{
const polyPatch& pp = patches[findPatchID(patches, names[i])];
n += pp.size();
}
labelList faceLabels(n);
n = 0;
forAll(names, i)
{
const polyPatch& pp = patches[findPatchID(patches, names[i])];
forAll(pp, j)
{
faceLabels[n++] = pp.start()+j;
}
}
return faceLabels;
}
void zoneFaces
(
const faceZoneMesh& fzs,
const wordList& names,
labelList& faceLabels,
bitSet& faceFlip
)
{
label n = 0;
forAll(names, i)
{
const auto& pp = fzs[fzs.findZoneID(names[i])];
n += pp.size();
}
faceLabels.setSize(n);
faceFlip.setSize(n);
n = 0;
forAll(names, i)
{
const auto& pp = fzs[fzs.findZoneID(names[i])];
const boolList& ppFlip = pp.flipMap();
forAll(pp, i)
{
faceLabels[n] = pp[i];
faceFlip[n] = ppFlip[i];
n++;
}
}
}
void updateFaceLabels(const mapPolyMesh& map, labelList& faceLabels)
{
const labelList& reverseMap = map.reverseFaceMap();
labelList newFaceLabels(faceLabels.size());
label newI = 0;
forAll(faceLabels, i)
{
label oldFacei = faceLabels[i];
if (reverseMap[oldFacei] >= 0)
{
newFaceLabels[newI++] = reverseMap[oldFacei];
}
}
newFaceLabels.setSize(newI);
faceLabels.transfer(newFaceLabels);
}
void updateCellSet(const mapPolyMesh& map, labelHashSet& cellLabels)
{
const labelList& reverseMap = map.reverseCellMap();
labelHashSet newCellLabels(2*cellLabels.size());
forAll(cellLabels, i)
{
label oldCelli = cellLabels[i];
if (reverseMap[oldCelli] >= 0)
{
newCellLabels.insert(reverseMap[oldCelli]);
}
}
cellLabels.transfer(newCellLabels);
}
template
void changeFrontBackPatches
(
polyMesh& mesh,
const word& frontPatchName,
const word& backPatchName
)
{
const polyBoundaryMesh& patches = mesh.boundaryMesh();
label frontPatchi = findPatchID(patches, frontPatchName);
label backPatchi = findPatchID(patches, backPatchName);
DynamicList newPatches(patches.size());
forAll(patches, patchi)
{
const polyPatch& pp(patches[patchi]);
if (patchi == frontPatchi || patchi == backPatchi)
{
newPatches.append
(
new PatchType
(
pp.name(),
pp.size(),
pp.start(),
pp.index(),
mesh.boundaryMesh(),
PatchType::typeName
)
);
}
else
{
newPatches.append(pp.clone(mesh.boundaryMesh()).ptr());
}
}
// Edit patches
mesh.removeBoundary();
mesh.addPatches(newPatches, true);
}
int main(int argc, char *argv[])
{
argList::addNote
(
"Extrude mesh from existing patch."
);
#include "addRegionOption.H"
argList::addOption("dict", "file", "Alternative extrudeMeshDict");
#include "setRootCase.H"
#include "createTimeExtruded.H"
// Get optional regionName
word regionName;
word regionDir;
if (args.readIfPresent("region", regionName))
{
regionDir = regionName;
Info<< "Create mesh " << regionName << " for time = "
<< runTimeExtruded.timeName() << nl << endl;
}
else
{
regionName = fvMesh::defaultRegion;
Info<< "Create mesh for time = "
<< runTimeExtruded.timeName() << nl << endl;
}
const IOdictionary dict
(
IOobject::selectIO
(
IOobject
(
"extrudeMeshDict",
runTimeExtruded.system(),
runTimeExtruded,
IOobject::MUST_READ_IF_MODIFIED,
IOobject::NO_WRITE
),
args.getOrDefault("dict", "")
)
);
// Point generator
autoPtr model(extrudeModel::New(dict));
// Whether to flip normals
const bool flipNormals(dict.get("flipNormals"));
// What to extrude
const ExtrudeMode mode = ExtrudeModeNames.get("constructFrom", dict);
// Any merging of small edges
const scalar mergeTol(dict.getOrDefault("mergeTol", 1e-4));
Info<< "Extruding from " << ExtrudeModeNames[mode]
<< " using model " << model().type() << endl;
if (flipNormals)
{
Info<< "Flipping normals before extruding" << endl;
}
if (mergeTol > 0)
{
Info<< "Collapsing edges < " << mergeTol << " of bounding box" << endl;
}
else
{
Info<< "Not collapsing any edges after extrusion" << endl;
}
Info<< endl;
// Generated mesh (one of either)
autoPtr meshFromMesh;
autoPtr meshFromSurface;
// Faces on front and back for stitching (in case of mergeFaces)
word frontPatchName;
labelList frontPatchFaces;
word backPatchName;
labelList backPatchFaces;
// Optional added cells (get written to cellSet)
labelHashSet addedCellsSet;
// Optional refinement data
autoPtr refDataPtr;
if (mode == PATCH || mode == MESH)
{
if (flipNormals && mode == MESH)
{
FatalErrorInFunction
<< "Flipping normals not supported for extrusions from mesh."
<< exit(FatalError);
}
fileName sourceCasePath(dict.get("sourceCase").expand());
fileName sourceRootDir = sourceCasePath.path();
fileName sourceCaseDir = sourceCasePath.name();
if (Pstream::parRun())
{
sourceCaseDir =
sourceCaseDir/("processor" + Foam::name(Pstream::myProcNo()));
}
wordList sourcePatches;
wordList sourceFaceZones;
if
(
dict.readIfPresent
(
"sourcePatches",
sourcePatches,
keyType::LITERAL
)
)
{
if (sourcePatches.size() == 1)
{
frontPatchName = sourcePatches[0];
}
Info<< "Extruding patches " << sourcePatches
<< " on mesh " << sourceCasePath << nl
<< endl;
}
else
{
dict.readEntry("sourceFaceZones", sourceFaceZones);
Info<< "Extruding faceZones " << sourceFaceZones
<< " on mesh " << sourceCasePath << nl
<< endl;
}
Time runTime
(
Time::controlDictName,
sourceRootDir,
sourceCaseDir
);
#include "createNamedMesh.H"
const polyBoundaryMesh& patches = mesh.boundaryMesh();
// Extrusion engine. Either adding to existing mesh or
// creating separate mesh.
addPatchCellLayer layerExtrude(mesh, (mode == MESH));
labelList meshFaces;
bitSet faceFlips;
if (sourceFaceZones.size())
{
zoneFaces(mesh.faceZones(), sourceFaceZones, meshFaces, faceFlips);
}
else
{
meshFaces = patchFaces(patches, sourcePatches);
faceFlips.setSize(meshFaces.size());
}
if (mode == PATCH && flipNormals)
{
// Cheat. Flip patch faces in mesh. This invalidates the
// mesh (open cells) but does produce the correct extrusion.
polyTopoChange meshMod(mesh);
forAll(meshFaces, i)
{
label meshFacei = meshFaces[i];
label patchi = patches.whichPatch(meshFacei);
label own = mesh.faceOwner()[meshFacei];
label nei = -1;
if (patchi == -1)
{
nei = mesh.faceNeighbour()[meshFacei];
}
label zoneI = mesh.faceZones().whichZone(meshFacei);
bool zoneFlip = false;
if (zoneI != -1)
{
label index = mesh.faceZones()[zoneI].whichFace(meshFacei);
zoneFlip = mesh.faceZones()[zoneI].flipMap()[index];
}
meshMod.modifyFace
(
mesh.faces()[meshFacei].reverseFace(), // modified face
meshFacei, // label of face
own, // owner
nei, // neighbour
true, // face flip
patchi, // patch for face
zoneI, // zone for face
zoneFlip // face flip in zone
);
}
// Change the mesh. No inflation.
autoPtr map = meshMod.changeMesh(mesh, false);
// Update fields
mesh.updateMesh(map());
// Move mesh (since morphing does not do this)
if (map().hasMotionPoints())
{
mesh.movePoints(map().preMotionPoints());
}
}
indirectPrimitivePatch extrudePatch
(
IndirectList
(
mesh.faces(),
meshFaces
),
mesh.points()
);
// Determine extrudePatch normal
pointField extrudePatchPointNormals
(
PatchTools::pointNormals(mesh, extrudePatch, faceFlips)
);
// Precalculate mesh edges for pp.edges.
const labelList meshEdges
(
extrudePatch.meshEdges
(
mesh.edges(),
mesh.pointEdges()
)
);
// Global face indices engine
const globalIndex globalFaces(mesh.nFaces());
// Determine extrudePatch.edgeFaces in global numbering (so across
// coupled patches)
labelListList edgeGlobalFaces
(
addPatchCellLayer::globalEdgeFaces
(
mesh,
globalFaces,
extrudePatch
)
);
// Determine what patches boundary edges need to get extruded into.
// This might actually cause edge-connected processors to become
// face-connected so might need to introduce new processor boundaries.
// Calculates:
// - per pp.edge the patch to extrude into
// - any additional processor boundaries (patchToNbrProc = map from
// new patchID to neighbour processor)
// - number of new patches (nPatches)
labelList edgePatchID;
labelList edgeZoneID;
boolList edgeFlip;
labelList inflateFaceID;
label nPatches;
Map