Merge branch 'feature-splitMeshRegions_autoPatch' into 'develop'

Draft: Extend splitMeshRegions to automatically create AMI inter-region patches

See merge request Development/openfoam!525
This commit is contained in:
Mattijs Janssens
2025-09-26 09:51:31 +00:00

View File

@ -43,8 +43,9 @@ Description
- volScalarField with regions as different scalars (-detectOnly) - volScalarField with regions as different scalars (-detectOnly)
or or
- mesh with multiple regions and mapped patches. These patches - mesh with multiple regions and mapped patches. These patches
either cover the whole interface between two region (default) or either cover the whole interface between two region (default),
only part according to faceZones (-useFaceZones) only part according to faceZones (-useFaceZones) or be auto-generated
according to an AMI method (see below)
or or
- mesh with cells put into cellZones (-makeCellZones) - mesh with cells put into cellZones (-makeCellZones)
@ -98,6 +99,14 @@ Description
- boundaryRegionAddressing : for every patch in this region the - boundaryRegionAddressing : for every patch in this region the
patch in the original mesh (or -1 if added patch) patch in the original mesh (or -1 if added patch)
- auto-generate patches using AMI area-overlap detection. This requires a
patchSet to apply it to and an optional AMIMethod (default is
faceAreaWeightAMI2D).
-autoPatch '("solid*")'
-AMIMethod faceAreaWeightAMI2D
Any mapped patch thus generated should probably use the
nearestPatchFaceAMI sampling method.
\*---------------------------------------------------------------------------*/ \*---------------------------------------------------------------------------*/
#include "argList.H" #include "argList.H"
@ -115,6 +124,7 @@ Description
#include "mappedWallPolyPatch.H" #include "mappedWallPolyPatch.H"
#include "fvMeshTools.H" #include "fvMeshTools.H"
#include "processorMeshes.H" #include "processorMeshes.H"
#include "faceAreaWeightAMI2D.H"
using namespace Foam; using namespace Foam;
@ -296,12 +306,115 @@ void addToInterface
} }
labelList getMinBoundaryValue
(
const polyMesh& mesh,
const word& AMIMethod,
const labelList& matchPatchIDs,
const labelList& cellRegion
)
{
// Neighbour cellRegion.
labelList coupledRegion(mesh.nBoundaryFaces());
forAll(coupledRegion, i)
{
label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
coupledRegion[i] = cellRegion[celli];
}
syncTools::swapBoundaryFaceList(mesh, coupledRegion);
// Add approximate matches
forAll(matchPatchIDs, i)
{
const label patchi = matchPatchIDs[i];
const auto& pp = mesh.boundaryMesh()[patchi];
for (label j = i+1; j < matchPatchIDs.size(); ++j)
{
const label nbrPatchi = matchPatchIDs[j];
const auto& nbrPp = mesh.boundaryMesh()[nbrPatchi];
// Use AMI to try and find matches
auto AMPtr(AMIInterpolation::New(AMIMethod));
AMPtr->calculate(pp, nbrPp, nullptr);
if
(
gAverage(AMPtr->tgtWeightsSum()) > SMALL
|| gAverage(AMPtr->srcWeightsSum()) > SMALL
)
{
// Pull remote data local
labelList thisDecomp(pp.size(), labelMax);
AMPtr->interpolateToSource
(
labelList(cellRegion, nbrPp.faceCells()),
[]
(
label& res,
const label facei,
const label& fld,
const scalar& w
)
{
res = min(res, fld);
},
thisDecomp,
thisDecomp // used in case of low-weight-corr
);
// Put thisDecomp into coupledRegion. Check for unmatched faces.
forAll(thisDecomp, i)
{
if (thisDecomp[i] != labelMax)
{
coupledRegion[pp.offset()+i] = thisDecomp[i];
}
}
labelList nbrDecomp(nbrPp.size(), labelMax);
AMPtr->interpolateToTarget
(
labelList(cellRegion, pp.faceCells()), //thisDecomp,
[]
(
label& res,
const label facei,
const label& fld,
const scalar& w
)
{
res = min(res, fld);
},
nbrDecomp,
nbrDecomp // used in case of low-weight-corr
);
// Put nbrDecomp into coupledRegion. Check for unmatched faces/
forAll(nbrDecomp, i)
{
if (nbrDecomp[i] != labelMax)
{
coupledRegion[nbrPp.offset()+i] = nbrDecomp[i];
}
}
}
}
}
return coupledRegion;
}
// Get region-region interface name and sizes. // Get region-region interface name and sizes.
// Returns interfaces as straight list for looping in identical order. // Returns interfaces as straight list for looping in identical order.
void getInterfaceSizes void getInterfaceSizes
( (
const polyMesh& mesh, const polyMesh& mesh,
const bool useFaceZones, const bool useFaceZones,
const word& AMIMethod,
const labelList& matchPatchIDs,
const labelList& cellRegion, const labelList& cellRegion,
const wordList& regionNames, const wordList& regionNames,
@ -340,15 +453,16 @@ void getInterfaceSizes
// Boundary faces // Boundary faces
// ~~~~~~~~~~~~~~ // ~~~~~~~~~~~~~~
// Neighbour cellRegion. const labelList coupledRegion
labelList coupledRegion(mesh.nBoundaryFaces()); (
getMinBoundaryValue
forAll(coupledRegion, i) (
{ mesh,
label celli = mesh.faceOwner()[i+mesh.nInternalFaces()]; AMIMethod,
coupledRegion[i] = cellRegion[celli]; matchPatchIDs,
} cellRegion
syncTools::swapBoundaryFaceList(mesh, coupledRegion); )
);
forAll(coupledRegion, i) forAll(coupledRegion, i)
{ {
@ -540,6 +654,8 @@ autoPtr<mapPolyMesh> createRegionMesh
const labelList& cellRegion, const labelList& cellRegion,
const label regionI, const label regionI,
const word& regionName, const word& regionName,
const word& AMIMethod,
const labelList& matchPatchIDs,
// Interface info // Interface info
const labelList& interfacePatches, const labelList& interfacePatches,
const labelList& faceToInterface, const labelList& faceToInterface,
@ -551,15 +667,16 @@ autoPtr<mapPolyMesh> createRegionMesh
fvMeshTools::createDummyFvMeshFiles(mesh, regionName, true); fvMeshTools::createDummyFvMeshFiles(mesh, regionName, true);
// Neighbour cellRegion. // Neighbour cellRegion.
labelList coupledRegion(mesh.nBoundaryFaces()); const labelList coupledRegion
(
forAll(coupledRegion, i) getMinBoundaryValue
{ (
label celli = mesh.faceOwner()[i+mesh.nInternalFaces()]; mesh,
coupledRegion[i] = cellRegion[celli]; AMIMethod,
} matchPatchIDs,
syncTools::swapBoundaryFaceList(mesh, coupledRegion); cellRegion
)
);
// Topology change container. Start off from existing mesh. // Topology change container. Start off from existing mesh.
polyTopoChange meshMod(mesh); polyTopoChange meshMod(mesh);
@ -578,20 +695,16 @@ autoPtr<mapPolyMesh> createRegionMesh
labelList exposedPatchIDs(exposedFaces.size()); labelList exposedPatchIDs(exposedFaces.size());
forAll(exposedFaces, i) forAll(exposedFaces, i)
{ {
label facei = exposedFaces[i]; const label facei = exposedFaces[i];
label interfacei = faceToInterface[facei]; const label interfacei = faceToInterface[facei];
label ownRegion = cellRegion[mesh.faceOwner()[facei]]; const label ownRegion = cellRegion[mesh.faceOwner()[facei]];
label neiRegion = -1; const label neiRegion
(
if (mesh.isInternalFace(facei)) mesh.isInternalFace(facei)
{ ? cellRegion[mesh.faceNeighbour()[facei]]
neiRegion = cellRegion[mesh.faceNeighbour()[facei]]; : coupledRegion[facei-mesh.nInternalFaces()]
} );
else
{
neiRegion = coupledRegion[facei-mesh.nInternalFaces()];
}
// Check which side is being kept - determines which of the two // Check which side is being kept - determines which of the two
@ -638,6 +751,62 @@ autoPtr<mapPolyMesh> createRegionMesh
meshMod meshMod
); );
// Do re-patching on non-removed cells ourselves. These are not exposed
// faces but are boundary faces
for (label bFacei = 0; bFacei < mesh.nBoundaryFaces(); bFacei++)
{
const label facei = mesh.nInternalFaces()+bFacei;
if (!meshMod.faceRemoved(facei))
{
const label interfacei = faceToInterface[facei];
const label ownRegion = cellRegion[mesh.faceOwner()[facei]];
const label neiRegion = coupledRegion[bFacei];
label exposedPatchID = -1;
if (ownRegion == regionI)
{
if (regionI < neiRegion)
{
exposedPatchID = interfacePatches[interfacei];
}
else if (regionI > neiRegion)
{
exposedPatchID = interfacePatches[interfacei]+1;
}
}
else if (neiRegion == regionI)
{
if (regionI < ownRegion)
{
exposedPatchID = interfacePatches[interfacei];
}
else if (regionI > ownRegion)
{
exposedPatchID = interfacePatches[interfacei]+1;
}
}
if (exposedPatchID != -1)
{
// In-place modify the patch
DynamicList<label>& patchID =
const_cast<DynamicList<label>&>(meshMod.region());
//Pout<< "For face:" << facei
// << " on interface:" << interfacei
// << " modifying from own:" << meshMod.faceOwner()[facei]
// << " nei:" << meshMod.faceNeighbour()[facei]
// << " verts:" << meshMod.faces()[facei]
// << " patch " << patchID[facei]
// << " to " << exposedPatchID << endl;
patchID[facei] = exposedPatchID;
}
}
}
autoPtr<mapPolyMesh> map = meshMod.makeMesh autoPtr<mapPolyMesh> map = meshMod.makeMesh
( (
newMesh, newMesh,
@ -662,6 +831,8 @@ void createAndWriteRegion
const labelList& cellRegion, const labelList& cellRegion,
const wordList& regionNames, const wordList& regionNames,
const bool prefixRegion, const bool prefixRegion,
const word& AMIMethod,
const labelList& matchPatchIDs,
const labelList& faceToInterface, const labelList& faceToInterface,
const labelList& interfacePatches, const labelList& interfacePatches,
const label regionI, const label regionI,
@ -678,6 +849,8 @@ void createAndWriteRegion
cellRegion, cellRegion,
regionI, regionI,
regionNames[regionI], regionNames[regionI],
AMIMethod,
matchPatchIDs,
interfacePatches, interfacePatches,
faceToInterface, faceToInterface,
newMesh newMesh
@ -976,6 +1149,7 @@ labelList addRegionPatches
const wordList& regionNames, const wordList& regionNames,
const edgeList& interfaces, const edgeList& interfaces,
const List<Pair<word>>& interfaceNames const List<Pair<word>>& interfaceNames
//const List<mappedPatchBase::sampleMode>& interfaceModes
) )
{ {
Info<< nl << "Adding patches" << nl << endl; Info<< nl << "Adding patches" << nl << endl;
@ -999,7 +1173,7 @@ labelList addRegionPatches
0, // overridden 0, // overridden
0, // overridden 0, // overridden
regionNames[e[1]], // sampleRegion regionNames[e[1]], // sampleRegion
mappedPatchBase::NEARESTPATCHFACE, mappedPatchBase::NEARESTPATCHFACE, //interfaceModes[interI]
names[1], // samplePatch names[1], // samplePatch
point::zero, // offset point::zero, // offset
mesh.boundaryMesh() mesh.boundaryMesh()
@ -1021,7 +1195,7 @@ labelList addRegionPatches
0, 0,
0, 0,
regionNames[e[0]], // sampleRegion regionNames[e[0]], // sampleRegion
mappedPatchBase::NEARESTPATCHFACE, mappedPatchBase::NEARESTPATCHFACE, //interfaceModes[interI]
names[0], names[0],
point::zero, // offset point::zero, // offset
mesh.boundaryMesh() mesh.boundaryMesh()
@ -1411,6 +1585,7 @@ int main(int argc, char *argv[])
"Split mesh into multiple regions (detected by walking across faces)" "Split mesh into multiple regions (detected by walking across faces)"
); );
#include "addRegionOption.H" #include "addRegionOption.H"
#include "addOverwriteOption.H" #include "addOverwriteOption.H"
argList::addBoolOption argList::addBoolOption
( (
@ -1477,6 +1652,18 @@ int main(int argc, char *argv[])
"useFaceZones", "useFaceZones",
"Use faceZones to patch inter-region faces instead of single patch" "Use faceZones to patch inter-region faces instead of single patch"
); );
argList::addOption
(
"autoPatch",
"lists of patches",
"Find overlapping faces to auto-generate interface patches"
);
argList::addOption
(
"AMIMethod",
"word",
"type of AMI matching method"
);
argList::addBoolOption argList::addBoolOption
( (
"prefixRegion", "prefixRegion",
@ -1489,6 +1676,12 @@ int main(int argc, char *argv[])
#include "createTime.H" #include "createTime.H"
#include "createNamedMesh.H" #include "createNamedMesh.H"
// Note: could try to read multiple meshes and merge into one before
// operation but this would give problems with unique prefixes:
// - patches get renamed. So patchFields would need to be renamed.
// - what about e.g. 'samplePatch' in mapped patches?
const word oldInstance = mesh.pointsInstance(); const word oldInstance = mesh.pointsInstance();
word blockedFacesName; word blockedFacesName;
@ -1512,6 +1705,14 @@ int main(int argc, char *argv[])
const bool useFaceZones = args.found("useFaceZones"); const bool useFaceZones = args.found("useFaceZones");
const bool prefixRegion = args.found("prefixRegion"); const bool prefixRegion = args.found("prefixRegion");
labelList matchPatchIDs;
word AMIMethod(faceAreaWeightAMI2D::typeName);
if (args.found("autoPatch"))
{
const wordRes patchNames(args.getList<wordRe>("autoPatch"));
matchPatchIDs = mesh.boundaryMesh().indices(patchNames);
args.readIfPresent("AMIMethod", AMIMethod);
}
if if
( (
@ -1541,6 +1742,13 @@ int main(int argc, char *argv[])
} }
if (matchPatchIDs.size())
{
Info<< "Auto-detecting matching faces out of patches "
<< UIndirectList<word>(mesh.boundaryMesh().names(), matchPatchIDs)
<< nl << endl;
}
if (insidePoint && largestOnly) if (insidePoint && largestOnly)
{ {
@ -1608,8 +1816,8 @@ int main(int argc, char *argv[])
<< " This requires all" << " This requires all"
<< " cells to be in one and only one cellZone." << nl << endl; << " cells to be in one and only one cellZone." << nl << endl;
// Collect sets of zones into clusters. If no cluster is just an identity // Collect sets of zones into clusters. If no cluster is just an
// list (cluster 0 is cellZone 0 etc.) // identity list (cluster 0 is cellZone 0 etc.)
wordList clusterNames; wordList clusterNames;
labelListList clusterToZones; labelListList clusterToZones;
labelList zoneToCluster; labelList zoneToCluster;
@ -1924,6 +2132,8 @@ int main(int argc, char *argv[])
( (
mesh, mesh,
useFaceZones, useFaceZones,
AMIMethod,
matchPatchIDs,
cellRegion, cellRegion,
regionNames, regionNames,
@ -2103,6 +2313,7 @@ int main(int argc, char *argv[])
regionNames, regionNames,
interfaces, interfaces,
interfaceNames interfaceNames
//interfaceModes
) )
); );
@ -2155,6 +2366,8 @@ int main(int argc, char *argv[])
cellRegion, cellRegion,
regionNames, regionNames,
prefixRegion, prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface, faceToInterface,
interfacePatches, interfacePatches,
regionI, regionI,
@ -2176,6 +2389,8 @@ int main(int argc, char *argv[])
cellRegion, cellRegion,
regionNames, regionNames,
prefixRegion, prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface, faceToInterface,
interfacePatches, interfacePatches,
regionI, regionI,
@ -2197,6 +2412,8 @@ int main(int argc, char *argv[])
cellRegion, cellRegion,
regionNames, regionNames,
prefixRegion, prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface, faceToInterface,
interfacePatches, interfacePatches,
regionI, regionI,