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