Files
OpenFOAM-12/applications/utilities/mesh/manipulation/createNonConformalCouples/createNonConformalCouples.C
Will Bainbridge b1d6e64d02 createNonConformalCouples: Put non-conformal couple settings in a sub dictionary
Settings for the individual non-conformal couples can now be put in a
"nonConformalCouples" sub-dictionary of the
system/createNonConformalCouplesDict. For example:

    fields  no;

    nonConformalCouples // <-- new sub-dictionary
    {
        nonConformalCouple_none
        {
            patches         (nonCouple1 nonCouple2);
            transform       none;
        }

        nonConformalCouple_30deg
        {
            patches         (nonCoupleBehind nonCoupleAhead);
            transform       rotational;
            rotationAxis    (-1 0 0);
            rotationCentre  (0 0 0);
            rotationAngle   30;
        }
    }

This permits settings to be #include-d from files that themselves
contain sub-dictionaries without the utility treating those
sub-dictionaries as if they specify a non-conformal coupling. It also
makes the syntax more comparable to that of createBafflesDict.

The new "nonConformalCouples" sub-dictionary is optional, so this change
is backwards compatible. The new syntax is recommended, however, and all
examples have been changed accordingly.
2022-08-10 16:25:54 +01:00

430 lines
13 KiB
C++

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration | Website: https://openfoam.org
\\ / A nd | Copyright (C) 2011-2022 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
createNonConformalCouples
Description
Utility to create non-conformal couples between non-coupled patches.
Usage
\b createNonConformalCouples <patch1> <patch2>
Options:
- \par -overwrite \n
Replace the old mesh with the new one, rather than writing the new one
into a separate time directory
- \par -fields \n
Add non-conformal boundary conditions to the fields.
Note
If run with two arguments, these arguments specify the patches between
which a single couple is to be created. The resulting couple will not have
a transformation.
Usage
\b createNonConformalCouples
Note
If run without arguments then settings are read from a \b
system/createNonConformalCouplesDict dictionary (or from a different
dictionary specified by the \b -dict option). This dictionary can specify
the creation of multiple couples and/or couples with transformations.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "fvMeshStitchersStationary.H"
#include "fvMeshTools.H"
#include "IOobjectList.H"
#include "nonConformalCyclicPolyPatch.H"
#include "nonConformalErrorPolyPatch.H"
#include "nonConformalProcessorCyclicPolyPatch.H"
#include "polyMesh.H"
#include "processorPolyPatch.H"
#include "systemDict.H"
#include "Time.H"
#include "ReadFields.H"
#include "volFields.H"
#include "surfaceFields.H"
#include "pointFields.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void createNonConformalCouples
(
fvMesh& mesh,
const List<Pair<word>>& patchNames,
const wordList& cyclicNames,
const List<cyclicTransform>& transforms
)
{
const polyBoundaryMesh& patches = mesh.boundaryMesh();
List<polyPatch*> newPatches;
// Find the first processor patch and face
label firstProcPatchi = patches.size(), firstProcFacei = mesh.nFaces();
forAll(patches, patchi)
{
const polyPatch& pp = patches[patchi];
if (isA<processorPolyPatch>(pp) && firstProcPatchi == patches.size())
{
firstProcPatchi = patchi;
firstProcFacei = pp.start();
}
if (!isA<processorPolyPatch>(pp) && firstProcPatchi != patches.size())
{
FatalErrorInFunction
<< "Processor patches do not follow boundary patches"
<< exit(FatalError);
}
}
// Clone the non-processor patches
for (label patchi = 0; patchi < firstProcPatchi; ++ patchi)
{
const polyPatch& pp = patches[patchi];
newPatches.append
(
pp.clone(patches, patchi, pp.size(), pp.start()).ptr()
);
}
// Convenience function to generate patch names for the owner or neighbour
auto nccPatchNames = [&](const label i)
{
return
Pair<word>
(
cyclicNames[i] + "_on_" + patchNames[i][0],
cyclicNames[i] + "_on_" + patchNames[i][1]
);
};
// Add the cyclic patches
forAll(patchNames, i)
{
Info<< indent << "Adding "
<< nonConformalCyclicPolyPatch::typeName
<< " interfaces between patches: " << incrIndent << nl
<< indent << patchNames[i] << decrIndent << nl
<< indent << "Named:" << incrIndent << nl
<< indent << Pair<word>(nccPatchNames(i)) << decrIndent << nl
<< indent << "With transform: " << incrIndent << nl;
transforms[i].write(Info);
Info<< decrIndent << nl;
newPatches.append
(
new nonConformalCyclicPolyPatch
(
nccPatchNames(i)[0],
0,
firstProcFacei,
newPatches.size(),
patches,
nonConformalCyclicPolyPatch::typeName,
nccPatchNames(i)[1],
patchNames[i][0],
transforms[i]
)
);
newPatches.append
(
new nonConformalCyclicPolyPatch
(
nccPatchNames(i)[1],
0,
firstProcFacei,
newPatches.size(),
patches,
nonConformalCyclicPolyPatch::typeName,
nccPatchNames(i)[0],
patchNames[i][1],
inv(transforms[i])
)
);
}
// Add the error patches. Note there is only one for each source patch,
// regardless of how many interfaces are attached to that patch.
auto appendErrorPatches = [&](const bool owner)
{
wordHashSet patchANames;
forAll(patchNames, i)
{
patchANames.insert(patchNames[i][!owner]);
}
forAllConstIter(wordHashSet, patchANames, iter)
{
newPatches.append
(
new nonConformalErrorPolyPatch
(
nonConformalErrorPolyPatch::typeName + "_on_" + iter.key(),
0,
firstProcFacei,
newPatches.size(),
patches,
nonConformalErrorPolyPatch::typeName,
iter.key()
)
);
}
};
appendErrorPatches(true);
appendErrorPatches(false);
// Clone the processor patches
for (label patchi = firstProcPatchi; patchi < patches.size(); ++ patchi)
{
const polyPatch& pp = patches[patchi];
newPatches.append
(
pp.clone(patches, newPatches.size(), pp.size(), pp.start()).ptr()
);
}
// Add the processor cyclic patches
if (Pstream::parRun())
{
forAll(patchNames, i)
{
const polyPatch& patch1 = patches[patchNames[i][0]];
const polyPatch& patch2 = patches[patchNames[i][1]];
boolList procHasPatch1(Pstream::nProcs(), false);
procHasPatch1[Pstream::myProcNo()] = !patch1.empty();
Pstream::gatherList(procHasPatch1);
Pstream::scatterList(procHasPatch1);
boolList procHasPatch2(Pstream::nProcs(), false);
procHasPatch2[Pstream::myProcNo()] = !patch2.empty();
Pstream::gatherList(procHasPatch2);
Pstream::scatterList(procHasPatch2);
// Multiple cyclic interfaces must be ordered in a specific way for
// processor communication to function correctly.
//
// A communication that is sent from the cyclic owner is received
// on the cyclic neighbour and vice versa. Therefore, in a coupled
// pair of processors if one sends the owner first the other must
// receive the neighbour first.
//
// We ensure the above by ordering the patches so that for the
// lower indexed processor the owner interface comes first, and for
// the higher indexed processor the neighbour comes first.
auto appendProcPatches = [&](const bool owner, const bool first)
{
const boolList& procHasPatchA =
owner ? procHasPatch1 : procHasPatch2;
const boolList& procHasPatchB =
owner ? procHasPatch2 : procHasPatch1;
if (procHasPatchA[Pstream::myProcNo()])
{
forAll(procHasPatchB, proci)
{
if
(
(
(first && proci < Pstream::myProcNo())
|| (!first && proci > Pstream::myProcNo())
)
&& procHasPatchB[proci]
)
{
newPatches.append
(
new nonConformalProcessorCyclicPolyPatch
(
0,
mesh.nFaces(),
newPatches.size(),
patches,
Pstream::myProcNo(),
proci,
nccPatchNames(i)[!owner],
patchNames[i][!owner]
)
);
}
}
}
};
appendProcPatches(true, true);
appendProcPatches(false, true);
appendProcPatches(false, false);
appendProcPatches(true, false);
}
}
// Re-patch the mesh. Note that new patches are all constraints, so the
// dictionary and patch type do not get used.
forAll(newPatches, newPatchi)
{
fvMeshTools::addPatch
(
mesh,
*newPatches[newPatchi],
dictionary(),
calculatedFvPatchField<scalar>::typeName,
false
);
}
}
int main(int argc, char *argv[])
{
#include "addOverwriteOption.H"
#include "addRegionOption.H"
#include "addDictOption.H"
const bool haveArgs = argList::hasArgs(argc, argv);
if (haveArgs)
{
argList::validArgs.append("patch1");
argList::validArgs.append("patch2");
argList::addBoolOption
(
"fields",
"add non-conformal boundary conditions to the fields"
);
}
#include "setRootCase.H"
#include "createTime.H"
runTime.functionObjects().off();
// Flag to determine whether or not patches are added to fields
bool fields;
// Patch names between which to create couples, the associated cyclic name
// prefix and transformation (if any)
List<Pair<word>> patchNames;
wordList cyclicNames;
List<cyclicTransform> transforms;
// If there are patch name arguments, then we assume fields are not being
// changed, the cyclic name is just the cyclic typename, and that there is
// no transformation. If there are no arguments then get all this
// information from the system dictionary.
if (haveArgs)
{
fields = args.optionFound("fields");
patchNames.append(Pair<word>(args[1], args[2]));
cyclicNames.append(nonConformalCyclicPolyPatch::typeName);
transforms.append(cyclicTransform(true));
}
else
{
static const word dictName("createNonConformalCouplesDict");
IOdictionary dict(systemDict(dictName, args, runTime));
fields = dict.lookupOrDefault<bool>("fields", false);
const dictionary& couplesDict =
dict.optionalSubDict("nonConformalCouples");
forAllConstIter(dictionary, couplesDict, iter)
{
if (!iter().isDict()) continue;
patchNames.append(iter().dict().lookup<Pair<word>>("patches"));
cyclicNames.append(iter().dict().dictName());
transforms.append(cyclicTransform(iter().dict(), true));
}
}
Foam::word meshRegionName = polyMesh::defaultRegion;
args.optionReadIfPresent("region", meshRegionName);
const bool overwrite = args.optionFound("overwrite");
#include "createNamedMesh.H"
// Read the fields
IOobjectList objects(mesh, runTime.timeName());
if (fields) Info<< "Reading geometric fields" << nl << endl;
#include "readVolFields.H"
#include "readSurfaceFields.H"
#include "readPointFields.H"
if (fields) Info<< endl;
const word oldInstance = mesh.pointsInstance();
// Make sure the mesh is not connected before couples are added
fvMeshStitchers::stationary stitcher(mesh);
stitcher.disconnect(false, false);
createNonConformalCouples
(
mesh,
patchNames,
cyclicNames,
transforms
);
// Connect the mesh so that the new stitching topology gets written out
stitcher.connect(false, false, false);
mesh.setInstance(runTime.timeName());
// Set the precision of the points data to 10
IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
if (!overwrite)
{
runTime++;
}
else
{
mesh.setInstance(oldInstance);
}
// Write resulting mesh
Info<< "Writing mesh to " << runTime.timeName() << nl << endl;
mesh.write();
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //