libMesh
Loading...
Searching...
No Matches
exodusII_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/exodusII_io.h"
21
22#include "libmesh/boundary_info.h"
23#include "libmesh/cell_c0polyhedron.h"
24#include "libmesh/dof_map.h"
25#include "libmesh/dyna_io.h" // ElementDefinition for BEX
26#include "libmesh/enum_elem_type.h"
27#include "libmesh/elem.h"
28#include "libmesh/enum_to_string.h"
29#include "libmesh/equation_systems.h"
30#include "libmesh/exodusII_io_helper.h"
31#include "libmesh/face_c0polygon.h"
32#include "libmesh/fpe_disabler.h"
33#include "libmesh/int_range.h"
34#include "libmesh/libmesh_logging.h"
35#include "libmesh/mesh_base.h"
36#include "libmesh/mesh_communication.h"
37#include "libmesh/numeric_vector.h"
38#include "libmesh/parallel_mesh.h"
39#include "libmesh/parallel.h"
40#include "libmesh/system.h"
41#include "libmesh/utility.h"
42
43// TIMPI includes
44#include "timpi/parallel_sync.h"
45
46// C++ includes
47#include <cmath> // llround
48#include <cstring>
49#include <fstream>
50#include <map>
51#include <memory>
52#include <sstream>
53
54#ifdef LIBMESH_HAVE_EXODUS_API
55namespace
56{
57 using namespace libMesh;
58
59 const std::vector<Real> & bex_constraint_vec(std::size_t i,
60 const ExodusII_IO_Helper & helper)
61 {
62 std::size_t vec_offset = 0;
63 for (auto block_num : index_range(helper.bex_dense_constraint_vecs))
64 {
65 const auto & vecblock = helper.bex_dense_constraint_vecs[block_num];
66 libmesh_assert_greater_equal(i, vec_offset);
67 if (i - vec_offset < vecblock.size())
68 return vecblock[i - vec_offset];
69
70 vec_offset += vecblock.size();
71 }
72
73 libmesh_error_msg("Requested BEX coefficient vector " << i << " not found");
74 }
75
76#ifdef LIBMESH_USE_COMPLEX_NUMBERS
77 enum class SolutionOrdering
78 {
79 variable_major,
80 geometry_major
81 };
82
83 std::size_t ordered_solution_index (const std::size_t var,
84 const std::size_t geometry_index,
85 const std::size_t num_vars,
86 const std::size_t num_geometric_entries,
87 const SolutionOrdering ordering)
88 {
89 if (ordering == SolutionOrdering::variable_major)
90 return var * num_geometric_entries + geometry_index;
91 else
92 return geometry_index * num_vars + var;
93 }
94
95 std::vector<Real>
96 complex_soln_components (const std::vector<Number> & soln,
97 const std::size_t num_vars,
98 const bool write_complex_abs,
99 const SolutionOrdering source_ordering,
100 const SolutionOrdering destination_ordering)
101 {
102 const std::size_t num_values = soln.size();
103 libmesh_assert_greater(num_vars, 0);
104 libmesh_assert_equal_to(num_values % num_vars, 0);
105 const std::size_t num_geometric_entries = num_values / num_vars;
106
107 // This will contain the real and imaginary parts and the magnitude
108 // of the values in soln
109 const std::size_t nco = write_complex_abs ? 3 : 2;
110 const std::size_t num_complex_vars = nco * num_vars;
111 std::vector<Real> complex_soln(nco * num_values);
112
113 for (const auto var : make_range(num_vars))
114 for (const auto geometry_index : make_range(num_geometric_entries))
115 {
116 const Number value =
117 soln[ordered_solution_index
118 (var, geometry_index, num_vars, num_geometric_entries, source_ordering)];
119
120 const std::size_t complex_var = nco * var;
121 complex_soln[ordered_solution_index
122 (complex_var, geometry_index, num_complex_vars,
123 num_geometric_entries, destination_ordering)] = value.real();
124 complex_soln[ordered_solution_index
125 (complex_var + 1, geometry_index, num_complex_vars,
126 num_geometric_entries, destination_ordering)] = value.imag();
127
128 if (write_complex_abs)
129 complex_soln[ordered_solution_index
130 (complex_var + 2, geometry_index, num_complex_vars,
131 num_geometric_entries, destination_ordering)] = std::abs(value);
132 }
133
134 return complex_soln;
135 }
136#endif // LIBMESH_USE_COMPLEX_NUMBERS
137
138}
139#endif
140
141namespace libMesh
142{
143
144// ------------------------------------------------------------
145// ExodusII_IO class members
147 bool single_precision) :
150 /* is_parallel_format = */ false,
151 /* serial_only_needed_on_proc_0 = */ true),
153#ifdef LIBMESH_HAVE_EXODUS_API
154 exio_helper(std::make_unique<ExodusII_IO_Helper>(*this, false, true, single_precision)),
155 _timestep(1),
156 _verbose(false),
157 _append(false),
158#endif
159 _allow_empty_variables(false),
160 _write_complex_abs(true),
161 _set_unique_ids_from_maps(false),
162 _disc_bex(false)
163{
164 // if !LIBMESH_HAVE_EXODUS_API, we didn't use this
165 libmesh_ignore(single_precision);
166}
167
168
169
171 bool single_precision) :
174 /* is_parallel_format = */ false,
175 /* serial_only_needed_on_proc_0 = */ true),
177#ifdef LIBMESH_HAVE_EXODUS_API
178 exio_helper(std::make_unique<ExodusII_IO_Helper>(*this, false, true, single_precision)),
179 _timestep(1),
180 _verbose(false),
181 _append(false),
182#endif
183 _allow_empty_variables(false),
184 _write_complex_abs(true),
185 _set_unique_ids_from_maps(false),
186 _disc_bex(false)
187{
188 // if !LIBMESH_HAVE_EXODUS_API, we didn't use this
189 libmesh_ignore(single_precision);
190}
191
192
193
195{
196#ifdef LIBMESH_HAVE_EXODUS_API
198#else
199 return 0;
200#endif
201}
202
203
204
205void ExodusII_IO::set_extra_integer_vars(const std::vector<std::string> & extra_integer_vars)
206{
207 _extra_integer_vars = extra_integer_vars;
208}
209
210void ExodusII_IO::set_output_variables(const std::vector<std::string> & output_variables,
211 bool allow_empty)
212{
213 _output_variables = output_variables;
214 _allow_empty_variables = allow_empty;
215}
216
217
218
219void ExodusII_IO::write_discontinuous_exodusII(const std::string & name,
220 const EquationSystems & es,
221 const std::set<std::string> * system_names)
222{
223 std::vector<std::string> solution_names;
224 std::vector<Number> v;
225
226 es.build_variable_names (solution_names, nullptr, system_names);
227 es.build_discontinuous_solution_vector (v, system_names,
228 nullptr, false, /* defaults */
229 this->get_add_sides());
230 this->write_nodal_data_discontinuous(name, v, solution_names);
231}
232
233
234#ifdef LIBMESH_HAVE_EXODUS_API
235void ExodusII_IO::write_timestep_discontinuous (const std::string &fname,
236 const EquationSystems &es,
237 const int timestep,
238 const Real time,
239 const std::set<std::string> * system_names)
240{
241 _timestep = timestep;
242 write_discontinuous_equation_systems (fname,es,system_names);
243
245 return;
246
247 exio_helper->write_timestep(timestep, time);
248}
249
250#else
251void ExodusII_IO::write_timestep_discontinuous (const std::string & /* fname */,
252 const EquationSystems & /* es */,
253 const int /* timestep */,
254 const Real /* time */,
255 const std::set<std::string> * /*system_names*/)
256{ libmesh_error(); }
257#endif
258
259
260// ------------------------------------------------------------
261// When the Exodus API is present...
262#ifdef LIBMESH_HAVE_EXODUS_API
263
265{
266 exio_helper->close();
267}
268
269
270void ExodusII_IO::read (const std::string & fname)
271{
272 LOG_SCOPE("read()", "ExodusII_IO");
273
274 // Get a reference to the mesh we are reading
276
277 // Add extra integers into the mesh
278 std::vector<unsigned int> extra_ids;
279 for (auto & name : _extra_integer_vars)
280 extra_ids.push_back(mesh.add_elem_integer(name));
281
282 // Clear any existing mesh data
283 mesh.clear();
284
285 // Keep track of what kinds of elements this file contains
286 elems_of_dimension.clear();
287 elems_of_dimension.resize(4, false);
288
289 // Open the exodus file in EX_READ mode
290 exio_helper->open(fname.c_str(), /*read_only=*/true);
291
292 // Get header information from exodus file
293 exio_helper->read_and_store_header_info();
294
295 // Read the QA records
296 exio_helper->read_qa_records();
297
298 // Print header information
299 exio_helper->print_header();
300
301 // Read nodes from the exodus file
302 exio_helper->read_nodes();
303
304 // Reserve space for the nodes.
305 mesh.reserve_nodes(exio_helper->num_nodes);
306
307 // Read the node number map from the Exodus file. This is
308 // required if we want to preserve the numbering of nodes as it
309 // exists in the Exodus file. If the Exodus file does not contain
310 // a node_num_map, the identity map is returned by this call.
311 exio_helper->read_node_num_map();
312
313 // Read the element number map from the Exodus file. This is
314 // required if we want to preserve the numbering of elements as it
315 // exists in the Exodus file. If the Exodus file does not contain
316 // an elem_num_map, the identity map is returned by this call.
317 //
318 // We now do this before creating nodes, so if we have any spline
319 // nodes that need a NodeElem attached we can give them unused elem
320 // ids.
321 exio_helper->read_elem_num_map();
322
323 // Read any Bezier Extraction coefficient vectors from the file,
324 // such as might occur in an IsoGeometric Analysis (IGA) mesh.
325 exio_helper->read_bex_cv_blocks();
326
327 // If we have Rational Bezier weights, we'll need to
328 // store them.
329 unsigned char weight_index = 0;
330 const bool weights_exist = !exio_helper->w.empty();
331
332 // If we have Bezier extraction coefficients, we'll need to put
333 // NODEELEM elements on spline nodes, since our Rational Bezier
334 // elements will be connected to nodes derived from those; nothing
335 // else will be directly connected to the spline nodes.
336 const bool bex_cv_exist = !exio_helper->bex_dense_constraint_vecs.empty();
337
338 // If the user requests to set Node/Elem unique_ids based on the
339 // node/elem_num_maps, then it is very likely that those unique_ids
340 // will not be "unique" across the entire set of DofObjects (Nodes
341 // and Elems), although they should of course still be unique within
342 // the set of Nodes and the set of Elems on an individual basis. We
343 // therefore need to relax some of the uniqueness checking that is
344 // done in debug mode by setting the following flag on the Mesh.
347
348 // Even if weights don't exist, we still use RATIONAL_BERNSTEIN for
349 // Bezier-Bernstein BEX elements, we just use 1.0 as the weight on
350 // every node.
351 if (bex_cv_exist)
352 {
353 const Real default_weight = 1.0;
354 weight_index = cast_int<unsigned char>
355 (mesh.add_node_datum<Real>("rational_weight", true,
356 &default_weight));
358 mesh.set_default_mapping_data(weight_index);
359 }
360
361 std::unordered_map<const Node *, Elem *> spline_nodeelem_ptrs;
362
363 // Loop over the nodes, create Nodes with local processor_id 0.
364 for (auto i : make_range(exio_helper->num_nodes))
365 {
366 // Determine the libmesh node id implied by "i". The
367 // get_libmesh_node_id() helper function expects a 1-based
368 // Exodus node id, so we construct the "implied" Exodus node id
369 // from "i" by adding 1.
370 auto libmesh_node_id = exio_helper->get_libmesh_node_id(/*exodus_node_id=*/i+1);
371
372 // Catch the node that was added to the mesh
373 Node * added_node = mesh.add_point (Point(exio_helper->x[i], exio_helper->y[i], exio_helper->z[i]), libmesh_node_id);
374
375 // Sanity check: throw an error if the Mesh assigned an ID to
376 // the Node which does not match the libmesh_id we just determined.
377 libmesh_error_msg_if(added_node->id() != static_cast<unsigned>(libmesh_node_id),
378 "Error! Mesh assigned node ID "
379 << added_node->id()
380 << " which is different from the (zero-based) Exodus ID "
381 << libmesh_node_id
382 << "!");
383
384 // If the _set_unique_ids_from_maps flag is true, set the
385 // unique_id for "node", otherwise do nothing.
386 exio_helper->conditionally_set_node_unique_id(mesh, added_node, i);
387
388 // If we have a set of spline weights, these nodes are going to
389 // be used as control points for Bezier elements, and we need
390 // to attach a NodeElem to each to make sure it doesn't get
391 // flagged as an unused node.
392 if (weights_exist)
393 {
394 const auto w = exio_helper->w[i];
395 Point & p = *added_node;
396 p /= w; // Exodus Bezier Extraction stores spline nodes in projective space
397
398 added_node->set_extra_datum<Real>(weight_index, exio_helper->w[i]);
399 }
400
401 if (bex_cv_exist)
402 {
403 std::unique_ptr<Elem> elem = Elem::build(NODEELEM);
404
405 // Give the NodeElem ids at the end, so we can match any
406 // existing ids in the file for other elements
407 //
408 // We don't set the unique_id for this NodeElem here even if
409 // the user has set the _set_unique_ids_from_maps flag
410 // because these NodeElems don't have entries in the
411 // elem_num_map. Therefore, we just let the Mesh assign
412 // whatever unique_id is "next" as the Elem is added to the
413 // Mesh.
414 elem->set_id() = exio_helper->end_elem_id() + i;
415
416 elem->set_node(0, added_node);
417 Elem * added_elem = mesh.add_elem(std::move(elem));
418 spline_nodeelem_ptrs[added_node] = added_elem;
419 }
420 }
421
422 // This assert is no longer valid if the nodes are not numbered
423 // sequentially starting from 1 in the Exodus file.
424 // libmesh_assert_equal_to (static_cast<unsigned int>(exio_helper->num_nodes), mesh.n_nodes());
425
426 // Get information about all the element and edge blocks
427 exio_helper->read_block_info();
428
429 // Reserve space for the elements. Account for any NodeElem that
430 // have already been attached to spline control nodes.
431 mesh.reserve_elem(exio_helper->w.size() + exio_helper->num_elem);
432
433 // Read variables for extra integer IDs
434 std::vector<std::map<dof_id_type, Real>> elem_ids(extra_ids.size());
435 // We use the last time step to load the IDs
436 exio_helper->read_num_time_steps();
437 unsigned int last_step = exio_helper->num_time_steps;
438 for (auto i : index_range(extra_ids))
439 exio_helper->read_elemental_var_values(_extra_integer_vars[i], last_step, elem_ids[i]);
440
441 // Read in the element connectivity for each block.
442 int nelem_last_block = 0;
443
444 // If we're building Bezier elements from spline nodes, we need to
445 // calculate those elements' local nodes on the fly, and we'll be
446 // calculating them from constraint matrix columns, and we'll need
447 // to make sure that the same node is found each time it's
448 // calculated from multiple neighboring elements.
449 std::map<std::vector<std::pair<dof_id_type, Real>>, Node *> local_nodes;
450
451 // We'll set any spline NodeElem subdomain_id() values to exceed the
452 // maximum of subdomain_id() values set via Exodus block ids.
453 subdomain_id_type max_subdomain_id = std::numeric_limits<subdomain_id_type>::min();
454
455 // We've already added all the nodes explicitly specified in the
456 // file, but if we have spline nodes we may need to add assembly
457 // element nodes based on them. Add them contiguously so we're
458 // compatible with any subsequent code paths (including our
459 // ExodusII_IO::write()!) that don't support sparse ids.
461
462 // Loop over all the element blocks
463 for (int i=0; i<exio_helper->num_elem_blk; i++)
464 {
465 // Read the information for block i
466 exio_helper->read_elem_in_block (i);
467 const subdomain_id_type subdomain_id =
468 restrict_int<subdomain_id_type>(exio_helper->get_block_id(i));
469 max_subdomain_id = std::max(max_subdomain_id, subdomain_id);
470
471 // populate the map of names
472 std::string subdomain_name = exio_helper->get_block_name(i);
473 if (!subdomain_name.empty())
474 mesh.set_subdomain_name(subdomain_id, subdomain_name);
475
476 // Set any relevant node/edge maps for this element
477 const std::string type_str (exio_helper->get_elem_type());
478 const auto & conv = exio_helper->get_conversion(type_str);
479 const bool is_c0polygon = (conv.libmesh_elem_type() == C0POLYGON);
480 const bool is_c0polyhedron = (conv.libmesh_elem_type() == C0POLYHEDRON);
481 std::size_t c0polygon_connect_offset = 0;
482 std::size_t c0polyhedron_connect_offset = 0;
483
484 // Loop over all the faces in this block
485 int jmax = nelem_last_block+exio_helper->num_elem_this_blk;
486 for (int j=nelem_last_block; j<jmax; j++)
487 {
488 const int elem_num = j - nelem_last_block;
489 std::unique_ptr<Elem> uelem;
490
491 if (is_c0polygon)
492 {
493 const int n_elem_nodes = exio_helper->elem_node_counts[elem_num];
494 libmesh_error_msg_if(n_elem_nodes < 3,
495 "Error: Exodus NSIDED block element "
496 << elem_num
497 << " has only " << n_elem_nodes << " nodes.");
498
499 uelem = std::make_unique<C0Polygon>(cast_int<unsigned int>(n_elem_nodes));
500 }
501 else if (is_c0polyhedron)
502 {
503 const int n_faces = exio_helper->elem_face_counts[elem_num];
504 libmesh_error_msg_if(n_faces < 4,
505 "Error: Exodus NFACED block element "
506 << elem_num
507 << " has only " << n_faces << " faces.");
508
509 std::vector<std::shared_ptr<Polygon>>
510 sides(cast_int<std::size_t>(n_faces));
511
512 for (auto s : index_range(sides))
513 {
514 libmesh_assert_less(c0polyhedron_connect_offset,
515 exio_helper->connect.size());
516 const int exodus_face_id =
517 exio_helper->connect[c0polyhedron_connect_offset++];
518
519 libmesh_error_msg_if
520 (exodus_face_id <= 0 ||
521 exodus_face_id >
522 cast_int<int>(exio_helper->c0polyhedron_face_connect.size()),
523 "Error: Exodus NFACED block element "
524 << elem_num
525 << " references face ID " << exodus_face_id
526 << ", but the file contains "
527 << exio_helper->c0polyhedron_face_connect.size()
528 << " faces.");
529
530 const auto & face_nodes =
531 exio_helper->c0polyhedron_face_connect[exodus_face_id - 1];
532 auto side = std::make_shared<C0Polygon>
533 (cast_int<unsigned int>(face_nodes.size()));
534
535 for (auto n : index_range(face_nodes))
536 {
537 const auto libmesh_node_id =
538 exio_helper->get_libmesh_node_id(face_nodes[n]);
539 side->set_node(n, mesh.node_ptr(libmesh_node_id));
540 }
541
542 sides[s] = std::move(side);
543 }
544
545 std::unique_ptr<Node> mid_elem_node;
546 uelem = std::make_unique<C0Polyhedron>(sides, mid_elem_node);
547 if (mid_elem_node)
548 {
549 Node * added_node = mesh.add_node(std::move(mid_elem_node));
550 if (added_node->id() >= n_nodes)
551 n_nodes = added_node->id() + 1;
552 }
553 }
554 else
555 uelem = Elem::build(conv.libmesh_elem_type());
556
557 // Make sure that Exodus's number of nodes per Elem matches
558 // the number of Nodes for this type of Elem. We only check
559 // this for the first Elem in each block, since these values
560 // are the same for every Elem in the block.
561 if (!is_c0polygon && !is_c0polyhedron && !elem_num)
562 libmesh_error_msg_if(exio_helper->num_nodes_per_elem != static_cast<int>(uelem->n_nodes()),
563 "Error: Exodus file says "
564 << exio_helper->num_nodes_per_elem
565 << " nodes per Elem, but Elem type "
566 << Utility::enum_to_string(uelem->type())
567 << " has " << uelem->n_nodes() << " nodes.");
568
569 // Assign the current subdomain to this Elem
570 uelem->subdomain_id() = subdomain_id;
571
572 // Determine the libmesh elem id implied by "j". The
573 // ExodusII_IO_Helper::get_libmesh_elem_id() helper function
574 // expects a 1-based Exodus elem id, so we construct the
575 // "implied" Exodus elem id from "j" by adding 1.
576 auto libmesh_elem_id = exio_helper->get_libmesh_elem_id(/*exodus_elem_id=*/j+1);
577
578 uelem->set_id(libmesh_elem_id);
579
580 // Record that we have seen an element of dimension uelem->dim()
581 elems_of_dimension[uelem->dim()] = true;
582
583 // Catch the Elem pointer that the Mesh throws back
584 Elem * elem = mesh.add_elem(std::move(uelem));
585
586 // If the _set_unique_ids_from_maps flag is true, set the
587 // unique_id for "elem", otherwise do nothing.
588 exio_helper->conditionally_set_elem_unique_id(mesh, elem, j);
589
590 // If the Mesh assigned an ID different from the one we
591 // tried to give it, we should probably error.
592 libmesh_error_msg_if(elem->id() != static_cast<unsigned>(libmesh_elem_id),
593 "Error! Mesh assigned ID "
594 << elem->id()
595 << " which is different from the (zero-based) Exodus ID "
596 << libmesh_elem_id
597 << "!");
598
599 // Assign extra integer IDs
600 for (auto & id : extra_ids)
601 {
602 const Real v = elem_ids[id][elem->id()];
603
604 if (v == Real(-1))
605 {
607 continue;
608 }
609
610 // Ignore FE_INVALID here even if we've enabled FPEs; a
611 // thrown exception is preferred over an FPE signal.
612 FPEDisabler disable_fpes;
613 const long long iv = std::llround(v);
614
615 // Check if the real number is outside of the range we can
616 // convert exactly
617
618 long long max_representation = 1;
619 max_representation = (max_representation << std::min(std::numeric_limits<Real>::digits,
620 std::numeric_limits<double>::digits));
621 libmesh_error_msg_if(iv > max_representation,
622 "Error! An element integer value higher than "
623 << max_representation
624 << " was found! Exodus uses real numbers for storing element "
625 " integers, which can only represent integers from 0 to "
626 << max_representation
627 << ".");
628
629 libmesh_error_msg_if(iv < 0,
630 "Error! An element integer value less than -1"
631 << " was found! Exodus uses real numbers for storing element "
632 " integers, which can only represent integers from 0 to "
633 << max_representation
634 << ".");
635
636
637 elem->set_extra_integer(id, cast_int<dof_id_type>(iv));
638 }
639
640 // Set all the nodes for this element
641 //
642 // If we don't have any Bezier extraction operators, this
643 // is easy: we've already built all our nodes and just need
644 // to link to them.
645 if (is_c0polyhedron)
646 {
647 // Node pointers were already assigned while constructing
648 // the polygonal sides above.
649 }
650 else if (exio_helper->bex_cv_conn.empty())
651 {
652 const int n_nodes_this_elem =
653 is_c0polygon ?
654 exio_helper->elem_node_counts[elem_num] :
655 exio_helper->num_nodes_per_elem;
656
657 for (int k=0; k<n_nodes_this_elem; k++)
658 {
659 // Get index into this block's connectivity array
660 std::size_t gi =
661 is_c0polygon ?
662 c0polygon_connect_offset++ :
663 elem_num * exio_helper->num_nodes_per_elem + conv.get_node_map(k);
664 libmesh_assert_less(gi, exio_helper->connect.size());
665
666 // Get the 1-based Exodus node id from the "connect" array
667 auto exodus_node_id = exio_helper->connect[gi];
668
669 // Convert this index to a libMesh Node id
670 auto libmesh_node_id = exio_helper->get_libmesh_node_id(exodus_node_id);
671
672 // Set the node pointer in the Elem
673 elem->set_node(k, mesh.node_ptr(libmesh_node_id));
674 }
675 }
676 else // We have Bezier Extraction data
677 {
678 auto & constraint_rows = mesh.get_constraint_rows();
679
680 const DynaIO::ElementDefinition & dyna_elem_defn =
682 elem->dim(),
683 int(elem->default_order()));
684
685 std::vector<std::vector<Real>>
686 my_constraint_mat(exio_helper->bex_num_elem_cvs);
687 for (auto spline_node_index :
688 make_range(exio_helper->bex_num_elem_cvs))
689 {
690 my_constraint_mat[spline_node_index].resize(elem->n_nodes());
691
692 const auto & my_constraint_rows = exio_helper->bex_cv_conn[elem_num];
693 const unsigned long elem_coef_vec_index =
694 my_constraint_rows[spline_node_index] - 1; // Exodus isn't 0-based
695 const auto & my_vec = bex_constraint_vec(elem_coef_vec_index, *exio_helper);
696 for (auto elem_node_index :
697 make_range(elem->n_nodes()))
698 {
699 my_constraint_mat[spline_node_index][elem_node_index] =
700 my_vec[elem_node_index];
701 }
702
703 }
704
705 // The tailing entries in each element's connectivity
706 // vector are indices to Exodus constraint coefficient
707 // rows.
708
709 // Concatenating these rows gives a matrix with
710 // all the constraints for the element nodes: each
711 // column of that matrix is the constraint coefficients
712 // for the node associated with that column (via the
713 // Exodus numbering, not the libMesh numbering).
714 const auto & my_constraint_rows = exio_helper->bex_cv_conn[elem_num];
715
716 for (auto elem_node_index :
717 make_range(elem->n_nodes()))
718 {
719 // New finite element node data = dot product of
720 // constraint matrix columns with spline node data.
721 // Store each column's non-zero entries, along with
722 // the global spline node indices, as a key to
723 // identify shared finite element nodes.
724 std::vector<std::pair<dof_id_type, Real>> key;
725
726 for (auto spline_node_index :
727 make_range(exio_helper->bex_num_elem_cvs))
728 {
729 // Pick out a row of the element constraint matrix
730 const unsigned long elem_coef_vec_index =
731 my_constraint_rows[spline_node_index] - 1; // Exodus isn't 0-based
732
733 auto & coef_vec =
734 bex_constraint_vec(elem_coef_vec_index, *exio_helper);
735
736 // Get coef from this node's column intersect that row
737 const Real coef =
738 libmesh_vector_at(coef_vec, elem_node_index);
739
740 // Get the libMesh node corresponding to that row
741 const int gi = elem_num * exio_helper->bex_num_elem_cvs + spline_node_index;
742
743 // Get the 1-based Exodus node id from the "connect" array
744 auto exodus_node_id = exio_helper->connect[gi];
745
746 // Convert this index to a libMesh Node id
747 auto libmesh_node_id = exio_helper->get_libmesh_node_id(exodus_node_id);
748
749 if (coef != 0) // Ignore irrelevant spline nodes
750 key.emplace_back(libmesh_node_id, coef);
751 }
752
753 // Have we already created this node? Connect it.
754 if (const auto local_node_it = local_nodes.find(key);
755 local_node_it != local_nodes.end())
756 elem->set_node(dyna_elem_defn.nodes[elem_node_index], local_node_it->second);
757 // Have we not yet created this node? Construct it,
758 // along with its weight and libMesh constraint row,
759 // then connect it.
760 else
761 {
762 Point p(0);
763 Real w = 0;
764 std::vector<std::pair<std::pair<const Elem *, unsigned int>, Real>> constraint_row;
765
766 for (auto [libmesh_spline_node_id, coef] : key)
767 {
768 const Node & spline_node = mesh.node_ref(libmesh_spline_node_id);
769
770 p.add_scaled(spline_node, coef);
771 const Real spline_w = weights_exist ?
772 spline_node.get_extra_datum<Real>(weight_index) : 1;
773 w += coef * spline_w;
774
775 const Elem * nodeelem =
776 libmesh_map_find(spline_nodeelem_ptrs, &spline_node);
777 constraint_row.emplace_back(std::make_pair(nodeelem, 0), coef);
778 }
779
780 Node *n = mesh.add_point(p, n_nodes++);
781 if (weights_exist)
782 n->set_extra_datum<Real>(weight_index, w);
783
784 // If we're building disconnected Bezier
785 // extraction elements then we don't want to
786 // find the new nodes to reuse later; each
787 // finite element node will connect to only one
788 // element.
789 if (!_disc_bex)
790 local_nodes[key] = n;
791 elem->set_node(dyna_elem_defn.nodes[elem_node_index], n);
792
793 constraint_rows[n] = constraint_row;
794 }
795 }
796 }
797 }
798
799 libmesh_assert(!is_c0polygon ||
800 c0polygon_connect_offset == exio_helper->connect.size());
801 libmesh_assert(!is_c0polyhedron ||
802 c0polyhedron_connect_offset == exio_helper->connect.size());
803
804 // running sum of # of elements per block,
805 // (should equal total number of elements in the end)
806 nelem_last_block += exio_helper->num_elem_this_blk;
807 }
808
809 // Now we know enough to fix any spline NodeElem subdomains
810 max_subdomain_id++;
811 for (auto p : spline_nodeelem_ptrs)
812 p.second->subdomain_id() = max_subdomain_id;
813
814 // Read in edge blocks, storing information in the BoundaryInfo object.
815 // Edge blocks are treated as BCs.
816 exio_helper->read_edge_blocks(mesh);
817
818 // Set the mesh dimension to the largest encountered for an element
819 for (unsigned char i=0; i!=4; ++i)
820 if (elems_of_dimension[i])
822
823 // Read in sideset information -- this is useful for applying boundary conditions
824 {
825 // Get basic information about all sidesets
826 exio_helper->read_sideset_info();
827 int offset=0;
828 for (int i=0; i<exio_helper->num_side_sets; i++)
829 {
830 // Compute new offset
831 offset += (i > 0 ? exio_helper->num_sides_per_set[i-1] : 0);
832 exio_helper->read_sideset (i, offset);
833
834 std::string sideset_name = exio_helper->get_side_set_name(i);
835 if (!sideset_name.empty())
837 (cast_int<boundary_id_type>(exio_helper->get_side_set_id(i)))
838 = sideset_name;
839 }
840
841 for (auto e : index_range(exio_helper->elem_list))
842 {
843 // Call helper function to get the libmesh Elem id for the
844 // e'th entry in the current elem_list.
845 dof_id_type libmesh_elem_id =
846 exio_helper->get_libmesh_elem_id(exio_helper->elem_list[e]);
847
848 // Set any relevant node/edge maps for this element
849 Elem & elem = mesh.elem_ref(libmesh_elem_id);
850
851 const auto & conv = exio_helper->get_conversion(elem.type());
852
853 // Map the zero-based Exodus side numbering to the libmesh side numbering
854 unsigned int raw_side_index = exio_helper->side_list[e]-1;
855 std::size_t side_index_offset = conv.get_shellface_index_offset();
856
857 if (raw_side_index < side_index_offset)
858 {
859 // We assume this is a "shell face"
860 int mapped_shellface = raw_side_index;
861
862 // Check for errors
863 libmesh_error_msg_if(mapped_shellface < 0 || mapped_shellface >= 2,
864 "Bad 0-based shellface id: "
865 << mapped_shellface
866 << " detected in Exodus file "
867 << exio_helper->current_filename);
868
869 // Add this (elem,shellface,id) triplet to the BoundaryInfo object.
870 mesh.get_boundary_info().add_shellface (libmesh_elem_id,
871 cast_int<unsigned short>(mapped_shellface),
872 cast_int<boundary_id_type>(exio_helper->id_list[e]));
873 }
874 else
875 {
876 unsigned int side_index = static_cast<unsigned int>(raw_side_index - side_index_offset);
877 int mapped_side = conv.get_side_map(side_index);
878
879 // Check for errors
880 libmesh_error_msg_if(mapped_side == ExodusII_IO_Helper::Conversion::invalid_id,
881 "Invalid 1-based side id: "
882 << side_index
883 << " detected for "
885 << " in Exodus file "
886 << exio_helper->current_filename);
887
888 libmesh_error_msg_if(mapped_side < 0 ||
889 cast_int<unsigned int>(mapped_side) >= elem.n_sides(),
890 "Bad 0-based side id: "
891 << mapped_side
892 << " detected for "
894 << " in Exodus file "
895 << exio_helper->current_filename);
896
897 // Add this (elem,side,id) triplet to the BoundaryInfo object.
898 mesh.get_boundary_info().add_side (libmesh_elem_id,
899 cast_int<unsigned short>(mapped_side),
900 cast_int<boundary_id_type>(exio_helper->id_list[e]));
901 }
902 } // end for (elem_list)
903 } // end read sideset info
904
905 // Read in elemset information and apply to Mesh elements if present
906 {
907 exio_helper->read_elemset_info();
908
909 // Mimic behavior of sideset case where we store all the set
910 // information in a single array with offsets.
911 int offset=0;
912 for (int i=0; i<exio_helper->num_elem_sets; i++)
913 {
914 // Compute new offset
915 offset += (i > 0 ? exio_helper->num_elems_per_set[i-1] : 0);
916 exio_helper->read_elemset (i, offset);
917
918 // TODO: add support for elemset names
919 // std::string elemset_name = exio_helper->get_elem_set_name(i);
920 // if (!elemset_name.empty())
921 // mesh.get_boundary_info().elemset_name(cast_int<boundary_id_type>(exio_helper->get_elem_set_id(i))) = elemset_name;
922 }
923
924 // Debugging: print the concatenated list of elemset ids
925 // libMesh::out << "Concatenated list of elemset Elem ids (Exodus numbering):" << std::endl;
926 // for (const auto & id : exio_helper->elemset_list)
927 // libMesh::out << id << " ";
928 // libMesh::out << std::endl;
929
930 // Next we need to assign the elemset ids to the mesh using the
931 // Elem's "extra_integers" support, if we have any.
932 if (exio_helper->num_elem_all_elemsets)
933 {
934 // Build map from Elem -> {elemsets}. This is needed only
935 // temporarily to determine a unique set of elemset codes.
936 std::map<Elem *, MeshBase::elemset_type> elem_to_elemsets;
937 for (auto e : index_range(exio_helper->elemset_list))
938 {
939 // Call helper function to get the libmesh Elem id for the
940 // e'th entry in the current elemset_list.
941 dof_id_type libmesh_elem_id =
942 exio_helper->get_libmesh_elem_id(exio_helper->elemset_list[e]);
943
944 // Get a pointer to this Elem
945 Elem * elem = mesh.elem_ptr(libmesh_elem_id);
946
947 // Debugging:
948 // libMesh::out << "Elem " << elem->id() << " is in elemset " << exio_helper->elemset_id_list[e] << std::endl;
949
950 // Store elemset id in the map
951 elem_to_elemsets[elem].insert(exio_helper->elemset_id_list[e]);
952 }
953
954 // Create a set of unique elemsets
955 std::set<MeshBase::elemset_type> unique_elemsets;
956 for (const auto & pr : elem_to_elemsets)
957 unique_elemsets.insert(pr.second);
958
959 // Debugging: print the unique elemsets
960 // libMesh::out << "The set of unique elemsets which exist on the Mesh:" << std::endl;
961 // for (const auto & s : unique_elemsets)
962 // {
963 // for (const auto & elemset_id : s)
964 // libMesh::out << elemset_id << " ";
965 // libMesh::out << std::endl;
966 // }
967
968 // Enumerate the unique_elemsets and tell the mesh about them
969 dof_id_type code = 0;
970 for (const auto & s : unique_elemsets)
971 mesh.add_elemset_code(code++, s);
972
973 // Sanity check: make sure that MeshBase::n_elemsets() reports
974 // the expected value after calling MeshBase::add_elemset_code()
975 // one or more times.
976 libmesh_assert_msg(exio_helper->num_elem_sets == cast_int<int>(mesh.n_elemsets()),
977 "Error: mesh.n_elemsets() is " << mesh.n_elemsets()
978 << ", but mesh should have " << exio_helper->num_elem_sets << " elemsets.");
979
980 // Create storage for the extra integer on all Elems. Elems which
981 // are not in any set will use the default value of DofObject::invalid_id
982 unsigned int elemset_index =
983 mesh.add_elem_integer("elemset_code",
984 /*allocate_data=*/true);
985
986 // Store the appropriate extra_integer value on all Elems that need it.
987 for (const auto & [elem, s] : elem_to_elemsets)
988 elem->set_extra_integer(elemset_index, mesh.get_elemset_code(s));
989 }
990 } // done reading elemset info
991
992 // Read nodeset info
993 {
994 // This fills in the following fields of the helper for later use:
995 // nodeset_ids
996 // num_nodes_per_set
997 // num_node_df_per_set
998 // node_sets_node_index
999 // node_sets_dist_index
1000 // node_sets_node_list
1001 // node_sets_dist_fact
1002 exio_helper->read_all_nodesets();
1003
1004 for (int nodeset=0; nodeset<exio_helper->num_node_sets; nodeset++)
1005 {
1006 boundary_id_type nodeset_id =
1007 cast_int<boundary_id_type>(exio_helper->nodeset_ids[nodeset]);
1008
1009 std::string nodeset_name = exio_helper->get_node_set_name(nodeset);
1010 if (!nodeset_name.empty())
1011 mesh.get_boundary_info().nodeset_name(nodeset_id) = nodeset_name;
1012
1013 // Get starting index of node ids for current nodeset.
1014 unsigned int offset = exio_helper->node_sets_node_index[nodeset];
1015
1016 for (int i=0; i<exio_helper->num_nodes_per_set[nodeset]; ++i)
1017 {
1018 int exodus_node_id = exio_helper->node_sets_node_list[i + offset];
1019 auto libmesh_node_id = exio_helper->get_libmesh_node_id(exodus_node_id);
1020 mesh.get_boundary_info().add_node(libmesh_node_id, nodeset_id);
1021 }
1022 }
1023 }
1024
1025#if LIBMESH_DIM < 3
1026 libmesh_error_msg_if(mesh.mesh_dimension() > LIBMESH_DIM,
1027 "Cannot open dimension "
1028 << mesh.mesh_dimension()
1029 << " mesh file when configured without "
1030 << mesh.mesh_dimension()
1031 << "D support.");
1032#endif
1033}
1034
1035
1036
1038ExodusII_IO::read_header (const std::string & fname)
1039{
1040 // We will need the Communicator of the Mesh we were created with.
1042
1043 // Eventual return value
1044 ExodusHeaderInfo header_info;
1045
1046 // File I/O is done on processor 0, then broadcast to other procs
1047 if (mesh.processor_id() == 0)
1048 {
1049 // Open the exodus file in EX_READ mode
1050 exio_helper->open(fname.c_str(), /*read_only=*/true);
1051
1052 // Get header information from exodus file without updating the
1053 // Helper object's internal data structures.
1054 header_info = exio_helper->read_header();
1055
1056 // Close the file, we are now done with it. The goal is to keep the
1057 // exio_helper object unchanged while calling this function,
1058 // although it can't quite be marked "const" because we do have to
1059 // actually open/close the file. This way, it should be possible to
1060 // use the same ExodusII_IO object to read the headers of multiple
1061 // different mesh files.
1062 exio_helper->close();
1063 }
1064
1065 // Broadcast header_info to other procs before returning
1066 header_info.broadcast(mesh.comm());
1067
1068 // Return the information we read back to the user.
1069 return header_info;
1070}
1071
1072
1073
1074void ExodusII_IO::verbose (bool set_verbosity)
1075{
1076 _verbose = set_verbosity;
1077
1078 // Set the verbose flag in the helper object as well.
1079 exio_helper->verbose = _verbose;
1080}
1081
1082
1083
1085{
1086 _write_complex_abs = val;
1087}
1088
1090{
1092
1093 // Set this flag on the helper object as well. The helper needs to know about this
1094 // flag, since it sometimes needs to construct libmesh Node ids from nodal connectivity
1095 // arrays (see e.g. ExodusII_IO_Helper::read_edge_blocks()).
1096 exio_helper->set_unique_ids_from_maps = val;
1097}
1098
1100{
1101 exio_helper->use_mesh_dimension_instead_of_spatial_dimension(val);
1102}
1103
1104
1105
1107{
1108 exio_helper->write_as_dimension(dim);
1109}
1110
1111
1112
1114{
1115 libmesh_warning("This method may be deprecated in the future");
1116 exio_helper->set_coordinate_offset(p);
1117}
1118
1119
1120
1122{
1123 _append = val;
1124}
1125
1126
1127
1129{
1130 exio_helper->set_add_sides(val);
1131}
1132
1133
1134
1136{
1137 return exio_helper->get_add_sides();
1138}
1139
1140
1141
1142
1143const std::vector<Real> & ExodusII_IO::get_time_steps()
1144{
1145 libmesh_error_msg_if
1146 (!exio_helper->opened_for_reading,
1147 "ERROR, ExodusII file must be opened for reading before calling ExodusII_IO::get_time_steps()!");
1148
1149 exio_helper->read_time_steps();
1150 return exio_helper->time_steps;
1151}
1152
1153
1154
1156{
1157 libmesh_error_msg_if(!exio_helper->opened_for_reading && !exio_helper->opened_for_writing,
1158 "ERROR, ExodusII file must be opened for reading or writing before calling ExodusII_IO::get_num_time_steps()!");
1159
1160 exio_helper->read_num_time_steps();
1161 return exio_helper->num_time_steps;
1162}
1163
1164
1165
1167 std::string system_var_name,
1168 std::string exodus_var_name,
1169 unsigned int timestep)
1170{
1171 LOG_SCOPE("copy_nodal_solution()", "ExodusII_IO");
1172
1173 const unsigned int var_num = system.variable_number(system_var_name);
1174
1176
1177 libmesh_error_msg_if(mesh.allow_renumbering(),
1178 "ERROR, nodal data cannot be loaded if the mesh may be renumbered!");
1179
1180 // With Exodus files we only open them on processor 0, so that's the
1181 // where we have to do the data read too.
1182 if (system.comm().rank() == 0)
1183 {
1184 libmesh_error_msg_if(!exio_helper->opened_for_reading,
1185 "ERROR, ExodusII file must be opened for reading before copying a nodal solution!");
1186
1187 exio_helper->read_nodal_var_values(exodus_var_name, timestep);
1188 }
1189
1190 auto & node_var_value_map = exio_helper->nodal_var_values;
1191
1192 const bool serial_on_zero = mesh.is_serial_on_zero();
1193
1194 // If our mesh isn't serial, then non-root processors need to
1195 // request the data for their parts of the mesh and insert it
1196 // themselves.
1197 if (!serial_on_zero)
1198 {
1199 std::unordered_map<processor_id_type, std::vector<dof_id_type>> node_ids_to_request;
1200 if (this->processor_id() != 0)
1201 {
1202 std::vector<dof_id_type> node_ids;
1203 for (auto & node : mesh.local_node_ptr_range())
1204 node_ids.push_back(node->id());
1205 if (!node_ids.empty())
1206 node_ids_to_request[0] = std::move(node_ids);
1207 }
1208
1209 auto value_gather_functor =
1210 [& node_var_value_map]
1212 const std::vector<dof_id_type> & ids,
1213 std::vector<Real> & values)
1214 {
1215 const std::size_t query_size = ids.size();
1216 values.resize(query_size);
1217 for (std::size_t i=0; i != query_size; ++i)
1218 {
1219 if (const auto it = node_var_value_map.find(ids[i]);
1220 it != node_var_value_map.end())
1221 {
1222 values[i] = it->second;
1223 node_var_value_map.erase(it);
1224 }
1225 else
1226 values[i] = std::numeric_limits<Real>::quiet_NaN();
1227 }
1228 };
1229
1230 auto value_action_functor =
1231 [& node_var_value_map]
1233 const std::vector<dof_id_type> & ids,
1234 const std::vector<Real> & values)
1235 {
1236 const std::size_t query_size = ids.size();
1237 for (std::size_t i=0; i != query_size; ++i)
1238 if (!libmesh_isnan(values[i]))
1239 node_var_value_map[ids[i]] = values[i];
1240 };
1241
1242 Real * value_ex = nullptr;
1244 (system.comm(), node_ids_to_request, value_gather_functor,
1245 value_action_functor, value_ex);
1246 }
1247
1248 // Everybody inserts the data they've received. If we're
1249 // serial_on_zero then proc 0 inserts everybody's data and other
1250 // procs have empty map ranges.
1251 for (auto p : exio_helper->nodal_var_values)
1252 {
1253 dof_id_type i = p.first;
1254 const Node * node = MeshInput<MeshBase>::mesh().query_node_ptr(i);
1255
1256 if (node &&
1257 (serial_on_zero || node->processor_id() == system.processor_id()) &&
1258 node->n_comp(system.number(), var_num) > 0)
1259 {
1260 dof_id_type dof_index = node->dof_number(system.number(), var_num, 0);
1261
1262 // If the dof_index is local to this processor, set the value
1263 system.solution->set (dof_index, p.second);
1264 }
1265 }
1266
1267 system.solution->close();
1268 system.update();
1269}
1270
1271
1272
1274 std::string system_var_name,
1275 std::string exodus_var_name,
1276 unsigned int timestep)
1277{
1278 LOG_SCOPE("copy_elemental_solution()", "ExodusII_IO");
1279
1280 const unsigned int var_num = system.variable_number(system_var_name);
1281 // Assert that variable is an elemental data variable.
1282 //
1283 // NOTE: Currently, this reader is capable of reading only individual components of vector types,
1284 // and each must be written out to its own scalar elemental data variable.
1285 const auto & var_type = system.variable_type(var_num);
1286 libmesh_error_msg_if(!EquationSystems::is_elemental_data_fe_type(var_type),
1287 "Error! Trying to copy elemental solution into a variable that is not elemental data.");
1288
1290 const DofMap & dof_map = system.get_dof_map();
1291
1292 libmesh_error_msg_if(mesh.allow_renumbering(),
1293 "ERROR, elemental data cannot be loaded if the mesh may be renumbered!");
1294
1295 // Map from element ID to elemental variable value. We need to use
1296 // a map here rather than a vector (e.g. elem_var_values) since the
1297 // libmesh element numbering can contain "holes". This is the case
1298 // if we are reading elemental var values from an adaptively refined
1299 // mesh that has not been sequentially renumbered.
1300 std::map<dof_id_type, Real> elem_var_value_map;
1301
1302 // With Exodus files we only open them on processor 0, so that's the
1303 // where we have to do the data read too.
1304 if (system.comm().rank() == 0)
1305 {
1306 libmesh_error_msg_if(!exio_helper->opened_for_reading,
1307 "ERROR, ExodusII file must be opened for reading before copying an elemental solution!");
1308
1309 exio_helper->read_elemental_var_values(exodus_var_name, timestep, elem_var_value_map);
1310 }
1311
1312 const bool serial_on_zero = mesh.is_serial_on_zero();
1313
1314 // If our mesh isn't serial, then non-root processors need to
1315 // request the data for their parts of the mesh and insert it
1316 // themselves.
1317 if (!serial_on_zero)
1318 {
1319 std::unordered_map<processor_id_type, std::vector<dof_id_type>> elem_ids_to_request;
1320 if (this->processor_id() != 0)
1321 {
1322 std::vector<dof_id_type> elem_ids;
1323 for (auto & elem : mesh.active_local_element_ptr_range())
1324 elem_ids.push_back(elem->id());
1325
1326 if (!elem_ids.empty())
1327 elem_ids_to_request[0] = std::move(elem_ids);
1328 }
1329
1330 auto value_gather_functor =
1331 [& elem_var_value_map]
1333 const std::vector<dof_id_type> & ids,
1334 std::vector<Real> & values)
1335 {
1336 const std::size_t query_size = ids.size();
1337 values.resize(query_size);
1338 for (std::size_t i=0; i != query_size; ++i)
1339 {
1340 if (const auto it = elem_var_value_map.find(ids[i]);
1341 it != elem_var_value_map.end())
1342 {
1343 values[i] = it->second;
1344 elem_var_value_map.erase(it);
1345 }
1346 else
1347 values[i] = std::numeric_limits<Real>::quiet_NaN();
1348 }
1349 };
1350
1351 auto value_action_functor =
1352 [& elem_var_value_map]
1354 const std::vector<dof_id_type> & ids,
1355 const std::vector<Real> & values)
1356 {
1357 const std::size_t query_size = ids.size();
1358 for (std::size_t i=0; i != query_size; ++i)
1359 if (!libmesh_isnan(values[i]))
1360 elem_var_value_map[ids[i]] = values[i];
1361 };
1362
1363 Real * value_ex = nullptr;
1365 (system.comm(), elem_ids_to_request, value_gather_functor,
1366 value_action_functor, value_ex);
1367 }
1368
1369 std::map<dof_id_type, Real>::iterator
1370 it = elem_var_value_map.begin(),
1371 end = elem_var_value_map.end();
1372
1373 // Everybody inserts the data they've received. If we're
1374 // serial_on_zero then proc 0 inserts everybody's data and other
1375 // procs have empty map ranges.
1376 for (; it!=end; ++it)
1377 {
1378 const Elem * elem = mesh.query_elem_ptr(it->first);
1379
1380 if (elem && elem->n_comp(system.number(), var_num) > 0)
1381 {
1382 dof_id_type dof_index = elem->dof_number(system.number(), var_num, 0);
1383 if (serial_on_zero || dof_map.local_index(dof_index ))
1384 system.solution->set (dof_index, it->second);
1385 }
1386 }
1387
1388 system.solution->close();
1389 system.update();
1390}
1391
1393 std::vector<std::string> system_var_names,
1394 std::vector<std::string> exodus_var_names,
1395 unsigned int timestep)
1396{
1397 LOG_SCOPE("copy_scalar_solution()", "ExodusII_IO");
1398
1399 libmesh_error_msg_if(!exio_helper->opened_for_reading,
1400 "ERROR, ExodusII file must be opened for reading before copying a scalar solution!");
1401
1402 libmesh_error_msg_if(system_var_names.size() != exodus_var_names.size(),
1403 "ERROR, the number of system_var_names must match exodus_var_names.");
1404
1405 std::vector<Real> values_from_exodus;
1406 read_global_variable(exodus_var_names, timestep, values_from_exodus);
1407
1408#ifdef LIBMESH_HAVE_MPI
1409 if (this->n_processors() > 1)
1410 {
1411 const Parallel::MessageTag tag = this->comm().get_unique_tag(1);
1412 if (this->processor_id() == this->n_processors()-1)
1413 this->comm().receive(0, values_from_exodus, tag);
1414 if (this->processor_id() == 0)
1415 this->comm().send(this->n_processors()-1, values_from_exodus, tag);
1416 }
1417#endif
1418
1419 if (system.processor_id() == (system.n_processors()-1))
1420 {
1421 const DofMap & dof_map = system.get_dof_map();
1422
1423 for (auto i : index_range(system_var_names))
1424 {
1425 const unsigned int var_num = system.variable_scalar_number(system_var_names[i], 0);
1426
1427 std::vector<dof_id_type> SCALAR_dofs;
1428 dof_map.SCALAR_dof_indices(SCALAR_dofs, var_num);
1429
1430 system.solution->set (SCALAR_dofs[0], values_from_exodus[i]);
1431 }
1432 }
1433
1434 system.solution->close();
1435 system.update();
1436}
1437
1438void ExodusII_IO::read_elemental_variable(std::string elemental_var_name,
1439 unsigned int timestep,
1440 std::map<unsigned int, Real> & unique_id_to_value_map)
1441{
1442 LOG_SCOPE("read_elemental_variable()", "ExodusII_IO");
1443
1444 // Note that this function MUST be called before renumbering
1445 std::map<dof_id_type, Real> elem_var_value_map;
1446
1447 exio_helper->read_elemental_var_values(elemental_var_name, timestep, elem_var_value_map);
1448 for (auto & pr : elem_var_value_map)
1449 {
1450 const Elem * elem = MeshInput<MeshBase>::mesh().query_elem_ptr(pr.first);
1451 unique_id_to_value_map.emplace(elem->top_parent()->unique_id(), pr.second);
1452 }
1453}
1454
1455void ExodusII_IO::read_global_variable(std::vector<std::string> global_var_names,
1456 unsigned int timestep,
1457 std::vector<Real> & global_values)
1458{
1459 LOG_SCOPE("read_global_variable()", "ExodusII_IO");
1460
1461 std::size_t size = global_var_names.size();
1462 libmesh_error_msg_if(size == 0, "ERROR, empty list of global variables to read from the Exodus file.");
1463
1464 // read the values for all global variables
1465 std::vector<Real> values_from_exodus;
1466 exio_helper->read_var_names(ExodusII_IO_Helper::GLOBAL);
1467 exio_helper->read_global_values(values_from_exodus, timestep);
1468 std::vector<std::string> global_var_names_exodus = exio_helper->global_var_names;
1469
1470 if (values_from_exodus.size() == 0)
1471 return; // This will happen in parallel on procs that are not 0
1472
1473 global_values.clear();
1474 for (std::size_t i = 0; i != size; ++i)
1475 {
1476 // for each global variable in global_var_names, look the corresponding one in global_var_names_from_exodus
1477 // and fill global_values accordingly
1478 auto it = find(global_var_names_exodus.begin(), global_var_names_exodus.end(), global_var_names[i]);
1479 if (it != global_var_names_exodus.end())
1480 global_values.push_back(values_from_exodus[it - global_var_names_exodus.begin()]);
1481 else
1482 libmesh_error_msg("ERROR, Global variable " << global_var_names[i] << \
1483 " not found in Exodus file.");
1484 }
1485
1486}
1487
1489{
1490 LOG_SCOPE("write_element_data()", "ExodusII_IO");
1491
1492 // Be sure the file has been opened for writing!
1493 libmesh_error_msg_if(MeshOutput<MeshBase>::mesh().processor_id() == 0 && !exio_helper->opened_for_writing,
1494 "ERROR, ExodusII file must be initialized before outputting element variables.");
1495
1496 // This function currently only works on serialized meshes. We rely
1497 // on having a reference to a non-const MeshBase object from our
1498 // MeshInput parent class to construct a MeshSerializer object,
1499 // similar to what is done in ExodusII_IO::write(). Note that
1500 // calling ExodusII_IO::write_timestep() followed by
1501 // ExodusII_IO::write_element_data() when the underlying Mesh is a
1502 // DistributedMesh will result in an unnecessary additional
1503 // serialization/re-parallelization step.
1504 // The "true" specifies that we only need the mesh serialized to processor 0
1506
1507 // To be (possibly) filled with a filtered list of variable names to output.
1508 std::vector<std::string> names;
1509
1510 // If _output_variables is populated, build_elemental_solution_vector() will filter this list to
1511 // the variables that can be written as elemental data.
1512 if (_output_variables.size() > 0)
1513 names.assign(_output_variables.begin(), _output_variables.end());
1514
1515 // If we pass in a list of names to "build_elemental_solution_vector()"
1516 // it'll filter the variables coming back.
1517 std::vector<Number> soln;
1518 es.build_elemental_solution_vector(soln, names);
1519
1520 // Also, store the list of subdomains on which each variable is active
1521 std::vector<std::set<subdomain_id_type>> vars_active_subdomains;
1522 es.get_vars_active_subdomains(names, vars_active_subdomains);
1523
1524 if (soln.empty()) // If there is nothing to write just return
1525 return;
1526
1527 // The data must ultimately be written block by block. This means that this data
1528 // must be sorted appropriately.
1530 return;
1531
1533
1534#ifdef LIBMESH_USE_COMPLEX_NUMBERS
1535
1536 std::vector<std::string> complex_names =
1537 exio_helper->get_complex_names(names, _write_complex_abs);
1538
1539 std::vector<std::set<subdomain_id_type>>
1540 complex_vars_active_subdomains =
1541 exio_helper->get_complex_vars_active_subdomains(vars_active_subdomains,
1543 exio_helper->initialize_element_variables(complex_names, complex_vars_active_subdomains);
1544
1545 const std::vector<Real> complex_soln =
1546 complex_soln_components(soln, names.size(), _write_complex_abs,
1547 SolutionOrdering::variable_major,
1548 SolutionOrdering::variable_major);
1549
1550 exio_helper->write_element_values(mesh, complex_soln, _timestep, complex_vars_active_subdomains);
1551
1552#else
1553 exio_helper->initialize_element_variables(names, vars_active_subdomains);
1554 exio_helper->write_element_values(mesh, soln, _timestep, vars_active_subdomains);
1555#endif
1556}
1557
1558
1559
1560void
1562(const EquationSystems & es,
1563 const std::set<std::string> * system_names,
1564 const std::string & var_suffix)
1565{
1566 LOG_SCOPE("write_element_data_from_discontinuous_nodal_data()", "ExodusII_IO");
1567
1568 // Be sure that some other function has already opened the file and prepared it
1569 // for writing. This is the same behavior as the write_element_data() function
1570 // which we are trying to mimic.
1571 libmesh_error_msg_if(MeshOutput<MeshBase>::mesh().processor_id() == 0 && !exio_helper->opened_for_writing,
1572 "ERROR, ExodusII file must be initialized before outputting element variables.");
1573
1574 // This function currently only works on serialized meshes. The
1575 // "true" flag specifies that we only need the mesh serialized to
1576 // processor 0
1579 true);
1580
1581 // Note: in general we want to respect the contents of
1582 // _output_variables, only building a solution vector with values
1583 // from the requested variables. First build a list of all variable
1584 // names, then throw out ones that aren't in _output_variables, if
1585 // any.
1586 std::vector<std::string> var_names;
1587 es.build_variable_names (var_names, /*fetype=*/nullptr, system_names);
1588
1589 // Get a subset of all variable names that can be written directly
1590 // as elemental data. We treat those slightly differently since they
1591 // truly only have a single value per Elem.
1592 std::vector<std::string> elemental_var_names;
1593 if (!_output_variables.empty())
1594 elemental_var_names.assign(_output_variables.begin(), _output_variables.end());
1595 es.build_elemental_data_variable_names(elemental_var_names, system_names);
1596
1597 // Remove all names from var_names that are not in _output_variables.
1598 // Note: This approach avoids errors when the user provides invalid
1599 // variable names in _output_variables, as the code will not try to
1600 // write a variable that doesn't exist.
1601 if (!_output_variables.empty())
1602 {
1603 var_names.erase
1604 (std::remove_if
1605 (var_names.begin(),
1606 var_names.end(),
1607 [this](const std::string & name)
1608 {return !std::count(_output_variables.begin(),
1609 _output_variables.end(),
1610 name);}),
1611 var_names.end());
1612 }
1613
1614 // Build a solution vector, limiting the results to the variables in
1615 // var_names and the Systems in system_names, and only computing values
1616 // at the vertices.
1617 std::vector<Number> v;
1619 (v, system_names, &var_names, /*vertices_only=*/true);
1620
1621 // Get active subdomains for each variable in var_names.
1622 std::vector<std::set<subdomain_id_type>> vars_active_subdomains;
1623 es.get_vars_active_subdomains(var_names, vars_active_subdomains);
1624
1625 // Determine names of variables to write based on the number of
1626 // nodes/vertices the elements in different subdomains have.
1628 std::map<subdomain_id_type, unsigned int> subdomain_id_to_vertices_per_elem;
1629 for (const auto & elem : mesh.active_element_ptr_range())
1630 {
1631 // Try to insert key/value pair into the map. If this returns
1632 // false, check the returned iterator's value to make sure it
1633 // matches. It shouldn't actually be possible for this to fail
1634 // (since if the Mesh was like this it would have already
1635 // failed) but it doesn't hurt to be on the safe side.
1636 auto pr2 = subdomain_id_to_vertices_per_elem.emplace
1637 (elem->subdomain_id(), elem->n_vertices());
1638 libmesh_error_msg_if(!pr2.second && pr2.first->second != elem->n_vertices(),
1639 "Elem with different number of vertices found.");
1640 }
1641
1642 // Determine "derived" variable names. These names are created by
1643 // starting with the base variable name and appending the user's
1644 // variable_suffix (default: "_elem_node_") followed by a node id.
1645 //
1646 // Not every derived variable will be active on every subdomain,
1647 // even if the original variable _is_ active. Subdomains can have
1648 // different geometric element types (with differing numbers of
1649 // nodes), so some of the derived variable names will be inactive on
1650 // those subdomains.
1651 //
1652 // Since we would otherwise generate the same name once per
1653 // subdomain, we keep the list of names unique as we are creating
1654 // it. We can't use a std::set for this because we don't want the
1655 // variables names to be in a different order from the order
1656 // they were written in the call to: build_discontinuous_solution_vector()
1657 //
1658 // The list of derived variable names includes one for each vertex,
1659 // for higher-order elements we currently only write out vertex
1660 // values, but this could be changed in the future without too much
1661 // trouble.
1662 std::vector<std::string> derived_var_names;
1663
1664 // Keep track of mapping from derived_name to (orig_name, node_id)
1665 // pair. We will use this later to determine whether a given
1666 // variable is active on a given subdomain.
1667 std::map<std::string, std::pair<std::string, unsigned int>>
1668 derived_name_to_orig_name_and_node_id;
1669
1670 for (const auto & pr : subdomain_id_to_vertices_per_elem)
1671 {
1672 const subdomain_id_type sbd_id = pr.first;
1673 const unsigned int vertices_per_elem =
1674 subdomain_id_to_vertices_per_elem[sbd_id];
1675
1676 std::ostringstream oss;
1677 for (unsigned int n=0; n<vertices_per_elem; ++n)
1678 for (const auto & orig_var_name : var_names)
1679 {
1680 oss.str("");
1681 oss.clear();
1682 oss << orig_var_name << var_suffix << n;
1683 std::string derived_name = oss.str();
1684
1685 // Only add this var name if it's not already in the list.
1686 if (!std::count(derived_var_names.begin(), derived_var_names.end(), derived_name))
1687 {
1688 derived_var_names.push_back(derived_name);
1689 // Add entry for derived_name -> (orig_name, node_id) mapping.
1690 derived_name_to_orig_name_and_node_id[derived_name] =
1691 std::make_pair(orig_var_name, n);
1692 }
1693 }
1694 }
1695
1696 // For each derived variable name, determine whether it is active
1697 // based on how many nodes/vertices the elements in a given subdomain have,
1698 // and whether they were active on the subdomain to begin with.
1699 std::vector<std::set<subdomain_id_type>>
1700 derived_vars_active_subdomains(derived_var_names.size());
1701
1702 // A new data structure for keeping track of a list of variable names
1703 // that are in the discontinuous solution vector on each subdomain. Used
1704 // for indexing. Note: if a variable was inactive at the System level,
1705 // an entry for it will still be in the discontinuous solution vector,
1706 // but it will just have a value of zero. On the other hand, when we
1707 // create the derived variable names some of them are "inactive" on
1708 // different subdomains in the sense that they don't exist at all, i.e.
1709 // there is no zero padding for them. We need to be able to distinguish
1710 // between these two types in order to do the indexing into this vector
1711 // correctly.
1712 std::map<subdomain_id_type, std::vector<std::string>>
1713 subdomain_to_var_names;
1714
1715 for (auto derived_var_id : index_range(derived_var_names))
1716 {
1717 const auto & derived_name = derived_var_names[derived_var_id];
1718 const auto & [orig_name, node_id] =
1719 libmesh_map_find (derived_name_to_orig_name_and_node_id,
1720 derived_name);
1721
1722 // For each subdomain, determine whether the current variable
1723 // should be active on that subdomain.
1724 for (const auto & pr : subdomain_id_to_vertices_per_elem)
1725 {
1726 // Convenience variables for the current subdomain and the
1727 // number of nodes elements in this subdomain have.
1728 subdomain_id_type sbd_id = pr.first;
1729 unsigned int vertices_per_elem_this_sbd =
1730 subdomain_id_to_vertices_per_elem[sbd_id];
1731
1732 // Check whether variable orig_name was active on this
1733 // subdomain to begin with by looking in the
1734 // vars_active_subdomains container. We assume that the
1735 // location of orig_name in the var_names vector matches its
1736 // index in the vars_active_subdomains container.
1737 auto var_loc = std::find(var_names.begin(), var_names.end(), orig_name);
1738 libmesh_error_msg_if(var_loc == var_names.end(),
1739 "Variable " << orig_name << " somehow not found in var_names array.");
1740 auto var_id = std::distance(var_names.begin(), var_loc);
1741
1742 // The derived_var will only be active if this subdomain has
1743 // enough vertices for that to be the case.
1744 if (node_id < vertices_per_elem_this_sbd)
1745 {
1746 // Regardless of whether the original variable was not active on this subdomain,
1747 // the discontinuous solution vector will have zero padding for it, and
1748 // we will need to account for it. Therefore it should still be added to
1749 // the subdomain_to_var_names data structure!
1750 subdomain_to_var_names[sbd_id].push_back(derived_name);
1751
1752 // If the original variable was not active on the
1753 // current subdomain, it should not be added to the
1754 // derived_vars_active_subdomains data structure, since
1755 // it will not be written to the Exodus file.
1756
1757 // Determine if the original variable was active on the
1758 // current subdomain.
1759 bool orig_var_active =
1760 (vars_active_subdomains[var_id].empty() ||
1761 vars_active_subdomains[var_id].count(sbd_id));
1762
1763 // And only if it was, add it to the
1764 // derived_vars_active_subdomains data structure.
1765 if (orig_var_active)
1766 derived_vars_active_subdomains[derived_var_id].insert(sbd_id);
1767 }
1768 } // end loop over subdomain_id_to_vertices_per_elem
1769 } // end loop over derived_var_names
1770
1771 // At this point we've built the "true" list of derived names, but
1772 // if there are any elemental data variables in this list, we now want
1773 // to remove all but one copy of them from the derived_var_names list,
1774 // and rename them in (but not remove them from) the subdomain_to_var_names
1775 // list, and then update the derived_vars_active_subdomains containers
1776 // before finally calling the Exodus helper functions.
1777 for (auto & derived_var_name : derived_var_names)
1778 {
1779 // Get the original name associated with this derived name.
1780 const auto & name_and_id =
1781 libmesh_map_find (derived_name_to_orig_name_and_node_id,
1782 derived_var_name);
1783
1784 // Convenience variables for the map entry's contents.
1785 const std::string & orig_name = name_and_id.first;
1786
1787 // Was the original name an elemental data variable?
1788 if (std::count(elemental_var_names.begin(),
1789 elemental_var_names.end(),
1790 orig_name))
1791 {
1792 // Rename this variable in the subdomain_to_var_names vectors.
1793 for (auto & pr : subdomain_to_var_names)
1794 {
1795 // Reference to ordered list of variable names on this subdomain.
1796 auto & name_vec = pr.second;
1797
1798 auto name_vec_it =
1799 std::find(name_vec.begin(),
1800 name_vec.end(),
1801 derived_var_name);
1802
1803 if (name_vec_it != name_vec.end())
1804 {
1805 // Actually rename it back to the orig_name, dropping
1806 // the "_elem_corner_" stuff.
1807 *name_vec_it = orig_name;
1808 }
1809 }
1810
1811 // Finally, rename the variable in the derived_var_names vector itself.
1812 derived_var_name = orig_name;
1813 } // if (elemental data)
1814 } // end loop over derived names
1815
1816 // Now remove duplicate entries from derived_var_names after the first.
1817 // Also update the derived_vars_active_subdomains container in a consistent way.
1818 {
1819 std::vector<std::string> derived_var_names_edited;
1820 std::vector<std::set<subdomain_id_type>> derived_vars_active_subdomains_edited;
1821 std::vector<unsigned int> found_first(elemental_var_names.size());
1822
1823 for (auto i : index_range(derived_var_names))
1824 {
1825 const auto & derived_var_name = derived_var_names[i];
1826 const auto & active_set = derived_vars_active_subdomains[i];
1827
1828 // Determine whether we will keep this derived variable name in
1829 // the final container.
1830 bool keep = true;
1831 for (auto j : index_range(elemental_var_names))
1832 if (derived_var_name == elemental_var_names[j])
1833 {
1834 if (!found_first[j])
1835 found_first[j] = 1;
1836
1837 else
1838 keep = false;
1839 }
1840
1841 // We also don't keep variables that are not active on any subdomains.
1842 // Contrary to other uses of the var_active_subdomains container where
1843 // the empty set means "all" subdomains, here it really means "none".
1844 if (active_set.empty())
1845 keep = false;
1846
1847 if (keep)
1848 {
1849 derived_var_names_edited.push_back(derived_var_name);
1850 derived_vars_active_subdomains_edited.push_back(active_set);
1851 }
1852 }
1853
1854 // We built the filtered ranges, now swap them with the originals.
1855 derived_var_names.swap(derived_var_names_edited);
1856 derived_vars_active_subdomains.swap(derived_vars_active_subdomains_edited);
1857 }
1858
1859#ifdef LIBMESH_USE_COMPLEX_NUMBERS
1860 // Build complex variable names "r_foo", "i_foo", "a_foo" and the lists of
1861 // subdomains on which they are active.
1862 auto complex_var_names =
1863 exio_helper->get_complex_names(derived_var_names,
1865 auto complex_vars_active_subdomains =
1866 exio_helper->get_complex_vars_active_subdomains(derived_vars_active_subdomains,
1868 auto complex_subdomain_to_var_names =
1869 exio_helper->get_complex_subdomain_to_var_names(subdomain_to_var_names,
1871
1872 // Make expanded version of vector "v" in which each entry in the
1873 // original expands to an ("r_", "i_", "a_") triple.
1874 // "nco" is the number of complex outputs, which depends on whether
1875 // or not we are writing out the complex magnitudes.
1876 std::vector<Real> complex_v;
1877 int nco = _write_complex_abs ? 3 : 2;
1878 complex_v.reserve(nco * v.size());
1879 for (const auto & val : v)
1880 {
1881 complex_v.push_back(val.real());
1882 complex_v.push_back(val.imag());
1884 complex_v.push_back(std::abs(val));
1885 }
1886
1887 // Finally, initialize storage for the variables and write them to file.
1888 exio_helper->initialize_element_variables
1889 (complex_var_names, complex_vars_active_subdomains);
1890 exio_helper->write_element_values_element_major
1891 (mesh, complex_v, _timestep,
1892 complex_vars_active_subdomains,
1893 complex_var_names,
1894 complex_subdomain_to_var_names);
1895#else
1896
1897 // Call function which writes the derived variable names to the
1898 // Exodus file.
1899 exio_helper->initialize_element_variables(derived_var_names, derived_vars_active_subdomains);
1900
1901 // ES::build_discontinuous_solution_vector() creates a vector with
1902 // an element-major ordering, so call Helper::write_element_values()
1903 // passing false for the last argument.
1904 exio_helper->write_element_values_element_major
1905 (mesh, v, _timestep,
1906 derived_vars_active_subdomains,
1907 derived_var_names,
1908 subdomain_to_var_names);
1909#endif
1910}
1911
1912
1913
1914void ExodusII_IO::write_nodal_data (const std::string & fname,
1915 const std::vector<Number> & soln,
1916 const std::vector<std::string> & names)
1917{
1918 LOG_SCOPE("write_nodal_data()", "ExodusII_IO");
1919
1921
1922 int num_vars = cast_int<int>(names.size());
1923 dof_id_type num_nodes = mesh.n_nodes();
1924
1925 // The names of the variables to be output
1926 std::vector<std::string> output_names;
1927
1929 output_names = _output_variables;
1930 else
1931 output_names = names;
1932
1933#ifdef LIBMESH_USE_COMPLEX_NUMBERS
1934 std::vector<std::string> complex_names =
1935 exio_helper->get_complex_names(output_names,
1937
1938 // Call helper function for opening/initializing data, giving it the
1939 // complex variable names
1940 this->write_nodal_data_common(fname, complex_names, /*continuous=*/true);
1941#else
1942 // Call helper function for opening/initializing data
1943 this->write_nodal_data_common(fname, output_names, /*continuous=*/true);
1944#endif
1945
1946 if (mesh.processor_id())
1947 return;
1948
1949 // This will count the number of variables actually output
1950 for (int c=0; c<num_vars; c++)
1951 {
1952 std::stringstream name_to_find;
1953
1954 std::vector<std::string>::iterator pos =
1955 std::find(output_names.begin(), output_names.end(), names[c]);
1956 if (pos == output_names.end())
1957 continue;
1958
1959 unsigned int variable_name_position =
1960 cast_int<unsigned int>(pos - output_names.begin());
1961
1962 // Set up temporary vectors to be passed to Exodus to write the
1963 // nodal values for a single variable at a time.
1964#ifdef LIBMESH_USE_REAL_NUMBERS
1965 std::vector<Number> cur_soln;
1966
1967 // num_nodes is either exactly how much space we will need for
1968 // each vector, or a safe upper bound for the amount of memory
1969 // we will require when there are gaps in the numbering.
1970 cur_soln.reserve(num_nodes);
1971#else
1972 std::vector<Real> real_parts;
1973 std::vector<Real> imag_parts;
1974 std::vector<Real> magnitudes;
1975 real_parts.reserve(num_nodes);
1976 imag_parts.reserve(num_nodes);
1978 magnitudes.reserve(num_nodes);
1979#endif
1980
1981 // There could be gaps in soln based on node numbering, but in
1982 // serial the empty numbers are left empty.
1983 // There could also be offsets in soln based on "fake" nodes
1984 // inserted on each processor (because NumericVector indices
1985 // have to be contiguous); the helper keeps track of those.
1986 // We now copy the proper solution values contiguously into
1987 // "cur_soln", removing the gaps.
1988 for (const auto & node : mesh.node_ptr_range())
1989 {
1990 const dof_id_type idx =
1991 (exio_helper->node_id_to_vec_id(node->id()))
1992 * num_vars + c;
1993#ifdef LIBMESH_USE_REAL_NUMBERS
1994 cur_soln.push_back(soln[idx]);
1995#else
1996 real_parts.push_back(soln[idx].real());
1997 imag_parts.push_back(soln[idx].imag());
1999 magnitudes.push_back(std::abs(soln[idx]));
2000#endif
2001 }
2002
2003 // If we're adding extra sides, we need to add their data too.
2004 //
2005 // Because soln was created from a parallel NumericVector, its
2006 // numbering was contiguous on each processor; we need to use
2007 // the same offsets here, and we need to loop through elements
2008 // from earlier ranks first.
2009 if (exio_helper->get_add_sides())
2010 {
2011 std::vector<std::vector<const Elem *>>
2012 elems_by_pid(mesh.n_processors());
2013
2014 for (const auto & elem : mesh.active_element_ptr_range())
2015 elems_by_pid[elem->processor_id()].push_back(elem);
2016
2017 for (auto p : index_range(elems_by_pid))
2018 {
2019 dof_id_type global_idx =
2020 exio_helper->added_node_offset_on(p) * num_vars + c;
2021 for (const Elem * elem : elems_by_pid[p])
2022 {
2023 for (auto s : elem->side_index_range())
2024 {
2026 continue;
2027
2028 const std::vector<unsigned int> side_nodes =
2029 elem->nodes_on_side(s);
2030
2031 for (auto n : index_range(side_nodes))
2032 {
2033 libmesh_ignore(n);
2034 libmesh_assert_less(global_idx, soln.size());
2035#ifdef LIBMESH_USE_REAL_NUMBERS
2036 cur_soln.push_back(soln[global_idx]);
2037#else
2038 real_parts.push_back(soln[global_idx].real());
2039 imag_parts.push_back(soln[global_idx].imag());
2041 magnitudes.push_back(std::abs(soln[global_idx]));
2042#endif
2043 global_idx += num_vars;
2044 }
2045 }
2046 }
2047 }
2048 }
2049
2050 // Finally, actually call the Exodus API to write to file.
2051#ifdef LIBMESH_USE_REAL_NUMBERS
2052 exio_helper->write_nodal_values(variable_name_position+1, cur_soln, _timestep);
2053#else
2054 int nco = _write_complex_abs ? 3 : 2;
2055 exio_helper->write_nodal_values(nco*variable_name_position+1, real_parts, _timestep);
2056 exio_helper->write_nodal_values(nco*variable_name_position+2, imag_parts, _timestep);
2058 exio_helper->write_nodal_values(3*variable_name_position+3, magnitudes, _timestep);
2059#endif
2060
2061 }
2062}
2063
2064
2065
2066
2067void ExodusII_IO::write_information_records (const std::vector<std::string> & records)
2068{
2070 return;
2071
2072 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2073 "ERROR, ExodusII file must be initialized before outputting information records.");
2074
2075 exio_helper->write_information_records(records);
2076}
2077
2078
2079
2080void ExodusII_IO::write_global_data (const std::vector<Number> & soln,
2081 const std::vector<std::string> & names)
2082{
2083 LOG_SCOPE("write_global_data()", "ExodusII_IO");
2084
2086 return;
2087
2088 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2089 "ERROR, ExodusII file must be initialized before outputting global variables.");
2090
2091#ifdef LIBMESH_USE_COMPLEX_NUMBERS
2092
2093 std::vector<std::string> complex_names =
2094 exio_helper->get_complex_names(names,
2096
2097 exio_helper->initialize_global_variables(complex_names);
2098
2099 const std::vector<Real> complex_soln =
2100 complex_soln_components(soln, names.size(), _write_complex_abs,
2101 SolutionOrdering::variable_major,
2102 SolutionOrdering::variable_major);
2103
2104 exio_helper->write_global_values(complex_soln, _timestep);
2105
2106#else
2107 exio_helper->initialize_global_variables(names);
2108 exio_helper->write_global_values(soln, _timestep);
2109#endif
2110}
2111
2112
2113
2114void ExodusII_IO::write_timestep (const std::string & fname,
2115 const EquationSystems & es,
2116 const int timestep,
2117 const Real time,
2118 const std::set<std::string> * system_names)
2119{
2120 _timestep = timestep;
2121 MeshOutput<MeshBase>::write_equation_systems(fname,es,system_names);
2122
2124 return;
2125
2126 exio_helper->write_timestep(timestep, time);
2127}
2128
2129
2130void ExodusII_IO::write_equation_systems (const std::string & fname,
2131 const EquationSystems & es,
2132 const std::set<std::string> * system_names)
2133{
2134 write_timestep(fname, es, 1, 0, system_names);
2135}
2136
2137
2139{
2140 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2141 "ERROR, ExodusII file must be opened for writing "
2142 "before calling ExodusII_IO::write_elemsets()!");
2143
2145 exio_helper->write_elemsets(mesh);
2146}
2147
2148void
2150write_sideset_data(int timestep,
2151 const std::vector<std::string> & var_names,
2152 const std::vector<std::set<boundary_id_type>> & side_ids,
2153 const std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals)
2154{
2155 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2156 "ERROR, ExodusII file must be opened for writing "
2157 "before calling ExodusII_IO::write_sideset_data()!");
2158
2160 exio_helper->write_sideset_data(mesh, timestep, var_names, side_ids, bc_vals);
2161}
2162
2163
2164
2165void
2167read_sideset_data(int timestep,
2168 std::vector<std::string> & var_names,
2169 std::vector<std::set<boundary_id_type>> & side_ids,
2170 std::vector<std::map<BoundaryInfo::BCTuple, Real>> & bc_vals)
2171{
2172 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2173 "ERROR, ExodusII file must be opened for reading "
2174 "before calling ExodusII_IO::read_sideset_data()!");
2175
2177 exio_helper->read_sideset_data(mesh, timestep, var_names, side_ids, bc_vals);
2178}
2179
2180
2181
2182void
2184get_sideset_data_indices (std::map<BoundaryInfo::BCTuple, unsigned int> & bc_array_indices)
2185
2186{
2187 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2188 "ERROR, ExodusII file must be opened for reading "
2189 "before calling ExodusII_IO::get_sideset_data_indices()!");
2190
2192 exio_helper->get_sideset_data_indices(mesh, bc_array_indices);
2193}
2194
2195void
2197get_nodeset_data_indices (std::map<BoundaryInfo::NodeBCTuple, unsigned int> & bc_array_indices)
2198{
2199 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2200 "ERROR, ExodusII file must be opened for reading "
2201 "before calling ExodusII_IO::get_nodeset_data_indices()!");
2202
2203 exio_helper->get_nodeset_data_indices(bc_array_indices);
2204}
2205
2206void
2208write_nodeset_data (int timestep,
2209 const std::vector<std::string> & var_names,
2210 const std::vector<std::set<boundary_id_type>> & node_boundary_ids,
2211 const std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> & bc_vals)
2212{
2213 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2214 "ERROR, ExodusII file must be opened for writing "
2215 "before calling ExodusII_IO::write_nodeset_data()!");
2216
2217 exio_helper->write_nodeset_data(timestep, var_names, node_boundary_ids, bc_vals);
2218}
2219
2220
2221
2222void
2224read_nodeset_data (int timestep,
2225 std::vector<std::string> & var_names,
2226 std::vector<std::set<boundary_id_type>> & node_boundary_ids,
2227 std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> & bc_vals)
2228{
2229 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2230 "ERROR, ExodusII file must be opened for reading "
2231 "before calling ExodusII_IO::read_nodeset_data()!");
2232
2233 exio_helper->read_nodeset_data(timestep, var_names, node_boundary_ids, bc_vals);
2234}
2235
2236void
2238write_elemset_data (int timestep,
2239 const std::vector<std::string> & var_names,
2240 const std::vector<std::set<elemset_id_type>> & elemset_ids_in,
2241 const std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> & elemset_vals)
2242{
2243 libmesh_error_msg_if(!exio_helper->opened_for_writing,
2244 "ERROR, ExodusII file must be opened for writing "
2245 "before calling ExodusII_IO::write_elemset_data()!");
2246
2247 exio_helper->write_elemset_data(timestep, var_names, elemset_ids_in, elemset_vals);
2248}
2249
2250
2251
2252void
2254read_elemset_data (int timestep,
2255 std::vector<std::string> & var_names,
2256 std::vector<std::set<elemset_id_type>> & elemset_ids_in,
2257 std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> & elemset_vals)
2258{
2259 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2260 "ERROR, ExodusII file must be opened for reading "
2261 "before calling ExodusII_IO::read_elemset_data()!");
2262
2263 exio_helper->read_elemset_data(timestep, var_names, elemset_ids_in, elemset_vals);
2264}
2265
2266void
2267ExodusII_IO::get_elemset_data_indices (std::map<std::pair<dof_id_type, elemset_id_type>, unsigned int> & elemset_array_indices)
2268{
2269 libmesh_error_msg_if(!exio_helper->opened_for_reading,
2270 "ERROR, ExodusII file must be opened for reading "
2271 "before calling ExodusII_IO::get_elemset_data_indices()!");
2272
2273 exio_helper->get_elemset_data_indices(elemset_array_indices);
2274}
2275
2276
2277void ExodusII_IO::write (const std::string & fname)
2278{
2279 LOG_SCOPE("write()", "ExodusII_IO");
2280
2282
2283 // We may need to gather a DistributedMesh to output it, making that
2284 // const qualifier in our constructor a dirty lie
2285 // The "true" specifies that we only need the mesh serialized to processor 0
2286 MeshSerializer serialize
2287 (const_cast<MeshBase &>(mesh),
2289
2290 libmesh_assert( !exio_helper->opened_for_writing );
2291
2292 // If the user has set the append flag here, it doesn't really make
2293 // sense: the intent of this function is to write a Mesh with no
2294 // data, while "appending" is really intended to add data to an
2295 // existing file. If we're verbose, print a message to this effect.
2296 if (_append && _verbose)
2297 libmesh_warning("Warning: Appending in ExodusII_IO::write() does not make sense.\n"
2298 "Creating a new file instead!");
2299
2300 exio_helper->create(fname);
2301 exio_helper->initialize(fname,mesh);
2302 exio_helper->write_nodal_coordinates(mesh);
2303 exio_helper->write_elements(mesh);
2304 exio_helper->write_sidesets(mesh);
2305 exio_helper->write_nodesets(mesh);
2306 exio_helper->write_elemsets(mesh);
2307
2309 libmesh_warning("Warning: Mesh contains edge boundary IDs, but these "
2310 "are not supported by the ExodusII format.");
2311}
2312
2313
2314
2315void ExodusII_IO::write_nodal_data_discontinuous (const std::string & fname,
2316 const std::vector<Number> & soln,
2317 const std::vector<std::string> & names)
2318{
2319 LOG_SCOPE("write_nodal_data_discontinuous()", "ExodusII_IO");
2320
2322
2323#ifdef LIBMESH_USE_COMPLEX_NUMBERS
2324
2325 std::vector<std::string> complex_names =
2326 exio_helper->get_complex_names(names,
2328
2329 // Call helper function for opening/initializing data, giving it the
2330 // complex variable names
2331 this->write_nodal_data_common(fname, complex_names, /*continuous=*/false);
2332#else
2333 // Call helper function for opening/initializing data
2334 this->write_nodal_data_common(fname, names, /*continuous=*/false);
2335#endif
2336
2337 if (mesh.processor_id())
2338 return;
2339
2340 int num_vars = cast_int<int>(names.size());
2341 libmesh_assert_equal_to(soln.size() % num_vars, 0);
2342 int num_nodes = soln.size() / num_vars;
2343 libmesh_assert_equal_to(exio_helper->num_nodes, num_nodes);
2344
2345#ifndef NDEBUG
2346 if (!this->get_add_sides())
2347 {
2348 int num_real_nodes = 0;
2349 for (const auto & elem : mesh.active_element_ptr_range())
2350 num_real_nodes += elem->n_nodes();
2351 libmesh_assert_equal_to(num_real_nodes, num_nodes);
2352 }
2353#endif
2354
2355 for (int c=0; c<num_vars; c++)
2356 {
2357#ifdef LIBMESH_USE_COMPLEX_NUMBERS
2358 std::vector<Real> real_parts(num_nodes);
2359 std::vector<Real> imag_parts(num_nodes);
2360 std::vector<Real> magnitudes;
2362 magnitudes.resize(num_nodes);
2363
2364 // The number of complex outputs depends on whether or not we are
2365 // writing out the absolute values.
2366 int nco = _write_complex_abs ? 3 : 2;
2367
2368 for (int i=0; i<num_nodes; ++i)
2369 {
2370 real_parts[i] = soln[i*num_vars + c].real();
2371 imag_parts[i] = soln[i*num_vars + c].imag();
2373 magnitudes[i] = std::abs(soln[i*num_vars + c]);
2374 }
2375 exio_helper->write_nodal_values(nco*c+1, real_parts, _timestep);
2376 exio_helper->write_nodal_values(nco*c+2, imag_parts, _timestep);
2378 exio_helper->write_nodal_values(3*c+3, magnitudes, _timestep);
2379#else
2380 // Copy out this variable's solution
2381 std::vector<Number> cur_soln(num_nodes);
2382
2383 for (int i=0; i<num_nodes; i++)
2384 cur_soln[i] = soln[i*num_vars + c];
2385
2386 exio_helper->write_nodal_values(c+1,cur_soln,_timestep);
2387#endif
2388 }
2389}
2390
2391
2392
2394 const std::vector<std::string> & names,
2395 bool continuous)
2396{
2398
2399 // This function can be called multiple times, we only want to open
2400 // the ExodusII file the first time it's called.
2401 if (!exio_helper->opened_for_writing)
2402 {
2403 // If we're appending, open() the file with read_only=false,
2404 // otherwise create() it and write the contents of the mesh to
2405 // it.
2406 if (_append)
2407 {
2408 // We do our writing only from proc 0, to avoid race
2409 // conditions with Exodus 8
2411 {
2412 exio_helper->open(fname.c_str(), /*read_only=*/false);
2413 // If we're appending, it's not valid to call exio_helper->initialize()
2414 // or exio_helper->initialize_nodal_variables(), but we do need to set up
2415 // certain aspects of the Helper object itself, such as the number of nodes
2416 // and elements. We do that by reading the header...
2417 exio_helper->read_and_store_header_info();
2418
2419 // ...and reading the block info
2420 exio_helper->read_block_info();
2421 }
2422 // Keep other processors aware of what we've done on root
2423 else
2424 {
2425 exio_helper->opened_for_writing = true;
2426 exio_helper->current_filename = fname;
2427 }
2428 }
2429 else
2430 {
2431 exio_helper->create(fname);
2432
2433 // But some of our write calls are parallel-only, due to
2434 // calls to parallel-only getter functions.
2435 exio_helper->initialize(fname, mesh, !continuous);
2436
2437 exio_helper->write_nodal_coordinates(mesh, !continuous);
2438 exio_helper->write_elements(mesh, !continuous);
2439
2440 exio_helper->write_sidesets(mesh);
2441 exio_helper->write_nodesets(mesh);
2442 exio_helper->write_elemsets(mesh);
2443
2444 exio_helper->initialize_nodal_variables(names);
2445 }
2446 }
2447 else
2448 {
2449 // We are already open for writing, so check that the filename
2450 // passed to this function matches the filename currently in use
2451 // by the helper.
2452 libmesh_error_msg_if(fname != exio_helper->current_filename,
2453 "Error! This ExodusII_IO object is already associated with file: "
2454 << exio_helper->current_filename
2455 << ", cannot use it with requested file: "
2456 << fname);
2457 }
2458}
2459
2460const std::vector<std::string> & ExodusII_IO::get_nodal_var_names()
2461{
2462 exio_helper->read_var_names(ExodusII_IO_Helper::NODAL);
2463 return exio_helper->nodal_var_names;
2464}
2465
2466const std::vector<std::string> & ExodusII_IO::get_elem_var_names()
2467{
2469 return exio_helper->elem_var_names;
2470}
2471
2472const std::vector<std::string> & ExodusII_IO::get_global_var_names()
2473{
2474 exio_helper->read_var_names(ExodusII_IO_Helper::GLOBAL);
2475 return exio_helper->global_var_names;
2476}
2477
2478const std::vector<int> & ExodusII_IO::get_elem_num_map() const
2479{
2480 // We could make this function non-const and have it call
2481 // exio_helper->read_elem_num_map() before returning a reference...
2482 // but the intention is that this will be called some time after a
2483 // mesh is read in, in which case it would be doing extra work to
2484 // read in the elem_num_map twice.
2485 return exio_helper->elem_num_map;
2486}
2487
2488const std::vector<int> & ExodusII_IO::get_node_num_map() const
2489{
2490 return exio_helper->node_num_map;
2491}
2492
2494{
2495 // Provide a warning when accessing the helper object
2496 // since it is a non-public API and is likely to see
2497 // future API changes
2498 libmesh_experimental();
2499
2500 return *exio_helper;
2501}
2502
2503
2505{
2506 exio_helper->set_hdf5_writing(write_hdf5);
2507}
2508
2509
2510void ExodusII_IO::set_max_name_length(unsigned int max_length)
2511{
2512 exio_helper->set_max_name_length(max_length);
2513}
2514
2515
2517{
2518 _disc_bex = disc_bex;
2519}
2520
2521
2522
2523// LIBMESH_HAVE_EXODUS_API is not defined, declare error() versions of functions...
2524#else
2525
2526
2527
2528ExodusII_IO::~ExodusII_IO () = default;
2529
2530
2531
2532void ExodusII_IO::read (const std::string &)
2533{
2534 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2535}
2536
2537
2538
2539ExodusHeaderInfo ExodusII_IO::read_header (const std::string &)
2540{
2541 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2542}
2543
2544
2545
2546void ExodusII_IO::verbose (bool)
2547{
2548 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2549}
2550
2551
2552
2554{
2555 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2556}
2557
2558
2559
2561{
2562 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2563}
2564
2565
2566
2568{
2569 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2570}
2571
2572
2573
2574void ExodusII_IO::append(bool)
2575{
2576 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2577}
2578
2579
2580
2582{
2583 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2584}
2585
2586
2587
2589{
2590 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2591 return false;
2592}
2593
2594
2595
2596const std::vector<Real> & ExodusII_IO::get_time_steps()
2597{
2598 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2599}
2600
2601
2602
2604{
2605 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2606}
2607
2608
2610 std::string,
2611 std::string,
2612 unsigned int)
2613{
2614 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2615}
2616
2617
2618
2620 std::string,
2621 std::string,
2622 unsigned int)
2623{
2624 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2625}
2626
2627
2628
2630 std::vector<std::string>,
2631 std::vector<std::string>,
2632 unsigned int)
2633{
2634 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2635}
2636
2637
2638
2640{
2641 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2642}
2643
2644
2645
2646void
2648(const EquationSystems &,
2649 const std::set<std::string> *,
2650 const std::string & )
2651{
2652 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2653}
2654
2655
2656
2657void ExodusII_IO::write_nodal_data (const std::string &,
2658 const std::vector<Number> &,
2659 const std::vector<std::string> &)
2660{
2661 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2662}
2663
2664
2665
2666void ExodusII_IO::write_information_records (const std::vector<std::string> &)
2667{
2668 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2669}
2670
2671
2672
2673void ExodusII_IO::write_global_data (const std::vector<Number> &,
2674 const std::vector<std::string> &)
2675{
2676 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2677}
2678
2679
2680
2681void ExodusII_IO::write_timestep (const std::string &,
2682 const EquationSystems &,
2683 const int,
2684 const Real,
2685 const std::set<std::string> *)
2686{
2687 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2688}
2689
2690
2691void ExodusII_IO::write_equation_systems (const std::string &,
2692 const EquationSystems &,
2693 const std::set<std::string> *)
2694{
2695 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2696}
2697
2698
2700{
2701 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2702}
2703
2704void
2707 const std::vector<std::string> &,
2708 const std::vector<std::set<boundary_id_type>> &,
2709 const std::vector<std::map<BoundaryInfo::BCTuple, Real>> &)
2710{
2711 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2712}
2713
2714
2715
2716void
2719 std::vector<std::string> &,
2720 std::vector<std::set<boundary_id_type>> &,
2721 std::vector<std::map<BoundaryInfo::BCTuple, Real>> &)
2722{
2723 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2724}
2725
2726void
2728get_sideset_data_indices (std::map<BoundaryInfo::BCTuple, unsigned int> &)
2729
2730{
2731 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2732}
2733
2734void
2737 const std::vector<std::string> &,
2738 const std::vector<std::set<boundary_id_type>> &,
2739 const std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> &)
2740{
2741 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2742}
2743
2744void
2747 std::vector<std::string> &,
2748 std::vector<std::set<boundary_id_type>> &,
2749 std::vector<std::map<BoundaryInfo::NodeBCTuple, Real>> &)
2750{
2751 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2752}
2753
2754void
2756get_nodeset_data_indices (std::map<BoundaryInfo::NodeBCTuple, unsigned int> &)
2757{
2758 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2759}
2760
2761void
2764 const std::vector<std::string> &,
2765 const std::vector<std::set<elemset_id_type>> &,
2766 const std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> &)
2767{
2768 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2769}
2770
2771void
2774 std::vector<std::string> &,
2775 std::vector<std::set<elemset_id_type>> &,
2776 std::vector<std::map<std::pair<dof_id_type, elemset_id_type>, Real>> &)
2777{
2778 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2779}
2780
2781void
2783get_elemset_data_indices (std::map<std::pair<dof_id_type, elemset_id_type>, unsigned int> &)
2784{
2785 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2786}
2787
2788void ExodusII_IO::write (const std::string &)
2789{
2790 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2791}
2792
2793
2794
2795void ExodusII_IO::write_nodal_data_discontinuous (const std::string &,
2796 const std::vector<Number> &,
2797 const std::vector<std::string> &)
2798{
2799 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2800}
2801
2802
2803
2805 const std::vector<std::string> &,
2806 bool)
2807{
2808 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2809}
2810
2811
2812const std::vector<std::string> & ExodusII_IO::get_elem_var_names()
2813{
2814 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2815}
2816
2817const std::vector<std::string> & ExodusII_IO::get_nodal_var_names()
2818{
2819 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2820}
2821
2822const std::vector<std::string> & ExodusII_IO::get_global_var_names()
2823{
2824 libmesh_error_msg("ERROR, ExodusII API is not defined.");
2825}
2826
2828
2829#endif // LIBMESH_HAVE_EXODUS_API
2830} // namespace libMesh
unsigned int dim
MessageTag get_unique_tag(int tagvalue=MessageTag::invalid_tag) const
processor_id_type rank() const
Status receive(const unsigned int dest_processor_id, T &buf, const MessageTag &tag=any_tag) const
void send(const unsigned int dest_processor_id, const T &buf, const MessageTag &tag=no_tag) const
void add_shellface(const dof_id_type elem, const unsigned short int shellface, const boundary_id_type id)
Add shell face shellface of element number elem with boundary id id to the boundary information data ...
std::string & sideset_name(boundary_id_type id)
std::size_t n_edge_conds() const
std::string & nodeset_name(boundary_id_type id)
void add_node(const Node *node, const boundary_id_type id)
Add Node node with boundary id id to the boundary information data structures.
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.
This class handles the numbering of degrees of freedom on a mesh.
Definition dof_map.h:181
bool local_index(dof_id_type dof_index) const
Definition dof_map.h:967
void SCALAR_dof_indices(std::vector< dof_id_type > &di, const unsigned int vn, const bool old_dofs=false) const
Fills the vector di with the global degree of freedom indices corresponding to the SCALAR variable vn...
Definition dof_map.C:2605
unsigned int n_comp(const unsigned int s, const unsigned int var) const
Definition dof_object.h:978
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
processor_id_type processor_id() const
Definition dof_object.h:881
static constexpr dof_id_type invalid_id
An invalid id to distinguish an uninitialized DofObject.
Definition dof_object.h:473
unique_id_type unique_id() const
Definition dof_object.h:835
T get_extra_datum(const unsigned int index) const
Gets the value on this object of the extra datum associated with index, which should have been obtain...
void set_extra_datum(const unsigned int index, const T value)
Sets the value on this object of the extra datum associated with index, which should have been obtain...
dof_id_type id() const
Definition dof_object.h:819
void set_extra_integer(const unsigned int index, const dof_id_type value)
Sets the value on this object of the extra integer associated with index, which should have been obta...
static const ElementDefinition & find_elem_definition(dyna_int_type dyna_elem, int dim, int p)
Finds the ElementDefinition corresponding to a particular element type.
Definition dyna_io.C:741
This is the base class from which all geometric element types are derived.
Definition elem.h:96
virtual Node *& set_node(const unsigned int i)
Definition elem.h:2567
virtual unsigned int n_nodes() const =0
virtual Order default_order() const =0
static std::unique_ptr< Elem > build(const ElemType type, Elem *p=nullptr)
Definition elem.C:442
virtual unsigned short dim() const =0
virtual ElemType type() const =0
virtual unsigned int n_sides() const =0
const Elem * top_parent() const
Definition elem.h:3073
This is the EquationSystems class.
static bool is_elemental_data_fe_type(const FEType &type)
void build_variable_names(std::vector< std::string > &var_names, const FEType *type=nullptr, const std::set< std::string > *system_names=nullptr) const
Fill the input vector var_names with the names of the variables for each system.
void build_elemental_solution_vector(std::vector< Number > &soln, std::vector< std::string > &names) const
Retrieve the solution data for elemental data variables.
static bool redundant_added_side(const Elem &elem, unsigned int side)
void get_vars_active_subdomains(const std::vector< std::string > &names, std::vector< std::set< subdomain_id_type > > &vars_active_subdomains) const
Retrieve vars_active_subdomains, which indicates the active subdomains for each variable in names.
void build_elemental_data_variable_names(std::vector< std::string > &var_names, const std::set< std::string > *system_names=nullptr) const
Filter var_names to names of variables that can be represented as elemental data.
void build_discontinuous_solution_vector(std::vector< Number > &soln, const std::set< std::string > *system_names=nullptr, const std::vector< std::string > *var_names=nullptr, bool vertices_only=false, bool add_sides=false) const
Fill the input vector soln with solution values.
This class is used as both an external data structure for passing around Exodus file header informati...
void broadcast(const Parallel::Communicator &comm)
Broadcasts data from processor 0 to other procs using the provided Communicator.
static const int invalid_id
An invalid_id that can be returned to signal failure in case something goes wrong.
This is the ExodusII_IO_Helper class.
std::vector< std::vector< std::vector< Real > > > bex_dense_constraint_vecs
const std::vector< int > & get_node_num_map() const
Identical to the behavior of get_elem_num_map(), but for the node_num_map instead.
void set_max_name_length(unsigned int max_length)
For backwards compatibility, libMesh currently truncates names in ExodusII output to the old default ...
std::unique_ptr< ExodusII_IO_Helper > exio_helper
Only attempt to instantiate an ExodusII helper class if the Exodus API is defined.
virtual void write(const std::string &fname) override
This method implements writing a mesh to a specified file.
void copy_scalar_solution(System &system, std::vector< std::string > system_var_names, std::vector< std::string > exodus_var_names, unsigned int timestep=1)
Copy global variables into scalar variables of a System object.
bool _set_unique_ids_from_maps
Set Elem/Node unique_ids based on the elem_num_map and node_num_map contents during reading,...
void write_elemset_data(int timestep, const std::vector< std::string > &var_names, const std::vector< std::set< elemset_id_type > > &elemset_ids_in, const std::vector< std::map< std::pair< dof_id_type, elemset_id_type >, Real > > &elemset_vals)
The Exodus format can also store values on elemsets.
virtual bool get_add_sides() override
ExodusHeaderInfo read_header(const std::string &name)
Read only the header information, instead of the entire mesh.
void append(bool val)
If true, this flag will cause the ExodusII_IO object to attempt to open an existing file for writing,...
void read_elemset_data(int timestep, std::vector< std::string > &var_names, std::vector< std::set< elemset_id_type > > &elemset_ids_in, std::vector< std::map< std::pair< dof_id_type, elemset_id_type >, Real > > &elemset_vals)
Read all the elemset data at a particular timestep.
void set_coordinate_offset(Point p)
Allows you to set a vector that is added to the coordinates of all of the nodes.
virtual void write_equation_systems(const std::string &fname, const EquationSystems &es, const std::set< std::string > *system_names=nullptr) override
Writes out the solution for no specific time or timestep.
int _timestep
Stores the current value of the timestep when calling ExodusII_IO::write_timestep().
const std::vector< Real > & get_time_steps()
bool _verbose
should we be verbose?
bool _write_complex_abs
By default, when complex numbers are enabled, for each variable we write out three values: the real p...
const std::vector< std::string > & get_global_var_names()
Return list of the global variable names.
void get_elemset_data_indices(std::map< std::pair< dof_id_type, elemset_id_type >, unsigned int > &elemset_array_indices)
Similar to read_elemset_data(), but instead of creating one std::map per nodeset per variable,...
void verbose(bool set_verbosity)
Set the flag indicating if we should be verbose.
static int get_exodus_version()
void write_complex_magnitude(bool val)
Set the flag indicating whether the complex modulus should be written when complex numbers are enable...
void write_timestep(const std::string &fname, const EquationSystems &es, const int timestep, const Real time, const std::set< std::string > *system_names=nullptr)
Writes out the solution at a specific timestep.
std::vector< std::string > _extra_integer_vars
An optional list of variables in the EXODUS file that are to be used to set extra integers when loadi...
void copy_nodal_solution(System &system, std::string system_var_name, std::string exodus_var_name, unsigned int timestep=1)
If we read in a nodal solution while reading in a mesh, we can attempt to copy that nodal solution in...
const std::vector< int > & get_elem_num_map() const
Returns a const reference to the elem_num_map, which is a vector that is created when a Mesh is read ...
void set_output_variables(const std::vector< std::string > &output_variables, bool allow_empty=true)
Sets the list of variable names to be included in the output.
void set_hdf5_writing(bool write_hdf5)
Set to true (the default) to write files in an HDF5-based file format (when HDF5 is available),...
std::vector< std::string > _output_variables
The names of the variables to be output.
void write_as_dimension(unsigned dim)
Directly control the num_dim which is written to the Exodus file.
void write_timestep_discontinuous(const std::string &fname, const EquationSystems &es, const int timestep, const Real time, const std::set< std::string > *system_names=nullptr)
Writes a discontinuous solution at a specific timestep.
void read_global_variable(std::vector< std::string > global_var_names, unsigned int timestep, std::vector< Real > &global_values)
Given a vector of global variables and a time step, returns the values of the global variable at the ...
void write_element_data_from_discontinuous_nodal_data(const EquationSystems &es, const std::set< std::string > *system_names=nullptr, const std::string &var_suffix="_elem_node_")
Similar to the function above, but instead of only handling (CONSTANT, MONOMIAL) data,...
void write_information_records(const std::vector< std::string > &)
Write out information records.
void get_nodeset_data_indices(std::map< BoundaryInfo::NodeBCTuple, unsigned int > &bc_array_indices)
Similar to read_nodeset_data(), but instead of creating one std::map per nodeset per variable,...
const std::vector< std::string > & get_elem_var_names()
Return list of the elemental variable names.
void write_discontinuous_exodusII(const std::string &name, const EquationSystems &es, const std::set< std::string > *system_names=nullptr)
Writes a exodusII file with discontinuous data.
void set_unique_ids_from_maps(bool val)
If true, this flag enforces the following behaviors:
ExodusII_IO_Helper & get_exio_helper()
Return a reference to the ExodusII_IO_Helper object.
bool _disc_bex
Set to true (false is the default) to generate independent nodes for every Bezier Extraction element.
void copy_elemental_solution(System &system, std::string system_var_name, std::string exodus_var_name, unsigned int timestep=1)
If we read in a elemental solution while reading in a mesh, we can attempt to copy that elemental sol...
void use_mesh_dimension_instead_of_spatial_dimension(bool val)
In the general case, meshes containing 2D elements can be manifolds living in 3D space,...
void write_elemsets()
Write elemsets stored on the Mesh to file.
void write_nodeset_data(int timestep, const std::vector< std::string > &var_names, const std::vector< std::set< boundary_id_type > > &node_boundary_ids, const std::vector< std::map< BoundaryInfo::NodeBCTuple, Real > > &bc_vals)
The Exodus format can also store values on nodesets.
void write_global_data(const std::vector< Number > &, const std::vector< std::string > &)
Write out global variables.
void write_nodal_data_common(std::string fname, const std::vector< std::string > &names, bool continuous=true)
This function factors out a bunch of code which is common to the write_nodal_data() and write_nodal_d...
bool _append
Default false.
void read_sideset_data(int timestep, std::vector< std::string > &var_names, std::vector< std::set< boundary_id_type > > &side_ids, std::vector< std::map< BoundaryInfo::BCTuple, Real > > &bc_vals)
Similar to write_sideset_data(), this function is used to read the data at a particular timestep.
void read_elemental_variable(std::string elemental_var_name, unsigned int timestep, std::map< unsigned int, Real > &unique_id_to_value_map)
Given an elemental variable and a time step, returns a mapping from the elements (top parent) unique ...
void write_element_data(const EquationSystems &es)
Write out element solution.
void get_sideset_data_indices(std::map< BoundaryInfo::BCTuple, unsigned int > &bc_array_indices)
Similar to read_sideset_data(), but instead of creating one std::map per sideset per variable,...
void read_nodeset_data(int timestep, std::vector< std::string > &var_names, std::vector< std::set< boundary_id_type > > &node_boundary_ids, std::vector< std::map< BoundaryInfo::NodeBCTuple, Real > > &bc_vals)
Read all the nodeset data at a particular timestep.
const std::vector< std::string > & get_nodal_var_names()
Return list of the nodal variable names.
virtual void read(const std::string &name) override
This method implements reading a mesh from a specified file.
ExodusII_IO(MeshBase &mesh, bool single_precision=false)
Constructor.
virtual void write_nodal_data(const std::string &, const std::vector< Number > &, const std::vector< std::string > &) override
Write out a nodal solution.
bool _allow_empty_variables
Flag which controls the behavior of _output_variables: .) If true, _output_variables is allowed to re...
void write_added_sides(bool val)
By default, we only write out the elements physically stored in the mesh.
void set_extra_integer_vars(const std::vector< std::string > &extra_integer_vars)
Set the elemental variables in the Exodus file to be read into extra element integers.
void set_discontinuous_bex(bool disc_bex)
Set to true (false is the default) to generate independent nodes for every Bezier Extraction element ...
void write_nodal_data_discontinuous(const std::string &, const std::vector< Number > &, const std::vector< std::string > &) override
Write out a discontinuous nodal solution.
void write_sideset_data(int timestep, const std::vector< std::string > &var_names, const std::vector< std::set< boundary_id_type > > &side_ids, const std::vector< std::map< BoundaryInfo::BCTuple, Real > > &bc_vals)
The Exodus format can also store values on sidesets.
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
virtual const Node & node_ref(const dof_id_type i) const
Definition mesh_base.h:745
void set_default_mapping_data(const unsigned char data)
Set the default master space to physical space mapping basis functions to be used on newly added elem...
Definition mesh_base.h:968
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
Definition mesh_base.h:170
virtual const Node * node_ptr(const dof_id_type i) const =0
unsigned int mesh_dimension() const
Definition mesh_base.C:430
void allow_renumbering(bool allow)
If false is passed in then this mesh will no longer be renumbered when being prepared for use.
Definition mesh_base.h:1355
dof_id_type get_elemset_code(const MeshBase::elemset_type &id_set) const
Definition mesh_base.C:497
virtual dof_id_type n_nodes() const =0
void set_mesh_dimension(unsigned char d)
Resets the logical dimension of the mesh.
Definition mesh_base.h:423
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
unsigned int add_elem_integer(std::string name, bool allocate_data=true, dof_id_type default_value=DofObject::invalid_id)
Register an integer datum (of type dof_id_type) to be added to each element in the mesh.
Definition mesh_base.C:623
virtual Node * add_node(Node *n)=0
Add Node n to the end of the vertex array.
virtual void clear()
Deletes all the element and node data that is currently stored.
Definition mesh_base.C:1036
void set_default_mapping_type(const ElemMappingType type)
Set the default master space to physical space mapping basis functions to be used on newly added elem...
Definition mesh_base.h:950
unsigned int add_node_datum(const std::string &name, bool allocate_data=true, const T *default_value=nullptr)
Register a datum (of type T) to be added to each node in the mesh.
Definition mesh_base.h:2679
unsigned int n_elemsets() const
Returns the number of unique elemset ids which have been added via add_elemset_code(),...
Definition mesh_base.C:482
constraint_rows_type & get_constraint_rows()
Constraint rows accessors.
Definition mesh_base.h:1935
void add_elemset_code(dof_id_type code, MeshBase::elemset_type id_set)
Tabulate a user-defined "code" for elements which belong to the element sets specified in id_set.
Definition mesh_base.C:456
virtual const Elem * query_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.
virtual const Elem & elem_ref(const dof_id_type i) const
Definition mesh_base.h:788
void allow_node_and_elem_unique_id_overlap(bool allow)
If true is passed, then this mesh will no longer require unique_ids to be unique across the set of al...
Definition mesh_base.h:1387
virtual void reserve_elem(const dof_id_type ne)=0
Reserves space for a known number of elements.
virtual bool is_serial_on_zero() const
Definition mesh_base.h:364
virtual void reserve_nodes(const dof_id_type nn)=0
Reserves space for a known number of nodes.
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
virtual void write_equation_systems(const std::string &, const EquationSystems &, const std::set< std::string > *system_names=nullptr)
This method implements writing a mesh with data to a specified file where the data is taken from the ...
Definition mesh_output.C:31
virtual void write_discontinuous_equation_systems(const std::string &, const EquationSystems &, const std::set< std::string > *system_names=nullptr)
This method implements writing a mesh with discontinuous data to a specified file where the data is t...
Definition mesh_output.C:89
const MT & mesh() const
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
A Node is like a Point, but with more information.
Definition node.h:55
An object whose state is distributed along a set of processors.
processor_id_type processor_id() const
const Parallel::Communicator & comm() const
processor_id_type n_processors() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
Definition point.h:40
Manages consistently variables, degrees of freedom, and coefficient vectors.
Definition system.h:100
const FEType & variable_type(const unsigned int i) const
Definition system.C:2721
std::unique_ptr< NumericVector< Number > > solution
Data structure to hold solution values.
Definition system.h:1655
unsigned int variable_scalar_number(std::string_view var, unsigned int component) const
Definition system.h:2474
virtual void update()
Update the local values to reflect the solution on neighboring processors.
Definition system.C:498
unsigned int variable_number(std::string_view var) const
Definition system.C:1398
const DofMap & get_dof_map() const
Definition system.h:2417
unsigned int number() const
Definition system.h:2393
void add_scaled(const TypeVector< T2 > &, const T &)
Add a scaled value to this vector without creating a temporary.
MeshBase & mesh
void pull_parallel_vector_data(const Communicator &comm, const MapToVectors &queries, GatherFunctor &gather_data, const ActionFunctor &act_on_data, const datum *example)
std::string enum_to_string(const T e)
The libMesh namespace provides an interface to certain functionality in the library.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition int_range.h:153
int8_t boundary_id_type
Definition id_types.h:51
void libmesh_ignore(const Args &...)
@ RATIONAL_BERNSTEIN_MAP
libmesh_assert(ctx)
uint8_t dof_id_type
Definition id_types.h:67
bool libmesh_isnan(T x)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
uint8_t processor_id_type
Definition id_types.h:104
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition int_range.h:176
Defines mapping from libMesh element types to LS-DYNA element types or vice-versa.
Definition dyna_io.h:115
std::vector< unsigned int > nodes
Definition dyna_io.h:131
The FPEDisabler class puts Floating-Point Exception (FPE) trapping on hold during its lifetime,...
const dof_id_type n_nodes
Definition tecplot_io.C:67
static const bool value
Definition xdr_io.C:55