openfoam/applications/utilities/mesh/conversion/tetgenToFoam/tetgenToFoam.C

567 lines
14 KiB
C

/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | www.openfoam.com
\\/ M anipulation |
-------------------------------------------------------------------------------
Copyright (C) 2011-2016 OpenFOAM Foundation
Copyright (C) 2015-2021 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
tetgenToFoam
Group
grpMeshConversionUtilities
Description
Convert tetgen .ele and .node and .face files to an OpenFOAM mesh.
Make sure to use add boundary attributes to the smesh file
(5 fifth column in the element section)
and run tetgen with -f option.
Sample smesh file:
\verbatim
# cube.smesh -- A 10x10x10 cube
8 3
1 0 0 0
2 0 10 0
3 10 10 0
4 10 0 0
5 0 0 10
6 0 10 10
7 10 10 10
8 10 0 10
6 1 # 1 for boundary info present
4 1 2 3 4 11 # region number 11
4 5 6 7 8 21 # region number 21
4 1 2 6 5 3
4 4 3 7 8 43
4 1 5 8 4 5
4 2 6 7 3 65
0
0
\endverbatim
Note
- for some reason boundary faces point inwards. I just reverse them
always. Might use some geometric check instead.
- marked faces might not actually be boundary faces of mesh.
This is hopefully handled now by first constructing without boundaries
and then reconstructing with boundary faces.
\*---------------------------------------------------------------------------*/
#include "argList.H"
#include "Time.H"
#include "polyMesh.H"
#include "IFstream.H"
#include "cellModel.H"
using namespace Foam;
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Find label of face.
label findFace(const primitiveMesh& mesh, const face& f)
{
const labelList& pFaces = mesh.pointFaces()[f[0]];
forAll(pFaces, i)
{
label facei = pFaces[i];
if (mesh.faces()[facei] == f)
{
return facei;
}
}
FatalErrorInFunction
<< "Cannot find face " << f << " in mesh." << abort(FatalError);
return -1;
}
int main(int argc, char *argv[])
{
argList::addNote
(
"Convert tetgen .ele and .node and .face files to an OpenFOAM mesh"
);
argList::addArgument("prefix", "The prefix for the input tetgen files");
argList::addBoolOption
(
"noFaceFile",
"Skip reading .face file for boundary information"
);
#include "setRootCase.H"
#include "createTime.H"
const auto prefix = args.get<fileName>(1);
const bool readFaceFile = !args.found("noFaceFile");
const fileName nodeFile(prefix + ".node");
const fileName eleFile(prefix + ".ele");
const fileName faceFile(prefix + ".face");
if (!readFaceFile)
{
Info<< "Files:" << endl
<< " nodes : " << nodeFile << endl
<< " elems : " << eleFile << endl
<< endl;
}
else
{
Info<< "Files:" << endl
<< " nodes : " << nodeFile << endl
<< " elems : " << eleFile << endl
<< " faces : " << faceFile << endl
<< endl;
Info<< "Reading .face file for boundary information" << nl << endl;
}
if (!isFile(nodeFile) || !isFile(eleFile))
{
FatalErrorInFunction
<< "Cannot read " << nodeFile << " or " << eleFile
<< exit(FatalError);
}
if (readFaceFile && !isFile(faceFile))
{
FatalErrorInFunction
<< "Cannot read " << faceFile << endl
<< "Did you run tetgen with -f option?" << endl
<< "If you don't want to read the .face file and thus not have"
<< " patches please\nrerun with the -noFaceFile option"
<< exit(FatalError);
}
IFstream nodeStream(nodeFile);
//
// Read nodes.
//
// Read header.
string line;
do
{
nodeStream.getLine(line);
}
while (line.starts_with('#'));
IStringStream nodeLine(line);
label nNodes, nDims, nNodeAttr;
bool hasRegion;
nodeLine >> nNodes >> nDims >> nNodeAttr >> hasRegion;
Info<< "Read .node header:" << endl
<< " nodes : " << nNodes << endl
<< " nDims : " << nDims << endl
<< " nAttr : " << nNodeAttr << endl
<< " hasRegion : " << hasRegion << endl
<< endl;
//
// read points
//
pointField points(nNodes);
Map<label> nodeToPoint(nNodes);
{
labelList pointIndex(nNodes);
label pointi = 0;
while (nodeStream.good())
{
nodeStream.getLine(line);
if (line.size() && line[0] != '#')
{
IStringStream nodeLine(line);
label nodeI;
scalar x, y, z;
label dummy;
nodeLine >> nodeI >> x >> y >> z;
for (label i = 0; i < nNodeAttr; i++)
{
nodeLine >> dummy;
}
if (hasRegion)
{
nodeLine >> dummy;
}
// Store point and node number.
points[pointi] = point(x, y, z);
nodeToPoint.insert(nodeI, pointi);
pointi++;
}
}
if (pointi != nNodes)
{
FatalIOErrorInFunction(nodeStream)
<< "Only " << pointi << " nodes present instead of " << nNodes
<< " from header." << exit(FatalIOError);
}
}
//
// read elements
//
IFstream eleStream(eleFile);
do
{
eleStream.getLine(line);
}
while (line.starts_with('#'));
IStringStream eleLine(line);
label nTets, nPtsPerTet, nElemAttr;
eleLine >> nTets >> nPtsPerTet >> nElemAttr;
Info<< "Read .ele header:" << endl
<< " tets : " << nTets << endl
<< " pointsPerTet : " << nPtsPerTet << endl
<< " nAttr : " << nElemAttr << endl
<< endl;
if (nPtsPerTet != 4)
{
FatalIOErrorInFunction(eleStream)
<< "Cannot handle tets with "
<< nPtsPerTet << " points per tetrahedron in .ele file" << endl
<< "Can only handle tetrahedra with four points"
<< exit(FatalIOError);
}
if (nElemAttr != 0)
{
WarningInFunction
<< "Element attributes (third elemenent in .ele header)"
<< " not used" << endl;
}
const cellModel& tet = cellModel::ref(cellModel::TET);
labelList tetPoints(4);
cellShapeList cells(nTets);
label celli = 0;
while (eleStream.good())
{
eleStream.getLine(line);
if (line.size() && line[0] != '#')
{
IStringStream eleLine(line);
label elemI;
eleLine >> elemI;
for (label i = 0; i < 4; i++)
{
label nodeI;
eleLine >> nodeI;
tetPoints[i] = nodeToPoint[nodeI];
}
cells[celli++].reset(tet, tetPoints);
// Skip attributes
for (label i = 0; i < nElemAttr; i++)
{
label dummy;
eleLine >> dummy;
}
}
}
//
// Construct mesh with default boundary only
//
auto meshPtr = autoPtr<polyMesh>::New
(
IOobject
(
polyMesh::defaultRegion,
runTime.constant(),
runTime,
IOobject::NO_READ,
IOobject::AUTO_WRITE
),
pointField(points), // Copy of points
cells,
faceListList(),
wordList(), // boundaryPatchNames
wordList(), // boundaryPatchTypes
"defaultFaces",
polyPatch::typeName,
wordList()
);
const polyMesh& mesh = *meshPtr;
if (readFaceFile)
{
label nPatches = 0;
// List of Foam vertices per boundary face
faceList boundaryFaces;
// For each boundary faces the Foam patchID
labelList boundaryPatch;
//
// read boundary faces
//
IFstream faceStream(faceFile);
do
{
faceStream.getLine(line);
}
while (line.starts_with('#'));
IStringStream faceLine(line);
label nFaces, nFaceAttr;
faceLine >> nFaces >> nFaceAttr;
Info<< "Read .face header:" << endl
<< " faces : " << nFaces << endl
<< " nAttr : " << nFaceAttr << endl
<< endl;
if (nFaceAttr != 1)
{
FatalIOErrorInFunction(faceStream)
<< "Expect boundary markers to be"
<< " present in .face file." << endl
<< "This is the second number in the header which is now:"
<< nFaceAttr << exit(FatalIOError);
}
// List of Foam vertices per boundary face
boundaryFaces.setSize(nFaces);
// For each boundary faces the Foam patchID
boundaryPatch.setSize(nFaces);
boundaryPatch = -1;
label facei = 0;
// Region to patch conversion
Map<label> regionToPatch;
face f(3);
while (faceStream.good())
{
faceStream.getLine(line);
if (line.size() && line[0] != '#')
{
IStringStream faceLine(line);
label tetGenFacei, dummy, region;
faceLine >> tetGenFacei;
// Read face and reverse orientation (Foam needs outwards
// pointing)
for (label i = 0; i < 3; i++)
{
label nodeI;
faceLine >> nodeI;
f[2-i] = nodeToPoint[nodeI];
}
if (findFace(mesh, f) >= mesh.nInternalFaces())
{
boundaryFaces[facei] = f;
if (nFaceAttr > 0)
{
// First attribute is the region number
faceLine >> region;
// Get Foam patchID and update region->patch table.
label patchi = 0;
const auto patchFind = regionToPatch.cfind(region);
if (patchFind.found())
{
patchi = *patchFind;
}
else
{
patchi = nPatches;
Info<< "Mapping tetgen region " << region
<< " to patch "
<< patchi << endl;
regionToPatch.insert(region, nPatches++);
}
boundaryPatch[facei] = patchi;
// Skip remaining attributes
for (label i = 1; i < nFaceAttr; i++)
{
faceLine >> dummy;
}
}
facei++;
}
}
}
// Trim
boundaryFaces.setSize(facei);
boundaryPatch.setSize(facei);
// Print region to patch mapping
Info<< "Regions:" << endl;
forAllConstIters(regionToPatch, iter)
{
Info<< " region:" << iter.key()
<< '\t' << "patch:" << iter.val() << nl;
}
Info<< endl;
// Storage for boundary faces
faceListList patchFaces(nPatches);
wordList patchNames(nPatches);
forAll(patchNames, patchi)
{
patchNames[patchi] = polyPatch::defaultName(patchi);
}
wordList patchTypes(nPatches, polyPatch::typeName);
word defaultFacesName = "defaultFaces";
word defaultFacesType = polyPatch::typeName;
wordList patchPhysicalTypes(nPatches, polyPatch::typeName);
// Sort boundaryFaces by patch using boundaryPatch.
List<DynamicList<face>> allPatchFaces(nPatches);
forAll(boundaryPatch, facei)
{
label patchi = boundaryPatch[facei];
allPatchFaces[patchi].append(boundaryFaces[facei]);
}
Info<< "Patch sizes:" << endl;
forAll(allPatchFaces, patchi)
{
Info<< " " << patchNames[patchi] << " : "
<< allPatchFaces[patchi].size() << endl;
patchFaces[patchi].transfer(allPatchFaces[patchi]);
}
Info<< endl;
meshPtr.reset
(
new polyMesh
(
IOobject
(
polyMesh::defaultRegion,
runTime.constant(),
runTime
),
std::move(points),
cells,
patchFaces,
patchNames,
patchTypes,
defaultFacesName,
defaultFacesType,
patchPhysicalTypes
)
);
}
// Set the precision of the points data to 10
IOstream::defaultPrecision(max(10u, IOstream::defaultPrecision()));
Info<< "Writing mesh to " << runTime.constant() << endl << endl;
meshPtr().removeFiles();
meshPtr().write();
Info<< "End\n" << endl;
return 0;
}
// ************************************************************************* //