Compare commits

...

5 Commits

4 changed files with 355 additions and 47 deletions

View File

@ -1157,6 +1157,34 @@ Foam::label Foam::checkGeometry
globalFaces,
globalPoints
);
if (cpp.sameWorld())
{
//- Get the patch on the region
const polyPatch& nbrPp = cpp.samplePolyPatch();
// Collect neighbour geometry
faceList mergedFaces;
pointField mergedPoints;
autoPtr<globalIndex> globalFaces;
autoPtr<globalIndex> globalPoints;
collectAndWriteAMIWeights
(
cpp.sampleMesh(),
wr,
outputDir / pName + "-tgt_" + tmName,
ami.tgtWeightsSum(),
nbrPp.localFaces(),
nbrPp.meshPoints(),
nbrPp.meshPointMap(),
mergedFaces,
mergedPoints,
globalFaces,
globalPoints
);
}
}
}
}

View File

@ -43,8 +43,9 @@ Description
- volScalarField with regions as different scalars (-detectOnly)
or
- mesh with multiple regions and mapped patches. These patches
either cover the whole interface between two region (default) or
only part according to faceZones (-useFaceZones)
either cover the whole interface between two region (default),
only part according to faceZones (-useFaceZones) or be auto-generated
according to an AMI method (see below)
or
- mesh with cells put into cellZones (-makeCellZones)
@ -57,7 +58,7 @@ Description
-combineZones '((zoneA "zoneB.*")(none otherZone))
This can be combined with e.g. 'cellZones' or 'cellZonesOnly'. The
addZones option supplies the destination region name as first element in
the list. The combineZones option synthesises the region name e.g.
the list. The combineZones option synthesises the region name e.g.
zoneA_zoneB0_zoneB1
- cellZonesOnly does not do a walk and uses the cellZones only. Use
@ -98,6 +99,14 @@ Description
- boundaryRegionAddressing : for every patch in this region the
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"
@ -116,6 +125,7 @@ Description
#include "fvMeshTools.H"
#include "zeroGradientFvPatchFields.H"
#include "processorMeshes.H"
#include "faceAreaWeightAMI2D.H"
using namespace Foam;
@ -317,12 +327,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.
// Returns interfaces as straight list for looping in identical order.
void getInterfaceSizes
(
const polyMesh& mesh,
const bool useFaceZones,
const word& AMIMethod,
const labelList& matchPatchIDs,
const labelList& cellRegion,
const wordList& regionNames,
@ -361,15 +474,16 @@ void getInterfaceSizes
// Boundary faces
// ~~~~~~~~~~~~~~
// Neighbour cellRegion.
labelList coupledRegion(mesh.nBoundaryFaces());
forAll(coupledRegion, i)
{
label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
coupledRegion[i] = cellRegion[celli];
}
syncTools::swapBoundaryFaceList(mesh, coupledRegion);
const labelList coupledRegion
(
getMinBoundaryValue
(
mesh,
AMIMethod,
matchPatchIDs,
cellRegion
)
);
forAll(coupledRegion, i)
{
@ -583,6 +697,8 @@ autoPtr<mapPolyMesh> createRegionMesh
const labelList& cellRegion,
const label regionI,
const word& regionName,
const word& AMIMethod,
const labelList& matchPatchIDs,
// Interface info
const labelList& interfacePatches,
const labelList& faceToInterface,
@ -594,15 +710,16 @@ autoPtr<mapPolyMesh> createRegionMesh
fvMeshTools::createDummyFvMeshFiles(mesh, regionName, true);
// Neighbour cellRegion.
labelList coupledRegion(mesh.nBoundaryFaces());
forAll(coupledRegion, i)
{
label celli = mesh.faceOwner()[i+mesh.nInternalFaces()];
coupledRegion[i] = cellRegion[celli];
}
syncTools::swapBoundaryFaceList(mesh, coupledRegion);
const labelList coupledRegion
(
getMinBoundaryValue
(
mesh,
AMIMethod,
matchPatchIDs,
cellRegion
)
);
// Topology change container. Start off from existing mesh.
polyTopoChange meshMod(mesh);
@ -621,20 +738,16 @@ autoPtr<mapPolyMesh> createRegionMesh
labelList exposedPatchIDs(exposedFaces.size());
forAll(exposedFaces, i)
{
label facei = exposedFaces[i];
label interfacei = faceToInterface[facei];
const label facei = exposedFaces[i];
const label interfacei = faceToInterface[facei];
label ownRegion = cellRegion[mesh.faceOwner()[facei]];
label neiRegion = -1;
if (mesh.isInternalFace(facei))
{
neiRegion = cellRegion[mesh.faceNeighbour()[facei]];
}
else
{
neiRegion = coupledRegion[facei-mesh.nInternalFaces()];
}
const label ownRegion = cellRegion[mesh.faceOwner()[facei]];
const label neiRegion
(
mesh.isInternalFace(facei)
? cellRegion[mesh.faceNeighbour()[facei]]
: coupledRegion[facei-mesh.nInternalFaces()]
);
// Check which side is being kept - determines which of the two
@ -681,6 +794,62 @@ autoPtr<mapPolyMesh> createRegionMesh
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
(
newMesh,
@ -705,6 +874,8 @@ void createAndWriteRegion
const labelList& cellRegion,
const wordList& regionNames,
const bool prefixRegion,
const word& AMIMethod,
const labelList& matchPatchIDs,
const labelList& faceToInterface,
const labelList& interfacePatches,
const label regionI,
@ -721,6 +892,8 @@ void createAndWriteRegion
cellRegion,
regionI,
regionNames[regionI],
AMIMethod,
matchPatchIDs,
interfacePatches,
faceToInterface,
newMesh
@ -1019,6 +1192,7 @@ labelList addRegionPatches
const wordList& regionNames,
const edgeList& interfaces,
const List<Pair<word>>& interfaceNames
//const List<mappedPatchBase::sampleMode>& interfaceModes
)
{
Info<< nl << "Adding patches" << nl << endl;
@ -1042,7 +1216,7 @@ labelList addRegionPatches
0, // overridden
0, // overridden
regionNames[e[1]], // sampleRegion
mappedPatchBase::NEARESTPATCHFACE,
mappedPatchBase::NEARESTPATCHFACE, //interfaceModes[interI]
names[1], // samplePatch
point::zero, // offset
mesh.boundaryMesh()
@ -1064,7 +1238,7 @@ labelList addRegionPatches
0,
0,
regionNames[e[0]], // sampleRegion
mappedPatchBase::NEARESTPATCHFACE,
mappedPatchBase::NEARESTPATCHFACE, //interfaceModes[interI]
names[0],
point::zero, // offset
mesh.boundaryMesh()
@ -1455,6 +1629,7 @@ int main(int argc, char *argv[])
"Split mesh into multiple regions (detected by walking across faces)"
);
#include "addRegionOption.H"
#include "addOverwriteOption.H"
argList::addBoolOption
(
@ -1521,6 +1696,18 @@ int main(int argc, char *argv[])
"useFaceZones",
"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
(
"prefixRegion",
@ -1533,6 +1720,12 @@ int main(int argc, char *argv[])
#include "createTime.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();
word blockedFacesName;
@ -1556,6 +1749,14 @@ int main(int argc, char *argv[])
const bool useFaceZones = args.found("useFaceZones");
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
(
@ -1585,6 +1786,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)
{
@ -1652,8 +1860,8 @@ int main(int argc, char *argv[])
<< " This requires all"
<< " cells to be in one and only one cellZone." << nl << endl;
// Collect sets of zones into clusters. If no cluster is just an identity
// list (cluster 0 is cellZone 0 etc.)
// Collect sets of zones into clusters. If no cluster is just an
// identity list (cluster 0 is cellZone 0 etc.)
wordList clusterNames;
labelListList clusterToZones;
labelList zoneToCluster;
@ -1829,7 +2037,7 @@ int main(int argc, char *argv[])
{
label ownCluster = clusterID[mesh.faceOwner()[facei]];
label neiCluster = clusterID[mesh.faceNeighbour()[facei]];
if (ownCluster != neiCluster)
{
blockedFace[facei] = true;
@ -1968,6 +2176,8 @@ int main(int argc, char *argv[])
(
mesh,
useFaceZones,
AMIMethod,
matchPatchIDs,
cellRegion,
regionNames,
@ -2147,6 +2357,7 @@ int main(int argc, char *argv[])
regionNames,
interfaces,
interfaceNames
//interfaceModes
)
);
@ -2199,6 +2410,8 @@ int main(int argc, char *argv[])
cellRegion,
regionNames,
prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface,
interfacePatches,
regionI,
@ -2220,6 +2433,8 @@ int main(int argc, char *argv[])
cellRegion,
regionNames,
prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface,
interfacePatches,
regionI,
@ -2241,6 +2456,8 @@ int main(int argc, char *argv[])
cellRegion,
regionNames,
prefixRegion,
AMIMethod,
matchPatchIDs,
faceToInterface,
interfacePatches,
regionI,

View File

@ -47,6 +47,9 @@ pointSync false;
// Patches to create.
patches
(
// Example of creating cyclic patch pair
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
// Name of new patch
name cyc_half0;
@ -70,10 +73,17 @@ patches
//matchTolerance 1E-2;
}
// How to construct: either from 'patches' or 'set'
// How to select the faces:
// - set : specify faceSet in 'set'
// - patches : specify names in 'patches'
// - autoPatch : attempts automatic patching of the specified
// candidates in 'patches'.
// - single region : match in the region itself
// - multi regions : match in between regions only
constructFrom patches;
// If constructFrom = patches : names of patches. Wildcards allowed.
// If constructFrom = patches or autoPatch: names of patches.
// Wildcards&patchGroups allowed.
patches (periodic1);
// If constructFrom = set : name of faceSet
@ -98,15 +108,66 @@ patches
// separationVector (1 0 0);
}
// How to construct: either from 'patches' or 'set'
// How to select the faces:
// - set : specify faceSet in 'set'
// - patches : specify names in 'patches'
// - autoPatch : attempts automatic patching of the specified
// candidates in 'patches'.
// - single region : match in the region itself
// - multi regions : match in between regions only
constructFrom patches;
// If constructFrom = patches : names of patches. Wildcards allowed.
// If constructFrom = patches or autoPatch: names of patches.
// Wildcards&patchGroups allowed.
patches (periodic2);
// If constructFrom = set : name of faceSet
set f0;
}
// Example of creating mapped patches using geometric matching
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
// Name of new patch
name solid;
// Dictionary to construct new patch from
patchInfo
{
type mappedPatch;
sampleMode nearestPatchFaceAMI;
AMIMethod faceAreaWeightAMI;
// Overwritten
//sampleRegion otherRegion;
//samplePatch otherPatch;
//- Optional override of added patchfields. If not specified
// any added patchfields are of type calculated.
patchFields
{
T
{
type fixedValue;
value uniform 300;
}
}
}
// How to select the faces:
// - set : specify faceSet in 'set'
// - patches : specify names in 'patches'
// - autoPatch : attempts automatic patching of the specified
// candidates in 'patches'.
// - single region : match in the region itself
// - multi regions : match in between regions only
constructFrom autoPatch;
// If constructFrom = patches or autoPatch: names of patches.
// Wildcards&patchGroups allowed.
patches (coupling_group);
}
);
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2021 OpenCFD Ltd.
Copyright (C) 2015-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -1354,7 +1354,9 @@ void Foam::argList::parse
// Disable any parallel comms happening inside the fileHandler
// since we are on master. This can happen e.g. inside
// the masterUncollated/collated handler.
// the masterUncollated/collated handler. Note that we
// also have to protect the actual dictionary parsing since
// it might trigger file access (e.g. #include, #codeStream)
const bool oldParRun = Pstream::parRun(false);
autoPtr<ISstream> dictStream
@ -1362,8 +1364,6 @@ void Foam::argList::parse
fileHandler().NewIFstream(source)
);
Pstream::parRun(oldParRun); // Restore parallel state
if (dictStream && dictStream->good())
{
dictionary decompDict(*dictStream);
@ -1412,6 +1412,8 @@ void Foam::argList::parse
}
}
Pstream::parRun(oldParRun); // Restore parallel state
if (Pstream::nProcs() == 1)
{
Warning