Line data Source code
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 :
68 13 : UNVIO::UNVIO (MeshBase & mesh) :
69 : MeshInput<MeshBase> (mesh),
70 : MeshOutput<MeshBase>(mesh),
71 13 : _verbose (false)
72 : {
73 13 : }
74 :
75 :
76 :
77 0 : UNVIO::UNVIO (const MeshBase & mesh) :
78 : MeshOutput<MeshBase> (mesh),
79 0 : _verbose (false)
80 : {
81 0 : }
82 :
83 :
84 :
85 11 : UNVIO::~UNVIO () = default;
86 :
87 :
88 :
89 39 : bool & UNVIO::verbose ()
90 : {
91 39 : return _verbose;
92 : }
93 :
94 :
95 :
96 13 : void UNVIO::read (const std::string & file_name)
97 : {
98 13 : if (Utility::contains(file_name, ".gz"))
99 : {
100 : #ifdef LIBMESH_HAVE_GZSTREAM
101 :
102 0 : igzstream in_stream (file_name.c_str());
103 0 : 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 0 : return;
111 0 : }
112 :
113 : else
114 : {
115 14 : std::ifstream in_stream (file_name.c_str());
116 13 : this->read_implementation (in_stream);
117 1 : return;
118 11 : }
119 : }
120 :
121 :
122 13 : void UNVIO::read_implementation (std::istream & in_stream)
123 : {
124 : // Keep track of what kinds of elements this file contains
125 13 : elems_of_dimension.clear();
126 13 : elems_of_dimension.resize(4, false);
127 :
128 : {
129 14 : 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 1 : found_node = false,
134 1 : found_elem = false,
135 1 : found_group = false;
136 :
137 : // strings for reading the file line by line
138 : std::string
139 2 : old_line,
140 2 : 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 8 : old_line = current_line;
147 :
148 : // Try to read something. This may set EOF!
149 103 : std::getline(in_stream, current_line);
150 :
151 : // If the stream is still "valid", parse the line
152 111 : 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 76 : current_line.erase(std::remove_if(current_line.begin(), current_line.end(),
158 302 : [](unsigned char const c){return std::isspace(c);}),
159 21 : current_line.end());
160 :
161 : // Parse the nodes section
162 96 : if (current_line == _nodes_dataset_label &&
163 1 : old_line == "-1")
164 : {
165 1 : found_node = true;
166 13 : this->nodes_in(in_stream);
167 : }
168 :
169 : // Parse the elements section
170 84 : else if (current_line == _elements_dataset_label &&
171 1 : old_line == "-1")
172 : {
173 : // The current implementation requires the nodes to
174 : // have been read before reaching the elements
175 : // section.
176 13 : libmesh_error_msg_if(!found_node,
177 : "ERROR: The Nodes section must come before the Elements section of the UNV file!");
178 :
179 1 : found_elem = true;
180 13 : this->elements_in(in_stream);
181 : }
182 :
183 : // Parse the groups section
184 59 : else if (current_line == _groups_dataset_label &&
185 0 : 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 0 : 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 0 : found_group = true;
194 0 : this->groups_in(in_stream);
195 : }
196 :
197 : // We can stop reading once we've found the nodes, elements,
198 : // and group sections.
199 90 : if (found_node && found_elem && found_group)
200 0 : break;
201 :
202 : // If we made it here, we're not done yet, so keep reading
203 7 : continue;
204 76 : }
205 :
206 : // if (!in_stream) check to see if EOF was set. If so, break out of while loop.
207 13 : if (in_stream.eof())
208 1 : break;
209 :
210 : // If !in_stream and !in_stream.eof(), stream is in a bad state!
211 0 : libmesh_error_msg("Stream is bad! Perhaps the file does not exist?");
212 83 : } // 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 13 : libmesh_error_msg_if(!found_node, "ERROR: Could not find nodes!");
217 13 : 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 13 : 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
239 13 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
240 :
241 13 : unsigned char max_dim = this->max_elem_dimension_seen();
242 :
243 78065 : for (const auto & elem : mesh.element_ptr_range())
244 42540 : if (elem->dim() < max_dim)
245 11 : mesh.delete_elem(elem);
246 : }
247 :
248 13 : if (this->verbose())
249 0 : libMesh::out << " Finished." << std::endl << std::endl;
250 : }
251 13 : }
252 :
253 :
254 :
255 :
256 :
257 0 : void UNVIO::write (const std::string & file_name)
258 : {
259 0 : if (Utility::contains(file_name, ".gz"))
260 : {
261 : #ifdef LIBMESH_HAVE_GZSTREAM
262 :
263 0 : ogzstream out_stream(file_name.c_str());
264 0 : 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 0 : return;
273 0 : }
274 :
275 : else
276 : {
277 0 : std::ofstream out_stream (file_name.c_str());
278 0 : this->write_implementation (out_stream);
279 0 : return;
280 0 : }
281 : }
282 :
283 :
284 :
285 :
286 0 : void UNVIO::write_implementation (std::ostream & out_file)
287 : {
288 0 : libmesh_error_msg_if(!out_file.good(), "ERROR: Output file not good.");
289 :
290 : // write the nodes, then the elements
291 0 : this->nodes_out (out_file);
292 0 : this->elements_out (out_file);
293 0 : }
294 :
295 :
296 :
297 :
298 13 : void UNVIO::nodes_in (std::istream & in_file)
299 : {
300 2 : LOG_SCOPE("nodes_in()","UNVIO");
301 :
302 13 : if (this->verbose())
303 0 : libMesh::out << " Reading nodes" << std::endl;
304 :
305 13 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
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 2 : std::string line;
315 14 : std::istringstream coords_stream;
316 :
317 : // Continue reading nodes until there are none left
318 1 : unsigned ctr = 0;
319 : while (true)
320 : {
321 : // Read the node label
322 49018 : in_file >> node_label;
323 :
324 : // Break out of the while loop when we hit -1
325 49018 : if (node_label == -1)
326 1 : 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 49005 : std::getline(in_file, line);
334 :
335 : // read the floating-point data
336 49005 : std::getline(in_file, line);
337 :
338 : // Replace any "D" characters used for exponents with "E"
339 3977 : size_t last_pos = 0;
340 : while (true)
341 : {
342 176269 : last_pos = line.find("D", last_pos);
343 :
344 192177 : if (last_pos != std::string::npos)
345 143172 : line.replace(last_pos, 1, "E");
346 : else
347 3977 : break;
348 : }
349 :
350 : // Construct a stream object from the current line and then
351 : // stream in the xyz values.
352 3977 : coords_stream.str(line);
353 49005 : 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 3977 : coords_stream >> xyz[0] >> xyz[1] >> xyz[2];
360 :
361 49005 : Point p(xyz[0]);
362 : #if LIBMESH_DIM > 1
363 49005 : p(1) = xyz[1];
364 : #else
365 : libmesh_assert_equal_to(xyz[1], 0);
366 : #endif
367 : #if LIBMESH_DIM > 2
368 49005 : 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 49005 : Node * added_node = mesh.add_point(p, ctr++);
375 :
376 : // Maintain the mapping between UNV node ids and libmesh Node
377 : // pointers.
378 49005 : _unv_node_id_to_libmesh_node_ptr[node_label] = added_node;
379 49005 : }
380 24 : }
381 :
382 :
383 :
384 26 : unsigned char UNVIO::max_elem_dimension_seen ()
385 : {
386 2 : unsigned char max_dim = 0;
387 :
388 : unsigned char elem_dimensions_size = cast_int<unsigned char>
389 2 : (elems_of_dimension.size());
390 : // The elems_of_dimension array is 1-based in the UNV reader
391 104 : for (unsigned char i=1; i<elem_dimensions_size; ++i)
392 84 : if (elems_of_dimension[i])
393 2 : max_dim = i;
394 :
395 26 : return max_dim;
396 : }
397 :
398 :
399 :
400 160 : 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 160 : std::string::size_type position = number.find("D", 6);
404 :
405 160 : if (position != std::string::npos)
406 : {
407 : // replace "D" in string
408 0 : number.replace(position, 1, "e");
409 0 : return true;
410 : }
411 : else
412 0 : return false;
413 : }
414 :
415 :
416 :
417 0 : void UNVIO::groups_in (std::istream & in_file)
418 : {
419 : // Grab reference to the Mesh, so we can add boundary info data to it
420 0 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
421 :
422 : // Record the max and min element dimension seen while reading the file.
423 0 : 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 0 : 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 0 : in_file >> group_number;
440 :
441 0 : if (group_number == -1)
442 0 : 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 0 : std::string group_name;
456 : {
457 : unsigned dummy;
458 0 : in_file >> dummy >> dummy >> dummy >> dummy >> dummy >> dummy
459 0 : >> num_entities;
460 :
461 : // The second record has 1 field, the group name
462 0 : 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 0 : is_subdomain_group = false,
469 0 : 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 0 : for (unsigned entity=0; entity<num_entities; ++entity)
487 : {
488 0 : in_file >> entity_type_code >> entity_tag >> dummy >> dummy;
489 :
490 0 : if (entity_type_code != 8)
491 0 : libMesh::err << "Warning, unrecognized entity type code = "
492 0 : << entity_type_code
493 0 : << " in group "
494 0 : << group_name
495 0 : << std::endl;
496 :
497 : // Try to find this ID in the map from UNV element ids to libmesh ids.
498 0 : if (const auto it = _unv_elem_id_to_libmesh_elem_id.find(entity_tag);
499 0 : it != _unv_elem_id_to_libmesh_elem_id.end())
500 : {
501 0 : unsigned libmesh_elem_id = it->second;
502 :
503 : // Attempt to get a pointer to the elem listed in the group
504 0 : Elem * group_elem = mesh.elem_ptr(libmesh_elem_id);
505 :
506 : // dim < max_dim means the Elem defines a boundary condition
507 0 : if (group_elem->dim() < max_dim)
508 : {
509 0 : 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 0 : 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 0 : group_elem->subdomain_id() =
522 0 : cast_int<subdomain_id_type>(group_number);
523 :
524 : // Store the lower-dimensional element in the provide_bcs container.
525 0 : provide_bcs.emplace(group_elem->key(), group_elem);
526 : }
527 :
528 : // dim == max_dim means this group defines a subdomain ID
529 0 : else if (group_elem->dim() == max_dim)
530 : {
531 0 : is_subdomain_group = true;
532 0 : group_elem->subdomain_id() =
533 0 : cast_int<subdomain_id_type>(group_number);
534 : }
535 :
536 : else
537 0 : libmesh_error_msg("ERROR: Found an elem with dim=" \
538 : << group_elem->dim() << " > " << "max_dim=" << +max_dim);
539 : }
540 : else
541 0 : 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 0 : if (is_sideset_group)
547 0 : mesh.get_boundary_info().sideset_name
548 0 : (cast_int<boundary_id_type>(group_number)) = group_name;
549 :
550 0 : if (is_subdomain_group)
551 : mesh.subdomain_name
552 0 : (cast_int<subdomain_id_type>(group_number)) = group_name;
553 :
554 0 : } // end while (true)
555 :
556 : // Loop over elements and try to assign boundary information
557 0 : for (auto & elem : mesh.active_element_ptr_range())
558 0 : if (elem->dim() == max_dim)
559 0 : for (auto sn : elem->side_index_range())
560 0 : 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 0 : std::unique_ptr<Elem> side (elem->build_side_ptr(sn));
571 :
572 : // Get a pointer to the lower-dimensional element
573 0 : Elem * lower_dim_elem = pr.second;
574 :
575 : // This was a hash, so it might not be perfect. Let's verify...
576 0 : if (*lower_dim_elem == *side)
577 0 : mesh.get_boundary_info().add_side(elem, sn, lower_dim_elem->subdomain_id());
578 0 : }
579 0 : }
580 :
581 :
582 :
583 13 : void UNVIO::elements_in (std::istream & in_file)
584 : {
585 2 : LOG_SCOPE("elements_in()","UNVIO");
586 :
587 13 : if (this->verbose())
588 0 : libMesh::out << " Reading elements" << std::endl;
589 :
590 13 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
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 13 : 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 1 : unsigned ctr = 0;
621 : while (true)
622 : {
623 : // read element label, break out when we read -1
624 42553 : in_file >> element_label;
625 :
626 42553 : if (element_label == -1)
627 1 : break;
628 :
629 3520 : in_file >> fe_descriptor_id // read FE descriptor id
630 3520 : >> phys_prop_tab_num // (not supported yet)
631 3520 : >> mat_prop_tab_num // (not supported yet)
632 3520 : >> color // (not supported yet)
633 3520 : >> 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 42540 : if (fe_descriptor_id < 25)
648 : {
649 : unsigned dummy;
650 0 : in_file >> dummy >> dummy >> dummy;
651 : }
652 :
653 : // read node labels (1-based)
654 371640 : for (unsigned int j=1; j<=n_nodes; j++)
655 329100 : in_file >> node_labels[j];
656 :
657 : // element pointer, to be allocated
658 35500 : std::unique_ptr<Elem> elem;
659 :
660 42540 : switch (fe_descriptor_id)
661 : {
662 0 : case 11: // Rod
663 : {
664 0 : elem = Elem::build(EDGE2);
665 :
666 0 : assign_elem_nodes[1]=0;
667 0 : assign_elem_nodes[2]=1;
668 0 : break;
669 : }
670 :
671 0 : case 41: // Plane Stress Linear Triangle
672 : case 91: // Thin Shell Linear Triangle
673 : {
674 0 : elem = Elem::build(TRI3); // create new element
675 :
676 0 : assign_elem_nodes[1]=0;
677 0 : assign_elem_nodes[2]=2;
678 0 : assign_elem_nodes[3]=1;
679 0 : break;
680 : }
681 :
682 0 : case 42: // Plane Stress Quadratic Triangle
683 : case 92: // Thin Shell Quadratic Triangle
684 : {
685 0 : elem = Elem::build(TRI6); // create new element
686 :
687 0 : assign_elem_nodes[1]=0;
688 0 : assign_elem_nodes[2]=5;
689 0 : assign_elem_nodes[3]=2;
690 0 : assign_elem_nodes[4]=4;
691 0 : assign_elem_nodes[5]=1;
692 0 : assign_elem_nodes[6]=3;
693 0 : break;
694 : }
695 :
696 0 : case 43: // Plane Stress Cubic Triangle
697 0 : libmesh_error_msg("ERROR: UNV-element type 43: Plane Stress Cubic Triangle not supported.");
698 :
699 0 : case 44: // Plane Stress Linear Quadrilateral
700 : case 94: // Thin Shell Linear Quadrilateral
701 : {
702 0 : elem = Elem::build(QUAD4); // create new element
703 :
704 0 : assign_elem_nodes[1]=0;
705 0 : assign_elem_nodes[2]=3;
706 0 : assign_elem_nodes[3]=2;
707 0 : assign_elem_nodes[4]=1;
708 0 : break;
709 : }
710 :
711 0 : case 45: // Plane Stress Quadratic Quadrilateral
712 : case 95: // Thin Shell Quadratic Quadrilateral
713 : {
714 0 : elem = Elem::build(QUAD8); // create new element
715 :
716 0 : assign_elem_nodes[1]=0;
717 0 : assign_elem_nodes[2]=7;
718 0 : assign_elem_nodes[3]=3;
719 0 : assign_elem_nodes[4]=6;
720 0 : assign_elem_nodes[5]=2;
721 0 : assign_elem_nodes[6]=5;
722 0 : assign_elem_nodes[7]=1;
723 0 : assign_elem_nodes[8]=4;
724 0 : break;
725 : }
726 :
727 300 : case 300: // Thin Shell Quadratic Quadrilateral (nine nodes)
728 : {
729 600 : elem = Elem::build(QUAD9); // create new element
730 :
731 300 : assign_elem_nodes[1]=0;
732 300 : assign_elem_nodes[2]=7;
733 300 : assign_elem_nodes[3]=3;
734 300 : assign_elem_nodes[4]=6;
735 300 : assign_elem_nodes[5]=2;
736 300 : assign_elem_nodes[6]=5;
737 300 : assign_elem_nodes[7]=1;
738 300 : assign_elem_nodes[8]=4;
739 300 : assign_elem_nodes[9]=8;
740 300 : break;
741 : }
742 :
743 0 : case 46: // Plane Stress Cubic Quadrilateral
744 0 : libmesh_error_msg("ERROR: UNV-element type 46: Plane Stress Cubic Quadrilateral not supported.");
745 :
746 0 : case 111: // Solid Linear Tetrahedron
747 : {
748 0 : elem = Elem::build(TET4); // create new element
749 :
750 0 : assign_elem_nodes[1]=0;
751 0 : assign_elem_nodes[2]=1;
752 0 : assign_elem_nodes[3]=2;
753 0 : assign_elem_nodes[4]=3;
754 0 : break;
755 : }
756 :
757 5760 : case 112: // Solid Linear Prism
758 : {
759 10560 : elem = Elem::build(PRISM6); // create new element
760 :
761 5760 : assign_elem_nodes[1]=0;
762 5760 : assign_elem_nodes[2]=1;
763 5760 : assign_elem_nodes[3]=2;
764 5760 : assign_elem_nodes[4]=3;
765 5760 : assign_elem_nodes[5]=4;
766 5760 : assign_elem_nodes[6]=5;
767 5760 : break;
768 : }
769 :
770 36480 : case 115: // Solid Linear Brick
771 : {
772 66880 : elem = Elem::build(HEX8); // create new element
773 :
774 36480 : assign_elem_nodes[1]=0;
775 36480 : assign_elem_nodes[2]=4;
776 36480 : assign_elem_nodes[3]=5;
777 36480 : assign_elem_nodes[4]=1;
778 36480 : assign_elem_nodes[5]=3;
779 36480 : assign_elem_nodes[6]=7;
780 36480 : assign_elem_nodes[7]=6;
781 36480 : assign_elem_nodes[8]=2;
782 36480 : break;
783 : }
784 :
785 0 : case 116: // Solid Quadratic Brick
786 : {
787 0 : elem = Elem::build(HEX20); // create new element
788 :
789 0 : assign_elem_nodes[1]=0;
790 0 : assign_elem_nodes[2]=12;
791 0 : assign_elem_nodes[3]=4;
792 0 : assign_elem_nodes[4]=16;
793 0 : assign_elem_nodes[5]=5;
794 0 : assign_elem_nodes[6]=13;
795 0 : assign_elem_nodes[7]=1;
796 0 : assign_elem_nodes[8]=8;
797 :
798 0 : assign_elem_nodes[9]=11;
799 0 : assign_elem_nodes[10]=19;
800 0 : assign_elem_nodes[11]=17;
801 0 : assign_elem_nodes[12]=9;
802 :
803 0 : assign_elem_nodes[13]=3;
804 0 : assign_elem_nodes[14]=15;
805 0 : assign_elem_nodes[15]=7;
806 0 : assign_elem_nodes[16]=18;
807 0 : assign_elem_nodes[17]=6;
808 0 : assign_elem_nodes[18]=14;
809 0 : assign_elem_nodes[19]=2;
810 0 : assign_elem_nodes[20]=10;
811 0 : break;
812 : }
813 :
814 0 : case 117: // Solid Cubic Brick
815 0 : libmesh_error_msg("Error: UNV-element type 117: Solid Cubic Brick not supported.");
816 :
817 0 : case 118: // Solid Quadratic Tetrahedron
818 : {
819 0 : elem = Elem::build(TET10); // create new element
820 :
821 0 : assign_elem_nodes[1]=0;
822 0 : assign_elem_nodes[2]=4;
823 0 : assign_elem_nodes[3]=1;
824 0 : assign_elem_nodes[4]=5;
825 0 : assign_elem_nodes[5]=2;
826 0 : assign_elem_nodes[6]=6;
827 0 : assign_elem_nodes[7]=7;
828 0 : assign_elem_nodes[8]=8;
829 0 : assign_elem_nodes[9]=9;
830 0 : assign_elem_nodes[10]=3;
831 0 : break;
832 : }
833 :
834 0 : default: // Unrecognized element type
835 0 : libmesh_error_msg("ERROR: UNV-element type " << fe_descriptor_id << " not supported.");
836 : }
837 :
838 : // nodes are being stored in element
839 371640 : 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 356300 : libmesh_map_find(_unv_node_id_to_libmesh_node_ptr, node_labels[j]);
844 :
845 329100 : elem->set_node(assign_elem_nodes[j], node_ptr);
846 : }
847 :
848 42540 : elems_of_dimension[elem->dim()] = true;
849 :
850 : // Set the element's ID
851 35200 : elem->set_id(ctr);
852 :
853 : // Maintain a map from the libmesh (0-based) numbering to the
854 : // UNV numbering.
855 42540 : _unv_elem_id_to_libmesh_elem_id[element_label] = ctr;
856 :
857 : // Add the element to the Mesh
858 46060 : mesh.add_elem(std::move(elem));
859 :
860 : // Increment the counter for the next iteration
861 42540 : ctr++;
862 42540 : } // end while (true)
863 13 : }
864 :
865 :
866 :
867 :
868 :
869 :
870 0 : void UNVIO::nodes_out (std::ostream & out_file)
871 : {
872 0 : if (this->verbose())
873 0 : libMesh::out << " Writing " << MeshOutput<MeshBase>::mesh().n_nodes() << " nodes" << std::endl;
874 :
875 : // Write beginning of dataset
876 : out_file << " -1\n"
877 : << " "
878 : << _nodes_dataset_label
879 0 : << '\n';
880 :
881 : unsigned int
882 0 : exp_coord_sys_dummy = 0, // export coordinate sys. (not supported yet)
883 0 : disp_coord_sys_dummy = 0, // displacement coordinate sys. (not supp. yet)
884 0 : color_dummy = 0; // color(not supported yet)
885 :
886 : // A reference to the parent class's mesh
887 0 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
888 :
889 : // Use scientific notation with capital E and default 17 digits for printing out the coordinates
890 0 : out_file << std::scientific << std::setprecision(this->ascii_precision()) << std::uppercase;
891 :
892 0 : for (const auto & current_node : mesh.node_ptr_range())
893 : {
894 0 : dof_id_type node_id = current_node->id();
895 :
896 0 : out_file << std::setw(10) << node_id
897 0 : << std::setw(10) << exp_coord_sys_dummy
898 0 : << std::setw(10) << disp_coord_sys_dummy
899 0 : << std::setw(10) << color_dummy
900 0 : << '\n';
901 :
902 : // The coordinates - always write out three coords regardless of LIBMESH_DIM
903 0 : Real x = (*current_node)(0);
904 :
905 : #if LIBMESH_DIM > 1
906 0 : Real y = (*current_node)(1);
907 : #else
908 : Real y = 0.;
909 : #endif
910 :
911 : #if LIBMESH_DIM > 2
912 0 : Real z = (*current_node)(2);
913 : #else
914 : Real z = 0.;
915 : #endif
916 :
917 0 : out_file << std::setw(25) << x
918 0 : << std::setw(25) << y
919 0 : << std::setw(25) << z
920 0 : << '\n';
921 0 : }
922 :
923 : // Write end of dataset
924 0 : out_file << " -1\n";
925 0 : }
926 :
927 :
928 :
929 :
930 :
931 :
932 0 : void UNVIO::elements_out(std::ostream & out_file)
933 : {
934 0 : if (this->verbose())
935 0 : libMesh::out << " Writing elements" << std::endl;
936 :
937 : // Write beginning of dataset
938 : out_file << " -1\n"
939 : << " "
940 : << _elements_dataset_label
941 0 : << "\n";
942 :
943 : unsigned
944 0 : fe_descriptor_id = 0, // FE descriptor id
945 0 : phys_prop_tab_dummy = 2, // physical property (not supported yet)
946 0 : mat_prop_tab_dummy = 1, // material property (not supported yet)
947 0 : 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 0 : unsigned int n_elem_written=0;
965 :
966 : // A reference to the parent class's mesh
967 0 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
968 :
969 0 : for (const auto & elem : mesh.element_ptr_range())
970 : {
971 0 : switch (elem->type())
972 : {
973 :
974 0 : case TRI3:
975 : {
976 0 : fe_descriptor_id = 41; // Plane Stress Linear Triangle
977 0 : assign_elem_nodes[0] = 0;
978 0 : assign_elem_nodes[1] = 2;
979 0 : assign_elem_nodes[2] = 1;
980 0 : break;
981 : }
982 :
983 0 : case TRI6:
984 : {
985 0 : fe_descriptor_id = 42; // Plane Stress Quadratic Triangle
986 0 : assign_elem_nodes[0] = 0;
987 0 : assign_elem_nodes[1] = 5;
988 0 : assign_elem_nodes[2] = 2;
989 0 : assign_elem_nodes[3] = 4;
990 0 : assign_elem_nodes[4] = 1;
991 0 : assign_elem_nodes[5] = 3;
992 0 : break;
993 : }
994 :
995 0 : case QUAD4:
996 : {
997 0 : fe_descriptor_id = 44; // Plane Stress Linear Quadrilateral
998 0 : assign_elem_nodes[0] = 0;
999 0 : assign_elem_nodes[1] = 3;
1000 0 : assign_elem_nodes[2] = 2;
1001 0 : assign_elem_nodes[3] = 1;
1002 0 : break;
1003 : }
1004 :
1005 0 : case QUAD8:
1006 : {
1007 0 : fe_descriptor_id = 45; // Plane Stress Quadratic Quadrilateral
1008 0 : assign_elem_nodes[0] = 0;
1009 0 : assign_elem_nodes[1] = 7;
1010 0 : assign_elem_nodes[2] = 3;
1011 0 : assign_elem_nodes[3] = 6;
1012 0 : assign_elem_nodes[4] = 2;
1013 0 : assign_elem_nodes[5] = 5;
1014 0 : assign_elem_nodes[6] = 1;
1015 0 : assign_elem_nodes[7] = 4;
1016 0 : break;
1017 : }
1018 :
1019 0 : case QUAD9:
1020 : {
1021 0 : fe_descriptor_id = 300; // Plane Stress Quadratic Quadrilateral
1022 0 : assign_elem_nodes[0] = 0;
1023 0 : assign_elem_nodes[1] = 7;
1024 0 : assign_elem_nodes[2] = 3;
1025 0 : assign_elem_nodes[3] = 6;
1026 0 : assign_elem_nodes[4] = 2;
1027 0 : assign_elem_nodes[5] = 5;
1028 0 : assign_elem_nodes[6] = 1;
1029 0 : assign_elem_nodes[7] = 4;
1030 0 : assign_elem_nodes[8] = 8;
1031 0 : break;
1032 : }
1033 :
1034 0 : case TET4:
1035 : {
1036 0 : fe_descriptor_id = 111; // Solid Linear Tetrahedron
1037 0 : assign_elem_nodes[0] = 0;
1038 0 : assign_elem_nodes[1] = 1;
1039 0 : assign_elem_nodes[2] = 2;
1040 0 : assign_elem_nodes[3] = 3;
1041 0 : break;
1042 : }
1043 :
1044 0 : case PRISM6:
1045 : {
1046 0 : fe_descriptor_id = 112; // Solid Linear Prism
1047 0 : assign_elem_nodes[0] = 0;
1048 0 : assign_elem_nodes[1] = 1;
1049 0 : assign_elem_nodes[2] = 2;
1050 0 : assign_elem_nodes[3] = 3;
1051 0 : assign_elem_nodes[4] = 4;
1052 0 : assign_elem_nodes[5] = 5;
1053 0 : break;
1054 : }
1055 :
1056 0 : case HEX8:
1057 : {
1058 0 : fe_descriptor_id = 115; // Solid Linear Brick
1059 0 : assign_elem_nodes[0] = 0;
1060 0 : assign_elem_nodes[1] = 4;
1061 0 : assign_elem_nodes[2] = 5;
1062 0 : assign_elem_nodes[3] = 1;
1063 0 : assign_elem_nodes[4] = 3;
1064 0 : assign_elem_nodes[5] = 7;
1065 0 : assign_elem_nodes[6] = 6;
1066 0 : assign_elem_nodes[7] = 2;
1067 0 : break;
1068 : }
1069 :
1070 0 : case HEX20:
1071 : {
1072 0 : fe_descriptor_id = 116; // Solid Quadratic Brick
1073 0 : assign_elem_nodes[ 0] = 0;
1074 0 : assign_elem_nodes[ 1] = 12;
1075 0 : assign_elem_nodes[ 2] = 4;
1076 0 : assign_elem_nodes[ 3] = 16;
1077 0 : assign_elem_nodes[ 4] = 5;
1078 0 : assign_elem_nodes[ 5] = 13;
1079 0 : assign_elem_nodes[ 6] = 1;
1080 0 : assign_elem_nodes[ 7] = 8;
1081 :
1082 0 : assign_elem_nodes[ 8] = 11;
1083 0 : assign_elem_nodes[ 9] = 19;
1084 0 : assign_elem_nodes[10] = 17;
1085 0 : assign_elem_nodes[11] = 9;
1086 :
1087 0 : assign_elem_nodes[12] = 3;
1088 0 : assign_elem_nodes[13] = 15;
1089 0 : assign_elem_nodes[14] = 7;
1090 0 : assign_elem_nodes[15] = 18;
1091 0 : assign_elem_nodes[16] = 6;
1092 0 : assign_elem_nodes[17] = 14;
1093 0 : assign_elem_nodes[18] = 2;
1094 0 : assign_elem_nodes[19] = 10;
1095 :
1096 :
1097 0 : break;
1098 : }
1099 :
1100 0 : case TET10:
1101 : {
1102 0 : fe_descriptor_id = 118; // Solid Quadratic Tetrahedron
1103 0 : assign_elem_nodes[0] = 0;
1104 0 : assign_elem_nodes[1] = 4;
1105 0 : assign_elem_nodes[2] = 1;
1106 0 : assign_elem_nodes[3] = 5;
1107 0 : assign_elem_nodes[4] = 2;
1108 0 : assign_elem_nodes[5] = 6;
1109 0 : assign_elem_nodes[6] = 7;
1110 0 : assign_elem_nodes[7] = 8;
1111 0 : assign_elem_nodes[8] = 9;
1112 0 : assign_elem_nodes[9] = 3;
1113 0 : break;
1114 : }
1115 :
1116 0 : default:
1117 0 : libmesh_error_msg("ERROR: Element type = " << Utility::enum_to_string(elem->type()) << " not supported in " << "UNVIO!");
1118 : }
1119 :
1120 0 : dof_id_type elem_id = elem->id();
1121 :
1122 0 : out_file << std::setw(10) << elem_id // element ID
1123 0 : << std::setw(10) << fe_descriptor_id // type of element
1124 0 : << std::setw(10) << phys_prop_tab_dummy // not supported
1125 0 : << std::setw(10) << mat_prop_tab_dummy // not supported
1126 0 : << std::setw(10) << color_dummy // not supported
1127 0 : << std::setw(10) << elem->n_nodes() // No. of nodes per element
1128 0 : << '\n';
1129 :
1130 0 : 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 0 : const Node * node_in_unv_order = elem->node_ptr(assign_elem_nodes[j]);
1136 :
1137 : // new record after 8 id entries
1138 0 : if (j==8 || j==16)
1139 0 : out_file << '\n';
1140 :
1141 : // Write foreign label for this node
1142 0 : dof_id_type node_id = node_in_unv_order->id();
1143 :
1144 0 : out_file << std::setw(10) << node_id;
1145 : }
1146 :
1147 0 : out_file << '\n';
1148 :
1149 0 : n_elem_written++;
1150 0 : }
1151 :
1152 0 : if (this->verbose())
1153 0 : libMesh::out << " Finished writing " << n_elem_written << " elements" << std::endl;
1154 :
1155 : // Write end of dataset
1156 0 : out_file << " -1\n";
1157 0 : }
1158 :
1159 :
1160 :
1161 1 : void UNVIO::read_dataset(std::string file_name)
1162 : {
1163 1 : std::ifstream in_stream(file_name.c_str());
1164 :
1165 1 : libmesh_error_msg_if(!in_stream.good(), "Error opening UNV data file.");
1166 :
1167 0 : std::string olds, news, dummy;
1168 :
1169 : while (true)
1170 : {
1171 2 : in_stream >> olds >> news;
1172 :
1173 : // A "-1" followed by a number means the beginning of a dataset.
1174 4 : while (((olds != "-1") || (news == "-1")) && !in_stream.eof())
1175 : {
1176 0 : olds = news;
1177 0 : in_stream >> news;
1178 : }
1179 :
1180 2 : if (in_stream.eof())
1181 0 : break;
1182 :
1183 : // We only support reading datasets in the "2414" format.
1184 1 : 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 4 : for (unsigned int i=0; i<3; i++)
1189 3 : 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 0 : in_stream >> dataset_location;
1197 :
1198 : // Currently only nodal datasets are supported.
1199 1 : 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 7 : for (unsigned int i=0; i<6; i++)
1203 6 : 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 0 : in_stream >> model_type // not used here
1220 0 : >> analysis_type // not used here
1221 0 : >> data_characteristic // not used here
1222 0 : >> result_type // not used here
1223 0 : >> data_type
1224 0 : >> num_vals_per_node;
1225 :
1226 : // Ignore the rest of record 9, and records 10-13.
1227 6 : for (unsigned int i=0; i<5; i++)
1228 5 : 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 0 : std::vector<Number> values;
1234 :
1235 : while (true)
1236 : {
1237 161 : in_stream >> f_n_id;
1238 :
1239 : // If -1 then we have reached the end of the dataset.
1240 161 : if (f_n_id == -1)
1241 0 : break;
1242 :
1243 : // Resize the values vector (usually data in three
1244 : // principle directions, i.e. num_vals_per_node = 3).
1245 160 : values.resize(num_vals_per_node);
1246 :
1247 : // Read the values for the respective node.
1248 320 : 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 160 : if (data_type == 2 || data_type == 4)
1257 : {
1258 0 : std::string buf;
1259 0 : in_stream >> buf;
1260 0 : this->need_D_to_e(buf);
1261 : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
1262 0 : values[data_cnt] = Complex(std::atof(buf.c_str()), 0.);
1263 : #else
1264 0 : values[data_cnt] = std::atof(buf.c_str());
1265 : #endif
1266 : }
1267 :
1268 160 : 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 160 : in_stream >> buf;
1275 :
1276 160 : if (this->need_D_to_e(buf))
1277 : {
1278 : re_val = std::atof(buf.c_str());
1279 0 : in_stream >> buf;
1280 0 : this->need_D_to_e(buf);
1281 0 : 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 160 : values[data_cnt] = Complex(re_val,im_val);
1290 : #else
1291 :
1292 0 : libmesh_error_msg("ERROR: Complex data only supported when libMesh is configured with --enable-complex!");
1293 : #endif
1294 : }
1295 :
1296 : else
1297 0 : 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 160 : 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 160 : _node_data[node_ptr] = values;
1309 0 : } // end while (true)
1310 : } // end if (news == "2414")
1311 0 : } // end while (true)
1312 1 : }
1313 :
1314 :
1315 :
1316 : const std::vector<Number> *
1317 1281 : UNVIO::get_data (Node * node) const
1318 : {
1319 1281 : if (const auto it = _node_data.find(node);
1320 0 : it == _node_data.end())
1321 0 : return nullptr;
1322 : else
1323 160 : return &(it->second);
1324 : }
1325 :
1326 :
1327 : } // namespace libMesh
|