libMesh
Loading...
Searching...
No Matches
unv_io.C
Go to the documentation of this file.
1// The libMesh Finite Element Library.
2// Copyright (C) 2002-2026 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
54namespace libMesh
55{
56
57//-----------------------------------------------------------------------------
58// UNVIO class static members
59const std::string UNVIO::_nodes_dataset_label = "2411";
60const std::string UNVIO::_elements_dataset_label = "2412";
61const 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
85UNVIO::~UNVIO () = default;
86
87
88
90{
91 return _verbose;
92}
93
94
95
96void UNVIO::read (const std::string & file_name)
97{
98 if (Utility::contains(file_name, ".gz"))
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
122void 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
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
257void UNVIO::write (const std::string & file_name)
258{
259 if (Utility::contains(file_name, ".gz"))
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
286void 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
298void 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
400bool 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
417void 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 restrict_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 restrict_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
583void 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
870void 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
932void 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
1161void 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
1316const std::vector<Number> *
1317UNVIO::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
std::string & sideset_name(boundary_id_type id)
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.
dof_id_type id() const
Definition dof_object.h:819
This is the base class from which all geometric element types are derived.
Definition elem.h:96
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual unsigned short dim() const =0
subdomain_id_type subdomain_id() const
Definition elem.h:2591
virtual dof_id_type key(const unsigned int s) const =0
This is the MeshBase class.
Definition mesh_base.h:81
void set_subdomain_name(subdomain_id_type id, const std::string &name, bool synchronous=false)
Sets the name for the provided id.
Definition mesh_base.C:1914
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual void delete_elem(Elem *e)=0
Removes element e from the mesh.
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.
virtual const Elem * elem_ptr(const dof_id_type i) const =0
virtual Elem * add_elem(Elem *e)=0
Add elem e to the end of the element array.
This class defines an abstract interface for Mesh input.
Definition mesh_input.h:49
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
This class defines an abstract interface for Mesh output.
Definition mesh_output.h:54
unsigned int & ascii_precision()
Return/set the precision to use when writing ASCII files.
const MT & mesh() const
A Node is like a Point, but with more information.
Definition node.h:55
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
static const std::string _nodes_dataset_label
label for the node dataset
Definition unv_io.h:193
std::map< unsigned, unsigned > _unv_elem_id_to_libmesh_elem_id
Map UNV element IDs to libmesh element IDs.
Definition unv_io.h:208
void read_dataset(std::string file_name)
Read a UNV data file containing a dataset of type "2414".
Definition unv_io.C:1161
static const std::string _groups_dataset_label
label for the groups dataset
Definition unv_io.h:203
virtual ~UNVIO()
Destructor.
bool & verbose()
Set the flag indicating if we should be verbose.
Definition unv_io.C:89
virtual void write(const std::string &) override
This method implements writing a mesh to a specified file.
Definition unv_io.C:257
void elements_out(std::ostream &out_file)
Outputs the element data to the file out_file.
Definition unv_io.C:932
const std::vector< Number > * get_data(Node *node) const
Definition unv_io.C:1317
bool need_D_to_e(std::string &number)
Replaces "1.1111D+00" with "1.1111e+00" if necessary.
Definition unv_io.C:400
void nodes_out(std::ostream &out_file)
Outputs nodes to the file out_file.
Definition unv_io.C:870
static const std::string _elements_dataset_label
label for the element dataset
Definition unv_io.h:198
bool _verbose
should be be verbose?
Definition unv_io.h:182
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
void groups_in(std::istream &in_file)
Reads the "groups" section of the file.
Definition unv_io.C:417
virtual void read(const std::string &) override
This method implements reading a mesh from a specified file.
Definition unv_io.C:96
UNVIO(MeshBase &mesh)
Constructor.
Definition unv_io.C:68
unsigned char max_elem_dimension_seen()
Definition unv_io.C:384
void nodes_in(std::istream &in_file)
Read nodes from file.
Definition unv_io.C:298
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
void write_implementation(std::ostream &out_stream)
The actual implementation of the write function.
Definition unv_io.C:286
void read_implementation(std::istream &in_stream)
The actual implementation of the read function.
Definition unv_io.C:122
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
MeshBase & mesh
bool contains(std::string_view superstring, std::string_view substring)
Look for a substring within a string.
Definition utility.C:205
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
OStreamProxy err
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
std::complex< Real > Complex
OStreamProxy out
uint8_t dof_id_type
Definition id_types.h:67
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
const dof_id_type n_nodes
Definition tecplot_io.C:67