mirror of
https://develop.openfoam.com/Development/openfoam.git
synced 2025-11-28 03:28:01 +00:00
- for reciprocal values, gMinMax() first and then calculate the reciprocal, which avoids creating temporaries STYLE: prefer MinMax to separate min/max accounting COMP: namespace qualify min/max for deltaT, CourantNo, etc (#3348)
402 lines
10 KiB
C
402 lines
10 KiB
C
/*---------------------------------------------------------------------------*\
|
|
========= |
|
|
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
|
|
\\ / O peration |
|
|
\\ / A nd | www.openfoam.com
|
|
\\/ M anipulation |
|
|
-------------------------------------------------------------------------------
|
|
Copyright (C) 2011-2017 OpenFOAM Foundation
|
|
Copyright (C) 2018-2020 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/>.
|
|
|
|
Application
|
|
refineMesh
|
|
|
|
Group
|
|
grpMeshManipulationUtilities
|
|
|
|
Description
|
|
Utility to refine cells in multiple directions.
|
|
|
|
Command-line option handling:
|
|
- If -all specified or no refineMeshDict exists or, refine all cells
|
|
- If -dict \<file\> specified refine according to \<file\>
|
|
- If refineMeshDict exists refine according to refineMeshDict
|
|
|
|
When the refinement or all cells is selected apply 3D refinement for 3D
|
|
cases and 2D refinement for 2D cases.
|
|
|
|
\*---------------------------------------------------------------------------*/
|
|
|
|
#include "argList.H"
|
|
#include "polyMesh.H"
|
|
#include "Time.H"
|
|
#include "cellSet.H"
|
|
#include "multiDirRefinement.H"
|
|
#include "labelIOList.H"
|
|
#include "IOdictionary.H"
|
|
#include "syncTools.H"
|
|
|
|
using namespace Foam;
|
|
|
|
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
|
|
|
|
// Max cos angle for edges to be considered aligned with axis.
|
|
static const scalar edgeTol = 1e-3;
|
|
|
|
|
|
// Print edge statistics on mesh.
|
|
void printEdgeStats(const polyMesh& mesh)
|
|
{
|
|
label nAny(0);
|
|
label nX(0);
|
|
label nY(0);
|
|
label nZ(0);
|
|
|
|
scalarMinMax limitsAny(GREAT, -GREAT);
|
|
scalarMinMax limitsX(limitsAny);
|
|
scalarMinMax limitsY(limitsAny);
|
|
scalarMinMax limitsZ(limitsAny);
|
|
|
|
bitSet isMasterEdge(syncTools::getMasterEdges(mesh));
|
|
|
|
const edgeList& edges = mesh.edges();
|
|
|
|
for (const label edgei : isMasterEdge)
|
|
{
|
|
const edge& e = edges[edgei];
|
|
|
|
vector eVec(e.vec(mesh.points()));
|
|
|
|
scalar eMag = mag(eVec);
|
|
|
|
eVec /= eMag;
|
|
|
|
if (mag(eVec.x()) > 1-edgeTol)
|
|
{
|
|
limitsX.add(eMag);
|
|
nX++;
|
|
}
|
|
else if (mag(eVec.y()) > 1-edgeTol)
|
|
{
|
|
limitsY.add(eMag);
|
|
nY++;
|
|
}
|
|
else if (mag(eVec.z()) > 1-edgeTol)
|
|
{
|
|
limitsZ.add(eMag);
|
|
nZ++;
|
|
}
|
|
else
|
|
{
|
|
limitsAny.add(eMag);
|
|
nAny++;
|
|
}
|
|
}
|
|
|
|
reduce(nX, sumOp<label>());
|
|
reduce(nY, sumOp<label>());
|
|
reduce(nZ, sumOp<label>());
|
|
reduce(nAny, sumOp<label>());
|
|
|
|
reduce(limitsX, sumOp<scalarMinMax>());
|
|
reduce(limitsY, sumOp<scalarMinMax>());
|
|
reduce(limitsZ, sumOp<scalarMinMax>());
|
|
reduce(limitsAny, sumOp<scalarMinMax>());
|
|
|
|
Info<< "Mesh edge statistics:" << nl
|
|
<< " x aligned : number:" << nX
|
|
<< "\tminLen:" << limitsX.min() << "\tmaxLen:" << limitsX.max() << nl
|
|
<< " y aligned : number:" << nY
|
|
<< "\tminLen:" << limitsY.min() << "\tmaxLen:" << limitsY.max() << nl
|
|
<< " z aligned : number:" << nZ
|
|
<< "\tminLen:" << limitsZ.min() << "\tmaxLen:" << limitsZ.max() << nl
|
|
<< " other : number:" << nAny
|
|
<< "\tminLen:" << limitsAny.min()
|
|
<< "\tmaxLen:" << limitsAny.max() << nl << endl;
|
|
}
|
|
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
argList::addNote
|
|
(
|
|
"Refine cells in multiple directions"
|
|
);
|
|
|
|
#include "addOverwriteOption.H"
|
|
#include "addRegionOption.H"
|
|
|
|
argList::addOption("dict", "file", "Alternative refineMeshDict");
|
|
|
|
argList::addBoolOption
|
|
(
|
|
"all",
|
|
"Refine all cells"
|
|
);
|
|
|
|
argList::noFunctionObjects(); // Never use function objects
|
|
|
|
#include "setRootCase.H"
|
|
#include "createTime.H"
|
|
#include "createNamedPolyMesh.H"
|
|
|
|
const word oldInstance = mesh.pointsInstance();
|
|
|
|
printEdgeStats(mesh);
|
|
|
|
//
|
|
// Read/construct control dictionary
|
|
//
|
|
|
|
const bool refineAllCells = args.found("all");
|
|
const bool overwrite = args.found("overwrite");
|
|
|
|
// List of cells to refine
|
|
labelList refCells;
|
|
|
|
// Dictionary to control refinement
|
|
dictionary refineDict;
|
|
|
|
// The -all option has precedence over -dict, or anything else
|
|
if (!refineAllCells)
|
|
{
|
|
const word dictName("refineMeshDict");
|
|
|
|
// Obtain dictPath here for messages
|
|
fileName dictPath = args.getOrDefault<fileName>("dict", "");
|
|
|
|
IOobject dictIO = IOobject::selectIO
|
|
(
|
|
IOobject
|
|
(
|
|
dictName,
|
|
runTime.system(),
|
|
mesh,
|
|
IOobject::MUST_READ
|
|
),
|
|
dictPath
|
|
);
|
|
|
|
// The reported dictPath will not be full resolved for the output
|
|
// (it will just be the -dict value) but this is purely cosmetic.
|
|
|
|
if (dictIO.typeHeaderOk<IOdictionary>(true))
|
|
{
|
|
refineDict = IOdictionary(dictIO);
|
|
|
|
Info<< "Refining according to ";
|
|
|
|
if (dictPath.empty())
|
|
{
|
|
Info<< dictName;
|
|
}
|
|
else
|
|
{
|
|
Info<< dictPath;
|
|
}
|
|
Info<< nl << endl;
|
|
}
|
|
else if (dictPath.empty())
|
|
{
|
|
Info<< "Refinement dictionary " << dictName << " not found" << nl;
|
|
}
|
|
else
|
|
{
|
|
FatalErrorInFunction
|
|
<< "Cannot open specified refinement dictionary "
|
|
<< dictPath
|
|
<< exit(FatalError);
|
|
}
|
|
}
|
|
|
|
if (refineDict.size())
|
|
{
|
|
const word setName(refineDict.get<word>("set"));
|
|
|
|
cellSet cells(mesh, setName);
|
|
|
|
Info<< "Read " << returnReduce(cells.size(), sumOp<label>())
|
|
<< " cells from cellSet "
|
|
<< cells.instance()/cells.local()/cells.name()
|
|
<< endl << endl;
|
|
|
|
refCells = cells.toc();
|
|
}
|
|
else
|
|
{
|
|
Info<< "Refining all cells" << nl << endl;
|
|
|
|
// Select all cells
|
|
refCells = identity(mesh.nCells());
|
|
|
|
if (mesh.nGeometricD() == 3)
|
|
{
|
|
Info<< "3D case; refining all directions" << nl << endl;
|
|
|
|
wordList directions(3);
|
|
directions[0] = "tan1";
|
|
directions[1] = "tan2";
|
|
directions[2] = "normal";
|
|
refineDict.add("directions", directions);
|
|
|
|
// Use hex cutter
|
|
refineDict.add("useHexTopology", "true");
|
|
}
|
|
else
|
|
{
|
|
const Vector<label> dirs(mesh.geometricD());
|
|
|
|
wordList directions(2);
|
|
|
|
if (dirs.x() == -1)
|
|
{
|
|
Info<< "2D case; refining in directions y,z\n" << endl;
|
|
directions[0] = "tan2";
|
|
directions[1] = "normal";
|
|
}
|
|
else if (dirs.y() == -1)
|
|
{
|
|
Info<< "2D case; refining in directions x,z\n" << endl;
|
|
directions[0] = "tan1";
|
|
directions[1] = "normal";
|
|
}
|
|
else
|
|
{
|
|
Info<< "2D case; refining in directions x,y\n" << endl;
|
|
directions[0] = "tan1";
|
|
directions[1] = "tan2";
|
|
}
|
|
|
|
refineDict.add("directions", directions);
|
|
|
|
// Use standard cutter
|
|
refineDict.add("useHexTopology", "false");
|
|
}
|
|
|
|
refineDict.add("coordinateSystem", "global");
|
|
|
|
dictionary coeffsDict;
|
|
coeffsDict.add("tan1", vector(1, 0, 0));
|
|
coeffsDict.add("tan2", vector(0, 1, 0));
|
|
refineDict.add("globalCoeffs", coeffsDict);
|
|
|
|
refineDict.add("geometricCut", "false");
|
|
refineDict.add("writeMesh", "false");
|
|
}
|
|
|
|
|
|
string oldTimeName(runTime.timeName());
|
|
|
|
if (!overwrite)
|
|
{
|
|
++runTime;
|
|
}
|
|
|
|
|
|
// Multi-directional refinement (does multiple iterations)
|
|
multiDirRefinement multiRef(mesh, refCells, refineDict);
|
|
|
|
|
|
// Write resulting mesh
|
|
if (overwrite)
|
|
{
|
|
mesh.setInstance(oldInstance);
|
|
}
|
|
mesh.write();
|
|
|
|
|
|
// Get list of cell splits.
|
|
// (is for every cell in old mesh the cells they have been split into)
|
|
const labelListList& oldToNew = multiRef.addedCells();
|
|
|
|
|
|
// Create cellSet with added cells for easy inspection
|
|
cellSet newCells(mesh, "refinedCells", refCells.size());
|
|
|
|
for (const labelList& added : oldToNew)
|
|
{
|
|
newCells.insert(added);
|
|
}
|
|
|
|
Info<< "Writing refined cells ("
|
|
<< returnReduce(newCells.size(), sumOp<label>())
|
|
<< ") to cellSet "
|
|
<< newCells.instance()/newCells.local()/newCells.name()
|
|
<< endl << endl;
|
|
|
|
newCells.write();
|
|
|
|
|
|
//
|
|
// Invert cell split to construct map from new to old
|
|
//
|
|
|
|
labelIOList newToOld
|
|
(
|
|
IOobject
|
|
(
|
|
"cellMap",
|
|
runTime.timeName(),
|
|
polyMesh::meshSubDir,
|
|
mesh,
|
|
IOobject::NO_READ,
|
|
IOobject::AUTO_WRITE
|
|
),
|
|
mesh.nCells()
|
|
);
|
|
newToOld.note() =
|
|
"From cells in mesh at "
|
|
+ runTime.timeName()
|
|
+ " to cells in mesh at "
|
|
+ oldTimeName;
|
|
|
|
|
|
forAll(oldToNew, oldCelli)
|
|
{
|
|
const labelList& added = oldToNew[oldCelli];
|
|
|
|
if (added.size())
|
|
{
|
|
for (const label celli : added)
|
|
{
|
|
newToOld[celli] = oldCelli;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Unrefined cell
|
|
newToOld[oldCelli] = oldCelli;
|
|
}
|
|
}
|
|
|
|
Info<< "Writing map from new to old cell to "
|
|
<< newToOld.objectPath() << nl << endl;
|
|
|
|
newToOld.write();
|
|
|
|
printEdgeStats(mesh);
|
|
|
|
Info<< "End\n" << endl;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
// ************************************************************************* //
|