libMesh
unv_io.C
Go to the documentation of this file.
1 // The libMesh Finite Element Library.
2 // Copyright (C) 2002-2025 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 
19 // Local includes
20 #include "libmesh/libmesh_config.h"
21 #include "libmesh/libmesh_logging.h"
22 #include "libmesh/unv_io.h"
23 #include "libmesh/mesh_base.h"
24 #include "libmesh/edge_edge2.h"
25 #include "libmesh/face_quad4.h"
26 #include "libmesh/face_tri3.h"
27 #include "libmesh/face_tri6.h"
28 #include "libmesh/face_quad8.h"
29 #include "libmesh/face_quad9.h"
30 #include "libmesh/cell_tet4.h"
31 #include "libmesh/cell_hex8.h"
32 #include "libmesh/cell_hex20.h"
33 #include "libmesh/cell_tet10.h"
34 #include "libmesh/cell_prism6.h"
35 #include "libmesh/utility.h"
36 #include "libmesh/boundary_info.h"
37 #include "libmesh/enum_to_string.h"
38 
39 // C++ includes
40 #include <array>
41 #include <iomanip>
42 #include <algorithm> // for std::sort
43 #include <fstream>
44 #include <cctype> // isspace
45 #include <sstream> // std::istringstream
46 #include <unordered_map>
47 
48 #ifdef LIBMESH_HAVE_GZSTREAM
49 # include "gzstream.h" // For reading/writing compressed streams
50 #endif
51 
52 
53 
54 namespace libMesh
55 {
56 
57 //-----------------------------------------------------------------------------
58 // UNVIO class static members
59 const std::string UNVIO::_nodes_dataset_label = "2411";
60 const std::string UNVIO::_elements_dataset_label = "2412";
61 const std::string UNVIO::_groups_dataset_label = "2467";
62 
63 
64 
65 // ------------------------------------------------------------
66 // UNVIO class members
67 
71  _verbose (false)
72 {
73 }
74 
75 
76 
79  _verbose (false)
80 {
81 }
82 
83 
84 
85 UNVIO::~UNVIO () = default;
86 
87 
88 
89 bool & UNVIO::verbose ()
90 {
91  return _verbose;
92 }
93 
94 
95 
96 void UNVIO::read (const std::string & file_name)
97 {
98  if (file_name.rfind(".gz") < file_name.size())
99  {
100 #ifdef LIBMESH_HAVE_GZSTREAM
101 
102  igzstream in_stream (file_name.c_str());
103  this->read_implementation (in_stream);
104 
105 #else
106 
107  libmesh_error_msg("ERROR: You must have the zlib.h header files and libraries to read and write compressed streams.");
108 
109 #endif
110  return;
111  }
112 
113  else
114  {
115  std::ifstream in_stream (file_name.c_str());
116  this->read_implementation (in_stream);
117  return;
118  }
119 }
120 
121 
122 void UNVIO::read_implementation (std::istream & in_stream)
123 {
124  // Keep track of what kinds of elements this file contains
125  elems_of_dimension.clear();
126  elems_of_dimension.resize(4, false);
127 
128  {
129  libmesh_error_msg_if(!in_stream.good(), "ERROR: Input file not good.");
130 
131  // Flags to be set when certain sections are encountered
132  bool
133  found_node = false,
134  found_elem = false,
135  found_group = false;
136 
137  // strings for reading the file line by line
138  std::string
139  old_line,
140  current_line;
141 
142  while (true)
143  {
144  // Save off the old_line. This will provide extra reliability
145  // for detecting the beginnings of the different sections.
146  old_line = current_line;
147 
148  // Try to read something. This may set EOF!
149  std::getline(in_stream, current_line);
150 
151  // If the stream is still "valid", parse the line
152  if (in_stream)
153  {
154  // UNV files always have some amount of leading
155  // whitespace, let's not rely on exactly how much... This
156  // command deletes it.
157  current_line.erase(std::remove_if(current_line.begin(), current_line.end(),
158  [](unsigned char const c){return std::isspace(c);}),
159  current_line.end());
160 
161  // Parse the nodes section
162  if (current_line == _nodes_dataset_label &&
163  old_line == "-1")
164  {
165  found_node = true;
166  this->nodes_in(in_stream);
167  }
168 
169  // Parse the elements section
170  else if (current_line == _elements_dataset_label &&
171  old_line == "-1")
172  {
173  // The current implementation requires the nodes to
174  // have been read before reaching the elements
175  // section.
176  libmesh_error_msg_if(!found_node,
177  "ERROR: The Nodes section must come before the Elements section of the UNV file!");
178 
179  found_elem = true;
180  this->elements_in(in_stream);
181  }
182 
183  // Parse the groups section
184  else if (current_line == _groups_dataset_label &&
185  old_line == "-1")
186  {
187  // The current implementation requires the nodes and
188  // elements to have already been read before reaching
189  // the groups section.
190  libmesh_error_msg_if(!found_node || !found_elem,
191  "ERROR: The Nodes and Elements sections must come before the Groups section of the UNV file!");
192 
193  found_group = true;
194  this->groups_in(in_stream);
195  }
196 
197  // We can stop reading once we've found the nodes, elements,
198  // and group sections.
199  if (found_node && found_elem && found_group)
200  break;
201 
202  // If we made it here, we're not done yet, so keep reading
203  continue;
204  }
205 
206  // if (!in_stream) check to see if EOF was set. If so, break out of while loop.
207  if (in_stream.eof())
208  break;
209 
210  // If !in_stream and !in_stream.eof(), stream is in a bad state!
211  libmesh_error_msg("Stream is bad! Perhaps the file does not exist?");
212  } // end while (true)
213 
214  // By now we better have found the datasets for nodes and elements,
215  // otherwise the unv file is bad!
216  libmesh_error_msg_if(!found_node, "ERROR: Could not find nodes!");
217  libmesh_error_msg_if(!found_elem, "ERROR: Could not find elements!");
218  }
219 
220 
221 
222  {
223  // Set the mesh dimension to the largest element dimension encountered
224  MeshInput<MeshBase>::mesh().set_mesh_dimension(max_elem_dimension_seen());
225 
226 #if LIBMESH_DIM < 3
227  libmesh_error_msg_if(MeshInput<MeshBase>::mesh().mesh_dimension() > LIBMESH_DIM,
228  "Cannot open dimension "
229  << MeshInput<MeshBase>::mesh().mesh_dimension()
230  << " mesh file when configured without "
231  << MeshInput<MeshBase>::mesh().mesh_dimension()
232  << "D support." );
233 #endif
234 
235  // Delete any lower-dimensional elements that might have been
236  // added to the mesh strictly for setting BCs.
237  {
238  // Grab reference to the Mesh
240 
241  unsigned char max_dim = this->max_elem_dimension_seen();
242 
243  for (const auto & elem : mesh.element_ptr_range())
244  if (elem->dim() < max_dim)
245  mesh.delete_elem(elem);
246  }
247 
248  if (this->verbose())
249  libMesh::out << " Finished." << std::endl << std::endl;
250  }
251 }
252 
253 
254 
255 
256 
257 void UNVIO::write (const std::string & file_name)
258 {
259  if (file_name.rfind(".gz") < file_name.size())
260  {
261 #ifdef LIBMESH_HAVE_GZSTREAM
262 
263  ogzstream out_stream(file_name.c_str());
264  this->write_implementation (out_stream);
265 
266 #else
267 
268  libmesh_error_msg("ERROR: You must have the zlib.h header files and libraries to read and write compressed streams.");
269 
270 #endif
271 
272  return;
273  }
274 
275  else
276  {
277  std::ofstream out_stream (file_name.c_str());
278  this->write_implementation (out_stream);
279  return;
280  }
281 }
282 
283 
284 
285 
286 void UNVIO::write_implementation (std::ostream & out_file)
287 {
288  libmesh_error_msg_if(!out_file.good(), "ERROR: Output file not good.");
289 
290  // write the nodes, then the elements
291  this->nodes_out (out_file);
292  this->elements_out (out_file);
293 }
294 
295 
296 
297 
298 void UNVIO::nodes_in (std::istream & in_file)
299 {
300  LOG_SCOPE("nodes_in()","UNVIO");
301 
302  if (this->verbose())
303  libMesh::out << " Reading nodes" << std::endl;
304 
306 
307  // node label, we use an int here so we can read in a -1
308  int node_label;
309 
310  // We'll just read the floating point values as strings even when
311  // there are no "D" characters in the file. This will make parsing
312  // the file a bit slower but the logic to do so will all be
313  // simplified and in one place...
314  std::string line;
315  std::istringstream coords_stream;
316 
317  // Continue reading nodes until there are none left
318  unsigned ctr = 0;
319  while (true)
320  {
321  // Read the node label
322  in_file >> node_label;
323 
324  // Break out of the while loop when we hit -1
325  if (node_label == -1)
326  break;
327 
328  // Read and discard the the rest of the node data on this line
329  // which we do not currently use:
330  // .) exp_coord_sys_num
331  // .) disp_coord_sys_num
332  // .) color
333  std::getline(in_file, line);
334 
335  // read the floating-point data
336  std::getline(in_file, line);
337 
338  // Replace any "D" characters used for exponents with "E"
339  size_t last_pos = 0;
340  while (true)
341  {
342  last_pos = line.find("D", last_pos);
343 
344  if (last_pos != std::string::npos)
345  line.replace(last_pos, 1, "E");
346  else
347  break;
348  }
349 
350  // Construct a stream object from the current line and then
351  // stream in the xyz values.
352  coords_stream.str(line);
353  coords_stream.clear(); // clear iostate bits! Important!
354 
355  // always 3 coordinates in the UNV file, no matter
356  // what LIBMESH_DIM is.
357  std::array<Real, 3> xyz;
358 
359  coords_stream >> xyz[0] >> xyz[1] >> xyz[2];
360 
361  Point p(xyz[0]);
362 #if LIBMESH_DIM > 1
363  p(1) = xyz[1];
364 #else
365  libmesh_assert_equal_to(xyz[1], 0);
366 #endif
367 #if LIBMESH_DIM > 2
368  p(2) = xyz[2];
369 #else
370  libmesh_assert_equal_to(xyz[2], 0);
371 #endif
372 
373  // Add node to the Mesh, bump the counter.
374  Node * added_node = mesh.add_point(p, ctr++);
375 
376  // Maintain the mapping between UNV node ids and libmesh Node
377  // pointers.
378  _unv_node_id_to_libmesh_node_ptr[node_label] = added_node;
379  }
380 }
381 
382 
383 
385 {
386  unsigned char max_dim = 0;
387 
388  unsigned char elem_dimensions_size = cast_int<unsigned char>
389  (elems_of_dimension.size());
390  // The elems_of_dimension array is 1-based in the UNV reader
391  for (unsigned char i=1; i<elem_dimensions_size; ++i)
392  if (elems_of_dimension[i])
393  max_dim = i;
394 
395  return max_dim;
396 }
397 
398 
399 
400 bool UNVIO::need_D_to_e (std::string & number)
401 {
402  // find "D" in string, start looking at 6th element since dont expect a "D" earlier.
403  std::string::size_type position = number.find("D", 6);
404 
405  if (position != std::string::npos)
406  {
407  // replace "D" in string
408  number.replace(position, 1, "e");
409  return true;
410  }
411  else
412  return false;
413 }
414 
415 
416 
417 void UNVIO::groups_in (std::istream & in_file)
418 {
419  // Grab reference to the Mesh, so we can add boundary info data to it
421 
422  // Record the max and min element dimension seen while reading the file.
423  unsigned char max_dim = this->max_elem_dimension_seen();
424 
425  // Container which stores lower-dimensional elements (based on
426  // Elem::key()) for later assignment of boundary conditions. We
427  // could use e.g. an unordered_set with Elems as keys for this, but
428  // this turns out to be much slower on the search side, since we
429  // have to build an entire side in order to search, rather than just
430  // calling elem->key(side) to compute a key.
431  typedef std::unordered_multimap<dof_id_type, Elem *> map_type;
432  map_type provide_bcs;
433 
434  // Read groups until there aren't any more to read...
435  while (true)
436  {
437  // If we read a -1, it means there is nothing else to read in this section.
438  int group_number;
439  in_file >> group_number;
440 
441  if (group_number == -1)
442  break;
443 
444  // The first record consists of 8 entries:
445  // Field 1 -- group number (that we just read)
446  // Field 2 -- active constraint set no. for group
447  // Field 3 -- active restraint set no. for group
448  // Field 4 -- active load set no. for group
449  // Field 5 -- active dof set no. for group
450  // Field 6 -- active temperature set no. for group
451  // Field 7 -- active contact set no. for group
452  // Field 8 -- number of entities in group
453  // Only the first and last of these are relevant to us...
454  unsigned num_entities;
455  std::string group_name;
456  {
457  unsigned dummy;
458  in_file >> dummy >> dummy >> dummy >> dummy >> dummy >> dummy
459  >> num_entities;
460 
461  // The second record has 1 field, the group name
462  in_file >> group_name;
463  }
464 
465  // The dimension of the elements in the group will determine
466  // whether this is a sideset group or a subdomain group.
467  bool
468  is_subdomain_group = false,
469  is_sideset_group = false;
470 
471  // Each subsequent line has 8 entries, there are two entity type
472  // codes and two tags per line that we need to read. In all my
473  // examples, the entity type code was always "8", which stands for
474  // "finite elements" but it's possible that we could eventually
475  // support other entity type codes...
476  // Field 1 -- entity type code
477  // Field 2 -- entity tag
478  // Field 3 -- entity node leaf id.
479  // Field 4 -- entity component/ ham id.
480  // Field 5 -- entity type code
481  // Field 6 -- entity tag
482  // Field 7 -- entity node leaf id.
483  // Field 8 -- entity component/ ham id.
484  {
485  unsigned entity_type_code, entity_tag, dummy;
486  for (unsigned entity=0; entity<num_entities; ++entity)
487  {
488  in_file >> entity_type_code >> entity_tag >> dummy >> dummy;
489 
490  if (entity_type_code != 8)
491  libMesh::err << "Warning, unrecognized entity type code = "
492  << entity_type_code
493  << " in group "
494  << group_name
495  << std::endl;
496 
497  // Try to find this ID in the map from UNV element ids to libmesh ids.
498  if (const auto it = _unv_elem_id_to_libmesh_elem_id.find(entity_tag);
500  {
501  unsigned libmesh_elem_id = it->second;
502 
503  // Attempt to get a pointer to the elem listed in the group
504  Elem * group_elem = mesh.elem_ptr(libmesh_elem_id);
505 
506  // dim < max_dim means the Elem defines a boundary condition
507  if (group_elem->dim() < max_dim)
508  {
509  is_sideset_group = true;
510 
511  // We can only handle elements that are *exactly*
512  // one dimension lower than the max element
513  // dimension. Not sure if "edge" BCs in 3D
514  // actually make sense/are required...
515  libmesh_error_msg_if(group_elem->dim()+1 != max_dim,
516  "ERROR: Expected boundary element of dimension "
517  << max_dim-1 << " but got " << group_elem->dim());
518 
519  // Set the current group number as the lower-dimensional element's subdomain ID.
520  // We will use this later to set a boundary ID.
521  group_elem->subdomain_id() =
522  cast_int<subdomain_id_type>(group_number);
523 
524  // Store the lower-dimensional element in the provide_bcs container.
525  provide_bcs.emplace(group_elem->key(), group_elem);
526  }
527 
528  // dim == max_dim means this group defines a subdomain ID
529  else if (group_elem->dim() == max_dim)
530  {
531  is_subdomain_group = true;
532  group_elem->subdomain_id() =
533  cast_int<subdomain_id_type>(group_number);
534  }
535 
536  else
537  libmesh_error_msg("ERROR: Found an elem with dim=" \
538  << group_elem->dim() << " > " << "max_dim=" << +max_dim);
539  }
540  else
541  libMesh::err << "WARNING: UNV Element " << entity_tag << " not found while parsing groups." << std::endl;
542  } // end for (entity)
543  } // end scope
544 
545  // Associate this group_number with the group_name in the BoundaryInfo object.
546  if (is_sideset_group)
548  (cast_int<boundary_id_type>(group_number)) = group_name;
549 
550  if (is_subdomain_group)
552  (cast_int<subdomain_id_type>(group_number)) = group_name;
553 
554  } // end while (true)
555 
556  // Loop over elements and try to assign boundary information
557  for (auto & elem : mesh.active_element_ptr_range())
558  if (elem->dim() == max_dim)
559  for (auto sn : elem->side_index_range())
560  for (const auto & pr : as_range(provide_bcs.equal_range (elem->key(sn))))
561  {
562  // This is a max-dimension element that may require BCs.
563  // For each of its sides, including internal sides, we'll
564  // see if a lower-dimensional element provides boundary
565  // information for it. Note that we have not yet called
566  // find_neighbors(), so we can't use elem->neighbor(sn) in
567  // this algorithm...
568 
569  // Build a side to confirm the hash mapped to the correct side.
570  std::unique_ptr<Elem> side (elem->build_side_ptr(sn));
571 
572  // Get a pointer to the lower-dimensional element
573  Elem * lower_dim_elem = pr.second;
574 
575  // This was a hash, so it might not be perfect. Let's verify...
576  if (*lower_dim_elem == *side)
577  mesh.get_boundary_info().add_side(elem, sn, lower_dim_elem->subdomain_id());
578  }
579 }
580 
581 
582 
583 void UNVIO::elements_in (std::istream & in_file)
584 {
585  LOG_SCOPE("elements_in()","UNVIO");
586 
587  if (this->verbose())
588  libMesh::out << " Reading elements" << std::endl;
589 
591 
592  // node label, we use an int here so we can read in a -1
593  int element_label;
594 
595  unsigned
596  n_nodes, // number of nodes on element
597  fe_descriptor_id, // FE descriptor id
598  phys_prop_tab_num, // physical property table number (not supported yet)
599  mat_prop_tab_num, // material property table number (not supported yet)
600  color; // color (not supported yet)
601 
602  // vector that temporarily holds the node labels defining element
603  std::vector<unsigned int> node_labels (21);
604 
605  // vector that assigns element nodes to their correct position
606  // for example:
607  // 44:plane stress | QUAD4
608  // linear quadrilateral |
609  // position in UNV-file | position in libmesh
610  // assign_elem_node[1] = 0
611  // assign_elem_node[2] = 3
612  // assign_elem_node[3] = 2
613  // assign_elem_node[4] = 1
614  //
615  // UNV is 1-based, we leave the 0th element of the vectors unused in order
616  // to prevent confusion, this way we can store elements with up to 20 nodes
617  unsigned int assign_elem_nodes[21];
618 
619  // Read elements until there are none left
620  unsigned ctr = 0;
621  while (true)
622  {
623  // read element label, break out when we read -1
624  in_file >> element_label;
625 
626  if (element_label == -1)
627  break;
628 
629  in_file >> fe_descriptor_id // read FE descriptor id
630  >> phys_prop_tab_num // (not supported yet)
631  >> mat_prop_tab_num // (not supported yet)
632  >> color // (not supported yet)
633  >> n_nodes; // read number of nodes on element
634 
635  // For "beam" type elements, the next three numbers are:
636  // .) beam orientation node number
637  // .) beam fore-end cross section number
638  // .) beam aft-end cross section number
639  // which we discard in libmesh. The "beam" type elements:
640  // 11 Rod
641  // 21 Linear beam
642  // 22 Tapered beam
643  // 23 Curved beam
644  // 24 Parabolic beam
645  // all have fe_descriptor_id < 25.
646  // http://www.sdrl.uc.edu/universal-file-formats-for-modal-analysis-testing-1/file-format-storehouse/unv_2412.htm
647  if (fe_descriptor_id < 25)
648  {
649  unsigned dummy;
650  in_file >> dummy >> dummy >> dummy;
651  }
652 
653  // read node labels (1-based)
654  for (unsigned int j=1; j<=n_nodes; j++)
655  in_file >> node_labels[j];
656 
657  // element pointer, to be allocated
658  std::unique_ptr<Elem> elem;
659 
660  switch (fe_descriptor_id)
661  {
662  case 11: // Rod
663  {
664  elem = Elem::build(EDGE2);
665 
666  assign_elem_nodes[1]=0;
667  assign_elem_nodes[2]=1;
668  break;
669  }
670 
671  case 41: // Plane Stress Linear Triangle
672  case 91: // Thin Shell Linear Triangle
673  {
674  elem = Elem::build(TRI3); // create new element
675 
676  assign_elem_nodes[1]=0;
677  assign_elem_nodes[2]=2;
678  assign_elem_nodes[3]=1;
679  break;
680  }
681 
682  case 42: // Plane Stress Quadratic Triangle
683  case 92: // Thin Shell Quadratic Triangle
684  {
685  elem = Elem::build(TRI6); // create new element
686 
687  assign_elem_nodes[1]=0;
688  assign_elem_nodes[2]=5;
689  assign_elem_nodes[3]=2;
690  assign_elem_nodes[4]=4;
691  assign_elem_nodes[5]=1;
692  assign_elem_nodes[6]=3;
693  break;
694  }
695 
696  case 43: // Plane Stress Cubic Triangle
697  libmesh_error_msg("ERROR: UNV-element type 43: Plane Stress Cubic Triangle not supported.");
698 
699  case 44: // Plane Stress Linear Quadrilateral
700  case 94: // Thin Shell Linear Quadrilateral
701  {
702  elem = Elem::build(QUAD4); // create new element
703 
704  assign_elem_nodes[1]=0;
705  assign_elem_nodes[2]=3;
706  assign_elem_nodes[3]=2;
707  assign_elem_nodes[4]=1;
708  break;
709  }
710 
711  case 45: // Plane Stress Quadratic Quadrilateral
712  case 95: // Thin Shell Quadratic Quadrilateral
713  {
714  elem = Elem::build(QUAD8); // create new element
715 
716  assign_elem_nodes[1]=0;
717  assign_elem_nodes[2]=7;
718  assign_elem_nodes[3]=3;
719  assign_elem_nodes[4]=6;
720  assign_elem_nodes[5]=2;
721  assign_elem_nodes[6]=5;
722  assign_elem_nodes[7]=1;
723  assign_elem_nodes[8]=4;
724  break;
725  }
726 
727  case 300: // Thin Shell Quadratic Quadrilateral (nine nodes)
728  {
729  elem = Elem::build(QUAD9); // create new element
730 
731  assign_elem_nodes[1]=0;
732  assign_elem_nodes[2]=7;
733  assign_elem_nodes[3]=3;
734  assign_elem_nodes[4]=6;
735  assign_elem_nodes[5]=2;
736  assign_elem_nodes[6]=5;
737  assign_elem_nodes[7]=1;
738  assign_elem_nodes[8]=4;
739  assign_elem_nodes[9]=8;
740  break;
741  }
742 
743  case 46: // Plane Stress Cubic Quadrilateral
744  libmesh_error_msg("ERROR: UNV-element type 46: Plane Stress Cubic Quadrilateral not supported.");
745 
746  case 111: // Solid Linear Tetrahedron
747  {
748  elem = Elem::build(TET4); // create new element
749 
750  assign_elem_nodes[1]=0;
751  assign_elem_nodes[2]=1;
752  assign_elem_nodes[3]=2;
753  assign_elem_nodes[4]=3;
754  break;
755  }
756 
757  case 112: // Solid Linear Prism
758  {
759  elem = Elem::build(PRISM6); // create new element
760 
761  assign_elem_nodes[1]=0;
762  assign_elem_nodes[2]=1;
763  assign_elem_nodes[3]=2;
764  assign_elem_nodes[4]=3;
765  assign_elem_nodes[5]=4;
766  assign_elem_nodes[6]=5;
767  break;
768  }
769 
770  case 115: // Solid Linear Brick
771  {
772  elem = Elem::build(HEX8); // create new element
773 
774  assign_elem_nodes[1]=0;
775  assign_elem_nodes[2]=4;
776  assign_elem_nodes[3]=5;
777  assign_elem_nodes[4]=1;
778  assign_elem_nodes[5]=3;
779  assign_elem_nodes[6]=7;
780  assign_elem_nodes[7]=6;
781  assign_elem_nodes[8]=2;
782  break;
783  }
784 
785  case 116: // Solid Quadratic Brick
786  {
787  elem = Elem::build(HEX20); // create new element
788 
789  assign_elem_nodes[1]=0;
790  assign_elem_nodes[2]=12;
791  assign_elem_nodes[3]=4;
792  assign_elem_nodes[4]=16;
793  assign_elem_nodes[5]=5;
794  assign_elem_nodes[6]=13;
795  assign_elem_nodes[7]=1;
796  assign_elem_nodes[8]=8;
797 
798  assign_elem_nodes[9]=11;
799  assign_elem_nodes[10]=19;
800  assign_elem_nodes[11]=17;
801  assign_elem_nodes[12]=9;
802 
803  assign_elem_nodes[13]=3;
804  assign_elem_nodes[14]=15;
805  assign_elem_nodes[15]=7;
806  assign_elem_nodes[16]=18;
807  assign_elem_nodes[17]=6;
808  assign_elem_nodes[18]=14;
809  assign_elem_nodes[19]=2;
810  assign_elem_nodes[20]=10;
811  break;
812  }
813 
814  case 117: // Solid Cubic Brick
815  libmesh_error_msg("Error: UNV-element type 117: Solid Cubic Brick not supported.");
816 
817  case 118: // Solid Quadratic Tetrahedron
818  {
819  elem = Elem::build(TET10); // create new element
820 
821  assign_elem_nodes[1]=0;
822  assign_elem_nodes[2]=4;
823  assign_elem_nodes[3]=1;
824  assign_elem_nodes[4]=5;
825  assign_elem_nodes[5]=2;
826  assign_elem_nodes[6]=6;
827  assign_elem_nodes[7]=7;
828  assign_elem_nodes[8]=8;
829  assign_elem_nodes[9]=9;
830  assign_elem_nodes[10]=3;
831  break;
832  }
833 
834  default: // Unrecognized element type
835  libmesh_error_msg("ERROR: UNV-element type " << fe_descriptor_id << " not supported.");
836  }
837 
838  // nodes are being stored in element
839  for (dof_id_type j=1; j<=n_nodes; j++)
840  {
841  // Map the UNV node ID to the libmesh node ID
842  auto & node_ptr =
843  libmesh_map_find(_unv_node_id_to_libmesh_node_ptr, node_labels[j]);
844 
845  elem->set_node(assign_elem_nodes[j], node_ptr);
846  }
847 
848  elems_of_dimension[elem->dim()] = true;
849 
850  // Set the element's ID
851  elem->set_id(ctr);
852 
853  // Maintain a map from the libmesh (0-based) numbering to the
854  // UNV numbering.
855  _unv_elem_id_to_libmesh_elem_id[element_label] = ctr;
856 
857  // Add the element to the Mesh
858  mesh.add_elem(std::move(elem));
859 
860  // Increment the counter for the next iteration
861  ctr++;
862  } // end while (true)
863 }
864 
865 
866 
867 
868 
869 
870 void UNVIO::nodes_out (std::ostream & out_file)
871 {
872  if (this->verbose())
873  libMesh::out << " Writing " << MeshOutput<MeshBase>::mesh().n_nodes() << " nodes" << std::endl;
874 
875  // Write beginning of dataset
876  out_file << " -1\n"
877  << " "
879  << '\n';
880 
881  unsigned int
882  exp_coord_sys_dummy = 0, // export coordinate sys. (not supported yet)
883  disp_coord_sys_dummy = 0, // displacement coordinate sys. (not supp. yet)
884  color_dummy = 0; // color(not supported yet)
885 
886  // A reference to the parent class's mesh
888 
889  // Use scientific notation with capital E and default 17 digits for printing out the coordinates
890  out_file << std::scientific << std::setprecision(this->ascii_precision()) << std::uppercase;
891 
892  for (const auto & current_node : mesh.node_ptr_range())
893  {
894  dof_id_type node_id = current_node->id();
895 
896  out_file << std::setw(10) << node_id
897  << std::setw(10) << exp_coord_sys_dummy
898  << std::setw(10) << disp_coord_sys_dummy
899  << std::setw(10) << color_dummy
900  << '\n';
901 
902  // The coordinates - always write out three coords regardless of LIBMESH_DIM
903  Real x = (*current_node)(0);
904 
905 #if LIBMESH_DIM > 1
906  Real y = (*current_node)(1);
907 #else
908  Real y = 0.;
909 #endif
910 
911 #if LIBMESH_DIM > 2
912  Real z = (*current_node)(2);
913 #else
914  Real z = 0.;
915 #endif
916 
917  out_file << std::setw(25) << x
918  << std::setw(25) << y
919  << std::setw(25) << z
920  << '\n';
921  }
922 
923  // Write end of dataset
924  out_file << " -1\n";
925 }
926 
927 
928 
929 
930 
931 
932 void UNVIO::elements_out(std::ostream & out_file)
933 {
934  if (this->verbose())
935  libMesh::out << " Writing elements" << std::endl;
936 
937  // Write beginning of dataset
938  out_file << " -1\n"
939  << " "
941  << "\n";
942 
943  unsigned
944  fe_descriptor_id = 0, // FE descriptor id
945  phys_prop_tab_dummy = 2, // physical property (not supported yet)
946  mat_prop_tab_dummy = 1, // material property (not supported yet)
947  color_dummy = 7; // color (not supported yet)
948 
949  // vector that assigns element nodes to their correct position
950  // currently only elements with up to 20 nodes
951  //
952  // Example:
953  // QUAD4 | 44:plane stress
954  // | linear quad
955  // position in libMesh | UNV numbering
956  // (note: 0-based) | (note: 1-based)
957  //
958  // assign_elem_node[0] = 0
959  // assign_elem_node[1] = 3
960  // assign_elem_node[2] = 2
961  // assign_elem_node[3] = 1
962  unsigned int assign_elem_nodes[20];
963 
964  unsigned int n_elem_written=0;
965 
966  // A reference to the parent class's mesh
968 
969  for (const auto & elem : mesh.element_ptr_range())
970  {
971  switch (elem->type())
972  {
973 
974  case TRI3:
975  {
976  fe_descriptor_id = 41; // Plane Stress Linear Triangle
977  assign_elem_nodes[0] = 0;
978  assign_elem_nodes[1] = 2;
979  assign_elem_nodes[2] = 1;
980  break;
981  }
982 
983  case TRI6:
984  {
985  fe_descriptor_id = 42; // Plane Stress Quadratic Triangle
986  assign_elem_nodes[0] = 0;
987  assign_elem_nodes[1] = 5;
988  assign_elem_nodes[2] = 2;
989  assign_elem_nodes[3] = 4;
990  assign_elem_nodes[4] = 1;
991  assign_elem_nodes[5] = 3;
992  break;
993  }
994 
995  case QUAD4:
996  {
997  fe_descriptor_id = 44; // Plane Stress Linear Quadrilateral
998  assign_elem_nodes[0] = 0;
999  assign_elem_nodes[1] = 3;
1000  assign_elem_nodes[2] = 2;
1001  assign_elem_nodes[3] = 1;
1002  break;
1003  }
1004 
1005  case QUAD8:
1006  {
1007  fe_descriptor_id = 45; // Plane Stress Quadratic Quadrilateral
1008  assign_elem_nodes[0] = 0;
1009  assign_elem_nodes[1] = 7;
1010  assign_elem_nodes[2] = 3;
1011  assign_elem_nodes[3] = 6;
1012  assign_elem_nodes[4] = 2;
1013  assign_elem_nodes[5] = 5;
1014  assign_elem_nodes[6] = 1;
1015  assign_elem_nodes[7] = 4;
1016  break;
1017  }
1018 
1019  case QUAD9:
1020  {
1021  fe_descriptor_id = 300; // Plane Stress Quadratic Quadrilateral
1022  assign_elem_nodes[0] = 0;
1023  assign_elem_nodes[1] = 7;
1024  assign_elem_nodes[2] = 3;
1025  assign_elem_nodes[3] = 6;
1026  assign_elem_nodes[4] = 2;
1027  assign_elem_nodes[5] = 5;
1028  assign_elem_nodes[6] = 1;
1029  assign_elem_nodes[7] = 4;
1030  assign_elem_nodes[8] = 8;
1031  break;
1032  }
1033 
1034  case TET4:
1035  {
1036  fe_descriptor_id = 111; // Solid Linear Tetrahedron
1037  assign_elem_nodes[0] = 0;
1038  assign_elem_nodes[1] = 1;
1039  assign_elem_nodes[2] = 2;
1040  assign_elem_nodes[3] = 3;
1041  break;
1042  }
1043 
1044  case PRISM6:
1045  {
1046  fe_descriptor_id = 112; // Solid Linear Prism
1047  assign_elem_nodes[0] = 0;
1048  assign_elem_nodes[1] = 1;
1049  assign_elem_nodes[2] = 2;
1050  assign_elem_nodes[3] = 3;
1051  assign_elem_nodes[4] = 4;
1052  assign_elem_nodes[5] = 5;
1053  break;
1054  }
1055 
1056  case HEX8:
1057  {
1058  fe_descriptor_id = 115; // Solid Linear Brick
1059  assign_elem_nodes[0] = 0;
1060  assign_elem_nodes[1] = 4;
1061  assign_elem_nodes[2] = 5;
1062  assign_elem_nodes[3] = 1;
1063  assign_elem_nodes[4] = 3;
1064  assign_elem_nodes[5] = 7;
1065  assign_elem_nodes[6] = 6;
1066  assign_elem_nodes[7] = 2;
1067  break;
1068  }
1069 
1070  case HEX20:
1071  {
1072  fe_descriptor_id = 116; // Solid Quadratic Brick
1073  assign_elem_nodes[ 0] = 0;
1074  assign_elem_nodes[ 1] = 12;
1075  assign_elem_nodes[ 2] = 4;
1076  assign_elem_nodes[ 3] = 16;
1077  assign_elem_nodes[ 4] = 5;
1078  assign_elem_nodes[ 5] = 13;
1079  assign_elem_nodes[ 6] = 1;
1080  assign_elem_nodes[ 7] = 8;
1081 
1082  assign_elem_nodes[ 8] = 11;
1083  assign_elem_nodes[ 9] = 19;
1084  assign_elem_nodes[10] = 17;
1085  assign_elem_nodes[11] = 9;
1086 
1087  assign_elem_nodes[12] = 3;
1088  assign_elem_nodes[13] = 15;
1089  assign_elem_nodes[14] = 7;
1090  assign_elem_nodes[15] = 18;
1091  assign_elem_nodes[16] = 6;
1092  assign_elem_nodes[17] = 14;
1093  assign_elem_nodes[18] = 2;
1094  assign_elem_nodes[19] = 10;
1095 
1096 
1097  break;
1098  }
1099 
1100  case TET10:
1101  {
1102  fe_descriptor_id = 118; // Solid Quadratic Tetrahedron
1103  assign_elem_nodes[0] = 0;
1104  assign_elem_nodes[1] = 4;
1105  assign_elem_nodes[2] = 1;
1106  assign_elem_nodes[3] = 5;
1107  assign_elem_nodes[4] = 2;
1108  assign_elem_nodes[5] = 6;
1109  assign_elem_nodes[6] = 7;
1110  assign_elem_nodes[7] = 8;
1111  assign_elem_nodes[8] = 9;
1112  assign_elem_nodes[9] = 3;
1113  break;
1114  }
1115 
1116  default:
1117  libmesh_error_msg("ERROR: Element type = " << Utility::enum_to_string(elem->type()) << " not supported in " << "UNVIO!");
1118  }
1119 
1120  dof_id_type elem_id = elem->id();
1121 
1122  out_file << std::setw(10) << elem_id // element ID
1123  << std::setw(10) << fe_descriptor_id // type of element
1124  << std::setw(10) << phys_prop_tab_dummy // not supported
1125  << std::setw(10) << mat_prop_tab_dummy // not supported
1126  << std::setw(10) << color_dummy // not supported
1127  << std::setw(10) << elem->n_nodes() // No. of nodes per element
1128  << '\n';
1129 
1130  for (auto j : elem->node_index_range())
1131  {
1132  // assign_elem_nodes[j]-th node: i.e., j loops over the
1133  // libMesh numbering, and assign_elem_nodes[j] over the
1134  // UNV numbering.
1135  const Node * node_in_unv_order = elem->node_ptr(assign_elem_nodes[j]);
1136 
1137  // new record after 8 id entries
1138  if (j==8 || j==16)
1139  out_file << '\n';
1140 
1141  // Write foreign label for this node
1142  dof_id_type node_id = node_in_unv_order->id();
1143 
1144  out_file << std::setw(10) << node_id;
1145  }
1146 
1147  out_file << '\n';
1148 
1149  n_elem_written++;
1150  }
1151 
1152  if (this->verbose())
1153  libMesh::out << " Finished writing " << n_elem_written << " elements" << std::endl;
1154 
1155  // Write end of dataset
1156  out_file << " -1\n";
1157 }
1158 
1159 
1160 
1161 void UNVIO::read_dataset(std::string file_name)
1162 {
1163  std::ifstream in_stream(file_name.c_str());
1164 
1165  libmesh_error_msg_if(!in_stream.good(), "Error opening UNV data file.");
1166 
1167  std::string olds, news, dummy;
1168 
1169  while (true)
1170  {
1171  in_stream >> olds >> news;
1172 
1173  // A "-1" followed by a number means the beginning of a dataset.
1174  while (((olds != "-1") || (news == "-1")) && !in_stream.eof())
1175  {
1176  olds = news;
1177  in_stream >> news;
1178  }
1179 
1180  if (in_stream.eof())
1181  break;
1182 
1183  // We only support reading datasets in the "2414" format.
1184  if (news == "2414")
1185  {
1186  // Ignore the rest of the current line and the next two
1187  // lines that contain analysis dataset label and name.
1188  for (unsigned int i=0; i<3; i++)
1189  std::getline(in_stream, dummy);
1190 
1191  // Read the dataset location, where
1192  // 1: Data at nodes
1193  // 2: Data on elements
1194  // Currently only data on nodes is supported.
1195  unsigned int dataset_location;
1196  in_stream >> dataset_location;
1197 
1198  // Currently only nodal datasets are supported.
1199  libmesh_error_msg_if(dataset_location != 1, "ERROR: Currently only Data at nodes is supported.");
1200 
1201  // Ignore the rest of this line and the next five records.
1202  for (unsigned int i=0; i<6; i++)
1203  std::getline(in_stream, dummy);
1204 
1205  // These data are all of no interest to us...
1206  unsigned int
1207  model_type,
1208  analysis_type,
1209  data_characteristic,
1210  result_type;
1211 
1212  // The type of data (complex, real, float, double etc, see
1213  // below).
1214  unsigned int data_type;
1215 
1216  // The number of floating-point values per entity.
1217  unsigned int num_vals_per_node;
1218 
1219  in_stream >> model_type // not used here
1220  >> analysis_type // not used here
1221  >> data_characteristic // not used here
1222  >> result_type // not used here
1223  >> data_type
1224  >> num_vals_per_node;
1225 
1226  // Ignore the rest of record 9, and records 10-13.
1227  for (unsigned int i=0; i<5; i++)
1228  std::getline(in_stream, dummy);
1229 
1230  // Now get the foreign (aka UNV node) node number and
1231  // the respective nodal data.
1232  int f_n_id;
1233  std::vector<Number> values;
1234 
1235  while (true)
1236  {
1237  in_stream >> f_n_id;
1238 
1239  // If -1 then we have reached the end of the dataset.
1240  if (f_n_id == -1)
1241  break;
1242 
1243  // Resize the values vector (usually data in three
1244  // principle directions, i.e. num_vals_per_node = 3).
1245  values.resize(num_vals_per_node);
1246 
1247  // Read the values for the respective node.
1248  for (unsigned int data_cnt=0; data_cnt<num_vals_per_node; data_cnt++)
1249  {
1250  // Check what data type we are reading.
1251  // 2,4: Real
1252  // 5,6: Complex
1253  // other data types are not supported yet.
1254  // As again, these floats may also be written
1255  // using a 'D' instead of an 'e'.
1256  if (data_type == 2 || data_type == 4)
1257  {
1258  std::string buf;
1259  in_stream >> buf;
1260  this->need_D_to_e(buf);
1261 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
1262  values[data_cnt] = Complex(std::atof(buf.c_str()), 0.);
1263 #else
1264  values[data_cnt] = std::atof(buf.c_str());
1265 #endif
1266  }
1267 
1268  else if (data_type == 5 || data_type == 6)
1269  {
1270 #ifdef LIBMESH_USE_COMPLEX_NUMBERS
1271  Real re_val, im_val;
1272 
1273  std::string buf;
1274  in_stream >> buf;
1275 
1276  if (this->need_D_to_e(buf))
1277  {
1278  re_val = std::atof(buf.c_str());
1279  in_stream >> buf;
1280  this->need_D_to_e(buf);
1281  im_val = std::atof(buf.c_str());
1282  }
1283  else
1284  {
1285  re_val = std::atof(buf.c_str());
1286  in_stream >> im_val;
1287  }
1288 
1289  values[data_cnt] = Complex(re_val,im_val);
1290 #else
1291 
1292  libmesh_error_msg("ERROR: Complex data only supported when libMesh is configured with --enable-complex!");
1293 #endif
1294  }
1295 
1296  else
1297  libmesh_error_msg("ERROR: Data type not supported.");
1298 
1299  } // end loop data_cnt
1300 
1301  // Get a pointer to the Node associated with the UNV node id.
1302  auto & node_ptr = libmesh_map_find(_unv_node_id_to_libmesh_node_ptr, f_n_id);
1303 
1304  // Store the nodal values in our map which uses the
1305  // libMesh Node* as the key. We use operator[] here
1306  // because we want to create an empty vector if the
1307  // entry does not already exist.
1308  _node_data[node_ptr] = values;
1309  } // end while (true)
1310  } // end if (news == "2414")
1311  } // end while (true)
1312 }
1313 
1314 
1315 
1316 const std::vector<Number> *
1317 UNVIO::get_data (Node * node) const
1318 {
1319  if (const auto it = _node_data.find(node);
1320  it == _node_data.end())
1321  return nullptr;
1322  else
1323  return &(it->second);
1324 }
1325 
1326 
1327 } // namespace libMesh
OStreamProxy err
const MT & mesh() const
Definition: mesh_output.h:259
void write_implementation(std::ostream &out_stream)
The actual implementation of the write function.
Definition: unv_io.C:286
bool _verbose
should be be verbose?
Definition: unv_io.h:182
UNVIO(MeshBase &mesh)
Constructor.
Definition: unv_io.C:68
A Node is like a Point, but with more information.
Definition: node.h:52
bool & verbose()
Set the flag indicating if we should be verbose.
Definition: unv_io.C:89
MPI_Datatype data_type
std::map< dof_id_type, Node * > _unv_node_id_to_libmesh_node_ptr
Maps UNV node IDs to libMesh Node*s.
Definition: unv_io.h:188
static const std::string _elements_dataset_label
label for the element dataset
Definition: unv_io.h:198
std::vector< bool > elems_of_dimension
A vector of bools describing what dimension elements have been encountered when reading a mesh...
Definition: mesh_input.h:107
void nodes_in(std::istream &in_file)
Read nodes from file.
Definition: unv_io.C:298
void read_implementation(std::istream &in_stream)
The actual implementation of the read function.
Definition: unv_io.C:122
void read_dataset(std::string file_name)
Read a UNV data file containing a dataset of type "2414".
Definition: unv_io.C:1161
This is the base class from which all geometric element types are derived.
Definition: elem.h:94
MeshBase & mesh
std::map< Node *, std::vector< Number > > _node_data
Map from libMesh Node* to data at that node, as read in by the read_dataset() function.
Definition: unv_io.h:214
unsigned int & ascii_precision()
Return/set the precision to use when writing ASCII files.
Definition: mesh_output.h:269
This class defines an abstract interface for Mesh output.
Definition: mesh_output.h:53
unsigned char max_elem_dimension_seen()
Definition: unv_io.C:384
The libMesh namespace provides an interface to certain functionality in the library.
void nodes_out(std::ostream &out_file)
Outputs nodes to the file out_file.
Definition: unv_io.C:870
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition: mesh_base.h:165
static const std::string _groups_dataset_label
label for the groups dataset
Definition: unv_io.h:203
void elements_out(std::ostream &out_file)
Outputs the element data to the file out_file.
Definition: unv_io.C:932
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
Add a new Node at Point p to the end of the vertex array, with processor_id procid.
This is the MeshBase class.
Definition: mesh_base.h:75
virtual void write(const std::string &) override
This method implements writing a mesh to a specified file.
Definition: unv_io.C:257
const dof_id_type n_nodes
Definition: tecplot_io.C:67
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
dof_id_type id() const
Definition: dof_object.h:828
This class defines an abstract interface for Mesh input.
Definition: mesh_base.h:57
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition: elem.C:444
virtual dof_id_type key(const unsigned int s) const =0
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
Definition: simple_range.h:57
std::string & subdomain_name(subdomain_id_type id)
Definition: mesh_base.C:1692
std::map< unsigned, unsigned > _unv_elem_id_to_libmesh_elem_id
Map UNV element IDs to libmesh element IDs.
Definition: unv_io.h:208
std::string & sideset_name(boundary_id_type id)
std::string enum_to_string(const T e)
virtual const Elem * elem_ptr(const dof_id_type i) const =0
std::complex< Real > Complex
bool need_D_to_e(std::string &number)
Replaces "1.1111D+00" with "1.1111e+00" if necessary.
Definition: unv_io.C:400
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
Definition: elem.h:2582
virtual unsigned short dim() const =0
OStreamProxy out
void add_side(const dof_id_type elem, const unsigned short int side, const boundary_id_type id)
Add side side of element number elem with boundary id id to the boundary information data structure...
const std::vector< Number > * get_data(Node *node) const
Definition: unv_io.C:1317
virtual ~UNVIO()
Destructor.
static const std::string _nodes_dataset_label
label for the node dataset
Definition: unv_io.h:193
virtual void read(const std::string &) override
This method implements reading a mesh from a specified file.
Definition: unv_io.C:96
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition: point.h:39
void elements_in(std::istream &in_file)
Method reads elements and stores them in std::vector<Elem *> _elements in the same order as they come...
Definition: unv_io.C:583
uint8_t dof_id_type
Definition: id_types.h:67
void groups_in(std::istream &in_file)
Reads the "groups" section of the file.
Definition: unv_io.C:417