Files
openfoam/src/finiteArea/faMesh/faMeshTools/faMeshTools.C
Mark Olesen a597c044c7 BUG: inconsistent faceArea on processor boundaries (fixes #2683)
- was missing evaluateCoupled on the initial faceAreaNormals field
  (related to #2507)

ENH: simplify/consistent geometry updating
2023-02-15 17:22:12 +01:00

471 lines
12 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2012-2016 OpenFOAM Foundation
Copyright (C) 2015-2023 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 <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "faMeshTools.H"
#include "faBoundaryMeshEntries.H"
#include "areaFields.H"
#include "edgeFields.H"
#include "polyMesh.H"
#include "processorFaPatch.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
void Foam::faMeshTools::unregisterMesh(const faMesh& mesh)
{
auto& obr = const_cast<objectRegistry&>(mesh.thisDb());
// Checkout by name (casting ambiguity)
obr.checkOut(faMesh::typeName);
obr.checkOut("faBoundaryMesh");
obr.checkOut("faSchemes");
obr.checkOut("faSolution");
}
void Foam::faMeshTools::forceDemandDriven(faMesh& mesh)
{
(void)mesh.globalData();
(void)mesh.Le();
(void)mesh.magLe();
(void)mesh.areaCentres();
(void)mesh.edgeCentres();
(void)mesh.faceAreaNormals();
(void)mesh.edgeAreaNormals();
(void)mesh.pointAreaNormals();
(void)mesh.faceCurvatures();
(void)mesh.edgeTransformTensors();
mesh.syncGeom();
}
Foam::autoPtr<Foam::faMesh> Foam::faMeshTools::newMesh
(
const IOobject& io,
const polyMesh& pMesh,
const bool masterOnlyReading,
const bool verbose
)
{
// Region name
// ~~~~~~~~~~~
const fileName meshSubDir
(
pMesh.regionName() / faMesh::meshSubDir
);
fileName facesInstance;
// Patch types
// ~~~~~~~~~~~
// Read and scatter master patches (without reading master mesh!)
PtrList<entry> patchEntries;
if (Pstream::master())
{
const bool oldParRun = Pstream::parRun(false);
facesInstance = io.time().findInstance
(
meshSubDir,
"faceLabels",
IOobject::MUST_READ
);
patchEntries = faBoundaryMeshEntries
(
IOobject
(
"faBoundary",
facesInstance,
meshSubDir,
io.db(),
IOobject::MUST_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
)
);
Pstream::parRun(oldParRun);
}
// Broadcast information to all
Pstream::broadcasts
(
UPstream::worldComm,
patchEntries,
facesInstance
);
// Dummy meshes
// ~~~~~~~~~~~~
// Set up to read-if-present. Note: does not search for mesh so set
// instance explicitly
IOobject meshIO(io);
meshIO.instance() = facesInstance;
meshIO.readOpt(IOobject::READ_IF_PRESENT);
// For mesh components (faceLabels, ...)
IOobject cmptIO(meshIO, "faceLabels", meshSubDir);
cmptIO.readOpt(IOobject::MUST_READ);
cmptIO.writeOpt(IOobject::NO_WRITE);
cmptIO.registerObject(false);
// Check who has a mesh
const fileName meshDir = io.time().path()/facesInstance/meshSubDir;
bool haveMesh = isDir(meshDir);
if (masterOnlyReading && !Pstream::master())
{
haveMesh = false;
meshIO.readOpt(IOobject::NO_READ);
}
if (!haveMesh)
{
cmptIO.readOpt(IOobject::NO_READ);
}
// Read mesh
// ~~~~~~~~~
// Now all processors use supplied points,faces etc
// Note: solution, schemes are also using the supplied IOobject so
// on slave will be NO_READ, on master READ_IF_PRESENT. This will
// conflict with e.g. timeStampMaster reading so switch off.
const auto oldCheckType = IOobject::fileModificationChecking;
IOobject::fileModificationChecking = IOobject::timeStamp;
// faceLabels
cmptIO.rename("faceLabels");
labelIOList faceLabels(cmptIO);
auto meshPtr = autoPtr<faMesh>::New
(
pMesh,
std::move(faceLabels),
meshIO
);
auto& mesh = *meshPtr;
IOobject::fileModificationChecking = oldCheckType;
// Some processors without patches? - add patches
if (returnReduceOr(mesh.boundary().empty()))
{
// Use patchEntries, which were read on master and broadcast
faPatchList patches(patchEntries.size());
label nPatches = 0;
const bool isEmptyMesh = (mesh.faceLabels().empty());
forAll(patchEntries, patchi)
{
const entry& e = patchEntries[patchi];
const word type(e.dict().get<word>("type"));
const word& name = e.keyword();
if
(
type == processorFaPatch::typeName
)
{
// Stop at the first processor patch.
// - logic will not work with inter-mixed proc-patches anyhow
break;
}
else
{
dictionary patchDict(e.dict());
if (isEmptyMesh)
{
patchDict.set("edgeLabels", labelList());
}
patches.set
(
patchi,
faPatch::New
(
name,
patchDict,
nPatches++,
mesh.boundary()
)
);
}
}
patches.resize(nPatches);
mesh.addFaPatches(patches, false); // No parallel comms
}
// Recreate basic geometry, globalMeshData etc.
mesh.init(false);
(void)mesh.globalData();
return meshPtr;
}
Foam::autoPtr<Foam::faMesh> Foam::faMeshTools::loadOrCreateMesh
(
const IOobject& io,
const polyMesh& pMesh,
const bool decompose,
const bool verbose
)
{
// Region name
// ~~~~~~~~~~~
const fileName meshSubDir
(
pMesh.regionName() / faMesh::meshSubDir
);
// Patch types
// ~~~~~~~~~~~
// Read and scatter master patches (without reading master mesh!)
PtrList<entry> patchEntries;
if (Pstream::master())
{
const bool oldParRun = Pstream::parRun(false);
patchEntries = faBoundaryMeshEntries
(
IOobject
(
"faBoundary",
io.instance(),
meshSubDir,
io.db(),
IOobject::MUST_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
)
);
Pstream::parRun(oldParRun);
}
// Broadcast: send patches to all
Pstream::broadcast(patchEntries); // == worldComm;
/// Info<< patchEntries << nl;
// Dummy meshes
// ~~~~~~~~~~~~
// Check who has or needs a mesh.
// For 'decompose', only need mesh on master.
// Otherwise check for presence of the "faceLabels" file
bool haveMesh =
(
decompose
? Pstream::master()
: fileHandler().isFile
(
fileHandler().filePath
(
io.time().path()/io.instance()/meshSubDir/"faceLabels"
)
)
);
if (!haveMesh)
{
const bool oldParRun = Pstream::parRun(false);
// Create dummy mesh - on procs that don't already have a mesh
faMesh dummyMesh
(
pMesh,
labelList(),
IOobject(io, IOobject::NO_READ, IOobject::AUTO_WRITE)
);
// Add patches
faPatchList patches(patchEntries.size());
label nPatches = 0;
forAll(patchEntries, patchi)
{
const entry& e = patchEntries[patchi];
const word type(e.dict().get<word>("type"));
const word& name = e.keyword();
if
(
type == processorFaPatch::typeName
)
{
// Stop at the first processor patch.
// - logic will not work with inter-mixed proc-patches anyhow
break;
}
else
{
dictionary patchDict(e.dict());
patchDict.set("edgeLabels", labelList());
patches.set
(
patchi,
faPatch::New
(
name,
patchDict,
nPatches++,
dummyMesh.boundary()
)
);
}
}
patches.resize(nPatches);
dummyMesh.addFaPatches(patches, false); // No parallel comms
// Bad hack, but the underlying polyMesh is NO_WRITE
// so it does not create the faMesh subDir for us...
Foam::mkDir(dummyMesh.boundary().path());
//Pout<< "Writing dummy mesh to " << dummyMesh.boundary().path()
// << endl;
dummyMesh.write();
Pstream::parRun(oldParRun); // Restore parallel state
}
// Read mesh
// ~~~~~~~~~
// Now all processors have a (possibly zero size) mesh so read in
// parallel
/// Pout<< "Reading area mesh from " << io.objectRelPath() << endl;
auto meshPtr = autoPtr<faMesh>::New(pMesh, false);
faMesh& mesh = *meshPtr;
// Sync patches
// ~~~~~~~~~~~~
if (!Pstream::master() && haveMesh)
{
// Check master names against mine
const faBoundaryMesh& patches = mesh.boundary();
forAll(patchEntries, patchi)
{
const entry& e = patchEntries[patchi];
const word type(e.dict().get<word>("type"));
const word& name = e.keyword();
if
(
type == processorFaPatch::typeName
)
{
break;
}
if (patchi >= patches.size())
{
FatalErrorInFunction
<< "Non-processor patches not synchronised."
<< endl
<< "Processor " << Pstream::myProcNo()
<< " has only " << patches.size()
<< " patches, master has "
<< patchi
<< exit(FatalError);
}
if
(
type != patches[patchi].type()
|| name != patches[patchi].name()
)
{
FatalErrorInFunction
<< "Non-processor patches not synchronised."
<< endl
<< "Master patch " << patchi
<< " name:" << type
<< " type:" << type << endl
<< "Processor " << Pstream::myProcNo()
<< " patch " << patchi
<< " has name:" << patches[patchi].name()
<< " type:" << patches[patchi].type()
<< exit(FatalError);
}
}
}
// Recreate basic geometry, globalMeshData etc.
mesh.init(false);
(void)mesh.globalData();
/// #if 0
/// faMeshTools::forceDemandDriven(mesh);
/// faMeshTools::unregisterMesh(mesh);
/// #endif
// Do some checks.
// Check if the boundary definition is unique
// and processor patches are correct
mesh.boundary().checkDefinition(verbose);
mesh.boundary().checkParallelSync(verbose);
return meshPtr;
}
// ************************************************************************* //