LifeV
ParserGmsh.hpp
Go to the documentation of this file.
1 /********************************************************************************
2  Copyright (C) 2004, 2005, 2007 EPFL, Politecnico di Milano, INRIA
3  Copyright (C) 2010 EPFL, Politecnico di Milano, Emory University
4 
5  This file is part of LifeV.
6 
7  LifeV is free software; you can redistribute it and/or modify
8  it under the terms of the GNU Lesser General Public License as published by
9  the Free Software Foundation, either version 3 of the License, or
10  (at your option) any later version.
11 
12  LifeV is distributed in the hope that it will be useful,
13  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15  Lesser General Public License for more details.
16 
17  You should have received a copy of the GNU Lesser General Public License
18  along with LifeV. If not, see <http://www.gnu.org/licenses/>.
19 ********************************************************************************/
20 
21 /**
22  * @file ParserGmsh.hpp
23  * @brief Gmsh files (.gmsh, .msh) reader.
24  * @author Simone Pezzuto <simone.pezzuto@mail.polimi.it>
25  * @date 10/2012
26 **/
27 
28 #ifndef PARSER_GMSH_HPP__
29 #define PARSER_GMSH_HPP__
30 
31 #include <lifev/core/LifeV.hpp>
32 #include <lifev/core/mesh/BareMesh.hpp>
33 
34 #include <fstream>
35 #include <deque>
36 
37 namespace LifeV
38 {
39 
40 namespace MeshIO
41 {
42 
43 namespace
44 {
45 // Gmsh doesn't specify anything on int type
46 typedef int gmsh_int_t;
47 typedef double gmsh_float_t;
48 
49 typedef struct
50 {
52  short type;
55 } gmsh_elm_t;
56 
57 static const LifeV::UInt elm_nodes_num[] =
58 {
59  2, 3, 4, 4, 8, 6, 5, 3, 6, 9, 10, 27, 18, 14,
60  1, 8, 20, 15, 13, 9, 10, 12, 15, 15, 21, 4, 5, 6,
61  20, 35, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
62  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
63  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
64  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
65  0, 0, 0, 0, 0, 0, 0, 64, 125
66 };
67 
68 namespace utils
69 {
70 namespace detail
71 {
72 // int to type (see std::integral_constant)
73 template <int id>
74 struct shape_id
75 {
76  static const int value = id;
77 };
78 
79 // type container
80 template <typename T>
81 struct shape_type
82 {
83  typedef T type;
84 };
85 }
86 
87 // <shape, int> map
88 // the friend function is dummy, but it's useful
89 // because if shape is associated to more than one
90 // int (or viceversa), an error is raised
91 template <typename shape, int id>
93  : detail::shape_id<id>, detail::shape_type<shape>
94 {
95 private:
96  friend detail::shape_type<shape> registered (detail::shape_id<id>)
97  {
98  return detail::shape_type<shape>();
99  }
100 };
101 
102 // this is the register
103 template <typename geoshape>
104 struct id_of;
105 
106 // shortcut macro
107 # define GMSH_REGISTER_SHAPE(shape, id)
108  template <> struct id_of<shape> : register_shape<shape, id> {}
109 
110 // gmsh shapes (same for legacy format)
123 
124 // Remove the macro
125 # undef GMSH_REGISTER_SHAPE
126 
127 // LifeV shapes
128 template <typename GeoShape>
130 {
131  typedef GeoShape elem_type;
132  typedef typename elem_type::GeoBShape facet_type;
133  typedef typename facet_type::GeoBShape ridge_type;
134  typedef typename ridge_type::GeoBShape peak_type;
135  static const int dim = GeoShape::S_nDimensions;
136  enum
137  {
138  elem_id = id_of<elem_type>::value,
139  facet_id = id_of<facet_type>::value,
140  ridge_id = id_of<ridge_type>::value,
141  peak_id = id_of<peak_type>::value
142  };
143 };
144 } // anonymous
145 } // utils
146 
147 /**
148  * @fn ReadGmshFile
149  * @brief Reads a .msh file (ASCII, binary or legacy).
150  *
151  * @param filename, name of the file
152  * @param baremesh, a baremesh object to be filled
153  * @param regionFlag, the id of the mesh (default 0)
154  *
155  * @return true if everything is ok
156 **/
157 template <typename GeoShape>
158 bool ReadGmshFile (const std::string& filename,
159  LifeV::BareMesh<GeoShape>& baremesh,
160  LifeV::ID regionFlag = 0,
161  bool verbose = false)
162 {
163 
164  // Check if the file is valid
165  // ==========================
166  std::ifstream ifile (filename.c_str(), std::ios::in | std::ios::binary);
167  if (ifile.fail() )
168  {
169  // Error: file not found
170  std::cerr << "[ERROR:GmshIO] File '"
171  << filename << "' not found." << std::endl;
172  return false;
173  }
174 
175  // Clean baremesh object
176  baremesh.clear();
177 
178  // ============
179  // begin.HEADER
180  // ============
181  bool is_binary = false, is_legacy = false;
182  bool is_differ_endian = false;
183  {
184  bool status_ok = false;
185  std::string line;
186  std::getline (ifile, line);
187  if (line == "$MeshFormat")
188  {
189  // Parse the next line
190  std::getline (ifile, line);
191  std::stringstream iss (line);
192 
193  float version;
194  int doublesize;
195  iss >> version >> is_binary >> doublesize;
196  status_ok = iss.eof();
197 
198  // Endianness
199  if (is_binary)
200  {
201  // Check file endianness
202  union
203  {
204  gmsh_int_t i;
205  char c[sizeof (gmsh_int_t)];
206  } one;
207  ifile.read (one.c, sizeof (gmsh_int_t) );
208  ifile.ignore (1);
209  is_differ_endian = (one.i != 1);
210  // Check if system is little-endian
211  if (is_differ_endian)
212  {
213  std::cerr << "[ERROR:GmshIO] Different endianness of system"
214  << " and file not supported" << std::endl;
215  return false;
216  }
217  }
218 
219  if (verbose)
220  std::clog << "[INFO:GmshIO] Found gmsh header with "
221  << (is_binary ? "binary" : "ASCII")
222  << " data." << std::endl;
223  }
224  else if (line == "$NOD")
225  {
226  // Legacy format
227  is_legacy = true;
228  status_ok = true;
229 
230  if (verbose)
231  std::clog << "[INFO:GmshIO] Found LEGACY gmsh header"
232  << std::endl;
233  }
234 
235  // Status should be fine if everything has been found
236  if (!status_ok)
237  {
238  std::cerr << "[ERROR:GmshIO] Error during parsing header"
239  << std::endl;
240  return false;
241  }
242  }
243 
244  // ===========
245  // begin.NODES
246  // ===========
247  {
248  // Back to the initial position
249  ifile.seekg (0, std::ios::beg);
250 
251  // Look for the node section
252  std::string node_tag = (is_legacy) ? "$NOD" : "$Nodes";
253 
254  // Seeking to the correct position
255  std::string line;
256  while (std::getline (ifile, line) && line != node_tag) {};
257  if (ifile.eof() )
258  {
259  std::cerr << "[ERROR:GmshIO] Nodes section not found" << std::endl;
260  return false;
261  }
262 
263  // Next line is the number of nodes
264  std::getline (ifile, line);
265  std::size_t num_nodes = atoi (line.c_str() );
266  if (!num_nodes)
267  {
268  std::cerr << "[ERROR:GmshIO] Number of nodes not correct" << std::endl;
269  return false;
270  }
271 
272  // Reshape the points datastructure: 3 x nnodes
273  baremesh.points.reshape (3, num_nodes);
274  baremesh.pointMarkers.resize (num_nodes);
275 
276  std::stringstream iss;
277 
278  // Loading the nodes (same for legacy)
279  for (std::size_t i = 0; i < num_nodes; ++i)
280  {
281  gmsh_int_t id;
282  gmsh_float_t p[3];
283 
284  if (is_binary)
285  {
286  // Binary case
287  ifile.read (reinterpret_cast<char*> (&id), sizeof (gmsh_int_t) );
288  ifile.read (reinterpret_cast<char*> (p), 3 * sizeof (gmsh_float_t) );
289  }
290  else
291  {
292  // ASCII case (also if legacy)
293  std::getline (ifile, line);
294  iss.clear();
295  iss.str (line.c_str() );
296  // Parsing
297  iss >> id >> p[0] >> p[1] >> p[2];
298  }
299 
300  // Adds the point
301  baremesh.points (0, id - 1) = p[0];
302  baremesh.points (1, id - 1) = p[1];
303  baremesh.points (2, id - 1) = p[2];
304 
305  }
306  if (is_binary)
307  {
308  ifile.ignore (1);
309  }
310 
311  // Check if next line is $EndNodes
312  std::getline (ifile, line);
313  if (line != "$EndNodes" && line != "$ENDNOD")
314  {
315  std::cerr << "[ERROR:GmshIO] Something wrong with Nodes section"
316  << std::endl;
317  return false;
318  }
319 
320  if (verbose)
321  std::clog << "[INFO:GmshIO] Found " << num_nodes << " nodes."
322  << std::endl;
323  }
324 
325  // ==============
326  // begin.ELEMENTS
327  // ==============
328  {
329  // A gmsh elements can be anything, i.e. volume, edge, face, point
330  // with an arbitrary number of tags.
331  std::string elm_tag = (is_legacy) ? "$ELM" : "$Elements";
332 
333  // Seeking to the correct position
334  std::string line;
335  while (std::getline (ifile, line) && line != elm_tag) {};
336  if (ifile.eof() )
337  {
338  std::cerr << "[ERROR:GmshIO] Elements section not found" << std::endl;
339  return false;
340  }
341  // Next line is the number of elements
342  std::getline (ifile, line);
343  std::size_t num_elms = atoi (line.c_str() );
344 
345  // Don't know how many elements of each type there are, so we cannot
346  // resize the data structures yet.
347  baremesh.nDimensions = utils::adm_shapes<GeoShape>::dim;
348 
349  // Admissible shapes ids inherited from GeoShape
350  const short s_ids[4] =
351  {
352  utils::adm_shapes<GeoShape>::elem_id,
353  utils::adm_shapes<GeoShape>::facet_id,
354  utils::adm_shapes<GeoShape>::ridge_id,
355  utils::adm_shapes<GeoShape>::peak_id
356  };
357 
358  // Stores the (gmsh) elements nodes and the type
359  std::deque<gmsh_elm_t> elems[4];
360 
361 
362  // Loading the elements
363  bool status_ok = false;
364  if (is_binary)
365  {
366  // Binary format
367  gmsh_int_t elm_count = 0;
368  do
369  {
370  // header = { elm_type, num_elems, num_tags }
371  gmsh_int_t header[3];
372  ifile.read (reinterpret_cast<char*> (header), 3 * sizeof (gmsh_int_t) );
373  // The type of the element, from 0 (elem) to 3 (peak)
374  short s_type = static_cast<short> (std::find (s_ids, s_ids + 4, header[0]) - s_ids);
375  // Is the shape admissible?
376  if (s_type == 4)
377  {
378  // Found a shape that cannot be stored
379  std::cerr << "[ERROR:GmshIO] Found elements "
380  << " of type " << header[0]
381  << " , which is not an allowed shape." << std::endl;
382  status_ok = false;
383  break;
384  }
385  // Nodes of element of this type
386  gmsh_int_t nnodes = elm_nodes_num[header[0] - 1];
387  // Next we have the elements { tags, nodes, id } * num_elems
388  gmsh_int_t total_size = header[1] * (1 + header[2] + nnodes);
389  std::vector<gmsh_int_t> e (total_size);
390  // Read at once
391  ifile.read (reinterpret_cast<char*> (&e[0]), total_size * sizeof (gmsh_int_t) );
392  // Increment counter
393  elm_count += header[1];
394  // Fill the queue
395  for (gmsh_int_t k = 0; k < header[1]; ++k)
396  {
397  gmsh_elm_t tmp;
398  tmp.type = header[0];
399  // Fill the tags
400  tmp.tags.reserve (header[2]);
401  std::copy (e.begin(), e.begin() + header[2] + 1, std::back_inserter (tmp.tags) );
402  // Fill the nodes
403  tmp.nodes.reserve (nnodes);
404  std::copy (e.begin() + header[2] + 1, e.end(), std::back_inserter (tmp.nodes) );
405  // Save
406  elems[s_type].push_back (tmp);
407  }
408  status_ok = true;
409  }
410  while (elm_count < static_cast<gmsh_int_t> (num_elms) );
411  // Skip return
412  ifile.ignore (1);
413  }
414  else
415  {
416  // ASCII format
417  typedef std::istream_iterator<gmsh_int_t> iss_it;
418  std::stringstream iss;
419  for (std::size_t i = 0; i < num_elms; ++i)
420  {
421  gmsh_int_t id, type, num_tags;
422 
423  // Gathering id, type and number of tags
424  std::getline (ifile, line);
425  iss.clear();
426  iss.str (line);
427  iss >> id >> type >> num_tags;
428  if (is_legacy)
429  {
430  num_tags = 2;
431  }
432 
433  // The type of the element, from 0 (elem) to 3 (peak)
434  short s_type = static_cast<short> (std::find (s_ids, s_ids + 4, type) - s_ids);
435  // Is the shape admissible?
436  if (s_type == 4)
437  {
438  // Found a shape that cannot be stored
439  std::cerr << "[ERROR:GmshIO] Element " << id
440  << ", of type " << type
441  << ", has a not allowed shape." << std::endl;
442  status_ok = false;
443  break;
444  }
445 
446  // Parsing nodes and tags
447  gmsh_elm_t tmp;
448  tmp.type = type;
449  // Fill the tags
450  tmp.tags.resize (num_tags);
451  for (int k = 0; k < num_tags; ++k)
452  {
453  iss >> tmp.tags[k];
454  }
455  // Fill the values
456  int nnodes = elm_nodes_num[type - 1];
457  tmp.nodes.reserve (nnodes);
458  std::copy (iss_it (iss), iss_it(), std::back_inserter (tmp.nodes) );
459  // Saving
460  elems[s_type].push_back (tmp);
461  status_ok = iss.eof();
462  }
463  }
464  // Check if next line is $EndElements
465  std::getline (ifile, line);
466  if (!status_ok || (line != "$EndElements" && line != "$ENDELM") )
467  {
468  std::cerr << "[ERROR:GmshIO] Something wrong with Elements section." << std::endl;
469  return false;
470  }
471 
472  // Now we can update the mesh
473  if (verbose)
474  std::clog << "[INFO:GmshIO] Highest dimension should be "
475  << baremesh.nDimensions << "." << std::endl;
476  if (!elems[0].size() )
477  {
478  std::cerr << "[ERROR:GmshIO] No "
479  << baremesh.nDimensions << "d elements found!" << std::endl;
480  return false;
481  }
482 
483  // BareMesh data structures
484  LifeV::ArraySimple<LifeV::UInt>* bare_elm_ptr[3] =
485  {
486  &baremesh.elements,
487  &baremesh.facets,
488  &baremesh.ridges
489  };
490  std::vector<LifeV::ID>* bare_mrk_ptr[4] =
491  {
492  &baremesh.elementMarkers,
493  &baremesh.facetMarkers,
494  &baremesh.ridgeMarkers,
495  &baremesh.pointMarkers
496  };
497 
498  for (LifeV::UInt s = 0; s < baremesh.nDimensions + 1; ++s)
499  {
500  // (n-s)-dimensional element
501  LifeV::UInt num_s_elems = elems[s].size();
502  LifeV::UInt num_s_nodes = elm_nodes_num[s_ids[s] - 1];
503  // Allocating memory inside baremesh
504  if (s < 3)
505  {
506  bare_elm_ptr[s]->reshape (num_s_nodes, num_s_elems);
507  }
508  bare_mrk_ptr[s]->resize (num_s_elems);
509 
510  if (verbose)
511  std::clog << "[INFO:GmshIO] Found " << num_s_elems
512  << " marked " << (baremesh.nDimensions - s)
513  << "d elements." << std::endl;
514 
515  // Fill the values
516  for (LifeV::UInt i = 0; i < num_s_elems; ++i)
517  {
518  // Marker (only the first one)
519  bare_mrk_ptr[s]->at (i) = elems[s][i].tags[1];
520  // Nodes
521  if (s < 3)
522  {
523  for (LifeV::UInt k = 0; k < num_s_nodes; ++k)
524  {
525  (*bare_elm_ptr[s]) (k, i) = elems[s][i].nodes[k] - 1;
526  }
527  }
528  }
529  }
530 
531  // In general we have no information about boundary points and facets
532  // So we assume that all are boundary facets, and let the mesh checker
533  // repair it if necessary
534  baremesh.numBoundaryPoints = 0;
535  baremesh.numBoundaryFacets = baremesh.facets.numberOfColumns();
536 
537  }
538 
539  // ============
540  // begin.OTHERS
541  // ============
542  baremesh.regionMarkerID = regionFlag;
543 
544  // Everything seems fine
545  return true;
546 }
547 
548 
549 } // GmshIO
550 
551 } // LifeV
552 
553 #endif // PARSER_GMSH_HPP__
#define GMSH_REGISTER_SHAPE(shape, id)
Definition: ParserGmsh.hpp:107
friend detail::shape_type< shape > registered(detail::shape_id< id >)
Definition: ParserGmsh.hpp:96
A struct for a bare mesh.
Definition: BareMesh.hpp:53