gmsh_io.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2018 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 // C++ includes
19 #include <fstream>
20 #include <set>
21 #include <cstring> // std::memcpy
22 #include <numeric>
23 #include <unordered_map>
24 
25 // Local includes
26 #include "libmesh/libmesh_config.h"
28 #include "libmesh/boundary_info.h"
29 #include "libmesh/elem.h"
30 #include "libmesh/gmsh_io.h"
31 #include "libmesh/mesh_base.h"
32 
33 namespace libMesh
34 {
35 
36 // Initialize the static data member
38 
39 
40 
41 // Definition of the static function which constructs the ElementMaps object.
43 {
44  // Object to be filled up
45  ElementMaps em;
46 
47  // POINT (import only)
48  em.in.insert(std::make_pair(15, ElementDefinition(NODEELEM, 15, 0, 1)));
49 
50  // Add elements with trivial node mappings
51  em.add_def(ElementDefinition(EDGE2, 1, 1, 2));
52  em.add_def(ElementDefinition(EDGE3, 8, 1, 3));
53  em.add_def(ElementDefinition(TRI3, 2, 2, 3));
54  em.add_def(ElementDefinition(TRI6, 9, 2, 6));
55  em.add_def(ElementDefinition(QUAD4, 3, 2, 4));
56  em.add_def(ElementDefinition(QUAD8, 16, 2, 8));
57  em.add_def(ElementDefinition(QUAD9, 10, 2, 9));
58  em.add_def(ElementDefinition(HEX8, 5, 3, 8));
59  em.add_def(ElementDefinition(TET4, 4, 3, 4));
60  em.add_def(ElementDefinition(PRISM6, 6, 3, 6));
61  em.add_def(ElementDefinition(PYRAMID5, 7, 3, 5));
62 
63  // Add elements with non-trivial node mappings
64 
65  // HEX20
66  {
67  ElementDefinition eledef(HEX20, 17, 3, 20);
68  const unsigned int nodes[] = {0,1,2,3,4,5,6,7,8,11,12,9,13,10,14,15,16,19,17,18};
69  std::vector<unsigned int>(nodes, nodes+eledef.nnodes).swap(eledef.nodes); // swap trick
70  em.add_def(eledef);
71  }
72 
73  // HEX27
74  {
75  ElementDefinition eledef(HEX27, 12, 3, 27);
76  const unsigned int nodes[] = {0,1,2,3,4,5,6,7,8,11,12,9,13,10,14,
77  15,16,19,17,18,20,21,24,22,23,25,26};
78  std::vector<unsigned int>(nodes, nodes+eledef.nnodes).swap(eledef.nodes); // swap trick
79  em.add_def(eledef);
80  }
81 
82  // TET10
83  {
84  ElementDefinition eledef(TET10, 11, 3, 10);
85  const unsigned int nodes[] = {0,1,2,3,4,5,6,7,9,8};
86  std::vector<unsigned int>(nodes, nodes+eledef.nnodes).swap(eledef.nodes); // swap trick
87  em.add_def(eledef);
88  }
89 
90  // PRISM15
91  {
92  ElementDefinition eledef(PRISM15, 18, 3, 15);
93  const unsigned int nodes[] = {0,1,2,3,4,5,6,8,9,7,10,11,12,14,13};
94  std::vector<unsigned int>(nodes, nodes+eledef.nnodes).swap(eledef.nodes); // swap trick
95  em.add_def(eledef);
96  }
97 
98  // PRISM18
99  {
100  ElementDefinition eledef(PRISM18, 13, 3, 18);
101  const unsigned int nodes[] = {0,1,2,3,4,5,6,8,9,7,10,11,12,14,13,15,17,16};
102  std::vector<unsigned int>(nodes, nodes+eledef.nnodes).swap(eledef.nodes); // swap trick
103  em.add_def(eledef);
104  }
105 
106  return em;
107 }
108 
109 
110 
113  _binary(false),
114  _write_lower_dimensional_elements(true)
115 {
116 }
117 
118 
119 
123  _binary (false),
124  _write_lower_dimensional_elements(true)
125 {
126 }
127 
128 
129 
130 bool & GmshIO::binary ()
131 {
132  return _binary;
133 }
134 
135 
136 
138 {
140 }
141 
142 
143 
144 void GmshIO::read (const std::string & name)
145 {
146  std::ifstream in (name.c_str());
147  this->read_mesh (in);
148 }
149 
150 
151 
152 void GmshIO::read_mesh(std::istream & in)
153 {
154  // This is a serial-only process for now;
155  // the Mesh should be read on processor 0 and
156  // broadcast later
157  libmesh_assert_equal_to (MeshOutput<MeshBase>::mesh().processor_id(), 0);
158 
159  libmesh_assert(in.good());
160 
161  // clear any data in the mesh
163  mesh.clear();
164 
165  // some variables
166  int format=0, size=0;
167  Real version = 1.0;
168 
169  // Keep track of lower-dimensional blocks which are not BCs, but
170  // actually blocks of lower-dimensional elements.
171  std::set<subdomain_id_type> lower_dimensional_blocks;
172 
173  // Mapping from physical id -> (physical dim, physical name) pairs.
174  // These can refer to either "sidesets" or "subdomains"; we need to
175  // wait until the Mesh has been read to know which is which. Note
176  // that we are using 'int' as the key here rather than
177  // subdomain_id_type or boundary_id_type, since at this point, it
178  // could be either.
179  typedef std::pair<unsigned, std::string> GmshPhysical;
180  std::map<int, GmshPhysical> gmsh_physicals;
181 
182  // map to hold the node numbers for translation
183  // note the the nodes can be non-consecutive
184  std::map<unsigned int, unsigned int> nodetrans;
185 
186  // For reading the file line by line
187  std::string s;
188 
189  while (true)
190  {
191  // Try to read something. This may set EOF!
192  std::getline(in, s);
193 
194  if (in)
195  {
196  // Process s...
197 
198  if (s.find("$MeshFormat") == static_cast<std::string::size_type>(0))
199  {
200  in >> version >> format >> size;
201  if ((version != 2.0) && (version != 2.1) && (version != 2.2))
202  {
203  // Some notes on gmsh mesh versions:
204  //
205  // Mesh version 2.0 goes back as far as I know. It's not explicitly
206  // mentioned here: http://www.geuz.org/gmsh/doc/VERSIONS.txt
207  //
208  // As of gmsh-2.4.0:
209  // bumped mesh version format to 2.1 (small change in the $PhysicalNames
210  // section, where the group dimension is now required);
211  // [Since we don't even parse the PhysicalNames section at the time
212  // of this writing, I don't think this change affects us.]
213  //
214  // Mesh version 2.2 tested by Manav Bhatia; no other
215  // libMesh code changes were required for support
216  libmesh_error_msg("Error: Unknown msh file version " << version);
217  }
218 
219  if (format)
220  libmesh_error_msg("Error: Unknown data format for mesh in Gmsh reader.");
221  }
222 
223  // Read and process the "PhysicalNames" section.
224  else if (s.find("$PhysicalNames") == static_cast<std::string::size_type>(0))
225  {
226  // The lines in the PhysicalNames section should look like the following:
227  // 2 1 "frac" lower_dimensional_block
228  // 2 3 "top"
229  // 2 4 "bottom"
230  // 3 2 "volume"
231 
232  // Read in the number of physical groups to expect in the file.
233  unsigned int num_physical_groups = 0;
234  in >> num_physical_groups;
235 
236  // Read rest of line including newline character.
237  std::getline(in, s);
238 
239  for (unsigned int i=0; i<num_physical_groups; ++i)
240  {
241  // Read an entire line of the PhysicalNames section.
242  std::getline(in, s);
243 
244  // Use an istringstream to extract the physical
245  // dimension, physical id, and physical name from
246  // this line.
247  std::istringstream s_stream(s);
248  unsigned phys_dim;
249  int phys_id;
250  std::string phys_name;
251  s_stream >> phys_dim >> phys_id >> phys_name;
252 
253  // Not sure if this is true for all Gmsh files, but
254  // my test file has quotes around the phys_name
255  // string. So let's erase any quotes now...
256  phys_name.erase(std::remove(phys_name.begin(), phys_name.end(), '"'), phys_name.end());
257 
258  // Record this ID for later assignment of subdomain/sideset names.
259  gmsh_physicals[phys_id] = std::make_pair(phys_dim, phys_name);
260 
261  // If 's' also contains the libmesh-specific string
262  // "lower_dimensional_block", add this block ID to
263  // the list of blocks which are not boundary
264  // conditions.
265  if (s.find("lower_dimensional_block") != std::string::npos)
266  {
267  lower_dimensional_blocks.insert(cast_int<subdomain_id_type>(phys_id));
268 
269  // The user has explicitly told us that this
270  // block is a subdomain, so set that association
271  // in the Mesh.
272  mesh.subdomain_name(cast_int<subdomain_id_type>(phys_id)) = phys_name;
273  }
274  }
275  }
276 
277  // read the node block
278  else if (s.find("$NOD") == static_cast<std::string::size_type>(0) ||
279  s.find("$NOE") == static_cast<std::string::size_type>(0) ||
280  s.find("$Nodes") == static_cast<std::string::size_type>(0))
281  {
282  unsigned int num_nodes = 0;
283  in >> num_nodes;
284  mesh.reserve_nodes (num_nodes);
285 
286  // read in the nodal coordinates and form points.
287  Real x, y, z;
288  unsigned int id;
289 
290  // add the nodal coordinates to the mesh
291  for (unsigned int i=0; i<num_nodes; ++i)
292  {
293  in >> id >> x >> y >> z;
294  mesh.add_point (Point(x, y, z), i);
295  nodetrans[id] = i;
296  }
297 
298  // read the $ENDNOD delimiter
299  std::getline(in, s);
300  }
301 
302 
303  // Read the element block
304  else if (s.find("$ELM") == static_cast<std::string::size_type>(0) ||
305  s.find("$Elements") == static_cast<std::string::size_type>(0))
306  {
307  // For reading the number of elements and the node ids from the stream
308  unsigned int
309  num_elem = 0,
310  node_id = 0;
311 
312  // read how many elements are there, and reserve space in the mesh
313  in >> num_elem;
314  mesh.reserve_elem (num_elem);
315 
316  // As of version 2.2, the format for each element line is:
317  // elm-number elm-type number-of-tags < tag > ... node-number-list
318  // From the Gmsh docs:
319  // * the first tag is the number of the
320  // physical entity to which the element belongs
321  // * the second is the number of the elementary geometrical
322  // entity to which the element belongs
323  // * the third is the number of mesh partitions to which the element
324  // belongs
325  // * The rest of the tags are the partition ids (negative
326  // partition ids indicate ghost cells). A zero tag is
327  // equivalent to no tag. Gmsh and most codes using the
328  // MSH 2 format require at least the first two tags
329  // (physical and elementary tags).
330 
331  // Keep track of all the element dimensions seen
332  std::vector<unsigned> elem_dimensions_seen(3);
333 
334  // read the elements
335  for (unsigned int iel=0; iel<num_elem; ++iel)
336  {
337  unsigned int
338  id, type,
339  physical=1, elementary=1,
340  nnodes=0, ntags;
341 
342  // Note: tag has to be an int because it could be negative,
343  // see above.
344  int tag;
345 
346  if (version <= 1.0)
347  in >> id >> type >> physical >> elementary >> nnodes;
348 
349  else
350  {
351  in >> id >> type >> ntags;
352 
353  if (ntags > 2)
354  libmesh_do_once(libMesh::err << "Warning, ntags=" << ntags << ", but we currently only support reading 2 flags." << std::endl;);
355 
356  for (unsigned int j = 0; j < ntags; j++)
357  {
358  in >> tag;
359  if (j == 0)
360  physical = tag;
361  else if (j == 1)
362  elementary = tag;
363  }
364  }
365 
366  // Consult the import element table to determine which element to build
367  auto eletypes_it = _element_maps.in.find(type);
368 
369  // Make sure we actually found something
370  if (eletypes_it == _element_maps.in.end())
371  libmesh_error_msg("Element type " << type << " not found!");
372 
373  // Get a reference to the ElementDefinition
374  const GmshIO::ElementDefinition & eletype = eletypes_it->second;
375 
376  // If we read nnodes, make sure it matches the number in eletype.nnodes
377  if (nnodes != 0 && nnodes != eletype.nnodes)
378  libmesh_error_msg("nnodes = " << nnodes << " and eletype.nnodes = " << eletype.nnodes << " do not match.");
379 
380  // Assign the value from the eletype object.
381  nnodes = eletype.nnodes;
382 
383  // Don't add 0-dimensional "point" elements to the
384  // Mesh. They should *always* be treated as boundary
385  // "nodeset" data.
386  if (eletype.dim > 0)
387  {
388  // Record this element dimension as being "seen".
389  // We will treat all elements with dimension <
390  // max(dimension) as specifying boundary conditions,
391  // but we won't know what max_elem_dimension_seen is
392  // until we read the entire file.
393  elem_dimensions_seen[eletype.dim-1] = 1;
394 
395  // Add the element to the mesh
396  {
397  Elem * elem = Elem::build(eletype.type).release();
398  elem->set_id(iel);
399  elem = mesh.add_elem(elem);
400 
401  // Make sure that the libmesh element we added has nnodes nodes.
402  if (elem->n_nodes() != nnodes)
403  libmesh_error_msg("Number of nodes for element " \
404  << id \
405  << " of type " << eletype.type \
406  << " (Gmsh type " << type \
407  << ") does not match Libmesh definition. " \
408  << "I expected " << elem->n_nodes() \
409  << " nodes, but got " << nnodes);
410 
411  // Add node pointers to the elements.
412  // If there is a node translation table, use it.
413  if (eletype.nodes.size() > 0)
414  for (unsigned int i=0; i<nnodes; i++)
415  {
416  in >> node_id;
417  elem->set_node(eletype.nodes[i]) = mesh.node_ptr(nodetrans[node_id]);
418  }
419  else
420  {
421  for (unsigned int i=0; i<nnodes; i++)
422  {
423  in >> node_id;
424  elem->set_node(i) = mesh.node_ptr(nodetrans[node_id]);
425  }
426  }
427 
428  // Finally, set the subdomain ID to physical. If this is a lower-dimension element, this ID will
429  // eventually go into the Mesh's BoundaryInfo object.
430  elem->subdomain_id() = static_cast<subdomain_id_type>(physical);
431  }
432  }
433 
434  // Handle 0-dimensional elements (points) by adding
435  // them to the BoundaryInfo object with
436  // boundary_id == physical.
437  else
438  {
439  // This seems like it should always be the same
440  // number as the 'id' we already read in on this
441  // line. At least it was in the example gmsh
442  // file I had...
443  in >> node_id;
445  (nodetrans[node_id],
446  static_cast<boundary_id_type>(physical));
447  }
448  } // element loop
449 
450  // read the $ENDELM delimiter
451  std::getline(in, s);
452 
453  // Record the max and min element dimension seen while reading the file.
454  unsigned char
455  max_elem_dimension_seen=1,
456  min_elem_dimension_seen=3;
457 
458  for (std::size_t i=0; i<elem_dimensions_seen.size(); ++i)
459  if (elem_dimensions_seen[i])
460  {
461  // Debugging
462  // libMesh::out << "Seen elements of dimension " << i+1 << std::endl;
463  max_elem_dimension_seen =
464  std::max(max_elem_dimension_seen, cast_int<unsigned char>(i+1));
465  min_elem_dimension_seen =
466  std::min(min_elem_dimension_seen, cast_int<unsigned char>(i+1));
467  }
468 
469  // Debugging:
470  // libMesh::out << "max_elem_dimension_seen=" << max_elem_dimension_seen << std::endl;
471  // libMesh::out << "min_elem_dimension_seen=" << min_elem_dimension_seen << std::endl;
472 
473  // If the difference between the max and min element dimension seen is larger than
474  // 1, (e.g. the file has 1D and 3D elements only) we don't handle this case.
475  if (max_elem_dimension_seen - min_elem_dimension_seen > 1)
476  libmesh_error_msg("Cannot handle meshes with dimension mismatch greater than 1.");
477 
478  // How many different element dimensions did we see while reading from file?
479  unsigned n_dims_seen = std::accumulate(elem_dimensions_seen.begin(),
480  elem_dimensions_seen.end(),
481  static_cast<unsigned>(0),
482  std::plus<unsigned>());
483 
484  // Have not yet tested a case where 1, 2, and 3D elements are all in the same Mesh,
485  // though it should theoretically be possible to handle.
486  if (n_dims_seen == 3)
487  libmesh_error_msg("Reading meshes with 1, 2, and 3D elements not currently supported.");
488 
489  // Set mesh_dimension based on the largest element dimension seen.
490  mesh.set_mesh_dimension(max_elem_dimension_seen);
491 
492  // Now that we know the maximum element dimension seen,
493  // we know whether the physical names are subdomain
494  // names or sideset names.
495  for (const auto & pr : gmsh_physicals)
496  {
497  // Extract data
498  int phys_id = pr.first;
499  unsigned phys_dim = pr.second.first;
500  const std::string & phys_name = pr.second.second;
501 
502  // If the physical's dimension matches the largest
503  // dimension we've seen, it's a subdomain name.
504  if (phys_dim == max_elem_dimension_seen)
505  mesh.subdomain_name(cast_int<subdomain_id_type>(phys_id)) = phys_name;
506 
507  // Otherwise, if it's not a lower-dimensional
508  // block, it's a sideset name.
509  else if (phys_dim < max_elem_dimension_seen &&
510  !lower_dimensional_blocks.count(cast_int<boundary_id_type>(phys_id)))
511  mesh.get_boundary_info().sideset_name(cast_int<boundary_id_type>(phys_id)) = phys_name;
512  }
513 
514  if (n_dims_seen > 1)
515  {
516  // Store lower-dimensional elements in a map sorted
517  // by Elem::key(). We use a multimap for two reasons:
518  // 1.) The hash function is not guaranteed to be
519  // unique, so different lower-dimensional elements
520  // could theoretically hash to the same value,
521  // although this is pretty unlikely.
522  // 2.) The Gmsh file may contain multiple
523  // lower-dimensional elements for a single side in
524  // order to implement multiple boundary ids for a
525  // single side. These lower-dimensional elements
526  // will all hash to the same value, and we need to
527  // be able to store all of them.
528  typedef std::unordered_multimap<dof_id_type, Elem *> provide_container_t;
529  provide_container_t provide_bcs;
530 
531  // 1st loop over active elements - get info about lower-dimensional elements.
532  for (auto & elem : mesh.active_element_ptr_range())
533  if (elem->dim() < max_elem_dimension_seen &&
534  !lower_dimensional_blocks.count(elem->subdomain_id()))
535  {
536  // To be consistent with the previous
537  // GmshIO behavior, add all the
538  // lower-dimensional elements' nodes to
539  // the Mesh's BoundaryInfo object with the
540  // lower-dimensional element's subdomain
541  // ID.
542  for (auto n : elem->node_index_range())
543  mesh.get_boundary_info().add_node(elem->node_id(n),
544  elem->subdomain_id());
545 
546  // Store this elem in a quickly-searchable
547  // container to use it to assign boundary
548  // conditions later.
549  provide_bcs.insert(std::make_pair(elem->key(), elem));
550  }
551 
552  // 2nd loop over active elements - use lower dimensional element data to set BCs for higher dimensional elements
553  for (auto & elem : mesh.active_element_ptr_range())
554  if (elem->dim() == max_elem_dimension_seen)
555  {
556  // This is a max-dimension element that
557  // may require BCs. For each of its
558  // sides, including internal sides, we'll
559  // see if one more more lower-dimensional elements
560  // provides boundary information for it.
561  // Note that we have not yet called
562  // find_neighbors(), so we can't use
563  // elem->neighbor(sn) in this algorithm...
564  for (auto sn : elem->side_index_range())
565  for (const auto & pr : as_range(provide_bcs.equal_range(elem->key(sn))))
566  {
567  // For each side side in the provide_bcs multimap...
568  // Construct the side for hash verification.
569  std::unique_ptr<Elem> side (elem->build_side_ptr(sn));
570 
571  // Construct the lower-dimensional element to compare to the side.
572  Elem * lower_dim_elem = pr.second;
573 
574  // This was a hash, so it might not be perfect. Let's verify...
575  if (*lower_dim_elem == *side)
576  {
577  // Add the lower-dimensional
578  // element's subdomain_id as a
579  // boundary_id for the
580  // higher-dimensional element.
581  boundary_id_type bid = cast_int<boundary_id_type>(lower_dim_elem->subdomain_id());
582  mesh.get_boundary_info().add_side(elem, sn, bid);
583  }
584  }
585  }
586 
587  // 3rd loop over active elements - Remove the lower-dimensional elements
588  for (auto & elem : mesh.active_element_ptr_range())
589  if (elem->dim() < max_elem_dimension_seen &&
590  !lower_dimensional_blocks.count(elem->subdomain_id()))
591  mesh.delete_elem(elem);
592  } // end if (n_dims_seen > 1)
593  } // if $ELM
594 
595  continue;
596  } // if (in)
597 
598 
599  // If !in, check to see if EOF was set. If so, break out
600  // of while loop.
601  if (in.eof())
602  break;
603 
604  // If !in and !in.eof(), stream is in a bad state!
605  libmesh_error_msg("Stream is bad! Perhaps the file does not exist?");
606 
607  } // while true
608 }
609 
610 
611 
612 void GmshIO::write (const std::string & name)
613 {
614  if (MeshOutput<MeshBase>::mesh().processor_id() == 0)
615  {
616  // Open the output file stream
617  std::ofstream out_stream (name.c_str());
618 
619  // Make sure it opened correctly
620  if (!out_stream.good())
621  libmesh_file_error(name.c_str());
622 
623  this->write_mesh (out_stream);
624  }
625 }
626 
627 
628 
629 void GmshIO::write_nodal_data (const std::string & fname,
630  const std::vector<Number> & soln,
631  const std::vector<std::string> & names)
632 {
633  LOG_SCOPE("write_nodal_data()", "GmshIO");
634 
635  if (MeshOutput<MeshBase>::mesh().processor_id() == 0)
636  this->write_post (fname, &soln, &names);
637 }
638 
639 
640 
641 void GmshIO::write_mesh (std::ostream & out_stream)
642 {
643  // Be sure that the stream is valid.
644  libmesh_assert (out_stream.good());
645 
646  // Get a const reference to the mesh
648 
649  // If requested, write out lower-dimensional elements for
650  // element-side-based boundary conditions.
651  std::size_t n_boundary_faces = 0;
653  n_boundary_faces = mesh.get_boundary_info().n_boundary_conds();
654 
655  // Note: we are using version 2.0 of the gmsh output format.
656 
657  // Write the file header.
658  out_stream << "$MeshFormat\n";
659  out_stream << "2.0 0 " << sizeof(Real) << '\n';
660  out_stream << "$EndMeshFormat\n";
661 
662  // write the nodes in (n x y z) format
663  out_stream << "$Nodes\n";
664  out_stream << mesh.n_nodes() << '\n';
665 
666  for (unsigned int v=0; v<mesh.n_nodes(); v++)
667  out_stream << mesh.node_ref(v).id()+1 << " "
668  << mesh.node_ref(v)(0) << " "
669  << mesh.node_ref(v)(1) << " "
670  << mesh.node_ref(v)(2) << '\n';
671  out_stream << "$EndNodes\n";
672 
673  {
674  // write the connectivity
675  out_stream << "$Elements\n";
676  out_stream << mesh.n_active_elem() + n_boundary_faces << '\n';
677 
678  // loop over the elements
679  for (const auto & elem : mesh.active_element_ptr_range())
680  {
681  // Make sure we have a valid entry for
682  // the current element type.
683  libmesh_assert (_element_maps.out.count(elem->type()));
684 
685  // consult the export element table
686  auto def_it = _element_maps.out.find(elem->type());
687 
688  // Assert that we found it
689  if (def_it == _element_maps.out.end())
690  libmesh_error_msg("Element type " << elem->type() << " not found in _element_maps.");
691 
692  // Get a reference to the ElementDefinition object
693  const ElementDefinition & eletype = def_it->second;
694 
695  // The element mapper better not require any more nodes
696  // than are present in the current element!
697  libmesh_assert_less_equal (eletype.nodes.size(), elem->n_nodes());
698 
699  // elements ids are 1 based in Gmsh
700  out_stream << elem->id()+1 << " ";
701  // element type
702  out_stream << eletype.gmsh_type;
703 
704  // write the number of tags (3) and their values:
705  // 1 (physical entity)
706  // 2 (geometric entity)
707  // 3 (partition entity)
708  out_stream << " 3 "
709  << static_cast<unsigned int>(elem->subdomain_id())
710  << " 0 "
711  << elem->processor_id()+1
712  << " ";
713 
714  // if there is a node translation table, use it
715  if (eletype.nodes.size() > 0)
716  for (auto i : elem->node_index_range())
717  out_stream << elem->node_id(eletype.nodes[i])+1 << " "; // gmsh is 1-based
718  // otherwise keep the same node order
719  else
720  for (auto i : elem->node_index_range())
721  out_stream << elem->node_id(i)+1 << " "; // gmsh is 1-based
722  out_stream << "\n";
723  } // element loop
724  }
725 
726  {
727  // A counter for writing surface elements to the Gmsh file
728  // sequentially. We start numbering them with a number strictly
729  // larger than the largest element ID in the mesh. Note: the
730  // MeshBase docs say "greater than or equal to" the maximum
731  // element id in the mesh, so technically we might need a +1 here,
732  // but all of the implementations return an ID strictly greater
733  // than the largest element ID in the Mesh.
734  unsigned int e_id = mesh.max_elem_id();
735 
736  // loop over the elements, writing out boundary faces
737  if (n_boundary_faces)
738  {
739  // Build a list of (elem, side, bc) tuples.
740  auto bc_triples = mesh.get_boundary_info().build_side_list();
741 
742  // Loop over these lists, writing data to the file.
743  for (const auto & t : bc_triples)
744  {
745  const Elem & elem = mesh.elem_ref(std::get<0>(t));
746 
747  std::unique_ptr<const Elem> side = elem.build_side_ptr(std::get<1>(t));
748 
749  // Map from libmesh elem type to gmsh elem type.
750  auto def_it = _element_maps.out.find(side->type());
751 
752  // If we didn't find it, that's an error
753  if (def_it == _element_maps.out.end())
754  libmesh_error_msg("Element type " << side->type() << " not found in _element_maps.");
755 
756  // consult the export element table
757  const GmshIO::ElementDefinition & eletype = def_it->second;
758 
759  // The element mapper better not require any more nodes
760  // than are present in the current element!
761  libmesh_assert_less_equal (eletype.nodes.size(), side->n_nodes());
762 
763  // elements ids are 1-based in Gmsh
764  out_stream << e_id+1 << " ";
765 
766  // element type
767  out_stream << eletype.gmsh_type;
768 
769  // write the number of tags:
770  // 1 (physical entity)
771  // 2 (geometric entity)
772  // 3 (partition entity)
773  out_stream << " 3 "
774  << std::get<2>(t)
775  << " 0 "
776  << elem.processor_id()+1
777  << " ";
778 
779  // if there is a node translation table, use it
780  if (eletype.nodes.size() > 0)
781  for (auto i : side->node_index_range())
782  out_stream << side->node_id(eletype.nodes[i])+1 << " "; // gmsh is 1-based
783 
784  // otherwise keep the same node order
785  else
786  for (auto i : side->node_index_range())
787  out_stream << side->node_id(i)+1 << " "; // gmsh is 1-based
788 
789  // Go to the next line
790  out_stream << "\n";
791 
792  // increment this index too...
793  ++e_id;
794  }
795  }
796 
797  out_stream << "$EndElements\n";
798  }
799 }
800 
801 
802 
803 void GmshIO::write_post (const std::string & fname,
804  const std::vector<Number> * v,
805  const std::vector<std::string> * solution_names)
806 {
807 
808  // Should only do this on processor 0!
809  libmesh_assert_equal_to (MeshOutput<MeshBase>::mesh().processor_id(), 0);
810 
811  // Create an output stream
812  std::ofstream out_stream(fname.c_str());
813 
814  // Make sure it opened correctly
815  if (!out_stream.good())
816  libmesh_file_error(fname.c_str());
817 
818  // create a character buffer
819  char buf[80];
820 
821  // Get a constant reference to the mesh.
823 
824  // write the data
825  if ((solution_names != nullptr) && (v != nullptr))
826  {
827  const unsigned int n_vars =
828  cast_int<unsigned int>(solution_names->size());
829 
830  if (!(v->size() == mesh.n_nodes()*n_vars))
831  libMesh::err << "ERROR: v->size()=" << v->size()
832  << ", mesh.n_nodes()=" << mesh.n_nodes()
833  << ", n_vars=" << n_vars
834  << ", mesh.n_nodes()*n_vars=" << mesh.n_nodes()*n_vars
835  << "\n";
836 
837  libmesh_assert_equal_to (v->size(), mesh.n_nodes()*n_vars);
838 
839  // write the header
840  out_stream << "$PostFormat\n";
841  if (this->binary())
842  out_stream << "1.2 1 " << sizeof(double) << "\n";
843  else
844  out_stream << "1.2 0 " << sizeof(double) << "\n";
845  out_stream << "$EndPostFormat\n";
846 
847  // Loop over the elements to see how much of each type there are
848  unsigned int n_points=0, n_lines=0, n_triangles=0, n_quadrangles=0,
849  n_tetrahedra=0, n_hexahedra=0, n_prisms=0, n_pyramids=0;
850  unsigned int n_scalar=0, n_vector=0, n_tensor=0;
851  unsigned int nb_text2d=0, nb_text2d_chars=0, nb_text3d=0, nb_text3d_chars=0;
852 
853  {
854  for (const auto & elem : mesh.active_element_ptr_range())
855  {
856  const ElemType elemtype = elem->type();
857 
858  switch (elemtype)
859  {
860  case EDGE2:
861  case EDGE3:
862  case EDGE4:
863  {
864  n_lines += 1;
865  break;
866  }
867  case TRI3:
868  case TRI6:
869  {
870  n_triangles += 1;
871  break;
872  }
873  case QUAD4:
874  case QUAD8:
875  case QUAD9:
876  {
877  n_quadrangles += 1;
878  break;
879  }
880  case TET4:
881  case TET10:
882  {
883  n_tetrahedra += 1;
884  break;
885  }
886  case HEX8:
887  case HEX20:
888  case HEX27:
889  {
890  n_hexahedra += 1;
891  break;
892  }
893  case PRISM6:
894  case PRISM15:
895  case PRISM18:
896  {
897  n_prisms += 1;
898  break;
899  }
900  case PYRAMID5:
901  {
902  n_pyramids += 1;
903  break;
904  }
905  default:
906  libmesh_error_msg("ERROR: Nonexistent element type " << elem->type());
907  }
908  }
909  }
910 
911  // create a view for each variable
912  for (unsigned int ivar=0; ivar < n_vars; ivar++)
913  {
914  std::string varname = (*solution_names)[ivar];
915 
916  // at the moment, we just write out scalar quantities
917  // later this should be made configurable through
918  // options to the writer class
919  n_scalar = 1;
920 
921  // write the variable as a view, and the number of time steps
922  out_stream << "$View\n" << varname << " " << 1 << "\n";
923 
924  // write how many of each geometry type are written
925  out_stream << n_points * n_scalar << " "
926  << n_points * n_vector << " "
927  << n_points * n_tensor << " "
928  << n_lines * n_scalar << " "
929  << n_lines * n_vector << " "
930  << n_lines * n_tensor << " "
931  << n_triangles * n_scalar << " "
932  << n_triangles * n_vector << " "
933  << n_triangles * n_tensor << " "
934  << n_quadrangles * n_scalar << " "
935  << n_quadrangles * n_vector << " "
936  << n_quadrangles * n_tensor << " "
937  << n_tetrahedra * n_scalar << " "
938  << n_tetrahedra * n_vector << " "
939  << n_tetrahedra * n_tensor << " "
940  << n_hexahedra * n_scalar << " "
941  << n_hexahedra * n_vector << " "
942  << n_hexahedra * n_tensor << " "
943  << n_prisms * n_scalar << " "
944  << n_prisms * n_vector << " "
945  << n_prisms * n_tensor << " "
946  << n_pyramids * n_scalar << " "
947  << n_pyramids * n_vector << " "
948  << n_pyramids * n_tensor << " "
949  << nb_text2d << " "
950  << nb_text2d_chars << " "
951  << nb_text3d << " "
952  << nb_text3d_chars << "\n";
953 
954  // if binary, write a marker to identify the endianness of the file
955  if (this->binary())
956  {
957  const int one = 1;
958  std::memcpy(buf, &one, sizeof(int));
959  out_stream.write(buf, sizeof(int));
960  }
961 
962  // the time steps (there is just 1 at the moment)
963  if (this->binary())
964  {
965  double one = 1;
966  std::memcpy(buf, &one, sizeof(double));
967  out_stream.write(buf, sizeof(double));
968  }
969  else
970  out_stream << "1\n";
971 
972  // Loop over the elements and write out the data
973  for (const auto & elem : mesh.active_element_ptr_range())
974  {
975  // this is quite crappy, but I did not invent that file format!
976  for (unsigned int d=0; d<3; d++) // loop over the dimensions
977  {
978  for (unsigned int n=0; n < elem->n_vertices(); n++) // loop over vertices
979  {
980  const Point & vertex = elem->point(n);
981  if (this->binary())
982  {
983  double tmp = vertex(d);
984  std::memcpy(buf, &tmp, sizeof(double));
985  out_stream.write(reinterpret_cast<char *>(buf), sizeof(double));
986  }
987  else
988  out_stream << vertex(d) << " ";
989  }
990  if (!this->binary())
991  out_stream << "\n";
992  }
993 
994  // now finally write out the data
995  for (unsigned int i=0; i < elem->n_vertices(); i++) // loop over vertices
996  if (this->binary())
997  {
998 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
999  libMesh::out << "WARNING: Gmsh::write_post does not fully support "
1000  << "complex numbers. Will only write the real part of "
1001  << "variable " << varname << std::endl;
1002 #endif
1003  double tmp = libmesh_real((*v)[elem->node_id(i)*n_vars + ivar]);
1004  std::memcpy(buf, &tmp, sizeof(double));
1005  out_stream.write(reinterpret_cast<char *>(buf), sizeof(double));
1006  }
1007  else
1008  {
1009 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
1010  libMesh::out << "WARNING: Gmsh::write_post does not fully support "
1011  << "complex numbers. Will only write the real part of "
1012  << "variable " << varname << std::endl;
1013 #endif
1014  out_stream << libmesh_real((*v)[elem->node_id(i)*n_vars + ivar]) << "\n";
1015  }
1016  }
1017  if (this->binary())
1018  out_stream << "\n";
1019  out_stream << "$EndView\n";
1020 
1021  } // end variable loop (writing the views)
1022  }
1023 }
1024 
1025 } // namespace libMesh
T libmesh_real(T a)
std::string name(const ElemQuality q)
Definition: elem_quality.C:42
const MT & mesh() const
Definition: mesh_output.h:234
std::size_t n_boundary_conds() const
virtual void reserve_nodes(const dof_id_type nn)=0
virtual void write(const std::string &name) override
Definition: gmsh_io.C:612
virtual dof_id_type n_active_elem() const =0
virtual Node *& set_node(const unsigned int i)
Definition: elem.h:2024
bool & write_lower_dimensional_elements()
Definition: gmsh_io.C:137
unsigned short int side
Definition: xdr_io.C:50
The base class for all geometric element types.
Definition: elem.h:100
MeshBase & mesh
virtual void write_nodal_data(const std::string &, const std::vector< Number > &, const std::vector< std::string > &) override
Definition: gmsh_io.C:629
virtual SimpleRange< element_iterator > active_element_ptr_range()=0
bool & binary()
Definition: gmsh_io.C:130
const unsigned int n_vars
Definition: tecplot_io.C:69
const BoundaryInfo & get_boundary_info() const
Definition: mesh_base.h:131
std::map< unsigned int, ElementDefinition > in
Definition: gmsh_io.h:186
long double max(long double a, double b)
virtual Node * add_point(const Point &p, const dof_id_type id=DofObject::invalid_id, const processor_id_type proc_id=DofObject::invalid_processor_id)=0
Base class for Mesh.
Definition: mesh_base.h:77
dof_id_type & set_id()
Definition: dof_object.h:664
void build_side_list(std::vector< dof_id_type > &element_id_list, std::vector< unsigned short int > &side_list, std::vector< boundary_id_type > &bc_id_list) const
virtual std::unique_ptr< Elem > build_side_ptr(const unsigned int i, bool proxy=true)=0
std::map< ElemType, ElementDefinition > out
Definition: gmsh_io.h:185
void add_node(const Node *node, const boundary_id_type id)
int8_t boundary_id_type
Definition: id_types.h:51
virtual void delete_elem(Elem *e)=0
dof_id_type id() const
Definition: dof_object.h:655
virtual unsigned int n_nodes() const =0
virtual Elem * add_elem(Elem *e)=0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:245
virtual dof_id_type max_elem_id() const =0
SimpleRange< I > as_range(const std::pair< I, I > &p)
Definition: simple_range.h:57
void write_mesh(std::ostream &out)
Definition: gmsh_io.C:641
std::string & subdomain_name(subdomain_id_type id)
Definition: mesh_base.C:538
OStreamProxy err(std::cerr)
void set_mesh_dimension(unsigned char d)
Definition: mesh_base.h:213
bool _write_lower_dimensional_elements
Definition: gmsh_io.h:148
virtual void clear()
Definition: mesh_base.C:260
std::string & sideset_name(boundary_id_type id)
static ElementMaps build_element_maps()
Definition: gmsh_io.C:42
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
Definition: elem.h:2034
void swap(Iterator &lhs, Iterator &rhs)
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
virtual const Elem & elem_ref(const dof_id_type i) const
Definition: mesh_base.h:504
static ElementMaps _element_maps
Definition: gmsh_io.h:193
virtual const Node & node_ref(const dof_id_type i) const
Definition: mesh_base.h:434
GmshIO(MeshBase &mesh)
Definition: gmsh_io.C:120
void add_def(const ElementDefinition &eledef)
Definition: gmsh_io.h:179
virtual void read(const std::string &name) override
Definition: gmsh_io.C:144
virtual const Node * node_ptr(const dof_id_type i) const =0
OStreamProxy out(std::cout)
void write_post(const std::string &, const std::vector< Number > *=nullptr, const std::vector< std::string > *=nullptr)
Definition: gmsh_io.C:803
processor_id_type processor_id() const
Definition: dof_object.h:717
long double min(long double a, double b)
A geometric point in (x,y,z) space.
Definition: point.h:38
virtual void reserve_elem(const dof_id_type ne)=0
virtual dof_id_type n_nodes() const =0
void read_mesh(std::istream &in)
Definition: gmsh_io.C:152
std::vector< unsigned int > nodes
Definition: gmsh_io.h:169