1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
11 OpenFOAM is free software: you can redistribute it and/or modify it
12 under the terms of the GNU General Public License as published by
13 the Free Software Foundation, either version 3 of the License, or
14 (at your option) any later version.
16 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 Converts .ele and .node and .face files, written by tetgen.
27 Make sure to use add boundary attributes to the smesh file
28 (5 fifth column in the element section)
29 and run tetgen with -f option.
33 # cube.smesh -- A 10x10x10 cube
43 6 1 # 1 for boundary info present
44 4 1 2 3 4 11 # region number 11
45 4 5 6 7 8 21 # region number 21
55 - for some reason boundary faces point inwards. I just reverse them
56 always. Might use some geometric check instead.
57 - marked faces might not actually be boundary faces of mesh.
58 This is hopefully handled now by first constructing without boundaries
59 and then reconstructing with boundary faces.
61 \*---------------------------------------------------------------------------*/
67 #include "cellModeller.H"
71 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
73 // Find label of face.
74 label findFace(const primitiveMesh& mesh, const face& f)
76 const labelList& pFaces = mesh.pointFaces()[f[0]];
80 label faceI = pFaces[i];
82 if (mesh.faces()[faceI] == f)
88 FatalErrorIn("findFace(const primitiveMesh&, const face&)")
89 << "Cannot find face " << f << " in mesh." << abort(FatalError);
97 int main(int argc, char *argv[])
99 argList::validArgs.append("file prefix");
100 argList::addBoolOption
103 "skip reading .face file for boundary information"
106 # include "setRootCase.H"
107 # include "createTime.H"
109 const fileName prefix = args[1];
110 const bool readFaceFile = !args.optionFound("noFaceFile");
112 const fileName nodeFile(prefix + ".node");
113 const fileName eleFile(prefix + ".ele");
114 const fileName faceFile(prefix + ".face");
118 Info<< "Files:" << endl
119 << " nodes : " << nodeFile << endl
120 << " elems : " << eleFile << endl
125 Info<< "Files:" << endl
126 << " nodes : " << nodeFile << endl
127 << " elems : " << eleFile << endl
128 << " faces : " << faceFile << endl
131 Info<< "Reading .face file for boundary information" << nl << endl;
134 if (!isFile(nodeFile) || !isFile(eleFile))
136 FatalErrorIn(args.executable())
137 << "Cannot read " << nodeFile << " or " << eleFile
141 if (readFaceFile && !isFile(faceFile))
143 FatalErrorIn(args.executable())
144 << "Cannot read " << faceFile << endl
145 << "Did you run tetgen with -f option?" << endl
146 << "If you don't want to read the .face file and thus not have"
147 << " patches please\nrerun with the -noFaceFile option"
152 IFstream nodeStream(nodeFile);
163 nodeStream.getLine(line);
165 while (line.size() && line[0] == '#');
167 IStringStream nodeLine(line);
169 label nNodes, nDims, nNodeAttr;
172 nodeLine >> nNodes >> nDims >> nNodeAttr >> hasRegion;
175 Info<< "Read .node header:" << endl
176 << " nodes : " << nNodes << endl
177 << " nDims : " << nDims << endl
178 << " nAttr : " << nNodeAttr << endl
179 << " hasRegion : " << hasRegion << endl
186 pointField points(nNodes);
187 Map<label> nodeToPoint(nNodes);
190 labelList pointIndex(nNodes);
194 while (nodeStream.good())
196 nodeStream.getLine(line);
198 if (line.size() && line[0] != '#')
200 IStringStream nodeLine(line);
206 nodeLine >> nodeI >> x >> y >> z;
208 for (label i = 0; i < nNodeAttr; i++)
218 // Store point and node number.
219 points[pointI] = point(x, y, z);
220 nodeToPoint.insert(nodeI, pointI);
224 if (pointI != nNodes)
226 FatalIOErrorIn(args.executable().c_str(), nodeStream)
227 << "Only " << pointI << " nodes present instead of " << nNodes
228 << " from header." << exit(FatalIOError);
236 IFstream eleStream(eleFile);
240 eleStream.getLine(line);
242 while (line.size() && line[0] == '#');
244 IStringStream eleLine(line);
246 label nTets, nPtsPerTet, nElemAttr;
248 eleLine >> nTets >> nPtsPerTet >> nElemAttr;
251 Info<< "Read .ele header:" << endl
252 << " tets : " << nTets << endl
253 << " pointsPerTet : " << nPtsPerTet << endl
254 << " nAttr : " << nElemAttr << endl
259 FatalIOErrorIn(args.executable().c_str(), eleStream)
260 << "Cannot handle tets with "
261 << nPtsPerTet << " points per tetrahedron in .ele file" << endl
262 << "Can only handle tetrahedra with four points"
263 << exit(FatalIOError);
268 WarningIn(args.executable())
269 << "Element attributes (third elemenent in .ele header)"
270 << " not used" << endl;
275 const cellModel& tet = *(cellModeller::lookup("tet"));
277 labelList tetPoints(4);
279 cellShapeList cells(nTets);
282 while (eleStream.good())
284 eleStream.getLine(line);
286 if (line.size() && line[0] != '#')
288 IStringStream eleLine(line);
293 for (label i = 0; i < 4; i++)
297 tetPoints[i] = nodeToPoint[nodeI];
300 cells[cellI++] = cellShape(tet, tetPoints);
303 for (label i = 0; i < nElemAttr; i++)
314 // Construct mesh with default boundary only
317 autoPtr<polyMesh> meshPtr
323 polyMesh::defaultRegion,
330 wordList(0), // boundaryPatchNames
331 wordList(0), // boundaryPatchTypes
337 const polyMesh& mesh = meshPtr;
345 // List of Foam vertices per boundary face
346 faceList boundaryFaces;
348 // For each boundary faces the Foam patchID
349 labelList boundaryPatch;
352 // read boundary faces
355 IFstream faceStream(faceFile);
359 faceStream.getLine(line);
361 while (line.size() && line[0] == '#');
363 IStringStream faceLine(line);
365 label nFaces, nFaceAttr;
367 faceLine >> nFaces >> nFaceAttr;
370 Info<< "Read .face header:" << endl
371 << " faces : " << nFaces << endl
372 << " nAttr : " << nFaceAttr << endl
378 FatalIOErrorIn(args.executable().c_str(), faceStream)
379 << "Expect boundary markers to be"
380 << " present in .face file." << endl
381 << "This is the second number in the header which is now:"
382 << nFaceAttr << exit(FatalIOError);
385 // List of Foam vertices per boundary face
386 boundaryFaces.setSize(nFaces);
388 // For each boundary faces the Foam patchID
389 boundaryPatch.setSize(nFaces);
394 // Region to patch conversion
395 Map<label> regionToPatch;
399 while (faceStream.good())
401 faceStream.getLine(line);
403 if (line.size() && line[0] != '#')
405 IStringStream faceLine(line);
407 label tetGenFaceI, dummy, region;
409 faceLine >> tetGenFaceI;
411 // Read face and reverse orientation (Foam needs outwards
413 for (label i = 0; i < 3; i++)
417 f[2-i] = nodeToPoint[nodeI];
421 if (findFace(mesh, f) >= mesh.nInternalFaces())
423 boundaryFaces[faceI] = f;
427 // First attribute is the region number
431 // Get Foam patchID and update region->patch table.
434 Map<label>::iterator patchFind =
435 regionToPatch.find(region);
437 if (patchFind == regionToPatch.end())
441 Info<< "Mapping tetgen region " << region
445 regionToPatch.insert(region, nPatches++);
449 patchI = patchFind();
452 boundaryPatch[faceI] = patchI;
454 // Skip remaining attributes
455 for (label i = 1; i < nFaceAttr; i++)
468 boundaryFaces.setSize(faceI);
469 boundaryPatch.setSize(faceI);
472 // Print region to patch mapping
473 Info<< "Regions:" << endl;
475 forAllConstIter(Map<label>, regionToPatch, iter)
477 Info<< " region:" << iter.key() << '\t' << "patch:"
483 // Storage for boundary faces
484 faceListList patchFaces(nPatches);
485 wordList patchNames(nPatches);
487 forAll(patchNames, patchI)
489 patchNames[patchI] = word("patch") + name(patchI);
492 wordList patchTypes(nPatches, polyPatch::typeName);
493 word defaultFacesName = "defaultFaces";
494 word defaultFacesType = polyPatch::typeName;
495 wordList patchPhysicalTypes(nPatches, polyPatch::typeName);
498 // Sort boundaryFaces by patch using boundaryPatch.
499 List<DynamicList<face> > allPatchFaces(nPatches);
501 forAll(boundaryPatch, faceI)
503 label patchI = boundaryPatch[faceI];
505 allPatchFaces[patchI].append(boundaryFaces[faceI]);
508 Info<< "Patch sizes:" << endl;
510 forAll(allPatchFaces, patchI)
512 Info<< " " << patchNames[patchI] << " : "
513 << allPatchFaces[patchI].size() << endl;
515 patchFaces[patchI].transfer(allPatchFaces[patchI]);
527 polyMesh::defaultRegion,
544 Info<< "Writing mesh to " << runTime.constant() << endl << endl;
548 Info<< "End\n" << endl;
554 // ************************************************************************* //