Now faceZones are handled directly by the applications and the new faceZone::topoChange function so that any face can now be in any number of zones, significantly increasing the flexibility and usefulness of faceZones. This completes the generalisation of cellZone, faceZone and pointZone to support multiple zones for each cell, face or point respectively. Next step will be to make zones polymorphic and run-time selectable so that they can alter during the run and adapt to moving meshes for example.
718 lines
22 KiB
C++
718 lines
22 KiB
C++
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration | Website: https://openfoam.org
|
|
\\ / A nd | Copyright (C) 2011-2024 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/>.
|
|
|
|
Application
|
|
createBaffles
|
|
|
|
Description
|
|
Makes faces into boundary faces. Does not duplicate points.
|
|
|
|
Notes:
|
|
- If any coupled patch face is selected for baffling the opposite member
|
|
has to be selected for baffling as well.
|
|
- If fields are being modified then boundary conditions must be specified
|
|
within the patch's patchFields sub-dictionary.
|
|
- Any patches left with no faces will be removed, except for patches of
|
|
coupled, internal, of non-conformal type
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "argList.H"
|
|
#include "Time.H"
|
|
#include "polyTopoChange.H"
|
|
#include "ReadFields.H"
|
|
#include "volFields.H"
|
|
#include "surfaceFields.H"
|
|
#include "pointFields.H"
|
|
#include "fvMeshMapper.H"
|
|
#include "faceSelection.H"
|
|
#include "searchableSurface.H"
|
|
#include "fvMeshTools.H"
|
|
#include "systemDict.H"
|
|
#include "processorPolyPatch.H"
|
|
#include "internalPolyPatch.H"
|
|
#include "nonConformalPolyPatch.H"
|
|
|
|
using namespace Foam;
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
void filterPatches(fvMesh& mesh, const HashSet<word>& bafflePatches)
|
|
{
|
|
// Remove any zero-sized patches, except for constraint types, and any
|
|
// specified in the system dictionary
|
|
|
|
const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
|
|
|
|
label newPatchi = 0;
|
|
|
|
// List of patch's new indices
|
|
labelList oldToNew(bMesh.size(), -1);
|
|
|
|
// Add all the kept non-processor patches to the list first
|
|
forAll(bMesh, patchi)
|
|
{
|
|
const polyPatch& pp = bMesh[patchi];
|
|
|
|
if (!isA<processorPolyPatch>(pp))
|
|
{
|
|
if
|
|
(
|
|
bafflePatches.found(pp.name())
|
|
|| fvPatch::constraintType(pp.type())
|
|
|| returnReduce(pp.size(), sumOp<label>())
|
|
)
|
|
{
|
|
oldToNew[patchi] = newPatchi++;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now add all the processor patches
|
|
forAll(bMesh, patchi)
|
|
{
|
|
const polyPatch& pp = bMesh[patchi];
|
|
|
|
if (isA<processorPolyPatch>(pp))
|
|
{
|
|
oldToNew[patchi] = newPatchi++;
|
|
}
|
|
}
|
|
|
|
// Note how many patches are to be kept
|
|
const label nKeepPatches = newPatchi;
|
|
|
|
// If there are any to remove ...
|
|
if (nKeepPatches != bMesh.size())
|
|
{
|
|
Info<< endl << "Removing zero-sized patches:" << endl << incrIndent;
|
|
|
|
// Add all the removed patches to the list
|
|
forAll(oldToNew, patchi)
|
|
{
|
|
if (oldToNew[patchi] == -1)
|
|
{
|
|
Info<< indent << bMesh[patchi].name()
|
|
<< " type " << bMesh[patchi].type()
|
|
<< " at position " << patchi << endl;
|
|
|
|
oldToNew[patchi] = newPatchi++;
|
|
}
|
|
}
|
|
|
|
Info<< decrIndent;
|
|
|
|
// Permute the mesh, keeping only the ones not to be removed
|
|
fvMeshTools::reorderPatches(mesh, oldToNew, nKeepPatches, true);
|
|
|
|
Info<< endl;
|
|
}
|
|
}
|
|
|
|
|
|
void modifyOrAddFace
|
|
(
|
|
polyTopoChange& meshMod,
|
|
const face& f,
|
|
const label facei,
|
|
const label own,
|
|
const bool flipFaceFlux,
|
|
const label newPatchi,
|
|
PackedBoolList& modifiedFace
|
|
)
|
|
{
|
|
if (!modifiedFace[facei])
|
|
{
|
|
// First usage of face. Modify.
|
|
meshMod.modifyFace
|
|
(
|
|
f, // modified face
|
|
facei, // label of face
|
|
own, // owner
|
|
-1, // neighbour
|
|
flipFaceFlux, // face flip
|
|
newPatchi // patch for face
|
|
);
|
|
|
|
modifiedFace[facei] = 1;
|
|
}
|
|
else
|
|
{
|
|
// Second usage of face. Add.
|
|
meshMod.addFace
|
|
(
|
|
f, // modified face
|
|
own, // owner
|
|
-1, // neighbour
|
|
facei, // master face
|
|
flipFaceFlux, // face flip
|
|
newPatchi // patch for face
|
|
);
|
|
}
|
|
}
|
|
|
|
|
|
label createFaces
|
|
(
|
|
const bool internalFacesOnly,
|
|
const fvMesh& mesh,
|
|
const faceZone& fZone,
|
|
const label newOwnerPatchi,
|
|
const label newNeighbourPatchi,
|
|
polyTopoChange& meshMod,
|
|
PackedBoolList& modifiedFace
|
|
)
|
|
{
|
|
label nModified = 0;
|
|
|
|
const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
|
|
|
|
// Pass 1. Do selected side of zone
|
|
for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
|
|
{
|
|
const label zoneFacei = fZone.whichFace(facei);
|
|
|
|
if (zoneFacei != -1)
|
|
{
|
|
if (!fZone.flipMap()[zoneFacei])
|
|
{
|
|
// Use owner side of face
|
|
modifyOrAddFace
|
|
(
|
|
meshMod,
|
|
mesh.faces()[facei], // modified face
|
|
facei, // label of face
|
|
mesh.faceOwner()[facei],// owner
|
|
false, // face flip
|
|
newOwnerPatchi, // patch for face
|
|
modifiedFace // modify or add status
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Use neighbour side of face.
|
|
// To keep faceZone pointing out of original neighbour
|
|
// we don't need to set faceFlip since that cell
|
|
// now becomes the owner
|
|
modifyOrAddFace
|
|
(
|
|
meshMod,
|
|
mesh.faces()[facei].reverseFace(), // modified face
|
|
facei, // label of face
|
|
mesh.faceNeighbour()[facei],// owner
|
|
true, // face flip
|
|
newOwnerPatchi, // patch for face
|
|
modifiedFace // modify or add status
|
|
);
|
|
}
|
|
|
|
nModified++;
|
|
}
|
|
}
|
|
|
|
// Pass 2. Do other side of zone
|
|
for (label facei = 0; facei < mesh.nInternalFaces(); facei++)
|
|
{
|
|
label zoneFacei = fZone.whichFace(facei);
|
|
|
|
if (zoneFacei != -1)
|
|
{
|
|
if (!fZone.flipMap()[zoneFacei])
|
|
{
|
|
// Use neighbour side of face
|
|
modifyOrAddFace
|
|
(
|
|
meshMod,
|
|
mesh.faces()[facei].reverseFace(), // modified face
|
|
facei, // label of face
|
|
mesh.faceNeighbour()[facei], // owner
|
|
true, // face flip
|
|
newNeighbourPatchi, // patch for face
|
|
modifiedFace // modify or add
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// Use owner side of face
|
|
modifyOrAddFace
|
|
(
|
|
meshMod,
|
|
mesh.faces()[facei], // modified face
|
|
facei, // label of face
|
|
mesh.faceOwner()[facei],// owner
|
|
false, // face flip
|
|
newNeighbourPatchi, // patch for face
|
|
modifiedFace // modify or add status
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Modify any boundary faces
|
|
forAll(bMesh, patchi)
|
|
{
|
|
const polyPatch& pp = bMesh[patchi];
|
|
|
|
if (pp.coupled() || !internalFacesOnly)
|
|
{
|
|
forAll(pp, i)
|
|
{
|
|
const label facei = pp.start() + i;
|
|
const label zoneFacei = fZone.whichFace(facei);
|
|
|
|
if (zoneFacei != -1)
|
|
{
|
|
const polyPatch& newPp =
|
|
fZone.flipMap()[zoneFacei]
|
|
? bMesh[newNeighbourPatchi]
|
|
: bMesh[newOwnerPatchi];
|
|
|
|
// We cannot move coupled faces to different coupled
|
|
// faces. Generate an error if this is attempted.
|
|
if (pp.coupled() && newPp.coupled())
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Face on coupled patch \"" << pp.name()
|
|
<< "\" selected for conversion to coupled "
|
|
<< "patch \"" << newPp.name() << "\". "
|
|
<< "Conversion from coupled patch to coupled "
|
|
<< "patch is not allowed."
|
|
<< exit(FatalError);
|
|
}
|
|
|
|
modifyOrAddFace
|
|
(
|
|
meshMod,
|
|
mesh.faces()[facei], // modified face
|
|
facei, // label of face
|
|
mesh.faceOwner()[facei], // owner
|
|
false, // face flip
|
|
newPp.index(), // patch for face
|
|
modifiedFace // modify or add
|
|
);
|
|
|
|
nModified++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return returnReduce(nModified, sumOp<label>());
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
argList::addNote
|
|
(
|
|
"Makes internal faces into boundary faces.\n"
|
|
"Does not duplicate points."
|
|
);
|
|
#include "addDictOption.H"
|
|
#include "addOverwriteOption.H"
|
|
#include "addRegionOption.H"
|
|
|
|
#include "setRootCase.H"
|
|
#include "createTimeNoFunctionObjects.H"
|
|
#include "createRegionMeshNoChangers.H"
|
|
|
|
const polyBoundaryMesh& bMesh = mesh.boundaryMesh();
|
|
|
|
const bool overwrite = args.optionFound("overwrite");
|
|
|
|
const word oldInstance = mesh.pointsInstance();
|
|
|
|
// Read the system dictionary and global controls
|
|
const dictionary dict(systemDict("createBafflesDict", args, mesh));
|
|
Switch internalFacesOnly = dict.lookup<Switch>("internalFacesOnly");
|
|
const Switch fields = dict.lookupOrDefault("fields", false);
|
|
|
|
// Create face selections
|
|
PtrList<faceSelection> selectors;
|
|
{
|
|
const dictionary& selectionsDict = dict.subDict("baffles");
|
|
|
|
label n = 0;
|
|
forAllConstIter(dictionary, selectionsDict, iter)
|
|
{
|
|
if (iter().isDict())
|
|
{
|
|
n++;
|
|
}
|
|
}
|
|
selectors.setSize(n);
|
|
|
|
n = 0;
|
|
forAllConstIter(dictionary, selectionsDict, iter)
|
|
{
|
|
if (iter().isDict())
|
|
{
|
|
selectors.set
|
|
(
|
|
n++,
|
|
faceSelection::New(iter().keyword(), mesh, iter().dict())
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (internalFacesOnly)
|
|
{
|
|
Info<< "Not converting faces on non-coupled patches." << nl << endl;
|
|
}
|
|
|
|
// Read fields
|
|
IOobjectList objects(mesh, runTime.name());
|
|
if (fields) Info<< "Reading geometric fields" << nl << endl;
|
|
#include "readVolFields.H"
|
|
#include "readSurfaceFields.H"
|
|
#include "readPointFields.H"
|
|
if (fields) Info<< endl;
|
|
|
|
// Creating faceZones for selectors that are not already faceZones
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const word& name = selectors[selectorI].name();
|
|
|
|
if (mesh.faceZones().findIndex(name) == -1)
|
|
{
|
|
const label sz = mesh.faceZones().size();
|
|
|
|
labelList addr(0);
|
|
boolList flip(0);
|
|
|
|
mesh.faceZones().setSize(sz+1);
|
|
mesh.faceZones().set
|
|
(
|
|
sz,
|
|
new faceZone(name, addr, flip, mesh.faceZones())
|
|
);
|
|
}
|
|
}
|
|
|
|
// Per face, its zone ID and flip status
|
|
labelList faceToZoneID(mesh.nFaces(), -1);
|
|
boolList faceToFlip(mesh.nFaces(), false);
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const word& name = selectors[selectorI].name();
|
|
label zoneID = mesh.faceZones().findIndex(name);
|
|
|
|
selectors[selectorI].select(zoneID, faceToZoneID, faceToFlip);
|
|
}
|
|
|
|
// Add faces to faceZones
|
|
labelList nFaces(mesh.faceZones().size(), 0);
|
|
forAll(faceToZoneID, facei)
|
|
{
|
|
label zoneID = faceToZoneID[facei];
|
|
if (zoneID != -1)
|
|
{
|
|
nFaces[zoneID]++;
|
|
}
|
|
}
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const word& name = selectors[selectorI].name();
|
|
const label zoneID = mesh.faceZones().findIndex(name);
|
|
|
|
label& n = nFaces[zoneID];
|
|
labelList addr(n);
|
|
boolList flip(n);
|
|
|
|
n = 0;
|
|
forAll(faceToZoneID, facei)
|
|
{
|
|
label zone = faceToZoneID[facei];
|
|
if (zone == zoneID)
|
|
{
|
|
addr[n] = facei;
|
|
flip[n] = faceToFlip[facei];
|
|
n++;
|
|
}
|
|
}
|
|
|
|
Info<< "Created zone '" << name << "' with "
|
|
<< returnReduce(n, sumOp<label>()) << " faces" << endl;
|
|
|
|
mesh.faceZones().set
|
|
(
|
|
zoneID,
|
|
new faceZone(name, addr, flip, mesh.faceZones())
|
|
);
|
|
}
|
|
|
|
Info<< endl;
|
|
|
|
// Keywords for the two patch entries in each selector dictionary. Note
|
|
// "owner" and "neighbour" are preferred; "master" and "slave" are still
|
|
// permitted for backwards compatibility.
|
|
const FixedList<wordList, 2> patchEntryKeywords
|
|
({
|
|
wordList({"owner", "master"}),
|
|
wordList({"neighbour", "slave"})
|
|
});
|
|
|
|
// Create the baffles. Notes:
|
|
//
|
|
// - This is done in multiple steps
|
|
// - Patches are created with 'calculated' patchFields
|
|
// - Faces are moved into these patches
|
|
// - The patchFields are changed to the desired type
|
|
// - This helps resolve ordering issues
|
|
// - patchFields cannot be created at the same time as patches since a
|
|
// coupled patchField constructor frequently needs access to the
|
|
// neighbouring patch
|
|
// - patchFields need to be created after the faces have been added to
|
|
// the patch so that they don't later have to be mapped from nothing
|
|
//
|
|
|
|
// Add patches
|
|
HashSet<word> bafflePatches;
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const word& groupName = selectors[selectorI].name();
|
|
const dictionary& dict =
|
|
selectors[selectorI].dict().optionalSubDict("patches");
|
|
|
|
forAll(patchEntryKeywords, i)
|
|
{
|
|
dictionary patchDict =
|
|
dict.lookupEntryBackwardsCompatible
|
|
(
|
|
patchEntryKeywords[i],
|
|
false,
|
|
false
|
|
).dict();
|
|
|
|
const word patchName(patchDict.lookup<word>("name"));
|
|
|
|
if (bMesh.findIndex(patchName) == -1)
|
|
{
|
|
Info<< "Adding patch '" << patchName << "' to the mesh" << endl;
|
|
|
|
// Create the patch
|
|
patchDict.set("nFaces", 0);
|
|
patchDict.set("startFace", 0);
|
|
autoPtr<polyPatch> ppPtr
|
|
(
|
|
polyPatch::New
|
|
(
|
|
patchName,
|
|
patchDict,
|
|
0,
|
|
bMesh
|
|
)
|
|
);
|
|
polyPatch& pp = ppPtr();
|
|
|
|
// Add it to the group
|
|
if (!groupName.empty() && !pp.inGroup(groupName))
|
|
{
|
|
pp.inGroups().append(groupName);
|
|
}
|
|
|
|
// Add the patch to the mesh, creating calculated boundary
|
|
// conditions everywhere. These will be changed later.
|
|
fvMeshTools::addPatch
|
|
(
|
|
mesh,
|
|
pp,
|
|
dictionary(), // do not set specialised patchFields
|
|
calculatedFvPatchField<scalar>::typeName,
|
|
true // parallel sync'ed addition
|
|
);
|
|
}
|
|
else
|
|
{
|
|
Info<< "Patch '" << patchName
|
|
<< "' already exists in the mesh" << endl;
|
|
|
|
patchDict.remove("name");
|
|
patchDict.remove("patchFields");
|
|
|
|
if (!patchDict.empty())
|
|
{
|
|
WarningInFunction
|
|
<< "Patch settings found in " << patchDict.name()
|
|
<< " for patch '" << patchName << "', but this patch "
|
|
<< "already exists so these settings will not be used"
|
|
<< endl;
|
|
}
|
|
}
|
|
|
|
bafflePatches.insert(patchName);
|
|
}
|
|
}
|
|
|
|
Info<< endl;
|
|
|
|
// Make sure patches and zoneFaces are synchronised across couples
|
|
mesh.boundaryMesh().checkParallelSync(true);
|
|
mesh.faceZones().checkParallelSync(true);
|
|
|
|
// Do the topology changes. Notes:
|
|
//
|
|
// - Loop in incrementing face order (not necessary if faceZone ordered).
|
|
// Preserves any existing ordering on patch faces.
|
|
// - Two passes, do non-flip faces first and flip faces second. This
|
|
// guarantees that when e.g. creating a cyclic all faces from one
|
|
// side come first and faces from the other side next.
|
|
//
|
|
polyTopoChange meshMod(mesh);
|
|
{
|
|
PackedBoolList modifiedFace(mesh.nFaces());
|
|
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const word& groupName = selectors[selectorI].name();
|
|
|
|
const label fZonei = mesh.faceZones().findIndex(groupName);
|
|
const faceZone& fZone = mesh.faceZones()[fZonei];
|
|
|
|
const dictionary& dict =
|
|
selectors[selectorI].dict().optionalSubDict("patches");
|
|
|
|
FixedList<label, 2> newPatchIDs;
|
|
forAll(patchEntryKeywords, i)
|
|
{
|
|
const dictionary& patchDict =
|
|
dict.lookupEntryBackwardsCompatible
|
|
(
|
|
patchEntryKeywords[i],
|
|
false,
|
|
false
|
|
).dict();
|
|
|
|
const word patchName(patchDict.lookup<word>("name"));
|
|
|
|
newPatchIDs[i] = bMesh.findIndex(patchName);
|
|
}
|
|
|
|
const label nModified =
|
|
createFaces
|
|
(
|
|
internalFacesOnly,
|
|
mesh,
|
|
fZone,
|
|
newPatchIDs[0],
|
|
newPatchIDs[1],
|
|
meshMod,
|
|
modifiedFace
|
|
);
|
|
|
|
Info<< "Converted " << nModified
|
|
<< " faces into boundary faces on ";
|
|
if (newPatchIDs[0] == newPatchIDs[1])
|
|
{
|
|
Info<< "patch '" << bMesh[newPatchIDs[0]].name() << "'";
|
|
}
|
|
else
|
|
{
|
|
Info<< "patches '" << bMesh[newPatchIDs[0]].name()
|
|
<< "' and '" << bMesh[newPatchIDs[1]].name() << "'";
|
|
}
|
|
Info<< endl;
|
|
}
|
|
}
|
|
|
|
Info<< endl;
|
|
|
|
if (!overwrite)
|
|
{
|
|
runTime++;
|
|
}
|
|
|
|
// Change the mesh. Change points directly
|
|
autoPtr<polyTopoChangeMap> map = meshMod.changeMesh(mesh);
|
|
|
|
// Update fields
|
|
mesh.topoChange(map);
|
|
|
|
// Change the patch fields
|
|
HashSet<word> bafflePatchFields;
|
|
forAll(selectors, selectorI)
|
|
{
|
|
const dictionary& dict =
|
|
selectors[selectorI].dict().optionalSubDict("patches");
|
|
|
|
forAll(patchEntryKeywords, i)
|
|
{
|
|
const dictionary& patchDict =
|
|
dict.lookupEntryBackwardsCompatible
|
|
(
|
|
patchEntryKeywords[i],
|
|
false,
|
|
false
|
|
).dict();
|
|
|
|
const word patchName(patchDict.lookup<word>("name"));
|
|
|
|
if (!bafflePatchFields.found(patchName))
|
|
{
|
|
bafflePatchFields.insert(patchName);
|
|
|
|
dictionary patchFieldsDict =
|
|
patchDict.subOrEmptyDict("patchFields");
|
|
|
|
fvMeshTools::setPatchFields
|
|
(
|
|
mesh,
|
|
bMesh.findIndex(patchName),
|
|
patchFieldsDict
|
|
);
|
|
}
|
|
else
|
|
{
|
|
if (patchDict.found("patchFields"))
|
|
{
|
|
WarningInFunction
|
|
<< "Patch field settings found in " << patchDict.name()
|
|
<< " for patch '" << patchName << "', but fields have "
|
|
<< "already been set for this patch so these settings "
|
|
<< "will not be used" << endl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove any now zero-sized patches
|
|
filterPatches(mesh, bafflePatches);
|
|
|
|
if (overwrite)
|
|
{
|
|
mesh.setInstance(oldInstance);
|
|
}
|
|
|
|
Info<< "Writing mesh to " << runTime.name() << endl;
|
|
|
|
mesh.write();
|
|
|
|
Info<< "End\n" << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|