Compare commits

..

10 Commits

Author SHA1 Message Date
7207e9e3a9 TUT: add a multi-region finite-area example tutorial 2025-10-12 17:00:17 +02:00
6d2deda470 ENH: revise some regionFaModel parameter naming (#3419)
- the old naming grew organically and was previously restricted
  by avoiding name collisions with volume fields, which resulted
  in some minor consistencies

Common solid shell naming:

  | Keyword  | Default     | Description  | Key(old) | Old Naming     |
  | -------- | ----------- | ------------ | -------- | -------------- |
  | h        | hs (suffix) | thickness    | h        | "h_" + region  |
  | T        | Ts (suffix) | temperature  | -        | "Ts_" + region |

Vibration shells:

  | Keyword  | Default     | Description  | Key(old) | Old Naming     |
  | -------- | ----------- | ------------ | -------- | -------------- |
  |  as      | as (suffix) | acceleration | -        | "as_" + region |
  |  ps      | ps (suffix) | pres on shell| -        | "ps_" + region |
  |  ws      | ws (suffix) | displacement | -        | "ws_" + region |
  |  qs      | qs (suffix  | source field | -        | "qs_" + region |

Other:

  | Keyword  | Default     | Description  | Key(old) |
  | -------- | ----------- | ------------ | -------- |
  | Tprimary | T   | temperature (volume) | T        |

Joule heating: (possibly breaking naming changes)

  | Field    | Name                     | Name(old)                |
  | -------- | ------------------------ | ------------------------ |
  |  V       | \<scope\>:V (suffix)     | \<scope\>:V + region     |
  |  sigma   | \<scope\>:sigma (suffix) | \<scope\>:sigma + region |
2025-10-12 15:54:05 +02:00
c172b588c0 ENH: multi-region finiteArea support [parallel] (#3419)
- redistributePar (partly working)
2025-10-12 15:54:05 +02:00
c075af4f7b ENH: redistributePar cleanup
- remove old dead code (without file-handlers)
- update coding style
2025-10-12 15:54:05 +02:00
a1e5dcee43 ENH: redistributePar cleanup
- update and extend loadOrCreateMesh coding
- support bitSet return values
- accept checks with name/instance/local in IOobject parameter order
2025-10-12 15:54:05 +02:00
f2922f1ae9 ENH: multi-region finiteArea support [parallel] (#3419)
- decomposePar, reconstructPar, reconstructParMesh
2025-10-12 15:54:05 +02:00
1ae48880d2 ENH: multi-region finiteArea support [post-processing] (#3419)
- foamToEnsight, foamToVTK
2025-10-12 15:54:05 +02:00
9fc52d42eb ENH: multi-region finiteArea support [pre-processing] (#3419)
- checkFaMesh, makeFaMesh, setFields
2025-10-12 15:54:05 +02:00
c669f7b736 ENH: provide general static singleton for the faMeshesRegistry objects 2025-10-12 15:54:05 +02:00
27878797e2 ENH: add support for name suffixing in faOption and regionFaModel (#3419)
- in earlier versions, when area fields and volume fields were stored
  together, an automatic suffix of '_' + regionName was appended to
  many of the finite-area fields. This was needed to prevent name
  clashes between 'T' (area) and 'T' (volume) field names.

  This is no longer necessary, but some existing setups may rely on
  the old naming convention. To make this more flexible while also
  minimizing the number of changes, introduced a "suffixing"
  keyword for faOption and regionFaModel.

  Can specify suffixing as "default" to recover the (_ + region)
  naming scheme (can also currently also use true/false/none here).
  Or specify a suffix (eg, _shell) unrelated to any region name.

ENH: improve the code style and alignment in jouleHeatingSource

- make {fa,fv} versions more consistent, for better maintenance
2025-10-12 15:53:29 +02:00
127 changed files with 4634 additions and 3515 deletions

View File

@ -49,8 +49,7 @@ int main(int argc, char *argv[])
Info<< "mesh 0: " << mesh.sortedNames() << nl;
faMeshesRegistry& reg =
const_cast<faMeshesRegistry&>(faMeshesRegistry::New(mesh));
auto& reg = const_cast<faMeshesRegistry&>(faMeshesRegistry::New(mesh));
// faMeshesRegistry faReg = faMeshesRegistry(mesh);

View File

@ -1,3 +1,3 @@
foamToEnsight-check.C
foamToEnsight-check.cxx
EXE = $(FOAM_USER_APPBIN)/foamToEnsight-check

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2022-2023 OpenCFD Ltd.
Copyright (C) 2022-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -203,6 +203,7 @@ int main(int argc, char *argv[])
argList::addVerboseOption();
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
argList::addBoolOption
(
@ -251,6 +252,14 @@ int main(int argc, char *argv[])
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
// ------------------------------------------------------------------------
#include "createNamedMeshes.H"
@ -259,8 +268,8 @@ int main(int argc, char *argv[])
/// #include "createMeshAccounting.H"
PtrList<ensightMesh> ensightMeshes(regionNames.size());
PtrList<faMesh> meshesFa(regionNames.size());
PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
List<PtrList<faMesh>> meshesFa(regionNames.size());
List<PtrList<ensightFaMesh>> ensightMeshesFa(regionNames.size());
forAll(regionNames, regioni)
{
@ -273,21 +282,32 @@ int main(int argc, char *argv[])
);
ensightMeshes[regioni].verbose(optVerbose);
if (doFiniteArea)
if (!doFiniteArea)
{
autoPtr<faMesh> faMeshPtr(faMesh::TryNew(mesh));
continue;
}
meshesFa[regioni].resize_null(areaRegionNames.size());
ensightMeshesFa[regioni].resize_null(areaRegionNames.size());
autoPtr<faMesh> faMeshPtr(faMesh::TryNew(mesh));
forAll(areaRegionNames, areai)
{
const word& areaName = areaRegionNames[areai];
autoPtr<faMesh> faMeshPtr(faMesh::TryNew(areaName, mesh));
if (faMeshPtr)
{
meshesFa.set(regioni, std::move(faMeshPtr));
meshesFa[regioni].set(areai, std::move(faMeshPtr));
ensightMeshesFa.set
ensightMeshesFa[regioni].set
(
regioni,
new ensightFaMesh(meshesFa[regioni])
areai,
new ensightFaMesh(meshesFa[regioni][areai])
);
ensightMeshesFa[regioni].verbose(optVerbose);
ensightMeshesFa[regioni][areai].verbose(optVerbose);
}
}
}
@ -295,7 +315,7 @@ int main(int argc, char *argv[])
// ------------------------------------------------------------------------
if (Pstream::master())
if (UPstream::master())
{
Info<< "Checking " << timeDirs.size() << " time steps" << nl;
}
@ -317,17 +337,20 @@ int main(int argc, char *argv[])
auto& ensMesh = ensightMeshes[regioni];
// Finite-area (can be missing)
auto* ensFaMeshPtr = ensightMeshesFa.get(regioni);
auto& ensFaMeshes = ensightMeshesFa[regioni];
if (moving)
{
ensMesh.expire();
ensMesh.correct();
if (ensFaMeshPtr)
forAll(areaRegionNames, areai)
{
ensFaMeshPtr->expire();
ensFaMeshPtr->correct();
if (auto* ensFaMeshPtr = ensFaMeshes.get(areai))
{
ensFaMeshPtr->expire();
ensFaMeshPtr->correct();
}
}
}
@ -340,9 +363,15 @@ int main(int argc, char *argv[])
printInfo(ensMesh, optVerbose);
if (ensFaMeshPtr)
// finite-area
forAll(areaRegionNames, areai)
{
printInfo(*ensFaMeshPtr, optVerbose);
auto* ensFaMeshPtr = ensFaMeshes.get(areai);
if (ensFaMeshPtr)
{
printInfo(*ensFaMeshPtr, optVerbose);
}
}
}
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2017 Wikki Ltd
Copyright (C) 2021-2023 OpenCFD Ltd.
Copyright (C) 2021-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -28,7 +28,7 @@ Application
makeFaMesh
Description
Check a finiteArea mesh
Check a finite-area mesh
Original Authors
Zeljko Tukovic, FAMENA
@ -39,6 +39,7 @@ Original Authors
#include "Time.H"
#include "argList.H"
#include "faMesh.H"
#include "faMeshTools.H"
#include "polyMesh.H"
#include "areaFaMesh.H"
#include "edgeFaMesh.H"
@ -47,7 +48,7 @@ Original Authors
#include "processorFaPatch.H"
#include "foamVtkIndPatchWriter.H"
#include "foamVtkLineWriter.H"
#include "faMeshTools.H"
#include "regionProperties.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -59,7 +60,7 @@ int main(int argc, char *argv[])
{
argList::addNote
(
"Check a finiteArea mesh"
"Check a finite-area mesh"
);
argList::addBoolOption
@ -77,9 +78,15 @@ int main(int argc, char *argv[])
);
#include "addRegionOption.H"
#include "addFaRegionOption.H"
#include "addAllFaRegionOptions.H"
#include "setRootCase.H"
#include "createTime.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
// ------------------------------------------------------------------------
#include "createNamedPolyMesh.H"
int geometryOrder(1);
@ -91,16 +98,41 @@ int main(int argc, char *argv[])
faMesh::geometryOrder(geometryOrder);
}
#include "createNamedFaMesh.H"
Info<< "Time = " << runTime.timeName() << nl << endl;
// Mesh information (verbose)
faMeshTools::printMeshChecks(aMesh);
if (args.found("write-vtk"))
for (const word& areaName : areaRegionNames)
{
#include "faMeshWriteVTK.H"
Info<< "Create faMesh";
if (!polyMesh::regionName(areaName).empty())
{
Info<< " [" << areaName << "]";
}
Info<< " for time = " << runTime.timeName() << nl;
autoPtr<faMesh> faMeshPtr(faMesh::TryNew(areaName, mesh));
if (!faMeshPtr)
{
Info<< " ...failed to create area-mesh";
if (!polyMesh::regionName(areaName).empty())
{
Info<< " [" << areaName << "]";
}
Info<< endl;
continue;
}
else
{
Info<< endl;
}
const auto& aMesh = faMeshPtr();
// Mesh information (verbose)
faMeshTools::printMeshChecks(aMesh);
if (args.found("write-vtk"))
{
#include "faMeshWriteVTK.H"
}
}
Info<< "\nEnd\n" << endl;

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2023 OpenCFD Ltd.
Copyright (C) 2021-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@ -15,6 +15,16 @@ Description
\*---------------------------------------------------------------------------*/
// The base name for output files
word vtkBaseFileName(aMesh.regionName());
if (polyMesh::regionName(vtkBaseFileName).empty())
{
vtkBaseFileName = "finiteArea";
}
Info<< nl;
Info<< "Write faMesh in vtk format:" << nl;
{
// finiteArea - faces
vtk::uindirectPatchWriter writer
@ -23,7 +33,7 @@ Description
// vtk::formatType::INLINE_ASCII,
fileName
(
aMesh.time().globalPath() / "finiteArea"
aMesh.time().globalPath() / vtkBaseFileName
)
);
@ -32,7 +42,7 @@ Description
globalIndex procAddr(aMesh.nFaces());
labelList cellIDs;
if (Pstream::master())
if (UPstream::master())
{
cellIDs.resize(procAddr.totalSize());
for (const labelRange& range : procAddr.ranges())
@ -53,8 +63,7 @@ Description
writer.beginPointData(1);
writer.write("normal", aMesh.pointAreaNormals());
Info<< nl
<< "Wrote faMesh in vtk format: " << writer.output().name() << nl;
Info<< " " << writer.output().name() << nl;
}
{
@ -66,7 +75,7 @@ Description
// vtk::formatType::INLINE_ASCII,
fileName
(
aMesh.time().globalPath() / "finiteArea-edges"
aMesh.time().globalPath() / (vtkBaseFileName + "-edges")
)
);
@ -88,8 +97,7 @@ Description
writer.beginPointData(1);
writer.write("normal", aMesh.pointAreaNormals());
Info<< nl
<< "Wrote faMesh in vtk format: " << writer.output().name() << nl;
Info<< " " << writer.output().name() << nl;
}
{
@ -108,7 +116,7 @@ Description
// vtk::formatType::INLINE_ASCII,
fileName
(
aMesh.time().globalPath() / "finiteArea-edgesCentres"
aMesh.time().globalPath() / (vtkBaseFileName + "-edgesCentres")
)
);
@ -134,8 +142,7 @@ Description
writer.write("normal", fld);
}
Info<< nl
<< "Wrote faMesh in vtk format: " << writer.output().name() << nl;
Info<< " " << writer.output().name() << nl;
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023 OpenCFD Ltd.
Copyright (C) 2023-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@ -15,7 +15,7 @@ Description
Input
mesh (polyMesh)
meshDefDict (system/faMeshDefintion)
meshDefDict (system/finite-area/faMeshDefinition)
\*---------------------------------------------------------------------------*/
@ -25,13 +25,16 @@ Input
const polyBoundaryMesh& pbm = mesh.boundaryMesh();
wordRes polyPatchNames;
meshDefDict.readIfPresent("polyMeshPatches", polyPatchNames);
labelList patchIDs;
const labelList patchIDs
if
(
pbm.indices(polyPatchNames, true) // useGroups
);
wordRes select;
meshDefDict.readIfPresent("polyMeshPatches", select)
)
{
patchIDs = pbm.indices(select, true); // useGroups
}
label nFaceLabels = 0;
for (const label patchi : patchIDs)

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2024 OpenCFD Ltd.
Copyright (C) 2021-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@ -13,6 +13,18 @@ License
Description
Search for the appropriate faMeshDefinition dictionary...
Expects to find a faMeshDefinition file in a location specified
by the command-line option, or one of the standard locations.
For default area region (region0):
(1) system/finite-area/faMeshDefinition
(2) system/faMeshDefinition [legacy location - v2312 and earlier]
For a named area region:
(1) system/finite-area/<area-name>/faMeshDefinition
(2) system/finite-area/faMeshDefinition.<area-name>
[symmetric with faOptions]
Required Classes
- Foam::polyMesh
- Foam::IOdictionary
@ -24,81 +36,185 @@ Required Variables
- runTime [Time]
Provided Variables
- meshDefDict [IOdictionary]
- meshDictPtr [autoPtr<IOdictionary>]
- faMeshDefinitionPtr [autoPtr<IOdictionary>]
If the dictionary cannot be found, exits with an error message or
reports a warning (dry-run mode)
\*---------------------------------------------------------------------------*/
const word dictName("faMeshDefinition");
autoPtr<IOdictionary> meshDictPtr;
autoPtr<IOdictionary> faMeshDefinitionPtr;
{
fileName dictPath;
const word& regionDir = Foam::polyMesh::regionName(regionName);
const word& areaRegionDir = Foam::polyMesh::regionName(areaRegionName);
// Exit unless dry-run?
const bool exitOnFailure = (!args.dryRun());
if (args.readIfPresent("dict", dictPath))
const word& regionDir = polyMesh::regionName(regionName);
const word& areaRegionDir = polyMesh::regionName(areaRegionName);
// Canonical location
fileName dictPath
(
runTime.system()/regionDir
/ faMesh::prefix()/areaRegionDir/dictName
);
// Dictionary specified on the command-line ...
const bool specified = args.readIfPresent("dict", dictPath);
if (specified)
{
// Dictionary specified on the command-line ...
if (isDir(dictPath))
{
dictPath /= dictName;
}
}
else if
(
// Dictionary under system/faMeshDefinition ?
// (v2312 and earlier)
areaRegionDir.empty()
&& exists
(
runTime.path()/runTime.caseSystem()
/ regionDir/faMesh::meshSubDir/dictName
)
)
{
// Dictionary present directly in system/ (v2312 and earlier)
dictPath = runTime.system()/regionDir/dictName;
}
else
{
// Use system/finite-area/ directory, with region qualifications
dictPath =
(
runTime.system()/regionDir
/ faMesh::prefix()/areaRegionDir/dictName
);
}
IOobject meshDictIO
(
dictPath,
runTime,
IOobject::MUST_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER,
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER,
true // is globalObject
);
if (!meshDictIO.typeHeaderOk<IOdictionary>(true))
refPtr<IOobject> meshDictIOPtr;
bool foundIOobject = meshDictIO.typeHeaderOk<IOdictionary>(true);
// For reporting any alternative locations
dictPath.clear();
if (!foundIOobject && !specified)
{
FatalErrorInFunction
<< meshDictIO.objectPath() << nl
<< exit(FatalError);
if (!areaRegionDir.empty())
{
// Alternative location
// - system/finite-area/faMeshDefinition.<area-name>
dictPath =
(
runTime.system()/regionDir
/ faMesh::prefix()/IOobject::groupName(dictName, areaRegionDir)
);
}
else
{
// legacy location (v2312 and earlier)
// - system/faMeshDefinition
dictPath = runTime.system()/regionDir/dictName;
}
if (!dictPath.empty())
{
auto ioptr = autoPtr<IOobject>::New
(
dictPath,
runTime,
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER,
true // is globalObject
);
foundIOobject =
(
ioptr && ioptr->typeHeaderOk<IOdictionary>(true)
);
if (foundIOobject)
{
// Use if found
meshDictIOPtr = std::move(ioptr);
}
// Generally retain dictPath for error messages,
// but don't mention the really old legacy location
if (areaRegionDir.empty())
{
dictPath.clear();
}
}
}
Info<< "Creating faMesh from definition: "
<< meshDictIO.objectRelPath() << endl;
// Alternative location or regular location?
if (!meshDictIOPtr)
{
meshDictIOPtr.cref(meshDictIO);
}
const auto& io = meshDictIOPtr();
meshDictPtr = autoPtr<IOdictionary>::New(meshDictIO);
// Handle final success or failure
if (foundIOobject)
{
Info<< "----------------" << nl
<< "Using area-mesh definition";
if (!areaRegionDir.empty())
{
Info<< " [" << areaRegionName << "]";
}
Info<< nl
<< " " << io.objectRelPath() << nl << endl;
faMeshDefinitionPtr = autoPtr<IOdictionary>::New(io);
}
else
{
// Failure
if (exitOnFailure)
{
auto& err =
FatalErrorInFunction
<< "Did not find area-mesh definition";
if (!areaRegionDir.empty())
{
err<< " [" << areaRegionName << "]";
}
err << nl
<< " " << io.objectRelPath() << nl;
if (!dictPath.empty())
{
err << " " << dictPath << nl;
}
FatalError<< exit(FatalError);
}
else
{
// dry-run: warning
auto& err =
Warning << nl
<< "----------------" << nl
<< "Did not find area-mesh definition";
if (!areaRegionDir.empty())
{
err << " [" << areaRegionName << "]";
}
err << nl
<< " " << io.objectRelPath() << nl;
if (!dictPath.empty())
{
err << " " << dictPath << nl;
}
}
}
}
IOdictionary& meshDefDict = *meshDictPtr;
// ************************************************************************* //

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2017 Wikki Ltd
Copyright (C) 2021-2023 OpenCFD Ltd.
Copyright (C) 2021-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -40,7 +40,6 @@ Original Authors
#include "Time.H"
#include "argList.H"
#include "OSspecific.H"
#include "faMesh.H"
#include "faMeshTools.H"
#include "IOdictionary.H"
@ -53,6 +52,7 @@ Original Authors
#include "PtrListOps.H"
#include "foamVtkLineWriter.H"
#include "foamVtkIndPatchWriter.H"
#include "regionProperties.H"
#include "syncTools.H"
#include "OBJstream.H"
@ -104,12 +104,14 @@ int main(int argc, char *argv[])
);
#include "addRegionOption.H"
#include "addFaRegionOption.H"
#include "addAllFaRegionOptions.H"
#include "setRootCase.H"
#include "createTime.H"
#include "createNamedPolyMesh.H"
#include "getFaRegionOption.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
const bool doDecompose = !args.found("no-decompose");
const bool doDecompFields = !args.found("no-fields");
@ -123,56 +125,83 @@ int main(int argc, char *argv[])
Info<< "Skip decompose of finiteArea fields" << nl;
}
// Reading faMeshDefinition dictionary
#include "findMeshDefinitionDict.H"
// ------------------------------------------------------------------------
// Inject/overwrite name for optional 'empty' patch
word patchName;
if (args.readIfPresent("empty-patch", patchName))
for (const word& areaRegionName : areaRegionNames)
{
meshDefDict.add("emptyPatch", patchName, true);
}
// Reading faMeshDefinition dictionary
#include "findMeshDefinitionDict.H"
// Preliminary checks
#include "checkPatchTopology.H"
if (!faMeshDefinitionPtr)
{
if (args.dryRun())
{
break;
}
else
{
FatalErrorInFunction
<< "Did not find area-mesh definition"<< nl
<< exit(FatalError);
}
}
Info << "Create areaMesh";
if (!Foam::polyMesh::regionName(areaRegionName).empty())
{
Foam::Info << ' ' << areaRegionName;
}
Info << " for polyMesh at time = " << runTime.timeName() << nl;
auto& meshDefDict = (*faMeshDefinitionPtr);
// Create
faMesh aMesh(areaRegionName, mesh, meshDefDict);
// Mesh information (less verbose)
faMeshTools::printMeshChecks(aMesh, 0);
// Inject/overwrite name for optional 'empty' patch
if (word name; args.readIfPresent("empty-patch", name))
{
meshDefDict.add("emptyPatch", name, true);
}
if (args.found("write-edges-obj"))
{
#include "faMeshWriteEdgesOBJ.H"
}
// Preliminary checks
#include "checkPatchTopology.H"
if (args.found("write-vtk"))
{
#include "faMeshWriteVTK.H"
}
Info<< "Create area-mesh";
if (!polyMesh::regionName(areaRegionName).empty())
{
Info<< " [" << areaRegionName << "]";
}
Info<< " with polyMesh at time = " << runTime.timeName() << nl;
if (args.dryRun())
{
Info<< "\ndry-run: not writing mesh or decomposing fields\n" << nl;
}
else
{
// More precision (for points data)
IOstream::minPrecision(10);
// Create
faMesh aMesh(areaRegionName, mesh, meshDefDict);
Info<< nl << "Write finite area mesh." << nl;
aMesh.write();
// Mesh information (less verbose)
faMeshTools::printMeshChecks(aMesh, 0);
Info<< endl;
#include "decomposeFaFields.H"
if (args.found("write-edges-obj"))
{
#include "faMeshWriteEdgesOBJ.H"
}
if (args.found("write-vtk"))
{
#include "faMeshWriteVTK.H"
}
if (args.dryRun())
{
Info<< "\ndry-run: not writing mesh or decomposing fields\n" << nl;
}
else
{
// More precision (for points data)
IOstream::minPrecision(10);
Info<< nl << "Write finite area mesh";
if (const word& name = aMesh.regionName(); !name.empty())
{
Info<< " [" << name << "]";
}
Info<< nl;
aMesh.write();
Info<< endl;
#include "decomposeFaFields.H"
}
}
Info<< "\nEnd\n" << endl;

View File

@ -334,6 +334,7 @@ int main(int argc, char *argv[])
);
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
argList::addDryRunOption
(
@ -442,7 +443,6 @@ int main(int argc, char *argv[])
bool decomposeFieldsOnly = args.found("fields");
bool forceOverwrite = args.found("force");
// Set time from database
#include "createTime.H"
@ -491,9 +491,17 @@ int main(int argc, char *argv[])
decompDictFile = runTime.globalPath()/decompDictFile;
}
// Get region names
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
const bool optRegions =
(regionNames.size() != 1 || regionNames[0] != polyMesh::defaultRegion);
@ -813,56 +821,76 @@ int main(int argc, char *argv[])
// Field objects at this time
IOobjectList objects;
IOobjectList faObjects;
// faMesh fields - can have multiple finite-area per volume
HashTable<IOobjectList> faObjects;
if (doDecompFields)
{
// List of volume mesh objects for this instance
objects = IOobjectList(mesh, runTime.timeName());
// List of area mesh objects (assuming single region)
faObjects = IOobjectList
(
mesh.time(),
runTime.timeName(),
faMesh::dbDir(mesh, word::null),
IOobjectOption::NO_REGISTER
);
// Ignore generated fields: (cellDist)
objects.remove("cellDist");
// Lists of finite-area fields
faObjects.reserve(areaRegionNames.size());
for (const word& areaName : areaRegionNames)
{
// The finite-area objects for this area region
IOobjectList objs
(
faMesh::Registry(mesh),
runTime.timeName(),
polyMesh::regionName(areaName),
IOobjectOption::NO_REGISTER
);
if (!objs.empty())
{
faObjects.emplace_set(areaName, std::move(objs));
}
}
}
// Finite area handling
autoPtr<faMeshDecomposition> faMeshDecompPtr;
// - all area regions use the same volume decomposition
HashPtrTable<faMeshDecomposition> faMeshDecompHashes;
if (doFiniteArea)
{
const word boundaryInst =
mesh.time().findInstance(mesh.meshDir(), "boundary");
IOobject io
(
"faBoundary",
boundaryInst,
faMesh::meshDir(mesh, word::null),
mesh.time(),
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
);
if (io.typeHeaderOk<faBoundaryMesh>(true))
for (const word& areaName : areaRegionNames)
{
// Always based on the volume decomposition!
faMeshDecompPtr.reset
IOobject io
(
new faMeshDecomposition
(
mesh,
mesh.nProcs(),
mesh.model()
)
"faBoundary",
boundaryInst,
faMesh::meshDir(mesh, areaName),
mesh.time(),
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
);
if (io.typeHeaderOk<faBoundaryMesh>(true))
{
// Always based on the volume decomposition!
faMeshDecompHashes.set
(
areaName,
autoPtr<faMeshDecomposition>::New
(
areaName,
mesh,
mesh.nProcs(),
mesh.model()
)
);
}
}
}
@ -1066,7 +1094,7 @@ int main(int argc, char *argv[])
processorDb.setTime(runTime);
// read the mesh
// Read the mesh
if (!procMeshList.set(proci))
{
procMeshList.set
@ -1273,12 +1301,15 @@ int main(int argc, char *argv[])
}
// Finite area mesh and field decomposition
if (faMeshDecompPtr)
// Finite-area mesh and field decomposition
for (auto& iter : faMeshDecompHashes.sorted())
{
Info<< "\nFinite area mesh decomposition" << endl;
const word& areaName = iter.key();
faMeshDecomposition& aMesh = faMeshDecompPtr();
faMeshDecomposition& aMesh = *(iter.val());
Info<< "\nFinite area mesh decomposition: "
<< areaName << endl;
aMesh.decomposeMesh();
aMesh.writeDecomposition();
@ -1289,9 +1320,13 @@ int main(int argc, char *argv[])
faFieldDecomposer::fieldsCache areaFieldCache;
if (doDecompFields)
if
(
const auto objs = faObjects.cfind(areaName);
doDecompFields && objs.good()
)
{
areaFieldCache.readAllFields(aMesh, faObjects);
areaFieldCache.readAllFields(aMesh, objs.val());
}
const label nAreaFields = areaFieldCache.size();
@ -1322,7 +1357,7 @@ int main(int argc, char *argv[])
processorDb.setTime(runTime);
// Read the mesh
// Read the volume mesh
fvMesh procFvMesh
(
IOobject
@ -1333,7 +1368,7 @@ int main(int argc, char *argv[])
)
);
faMesh procMesh(procFvMesh);
faMesh procMesh(areaName, procFvMesh);
// // Does not work. HJ, 15/Aug/2017
// const labelIOList& faceProcAddressing =
@ -1392,7 +1427,11 @@ int main(int argc, char *argv[])
boundaryProcAddressing
);
areaFieldCache.decomposeAllFields(fieldDecomposer);
areaFieldCache.decomposeAllFields
(
fieldDecomposer,
args.verbose() // report
);
}
}
}

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2017 OpenFOAM Foundation
Copyright (C) 2015-2023 OpenCFD Ltd.
Copyright (C) 2015-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -91,6 +91,7 @@ int main(int argc, char *argv[])
argList::noParallel();
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
argList::addVerboseOption();
argList::addOption
@ -201,9 +202,17 @@ int main(int argc, char *argv[])
const bool newTimes = args.found("newTimes");
// Get region names
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
// Determine the processor count
label nProcs{0};
@ -391,19 +400,30 @@ int main(int argc, char *argv[])
IOobjectOption::NO_REGISTER
);
IOobjectList faObjects;
// The finite-area fields (multiple finite-area per volume)
HashTable<IOobjectList> faObjects;
if (doFiniteArea && doFields)
{
// List of area mesh objects (assuming single region)
// - scan on processor0
faObjects = IOobjectList
(
procMeshes.meshes()[0],
databases[0].timeName(),
faMesh::dbDir(word::null), // local relative to mesh
IOobjectOption::NO_REGISTER
);
// Lists of finite-area fields - scan on processor0
faObjects.reserve(areaRegionNames.size());
for (const word& areaName : areaRegionNames)
{
// The finite-area objects for this area region
IOobjectList objs
(
procMeshes.meshes()[0],
databases[0].timeName(),
faMesh::dbDir(areaName), // local relative to mesh
IOobjectOption::NO_REGISTER
);
if (!objs.empty())
{
faObjects.emplace_set(areaName, std::move(objs));
}
}
}
if (doFields)
@ -553,42 +573,60 @@ int main(int argc, char *argv[])
}
// If there are any FA fields, reconstruct them
if (!doFiniteArea)
// Reconstruct any finite-area fields
if (doFiniteArea)
{
bool hadFaFields = false;
for (const word& areaName : areaRegionNames)
{
const auto objs = faObjects.cfind(areaName);
if (!objs.good())
{
continue;
}
const auto& faObjs = objs.val();
if
(
!faObjs.count(fieldTypes::is_area)
&& !faObjs.count<edgeScalarField>()
)
{
continue;
}
hadFaFields = true;
Info<< "Reconstructing finite-area fields ["
<< polyMesh::regionName(areaName)
<< "]" << nl << endl;
const faMesh aMesh(areaName, mesh);
processorFaMeshes procFaMeshes
(
procMeshes.meshes(),
areaName
);
faFieldReconstructor reconstructor
(
aMesh,
procFaMeshes.meshes(),
procFaMeshes.edgeProcAddressing(),
procFaMeshes.faceProcAddressing(),
procFaMeshes.boundaryProcAddressing()
);
reconstructor.reconstructAllFields(faObjs);
}
if (!hadFaFields)
{
Info << "No finite-area fields" << nl << endl;
}
}
else if
(
faObjects.count<areaScalarField>()
|| faObjects.count<areaVectorField>()
|| faObjects.count<areaSphericalTensorField>()
|| faObjects.count<areaSymmTensorField>()
|| faObjects.count<areaTensorField>()
|| faObjects.count<edgeScalarField>()
)
{
Info << "Reconstructing FA fields" << nl << endl;
faMesh aMesh(mesh);
processorFaMeshes procFaMeshes(procMeshes.meshes());
faFieldReconstructor reconstructor
(
aMesh,
procFaMeshes.meshes(),
procFaMeshes.edgeProcAddressing(),
procFaMeshes.faceProcAddressing(),
procFaMeshes.boundaryProcAddressing()
);
reconstructor.reconstructAllFields(faObjects);
}
else
{
Info << "No FA fields" << nl << endl;
}
if (doReconstructSets)
{

View File

@ -119,17 +119,10 @@ autoPtr<faceCoupleInfo> determineCoupledFaces
const polyBoundaryMesh& masterPatches = masterMesh.boundaryMesh();
DynamicList<label> masterFaces
(
masterMesh.nFaces()
- masterMesh.nInternalFaces()
);
DynamicList<label> masterFaces(masterMesh.nBoundaryFaces());
forAll(masterPatches, patchi)
for (const polyPatch& pp : masterPatches)
{
const polyPatch& pp = masterPatches[patchi];
if (isA<processorPolyPatch>(pp))
{
for
@ -139,11 +132,8 @@ autoPtr<faceCoupleInfo> determineCoupledFaces
proci++
)
{
const string toProcString("to" + name(proci));
if (
pp.name().rfind(toProcString)
== (pp.name().size()-toProcString.size())
)
const string toProcString("to" + Foam::name(proci));
if (pp.name().ends_with(toProcString))
{
label meshFacei = pp.start();
forAll(pp, i)
@ -156,7 +146,6 @@ autoPtr<faceCoupleInfo> determineCoupledFaces
}
}
masterFaces.shrink();
// Pick up all patches on meshToAdd ending in "procBoundaryDDDtoYYY"
@ -164,16 +153,10 @@ autoPtr<faceCoupleInfo> determineCoupledFaces
const polyBoundaryMesh& addPatches = meshToAdd.boundaryMesh();
DynamicList<label> addFaces
(
meshToAdd.nFaces()
- meshToAdd.nInternalFaces()
);
DynamicList<label> addFaces(meshToAdd.nBoundaryFaces());
forAll(addPatches, patchi)
for (const polyPatch& pp : addPatches)
{
const polyPatch& pp = addPatches[patchi];
if (isA<processorPolyPatch>(pp))
{
bool isConnected = false;
@ -215,7 +198,6 @@ autoPtr<faceCoupleInfo> determineCoupledFaces
}
}
}
addFaces.shrink();
return autoPtr<faceCoupleInfo>::New
(
@ -502,15 +484,14 @@ void writeMaps
Info<< " pointProcAddressing" << endl;
ioAddr.rename("pointProcAddressing");
IOList<label>::writeContents(ioAddr, pointProcAddressing);
labelIOList::writeContents(ioAddr, pointProcAddressing);
// From processor face to reconstructed mesh face
Info<< " faceProcAddressing" << endl;
ioAddr.rename("faceProcAddressing");
labelIOList faceProcAddr(ioAddr, faceProcAddressing);
// - add turning index to faceProcAddressing.
// See reconstructPar for meaning of turning index.
labelList faceProcAddr(faceProcAddressing);
// Now add turning index to faceProcAddressing.
// See reconstructPar for meaning of turning index.
forAll(faceProcAddr, procFacei)
{
const label masterFacei = faceProcAddr[procFacei];
@ -545,19 +526,21 @@ void writeMaps
}
}
faceProcAddr.write();
Info<< " faceProcAddressing" << endl;
ioAddr.rename("faceProcAddressing");
labelIOList::writeContents(ioAddr, faceProcAddr);
faceProcAddr.clear();
// From processor cell to reconstructed mesh cell
Info<< " cellProcAddressing" << endl;
ioAddr.rename("cellProcAddressing");
IOList<label>::writeContents(ioAddr, cellProcAddressing);
labelIOList::writeContents(ioAddr, cellProcAddressing);
// From processor patch to reconstructed mesh patch
Info<< " boundaryProcAddressing" << endl;
ioAddr.rename("boundaryProcAddressing");
IOList<label>::writeContents(ioAddr, boundProcAddressing);
labelIOList::writeContents(ioAddr, boundProcAddressing);
Info<< endl;
}
@ -645,7 +628,8 @@ void sortFaEdgeMapping
{
// From faMeshReconstructor.C - edge shuffling on patches
Map<label> remapGlobal(2*onePatch.nEdges());
Map<label> remapGlobal;
remapGlobal.reserve(onePatch.nEdges());
for (label edgei = 0; edgei < onePatch.nInternalEdges(); ++edgei)
{
remapGlobal.insert(edgei, remapGlobal.size());
@ -779,6 +763,11 @@ int main(int argc, char *argv[])
);
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
// Prevent volume fields [with regionFaModels] from incidental
// triggering finite-area
regionModels::allowFaModels(false);
// Prevent volume BCs from triggering finite-area
regionModels::allowFaModels(false);
@ -841,9 +830,17 @@ int main(int argc, char *argv[])
}
}
// Get region names
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
// Determine the processor count
label nProcs{0};
@ -935,7 +932,8 @@ int main(int argc, char *argv[])
polyMesh::meshDir(regionName),
databases[0],
IOobject::NO_READ,
IOobject::NO_WRITE
IOobject::NO_WRITE,
IOobject::NO_REGISTER
);
// Problem: faceCompactIOList recognises both 'faceList' and
@ -1405,8 +1403,12 @@ int main(int argc, char *argv[])
// Read finite-area
PtrList<faMesh> procFaMeshes(databases.size());
// For each named area region that exists create a HashTable
// entry that will contain the PtrList for all processors
PtrList<polyMesh> procMeshes(databases.size());
HashTable<PtrList<faMesh>> procAreaRegionMeshes;
procAreaRegionMeshes.reserve(areaRegionNames.size());
forAll(databases, proci)
{
@ -1414,20 +1416,16 @@ int main(int argc, char *argv[])
<< "Read processor mesh: "
<< (databases[proci].caseName()/regionDir) << endl;
procMeshes.set
const polyMesh& procMesh = procMeshes.emplace
(
proci,
new polyMesh
IOobject
(
IOobject
(
regionName,
databases[proci].timeName(),
databases[proci]
)
regionName,
databases[proci].timeName(),
databases[proci]
)
);
const polyMesh& procMesh = procMeshes[proci];
writeMaps
(
@ -1450,38 +1448,67 @@ int main(int argc, char *argv[])
"boundary"
);
IOobject io
(
"faBoundary",
boundaryInst,
faMesh::meshDir(procMesh, word::null),
procMesh.time(),
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
);
if (io.typeHeaderOk<faBoundaryMesh>(true))
for (const word& areaName : areaRegionNames)
{
// Always based on the volume decomposition!
procFaMeshes.set(proci, new faMesh(procMesh));
IOobject io
(
"faBoundary",
boundaryInst,
faMesh::meshDir(procMesh, areaName),
procMesh.time(),
IOobject::READ_IF_PRESENT,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
);
if (io.typeHeaderOk<faBoundaryMesh>(true))
{
// Always based on the volume decomposition!
auto& procFaMeshes = procAreaRegionMeshes(areaName);
procFaMeshes.resize(databases.size());
procFaMeshes.set
(
proci,
new faMesh(areaName, procMesh)
);
}
}
}
}
// Finite-area mapping
doFiniteArea = false;
forAll(procFaMeshes, proci)
// A finite-area mapping exists if procFaMeshes was filled
// Re-read reconstructed polyMesh. Note: could probably be avoided
// by merging into loops above.
refPtr<polyMesh> masterPolyMeshPtr;
if (!procAreaRegionMeshes.empty())
{
if (procFaMeshes.set(proci))
{
doFiniteArea = true;
}
masterPolyMeshPtr.reset
(
new polyMesh
(
IOobject
(
regionName,
runTime.timeName(),
runTime
),
true
)
);
}
if (doFiniteArea)
// Process any finite-area meshes
for (const auto& iter : procAreaRegionMeshes.csorted())
{
const auto& areaName = iter.key();
const auto& procFaMeshes = iter.val();
const polyMesh& masterMesh = masterPolyMeshPtr();
// Addressing from processor to reconstructed case
labelListList faFaceProcAddressing(nProcs);
labelListList faEdgeProcAddressing(nProcs);
@ -1499,32 +1526,10 @@ int main(int argc, char *argv[])
faBoundProcAddressing[proci] = identity(bm.size());
// Mark processor patches
for
(
label patchi = bm.nNonProcessor();
patchi < bm.size();
++patchi
)
{
faBoundProcAddressing[proci][patchi] = -1;
}
faBoundProcAddressing[proci].slice(bm.nNonProcessor()) = -1;
}
// Re-read reconstructed polyMesh. Note: could probably be avoided
// by merging into loops above.
const polyMesh masterMesh
(
IOobject
(
regionName,
runTime.timeName(),
runTime
),
true
);
// faceProcAddressing
// ~~~~~~~~~~~~~~~~~~
@ -1559,13 +1564,13 @@ int main(int argc, char *argv[])
// Construct without patches
faMesh masterFaMesh
(
areaName,
masterMesh,
std::move(masterFaceLabels),
io
);
const uindirectPrimitivePatch& masterPatch =
masterFaMesh.patch();
const auto& masterPatch = masterFaMesh.patch();
// pointProcAddressing
@ -1577,7 +1582,7 @@ int main(int argc, char *argv[])
const auto& procPatch = procFaMeshes[proci].patch();
const auto& mp = procPatch.meshPoints();
labelList& pointAddr = faPointProcAddressing[proci];
auto& pointAddr = faPointProcAddressing[proci];
pointAddr.resize_nocopy(mp.size());
forAll(mp, i)
@ -1626,8 +1631,7 @@ int main(int argc, char *argv[])
label nPatches = 0;
forAll(completePatches, patchi)
{
const labelList& patchEdgeLabels =
singlePatchEdgeLabels[patchi];
const auto& patchEdgeLabels = singlePatchEdgeLabels[patchi];
const faPatch& fap = procMesh0.boundary()[patchi];
@ -1658,11 +1662,11 @@ int main(int argc, char *argv[])
// Serial mesh - no parallel communication
const bool oldParRun = Pstream::parRun(false);
const bool oldParRun = UPstream::parRun(false);
masterFaMesh.addFaPatches(completePatches);
Pstream::parRun(oldParRun); // Restore parallel state
UPstream::parRun(oldParRun); // Restore parallel state
// Write mesh & individual addressing

View File

@ -33,99 +33,266 @@ License
#include "decomposedBlockData.H"
#include "IFstream.H"
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
bool Foam::checkFileExistence(const fileName& fName)
// Trimmed-down version of lookupAndCacheProcessorsPath
// with Foam::exists() check. No caching.
// Check for two conditions:
// - file has to exist
// - if collated the entry has to exist inside the file
// Note: bypass fileOperation::filePath(IOobject&) since has problems
// with going to a different number of processors
// (in collated format). Use file-based searching instead
namespace Foam
{
// Trimmed-down version of lookupAndCacheProcessorsPath
// with Foam::exists() check. No caching.
// Check for two conditions:
// - file has to exist
// - if collated the entry has to exist inside the file
// Note: bypass fileOperation::filePath(IOobject&) since has problems
// with going to a different number of processors
// (in collated format). Use file-based searching instead
const auto& handler = Foam::fileHandler();
typedef fileOperation::procRangeType procRangeType;
fileName path, pDir, local;
procRangeType group;
label numProcs;
const label proci =
fileOperation::splitProcessorPath
(fName, path, pDir, local, group, numProcs);
// If indeed collated format:
// Collect block-number in individual filenames
// (might differ on different processors)
static bool checkFileExistenceCollated
(
const Foam::fileOperation& handler,
const Foam::fileName& fName
)
{
// using namespace Foam;
bool found = false;
if (proci != -1)
{
// Read all directories to see any beginning with processor
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const label handlerComm = handler.comm();
const fileNameList dirEntries
(
handler.readDir(path, fileName::Type::DIRECTORY)
);
const label globalProci = UPstream::myProcNo(UPstream::worldComm);
const label handlerProci = UPstream::myProcNo(handlerComm);
const label nHandlerProcs = UPstream::nProcs(handlerComm);
// Extract info from processorN or processorsNN
// - highest processor number
// - directory+offset containing data for proci
// label nProcs = 0;
for (const fileName& dirN : dirEntries)
// Determine my local block number
label myBlockNumber = -1;
{
// Analyse directory name
label rNum(-1);
const label readProci =
fileOperation::detectProcessorPath(dirN, group, &rNum);
fileOperation::procRangeType group;
label proci = fileOperation::detectProcessorPath(fName, group);
if (proci == readProci)
if (proci == -1 && group.empty())
{
// Found "processorN"
if (Foam::exists(path/dirN/local))
{
found = true;
break;
}
// 'processorsXXX' format so contains all ranks
// according to worldComm
myBlockNumber = globalProci;
}
else if (rNum != -1)
else
{
// "processorsNN" or "processorsNN_start-end"
if (group.empty())
{
// "processorsNN"
if (proci < rNum && Foam::exists(path/dirN/local))
{
found = true;
break;
}
}
else if (group.contains(proci))
{
// "processorsNN_start-end"
// - save the local proc offset
if (Foam::exists(path/dirN/local))
{
found = true;
break;
}
}
// 'processorsXXX_n-m' format so check for relative rank
myBlockNumber = handlerProci;
}
}
}
if (!found)
{
found = Foam::exists(fName);
// Since we are streaming anyhow, could also pack as tuple:
// Tuple2<fileName, label>
// Collect file names on master of local communicator
const fileNameList fNames
(
Pstream::listGatherValues
(
fName,
handlerComm,
UPstream::msgType()
)
);
// Collect block numbers on master of local communicator
const labelList myBlockNumbers
(
Pstream::listGatherValues
(
myBlockNumber,
handlerComm,
UPstream::msgType()
)
);
// Determine for all whether the filename exists in the collated file.
boolList allFound;
if (UPstream::master(handlerComm))
{
allFound.resize(nHandlerProcs, false);
// Store nBlocks and index of file that was used for nBlocks
label nBlocks = -1;
label blockRanki = -1;
forAll(fNames, ranki)
{
if
(
blockRanki == -1
|| (fNames[ranki] != fNames[blockRanki])
)
{
blockRanki = ranki;
IFstream is(fNames[ranki]);
nBlocks = decomposedBlockData::getNumBlocks(is);
}
allFound[ranki] = (myBlockNumbers[ranki] < nBlocks);
}
}
// Scatter using the handler communicator
found = Pstream::listScatterValues
(
allFound,
handlerComm,
UPstream::msgType()
);
}
return found;
}
} // End namespace
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
Foam::bitSet Foam::haveProcessorFile
(
const word& name, // eg "faces"
const fileName& instance, // eg "constant"
const fileName& local, // eg, polyMesh
const Time& runTime,
const bool verbose
)
{
const auto& handler = Foam::fileHandler();
const fileName fName
(
handler.filePath(runTime.path()/instance/local/name)
);
bool found = handler.isFile(fName);
// Assume non-collated (as fallback value).
// If everyone claims to have the file, use master to verify if
// collated is involved.
bool isCollated = false;
if (returnReduceAnd(found, UPstream::worldComm))
{
// Test for collated format.
// Note: can test only world-master. Since even host-collated will have
// same file format type for all processors
if (UPstream::master(UPstream::worldComm))
{
const bool oldParRun = UPstream::parRun(false);
if (IFstream is(fName); is.good())
{
IOobject io(name, instance, local, runTime);
io.readHeader(is);
isCollated = decomposedBlockData::isCollatedType(io);
}
UPstream::parRun(oldParRun);
}
Pstream::broadcast(isCollated, UPstream::worldComm);
}
// For collated, check that the corresponding blocks exist
if (isCollated)
{
found = checkFileExistenceCollated(handler, fName);
}
// Globally consistent information about who has the file
bitSet haveFileOnProc = bitSet::allGather(found, UPstream::worldComm);
if (verbose)
{
Info<< "Per processor availability of \""
<< name << "\" file in " << instance/local << nl
<< " " << flatOutput(haveFileOnProc) << nl << endl;
}
return haveFileOnProc;
}
Foam::boolList Foam::haveMeshFile
(
const word& name, // eg "faces"
const fileName& instance, // eg "constant"
const fileName& local, // eg, polyMesh
const Time& runTime,
const bool verbose
)
{
const auto& handler = Foam::fileHandler();
const fileName fName
(
handler.filePath(runTime.path()/instance/local/name)
);
bool found = handler.isFile(fName);
// Assume non-collated (as fallback value).
// If everyone claims to have the file, use master to verify if
// collated is involved.
bool isCollated = false;
if (returnReduceAnd(found, UPstream::worldComm))
{
// Test for collated format.
// Note: can test only world-master. Since even host-collated will have
// same file format type for all processors
if (UPstream::master(UPstream::worldComm))
{
const bool oldParRun = UPstream::parRun(false);
if (IFstream is(fName); is.good())
{
IOobject io(name, instance, local, runTime);
io.readHeader(is);
isCollated = decomposedBlockData::isCollatedType(io);
}
UPstream::parRun(oldParRun);
}
Pstream::broadcast(isCollated, UPstream::worldComm);
}
// For collated, check that the corresponding blocks exist
if (isCollated)
{
found = checkFileExistenceCollated(handler, fName);
}
// Globally consistent information about who has a mesh
boolList haveFileOnProc
(
UPstream::allGatherValues<bool>(found, UPstream::worldComm)
);
if (verbose)
{
Info<< "Per processor availability of \""
<< name << "\" file in " << instance/local << nl
<< " " << flatOutput(haveFileOnProc) << nl << endl;
}
return haveFileOnProc;
}
Foam::boolList Foam::haveMeshFile
(
@ -135,143 +302,49 @@ Foam::boolList Foam::haveMeshFile
const bool verbose
)
{
#if 0
// Simple directory scanning - too fragile
bool found = checkFileExistence(runTime.path()/meshPath/meshFile);
#else
// Trimmed-down version of lookupAndCacheProcessorsPath
// with Foam::exists() check. No caching.
// Check for two conditions:
// - file has to exist
// - if collated the entry has to exist inside the file
// Note: bypass fileOperation::filePath(IOobject&) since has problems
// with going to a different number of processors
// (in collated format). Use file-based searching instead
const auto& handler = Foam::fileHandler();
typedef fileOperation::procRangeType procRangeType;
const fileName fName
(
handler.filePath(runTime.path()/meshPath/meshFile)
);
bool found = handler.isFile(fName);
if (returnReduceAnd(found)) // worldComm
// Assume non-collated (as fallback value).
// If everyone claims to have the file, use master to verify if
// collated is involved.
bool isCollated = false;
if (returnReduceAnd(found, UPstream::worldComm))
{
// Bit tricky: avoid having all slaves open file since this involves
// reading it on master and broadcasting it. This fails if file > 2G.
// So instead only read on master
bool isCollated = false;
// Test for collated format.
// Note: can test only world-master. Since even host-collated will have
// same file format type for all processors
if (UPstream::master(UPstream::worldComm))
{
const bool oldParRun = UPstream::parRun(false);
IFstream is(fName);
if (is.good())
if (IFstream is(fName); is.good())
{
IOobject io(meshFile, meshPath, runTime);
io.readHeader(is);
isCollated = decomposedBlockData::isCollatedType(io);
}
UPstream::parRun(oldParRun);
}
Pstream::broadcast(isCollated); //UPstream::worldComm
// Collect block-number in individual filenames (might differ
// on different processors)
if (isCollated)
{
const label nProcs = UPstream::nProcs(fileHandler().comm());
const label myProcNo = UPstream::myProcNo(fileHandler().comm());
// Collect file names on master of local communicator
const fileNameList fNames
(
Pstream::listGatherValues
(
fName,
fileHandler().comm(),
UPstream::msgType()
)
);
// Collect local block number
label myBlockNumber = -1;
{
procRangeType group;
label proci = fileOperation::detectProcessorPath(fName, group);
if (proci == -1 && group.empty())
{
// 'processorsXXX' format so contains all ranks
// according to worldComm
myBlockNumber = UPstream::myProcNo(UPstream::worldComm);
}
else
{
// 'processorsXXX_n-m' format so check for the
// relative rank
myBlockNumber = myProcNo;
}
}
const labelList myBlockNumbers
(
Pstream::listGatherValues
(
myBlockNumber,
fileHandler().comm(),
UPstream::msgType()
)
);
// Determine for all whether the filename exists in the collated
// file.
boolList allFound(nProcs, false);
if (UPstream::master(fileHandler().comm()))
{
// Store nBlocks and index of file that was used for nBlocks
label nBlocks = -1;
label blockRanki = -1;
forAll(fNames, ranki)
{
if
(
blockRanki == -1
|| (fNames[ranki] != fNames[blockRanki])
)
{
blockRanki = ranki;
IFstream is(fNames[ranki]);
nBlocks = decomposedBlockData::getNumBlocks(is);
}
allFound[ranki] = (myBlockNumbers[ranki] < nBlocks);
}
}
found = Pstream::listScatterValues
(
allFound,
fileHandler().comm(),
UPstream::msgType()
);
}
Pstream::broadcast(isCollated, UPstream::worldComm);
}
#endif
// For collated, check that the corresponding blocks exist
if (isCollated)
{
found = checkFileExistenceCollated(handler, fName);
}
// Globally consistent information about who has a mesh
boolList haveFileOnProc

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2012 OpenFOAM Foundation
Copyright (C) 2022-2023 OpenCFD Ltd.
Copyright (C) 2022-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -48,15 +48,32 @@ namespace Foam
// Forward Declarations
class faMesh;
//- Check for availability of given file
bool checkFileExistence(const fileName& fName);
//- Check for availability of specified file (on all ranks)
bitSet haveProcessorFile
(
const word& name, // eg, "faces"
const fileName& instance, // eg, "constant"
const fileName& local, // eg, "polyMesh"
const Time& runTime,
const bool verbose = true
);
//- Check for availability of specified mesh file (default: "faces")
//- Check for availability of specified file (on all ranks)
boolList haveMeshFile
(
const word& name, // eg, "faces"
const fileName& instance, // eg, "constant"
const fileName& local, // eg, "polyMesh"
const Time& runTime,
const bool verbose = true
);
//- Check for availability of specified mesh file
boolList haveMeshFile
(
const Time& runTime,
const fileName& meshPath,
const word& meshFile = "faces",
const word& meshFile,
const bool verbose = true
);
@ -75,7 +92,7 @@ void masterMeshInstance
fileName& pointsInstance
);
}
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -21,44 +21,59 @@ Requires
// Initially all possible objects that are available at the final time
List<wordHashSet> availableRegionObjectNames(meshes.size());
List<wordHashSet> availableFaRegionObjectNames(meshes.size());
List<HashTable<wordHashSet>> availableFaRegionObjectNames(meshes.size());
forAll(meshes, regioni)
{
const auto& mesh = meshes[regioni];
IOobjectList objects;
IOobjectList faObjects;
HashTable<IOobjectList> faObjects;
if (doConvertFields && !timeDirs.empty())
{
// const word& checkTimeDir = timeDirs.back().name();
// List of volume mesh objects for this instance
objects = IOobjectList(mesh, timeDirs.back().name());
// List of area mesh objects (assuming single region)
faObjects = IOobjectList
(
mesh.time(),
timeDirs.back().name(),
faMesh::dbDir(mesh, word::null),
IOobjectOption::NO_REGISTER
);
if (fieldSelector)
{
objects.filterObjects(fieldSelector);
faObjects.filterObjects(fieldSelector);
}
// Remove "*_0" restart fields
objects.prune_0();
faObjects.prune_0();
if (!doPointValues)
{
// Prune point fields if disabled
objects.filterClasses(Foam::fieldTypes::is_point, true);
}
// The finite-area regions and their fields for this volume region
// and instance
faObjects.reserve(areaRegionNames.size());
for (const word& areaName : areaRegionNames)
{
IOobjectList objs
(
faMesh::Registry(mesh),
timeDirs.back().name(),
polyMesh::regionName(areaName),
IOobjectOption::NO_REGISTER
);
if (fieldSelector)
{
objs.filterObjects(fieldSelector);
}
// Remove "*_0" restart fields
objs.prune_0();
faObjects(areaName) = std::move(objs);
}
}
// Volume fields
@ -78,20 +93,29 @@ forAll(meshes, regioni)
}
// Area fields
if (!faObjects.empty())
for (const word& areaName : areaRegionNames)
{
wordList objectNames(faObjects.sortedNames());
wordList objectNames;
// Check availability for all times... (assuming single region)
checkData
if (const auto iter = faObjects.cfind(areaName); iter.good())
{
objectNames = iter.val().sortedNames();
// Check availability for all times...
checkData
(
faMesh::Registry(mesh),
timeDirs,
objectNames,
polyMesh::regionName(areaName)
);
}
availableFaRegionObjectNames[regioni].emplace_set
(
mesh.time(),
timeDirs,
objectNames,
faMesh::dbDir(mesh, word::null)
areaName,
std::move(objectNames)
);
availableFaRegionObjectNames[regioni] = objectNames;
}
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2021-2022 OpenCFD Ltd.
Copyright (C) 2021-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM, distributed under GPL-3.0-or-later.
@ -15,12 +15,14 @@ Description
\*---------------------------------------------------------------------------*/
// Cases and meshes per volume region
PtrList<ensightCase> ensightCases(regionNames.size());
PtrList<ensightMesh> ensightMeshes(regionNames.size());
PtrList<faMesh> meshesFa(regionNames.size());
PtrList<ensightCase> ensightCasesFa(regionNames.size());
PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
// Per volume region can have multiple finite-area regions
List<PtrDynList<faMesh>> meshesFa(regionNames.size());
List<PtrDynList<ensightCase>> ensightCasesFa(regionNames.size());
List<PtrDynList<ensightFaMesh>> ensightMeshesFa(regionNames.size());
{
forAll(regionNames, regioni)
@ -45,6 +47,7 @@ PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
}
}
// Volume mesh
ensightMeshes.set
(
regioni,
@ -59,33 +62,60 @@ PtrList<ensightFaMesh> ensightMeshesFa(regionNames.size());
new ensightCase(ensCasePath, ensCaseName, caseOpts)
);
if (doFiniteArea)
if (!doFiniteArea)
{
autoPtr<faMesh> faMeshPtr(faMesh::TryNew(mesh));
continue;
}
// Note: not every volume region is guaranteed to have
// any or all area region(s)
meshesFa[regioni].reserve_exact(areaRegionNames.size());
for (const word& areaName : areaRegionNames)
{
auto faMeshPtr = faMesh::TryNew(areaName, mesh);
if (faMeshPtr)
{
ensightCasesFa.set
(
regioni,
new ensightCase
(
ensCasePath/"finite-area",
"finite-area",
caseOpts
)
);
meshesFa.set(regioni, std::move(faMeshPtr));
ensightMeshesFa.set
(
regioni,
new ensightFaMesh(meshesFa[regioni])
);
ensightMeshesFa[regioni].verbose(optVerbose);
meshesFa[regioni].push_back(std::move(faMeshPtr));
}
}
// Setup the ensight components
const label nAreas = meshesFa[regioni].size();
ensightCasesFa[regioni].reserve_exact(nAreas);
ensightMeshesFa[regioni].reserve_exact(nAreas);
for (const faMesh& areaMesh : meshesFa[regioni])
{
// Use regionName() - automatically filters for defaultRegion
const word& areaName = areaMesh.regionName();
if (areaName.empty())
{
// single-region
ensightCasesFa[regioni].emplace_back
(
ensCasePath/"finite-area",
"finite-area",
caseOpts
);
}
else
{
// multi-region
ensightCasesFa[regioni].emplace_back
(
ensCasePath/"finite-area"/areaName,
areaName,
caseOpts
);
}
auto& ensFaMesh = ensightMeshesFa[regioni].emplace_back(areaMesh);
ensFaMesh.verbose(optVerbose);
}
}
}

View File

@ -124,6 +124,7 @@ Usage
#include "OFstream.H"
#include "Pstream.H"
#include "HashOps.H"
#include "PtrDynList.H"
#include "regionProperties.H"
#include "fvc.H"
@ -172,6 +173,7 @@ int main(int argc, char *argv[])
argList::addVerboseOption();
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
argList::addBoolOption
(
@ -439,6 +441,14 @@ int main(int argc, char *argv[])
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
// ------------------------------------------------------------------------
// Directory management
@ -518,29 +528,32 @@ int main(int argc, char *argv[])
polyMesh::readUpdateState meshState = mesh.readUpdate();
const bool moving = (meshState != polyMesh::UNCHANGED);
// Ensight
// Ensight (fvMesh)
auto& ensCase = ensightCases[regioni];
auto& ensMesh = ensightMeshes[regioni];
// Finite-area (can be missing)
auto* ensFaCasePtr = ensightCasesFa.get(regioni);
auto* ensFaMeshPtr = ensightMeshesFa.get(regioni);
ensCase.setTime(timeDirs[timei], timeIndex);
if (ensFaCasePtr)
// Finite-area (optional)
// Accounting exists for each volume region but may be empty
auto& ensFaCases = ensightCasesFa[regioni];
auto& ensFaMeshes = ensightMeshesFa[regioni];
for (auto& ensFaCase : ensFaCases)
{
ensFaCasePtr->setTime(timeDirs[timei], timeIndex);
ensFaCase.setTime(timeDirs[timei], timeIndex);
}
// Movement
if (moving)
{
ensMesh.expire();
ensMesh.correct();
if (ensFaMeshPtr)
for (auto& ensFaMesh : ensFaMeshes)
{
ensFaMeshPtr->expire();
ensFaMeshPtr->correct();
ensFaMesh.expire();
ensFaMesh.correct();
}
}
@ -555,15 +568,19 @@ int main(int argc, char *argv[])
}
// finite-area
if (ensFaCasePtr && ensFaMeshPtr)
forAll(ensFaMeshes, areai)
{
autoPtr<ensightGeoFile> os =
ensFaCasePtr->newGeometry(hasMovingMesh);
const auto& ensFaCase = ensFaCases[areai];
const auto& ensFaMesh = ensFaMeshes[areai];
ensFaMeshPtr->write(os.ref());
autoPtr<ensightGeoFile> os =
ensFaCase.newGeometry(hasMovingMesh);
ensFaMesh.write(os.ref());
}
}
// Objects at this time
IOobjectList objects(mesh, runTime.timeName());
@ -575,23 +592,37 @@ int main(int argc, char *argv[])
// Volume, internal, point fields
#include "convertVolumeFields.H"
// The finite-area objects at this time
IOobjectList faObjects;
if (ensFaMeshPtr)
// finite-area
forAll(ensFaMeshes, areai)
{
faObjects =
IOobjectList(ensFaMeshPtr->mesh(), runTime.timeName());
auto* ensFaCasePtr = ensFaCases.get(areai);
auto* ensFaMeshPtr = ensFaMeshes.get(areai);
faObjects.filterObjects
(
availableFaRegionObjectNames[regioni]
);
// The finite-area region objects at this time
IOobjectList faObjects;
if (ensFaMeshPtr)
{
const word& areaName = ensFaMeshPtr->mesh().name();
faObjects = IOobjectList
(
faMesh::Registry(mesh),
runTime.timeName(),
polyMesh::regionName(areaName),
IOobjectOption::NO_REGISTER
);
faObjects.filterObjects
(
availableFaRegionObjectNames[regioni](areaName)
);
}
// The finiteArea fields
#include "convertAreaFields.H"
}
// The finiteArea fields
#include "convertAreaFields.H"
// Lagrangian fields
#include "convertLagrangian.H"
}
@ -604,14 +635,13 @@ int main(int argc, char *argv[])
// Write cases
forAll(ensightCases, regioni)
{
// finite-volume
ensightCases[regioni].write();
}
forAll(ensightCasesFa, regioni)
{
if (ensightCasesFa.set(regioni))
// Finite-area (if any)
for (const auto& ensFaCase : ensightCasesFa[regioni])
{
ensightCasesFa[regioni].write();
ensFaCase.write();
}
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2018-2023 OpenCFD Ltd.
Copyright (C) 2018-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,7 +26,6 @@ License
\*---------------------------------------------------------------------------*/
#include "readFields.H"
#include "volFields.H"
// * * * * * * * * * * * * * * * Global Functions * * * * * * * * * * * * * //
@ -42,26 +41,30 @@ Foam::label Foam::checkData
wordHashSet goodFields;
IOobject io
(
"any-name", // placeholder
"constant", // placeholder
local,
obr,
IOobjectOption::NO_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER
);
for (const word& fieldName : objectNames)
{
// // If prune_0() not previously used...
// if (objectNames.ends_with("_0")) continue;
// In case prune_0() not previously used...
if (fieldName.ends_with("_0")) continue;
bool good = false;
for (const instant& inst : timeDirs)
{
good =
IOobject
(
fieldName,
inst.name(),
local,
obr,
IOobject::NO_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
).typeHeaderOk<volScalarField>(false, false);
io.resetHeader(fieldName);
io.instance() = inst.name();
good = io.typeHeaderOk<regIOobject>(false, false);
if (!good)
{

View File

@ -22,24 +22,66 @@ Description
//
// No subsetting!
if (doFiniteArea)
if (doFiniteArea && !areaRegionNames.empty())
{
using reportFields = foamToVtkReportFields;
autoPtr<faMesh> faMeshPtr;
// The (region) polyMesh being used. No subsetting possible
const auto& basePolyMesh = meshProxy.baseMesh();
const label nAreaFields = faObjects.count(Foam::fieldTypes::is_area);
if (nAreaFields || withMeshIds)
for (const word& areaName : areaRegionNames)
{
faMeshPtr = faMesh::TryNew(meshProxy.baseMesh());
}
const bool isDefaultRegion(polyMesh::regionName(areaName).empty());
if (faMeshPtr && (nAreaFields || withMeshIds))
{
const faMesh& areaMesh = faMeshPtr();
// CAUTION
// If we want to have constant access to the HashTable:
//
// (SEGFAULT)
// const auto& faObjs = faObjects.lookup(areaName, IOobjectList());
//
// Use an empty fallback to avoid binding to a temporary:
//
// const IOobjectList emptyObjectList;
// const auto& faObjs = faObjects.lookup(areaName, emptyObjectList);
reportFields::area(Info, faObjects);
// Since we do not need the area fields afterwards,
// just move them out from the HashTable
IOobjectList faObjs;
if (auto iter = faObjects.find(areaName); iter.good())
{
faObjs = std::move(iter.val());
}
const label nAreaFields = faObjs.count(Foam::fieldTypes::is_area);
autoPtr<faMesh> faMeshPtr;
if (nAreaFields || withMeshIds)
{
faMeshPtr = faMesh::TryNew(areaName, basePolyMesh);
}
if (!faMeshPtr)
{
if (!isDefaultRegion)
{
// Report any area region specified but missing
// - silently ignore region0
Info<< "No area-mesh [" << polyMesh::regionName(areaName)
<< "] on volume-region ["
<< basePolyMesh.regionName() << "]" << endl;
}
continue;
}
const auto& areaMesh = faMeshPtr();
Info<< "Using area-mesh [" << polyMesh::regionName(areaName)
<< "] on volume-region ["
<< basePolyMesh.regionName() << "]" << endl;
reportFields::area(Info, faObjs);
const auto& pp = faMeshPtr->patch();
@ -49,7 +91,12 @@ if (doFiniteArea)
writeOpts,
(
outputDir/regionDir/"finite-area"
/ "finiteArea" + timeDesc
/ (
isDefaultRegion
? fileName("finiteArea")
: fileName(areaName/areaName)
)
+ timeDesc
),
UPstream::parRun()
);
@ -96,7 +143,7 @@ if (doFiniteArea)
(
writer,
areaMesh,
faObjects,
faObjs,
true // syncPar
);

View File

@ -135,13 +135,13 @@ Note
#include "emptyPolyPatch.H"
#include "volPointInterpolation.H"
#include "faceZoneMesh.H"
#include "faMesh.H"
#include "areaFields.H"
#include "fvMeshSubsetProxy.H"
#include "faceSet.H"
#include "pointSet.H"
#include "HashOps.H"
#include "regionProperties.H"
#include "stringListOps.H" // For stringListOps::findMatching()
#include "Cloud.H"
#include "readFields.H"
@ -434,6 +434,7 @@ int main(int argc, char *argv[])
argList::addOptionCompat("one-boundary", {"allPatches", 1806});
#include "addAllRegionOptions.H"
#include "addAllFaRegionOptions.H"
argList::addOption
(
@ -630,6 +631,16 @@ int main(int argc, char *argv[])
// Handle -allRegions, -regions, -region
#include "getAllRegionOptions.H"
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!doFiniteArea)
{
areaRegionNames.clear(); // For consistency
}
// ------------------------------------------------------------------------
// Names for sets and zones
word cellSelectionName;
word faceSetName;
@ -777,8 +788,11 @@ int main(int argc, char *argv[])
}
}
// fvMesh fields
IOobjectList objects;
IOobjectList faObjects;
// faMesh fields (multiple finite-area regions per volume region)
HashTable<IOobjectList> faObjects;
if (doConvertFields)
{
@ -786,33 +800,51 @@ int main(int argc, char *argv[])
objects =
IOobjectList(meshProxy.baseMesh(), runTime.timeName());
// List of area mesh objects (assuming single region)
faObjects =
IOobjectList
(
runTime,
runTime.timeName(),
faMesh::dbDir(meshProxy.baseMesh(), word::null),
IOobjectOption::NO_REGISTER
);
if (fieldSelector)
{
objects.filterObjects(fieldSelector);
faObjects.filterObjects(fieldSelector);
}
// Remove "*_0" restart fields
objects.prune_0();
faObjects.prune_0();
if (!doPointValues)
{
// Prune point fields if disabled
objects.filterClasses(Foam::fieldTypes::is_point, true);
}
// Lists of finite-area fields
faObjects.reserve(areaRegionNames.size());
for (const word& areaName : areaRegionNames)
{
// The finite-area objects for given area region.
// Add as method to faMeshesRegistry?
IOobjectList objs
(
faMesh::Registry(meshProxy.baseMesh()),
runTime.timeName(),
polyMesh::regionName(areaName),
IOobjectOption::NO_REGISTER
);
if (fieldSelector)
{
objs.filterObjects(fieldSelector);
}
// Remove "*_0" restart fields
objs.prune_0();
if (!objs.empty())
{
faObjects.emplace_set(areaName, std::move(objs));
}
}
}
if (processorFieldsOnly)
{
// Processor-patches only and continue

View File

@ -1,10 +1,10 @@
EXE_INC = \
-I$(LIB_SRC)/meshTools/lnInclude \
-I$(LIB_SRC)/finiteVolume/lnInclude \
-I$(LIB_SRC)/finiteArea/lnInclude \
-I$(LIB_SRC)/meshTools/lnInclude
-I$(LIB_SRC)/finiteArea/lnInclude
EXE_LIBS = \
-lmeshTools \
-lfiniteVolume \
-lfiniteArea \
-lmeshTools \
-lgenericPatchFields

View File

@ -6,7 +6,7 @@
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2022-2024 OpenCFD Ltd.
Copyright (C) 2022-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -47,6 +47,7 @@ Description
#include "areaFields.H"
#include "coupledFvPatch.H"
#include "coupledFaPatch.H"
#include "regionProperties.H"
using namespace Foam;
@ -86,17 +87,18 @@ bool consumeUnusedType(const fieldDescription& fieldDesc, Istream& is)
//? typedef GeometricField<Type, faePatchField, areaMesh> fieldType3;
//? typedef GeometricField<Type, fvsPatchField, volMesh> fieldType4;
if
const bool isExpectedType
(
fieldDesc.type() == fieldType1::typeName
|| fieldDesc.type() == fieldType2::typeName
)
);
if (isExpectedType)
{
(void) pTraits<Type>(is);
return true;
}
return false;
return isExpectedType;
}
@ -122,13 +124,18 @@ bool setCellFieldType
(
const fieldDescription& fieldDesc,
const fvMesh& mesh,
const labelList& selectedCells,
const labelUList& selectedCells,
Istream& is
)
{
typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
if (fieldDesc.type() != fieldType::typeName)
const bool isExpectedType
(
fieldDesc.type() == fieldType::typeName
);
if (!isExpectedType)
{
return false;
}
@ -143,7 +150,9 @@ bool setCellFieldType
fieldDesc.name(),
mesh.thisDb().time().timeName(),
mesh.thisDb(),
IOobject::MUST_READ
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER
);
bool found = fieldHeader.typeHeaderOk<fieldType>(true);
@ -151,13 +160,8 @@ bool setCellFieldType
if (!found)
{
// Fallback to "constant" directory
fieldHeader = IOobject
(
fieldDesc.name(),
mesh.thisDb().time().constant(),
mesh.thisDb(),
IOobject::MUST_READ
);
fieldHeader.resetHeader();
fieldHeader.instance() = mesh.thisDb().time().constant();
found = fieldHeader.typeHeaderOk<fieldType>(true);
}
@ -171,7 +175,7 @@ bool setCellFieldType
fieldType field(fieldHeader, mesh, false);
if (isNull(selectedCells) || selectedCells.size() == field.size())
if (isNull(selectedCells) || (selectedCells.size() == field.size()))
{
field.primitiveFieldRef() = fieldValue;
}
@ -204,7 +208,7 @@ bool setCellFieldType
<< "Field " << fieldDesc.name() << " not found" << endl;
}
return true;
return isExpectedType;
}
@ -216,13 +220,18 @@ bool setAreaFieldType
(
const fieldDescription& fieldDesc,
const faMesh& mesh,
const labelList& selectedFaces,
const labelUList& selectedFaces,
Istream& is
)
{
typedef GeometricField<Type, faPatchField, areaMesh> fieldType;
if (fieldDesc.type() != fieldType::typeName)
const bool isExpectedType
(
fieldDesc.type() == fieldType::typeName
);
if (!isExpectedType)
{
return false;
}
@ -237,7 +246,9 @@ bool setAreaFieldType
fieldDesc.name(),
mesh.thisDb().time().timeName(),
mesh.thisDb(),
IOobject::MUST_READ
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER
);
bool found = fieldHeader.typeHeaderOk<fieldType>(true);
@ -245,13 +256,8 @@ bool setAreaFieldType
if (!found)
{
// Fallback to "constant" directory
fieldHeader = IOobject
(
fieldDesc.name(),
mesh.thisDb().time().constant(),
mesh.thisDb(),
IOobject::MUST_READ
);
fieldHeader.resetHeader();
fieldHeader.instance() = mesh.thisDb().time().constant(),
found = fieldHeader.typeHeaderOk<fieldType>(true);
}
@ -292,7 +298,7 @@ bool setAreaFieldType
<< "Field " << fieldDesc.name() << " not found" << endl;
}
return true;
return isExpectedType;
}
@ -304,13 +310,18 @@ bool setFaceFieldType
(
const fieldDescription& fieldDesc,
const fvMesh& mesh,
const labelList& selectedFaces,
const labelUList& selectedFaces,
Istream& is
)
{
typedef GeometricField<Type, fvPatchField, volMesh> fieldType;
if (fieldDesc.type() != fieldType::typeName)
const bool isExpectedType
(
fieldDesc.type() == fieldType::typeName
);
if (!isExpectedType)
{
return false;
}
@ -325,7 +336,9 @@ bool setFaceFieldType
fieldDesc.name(),
mesh.thisDb().time().timeName(),
mesh.thisDb(),
IOobject::MUST_READ
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::NO_REGISTER
);
bool found = fieldHeader.typeHeaderOk<fieldType>(true);
@ -333,13 +346,8 @@ bool setFaceFieldType
if (!found)
{
// Fallback to "constant" directory
fieldHeader = IOobject
(
fieldDesc.name(),
mesh.thisDb().time().constant(),
mesh.thisDb(),
IOobject::MUST_READ
);
fieldHeader.resetHeader();
fieldHeader.instance() = mesh.thisDb().time().constant();
found = fieldHeader.typeHeaderOk<fieldType>(true);
}
@ -355,15 +363,14 @@ bool setFaceFieldType
// Create flat list of selected faces and their value.
Field<Type> allBoundaryValues(mesh.nBoundaryFaces());
forAll(field.boundaryField(), patchi)
for (const auto& pfld : field.boundaryField())
{
SubField<Type>
(
allBoundaryValues,
field.boundaryField()[patchi].size(),
field.boundaryField()[patchi].patch().start()
- mesh.nInternalFaces()
) = field.boundaryField()[patchi];
pfld.size(),
pfld.patch().offset()
) = pfld;
}
// Override
@ -424,8 +431,7 @@ bool setFaceFieldType
(
allBoundaryValues,
fieldBf[patchi].size(),
fieldBf[patchi].patch().start()
- mesh.nInternalFaces()
fieldBf[patchi].patch().offset()
);
}
}
@ -445,7 +451,7 @@ bool setFaceFieldType
<< "Field " << fieldDesc.name() << " not found" << endl;
}
return true;
return isExpectedType;
}
@ -460,7 +466,7 @@ struct setCellField
(
const fieldDescription& fieldDesc,
const fvMesh& m,
const labelList& selectedCells,
const labelUList& selectedCells,
Istream& is
)
{
@ -477,11 +483,11 @@ struct setCellField
class iNew
{
const fvMesh& mesh_;
const labelList& selected_;
const labelUList& selected_;
public:
iNew(const fvMesh& mesh, const labelList& selectedCells)
iNew(const fvMesh& mesh, const labelUList& selectedCells) noexcept
:
mesh_(mesh),
selected_(selectedCells)
@ -528,7 +534,7 @@ struct setFaceField
(
const fieldDescription& fieldDesc,
const fvMesh& m,
const labelList& selectedFaces,
const labelUList& selectedFaces,
Istream& is
)
{
@ -545,11 +551,11 @@ struct setFaceField
class iNew
{
const fvMesh& mesh_;
const labelList& selected_;
const labelUList& selected_;
public:
iNew(const fvMesh& mesh, const labelList& selectedFaces)
iNew(const fvMesh& mesh, const labelUList& selectedFaces) noexcept
:
mesh_(mesh),
selected_(selectedFaces)
@ -596,7 +602,7 @@ struct setAreaField
(
const fieldDescription& fieldDesc,
const faMesh& m,
const labelList& selectedFaces,
const labelUList& selectedFaces,
Istream& is
)
{
@ -613,11 +619,11 @@ struct setAreaField
class iNew
{
const faMesh& mesh_;
const labelList& selected_;
const labelUList& selected_;
public:
iNew(const faMesh& mesh, const labelList& selectedFaces)
iNew(const faMesh& mesh, const labelUList& selectedFaces) noexcept
:
mesh_(mesh),
selected_(selectedFaces)
@ -675,6 +681,7 @@ int main(int argc, char *argv[])
);
#include "addRegionOption.H"
#include "addAllFaRegionOptions.H"
// -------------------------
@ -686,15 +693,59 @@ int main(int argc, char *argv[])
#include "createNamedMesh.H"
autoPtr<faMesh> faMeshPtr;
// Handle -all-area-regions, -area-regions, -area-region
#include "getAllFaRegionOptions.H"
if (!args.found("no-finite-area"))
if (args.found("no-finite-area"))
{
faMeshPtr = faMesh::TryNew(mesh);
areaRegionNames.clear(); // For consistency
}
if (faMeshPtr)
// ------------------------------------------------------------------------
PtrList<faMesh> faMeshes;
// Setup all area meshes on this region
if (!areaRegionNames.empty()) // ie, !args.found("no-finite-area")
{
Info<< "Detected finite-area mesh" << nl;
faMeshes.resize(areaRegionNames.size());
label nGoodRegions(0);
for (const word& areaName : areaRegionNames)
{
autoPtr<faMesh> faMeshPtr = faMesh::TryNew(areaName, mesh);
if (faMeshPtr)
{
faMeshes.set(nGoodRegions++, std::move(faMeshPtr));
}
}
faMeshes.resize(nGoodRegions);
}
if (faMeshes.size() == 1)
{
Info<< "Using finite-area mesh";
if
(
const word& name = polyMesh::regionName(faMeshes[0].name());
!name.empty()
)
{
Info<< " [" << name << "]";
}
Info<< nl;
}
else if (faMeshes.size() > 1)
{
Info<< "Detected finite-area meshes:";
for (const faMesh& areaMesh : faMeshes)
{
Info<< " [" << areaMesh.name() << "]";
}
Info<< nl;
}
const word dictName("setFieldsDict");
@ -712,37 +763,45 @@ int main(int argc, char *argv[])
// Default field values
if
(
const auto* eptr
= setFieldsDict.findEntry("defaultFieldValues", keyType::LITERAL)
)
{
const entry* eptr =
setFieldsDict.findEntry("defaultFieldValues", keyType::LITERAL);
ITstream& is = eptr->stream();
if (eptr)
Info<< "Setting volume field default values" << endl;
PtrList<setCellField> defaultFieldValues
(
is,
setCellField::iNew(mesh, labelList::null())
);
for (const faMesh& areaMesh : faMeshes)
{
ITstream& is = eptr->stream();
Info<< "Setting area field default values";
Info<< "Setting volume field default values" << endl;
PtrList<setCellField> defaultFieldValues
if
(
is,
setCellField::iNew(mesh, labelList::null())
);
if (faMeshPtr)
const word& name = polyMesh::regionName(areaMesh.name());
!name.empty()
)
{
const faMesh& areaMesh = faMeshPtr();
is.rewind();
Info<< "Setting area field default values" << endl;
PtrList<setAreaField> defaultFieldValues
(
is,
setAreaField::iNew(areaMesh, labelList::null())
);
Info<< " [" << name << "]";
}
Info<< endl;
is.rewind();
PtrList<setAreaField> defaultAreaFieldValues
(
is,
setAreaField::iNew(areaMesh, labelList::null())
);
}
Info<< endl;
}
@ -768,11 +827,15 @@ int main(int argc, char *argv[])
labelList selectedCells(subset.sortedToc());
Info<< " Selected "
<< returnReduce(selectedCells.size(), sumOp<label>())
<< '/'
<< returnReduce(mesh.nCells(), sumOp<label>())
<< " cells" << nl;
{
FixedList<label, 2> stats;
stats[0] = selectedCells.size();
stats[1] = mesh.nCells();
reduce(stats, sumOp<label>());
Info<< " Selected " << stats[0] << '/' << stats[1]
<< " cells" << nl;
}
ITstream& is = region.dict().lookup("fieldValues");
@ -806,10 +869,8 @@ int main(int argc, char *argv[])
setFaceField::iNew(mesh, selectedFaces)
);
if (faMeshPtr)
for (const faMesh& areaMesh : faMeshes)
{
const faMesh& areaMesh = faMeshPtr();
const labelUList& faceLabels = areaMesh.faceLabels();
// Transcribe from mesh faces to finite-area addressing
@ -828,11 +889,15 @@ int main(int argc, char *argv[])
}
areaFaces.resize(nUsed);
Info<< " Selected "
<< returnReduce(areaFaces.size(), sumOp<label>())
<< '/'
<< returnReduce(faceLabels.size(), sumOp<label>())
<< " area faces" << nl;
{
FixedList<label, 2> stats;
stats[0] = areaFaces.size();
stats[1] = faceLabels.size();
reduce(stats, sumOp<label>());
Info<< " Selected " << stats[0] << '/' << stats[1]
<< " area faces for " << areaMesh.name() << endl;
}
is.rewind();

View File

@ -855,7 +855,7 @@ bool Foam::UPstream::finishedRequest(const label i)
// This allows MPI to progress behind the scenes if it wishes.
int flag = 0;
if (i >= 0 && i < PstreamGlobals::outstandingRequests_.size())
if (i < 0 || i >= PstreamGlobals::outstandingRequests_.size())
{
auto& request = PstreamGlobals::outstandingRequests_[i];

View File

@ -218,11 +218,9 @@ tmp<vectorField> atmBoundaryLayer::U(const vectorField& pCf) const
const scalar groundMin = zDir() & ppMin_;
// (YGCJ:Table 1, RH:Eq. 6, HW:Eq. 5)
scalarField zEff(max((zDir() & pCf) - groundMin - d + z0, z0));
scalarField Un
(
(Ustar(z0)/kappa_)*log(zEff/z0)
(Ustar(z0)/kappa_)*log(((zDir() & pCf) - groundMin - d + z0)/z0)
);
return flowDir()*Un;
@ -237,9 +235,9 @@ tmp<scalarField> atmBoundaryLayer::k(const vectorField& pCf) const
const scalar groundMin = zDir() & ppMin_;
// (YGCJ:Eq. 21; RH:Eq. 7, HW:Eq. 6 when C1=0 and C2=1)
scalarField zEff(max((zDir() & pCf) - groundMin - d + z0, z0));
return sqr(Ustar(z0))/sqrt(Cmu_)*sqrt(C1_*log(zEff/z0) + C2_);
return
sqr(Ustar(z0))/sqrt(Cmu_)
*sqrt(C1_*log(((zDir() & pCf) - groundMin - d + z0)/z0) + C2_);
}
@ -251,9 +249,9 @@ tmp<scalarField> atmBoundaryLayer::epsilon(const vectorField& pCf) const
const scalar groundMin = zDir() & ppMin_;
// (YGCJ:Eq. 22; RH:Eq. 8, HW:Eq. 7 when C1=0 and C2=1)
scalarField zEff(max((zDir() & pCf) - groundMin - d + z0, z0));
return pow3(Ustar(z0))/(kappa_*zEff)*sqrt(C1_*log(zEff/z0) + C2_);
return
pow3(Ustar(z0))/(kappa_*((zDir() & pCf) - groundMin - d + z0))
*sqrt(C1_*log(((zDir() & pCf) - groundMin - d + z0)/z0) + C2_);
}
@ -265,9 +263,7 @@ tmp<scalarField> atmBoundaryLayer::omega(const vectorField& pCf) const
const scalar groundMin = zDir() & ppMin_;
// (YGJ:Eq. 13)
scalarField zEff(max((zDir() & pCf) - groundMin - d + z0, z0));
return Ustar(z0)/(kappa_*sqrt(Cmu_)*zEff);
return Ustar(z0)/(kappa_*sqrt(Cmu_)*((zDir() & pCf) - groundMin - d + z0));
}

View File

@ -9,7 +9,7 @@ faceSetOption/faceSetOption.C
derivedSources=sources/derived
$(derivedSources)/externalHeatFluxSource/externalHeatFluxSource.C
$(derivedSources)/jouleHeatingSource/jouleHeatingSource.C
$(derivedSources)/jouleHeatingSource/jouleHeatingSource.cxx
$(derivedSources)/contactHeatFluxSource/contactHeatFluxSource.C
$(derivedSources)/externalFileSource/externalFileSource.C

View File

@ -81,6 +81,33 @@ Foam::fa::option::option
active_(dict.getOrDefault("active", true)),
log(true)
{
// Suffix hint for variable names
if
(
coeffs_.readIfPresent("suffixing", suffixHint_)
|| dict.readIfPresent("suffixing", suffixHint_)
)
{
Switch sw = Switch::find(suffixHint_);
if (sw.good())
{
if (!sw) // No suffix
{
suffixHint_.clear();
}
}
else if (suffixHint_ == "default")
{
sw = true;
}
if (sw) // Default suffix
{
suffixHint_ = '_' + regionName_;
}
}
if (dict.readIfPresent("area", areaName_))
{
if (!sameRegionNames(areaName_, defaultAreaName))
@ -95,9 +122,14 @@ Foam::fa::option::option
}
}
Log << incrIndent << indent << "Source: " << name_
<< " [" << polyMesh::regionName(areaName_) << ']' << endl
<< decrIndent;
Log << incrIndent << indent << "Source: " << name_;
if (!polyMesh::regionName(areaName_).empty())
{
Log<< " [" << areaName_ << ']';
}
Info<< decrIndent << endl;
}
@ -117,13 +149,20 @@ Foam::autoPtr<Foam::fa::option> Foam::fa::option::New
coeffs.readIfPresent("area", areaName);
Info<< indent
<< "Selecting finite-area option, type " << modelType
<< " [" << polyMesh::regionName(areaName) << ']';
<< "Selecting finite-area option, type " << modelType;
if (!sameRegionNames(areaName, defaultAreaName))
if (sameRegionNames(areaName, defaultAreaName))
{
Info<< " != " << defaultAreaName << nl;
if (!polyMesh::regionName(areaName).empty())
{
Info<< " [" << areaName << ']';
}
}
else
{
Info<< " [" << areaName << "] != [" << defaultAreaName << ']';
}
Info<< endl;
mesh.time().libs().open

View File

@ -65,10 +65,13 @@ Usage
--> the selected faOption settings | dictionary | no | -
active | Flag to (de)activate faOption | bool | no | true
log | Flag to log faOption-related info | bool | no | true
suffixing | Suffix hint for option variables | word | no | -
\endtable
Note
- Source/sink options are to be added to the right-hand side of equations.
- Suffixing (true|false|none|default|...) currently selects between
no suffix and ('_'+region).
SourceFiles
faOption.C
@ -136,6 +139,9 @@ protected:
//- The model region name (finite-area)
word regionName_;
//- Suffix hint for variable names (default: "")
word suffixHint_;
// Protected Member Functions
@ -273,7 +279,7 @@ public:
//- The source name
const word& name() const noexcept { return name_; }
//- Return const access to the mesh database
//- Return const access to the volume mesh
const fvMesh& mesh() const noexcept { return mesh_; }
//- Return dictionary
@ -304,6 +310,18 @@ public:
inline bool active(bool on) noexcept;
// Helper Functions
//- The suffix hint when generating variable names
const word& suffixHint() const noexcept { return suffixHint_; }
//- Return the concatenation of \p base and the suffix hint
inline word suffixed(const char* base) const;
//- Return the concatenation of \p base and the suffix hint
inline word suffixed(const std::string& base) const;
// Checks
//- Is the source active?

View File

@ -61,4 +61,18 @@ inline const Foam::volSurfaceMapping& Foam::fa::option::vsm() const
}
inline Foam::word
Foam::fa::option::suffixed(const char* base) const
{
return word(base + suffixHint_);
}
inline Foam::word
Foam::fa::option::suffixed(const std::string& base) const
{
return word(base + suffixHint_);
}
// ************************************************************************* //

View File

@ -27,7 +27,6 @@ License
#include "faOptions.H"
#include "faMesh.H"
#include "faMeshesRegistry.H"
#include "Time.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -68,7 +67,7 @@ Foam::IOobject createIOobject
lookupName,
mesh.time().constant(),
// located under finite-area
faMeshesRegistry::New(mesh).thisDb(),
faMesh::Registry(mesh),
IOobjectOption::MUST_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::REGISTER
@ -188,11 +187,7 @@ Foam::fa::options& Foam::fa::options::New
);
// Registered under finite-area?
auto* ptr =
faMeshesRegistry::New(mesh).thisDb().getObjectPtr<fa::options>
(
lookupName
);
auto* ptr = faMesh::Registry(mesh).getObjectPtr<fa::options>(lookupName);
if (!ptr && polyMesh::regionName(defaultAreaName).empty())
{

View File

@ -30,7 +30,6 @@ License
#include "addToRunTimeSelectionTable.H"
#include "volFields.H"
#include "famSup.H"
#include "zeroGradientFaPatchFields.H"
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
@ -57,13 +56,10 @@ Foam::fa::contactHeatFluxSource::contactHeatFluxSource
:
fa::faceSetOption(sourceName, modelType, dict, mesh, defaultAreaName),
TName_(dict.getOrDefault<word>("T", "T")),
TprimaryName_(dict.get<word>("Tprimary")),
TprimaryName_(dict.getOrDefault<word>("Tprimary", "T")),
Tprimary_(mesh_.lookupObject<volScalarField>(TprimaryName_)),
thicknessLayers_(),
kappaLayers_(),
contactRes_(0),
curTimeIndex_(-1),
coupling_()
curTimeIndex_(-1)
{
fieldNames_.resize(1, TName_);
@ -131,30 +127,30 @@ void Foam::fa::contactHeatFluxSource::addSup
const label fieldi
)
{
if (isActive())
if (!isActive())
{
DebugInfo
<< name() << ": applying source to "
<< eqn.psi().name() << endl;
return;
}
if (curTimeIndex_ != mesh().time().timeIndex())
{
tmp<DimensionedField<scalar, areaMesh>> htcw(htc());
DebugInfo<< name() << ": applying source to " << eqn.psi().name() << endl;
// Wall temperature - mapped from primary field to finite-area
auto Twall = DimensionedField<scalar, areaMesh>::New
(
"Tw_" + option::name(),
regionMesh(),
dimensionedScalar(dimTemperature, Zero)
);
if (curTimeIndex_ != mesh().time().timeIndex())
{
tmp<DimensionedField<scalar, areaMesh>> htcw(htc());
vsm().mapInternalToSurface<scalar>(Tprimary_, Twall.ref().field());
// Wall temperature - mapped from primary field to finite-area
auto Twall = DimensionedField<scalar, areaMesh>::New
(
"Tw_" + option::name(),
regionMesh(),
dimensionedScalar(dimTemperature, Zero)
);
eqn += -fam::Sp(htcw(), eqn.psi()) + htcw()*Twall;
vsm().mapInternalToSurface<scalar>(Tprimary_, Twall.ref().field());
curTimeIndex_ = mesh().time().timeIndex();
}
eqn += -fam::Sp(htcw(), eqn.psi()) + htcw()*Twall;
curTimeIndex_ = mesh().time().timeIndex();
}
}
@ -190,8 +186,14 @@ bool Foam::fa::contactHeatFluxSource::read(const dictionary& dict)
const labelList& patches = regionMesh().whichPolyPatches();
coupling_.clear();
coupling_.resize(patches.empty() ? 0 : (patches.last()+1));
if (patches.empty())
{
coupling_.clear();
}
else
{
coupling_.resize_null(patches.back()+1);
}
for (const label patchi : patches)
{

View File

@ -40,6 +40,8 @@ Usage
{
// Mandatory entries (unmodifiable)
type contactHeatFluxSource;
// Optional entries (unmodifiable)
Tprimary <TprimaryFieldName>;
// Optional entries (runtime modifiable)
@ -60,12 +62,19 @@ Usage
\table
Property | Description | Type | Reqd | Dflt
type | Type name: contactHeatFluxSource | word | yes | -
Tprimary | Name of primary temperature field | word | yes | -
T | Name of operand temperature field | word | no | T
Tprimary | Name of primary temperature field | word | no | T
thicknessLayers | List of thicknesses of layers | scalarList | no | -
kappaLayers | List of conductivities of layers | scalarList | cndtnl | -
\endtable
Fields/variables used:
\table
Property | Description | Type | Deflt
T | Temperature | shell | T
Tprimary | Temperature | volume | T
\endtable
The inherited entries are elaborated in:
- \link faOption.H \endlink
- \link faceSetOption.H \endlink
@ -124,13 +133,13 @@ class contactHeatFluxSource
// Private Data
//- Name of temperature field
//- Name of shell temperature field (default: "T")
word TName_;
//- Name of primary temperature field
word TprimaryName_;
//- Name of volume temperature field (default: "T")
const word TprimaryName_;
//- Primary region temperature
//- Primary (volume) region temperature
const volScalarField& Tprimary_;
//- Thickness of layers

View File

@ -72,10 +72,6 @@ Foam::fa::externalHeatFluxSource::externalHeatFluxSource
fa::faceSetOption(sourceName, modelType, dict, m, defaultAreaName),
mode_(operationModeNames.get("mode", dict)),
TName_(dict.getOrDefault<word>("T", "T")),
Q_(nullptr),
q_(nullptr),
h_(nullptr),
Ta_(nullptr),
emissivity_(dict.getOrDefault<scalar>("emissivity", 0))
{
fieldNames_.resize(1, TName_);
@ -205,6 +201,11 @@ bool Foam::fa::externalHeatFluxSource::read(const dictionary& dict)
mode_ = operationModeNames.get("mode", dict);
Q_.reset(nullptr);
q_.reset(nullptr);
h_.reset(nullptr);
Ta_.reset(nullptr);
switch (mode_)
{
case fixedPower:

View File

@ -103,7 +103,7 @@ Usage
\verbatim
power | Use fixed power (supply Q)
flux | Use fixed heat flux (supply q)
coefficient | Use fixes heat transfer coefficient (supply h and T)
coefficient | Use fixes heat transfer coefficient (supply h and Ta)
\endverbatim
See also
@ -159,7 +159,7 @@ private:
//- Operation mode
operationMode mode_;
//- Name of temperature field
//- Name of shell temperature field (default: "T")
word TName_;
//- Heat power [W]
@ -174,7 +174,7 @@ private:
//- Ambient temperature [K]
autoPtr<Function1<scalar>> Ta_;
//- Optional surface emissivity for radiative transfer to ambient
//- Surface emissivity for radiative transfer to ambient (default: 0)
scalar emissivity_;

View File

@ -115,23 +115,30 @@ Usage
- \link faceSetOption.H \endlink
Note
- \c anisotropicElectricalConductivity=true enables
anisotropic (tensorial) electrical conductivity.
- \c anisotropicElectricalConductivity=false enables
isotropic (scalar) electrical conductivity.
- The electrical conductivity can be specified using either:
- If the \c sigma entry is present the electrical conductivity is specified
as a function of temperature using a \c Function1 type.
- If not present the \c sigma field will be read from file.
- If the \c anisotropicElectricalConductivity flag is set to \c true,
\c sigma should be specified as a tensor quantity.
If the \c sigma entry is present, the electrical conductivity is specified
as a function of temperature using a \c Function1 type, otherwise
the \c sigma field will be read from file.
When the \c anisotropicElectricalConductivity flag is set to \c true,
\c sigma should be specified as a \em tensor quantity instead of as
an isotropic \em scalar quantity.
BREAKING Naming changes from 2056 and earlier for the fields:
\table
Field | Scoped names | Scoped names (old)
V | \<scope\>:V (suffix) | \<scope\>:V_ + regionName
sigma | \<scope\>:sigma (suffix) | \<scope\>:sigma_ + regionName
\endtable
It is possible to replicate the older naming by specifying
the \c suffixing to ('_' + regionName).
See also
- Foam::Function1
- Foam::fv::jouleHeatingSource
SourceFiles
jouleHeatingSource.C
jouleHeatingSourceTemplates.C
jouleHeatingSource.cxx
jouleHeatingSourceImpl.cxx
\*---------------------------------------------------------------------------*/
@ -188,13 +195,13 @@ class jouleHeatingSource
void initialiseSigma
(
const dictionary& dict,
autoPtr<Function1<Type>>& sigmaVsTPtr
autoPtr<Function1<Type>>& sigmaFunctionPtr
);
//- Update the electrical conductivity field
template<class Type>
const GeometricField<Type, faPatchField, areaMesh>&
updateSigma(const autoPtr<Function1<Type>>& sigmaVsTPtr) const;
updateSigma(const autoPtr<Function1<Type>>& sigmaFunctionPtr) const;
public:
@ -229,22 +236,22 @@ public:
// Member Functions
// Evaluation
// Evaluation
//- Add explicit contribution to compressible momentum equation
virtual void addSup
(
const areaScalarField& h,
const areaScalarField& rho,
faMatrix<scalar>& eqn,
const label fieldi
);
//- Add explicit contribution to energy equation
virtual void addSup
(
const areaScalarField& h,
const areaScalarField& rho,
faMatrix<scalar>& eqn,
const label fieldi
);
// IO
// IO
//- Read source dictionary
virtual bool read(const dictionary& dict);
//- Read source dictionary
virtual bool read(const dictionary& dict);
};
@ -255,12 +262,6 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "jouleHeatingSourceTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -42,6 +42,12 @@ namespace fa
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Implementation
#include "jouleHeatingSourceImpl.cxx"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::fa::jouleHeatingSource::jouleHeatingSource
@ -59,7 +65,7 @@ Foam::fa::jouleHeatingSource::jouleHeatingSource
(
IOobject
(
IOobject::scopedName(typeName, "V_" + regionName_),
suffixed(IOobject::scopedName(typeName, "V")),
regionMesh().thisDb().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
@ -68,8 +74,6 @@ Foam::fa::jouleHeatingSource::jouleHeatingSource
),
regionMesh()
),
scalarSigmaVsTPtr_(nullptr),
tensorSigmaVsTPtr_(nullptr),
curTimeIndex_(-1),
nIter_(1),
anisotropicElectricalConductivity_(false)
@ -78,6 +82,8 @@ Foam::fa::jouleHeatingSource::jouleHeatingSource
fa::option::resetApplied();
read(dict);
if (anisotropicElectricalConductivity_)
{
Info<< " Using tensor electrical conductivity" << endl;
@ -90,8 +96,6 @@ Foam::fa::jouleHeatingSource::jouleHeatingSource
initialiseSigma(coeffs_, scalarSigmaVsTPtr_);
}
read(dict);
}
@ -105,12 +109,14 @@ void Foam::fa::jouleHeatingSource::addSup
const label fieldi
)
{
if (isActive())
if (!isActive())
{
DebugInfo
<< name() << ": applying source to "
<< eqn.psi().name() << endl;
return;
}
DebugInfo<< name() << ": applying source to " << eqn.psi().name() << endl;
{
if (curTimeIndex_ != mesh().time().timeIndex())
{
for (label i = 0; i < nIter_; ++i)
@ -118,8 +124,7 @@ void Foam::fa::jouleHeatingSource::addSup
if (anisotropicElectricalConductivity_)
{
// Update sigma as a function of T if required
const areaTensorField& sigma =
updateSigma(tensorSigmaVsTPtr_);
const auto& sigma = updateSigma(tensorSigmaVsTPtr_);
// Solve the electrical potential equation
faScalarMatrix VEqn(fam::laplacian(h*sigma, V_));
@ -129,8 +134,7 @@ void Foam::fa::jouleHeatingSource::addSup
else
{
// Update sigma as a function of T if required
const areaScalarField& sigma =
updateSigma(scalarSigmaVsTPtr_);
const auto& sigma = updateSigma(scalarSigmaVsTPtr_);
// Solve the electrical potential equation
faScalarMatrix VEqn(fam::laplacian(h*sigma, V_));
@ -145,7 +149,7 @@ void Foam::fa::jouleHeatingSource::addSup
// Add the Joule heating contribution
const word sigmaName
(
IOobject::scopedName(typeName, "sigma_" + regionName_)
IOobject::scopedName(typeName, "sigma") + suffixHint()
);
areaVectorField gradV("gradV", fac::grad(V_));
@ -162,15 +166,13 @@ void Foam::fa::jouleHeatingSource::addSup
if (anisotropicElectricalConductivity_)
{
const auto& sigma =
obr.lookupObject<areaTensorField>(sigmaName);
const auto& sigma = obr.lookupObject<areaTensorField>(sigmaName);
tsource = (h*sigma & gradV) & gradV;
}
else
{
const auto& sigma =
obr.lookupObject<areaScalarField>(sigmaName);
const auto& sigma = obr.lookupObject<areaScalarField>(sigmaName);
tsource = (h*sigma*gradV) & gradV;
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2024 OpenCFD Ltd.
Copyright (C) 2019-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -33,16 +33,21 @@ template<class Type>
void Foam::fa::jouleHeatingSource::initialiseSigma
(
const dictionary& dict,
autoPtr<Function1<Type>>& sigmaVsTPtr
autoPtr<Function1<Type>>& sigmaFunctionPtr
)
{
typedef GeometricField<Type, faPatchField, areaMesh> FieldType;
const word sigmaName
(
IOobject::scopedName(typeName, "sigma") + suffixHint()
);
auto& obr = regionMesh().thisDb();
IOobject io
(
IOobject::scopedName(typeName, "sigma_" + regionName_),
sigmaName,
obr.time().timeName(),
obr,
IOobject::NO_READ,
@ -52,11 +57,11 @@ void Foam::fa::jouleHeatingSource::initialiseSigma
autoPtr<FieldType> tsigma;
if (dict.found("sigma"))
{
// Sigma to be defined using a Function1 type
sigmaVsTPtr = Function1<Type>::New("sigma", dict, &mesh_);
// Is sigma defined using a Function1 type?
sigmaFunctionPtr = Function1<Type>::NewIfPresent("sigma", dict, &mesh_);
if (sigmaFunctionPtr)
{
tsigma.reset
(
new FieldType
@ -89,31 +94,34 @@ template<class Type>
const Foam::GeometricField<Type, Foam::faPatchField, Foam::areaMesh>&
Foam::fa::jouleHeatingSource::updateSigma
(
const autoPtr<Function1<Type>>& sigmaVsTPtr
const autoPtr<Function1<Type>>& sigmaFunctionPtr
) const
{
typedef GeometricField<Type, faPatchField, areaMesh> FieldType;
const word sigmaName
(
IOobject::scopedName(typeName, "sigma") + suffixHint()
);
const auto& obr = regionMesh().thisDb();
auto& sigma =
obr.lookupObjectRef<FieldType>
(
IOobject::scopedName(typeName, "sigma_" + regionName_)
);
auto& sigma = obr.lookupObjectRef<FieldType>(sigmaName);
if (!sigmaVsTPtr)
if (!sigmaFunctionPtr)
{
// Electrical conductivity field, sigma, was specified by the user
return sigma;
}
const auto& sigmaFunction = sigmaFunctionPtr();
const auto& T = obr.lookupObject<areaScalarField>(TName_);
// Internal field
forAll(sigma, i)
{
sigma[i] = sigmaVsTPtr->value(T[i]);
sigma[i] = sigmaFunction.value(T[i]);
}
@ -127,7 +135,7 @@ Foam::fa::jouleHeatingSource::updateSigma
const scalarField& Tbf = T.boundaryField()[patchi];
forAll(pf, facei)
{
pf[facei] = sigmaVsTPtr->value(Tbf[facei]);
pf[facei] = sigmaFunction.value(Tbf[facei]);
}
}
}

View File

@ -28,6 +28,7 @@ License
#include "faMesh.H"
#include "faMeshBoundaryHalo.H"
#include "faMeshesRegistry.H"
#include "faGlobalMeshData.H"
#include "Time.H"
#include "polyMesh.H"
@ -159,6 +160,12 @@ const Foam::objectRegistry* Foam::faMesh::registry(const polyMesh& pMesh)
// return obr.cfindObject<objectRegistry>(faMesh::prefix());
// }
// Forwarding
const Foam::objectRegistry& Foam::faMesh::Registry(const polyMesh& pMesh)
{
return faMeshesRegistry::Registry(pMesh);
}
const Foam::faMesh& Foam::faMesh::mesh
(

View File

@ -751,10 +751,14 @@ public:
// Database
//- The parent registry containing all finite-area meshes
//- on the polyMesh.
//- Find the singleton parent registry (on the polyMesh)
//- that contains all objects related to finite-area.
static const objectRegistry* registry(const polyMesh& pMesh);
//- Return the singleton parent registry (on the polyMesh)
//- that contains all objects related to finite-area.
static const objectRegistry& Registry(const polyMesh& pMesh);
//- The single-region finite-area region on the polyMesh.
//- Uses lookupObject semantics - Fatal if non-existent
static const faMesh& mesh(const polyMesh& pMesh);

View File

@ -27,8 +27,8 @@ License
#include "faMesh.H"
#include "faMeshesRegistry.H"
#include "Time.H"
#include "polyMesh.H"
#include "Time.H"
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
@ -43,6 +43,7 @@ Foam::faMeshRegistry::faMeshRegistry
IOobject
(
(areaName.empty() ? polyMesh::defaultRegion : areaName),
mesh.thisDb().time().timeName(),
faMeshesRegistry::New(mesh).thisDb(),
IOobjectOption::NO_READ,
IOobjectOption::AUTO_WRITE,

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023-2024 OpenCFD Ltd.
Copyright (C) 2023-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -86,6 +86,8 @@ bool Foam::faMeshesRegistry::writeObject
const bool writeOnProc
) const
{
// Could also restrict to faMesh only...
//
// for (const faMesh& m : objects_.csorted<faMesh>())
// {
// m.write(writeOnProc);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2023-2024 OpenCFD Ltd.
Copyright (C) 2023-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -144,14 +144,34 @@ public:
explicit faMeshesRegistry(const polyMesh& mesh);
// Factory Methods
//- Return the registry of objects on the singleton.
// Same as New(mesh).thisDb()
FOAM_NO_DANGLING_REFERENCE //< Reference stored in registry
static const objectRegistry& Registry(const polyMesh& mesh)
{
return MeshObject_type::New(mesh).thisDb();
}
// Database
//- Return the object registry
// It redirects to the private objects but uses some
// objectRegistry method naming
//- The registry of the objects
const objectRegistry& thisDb() const noexcept
{
return objects_;
}
//- Local relative to time
const fileName& dbDir() const
{
return objects_.dbDir();
}
//- The polyMesh reference
const polyMesh& mesh() const noexcept
{

View File

@ -14,7 +14,7 @@ $(derivedSources)/buoyancyEnergy/buoyancyEnergy.C
$(derivedSources)/buoyancyForce/buoyancyForce.C
$(derivedSources)/directionalPressureGradientExplicitSource/directionalPressureGradientExplicitSource.C
$(derivedSources)/explicitPorositySource/explicitPorositySource.C
$(derivedSources)/jouleHeatingSource/jouleHeatingSource.C
$(derivedSources)/jouleHeatingSource/jouleHeatingSource.cxx
$(derivedSources)/meanVelocityForce/meanVelocityForce.C
$(derivedSources)/meanVelocityForce/patchMeanVelocityForce/patchMeanVelocityForce.C
$(derivedSources)/multiphaseStabilizedTurbulence/multiphaseStabilizedTurbulence.C

View File

@ -137,25 +137,28 @@ Note
anisotropic (vectorial) electrical conductivity.
- \c anisotropicElectricalConductivity=false enables
isotropic (scalar) electrical conductivity.
- The electrical conductivity can be specified using either:
The electrical conductivity can be specified using either:
- If the \c sigma entry is present the electrical conductivity is specified
as a function of temperature using a \c Function1 type.
- If not present the \c sigma field will be read from file.
- If the \c anisotropicElectricalConductivity flag is set to \c true,
\c sigma should be specified as a vector quantity.
.
See also
- Foam::Function1
- Foam::coordSystem
- Foam::fa::jouleHeatingSource
SourceFiles
jouleHeatingSource.C
jouleHeatingSourceTemplates.C
jouleHeatingSource.cxx
jouleHeatingSourceImpl.cxx
\*---------------------------------------------------------------------------*/
#ifndef fv_jouleHeatingSource_H
#define fv_jouleHeatingSource_H
#ifndef Foam_fv_jouleHeatingSource_H
#define Foam_fv_jouleHeatingSource_H
#include "fvOption.H"
#include "Function1.H"
@ -185,9 +188,6 @@ class jouleHeatingSource
//- Electrical potential field / [V]
volScalarField V_;
//- Flag to indicate that the electrical conductivity is anisotropic
bool anisotropicElectricalConductivity_;
//- Electrical conductivity as a scalar function of temperature
autoPtr<Function1<scalar>> scalarSigmaVsTPtr_;
@ -200,6 +200,9 @@ class jouleHeatingSource
//- Current time index (used for updating)
label curTimeIndex_;
//- Flag to indicate that the electrical conductivity is anisotropic
bool anisotropicElectricalConductivity_;
// Private Member Functions
@ -214,13 +217,13 @@ class jouleHeatingSource
void initialiseSigma
(
const dictionary& dict,
autoPtr<Function1<Type>>& sigmaVsTPtr
autoPtr<Function1<Type>>& sigmaFunctionPtr
);
//- Update the electrical conductivity field
template<class Type>
const GeometricField<Type, fvPatchField, volMesh>&
updateSigma(const autoPtr<Function1<Type>>& sigmaVsTPtr) const;
updateSigma(const autoPtr<Function1<Type>>& sigmaFunctionPtr) const;
public:
@ -253,21 +256,21 @@ public:
// Member Functions
// Evaluation
// Evaluation
//- Add explicit contribution to compressible momentum equation
virtual void addSup
(
const volScalarField& rho,
fvMatrix<scalar>& eqn,
const label fieldi
);
//- Add explicit contribution to energy equation
virtual void addSup
(
const volScalarField& rho,
fvMatrix<scalar>& eqn,
const label fieldi
);
// IO
// IO
//- Read source dictionary
virtual bool read(const dictionary& dict);
//- Read source dictionary
virtual bool read(const dictionary& dict);
};
@ -278,12 +281,6 @@ public:
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#ifdef NoRepository
#include "jouleHeatingSourceTemplates.C"
#endif
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -29,7 +29,6 @@ License
#include "fvMatrices.H"
#include "fvmLaplacian.H"
#include "fvcGrad.H"
#include "zeroGradientFvPatchField.H"
#include "basicThermo.H"
#include "addToRunTimeSelectionTable.H"
@ -45,6 +44,12 @@ namespace fv
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Implementation
#include "jouleHeatingSourceImpl.cxx"
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::tmp<Foam::volSymmTensorField>
@ -115,11 +120,8 @@ Foam::fv::jouleHeatingSource::jouleHeatingSource
),
mesh
),
anisotropicElectricalConductivity_(false),
scalarSigmaVsTPtr_(nullptr),
vectorSigmaVsTPtr_(nullptr),
csysPtr_(nullptr),
curTimeIndex_(-1)
curTimeIndex_(-1),
anisotropicElectricalConductivity_(false)
{
// Set the field name to that of the energy
// field from which the temperature is obtained
@ -162,7 +164,7 @@ void Foam::fv::jouleHeatingSource::addSup
else
{
// Update sigma as a function of T if required
const volScalarField& sigma = updateSigma(scalarSigmaVsTPtr_);
const auto& sigma = updateSigma(scalarSigmaVsTPtr_);
// Solve the electrical potential equation
fvScalarMatrix VEqn(fvm::laplacian(sigma, V_));
@ -226,7 +228,7 @@ bool Foam::fv::jouleHeatingSource::read(const dictionary& dict)
initialiseSigma(coeffs_, scalarSigmaVsTPtr_);
csysPtr_.clear(); // Do not need coordinate system
csysPtr_.reset(nullptr); // Do not need coordinate system
}
return true;

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2016-2024 OpenCFD Ltd.
Copyright (C) 2016-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -27,18 +27,25 @@ License
#include "emptyFvPatchField.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
template<class Type>
void Foam::fv::jouleHeatingSource::initialiseSigma
(
const dictionary& dict,
autoPtr<Function1<Type>>& sigmaVsTPtr
autoPtr<Function1<Type>>& sigmaFunctionPtr
)
{
typedef GeometricField<Type, fvPatchField, volMesh> FieldType;
const word sigmaName
(
IOobject::scopedName(typeName, "sigma")
);
IOobject io
(
IOobject::scopedName(typeName, "sigma"),
sigmaName,
mesh_.time().timeName(),
mesh_.thisDb(),
IOobject::NO_READ,
@ -48,11 +55,11 @@ void Foam::fv::jouleHeatingSource::initialiseSigma
autoPtr<FieldType> tsigma;
if (dict.found("sigma"))
{
// Sigma to be defined using a Function1 type
sigmaVsTPtr = Function1<Type>::New("sigma", dict, &mesh_);
// Is sigma defined using a Function1 ?
sigmaFunctionPtr = Function1<Type>::NewIfPresent("sigma", dict, &mesh_);
if (sigmaFunctionPtr)
{
tsigma.reset
(
new FieldType
@ -85,28 +92,32 @@ template<class Type>
const Foam::GeometricField<Type, Foam::fvPatchField, Foam::volMesh>&
Foam::fv::jouleHeatingSource::updateSigma
(
const autoPtr<Function1<Type>>& sigmaVsTPtr
const autoPtr<Function1<Type>>& sigmaFunctionPtr
) const
{
typedef GeometricField<Type, fvPatchField, volMesh> FieldType;
auto& sigma = mesh_.lookupObjectRef<FieldType>
const word sigmaName
(
IOobject::scopedName(typeName, "sigma")
);
if (!sigmaVsTPtr)
auto& sigma = mesh_.lookupObjectRef<FieldType>(sigmaName);
if (!sigmaFunctionPtr)
{
// Electrical conductivity field, sigma, was specified by the user
return sigma;
}
const auto& sigmaFunction = sigmaFunctionPtr();
const auto& T = mesh_.lookupObject<volScalarField>(TName_);
// Internal field
forAll(sigma, i)
{
sigma[i] = sigmaVsTPtr->value(T[i]);
sigma[i] = sigmaFunction.value(T[i]);
}
@ -120,7 +131,7 @@ Foam::fv::jouleHeatingSource::updateSigma
const scalarField& Tbf = T.boundaryField()[patchi];
forAll(pf, facei)
{
pf[facei] = sigmaVsTPtr->value(Tbf[facei]);
pf[facei] = sigmaFunction.value(Tbf[facei]);
}
}
}

View File

@ -480,6 +480,7 @@ void Foam::faMeshReconstructor::createMesh()
(
new faMesh
(
procMesh_.name(),
*serialVolMesh_,
identity(singlePatchFaces_.size()) // faceLabels
)

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2023 OpenCFD Ltd.
Copyright (C) 2019-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -144,11 +144,23 @@ KirchhoffShell::KirchhoffShell
)
:
vibrationShellModel(modelType, mesh, dict),
h_
(
IOobject
(
dict.getOrDefault<word>("h", suffixed("hs")),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
regionMesh()
),
ps_
(
IOobject
(
"ps_" + regionName_,
dict.getOrDefault<word>("ps", suffixed("ps")),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::READ_IF_PRESENT,
@ -157,23 +169,11 @@ KirchhoffShell::KirchhoffShell
regionMesh(),
dimensionedScalar(dimPressure, Zero)
),
h_
(
IOobject
(
"h_" + regionName_,
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
regionMesh()
),
laplaceW_
(
IOobject
(
"laplaceW_" + regionName_,
suffixed("laplaceW"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -186,7 +186,7 @@ KirchhoffShell::KirchhoffShell
(
IOobject
(
"laplace2W_" + regionName_,
suffixed("laplace2W"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -199,7 +199,7 @@ KirchhoffShell::KirchhoffShell
(
IOobject
(
"w0_" + regionName_,
suffixed("w0"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -212,7 +212,7 @@ KirchhoffShell::KirchhoffShell
(
IOobject
(
"w00_" + regionName_,
suffixed("w00"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -225,7 +225,7 @@ KirchhoffShell::KirchhoffShell
(
IOobject
(
"laplaceW0_" + regionName_,
suffixed("laplaceW0"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -238,7 +238,7 @@ KirchhoffShell::KirchhoffShell
(
IOobject
(
"laplace2W0_" + regionName_,
suffixed("laplace2W0"),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,
@ -256,6 +256,7 @@ KirchhoffShell::KirchhoffShell
init(dict);
}
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
void KirchhoffShell::preEvolveRegion()

View File

@ -24,7 +24,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::regionFaModels::KirchhoffShell
Foam::regionModels::KirchhoffShell (Foam::regionFaModels)
Description
Vibration-shell finite-area model.
@ -51,9 +51,25 @@ Usage
\table
Property | Description | Type | Reqd | Deflt
vibrationShellModel | Type name: KirchhoffShell | word | yes | -
f0 | Damping coefficient [1/s] | scalar | yes | -
f1 | Damping coefficient [1/s] | scalar | yes | -
f2 | Damping coefficient [1/s] | scalar | yes | -
f0 | Damping coefficient [1/s] | scalar | yes | -
f1 | Damping coefficient [1/s] | scalar | yes | -
f2 | Damping coefficient [1/s] | scalar | yes | -
h | Name of thickness field | word | no | hs (suffix)
ps | Name of pressure field | word | no | ps (suffix)
\endtable
Fields/variables used:
\table
Property | Description | Type | Deflt
h | Thickness | shell | hs (suffix)
ps | Pressure on shell | shell | ps (suffix)
\endtable
Naming changes from 2056 and earlier:
\table
Keyword | Description | Keyword (old) | Deflt (old)
h | Thickness (shell) | - | "h_" + regionName
ps | Pressure (shell) | - | "ps_" + regionName
\endtable
The inherited entries are elaborated in:
@ -90,12 +106,12 @@ class KirchhoffShell
// Source term fields
//- Shell thickness [m]
areaScalarField h_;
//- External surface source [Pa]
const areaScalarField ps_;
//- Thickness [m]
areaScalarField h_;
//- Laplace of the displacement
areaScalarField laplaceW_;

View File

@ -25,20 +25,11 @@ $(kinematic)/injectionModel/injectionModelList/injectionModelList.C
$(kinematic)/injectionModel/injectionModel/injectionModel.C
$(kinematic)/injectionModel/injectionModel/injectionModelNew.C
/* Film separation models */
filmSeparation=$(kinematic)/injectionModel/filmSeparation
filmSeparationModels=$(filmSeparation)/filmSeparationModels
$(filmSeparation)/filmSeparation.C
$(filmSeparationModels)/filmSeparationModel/filmSeparationModel.C
$(filmSeparationModels)/filmSeparationModel/filmSeparationModelNew.C
$(filmSeparationModels)/OwenRyleyModel/OwenRyleyModel.C
$(filmSeparationModels)/FriedrichModel/FriedrichModel.C
cornerDetectionModels=$(filmSeparation)/cornerDetectionModels
$(cornerDetectionModels)/cornerDetectionModel/cornerDetectionModel.C
$(cornerDetectionModels)/cornerDetectionModel/cornerDetectionModelNew.C
$(cornerDetectionModels)/fluxBased/fluxBased.C
$(cornerDetectionModels)/geometryBased/geometryBased.C
$(kinematic)/injectionModel/filmSeparation/filmSeparation.C
$(kinematic)/injectionModel/filmSeparation/filmSeparationModels/filmSeparationModel/filmSeparationModel.C
$(kinematic)/injectionModel/filmSeparation/filmSeparationModels/filmSeparationModel/filmSeparationModelNew.C
$(kinematic)/injectionModel/filmSeparation/filmSeparationModels/OwenRyleyModel/OwenRyleyModel.C
$(kinematic)/injectionModel/filmSeparation/filmSeparationModels/FriedrichModel/FriedrichModel.C
$(kinematic)/injectionModel/BrunDrippingInjection/BrunDrippingInjection.C

View File

@ -11,8 +11,7 @@ EXE_INC = \
-I$(LIB_SRC)/TurbulenceModels/incompressible/lnInclude \
-I$(LIB_SRC)/thermophysicalModels/basic/lnInclude \
-I$(LIB_SRC)/transportModels/compressible/lnInclude \
-I$(LIB_SRC)/transportModels \
-I$(LIB_SRC)/fileFormats/lnInclude
-I$(LIB_SRC)/transportModels
LIB_LIBS = \
-lfiniteVolume \
@ -25,5 +24,4 @@ LIB_LIBS = \
-lthermophysicalProperties \
-lspecie \
-lfaOptions \
-ldistributionModels \
-lfileFormats
-ldistributionModels

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020-2025 OpenCFD Ltd.
Copyright (C) 2020-2023 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -26,9 +26,9 @@ License
\*---------------------------------------------------------------------------*/
#include "dynamicContactAngleForce.H"
#include "addToRunTimeSelectionTable.H"
#include "Function1.H"
#include "distributionModel.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -59,7 +59,6 @@ dynamicContactAngleForce::dynamicContactAngleForce
)
:
contactAngleForce(typeName, film, dict),
thetaPtr_(nullptr),
U_vs_thetaPtr_
(
Function1<scalar>::NewIfPresent
@ -81,56 +80,46 @@ dynamicContactAngleForce::dynamicContactAngleForce
)
),
rndGen_(label(0)),
distributionPtr_(nullptr)
distribution_
(
distributionModel::New
(
coeffDict_.subDict("distribution"),
rndGen_
)
)
{
if (U_vs_thetaPtr_ && T_vs_thetaPtr_)
{
FatalIOErrorInFunction(dict)
<< "Only one of Utheta or Ttheta should be provided; "
<< "both inputs cannot be used together."
<< "Entries Utheta and Ttheta could not be used together"
<< abort(FatalIOError);
}
thetaPtr_.emplace
(
IOobject
(
IOobject::scopedName(typeName, "theta"),
film.regionMesh().time().timeName(),
film.regionMesh().thisDb(),
IOobject::READ_IF_PRESENT,
IOobject::AUTO_WRITE
),
film.regionMesh(),
dimensionedScalar(dimless, Zero)
);
if (coeffDict_.findEntry("distribution"))
{
distributionPtr_ = distributionModel::New
(
coeffDict_.subDict("distribution"),
rndGen_
);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
dynamicContactAngleForce::~dynamicContactAngleForce()
{} // distributionModel was forward declared
// * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
tmp<areaScalarField> dynamicContactAngleForce::theta() const
{
areaScalarField& theta = thetaPtr_.ref();
auto ttheta = tmp<areaScalarField>::New
(
IOobject
(
IOobject::scopedName(typeName, "theta"),
film().regionMesh().time().timeName(),
film().regionMesh().thisDb(),
IOobject::NO_READ,
IOobject::NO_WRITE,
IOobject::NO_REGISTER
),
film().regionMesh(),
dimensionedScalar(dimless, Zero)
);
areaScalarField& theta = ttheta.ref();
scalarField& thetai = theta.ref();
if (U_vs_thetaPtr_)
{
// Initialize with the function of film speed
@ -147,16 +136,13 @@ tmp<areaScalarField> dynamicContactAngleForce::theta() const
thetai = T_vs_thetaPtr_->value(T());
}
if (distributionPtr_)
// Add the stochastic perturbation
forAll(thetai, facei)
{
// Add the stochastic perturbation
forAll(thetai, facei)
{
thetai[facei] += distributionPtr_->sample();
}
thetai[facei] += distribution_->sample();
}
return thetaPtr_();
return ttheta;
}

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2020-2025 OpenCFD Ltd.
Copyright (C) 2020-2022 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -112,9 +112,6 @@ class dynamicContactAngleForce
{
// Private Data
//- Contact-angle field in degrees
mutable autoPtr<areaScalarField> thetaPtr_;
//- Contact angle as a function of film speed
autoPtr<Function1<scalar>> U_vs_thetaPtr_;
@ -124,8 +121,17 @@ class dynamicContactAngleForce
//- Random number generator
Random rndGen_;
//- Stochastic perturbation model for contact angle
autoPtr<distributionModel> distributionPtr_;
//- Parcel size PDF model
const autoPtr<distributionModel> distribution_;
// Private Member Functions
//- No copy construct
dynamicContactAngleForce(const dynamicContactAngleForce&) = delete;
//- No copy assignment
void operator=(const dynamicContactAngleForce&) = delete;
protected:
@ -142,7 +148,7 @@ public:
// Constructors
//- Construct from surface film model and dictionary
//- Construct from surface film model
dynamicContactAngleForce
(
liquidFilmBase& film,
@ -151,7 +157,7 @@ public:
//- Destructor
virtual ~dynamicContactAngleForce();
virtual ~dynamicContactAngleForce() = default;
};

View File

@ -1,103 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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 "cornerDetectionModel.H"
#include "faMesh.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
defineTypeNameAndDebug(cornerDetectionModel, 0);
defineRunTimeSelectionTable(cornerDetectionModel, dictionary);
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::cornerDetectionModel::dihedralAngle
(
const vector& n0,
const vector& n1
) const
{
if (mag(n0) <= VSMALL || mag(n1) <= VSMALL)
{
#ifdef FULL_DEBUG
WarningInFunction
<< "Degenerate face normal magnitude (|n| ~ 0). "
<< "Returning 0 for dihedral angle." << nl;
#endif
return 0;
}
const scalar a = mag(n1 - n0);
const scalar b = mag(n1 + n0);
// The dihedral angle is calculated as 2*atan2(|n1 - n0|, |n1 + n0|),
// which gives the angle between the two normals n0 and n1.
scalar phi = scalar(2)*std::atan2(a, b);
// Clamp to [0, pi]
phi = max(0, min(constant::mathematical::pi, phi));
if (!std::isfinite(phi))
{
#ifdef FULL_DEBUG
WarningInFunction
<< "Non-finite dihedral angle computed. Returning 0." << nl;
#endif
return 0;
}
return phi;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::cornerDetectionModel::cornerDetectionModel
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
)
:
mesh_(mesh),
film_(film),
dict_(dict)
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::cornerDetectionModel::~cornerDetectionModel()
{} // faMesh was forward declared
// ************************************************************************* //

View File

@ -1,211 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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/>.
Namespace
Foam::cornerDetectionModels
Description
A namespace for various \c cornerDetection model implementations.
Class
Foam::cornerDetectionModel
Description
A base class for \c cornerDetection models.
SourceFiles
cornerDetectionModel.C
cornerDetectionModelNew.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_cornerDetectionModel_H
#define Foam_cornerDetectionModel_H
#include "liquidFilmBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward Declarations
class faMesh;
class dictionary;
/*---------------------------------------------------------------------------*\
Class cornerDetectionModel Declaration
\*---------------------------------------------------------------------------*/
class cornerDetectionModel
{
// Private Data
//- Const reference to the finite-area mesh
const faMesh& mesh_;
//- Const reference to the film
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film_;
//- Const reference to the dictionary
const dictionary& dict_;
//- Bitset for corner faces, true for corner faces
bitSet cornerFaces_;
//- List of corner angles [rad]
scalarList cornerAngles_;
protected:
// Protected Member Functions
//- Return the dihedral angle between two neighbour faces
// Robust 2*atan form (handles parallel normals better than acos)
// \param n0 First normal vector
// \param n1 Second normal vector
// \return The dihedral angle [rad]
scalar dihedralAngle(const vector& n0, const vector& n1) const;
public:
//- Runtime type information
TypeName("cornerDetectionModel");
// Declare runtime constructor selection table
declareRunTimeSelectionTable
(
autoPtr,
cornerDetectionModel,
dictionary,
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
),
(mesh, film, dict)
);
// Selectors
//- Return a reference to the selected cornerDetection model
static autoPtr<cornerDetectionModel> New
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
);
// Constructors
//- Construct from components
cornerDetectionModel
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
);
// Generated Methods
//- No copy construct
cornerDetectionModel(const cornerDetectionModel&) = delete;
//- No copy assignment
void operator=(const cornerDetectionModel&) = delete;
//- Destructor
virtual ~cornerDetectionModel();
// Member Functions
// Getters
//- Return const reference to the finite-area mesh
const faMesh& mesh() const noexcept { return mesh_; }
//- Return const reference to the film
const regionModels::areaSurfaceFilmModels::liquidFilmBase&
film() const noexcept { return film_; }
//- Return const reference to the dictionary
const dictionary& dict() const noexcept { return dict_; }
//- Return const reference to the corner faces bitset
const bitSet& getCornerFaces() const noexcept { return cornerFaces_; }
//- Return const reference to the corner angles list [rad]
const scalarList& getCornerAngles() const noexcept
{
return cornerAngles_;
}
// Setters
//- Set the corner faces bitset
void setCornerFaces(const bitSet& cornerFaces)
{
cornerFaces_ = cornerFaces;
}
//- Set the corner angles list [rad]
void setCornerAngles(const scalarList& cornerAngles)
{
cornerAngles_ = cornerAngles;
}
// Evaluation
//- Detect and store the corner faces
virtual bool detectCorners() = 0;
// I-O
//- Read the model dictionary
virtual bool read(const dictionary&) { return true; }
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,65 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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 "cornerDetectionModel.H"
#include "faMesh.H"
#include "liquidFilmBase.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
Foam::autoPtr<Foam::cornerDetectionModel> Foam::cornerDetectionModel::New
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
)
{
const word modelType
(
dict.getOrDefault<word>("cornerDetectionModel", "fluxBased")
);
Info<< " Selecting corner-detection model: " << modelType << nl << endl;
auto* ctorPtr = dictionaryConstructorTable(modelType);
if (!ctorPtr)
{
FatalIOErrorInLookup
(
dict,
"cornerDetectionModel",
modelType,
*dictionaryConstructorTablePtr_
) << exit(FatalIOError);
}
return autoPtr<cornerDetectionModel>(ctorPtr(mesh, film, dict));
}
// ************************************************************************* //

View File

@ -1,391 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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 "fluxBased.H"
#include "OBJstream.H"
#include "processorFaPatch.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace cornerDetectionModels
{
defineTypeNameAndDebug(fluxBased, 0);
addToRunTimeSelectionTable(cornerDetectionModel, fluxBased, dictionary);
}
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::bitSet Foam::cornerDetectionModels::fluxBased::identifyCornerEdges() const
{
// Return true if face normals converge, i.e. sharp edge
// Face-normal vectors diverge: no separation, converge: separation (maybe)
const auto isCornerEdgeSharp =
[](
const vector& fcO, // face-centre owner
const vector& fcN, // face-centre neigh
const vector& fnO, // face-normal owner
const vector& fnN // face-normal neigh
) noexcept -> bool
{
// Threshold for sharpness detection
constexpr scalar sharpEdgeThreshold = -1e-8;
// Relative centre and normal of the two faces sharing the edge
const vector relativePosition(fcN - fcO);
const vector relativeNormal(fnN - fnO);
// Sharp if normals converge along the centre-to-centre direction
return ((relativeNormal & relativePosition) < sharpEdgeThreshold);
};
// Cache the operand references
const areaVectorField& fc = mesh().areaCentres();
const areaVectorField& fn = mesh().faceAreaNormals();
const labelUList& own = mesh().edgeOwner();
const labelUList& nei = mesh().edgeNeighbour();
// Allocate the resource for the return object
bitSet cornerEdges(mesh().nEdges(), false);
// Internal edges (owner <-> neighbour)
forAll(nei, edgei)
{
const label faceO = own[edgei];
const label faceN = nei[edgei];
cornerEdges[edgei] = isCornerEdgeSharp
(
fc[faceO],
fc[faceN],
fn[faceO],
fn[faceN]
);
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerEdges;
// Check if processor face-normal vectors diverge (no separation)
// or converge (separation may occur)
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (!isA<processorFaPatch>(fap)) continue;
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdge0 = fap.start();
const auto& fcp = fc.boundaryField()[patchi];
const auto& fnp = fn.boundaryField()[patchi];
// Processor edges (owner <-| none)
forAll(fnp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei = internalEdge0 + bndEdgei;
cornerEdges[meshEdgei] = isCornerEdgeSharp
(
fc[faceO],
fcp[bndEdgei],
fn[faceO],
fnp[bndEdgei]
);
}
}
return cornerEdges;
}
Foam::bitSet Foam::cornerDetectionModels::fluxBased::identifyCornerFaces
(
const bitSet& cornerEdges
) const
{
// Marks the separating face based on edge flux sign
const auto markSeparation =
[](
bitSet& cornerFaces,
const scalar phiEdge,
const label faceO,
const label faceN = -1 /* = -1 for processor edges */
) noexcept -> void
{
constexpr scalar tol = 1e-8;
// Assuming no sources/sinks at the edge
if (phiEdge > tol) // From owner to neighbour
{
cornerFaces[faceO] = true;
}
else if ((phiEdge < -tol) && (faceN != -1)) // From nei to own
{
cornerFaces[faceN] = true;
}
};
// Cache the operand references
const edgeScalarField& phis = film().phi2s();
const labelUList& own = mesh().edgeOwner();
const labelUList& nei = mesh().edgeNeighbour();
// Allocate the resource for the return object
bitSet cornerFaces(mesh().faces().size(), false);
// Internal faces (owner <-> neighbour)
forAll(nei, edgei)
{
if (!cornerEdges[edgei]) continue;
markSeparation
(
cornerFaces,
phis[edgei],
own[edgei], // faceO
nei[edgei] // faceN
);
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerFaces;
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (!isA<processorFaPatch>(fap)) continue;
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdge0 = fap.start();
const auto& phisp = phis.boundaryField()[patchi];
// Processor faces (owner <-| none)
forAll(phisp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei = internalEdge0 + bndEdgei;
if (!cornerEdges[meshEdgei]) continue;
markSeparation
(
cornerFaces,
phisp[bndEdgei],
faceO
/*faceN = -1*/
);
}
}
return cornerFaces;
}
Foam::scalarList Foam::cornerDetectionModels::fluxBased::calcCornerAngles
(
const bitSet& faces,
const bitSet& edges
) const
{
// Cache the operand references
const areaVectorField& fn = mesh().faceAreaNormals();
const labelUList& own = mesh().edgeOwner();
const labelUList& nei = mesh().edgeNeighbour();
scalarList cornerFaceAngles(mesh().faces().size(), Zero);
// Internal edges (owner <-> neighbour)
forAll(nei, edgei)
{
if (!edges[edgei]) continue;
const label faceO = own[edgei];
const label faceN = nei[edgei];
// If neither adjacent face is flagged as a corner, skip the atan2 work
if (!faces[faceO] && !faces[faceN]) continue;
const scalar ang = this->dihedralAngle(fn[faceO], fn[faceN]);
if (faces[faceO]) cornerFaceAngles[faceO] = ang;
if (faces[faceN]) cornerFaceAngles[faceN] = ang;
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerFaceAngles;
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (!isA<processorFaPatch>(fap)) continue;
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdge0 = fap.start();
const auto& fnp = fn.boundaryField()[patchi];
// Processor edges (owner <-| none)
forAll(fnp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei = internalEdge0 + bndEdgei;
// Only if the mesh edge and owner face are both corners
if (!edges[meshEdgei] || !faces[faceO]) continue;
cornerFaceAngles[faceO] =
this->dihedralAngle(fn[faceO], fnp[bndEdgei]);
}
}
return cornerFaceAngles;
}
void Foam::cornerDetectionModels::fluxBased::writeEdgesAndFaces
(
const word& prefix
) const
{
const pointField& pts = mesh().points();
const word timeName(Foam::name(mesh().time().value()));
const word nameEdges("fluxBased-edges-" + timeName + ".obj");
const word nameFaces("fluxBased-faces-" + timeName + ".obj");
// Write OBJ of edge faces to file
OBJstream osEdges(mesh().time().path()/nameEdges);
const auto& edges = mesh().edges();
forAll(cornerEdges_, ei)
{
if (cornerEdges_[ei])
{
const edge& e = edges[ei];
osEdges.write(e, pts);
}
}
// Write OBJ of corner faces to file
OBJstream osFaces(mesh().time().path()/nameFaces);
const bitSet& cornerFaces = this->getCornerFaces();
const auto& faces = mesh().faces();
forAll(cornerFaces, fi)
{
if (cornerFaces[fi])
{
const face& f = faces[fi];
osFaces.write(f, pts);
}
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::cornerDetectionModels::fluxBased::fluxBased
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
)
:
cornerDetectionModel(mesh, film, dict),
init_(false)
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::cornerDetectionModels::fluxBased::detectCorners()
{
if (!init_ || mesh().moving())
{
// Identify and store corner edges based on face normals
cornerEdges_ = identifyCornerEdges();
init_ = true;
}
// Identify and store corner faces based on edge flux sign
this->setCornerFaces(identifyCornerFaces(cornerEdges_));
// Calculate and store corner face angles
const bitSet& cornerFaces = this->getCornerFaces();
this->setCornerAngles
(
calcCornerAngles(cornerFaces, cornerEdges_)
);
// Write edges and faces as OBJ sets for debug purposes, if need be
if (debug && mesh().time().writeTime())
{
writeEdgesAndFaces();
}
return true;
}
bool Foam::cornerDetectionModels::fluxBased::read(const dictionary& dict)
{
if (!cornerDetectionModel::read(dict))
{
return false;
}
// Force the re-identification of corner edges/faces
init_ = false;
return true;
}
// ************************************************************************* //

View File

@ -1,160 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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/>.
Class
Foam::cornerDetectionModels::fluxBased
Description
Flux-based corner detection model. Marks faces at which the liquid film can
separate based on the flux.
The model identifies sharp edges based on the face normals of the two
faces sharing the edge. Then, if the edge is sharp, the flux direction is
evaluated to mark the face through which the flux leaves the liquid film.
If the edge is sharp and the flux leaves through one of the two faces
sharing the edge, the face is marked as a corner face, where the film can
separate.
Usage
Minimal example in boundary-condition files:
\verbatim
filmSeparationCoeffs
{
// Inherited entries
...
// Optional entries
cornerDetectionModel fluxBased;
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
cornerDetectionModel | Corner detector model | word | no | fluxBased
\endtable
The inherited entries are elaborated in:
- \link cornerDetectionModel.H \endlink
SourceFiles
fluxBased.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_cornerDetectionModels_fluxBased_H
#define Foam_cornerDetectionModels_fluxBased_H
#include "cornerDetectionModel.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace cornerDetectionModels
{
/*---------------------------------------------------------------------------*\
Class fluxBased Declaration
\*---------------------------------------------------------------------------*/
class fluxBased
:
public cornerDetectionModel
{
// Private Data
//- Flag to deduce if the object is initialised
bool init_;
//- Identified corner edges
bitSet cornerEdges_;
// Private Member Functions
//- Return Boolean list of identified corner edges
bitSet identifyCornerEdges() const;
//- Return Boolean list of identified corner faces
bitSet identifyCornerFaces(const bitSet& cornerEdges) const;
//- Return the list of corner angles for each edge [rad]
scalarList calcCornerAngles
(
const bitSet& faces,
const bitSet& edges
) const;
// Write edges and faces as OBJ sets for debug purposes
void writeEdgesAndFaces(const word& prefix = "geomCorners") const;
public:
//- Runtime type information
TypeName("fluxBased");
// Constructors
//- Construct from components
fluxBased
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
);
// Destructor
virtual ~fluxBased() = default;
// Member Functions
// Evaluation
//- Detect and store the corner faces
virtual bool detectCorners();
// I-O
//- Read the model dictionary
virtual bool read(const dictionary&);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace cornerDetectionModels
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -1,586 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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 "geometryBased.H"
#include "processorFaPatch.H"
#include "unitConversion.H"
#include "syncTools.H"
#include "OBJstream.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace cornerDetectionModels
{
defineTypeNameAndDebug(geometryBased, 0);
addToRunTimeSelectionTable(cornerDetectionModel, geometryBased, dictionary);
}
}
const Foam::Enum
<
Foam::cornerDetectionModels::geometryBased::cornerCurveType
>
Foam::cornerDetectionModels::geometryBased::cornerCurveTypeNames
({
{ cornerCurveType::ANY, "any" },
{ cornerCurveType::CONCAVE , "concave" },
{ cornerCurveType::CONVEX , "convex" }
});
const Foam::Enum
<
Foam::cornerDetectionModels::geometryBased::cornerType
>
Foam::cornerDetectionModels::geometryBased::cornerTypeNames
({
{ cornerType::ALL, "sharpOrRound" },
{ cornerType::SHARP , "sharp" },
{ cornerType::ROUND , "round" }
});
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
Foam::scalar Foam::cornerDetectionModels::geometryBased::curvatureSign
(
const vector& t,
const vector& n0,
const vector& n1
) const
{
// t: unit edge tangent
// n0: owner face unit normal
// n1: neighbour face unit normal
scalar curvature = (t & (n0 ^ n1));
// Orientation: sign of triple product t . (n0 x n1)
// Positive => one sense (together with outward normals, treat as "convex");
// mapping to convex/concave is finally gated by 'cornerCurveType_'.
return sign(curvature);
}
void Foam::cornerDetectionModels::geometryBased::classifyEdges
(
bitSet& sharpEdges,
bitSet& roundEdges
) const
{
// Cache the operand references
const areaVectorField& nf = mesh().faceAreaNormals();
const edgeList& edges = mesh().edges();
const labelUList& own = mesh().edgeOwner(); // own.sz = nEdges
const labelUList& nei = mesh().edgeNeighbour(); // nei.sz = nInternalEdges
const pointField& pts = mesh().points();
// Convert input-angle parameters from degrees to radians
const scalar angSharp = degToRad(angleSharpDeg_);
const scalar angRoundMin = degToRad(angleRoundMinDeg_);
const scalar angRoundMax = degToRad(angleRoundMaxDeg_);
// Limit to subset of patches if requested
bitSet allowedFaces(mesh().nFaces(), true);
// Allocate the resource for the return objects
sharpEdges.resize(mesh().nEdges()); // internal + boundary edges
sharpEdges.reset();
roundEdges.resize(mesh().nEdges());
roundEdges.reset();
// Internal edges
const label nInternalEdges = mesh().nInternalEdges();
for (label ei = 0; ei < nInternalEdges; ++ei)
{
// Do not allow processing of edges shorter than 'minEdgeLength'
const edge& e = edges[ei];
const scalar le = e.mag(pts);
if (le <= max(minEdgeLength_, VSMALL)) continue;
// Do not allow processing of excluded faces
const label f0 = own[ei];
const label f1 = nei[ei];
if (!allowedFaces.test(f0) && !allowedFaces.test(f1)) continue;
// Calculate the dihedral angle and curvature per edge
const vector& n0 = nf[f0];
const vector& n1 = nf[f1];
const scalar phi = this->dihedralAngle(n0, n1); // [rad]
const scalar kappa = 2.0*Foam::sin(0.5*phi)/max(le, VSMALL); // [1/m]
const scalar R = (kappa > VSMALL ? scalar(1)/kappa : GREAT);
const vector tangent(e.unitVec(pts));
const scalar sgn = curvatureSign(tangent, n0, n1);
const bool curvatureType =
(cornerCurveType_ == cornerCurveType::ANY)
|| (cornerCurveType_ == cornerCurveType::CONVEX && sgn > 0)
|| (cornerCurveType_ == cornerCurveType::CONCAVE && sgn < 0);
// Do not allow processing of excluded curvature-type faces
if (!curvatureType) continue;
// Sharp: dihedral above threshold
if (phi >= angSharp && cornerType_ != cornerType::ROUND)
{
sharpEdges.set(ei);
continue; // do not double-classify as round
}
// Round: small-to-moderate angle but small radius (tight fillet)
if
(
phi >= angRoundMin && phi <= angRoundMax
&& R <= maxRoundRadius_
&& cornerType_ != cornerType::SHARP
)
{
roundEdges.set(ei);
}
}
// Optional binary smoothing (edge-neighbour OR)
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return;
// Boundary edges
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
const label patchi = fap.index();
const label boundaryEdge0 = fap.start();
const auto& nfp = nf.boundaryField()[patchi];
if (isA<processorFaPatch>(fap))
{
forAll(nfp, bEdgei)
{
const label meshEdgei = boundaryEdge0 + bEdgei;
// Do not allow processing of edges shorter than 'minEdgeLength'
const edge& e = edges[meshEdgei];
const scalar le = e.mag(pts);
if (le <= max(minEdgeLength_, VSMALL)) continue;
// Do not allow processing of excluded faces
const label faceO = own[meshEdgei];
if (!allowedFaces.test(faceO)) continue;
// Fetch normal vector of owner and neigh faces
const vector& n0 = nf[faceO];
const vector& n1 = nfp[bEdgei];
// Calculate the dihedral angle and curvature per edge
const scalar phi = this->dihedralAngle(n0, n1); // [rad]
const scalar kappa = 2.0*Foam::sin(0.5*phi)/max(le, VSMALL);
const scalar R = (kappa > VSMALL ? scalar(1)/kappa : GREAT);
const vector tangent(e.unitVec(pts));
const scalar sgn = curvatureSign(tangent, n0, n1);
const bool curvatureType =
(cornerCurveType_ == cornerCurveType::ANY)
|| (cornerCurveType_ == cornerCurveType::CONVEX && sgn > 0)
|| (cornerCurveType_ == cornerCurveType::CONCAVE && sgn < 0);
// Do not allow processing of excluded curvature-type faces
if (!curvatureType) continue;
// Sharp: dihedral above threshold
if (phi >= angSharp && cornerType_ != cornerType::ROUND)
{
sharpEdges.set(meshEdgei);
continue; // do not double-classify as round
}
// Round: small-to-moderate angle but small radius
if
(
phi >= angRoundMin && phi <= angRoundMax
&& R <= maxRoundRadius_
&& cornerType_ != cornerType::SHARP
)
{
roundEdges.set(meshEdgei);
}
}
}
else
{
forAll(nfp, bEdgei)
{
const label meshEdgei = boundaryEdge0 + bEdgei;
const label faceO = own[meshEdgei];
if (sharpBoundaryEdges_ && allowedFaces.test(faceO))
{
sharpEdges.set(meshEdgei);
}
// Do not allow round edges on physical boundaries
}
}
}
}
void Foam::cornerDetectionModels::geometryBased::edgesToFaces
(
const bitSet& edgeMask,
bitSet& faceMask
) const
{
// Cache the operand references
const labelUList& own = mesh().edgeOwner();
const labelUList& nei = mesh().edgeNeighbour();
// Allocate the resource for the return objects
faceMask.resize(mesh().nFaces());
faceMask.reset();
// Internal edges
const label nInternalEdges = mesh().nInternalEdges();
for (label ei = 0; ei < nInternalEdges; ++ei)
{
if (edgeMask.test(ei))
{
// pick the intersecting owner and neighbour faces at the edge
faceMask.set(nei[ei]);
}
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return;
// Boundary edges
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
const label bEdge0 = fap.start();
const label nbEdges = fap.size();
for (label bEdgei = 0; bEdgei < nbEdges; ++bEdgei)
{
const label meshEdgei = bEdge0 + bEdgei;
if (edgeMask.test(meshEdgei))
{
faceMask.set(own[meshEdgei]);
}
}
}
}
Foam::scalarList Foam::cornerDetectionModels::geometryBased::calcCornerAngles
(
const bitSet& faces,
const bitSet& edges
) const
{
// Cache the operand references
const areaVectorField& nf = mesh().faceAreaNormals();
const labelUList& own = mesh().edgeOwner();
const labelUList& nei = mesh().edgeNeighbour();
// Allocate the resource for the return object
scalarList cornerFaceAngles(mesh().faces().size(), Zero);
// Internal edges
const label nInternalEdges = mesh().nInternalEdges();
for (label ei = 0; ei < nInternalEdges; ++ei)
{
if (!edges[ei]) continue;
const label faceO = own[ei];
const label faceN = nei[ei];
// If neither adjacent face is flagged as a corner, skip the atan2 work
if (!faces[faceO] && !faces[faceN]) continue;
const scalar ang = this->dihedralAngle(nf[faceO], nf[faceN]);
if (faces[faceO]) cornerFaceAngles[faceO] = ang;
if (faces[faceN]) cornerFaceAngles[faceN] = ang;
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerFaceAngles;
// Boundary edges
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (!isA<processorFaPatch>(fap)) continue;
const label patchi = fap.index();
const label bEdge0 = fap.start();
const auto& nfp = nf.boundaryField()[patchi];
forAll(nfp, bEdgei)
{
const label meshEdgei = bEdge0 + bEdgei;
const label faceO = own[meshEdgei];
// Only if the mesh edge is a corner and the owner face is a corner
if (!edges[meshEdgei] || !faces[faceO]) continue;
cornerFaceAngles[faceO] =
this->dihedralAngle(nf[faceO], nfp[bEdgei]);
}
}
return cornerFaceAngles;
}
void Foam::cornerDetectionModels::geometryBased::writeEdgesAndFaces() const
{
// Cache the operand references
const auto& edges = mesh().edges();
const auto& faces = mesh().faces();
const pointField& pts = mesh().points();
// Generic writer for masked primitives (edge/face)
auto writeMasked =
[&](
const auto& geom,
const auto& mask,
const char* file
)
{
OBJstream os(mesh().time().path()/file);
forAll(mask, i) if (mask[i]) os.write(geom[i], pts);
};
const bool writeSharp =
(cornerType_ == cornerType::ALL || cornerType_ == cornerType::SHARP);
const bool writeRound =
(cornerType_ == cornerType::ALL || cornerType_ == cornerType::ROUND);
if (writeSharp)
{
writeMasked(edges, sharpEdges_, "sharp-edges.obj");
writeMasked(faces, sharpFaces_, "sharp-faces.obj");
}
if (writeRound)
{
writeMasked(edges, roundEdges_, "round-edges.obj");
writeMasked(faces, roundFaces_, "round-faces.obj");
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::cornerDetectionModels::geometryBased::geometryBased
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
)
:
cornerDetectionModel(mesh, film, dict),
init_(false)
{
read(dict);
}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::cornerDetectionModels::geometryBased::detectCorners()
{
if (!init_ || mesh().moving())
{
// Identify and store sharp/round edges/faces
classifyEdges(sharpEdges_, roundEdges_);
edgesToFaces(sharpEdges_, sharpFaces_);
edgesToFaces(roundEdges_, roundFaces_);
// Collect all operand edges/faces
cornerEdges_ = (sharpEdges_ | roundEdges_);
cornerFaces_ = (sharpFaces_ | roundFaces_);
// Pass the operand edges/faces to the film-separation model
this->setCornerFaces(cornerFaces_);
this->setCornerAngles
(
calcCornerAngles(cornerFaces_, cornerEdges_)
);
init_ = true;
}
// Write edges and faces as OBJ sets for debug purposes, if need be
if (debug && mesh().time().writeTime())
{
writeEdgesAndFaces();
}
return true;
}
bool Foam::cornerDetectionModels::geometryBased::read(const dictionary& dict)
{
if (!cornerDetectionModel::read(dict))
{
return false;
}
cornerCurveType_ = cornerCurveTypeNames.getOrDefault
(
"cornerCurveType",
dict,
cornerCurveType::ANY
);
cornerType_ = cornerTypeNames.getOrDefault
(
"cornerType",
dict,
cornerType::ALL
);
angleSharpDeg_ = dict.getOrDefault<scalar>("angleSharp", 45);
angleRoundMinDeg_ = dict.getOrDefault<scalar>("angleRoundMin", 5);
angleRoundMaxDeg_ = dict.getOrDefault<scalar>("angleRoundMax", 45);
maxRoundRadius_ = dict.getOrDefault<scalar>("maxRoundRadius", 2e-3);
minEdgeLength_ = dict.getOrDefault<scalar>("minEdgeLength", 0);
nSmooth_ = dict.getOrDefault<label>("nSmooth", 0);
sharpBoundaryEdges_ = dict.getOrDefault<bool>("sharpBoundaryEdges", false);
// Validate the input parameters
if (angleSharpDeg_ <= 0 || angleSharpDeg_ >= 180)
{
FatalIOErrorInFunction(dict)
<< "angleSharp (" << angleSharpDeg_
<< " deg) must be in (0, 180)."
<< exit(FatalIOError);
}
if
(
angleRoundMinDeg_ < 0 || angleRoundMaxDeg_ > 180
|| angleRoundMinDeg_ > angleRoundMaxDeg_
)
{
FatalIOErrorInFunction(dict)
<< "Inconsistent round-angle range: angleRoundMin="
<< angleRoundMinDeg_ << " deg, angleRoundMax=" << angleRoundMaxDeg_
<< " deg. Require 0 <= min <= max <= 180."
<< exit(FatalIOError);
}
if (angleSharpDeg_ <= angleRoundMaxDeg_)
{
WarningInFunction
<< "angleSharp (" << angleSharpDeg_
<< " deg) <= angleRoundMax (" << angleRoundMaxDeg_
<< " deg): sharp vs round thresholds overlap; "
<< "classification may be ambiguous."
<< nl;
}
if (maxRoundRadius_ < 0)
{
FatalIOErrorInFunction(dict)
<< "maxRoundRadius must be non-negative."
<< exit(FatalIOError);
}
if (minEdgeLength_ < 0)
{
FatalIOErrorInFunction(dict)
<< "minEdgeLength must be non-negative."
<< exit(FatalIOError);
}
if (nSmooth_ < 0)
{
FatalIOErrorInFunction(dict)
<< "nSmooth must be non-negative."
<< exit(FatalIOError);
}
sharpEdges_.clear();
roundEdges_.clear();
cornerEdges_.clear();
sharpFaces_.clear();
roundFaces_.clear();
cornerFaces_.clear();
// Force the re-identification of corner edges/faces
init_ = false;
return true;
}
// ************************************************************************* //

View File

@ -1,278 +0,0 @@
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2025 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/>.
Class
Foam::cornerDetectionModels::geometryBased
Description
Geometry-based corner detection model. Marks faces at which the liquid film
can separate based on the geometry.
The model identifies sharp edges based on the face normals of the two
faces sharing the edge. Then, if the edge is sharp, the curvature is
evaluated to mark the face through which the flux leaves the liquid film.
If the edge is sharp and the curvature is of the specified type, the face
is marked as a corner face, where the film can separate.
Usage
Minimal example in boundary-condition files:
\verbatim
filmSeparationCoeffs
{
// Inherited entries
...
// Optional entries
cornerDetectionModel geometryBased;
cornerCurveType <word>;
cornerType <word>;
angleSharp <scalar>; // [deg]
angleRoundMin <scalar>; // [deg]
angleRoundMax <scalar>; // [deg]
maxRoundRadius <scalar>; // [m]
minEdgeLength <scalar>; // [m]
nSmooth <label>; // [no. of passes]
sharpBoundaryEdges <bool>;
}
\endverbatim
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
cornerDetectionModel | Corner detector model | word | no | fluxBased
cornerCurveType | Corner-curvature type | word | no | any
cornerType | Corner type | word | no | sharpOrRound
angleSharp | Sharp-angle limit [deg] | scalar | no | 45
angleRoundMin | Minimum round-angle limit [deg] | scalar | no | 5
angleRoundMax | Maximum round-angle limit [deg] | scalar | no | 45
maxRoundRadius | Maximum round-radius limit [m] | scalar | no | 2e-3
minEdgeLength | Minimum edge length [m] | scalar | no | 0
nSmooth | No. of smoothing passes | label | no | 0
sharpBoundaryEdges | Treat boundary edges as sharp | bool | no | false
\endtable
Options for the \c cornerCurve entry:
\verbatim
any | Convex or concave corners
convex | Convex corners
concave | Concave corners
\endverbatim
Options for the \c cornerType entry:
\verbatim
sharp | Sharp corners
round | Round corners
sharpOrRound | Sharp or round corners
\endverbatim
The inherited entries are elaborated in:
- \link cornerDetectionModel.H \endlink
SourceFiles
geometryBased.C
\*---------------------------------------------------------------------------*/
#ifndef Foam_cornerDetectionModels_geometryBased_H
#define Foam_cornerDetectionModels_geometryBased_H
#include "cornerDetectionModel.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace cornerDetectionModels
{
/*---------------------------------------------------------------------------*\
Class geometryBased Declaration
\*---------------------------------------------------------------------------*/
class geometryBased
:
public cornerDetectionModel
{
// Private Enumerations
//- Options for the corner-curvature type
enum cornerCurveType : char
{
ANY = 0, //!< "Convex or concave corners"
CONVEX, //!< "Convex corners"
CONCAVE //!< "Concave corners"
};
//- Names for cornerCurveType
static const Enum<cornerCurveType> cornerCurveTypeNames;
//- Options for the corner type
enum cornerType : char
{
ALL = 0, //!< "Sharp or round corners"
SHARP, //!< "Sharp corners"
ROUND //!< "Round corners"
};
//- Names for cornerType
static const Enum<cornerType> cornerTypeNames;
// Private Data
//- Corner-curvature type
enum cornerCurveType cornerCurveType_;
//- Corner type
enum cornerType cornerType_;
//- Flag to deduce if the object is initialised
bool init_;
//- Bitset of edges identified as sharp
bitSet sharpEdges_;
//- Bitset of edges identified as round
bitSet roundEdges_;
//- Bitset of edges identified as a combination of sharp and round
bitSet cornerEdges_;
//- Bitset of faces identified as sharp
bitSet sharpFaces_;
//- Bitset of faces identified as round
bitSet roundFaces_;
//- Bitset of faces identified as a combination of sharp and round
bitSet cornerFaces_;
//- Sharp-angle limit
scalar angleSharpDeg_;
//- Minimum round-angle limit
scalar angleRoundMinDeg_;
//- Maximum round-angle limit
scalar angleRoundMaxDeg_;
//- Maximum round-radius limit
scalar maxRoundRadius_;
//- Minimum edge length; ignore edges shorter than this
scalar minEdgeLength_;
//- Number of smoothing passes on the binary edge mask
label nSmooth_;
//- Flag to treat one-face boundary edges as sharp
bool sharpBoundaryEdges_;
// Private Member Functions
//- Return the signed bending sense, sign(+1/-1) of curvature, across
//- an edge with respect to the edge tangent
scalar curvatureSign
(
const vector& t,
const vector& n0,
const vector& n1
) const;
//- Classify edges into sharp/round according to dihedral angle and
//- inferred radius
void classifyEdges
(
bitSet& sharpEdges,
bitSet& roundEdges
) const;
//- Convert an edge mask to a face mask (face is set if any of its
//- edges are set)
void edgesToFaces
(
const bitSet& edgeMask,
bitSet& faceMask
) const;
//- Return the list of corner angles [rad] for each edge
scalarList calcCornerAngles
(
const bitSet& faces,
const bitSet& edges
) const;
// Write edges and faces as OBJ sets for debug purposes
void writeEdgesAndFaces() const;
public:
//- Runtime type information
TypeName("geometryBased");
// Constructors
//- Construct from components
geometryBased
(
const faMesh& mesh,
const regionModels::areaSurfaceFilmModels::liquidFilmBase& film,
const dictionary& dict
);
// Destructor
virtual ~geometryBased() = default;
// Member Functions
// Evaluation
//- Detect and store the corner faces
virtual bool detectCorners();
// I-O
//- Read the model dictionary
virtual bool read(const dictionary& dict);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace cornerDetectionModels
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //

View File

@ -26,7 +26,6 @@ License
\*---------------------------------------------------------------------------*/
#include "FriedrichModel.H"
#include "cornerDetectionModel.H"
#include "processorFaPatch.H"
#include "unitConversion.H"
#include "addToRunTimeSelectionTable.H"
@ -54,6 +53,321 @@ FriedrichModel::separationTypeNames
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
bitSet FriedrichModel::calcCornerEdges() const
{
bitSet cornerEdges(mesh().nEdges(), false);
const areaVectorField& faceCentres = mesh().areaCentres();
const areaVectorField& faceNormals = mesh().faceAreaNormals();
const labelUList& own = mesh().edgeOwner();
const labelUList& nbr = mesh().edgeNeighbour();
// Check if internal face-normal vectors diverge (no separation)
// or converge (separation may occur)
forAll(nbr, edgei)
{
const label faceO = own[edgei];
const label faceN = nbr[edgei];
cornerEdges[edgei] = isCornerEdgeSharp
(
faceCentres[faceO],
faceCentres[faceN],
faceNormals[faceO],
faceNormals[faceN]
);
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerEdges;
// Check if processor face-normal vectors diverge (no separation)
// or converge (separation may occur)
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (isA<processorFaPatch>(fap))
{
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdgei = fap.start();
const auto& faceCentresp = faceCentres.boundaryField()[patchi];
const auto& faceNormalsp = faceNormals.boundaryField()[patchi];
forAll(faceNormalsp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei = internalEdgei + bndEdgei;
cornerEdges[meshEdgei] = isCornerEdgeSharp
(
faceCentres[faceO],
faceCentresp[bndEdgei],
faceNormals[faceO],
faceNormalsp[bndEdgei]
);
}
}
}
return cornerEdges;
}
bool FriedrichModel::isCornerEdgeSharp
(
const vector& faceCentreO,
const vector& faceCentreN,
const vector& faceNormalO,
const vector& faceNormalN
) const
{
// Calculate the relative position of centres of faces sharing an edge
const vector relativePosition(faceCentreN - faceCentreO);
// Calculate the relative normal of faces sharing an edge
const vector relativeNormal(faceNormalN - faceNormalO);
// Return true if the face normals converge, meaning that the edge is sharp
return ((relativeNormal & relativePosition) < -1e-8);
}
scalarList FriedrichModel::calcCornerAngles() const
{
scalarList cornerAngles(mesh().nEdges(), Zero);
const areaVectorField& faceNormals = mesh().faceAreaNormals();
const labelUList& own = mesh().edgeOwner();
const labelUList& nbr = mesh().edgeNeighbour();
// Process internal edges
forAll(nbr, edgei)
{
if (!cornerEdges_[edgei]) continue;
const label faceO = own[edgei];
const label faceN = nbr[edgei];
cornerAngles[edgei] = calcCornerAngle
(
faceNormals[faceO],
faceNormals[faceN]
);
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return cornerAngles;
// Process processor edges
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (isA<processorFaPatch>(fap))
{
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdgei = fap.start();
const auto& faceNormalsp = faceNormals.boundaryField()[patchi];
forAll(faceNormalsp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei = internalEdgei + bndEdgei;
if (!cornerEdges_[meshEdgei]) continue;
cornerAngles[meshEdgei] = calcCornerAngle
(
faceNormals[faceO],
faceNormalsp[bndEdgei]
);
}
}
}
return cornerAngles;
}
scalar FriedrichModel::calcCornerAngle
(
const vector& faceNormalO,
const vector& faceNormalN
) const
{
const scalar magFaceNormal = mag(faceNormalO)*mag(faceNormalN);
// Avoid any potential exceptions during the cosine calculations
if (magFaceNormal < SMALL) return 0;
scalar cosAngle = (faceNormalO & faceNormalN)/magFaceNormal;
cosAngle = clamp(cosAngle, -1, 1);
return std::acos(cosAngle);
}
bitSet FriedrichModel::calcSeparationFaces() const
{
bitSet separationFaces(mesh().faces().size(), false);
const edgeScalarField& phis = film().phi2s();
const labelUList& own = mesh().edgeOwner();
const labelUList& nbr = mesh().edgeNeighbour();
// Process internal faces
forAll(nbr, edgei)
{
if (!cornerEdges_[edgei]) continue;
const label faceO = own[edgei];
const label faceN = nbr[edgei];
isSeparationFace
(
separationFaces,
phis[edgei],
faceO,
faceN
);
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return separationFaces;
// Process processor faces
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (isA<processorFaPatch>(fap))
{
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdgei = fap.start();
const auto& phisp = phis.boundaryField()[patchi];
forAll(phisp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei(internalEdgei + bndEdgei);
if (!cornerEdges_[meshEdgei]) continue;
isSeparationFace
(
separationFaces,
phisp[bndEdgei],
faceO
);
}
}
}
return separationFaces;
}
void FriedrichModel::isSeparationFace
(
bitSet& separationFaces,
const scalar phiEdge,
const label faceO,
const label faceN
) const
{
const scalar tol = 1e-8;
// Assuming there are no sources/sinks at the edge
if (phiEdge > tol) // From owner to neighbour
{
separationFaces[faceO] = true;
}
else if ((phiEdge < -tol) && (faceN != -1)) // From neighbour to owner
{
separationFaces[faceN] = true;
}
}
scalarList FriedrichModel::calcSeparationAngles
(
const bitSet& separationFaces
) const
{
scalarList separationAngles(mesh().faces().size(), Zero);
const labelUList& own = mesh().edgeOwner();
const labelUList& nbr = mesh().edgeNeighbour();
// Process internal faces
forAll(nbr, edgei)
{
if (!cornerEdges_[edgei]) continue;
const label faceO = own[edgei];
const label faceN = nbr[edgei];
if (separationFaces[faceO])
{
separationAngles[faceO] = cornerAngles_[edgei];
}
if (separationFaces[faceN])
{
separationAngles[faceN] = cornerAngles_[edgei];
}
}
// Skip the rest of the routine if the simulation is a serial run
if (!Pstream::parRun()) return separationAngles;
// Process processor faces
const edgeScalarField& phis = film().phi2s();
const faBoundaryMesh& patches = mesh().boundary();
for (const faPatch& fap : patches)
{
if (isA<processorFaPatch>(fap))
{
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdgei = fap.start();
const auto& phisp = phis.boundaryField()[patchi];
forAll(phisp, bndEdgei)
{
const label faceO = edgeFaces[bndEdgei];
const label meshEdgei(internalEdgei + bndEdgei);
if (!cornerEdges_[meshEdgei]) continue;
if (separationFaces[faceO])
{
separationAngles[faceO] = cornerAngles_[meshEdgei];
}
}
}
}
return separationAngles;
}
tmp<scalarField> FriedrichModel::Fratio() const
{
const areaVectorField Up(film().Up());
@ -64,11 +378,10 @@ tmp<scalarField> FriedrichModel::Fratio() const
const areaScalarField& sigma = film().sigma();
// Identify the faces where separation may occur
const bitSet& separationFaces = cornerDetectorPtr_->getCornerFaces();
const bitSet separationFaces(calcSeparationFaces());
// Calculate the corner angles corresponding to the separation faces
const scalarList& separationAngles = cornerDetectorPtr_->getCornerAngles();
const scalarList separationAngles(calcSeparationAngles(separationFaces));
// Initialize the force ratio
auto tFratio = tmp<scalarField>::New(mesh().faces().size(), Zero);
@ -118,7 +431,7 @@ tmp<scalarField> FriedrichModel::Fratio() const
if (isA<processorFaPatch>(fap))
{
const label patchi = fap.index();
const auto& edgeFaces = fap.edgeFaces();
const label internalEdgei = fap.start();
const auto& hp = h.boundaryField()[patchi];
const auto& Ufp = Uf.boundaryField()[patchi];
@ -132,18 +445,18 @@ tmp<scalarField> FriedrichModel::Fratio() const
// Skip the routine if the face is not a candidate for separation
if (!separationFaces[i]) continue;
const label faceO = edgeFaces[i];
const label meshEdgei = internalEdgei + i;
// Calculate the corner-angle trigonometric values
const scalar sinAngle = std::sin(separationAngles[faceO]);
const scalar cosAngle = std::cos(separationAngles[faceO]);
const scalar sinAngle = std::sin(cornerAngles_[meshEdgei]);
const scalar cosAngle = std::cos(cornerAngles_[meshEdgei]);
// Reynolds number (FLW:Eq. 16)
const scalar Re = hp[i]*mag(Ufp[i])*rhop[i]/mup[i];
// Weber number (FLW:Eq. 17)
const vector Urel(Upp[i] - Ufp[i]);
const scalar We = hp[i]*rhop_*sqr(mag(Urel))/(2.0*sigmap[i]);
const vector Urelp(Upp[i] - Ufp[i]);
const scalar We = hp[i]*rhop_*sqr(mag(Urelp))/(2.0*sigmap[i]);
// Characteristic breakup length (FLW:Eq. 15)
const scalar Lb =
@ -186,12 +499,13 @@ FriedrichModel::FriedrichModel
separationType::FULL
)
),
cornerDetectorPtr_(cornerDetectionModel::New(mesh(), film, dict)),
rhop_(dict.getScalar("rhop")),
magG_(mag(film.g().value())),
C0_(dict.getOrDefault<scalar>("C0", 0.882)),
C1_(dict.getOrDefault<scalar>("C1", -1.908)),
C2_(dict.getOrDefault<scalar>("C2", 1.264))
C2_(dict.getOrDefault<scalar>("C2", 1.264)),
cornerEdges_(calcCornerEdges()),
cornerAngles_(calcCornerAngles())
{
if (rhop_ < VSMALL)
{
@ -209,18 +523,10 @@ FriedrichModel::FriedrichModel
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
FriedrichModel::~FriedrichModel()
{} // cornerDetectionModel was forward declared
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
tmp<scalarField> FriedrichModel::separatedMassRatio() const
{
cornerDetectorPtr_->detectCorners();
tmp<scalarField> tFratio = Fratio();
const auto& Fratio = tFratio.cref();
@ -270,25 +576,6 @@ tmp<scalarField> FriedrichModel::separatedMassRatio() const
areaFratio.primitiveFieldRef() = Fratio;
areaFratio.write();
}
{
areaScalarField cornerAngles
(
mesh().newIOobject("cornerAngles"),
mesh(),
dimensionedScalar(dimless, Zero)
);
const bitSet& cornerFaces = cornerDetectorPtr_->getCornerFaces();
const scalarList& angles = cornerDetectorPtr_->getCornerAngles();
forAll(cornerFaces, i)
{
if (!cornerFaces[i]) continue;
cornerAngles[i] = radToDeg(angles[i]);
}
cornerAngles.write();
}
}
@ -296,16 +583,6 @@ tmp<scalarField> FriedrichModel::separatedMassRatio() const
}
/*
bool FriedrichModel::read(const dictionary& dict) const
{
// Add the base-class reading later
// Read the film separation model dictionary
return true;
}
*/
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace filmSeparationModels

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2024-2025 OpenCFD Ltd.
Copyright (C) 2024 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -116,14 +116,11 @@ Usage
filmSeparationCoeffs
{
// Mandatory entries
model Friedrich;
rhop <scalar>;
model Friedrich;
rhop <scalar>;
// Optional entries
separationType <word>;
// Inherited entries
cornerDetectionModel <word>;
separationType <word>;
}
\endverbatim
@ -133,23 +130,14 @@ Usage
model | Model name: Friedrich | word | yes | -
rhop | Primary-phase density | scalar | yes | -
separationType | Film separation type | word | no | full
cornerDetectionModel | Corner detector model | word | no | flux
\endtable
The inherited entries are elaborated in:
- \link cornerDetectionModel.H \endlink
Options for the \c separationType entry:
\verbatim
full | Full film separation (Friedrich et al., 2008)
partial | Partial film separation (Zhang et al., 2018)
\endverbatim
Options for the \c cornerDetectionModel entry:
\verbatim
flux | Flux-based corner detection algorithm
\endverbatim
SourceFiles
FriedrichModel.C
@ -164,10 +152,6 @@ SourceFiles
namespace Foam
{
// Forward Declarations
class cornerDetectionModel;
namespace filmSeparationModels
{
@ -197,9 +181,6 @@ class FriedrichModel
//- Film separation type
enum separationType separation_;
//- Corner-detection model
mutable autoPtr<cornerDetectionModel> cornerDetectorPtr_;
//- Approximate uniform mass density of primary phase
scalar rhop_;
@ -215,9 +196,53 @@ class FriedrichModel
//- Empirical constant for the partial separation model
scalar C2_;
//- List of flags identifying sharp-corner edges where separation
//- may occur
bitSet cornerEdges_;
//- Corner angles of sharp-corner edges where separation may occur
scalarList cornerAngles_;
// Private Member Functions
//- Return Boolean list of identified sharp-corner edges
bitSet calcCornerEdges() const;
//- Return true if the given edge is identified as sharp
bool isCornerEdgeSharp
(
const vector& faceCentreO,
const vector& faceCentreN,
const vector& faceNormalO,
const vector& faceNormalN
) const;
//- Return the list of sharp-corner angles for each edge
scalarList calcCornerAngles() const;
//- Return the sharp-corner angle for a given edge
scalar calcCornerAngle
(
const vector& faceNormalO,
const vector& faceNormalN
) const;
//- Return Boolean list of identified separation faces
bitSet calcSeparationFaces() const;
//- Return true if the given face is identified as a separation face
void isSeparationFace
(
bitSet& separationFaces,
const scalar phiEdge,
const label faceO,
const label faceN = -1
) const;
//- Return the list of sharp-corner angles for each face
scalarList calcSeparationAngles(const bitSet& separationFaces) const;
//- Return the film-separation force ratio per face
tmp<scalarField> Fratio() const;
@ -239,7 +264,7 @@ public:
// Destructor
virtual ~FriedrichModel();
virtual ~FriedrichModel() = default;
// Member Functions

View File

@ -27,7 +27,6 @@ License
#include "regionFaModel.H"
#include "faMesh.H"
#include "faMeshesRegistry.H"
#include "Time.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
@ -74,7 +73,7 @@ Foam::IOobject createModelIOobject
(
objName,
mesh.time().constant(),
faMeshesRegistry::New(mesh).thisDb(),
faMesh::Registry(mesh),
IOobjectOption::NO_READ,
IOobjectOption::NO_WRITE,
IOobjectOption::REGISTER
@ -141,12 +140,71 @@ Foam::IOobject createPropertiesIOobject
// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
void Foam::regionModels::regionFaModel::constructMeshObjects()
void Foam::regionModels::regionFaModel::constructMeshObjects
(
// Just for error reference
const dictionary& dict
)
{
regionMeshPtr_.reset(nullptr);
#if 1
regionMeshPtr_.reset
(
new faMesh(areaName_, primaryMesh_)
);
#else
// With try/catch and error messages
// DIY
// regionMeshPtr_ = faMesh::TryNew(areaName_, primaryMesh_);
// More heavy handed, but gives a better chance of locating
// the source of the error.
{
const bool oldThrowingError = FatalError.throwing(true);
const bool oldThrowingIOerr = FatalIOError.throwing(true);
try
{
regionMeshPtr_.reset
(
new faMesh(areaName_, primaryMesh_)
);
}
catch (const Foam::error& err)
{
Warning << err << nl << endl;
// Trickery to get original message
err.write(Warning, false);
}
catch (const Foam::IOerror& err)
{
Warning << err << nl << endl;
// Trickery to get original message
err.write(Warning, false);
}
FatalError.throwing(oldThrowingError);
FatalIOError.throwing(oldThrowingIOerr);
}
if (!regionMeshPtr_)
{
FatalError
<< "Failed to create finite-area mesh [" << areaName_
<< "] for model: "<< modelName_ << nl
<< "A common cause is an incorrect or"
" missing 'area' entry in the setup" << nl
<< ">>>>" << nl
<< dict.relativeName() << dict << "<<<<" << endl
<< exit(FatalError);
}
#endif
}
@ -234,7 +292,35 @@ Foam::regionModels::regionFaModel::regionFaModel
regionName_(dict.get<word>("region")),
coeffs_(dict.subOrEmptyDict(modelName + "Coeffs"))
{
constructMeshObjects();
// Suffix hint for variable names
if
(
coeffs_.readIfPresent("suffixing", suffixHint_)
|| dict.readIfPresent("suffixing", suffixHint_)
)
{
Switch sw = Switch::find(suffixHint_);
if (sw.good())
{
if (!sw) // No suffix
{
suffixHint_.clear();
}
}
else if (suffixHint_ == "default")
{
// Use default suffix
sw = true;
}
if (sw) // Default suffix
{
suffixHint_ = '_' + regionName_;
}
}
constructMeshObjects(dict);
initialise();
if (readFields)
@ -250,9 +336,14 @@ void Foam::regionModels::regionFaModel::evolve()
{
if (active_)
{
Info<< "\nEvolving " << modelName_ << " for region "
<< regionMesh().name() << " : "
<< polyMesh::regionName(areaName_) << endl;
Info<< "\nEvolving " << modelName_
<< " for region " << regionMesh().name();
if (!polyMesh::regionName(areaName_).empty())
{
Info<< " [" << areaName_ << "]";
}
Info<< endl;
preEvolveRegion();
@ -265,7 +356,7 @@ void Foam::regionModels::regionFaModel::evolve()
{
Info<< incrIndent;
info();
Info<< endl << decrIndent;
Info<< decrIndent << endl;
}
}
}

View File

@ -57,9 +57,20 @@ Usage
region | Name of operand region | word | yes | -
area | Name of the finite-area mesh | word | no | region0
active | Flag to activate the model | bool | yes | -
suffixing | Suffix hint for model variables | word | no | -
infoOutput | Flag to activate information output | bool | no | false
\endtable
Note
The \c suffixing parameter is a hint that individual models may use
when automatically generating variable names internally. For example,
a model \em may provide ("T" + suffixing) and ("q" + suffixing)
as its default names temperature and flux fields, respectively.
If the user specifies \c suffixing = "_foo", those \em default names
would then become "T_foo" and "q_foo", respectively.
Suffixing (true|false|none|default|...) currently selects between
no suffix and ('_'+region).
SourceFiles
regionFaModelI.H
regionFaModel.C
@ -90,7 +101,7 @@ class regionFaModel
// Private Member Functions
//- Construct region mesh and fields
void constructMeshObjects();
void constructMeshObjects(const dictionary&);
//- Initialise the region
void initialise();
@ -118,18 +129,21 @@ protected:
//- Model name
const word modelName_;
//- The finite-area mesh name
//- Suffix hint for automatic model variable names (default: "")
word suffixHint_;
//- The finite-area mesh name (default: region0)
word areaName_;
//- Region name
word regionName_;
//- Pointer to the region mesh database
autoPtr<faMesh> regionMeshPtr_;
//- Model coefficients dictionary
dictionary coeffs_;
//- Pointer to the region mesh database
autoPtr<faMesh> regionMeshPtr_;
//- Dictionary of output properties
autoPtr<IOdictionary> outputPropertiesPtr_;
@ -224,6 +238,15 @@ public:
//- Return mapping between surface and volume fields
const volSurfaceMapping& vsm() const;
//- The suffix hint for automatic model variable names
const word& suffixHint() const noexcept { return suffixHint_; }
//- Return the concatenation of \p base and the suffix hint
inline word suffixed(const char* base) const;
//- Return the concatenation of \p base and the suffix hint
inline word suffixed(const std::string& base) const;
// Evolution

View File

@ -27,7 +27,7 @@ License
#include "regionFaModel.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
inline const Foam::faMesh& Foam::regionModels::regionFaModel::regionMesh() const
{
@ -106,4 +106,18 @@ inline bool Foam::regionModels::regionFaModel::isRegionPatch
}
inline Foam::word
Foam::regionModels::regionFaModel::suffixed(const char* base) const
{
return word(base + suffixHint_);
}
inline Foam::word
Foam::regionModels::regionFaModel::suffixed(const std::string& base) const
{
return word(base + suffixHint_);
}
// ************************************************************************* //

View File

@ -123,14 +123,29 @@ thermalShell::thermalShell
)
:
thermalShellModel(modelType, mesh, dict),
hName_(dict.getOrDefault<word>("h", suffixed("hs"))),
qsName_(dict.getOrDefault<word>("qs", suffixed("qs"))),
qrName_(dict.getOrDefault<word>("qr", "none")),
nNonOrthCorr_(1),
// Only need/want thermal solid properties
thermo_(dict.subDict("thermo"), solidProperties::THERMAL),
h_
(
IOobject
(
hName_,
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
regionMesh()
),
qs_
(
IOobject
(
"qs_" + regionName_,
qsName_,
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::READ_IF_PRESENT,
@ -139,19 +154,6 @@ thermalShell::thermalShell
regionMesh(),
dimensionedScalar(dimPower/dimArea, Zero)
),
h_
(
IOobject
(
"h_" + regionName_,
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
IOobject::AUTO_WRITE
),
regionMesh()
),
qrName_(dict.getOrDefault<word>("qr", "none")),
thickness_(dict.getOrDefault<scalar>("thickness", 0))
{
init(dict);

View File

@ -5,7 +5,7 @@
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2019-2020 OpenCFD Ltd.
Copyright (C) 2019-2025 OpenCFD Ltd.
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
@ -56,11 +56,28 @@ Usage
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
thermalShellModel | Type name: thermalShell | word | yes | -
thermo | Solid thermal properties | dictionary | yes | -
qr | Name of radiative heat flux field | word | no | none
thickness | Uniform film thickness [m] | scalar | choice | -
Property | Description | Type | Reqd | Deflt
thermalShellModel | Type name: thermalShell | word | yes | -
h | Name of thickness field | word | no | hs (suffix)
qs | Name of source field | word | no | qs (suffix)
qr | Name of radiative heat flux field | word | no | none
thermo | Solid thermal properties | dict | yes | -
thickness | Uniform shell thickness [m] | scalar | choice | -
\endtable
Fields/variables used:
\table
Property | Description | Type | Deflt
h | Thickness | shell | hs (suffix)
qs | Source field | shell | qs (suffix)
qr | Radiative heat flux field | volume | none
\endtable
Note the following naming changes from 2056 and earlier:
\table
Keyword | Description | Keyword (old) | Deflt (old)
h | Temperature (volume) | h | "h_" + regionName
qs | Temperature (shell) | qs | "qs_" + regionName
\endtable
The inherited entries are elaborated in:
@ -68,7 +85,6 @@ Usage
SourceFiles
thermalShell.C
thermalShellI.H
\*---------------------------------------------------------------------------*/
@ -108,31 +124,38 @@ protected:
// Protected Data
// Solution parameters
//- Name of shell thickness [height] field (default: "hs" + suffix)
const word hName_;
//- Number of non orthogonal correctors
label nNonOrthCorr_;
//- Name of surface energy source (default: "qs" + suffix)
const word qsName_;
//- Name of (volume) radiative flux field (default: none)
const word qrName_;
// Thermo properties
// Solution Parameters
//- Solid properties
solidProperties thermo_;
//- Number of non orthogonal correctors
label nNonOrthCorr_;
// Source term fields
// Thermo properties
//- External surface energy source [J/m2/s]
areaScalarField qs_;
//- Solid properties
solidProperties thermo_;
//- Film thickness [m]
areaScalarField h_;
//- Name of the primary region radiative flux
const word qrName_;
// Source term fields
//- Uniform film thickness [m]
scalar thickness_;
//- Shell thickness field [m]
areaScalarField h_;
//- External surface energy source [J/m2/s]
areaScalarField qs_;
//- Uniform shell thickness [m]
scalar thickness_;
// Protected Member Functions
@ -174,7 +197,7 @@ public:
// Fields
//- Return the film specific heat capacity [J/kg/K]
//- Return the shell specific heat capacity [J/kg/K]
const tmp<areaScalarField> Cp() const;
//- Return density [kg/m3]

View File

@ -50,13 +50,14 @@ thermalShellModel::thermalShellModel
)
:
regionFaModel(mesh, "thermalShell", modelType, dict, true),
TName_(dict.get<word>("T")),
Tp_(mesh.lookupObject<volScalarField>(TName_)),
TName_(dict.getOrDefault<word>("T", suffixed("Ts"))),
TprimaryName_(dict.getOrDefault<word>("Tprimary", "T")),
Tp_(mesh.lookupObject<volScalarField>(TprimaryName_)),
T_
(
IOobject
(
"Ts_" + regionName_,
TName_,
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
@ -71,8 +72,8 @@ thermalShellModel::thermalShellModel
{
if (faOptions_.optionList::empty())
{
Info<< "No finite area options present for area : "
<< polyMesh::regionName(regionFaModel::areaName()) << endl;
Info<< "No finite-area options present for area:"
<< regionFaModel::areaName() << endl;
}
}

View File

@ -24,7 +24,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::regionModels::thermalShellModels::thermalShellModel
Foam::regionModels::thermalShellModel
Description
Intermediate class for thermal-shell finite-area models.
@ -34,12 +34,12 @@ Usage
\verbatim
<patchName>
{
// Mandatory entries
T <word>;
// Optional entries
thermalShellModel <word>;
T <word>;
Tprimary <word>;
// Inherited entries
...
}
@ -47,12 +47,27 @@ Usage
where the entries mean:
\table
Property | Description | Type | Reqd | Deflt
T | Name of operand temperature field | word | yes | -
thermalShellModel | Name of thermal-shell model <!--
Property | Description | Type | Reqd | Deflt
T | Name of operand temperature field | word | no | Ts (suffix)
Tprimary | Name of primary temperature field | word | no | T
thermalShellModel | Name of thermalShellModel thermal-shell model <!--
--> | word | choice | -
\endtable
Fields/variables used:
\table
Property | Description | Type | Deflt
T | Temperature | shell | Ts (suffix)
Tprimary | Temperature | volume | T
\endtable
\b BREAKING Naming changes from 2056 and earlier:
\table
Keyword | Description | Keyword (old) | Deflt (old)
T | Temperature (shell) | - | "Ts_" + regionName
Tprimary | Temperature (volume) | T | -
\endtable
The inherited entries are elaborated in:
- \link regionFaModel.H \endlink
@ -89,8 +104,11 @@ protected:
// Protected Data
//- Name of the temperature field
word TName_;
//- Name of shell temperature field (default: "Ts" + suffix)
const word TName_;
//- Name of volume temperature field (default: "T")
const word TprimaryName_;
//- Primary (volume) region temperature
const volScalarField& Tp_;

View File

@ -53,7 +53,7 @@ vibrationShellModel::vibrationShellModel
(
IOobject
(
"ws_" + regionName_,
dict.getOrDefault<word>("ws", suffixed("ws")),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::MUST_READ,
@ -65,7 +65,7 @@ vibrationShellModel::vibrationShellModel
(
IOobject
(
"as_" + regionName_,
dict.getOrDefault<word>("as", suffixed("as")),
regionMesh().time().timeName(),
regionMesh().thisDb(),
IOobject::NO_READ,

View File

@ -24,7 +24,7 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::regionModels::thermalShellModels::vibrationShellModel
Foam::regionModels::vibrationShellModel
Description
Intermediate class for vibration-shell finite-area models.
@ -52,9 +52,19 @@ Usage
\table
Property | Description | Type | Reqd | Deflt
vibrationShellModel | Name of vibration-shell model | word | yes | -
p | Name of the coupled field in the primary <!--
--> region | word | yes | -
solid | Solid properties | dictionary | yes | -
p | Name of primary pressure field | word | yes | -
solid | Solid properties | dict | yes | -
as | Name of shell acceleration field | word | no | as (suffix)
ps | Name of pressure (source) on shell | word | no | ps (suffix)
ws | Name of shell displacement field | word | no | ws (suffix)
\endtable
Naming changes from 2056 and earlier:
\table
Keyword | Description | Keyword (old) | Deflt (old)
as | Acceleration (shell) | - | "as_" + regionName
ps | Pressure on shell | - | "ps_" + regionName
ws | Displacement (shell) | - | "ws_" + regionName
\endtable
The inherited entries are elaborated in:
@ -93,19 +103,19 @@ protected:
// Protected Data
//- Shell displacement
//- Shell displacement [m]
areaScalarField w_;
//- Shell acceleration
//- Shell acceleration [m/s2]
areaScalarField a_;
//- Solid properties
solidProperties solid_;
//- Name of the coupled field in the primary region
//- Name of primary region acoustic pressure field
word pName_;
//- Primary region acoustic pressure
//- Primary region acoustic pressure [Pa]
const volScalarField& pa_;
//- Reference to faOptions
@ -167,26 +177,26 @@ public:
// Member Functions
//- Return primary region pa
const volScalarField& pa() const noexcept { return pa_; }
//- Return the primary region presssure
const volScalarField& pa() const noexcept { return pa_; }
//- Return shell displacement
const areaScalarField& w() const noexcept { return w_; }
//- Return the shell displacement
const areaScalarField& w() const noexcept { return w_; }
//- Return shell acceleration
const areaScalarField& a() const noexcept { return a_; }
//- Return the shell acceleration
const areaScalarField& a() const noexcept { return a_; }
//- Return faOptions
Foam::fa::options& faOptions() noexcept { return faOptions_; }
//- Return faOptions
Foam::fa::options& faOptions() noexcept { return faOptions_; }
//- Return solid properties
const solidProperties& solid() const noexcept { return solid_; }
//- Return solid properties
const solidProperties& solid() const noexcept { return solid_; }
// Evolution
//- Pre-evolve region
virtual void preEvolveRegion();
//- Pre-evolve region
virtual void preEvolveRegion();
};

View File

@ -10,7 +10,7 @@ FoamFile
version 2.0;
format ascii;
class areaScalarField;
object h_ceilingShell;
object hs;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -10,7 +10,7 @@ FoamFile
version 2.0;
format ascii;
class areaScalarField;
object wS;
object ws;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -41,7 +41,7 @@ boundaryField
nu 0.22;
}
region vibrationShell;
region vibration;
vibrationShellModel KirchhoffShell;
f0 0.04;

View File

@ -17,12 +17,13 @@ FoamFile
pressure
{
type externalFileSource;
fieldName ws_vibrationShell;
//fieldName ws_shell;
fieldName ws;
tableName p;
active true;
timeStart 0.001;
duration 0.03;
region vibrationShell;
region vibration;
selectionMode all;
sampleFormat ensight;

View File

@ -1,7 +1,7 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
@ -26,8 +26,9 @@ d2dt2Schemes
gradSchemes
{
default leastSquares;
grad(ws_vibrationShell) leastSquares;
default leastSquares;
grad(ws) leastSquares;
grad(ws_shell) leastSquares;
}
divSchemes

View File

@ -1,7 +1,7 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
@ -16,7 +16,7 @@ FoamFile
solvers
{
ws_vibrationShell
"(ws|ws_.*)"
{
solver diagonal;
preconditioner DILU;

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object IDefault;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [ 1 0 -3 0 0 0 0 ];
internalField uniform 0;
boundaryField
{
".*"
{
type greyDiffusiveRadiation;
value uniform 0;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,80 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object T;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 0 0 1 0 0 0];
internalField uniform 300;
boundaryField
{
"floor.*"
{
type fixedValue;
type compressible::thermalShell;
active true;
area floor;
region lower;
thermalShellModel thermalShell;
thermo
{
// Thermal properties
rho 1000;
kappa 200;
Cp 600;
emissivity 0;
}
qr qr;
thickness 1e-3;
value uniform 700;
}
"ceiling.*"
{
type compressible::thermalShell;
active true;
area ceiling;
region upper;
thermalShellModel thermalShell;
thermo
{
// Thermal properties
rho 1000;
kappa 200;
Cp 600;
emissivity 0;
}
qr qr;
thickness 1e-3;
value uniform 300;
}
"fixedWall.*"
{
type zeroGradient;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,30 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volVectorField;
object U;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 1 -1 0 0 0 0];
internalField uniform (0 0 0);
boundaryField
{
".*"
{
type noSlip;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object alphat;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [1 -1 -1 0 0 0 0];
internalField uniform 0;
boundaryField
{
".*"
{
type compressible::alphatWallFunction;
value uniform 0;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object epsilon;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 2 -3 0 0 0 0];
internalField uniform 0.01;
boundaryField
{
".*"
{
type epsilonWallFunction;
value uniform 0.01;
}
}
// ************************************************************************* //

View File

@ -1,7 +1,7 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
@ -10,7 +10,7 @@ FoamFile
version 2.0;
format ascii;
class areaScalarField;
object Ts_ceilingShell;
object Ts;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //

View File

@ -1,7 +1,7 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
@ -10,7 +10,7 @@ FoamFile
version 2.0;
format ascii;
class areaScalarField;
object h_ceilingShell;
object hs;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
@ -27,4 +27,4 @@ boundaryField
}
// ************************************************************************* //
// ************************************************************************* //

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class areaScalarField;
object Ts;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 0 0 1 0 0 0];
internalField uniform 300;
boundaryField
{
"side.*"
{
type fixedValue;
value uniform 300;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,30 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class areaScalarField;
object hs;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 1 0 0 0 0 0];
internalField uniform 1e-3;
boundaryField
{
"side.*"
{
type zeroGradient;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object k;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 2 -2 0 0 0 0];
internalField uniform 0.1;
boundaryField
{
".*"
{
type kqRWallFunction;
value uniform 0.1;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,31 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object nut;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 2 -1 0 0 0 0];
internalField uniform 0;
boundaryField
{
".*"
{
type nutkWallFunction;
value uniform 0;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,43 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object p;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [1 -1 -2 0 0 0 0];
internalField uniform 1e5;
boundaryField
{
"floor.*"
{
type calculated;
value $internalField;
}
"ceiling.*"
{
type calculated;
value $internalField;
}
"fixedWall.*"
{
type calculated;
value $internalField;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,43 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class volScalarField;
object p_rgh;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [1 -1 -2 0 0 0 0];
internalField uniform 1e5;
boundaryField
{
"floor.*"
{
type fixedFluxPressure;
value $internalField;
}
"ceiling.*"
{
type fixedFluxPressure;
value $internalField;
}
"fixedWall.*"
{
type fixedFluxPressure;
value $internalField;
}
}
// ************************************************************************* //

View File

@ -0,0 +1,15 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/CleanFunctions # Tutorial clean functions
#------------------------------------------------------------------------------
cleanCase
## foamListRegions -finite-area
for region in ceiling floor
do
cleanFaMesh -region "$region"
rmdir "constant/finite-area/$region" 2>/dev/null
done
#------------------------------------------------------------------------------

View File

@ -0,0 +1,12 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
./Allrun.pre
runApplication makeFaMesh -all-area-regions
runApplication $(getApplication)
#------------------------------------------------------------------------------

View File

@ -0,0 +1,16 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
runApplication blockMesh
runApplication topoSet
runApplication createPatch -overwrite
runApplication makeFaMesh
# runApplication $(getApplication)
#------------------------------------------------------------------------------

View File

@ -0,0 +1,16 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
./Allrun.pre
runParallel -s decompose redistributePar -decompose -no-finite-area
runParallel makeFaMesh -all-area-regions
runParallel $(getApplication)
# runParallel -s reconstruct redistributePar -reconstruct
#------------------------------------------------------------------------------

View File

@ -0,0 +1,16 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
./Allrun.pre
runApplication makeFaMesh -all-area-regions
runApplication decomposePar -all-area-regions
runParallel $(getApplication)
runApplication reconstructPar -all-area-regions
#------------------------------------------------------------------------------

View File

@ -0,0 +1,12 @@
#!/bin/sh
cd "${0%/*}" || exit # Run from this directory
. ${WM_PROJECT_DIR:?}/bin/tools/RunFunctions # Tutorial run functions
#------------------------------------------------------------------------------
runApplication blockMesh
runApplication topoSet
runApplication createPatch -overwrite
#------------------------------------------------------------------------------

View File

@ -0,0 +1 @@
Demonstrates/tests some multi-regions with finite-area

View File

@ -0,0 +1,26 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
object boundaryRadiationProperties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
".*"
{
type lookup;
emissivity 0.7;
absorptivity 0.7;
transmissivity 0.0;
}
// ************************************************************************* //

View File

@ -0,0 +1,23 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2512 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class dictionary;
location "constant/finite-area";
object regionProperties;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
regions
(
shell (ceiling floor)
);
// ************************************************************************* //

View File

@ -0,0 +1,21 @@
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: v2506 |
| \\ / A nd | Website: www.openfoam.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
FoamFile
{
version 2.0;
format ascii;
class uniformDimensionedVectorField;
object g;
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
dimensions [0 1 -2 0 0 0 0];
value (0 -9.81 0);
// ************************************************************************* //

Some files were not shown because too many files have changed in this diff Show More