Commit 0f3932b3 authored by Mark OLESEN's avatar Mark OLESEN
Browse files

ENH: region-wise decomposition specification for decomposeParDict

  Within decomposeParDict, it is now possible to specify a different
  decomposition method, methods coefficients or number of subdomains
  for each region individually.

  The top-level numberOfSubdomains remains mandatory, since this
  specifies the number of domains for the entire simulation.
  The individual regions may use the same number or fewer domains.

  Any optional method coefficients can be specified in a general
  "coeffs" entry or a method-specific one, eg "metisCoeffs".

  For multiLevel, only the method-specific "multiLevelCoeffs" dictionary
  is used, and is also mandatory.

----

ENH: shortcut specification for multiLevel.

  In addition to the longer dictionary form, it is also possible to
  use a shorter notation for multiLevel decomposition when the same
  decomposition method applies to each level.
parent 241b44ea
......@@ -330,7 +330,6 @@ int main(int argc, char *argv[])
}
forAll(regionNames, regioni)
{
const word& regionName = regionNames[regioni];
......@@ -338,13 +337,12 @@ int main(int argc, char *argv[])
Info<< "\n\nDecomposing mesh " << regionName << nl << endl;
// Determine the existing processor count directly
label nProcs = fileHandler().nProcs(runTime.path(), regionDir);
// Get requested numberOfSubdomains. Note: have no mesh yet so
// cannot use decompositionModel::New
const label nDomains = readLabel
// Get requested numberOfSubdomains directly from the dictionary.
// Note: have no mesh yet so cannot use decompositionModel::New
const label nDomains = decompositionMethod::nDomains
(
IOdictionary
(
......@@ -362,7 +360,7 @@ int main(int argc, char *argv[])
),
decompDictFile
)
).lookup("numberOfSubdomains")
)
);
if (decomposeFieldsOnly)
......
......@@ -15,129 +15,109 @@ FoamFile
}
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
numberOfSubdomains 2;
//- The total number of domains (mandatory)
numberOfSubdomains 256;
//- The decomposition method (mandatory)
method scotch;
// method hierarchical;
// method simple;
// method metis;
// method manual;
// method multiLevel;
// method structured; // does 2D decomposition of structured mesh
// Optional decomposition constraints
//constraints
//{
// preserveBaffles
// {
// //- Keep owner and neighbour of baffles on same processor (i.e.
// // keep it detectable as a baffle). Baffles are two boundary face
// // sharing the same points
// type preserveBaffles;
// }
// preserveFaceZones
// {
// //- Keep owner and neighbour on same processor for faces in zones
// type preserveFaceZones;
// zones (".*");
// }
// preservePatches
// {
// //- Keep owner and neighbour on same processor for faces in patches
// // (only makes sense for cyclic patches. Not suitable for e.g.
// // cyclicAMI since these are not coupled on the patch level. Use
// // singleProcessorFaceSets for those)
// type preservePatches;
// patches (".*");
// }
// singleProcessorFaceSets
// {
// //- Keep all of faceSet on a single processor. This puts all cells
// // connected with a point, edge or face on the same processor.
// // (just having face connected cells might not guarantee a balanced
// // decomposition)
// // The processor can be -1 (the decompositionMethod chooses the
// // processor for a good load balance) or explicitly provided (upsets
// // balance)
// type singleProcessorFaceSets;
// singleProcessorFaceSets ((f1 -1));
// }
// refinementHistory
// {
// //- Decompose cells such that all cell originating from single cell
// // end up on same processor
// type refinementHistory;
// }
//}
// Deprecated form of specifying decomposition constraints:
//- Keep owner and neighbour on same processor for faces in zones:
// preserveFaceZones (heater solid1 solid3);
//- Keep owner and neighbour on same processor for faces in patches:
// (makes sense only for cyclic patches. Not suitable for e.g. cyclicAMI
// since these are not coupled on the patch level. Use
// singleProcessorFaceSets for those)
//preservePatches (cyclic_half0 cyclic_half1);
//- Keep all of faceSet on a single processor. This puts all cells
// connected with a point, edge or face on the same processor.
// (just having face connected cells might not guarantee a balanced
// decomposition)
// The processor can be -1 (the decompositionMethod chooses the processor
// for a good load balance) or explicitly provided (upsets balance).
//singleProcessorFaceSets ((f0 -1));
//- Optional region-wise decomposition.
// Can specify a different method.
// The number of subdomains can be less than the top-level numberOfSubdomains.
regions
{
water
{
numberOfSubdomains 128;
method metis;
}
//- Keep owner and neighbour of baffles on same processor (i.e. keep it
// detectable as a baffle). Baffles are two boundary face sharing the
// same points.
//preserveBaffles true;
".*solid"
{
numberOfSubdomains 4;
method metis;
}
heater
{
numberOfSubdomains 1;
method none;
}
}
//- Use the volScalarField named here as a weight for each cell in the
// decomposition. For example, use a particle population field to decompose
// for a balanced number of particles in a lagrangian simulation.
// weightField dsmcRhoNMean;
// Coefficients for the decomposition method are either as a
// general "coeffs" dictionary or method-specific "<method>Coeffs".
// For multiLevel, using multiLevelCoeffs only.
method scotch;
//method hierarchical;
// method simple;
// method metis;
// method manual;
// method multiLevel;
// method structured; // does 2D decomposition of structured mesh
multiLevelCoeffs
{
// Decomposition methods to apply in turn. This is like hierarchical but
// fully general - every method can be used at every level.
// multiLevel decomposition methods to apply in turn.
// This is like hierarchical but fully general
// - every method can be used at every level.
// Only sub-dictionaries containing the keyword "method" are used.
//
level0
{
numberOfSubdomains 64;
//method simple;
//simpleCoeffs
//{
// n (2 1 1);
// delta 0.001;
//}
numberOfSubdomains 16;
method scotch;
}
level1
{
numberOfSubdomains 4;
numberOfSubdomains 2;
method scotch;
coeffs
{
n (2 1 1);
delta 0.001;
}
}
level2
{
numberOfSubdomains 8;
// method simple;
method scotch;
}
}
multiLevelCoeffs
{
// Compact multiLevel specification, activated by the presence of the
// keywords "method" and "domains"
method scotch;
domains (16 2 8);
//// Or with implicit '16' for the first level with numberOfSubdomains=256
//domains (2 8);
}
// Desired output
// Other example coefficents
simpleCoeffs
{
n (2 1 1);
delta 0.001;
// delta 0.001; //< default value = 0.001
}
hierarchicalCoeffs
{
n (1 2 1);
delta 0.001;
order xyz;
// delta 0.001; //< default value = 0.001
// order xyz; //< default order = xyz
}
metisCoeffs
......@@ -181,9 +161,17 @@ structuredCoeffs
method scotch;
}
//- Use the volScalarField named here as a weight for each cell in the
// decomposition. For example, use a particle population field to decompose
// for a balanced number of particles in a lagrangian simulation.
// weightField dsmcRhoNMean;
//// Is the case distributed? Note: command-line argument -roots takes
//// precedence
//distributed yes;
//
//// Per slave (so nProcs-1 entries) the directory above the case.
//roots
//(
......@@ -191,4 +179,77 @@ structuredCoeffs
// "/tmp"
//);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Decomposition constraints
//constraints
//{
// preserveBaffles
// {
// //- Keep owner and neighbour of baffles on same processor (i.e.
// // keep it detectable as a baffle). Baffles are two boundary face
// // sharing the same points
// type preserveBaffles;
// }
// preserveFaceZones
// {
// //- Keep owner and neighbour on same processor for faces in zones
// type preserveFaceZones;
// zones (".*");
// }
// preservePatches
// {
// //- Keep owner and neighbour on same processor for faces in patches
// // (only makes sense for cyclic patches. Not suitable for e.g.
// // cyclicAMI since these are not coupled on the patch level. Use
// // singleProcessorFaceSets for those)
// type preservePatches;
// patches (".*");
// }
// singleProcessorFaceSets
// {
// //- Keep all of faceSet on a single processor. This puts all cells
// // connected with a point, edge or face on the same processor.
// // (just having face connected cells might not guarantee a balanced
// // decomposition)
// // The processor can be -1 (the decompositionMethod chooses the
// // processor for a good load balance) or explicitly provided (upsets
// // balance)
// type singleProcessorFaceSets;
// singleProcessorFaceSets ((f1 -1));
// }
// refinementHistory
// {
// //- Decompose cells such that all cell originating from single cell
// // end up on same processor
// type refinementHistory;
// }
//}
// Deprecated form of specifying decomposition constraints:
//- Keep owner and neighbour on same processor for faces in zones:
// preserveFaceZones (heater solid1 solid3);
//- Keep owner and neighbour on same processor for faces in patches:
// (makes sense only for cyclic patches. Not suitable for e.g. cyclicAMI
// since these are not coupled on the patch level. Use
// singleProcessorFaceSets for those)
//preservePatches (cyclic_half0 cyclic_half1);
//- Keep all of faceSet on a single processor. This puts all cells
// connected with a point, edge or face on the same processor.
// (just having face connected cells might not guarantee a balanced
// decomposition)
// The processor can be -1 (the decompositionMethod chooses the processor
// for a good load balance) or explicitly provided (upsets balance).
//singleProcessorFaceSets ((f0 -1));
//- Keep owner and neighbour of baffles on same processor (i.e. keep it
// detectable as a baffle). Baffles are two boundary face sharing the
// same points.
//preserveBaffles true;
// ************************************************************************* //
......@@ -49,10 +49,8 @@ void Foam::domainDecomposition::mark
labelList& elementToZone
)
{
forAll(zoneElems, i)
for (const label pointi : zoneElems)
{
label pointi = zoneElems[i];
if (elementToZone[pointi] == -1)
{
// First occurrence
......@@ -69,7 +67,6 @@ void Foam::domainDecomposition::mark
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
// from components
Foam::domainDecomposition::domainDecomposition
(
const IOobject& io,
......@@ -98,13 +95,13 @@ Foam::domainDecomposition::domainDecomposition
decompDictFile_(decompDictFile),
nProcs_
(
readInt
decompositionMethod::nDomains
(
decompositionModel::New
(
*this,
decompDictFile
).lookup("numberOfSubdomains")
)
)
),
distributed_(false),
......
......@@ -46,11 +46,11 @@ void Foam::domainDecomposition::distributeCells()
decompDictFile_
);
word weightName;
scalarField cellWeights;
if (method.found("weightField"))
{
word weightName = method.lookup("weightField");
if (method.readIfPresent("weightField", weightName))
{
volScalarField weights
(
IOobject
......
......@@ -58,7 +58,7 @@ int readNumProcs
dictFile = dictFile / dictName;
}
return readInt
return decompositionMethod::nDomains
(
IOdictionary
(
......@@ -75,7 +75,7 @@ int readNumProcs
),
dictFile
)
).lookup("numberOfSubdomains")
)
);
}
......
......@@ -73,10 +73,20 @@ Foam::label Foam::kahipDecomp::decomposeSerial
Foam::kahipDecomp::kahipDecomp
(
const dictionary& decompositionDict
const dictionary& decompDict
)
:
metisLikeDecomp(decompositionDict)
metisLikeDecomp("kahip", decompDict)
{}
Foam::kahipDecomp::kahipDecomp
(
const dictionary& decompDict,
const word& regionName
)
:
metisLikeDecomp("kahip", decompDict, regionName)
{}
......
......@@ -73,10 +73,20 @@ Foam::label Foam::metisDecomp::decomposeSerial
Foam::metisDecomp::metisDecomp
(
const dictionary& decompositionDict
const dictionary& decompDict
)
:
metisLikeDecomp(decompositionDict)
metisLikeDecomp("metis", decompDict)
{}
Foam::metisDecomp::metisDecomp
(
const dictionary& decompDict,
const word& regionName
)
:
metisLikeDecomp("metis", decompDict, regionName)
{}
......
......@@ -98,10 +98,22 @@ Foam::label Foam::ptscotchDecomp::decompose
Foam::ptscotchDecomp::ptscotchDecomp
(
const dictionary& decompositionDict
const dictionary& decompDict
)
:
decompositionMethod(decompositionDict)
decompositionMethod(decompDict),
coeffsDict_(dictionary::null)
{}
Foam::ptscotchDecomp::ptscotchDecomp
(
const dictionary& decompDict,
const word& regionName
)
:
decompositionMethod(decompDict, regionName),
coeffsDict_(dictionary::null)
{}
......
......@@ -80,10 +80,20 @@ Foam::label Foam::scotchDecomp::decomposeSerial
Foam::scotchDecomp::scotchDecomp
(
const dictionary& decompositionDict
const dictionary& decompDict
)
:
metisLikeDecomp(decompositionDict)
metisLikeDecomp("scotch", decompDict)
{}
Foam::scotchDecomp::scotchDecomp
(
const dictionary& decompDict,
const word& regionName
)
:
metisLikeDecomp("scotch", decompDict, regionName)
{}
......
......@@ -34,6 +34,7 @@ namespace Foam
defineTypeNameAndDebug(decompositionModel, 0);
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::decompositionModel::decompositionModel
......
......@@ -75,14 +75,14 @@ public:
// Selectors
//- Read (optionallly from absolute path) & register on mesh
//- Read (optionally from absolute path) & register on mesh
static const decompositionModel& New
(
const polyMesh& mesh,
const fileName& decompDictFile = ""
);
//- Read (optionallly from supplied dictionary) & register on mesh
//- Read (optionally from supplied dictionary) & register on mesh
static const decompositionModel& New
(
const polyMesh& mesh,
......@@ -94,42 +94,51 @@ public:
// Constructors
//- Construct from typeName or optional path to controlDictionary
decompositionModel(const polyMesh&, const fileName& = "");
decompositionModel
(
const polyMesh& mesh,
const fileName& decompDictFile = ""
);
//- Construct from typeName or optional path to controlDictionary
decompositionModel
(
const polyMesh&,
const polyMesh& mesh,
const dictionary& dict,
const fileName& = ""
const fileName& decompDictFile = ""
);
// Member functions
// Member Functions
decompositionMethod& decomposer() const
{
if (!decomposerPtr_.valid())
{
decomposerPtr_ = decompositionMethod::New(*this);
decomposerPtr_ =
decompositionMethod::New
(
*this,
this->mesh().name() // Name of mesh region
);
}
return decomposerPtr_();
}
//- Helper: return IOobject with optionally absolute path provided
static IOobject selectIO(const IOobject&, const fileName&);
static IOobject selectIO(const IOobject& io, const fileName& f);
// UpdateableMeshObject
// UpdateableMeshObject
virtual bool movePoints()
{
return false;
}
virtual bool movePoints()
{
return false;
}
virtual void updateMesh(const mapPolyMesh&)
{}
virtual void updateMesh(const mapPolyMesh&)
{}
};
......
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2017 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation, either version 3 of the License, or (at your option)
any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
details.