tetgenToFoam.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2015-2024 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 Application
28  tetgenToFoam
29 
30 Group
31  grpMeshConversionUtilities
32 
33 Description
34  Convert tetgen .ele and .node and .face files to an OpenFOAM mesh.
35 
36  Make sure to use add boundary attributes to the smesh file
37  (5 fifth column in the element section)
38  and run tetgen with -f option.
39 
40  Sample smesh file:
41  \verbatim
42  # cube.smesh -- A 10x10x10 cube
43  8 3
44  1 0 0 0
45  2 0 10 0
46  3 10 10 0
47  4 10 0 0
48  5 0 0 10
49  6 0 10 10
50  7 10 10 10
51  8 10 0 10
52  6 1 # 1 for boundary info present
53  4 1 2 3 4 11 # region number 11
54  4 5 6 7 8 21 # region number 21
55  4 1 2 6 5 3
56  4 4 3 7 8 43
57  4 1 5 8 4 5
58  4 2 6 7 3 65
59  0
60  0
61  \endverbatim
62 
63 Note
64  - for some reason boundary faces point inwards. I just reverse them
65  always. Might use some geometric check instead.
66  - marked faces might not actually be boundary faces of mesh.
67  This is hopefully handled now by first constructing without boundaries
68  and then reconstructing with boundary faces.
69 
70 \*---------------------------------------------------------------------------*/
71 
72 #include "argList.H"
73 #include "Time.H"
74 #include "polyMesh.H"
75 #include "IFstream.H"
76 #include "cellModel.H"
77 
78 using namespace Foam;
79 
80 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
81 
82 // Find label of face.
83 label findFace(const primitiveMesh& mesh, const face& f)
84 {
85  const labelList& pFaces = mesh.pointFaces()[f[0]];
86 
87  forAll(pFaces, i)
88  {
89  label facei = pFaces[i];
90 
91  if (mesh.faces()[facei] == f)
92  {
93  return facei;
94  }
95  }
96 
98  << "Cannot find face " << f << " in mesh." << abort(FatalError);
99 
100  return -1;
101 }
102 
103 
104 
105 int main(int argc, char *argv[])
106 {
108  (
109  "Convert tetgen .ele and .node and .face files to an OpenFOAM mesh"
110  );
111 
112  argList::addArgument("prefix", "The prefix for the input tetgen files");
114  (
115  "noFaceFile",
116  "Skip reading .face file for boundary information"
117  );
118 
119  #include "setRootCase.H"
120  #include "createTime.H"
121 
122  const auto prefix = args.get<fileName>(1);
123  const bool readFaceFile = !args.found("noFaceFile");
124 
125  const fileName nodeFile(prefix + ".node");
126  const fileName eleFile(prefix + ".ele");
127  const fileName faceFile(prefix + ".face");
128 
129  if (!readFaceFile)
130  {
131  Info<< "Files:" << endl
132  << " nodes : " << nodeFile << endl
133  << " elems : " << eleFile << endl
134  << endl;
135  }
136  else
137  {
138  Info<< "Files:" << endl
139  << " nodes : " << nodeFile << endl
140  << " elems : " << eleFile << endl
141  << " faces : " << faceFile << endl
142  << endl;
143 
144  Info<< "Reading .face file for boundary information" << nl << endl;
145  }
146 
147  if (!isFile(nodeFile) || !isFile(eleFile))
148  {
150  << "Cannot read " << nodeFile << " or " << eleFile
151  << exit(FatalError);
152  }
153 
154  if (readFaceFile && !isFile(faceFile))
155  {
157  << "Cannot read " << faceFile << endl
158  << "Did you run tetgen with -f option?" << endl
159  << "If you don't want to read the .face file and thus not have"
160  << " patches please\nrerun with the -noFaceFile option"
161  << exit(FatalError);
162  }
163 
164 
165  IFstream nodeStream(nodeFile);
166 
167  //
168  // Read nodes.
169  //
170 
171  // Read header.
172  string line;
173 
174  do
175  {
176  nodeStream.getLine(line);
177  }
178  while (line.starts_with('#'));
179 
180  IStringStream nodeLine(line);
181 
182  label nNodes, nDims, nNodeAttr;
183  bool hasRegion;
184 
185  nodeLine >> nNodes >> nDims >> nNodeAttr >> hasRegion;
186 
187 
188  Info<< "Read .node header:" << endl
189  << " nodes : " << nNodes << endl
190  << " nDims : " << nDims << endl
191  << " nAttr : " << nNodeAttr << endl
192  << " hasRegion : " << hasRegion << endl
193  << endl;
194 
195  //
196  // read points
197  //
198 
199  pointField points(nNodes);
200  Map<label> nodeToPoint(2*nNodes);
201 
202  {
203  labelList pointIndex(nNodes);
204 
205  label pointi = 0;
206 
207  while (nodeStream.good())
208  {
209  nodeStream.getLine(line);
210 
211  if (line.size() && line[0] != '#')
212  {
213  IStringStream nodeLine(line);
214 
215  label nodeI;
216  scalar x, y, z;
217  label dummy;
218 
219  nodeLine >> nodeI >> x >> y >> z;
220 
221  for (label i = 0; i < nNodeAttr; i++)
222  {
223  nodeLine >> dummy;
224  }
225 
226  if (hasRegion)
227  {
228  nodeLine >> dummy;
229  }
230 
231  // Store point and node number.
232  points[pointi] = point(x, y, z);
233  nodeToPoint.insert(nodeI, pointi);
234  pointi++;
235  }
236  }
237  if (pointi != nNodes)
238  {
239  FatalIOErrorInFunction(nodeStream)
240  << "Only " << pointi << " nodes present instead of " << nNodes
241  << " from header." << exit(FatalIOError);
242  }
243  }
244 
245  //
246  // read elements
247  //
248 
249  IFstream eleStream(eleFile);
250 
251  do
252  {
253  eleStream.getLine(line);
254  }
255  while (line.starts_with('#'));
256 
257  IStringStream eleLine(line);
258 
259  label nTets, nPtsPerTet, nElemAttr;
260 
261  eleLine >> nTets >> nPtsPerTet >> nElemAttr;
262 
263 
264  Info<< "Read .ele header:" << endl
265  << " tets : " << nTets << endl
266  << " pointsPerTet : " << nPtsPerTet << endl
267  << " nAttr : " << nElemAttr << endl
268  << endl;
269 
270  if (nPtsPerTet != 4)
271  {
272  FatalIOErrorInFunction(eleStream)
273  << "Cannot handle tets with "
274  << nPtsPerTet << " points per tetrahedron in .ele file" << endl
275  << "Can only handle tetrahedra with four points"
276  << exit(FatalIOError);
277  }
278 
279  if (nElemAttr != 0)
280  {
282  << "Element attributes (third elemenent in .ele header)"
283  << " not used" << endl;
284  }
285 
286 
288 
289  labelList tetPoints(4);
290 
291  cellShapeList cells(nTets);
292  label celli = 0;
293 
294  while (eleStream.good())
295  {
296  eleStream.getLine(line);
297 
298  if (line.size() && line[0] != '#')
299  {
300  IStringStream eleLine(line);
301 
302  label elemI;
303  eleLine >> elemI;
304 
305  for (label i = 0; i < 4; i++)
306  {
307  label nodeI;
308  eleLine >> nodeI;
309  tetPoints[i] = nodeToPoint[nodeI];
310  }
311 
312  cells[celli++].reset(tet, tetPoints);
313 
314  // Skip attributes
315  for (label i = 0; i < nElemAttr; i++)
316  {
317  label dummy;
318 
319  eleLine >> dummy;
320  }
321  }
322  }
323 
324 
325  //
326  // Construct mesh with default boundary only
327  //
328 
330  (
331  IOobject
332  (
334  runTime.constant(),
335  runTime,
338  ),
339  pointField(points), // Copy of points
340  cells,
341  faceListList(),
342  wordList(), // boundaryPatchNames
343  wordList(), // boundaryPatchTypes
344  "defaultFaces",
345  polyPatch::typeName,
346  wordList()
347  );
348  const polyMesh& mesh = *meshPtr;
349 
350 
351  if (readFaceFile)
352  {
353  label nPatches = 0;
354 
355  // List of Foam vertices per boundary face
356  faceList boundaryFaces;
357 
358  // For each boundary faces the Foam patchID
360 
361  //
362  // read boundary faces
363  //
364 
365  IFstream faceStream(faceFile);
366 
367  do
368  {
369  faceStream.getLine(line);
370  }
371  while (line.starts_with('#'));
372 
373  IStringStream faceLine(line);
374 
375  label nFaces, nFaceAttr;
376 
377  faceLine >> nFaces >> nFaceAttr;
378 
379 
380  Info<< "Read .face header:" << endl
381  << " faces : " << nFaces << endl
382  << " nAttr : " << nFaceAttr << endl
383  << endl;
384 
385 
386  if (nFaceAttr != 1)
387  {
388  FatalIOErrorInFunction(faceStream)
389  << "Expect boundary markers to be"
390  << " present in .face file." << endl
391  << "This is the second number in the header which is now:"
392  << nFaceAttr << exit(FatalIOError);
393  }
394 
395  // List of Foam vertices per boundary face
396  boundaryFaces.setSize(nFaces);
397 
398  // For each boundary faces the Foam patchID
399  boundaryPatch.setSize(nFaces);
400  boundaryPatch = -1;
401 
402  label facei = 0;
403 
404  // Region to patch conversion
405  Map<label> regionToPatch;
406 
407  face f(3);
408 
409  while (faceStream.good())
410  {
411  faceStream.getLine(line);
412 
413  if (line.size() && line[0] != '#')
414  {
415  IStringStream faceLine(line);
416 
417  label tetGenFacei, dummy, region;
418 
419  faceLine >> tetGenFacei;
420 
421  // Read face and reverse orientation (Foam needs outwards
422  // pointing)
423  for (label i = 0; i < 3; i++)
424  {
425  label nodeI;
426  faceLine >> nodeI;
427  f[2-i] = nodeToPoint[nodeI];
428  }
429 
430 
431  if (findFace(mesh, f) >= mesh.nInternalFaces())
432  {
433  boundaryFaces[facei] = f;
434 
435  if (nFaceAttr > 0)
436  {
437  // First attribute is the region number
438  faceLine >> region;
439 
440 
441  // Get Foam patchID and update region->patch table.
442  label patchi = regionToPatch.lookup(region, -1);
443 
444  if (patchi < 0)
445  {
446  patchi = nPatches;
447  regionToPatch.insert(region, nPatches++);
448 
449  Info<< "Mapping tetgen region " << region
450  << " to patch "
451  << patchi << endl;
452  }
453 
454  boundaryPatch[facei] = patchi;
455 
456  // Skip remaining attributes
457  for (label i = 1; i < nFaceAttr; i++)
458  {
459  faceLine >> dummy;
460  }
461  }
462 
463  facei++;
464  }
465  }
466  }
467 
468 
469  // Trim
470  boundaryFaces.setSize(facei);
471  boundaryPatch.setSize(facei);
472 
473 
474  // Print region to patch mapping
475  Info<< "Regions:" << endl;
476 
477  forAllConstIters(regionToPatch, iter)
478  {
479  Info<< " region:" << iter.key()
480  << '\t' << "patch:" << iter.val() << nl;
481  }
482  Info<< endl;
483 
484 
485  // Storage for boundary faces
486  faceListList patchFaces(nPatches);
488 
489  forAll(patchNames, patchi)
490  {
491  patchNames[patchi] = polyPatch::defaultName(patchi);
492  }
493 
494  wordList patchTypes(nPatches, polyPatch::typeName);
495  word defaultFacesName = "defaultFaces";
496  word defaultFacesType = polyPatch::typeName;
497  wordList patchPhysicalTypes(nPatches, polyPatch::typeName);
498 
499 
500  // Sort boundaryFaces by patch using boundaryPatch.
501  List<DynamicList<face>> allPatchFaces(nPatches);
502 
503  forAll(boundaryPatch, facei)
504  {
505  label patchi = boundaryPatch[facei];
506 
507  allPatchFaces[patchi].append(boundaryFaces[facei]);
508  }
509 
510  Info<< "Patch sizes:" << endl;
511 
512  forAll(allPatchFaces, patchi)
513  {
514  Info<< " " << patchNames[patchi] << " : "
515  << allPatchFaces[patchi].size() << endl;
516 
517  patchFaces[patchi].transfer(allPatchFaces[patchi]);
518  }
519 
520  Info<< endl;
521 
522 
523  meshPtr.reset
524  (
525  new polyMesh
526  (
527  IOobject
528  (
530  runTime.constant(),
531  runTime
532  ),
533  std::move(points),
534  cells,
535  patchFaces,
536  patchNames,
537  patchTypes,
540  patchPhysicalTypes
541  )
542  );
543  }
544 
545  // More precision (for points data)
547 
548  Info<< "Writing mesh to " << runTime.constant() << endl << endl;
549 
550  meshPtr().removeFiles();
551  meshPtr().write();
552 
553  Info<< "End\n" << endl;
554 
555  return 0;
556 }
557 
558 
559 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
word defaultFacesType
Definition: readKivaGrid.H:456
static word defaultName(const label n=-1)
Default patch name: "patch" or "patchN".
List< faceList > faceListList
List of faceList.
Definition: faceListFwd.H:44
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:462
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
A line primitive.
Definition: line.H:52
A class for handling file names.
Definition: fileName.H:72
Like polyPatch but without reference to mesh. Used in boundaryMesh to hold data on patches...
Definition: boundaryPatch.H:51
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:608
Cell-face mesh analysis engine.
Definition: primitiveMesh.H:75
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
wordList patchTypes(nPatches)
engineTime & runTime
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:374
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
static const cellModel & ref(const modelType model)
Look up reference to cellModel by enumeration. Fatal on failure.
Definition: cellModels.C:150
bool insert(const Key &key, const T &obj)
Copy insert a new entry, not overwriting existing entries.
Definition: HashTableI.H:152
scalar y
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
void setSize(const label n)
Alias for resize()
Definition: List.H:320
dynamicFvMesh & mesh
const cellShapeList & cells
const pointField & points
Foam::autoPtr< Foam::dynamicFvMesh > meshPtr
A class for handling words, derived from Foam::string.
Definition: word.H:63
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:406
wordList patchNames(nPatches)
label nInternalFaces() const noexcept
Number of internal faces.
virtual const faceList & faces() const
Return raw faces.
Definition: polyMesh.C:1103
static unsigned int minPrecision(unsigned int prec) noexcept
Set the minimum default precision.
Definition: IOstream.H:440
errorManip< error > abort(error &err)
Definition: errorManip.H:139
const word & constant() const noexcept
Return constant name.
Definition: TimePathsI.H:112
Input from file stream as an ISstream, normally using std::ifstream for the actual input...
Definition: IFstream.H:51
labelList f(nPoints)
Info<< "Finished reading KIVA file"<< endl;cellShapeList cellShapes(nPoints);labelList cellZoning(nPoints, -1);const cellModel &hex=cellModel::ref(cellModel::HEX);labelList hexLabels(8);label activeCells=0;labelList pointMap(nPoints);forAll(pointMap, i){ pointMap[i]=i;}for(label i=0;i< nPoints;i++){ if(f[i] > 0.0) { hexLabels[0]=i;hexLabels[1]=i1tab[i];hexLabels[2]=i3tab[i1tab[i]];hexLabels[3]=i3tab[i];hexLabels[4]=i8tab[i];hexLabels[5]=i1tab[i8tab[i]];hexLabels[6]=i3tab[i1tab[i8tab[i]]];hexLabels[7]=i3tab[i8tab[i]];cellShapes[activeCells].reset(hex, hexLabels);edgeList edges=cellShapes[activeCells].edges();forAll(edges, ei) { if(edges[ei].mag(points)< SMALL) { label start=pointMap[edges[ei].start()];while(start !=pointMap[start]) { start=pointMap[start];} label end=pointMap[edges[ei].end()];while(end !=pointMap[end]) { end=pointMap[end];} label minLabel=min(start, end);pointMap[start]=pointMap[end]=minLabel;} } cellZoning[activeCells]=idreg[i];activeCells++;}}cellShapes.setSize(activeCells);cellZoning.setSize(activeCells);forAll(cellShapes, celli){ cellShape &cs=cellShapes[celli];forAll(cs, i) { cs[i]=pointMap[cs[i]];} cs.collapse();}label bcIDs[11]={-1, 0, 2, 4, -1, 5, -1, 6, 7, 8, 9};const label nBCs=12;const word *kivaPatchTypes[nBCs]={ &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &wallPolyPatch::typeName, &symmetryPolyPatch::typeName, &wedgePolyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &polyPatch::typeName, &symmetryPolyPatch::typeName, &oldCyclicPolyPatch::typeName};enum patchTypeNames{ PISTON, VALVE, LINER, CYLINDERHEAD, AXIS, WEDGE, INFLOW, OUTFLOW, PRESIN, PRESOUT, SYMMETRYPLANE, CYCLIC};const char *kivaPatchNames[nBCs]={ "piston", "valve", "liner", "cylinderHead", "axis", "wedge", "inflow", "outflow", "presin", "presout", "symmetryPlane", "cyclic"};List< SLList< face > > pFaces[nBCs]
Definition: readKivaGrid.H:235
word defaultFacesName
Definition: readKivaGrid.H:455
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:877
List< word > wordList
List of word.
Definition: fileName.H:59
vector point
Point is a vector.
Definition: point.H:37
const T & lookup(const Key &key, const T &deflt) const
Return hashed entry if it exists, or return the given default.
Definition: HashTableI.H:222
T get(const label index) const
Get a value from the argument at index.
Definition: argListI.H:271
#define WarningInFunction
Report a warning using Foam::Warning.
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:637
Tet point storage. Default constructable (tetrahedron is not)
Definition: tetrahedron.H:82
Input from string buffer, using a ISstream. Always UNCOMPRESSED.
Definition: StringStream.H:52
Nothing to be read.
Automatically write from objectRegistry::writeObject()
Maps a geometry to a set of cell primitives.
Definition: cellModel.H:72
static void addArgument(const string &argName, const string &usage="")
Append a (mandatory) argument to validArgs.
Definition: argList.C:351
messageStream Info
Information stream (stdout output on master, null elsewhere)
const labelListList & pointFaces() const
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
Foam::argList args(argc, argv)
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:180
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:171
Namespace for OpenFOAM.
forAllConstIters(mixture.phases(), phase)
Definition: pEqn.H:28
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...