LCOV - code coverage report
Current view: top level - src/mesh - exodusII_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4229 (6a9aeb) with base 727f46 Lines: 680 850 80.0 %
Date: 2025-08-19 19:27:09 Functions: 61 84 72.6 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14