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 | Copyright (C) 2011-2016 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
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.
15 
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
19  for more details.
20 
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/>.
23 
24 Application
25  tetgenToFoam
26 
27 Description
28  Converts .ele and .node and .face files, written by tetgen.
29 
30  Make sure to use add boundary attributes to the smesh file
31  (5 fifth column in the element section)
32  and run tetgen with -f option.
33 
34  Sample smesh file:
35  \verbatim
36  # cube.smesh -- A 10x10x10 cube
37  8 3
38  1 0 0 0
39  2 0 10 0
40  3 10 10 0
41  4 10 0 0
42  5 0 0 10
43  6 0 10 10
44  7 10 10 10
45  8 10 0 10
46  6 1 # 1 for boundary info present
47  4 1 2 3 4 11 # region number 11
48  4 5 6 7 8 21 # region number 21
49  4 1 2 6 5 3
50  4 4 3 7 8 43
51  4 1 5 8 4 5
52  4 2 6 7 3 65
53  0
54  0
55  \endverbatim
56 
57 Note
58  - for some reason boundary faces point inwards. I just reverse them
59  always. Might use some geometric check instead.
60  - marked faces might not actually be boundary faces of mesh.
61  This is hopefully handled now by first constructing without boundaries
62  and then reconstructing with boundary faces.
63 
64 \*---------------------------------------------------------------------------*/
65 
66 #include "argList.H"
67 #include "Time.H"
68 #include "polyMesh.H"
69 #include "IFstream.H"
70 #include "cellModeller.H"
71 
72 using namespace Foam;
73 
74 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
75 
76 // Find label of face.
77 label findFace(const primitiveMesh& mesh, const face& f)
78 {
79  const labelList& pFaces = mesh.pointFaces()[f[0]];
80 
81  forAll(pFaces, i)
82  {
83  label facei = pFaces[i];
84 
85  if (mesh.faces()[facei] == f)
86  {
87  return facei;
88  }
89  }
90 
92  << "Cannot find face " << f << " in mesh." << abort(FatalError);
93 
94  return -1;
95 }
96 
97 
98 
99 int main(int argc, char *argv[])
100 {
101  argList::validArgs.append("file prefix");
103  (
104  "noFaceFile",
105  "skip reading .face file for boundary information"
106  );
107 
108  #include "setRootCase.H"
109  #include "createTime.H"
110 
111  const fileName prefix = args[1];
112  const bool readFaceFile = !args.optionFound("noFaceFile");
113 
114  const fileName nodeFile(prefix + ".node");
115  const fileName eleFile(prefix + ".ele");
116  const fileName faceFile(prefix + ".face");
117 
118  if (!readFaceFile)
119  {
120  Info<< "Files:" << endl
121  << " nodes : " << nodeFile << endl
122  << " elems : " << eleFile << endl
123  << endl;
124  }
125  else
126  {
127  Info<< "Files:" << endl
128  << " nodes : " << nodeFile << endl
129  << " elems : " << eleFile << endl
130  << " faces : " << faceFile << endl
131  << endl;
132 
133  Info<< "Reading .face file for boundary information" << nl << endl;
134  }
135 
136  if (!isFile(nodeFile) || !isFile(eleFile))
137  {
139  << "Cannot read " << nodeFile << " or " << eleFile
140  << exit(FatalError);
141  }
142 
143  if (readFaceFile && !isFile(faceFile))
144  {
146  << "Cannot read " << faceFile << endl
147  << "Did you run tetgen with -f option?" << endl
148  << "If you don't want to read the .face file and thus not have"
149  << " patches please\nrerun with the -noFaceFile option"
150  << exit(FatalError);
151  }
152 
153 
154  IFstream nodeStream(nodeFile);
155 
156  //
157  // Read nodes.
158  //
159 
160  // Read header.
161  string line;
162 
163  do
164  {
165  nodeStream.getLine(line);
166  }
167  while (line.size() && line[0] == '#');
168 
169  IStringStream nodeLine(line);
170 
171  label nNodes, nDims, nNodeAttr;
172  bool hasRegion;
173 
174  nodeLine >> nNodes >> nDims >> nNodeAttr >> hasRegion;
175 
176 
177  Info<< "Read .node header:" << endl
178  << " nodes : " << nNodes << endl
179  << " nDims : " << nDims << endl
180  << " nAttr : " << nNodeAttr << endl
181  << " hasRegion : " << hasRegion << endl
182  << endl;
183 
184  //
185  // read points
186  //
187 
188  pointField points(nNodes);
189  Map<label> nodeToPoint(nNodes);
190 
191  {
192  labelList pointIndex(nNodes);
193 
194  label pointi = 0;
195 
196  while (nodeStream.good())
197  {
198  nodeStream.getLine(line);
199 
200  if (line.size() && line[0] != '#')
201  {
202  IStringStream nodeLine(line);
203 
204  label nodeI;
205  scalar x, y, z;
206  label dummy;
207 
208  nodeLine >> nodeI >> x >> y >> z;
209 
210  for (label i = 0; i < nNodeAttr; i++)
211  {
212  nodeLine >> dummy;
213  }
214 
215  if (hasRegion)
216  {
217  nodeLine >> dummy;
218  }
219 
220  // Store point and node number.
221  points[pointi] = point(x, y, z);
222  nodeToPoint.insert(nodeI, pointi);
223  pointi++;
224  }
225  }
226  if (pointi != nNodes)
227  {
228  FatalIOErrorIn(args.executable().c_str(), nodeStream)
229  << "Only " << pointi << " nodes present instead of " << nNodes
230  << " from header." << exit(FatalIOError);
231  }
232  }
233 
234  //
235  // read elements
236  //
237 
238  IFstream eleStream(eleFile);
239 
240  do
241  {
242  eleStream.getLine(line);
243  }
244  while (line.size() && line[0] == '#');
245 
246  IStringStream eleLine(line);
247 
248  label nTets, nPtsPerTet, nElemAttr;
249 
250  eleLine >> nTets >> nPtsPerTet >> nElemAttr;
251 
252 
253  Info<< "Read .ele header:" << endl
254  << " tets : " << nTets << endl
255  << " pointsPerTet : " << nPtsPerTet << endl
256  << " nAttr : " << nElemAttr << endl
257  << endl;
258 
259  if (nPtsPerTet != 4)
260  {
261  FatalIOErrorIn(args.executable().c_str(), eleStream)
262  << "Cannot handle tets with "
263  << nPtsPerTet << " points per tetrahedron in .ele file" << endl
264  << "Can only handle tetrahedra with four points"
265  << exit(FatalIOError);
266  }
267 
268  if (nElemAttr != 0)
269  {
271  << "Element attributes (third elemenent in .ele header)"
272  << " not used" << endl;
273  }
274 
275 
276 
277  const cellModel& tet = *(cellModeller::lookup("tet"));
278 
279  labelList tetPoints(4);
280 
281  cellShapeList cells(nTets);
282  label celli = 0;
283 
284  while (eleStream.good())
285  {
286  eleStream.getLine(line);
287 
288  if (line.size() && line[0] != '#')
289  {
290  IStringStream eleLine(line);
291 
292  label elemI;
293  eleLine >> elemI;
294 
295  for (label i = 0; i < 4; i++)
296  {
297  label nodeI;
298  eleLine >> nodeI;
299  tetPoints[i] = nodeToPoint[nodeI];
300  }
301 
302  cells[celli++] = cellShape(tet, tetPoints);
303 
304  // Skip attributes
305  for (label i = 0; i < nElemAttr; i++)
306  {
307  label dummy;
308 
309  eleLine >> dummy;
310  }
311  }
312  }
313 
314 
315  //
316  // Construct mesh with default boundary only
317  //
318 
320  (
321  new polyMesh
322  (
323  IOobject
324  (
326  runTime.constant(),
327  runTime
328  ),
329  xferCopy(points),
330  cells,
331  faceListList(0),
332  wordList(0), // boundaryPatchNames
333  wordList(0), // boundaryPatchTypes
334  "defaultFaces",
335  polyPatch::typeName,
336  wordList(0)
337  )
338  );
339  const polyMesh& mesh = meshPtr;
340 
341 
342 
343  if (readFaceFile)
344  {
345  label nPatches = 0;
346 
347  // List of Foam vertices per boundary face
348  faceList boundaryFaces;
349 
350  // For each boundary faces the Foam patchID
351  labelList boundaryPatch;
352 
353  //
354  // read boundary faces
355  //
356 
357  IFstream faceStream(faceFile);
358 
359  do
360  {
361  faceStream.getLine(line);
362  }
363  while (line.size() && line[0] == '#');
364 
365  IStringStream faceLine(line);
366 
367  label nFaces, nFaceAttr;
368 
369  faceLine >> nFaces >> nFaceAttr;
370 
371 
372  Info<< "Read .face header:" << endl
373  << " faces : " << nFaces << endl
374  << " nAttr : " << nFaceAttr << endl
375  << endl;
376 
377 
378  if (nFaceAttr != 1)
379  {
380  FatalIOErrorIn(args.executable().c_str(), faceStream)
381  << "Expect boundary markers to be"
382  << " present in .face file." << endl
383  << "This is the second number in the header which is now:"
384  << nFaceAttr << exit(FatalIOError);
385  }
386 
387  // List of Foam vertices per boundary face
388  boundaryFaces.setSize(nFaces);
389 
390  // For each boundary faces the Foam patchID
391  boundaryPatch.setSize(nFaces);
392  boundaryPatch = -1;
393 
394  label facei = 0;
395 
396  // Region to patch conversion
397  Map<label> regionToPatch;
398 
399  face f(3);
400 
401  while (faceStream.good())
402  {
403  faceStream.getLine(line);
404 
405  if (line.size() && line[0] != '#')
406  {
407  IStringStream faceLine(line);
408 
409  label tetGenFacei, dummy, region;
410 
411  faceLine >> tetGenFacei;
412 
413  // Read face and reverse orientation (Foam needs outwards
414  // pointing)
415  for (label i = 0; i < 3; i++)
416  {
417  label nodeI;
418  faceLine >> nodeI;
419  f[2-i] = nodeToPoint[nodeI];
420  }
421 
422 
423  if (findFace(mesh, f) >= mesh.nInternalFaces())
424  {
425  boundaryFaces[facei] = f;
426 
427  if (nFaceAttr > 0)
428  {
429  // First attribute is the region number
430  faceLine >> region;
431 
432 
433  // Get Foam patchID and update region->patch table.
434  label patchi = 0;
435 
436  Map<label>::iterator patchFind =
437  regionToPatch.find(region);
438 
439  if (patchFind == regionToPatch.end())
440  {
441  patchi = nPatches;
442 
443  Info<< "Mapping tetgen region " << region
444  << " to Foam patch "
445  << patchi << endl;
446 
447  regionToPatch.insert(region, nPatches++);
448  }
449  else
450  {
451  patchi = patchFind();
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  forAllConstIter(Map<label>, regionToPatch, iter)
478  {
479  Info<< " region:" << iter.key() << '\t' << "patch:"
480  << iter() << endl;
481  }
482  Info<< endl;
483 
484 
485  // Storage for boundary faces
486  faceListList patchFaces(nPatches);
487  wordList patchNames(nPatches);
488 
489  forAll(patchNames, patchi)
490  {
491  patchNames[patchi] = word("patch") + name(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  xferMove(points),
534  cells,
535  patchFaces,
536  patchNames,
537  patchTypes,
540  patchPhysicalTypes
541  )
542  );
543  }
544 
545 
546  Info<< "Writing mesh to " << runTime.constant() << endl << endl;
547 
548  meshPtr().write();
549 
550  Info<< "End\n" << endl;
551 
552  return 0;
553 }
554 
555 
556 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:402
const labelListList & pointFaces() const
word defaultFacesType
Definition: readKivaGrid.H:461
List< faceList > faceListList
Definition: faceListFwd.H:45
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:428
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
static const cellModel * lookup(const word &)
Look up a model by name and return a pointer to the model or NULL.
Definition: cellModeller.C:88
A class for handling file names.
Definition: fileName.H:69
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static iteratorEnd end()
iteratorEnd set to beyond the end of any HashTable
Definition: HashTable.H:106
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:75
error FatalError
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:319
Cell-face mesh analysis engine.
Definition: primitiveMesh.H:74
static fvMesh * meshPtr
Definition: globalFoam.H:52
An analytical geometric cellShape.
Definition: cellShape.H:69
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:76
wordList patchTypes(nPatches)
bool isFile(const fileName &, const bool checkGzip=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:492
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:306
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:253
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:154
Xfer< T > xferCopy(const T &)
Construct by copying the contents of the arg.
const word & executable() const
Name of executable without the path.
Definition: argListI.H:30
bool insert(const Key &, const T &newElmt)
Insert a new hashedEntry.
Definition: HashTableI.H:80
iterator find(const Key &)
Find and return an iterator set at the hashedEntry.
Definition: HashTable.C:138
scalar y
const cellShapeList & cells
const pointField & points
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:108
Xfer< T > xferMove(T &)
Construct by transferring the contents of the arg.
A class for handling words, derived from string.
Definition: word.H:59
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:97
wordList patchNames(nPatches)
forAllConstIter(PtrDictionary< phaseModel >, mixture.phases(), phase)
Definition: pEqn.H:29
errorManip< error > abort(error &err)
Definition: errorManip.H:131
void reset(const label nPoints, const label nInternalFaces, const label nFaces, const label nCells)
Reset this primitiveMesh given the primitive array sizes.
static const char nl
Definition: Ostream.H:262
Input from file stream.
Definition: IFstream.H:81
labelList f(nPoints)
#define FatalIOErrorIn(functionName, ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:325
word defaultFacesName
Definition: readKivaGrid.H:460
word name(const complex &)
Return a string representation of a complex.
Definition: complex.C:47
List< word > wordList
A List of words.
Definition: fileName.H:54
void setSize(const label)
Reset size of List.
Definition: List.C:295
label patchi
vector point
Point is a vector.
Definition: point.H:41
#define WarningInFunction
Report a warning using Foam::Warning.
Tet storage. Null constructable (unfortunately tetrahedron<point, point> is not)
Definition: tetPoints.H:50
Input from memory buffer stream.
Definition: IStringStream.H:49
virtual const faceList & faces() const =0
Return faces.
Maps a geometry to a set of cell primitives, which enables geometric cell data to be calculated witho...
Definition: cellModel.H:64
messageStream Info
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: PtrList.H:53
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:83
Foam::argList args(argc, argv)
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:91
label nInternalFaces() const
virtual bool write() const
Write mesh using IO settings from time.
Definition: fvMesh.C:870
Namespace for OpenFOAM.
IOerror FatalIOError