LCOV - code coverage report
Current view: top level - src/outputs - Exodus.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: #32971 (54bef8) with base c6cf66 Lines: 224 243 92.2 %
Date: 2026-05-29 20:35:17 Functions: 19 21 90.5 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : //* This file is part of the MOOSE framework
       2             : //* https://mooseframework.inl.gov
       3             : //*
       4             : //* All rights reserved, see COPYRIGHT for full restrictions
       5             : //* https://github.com/idaholab/moose/blob/master/COPYRIGHT
       6             : //*
       7             : //* Licensed under LGPL 2.1, please see LICENSE for details
       8             : //* https://www.gnu.org/licenses/lgpl-2.1.html
       9             : 
      10             : #include "Exodus.h"
      11             : 
      12             : // Moose includes
      13             : #include "DisplacedProblem.h"
      14             : #include "ExodusFormatter.h"
      15             : #include "FEProblem.h"
      16             : #include "FileMesh.h"
      17             : #include "MooseApp.h"
      18             : #include "MooseVariableScalar.h"
      19             : #include "LockFile.h"
      20             : 
      21             : #include "libmesh/exodusII_io.h"
      22             : #include "libmesh/libmesh_config.h" // LIBMESH_HAVE_HDF5
      23             : 
      24             : using namespace libMesh;
      25             : 
      26             : registerMooseObject("MooseApp", Exodus);
      27             : 
      28             : InputParameters
      29       78786 : Exodus::validParams()
      30             : {
      31             :   // Get the base class parameters
      32       78786 :   InputParameters params = SampledOutput::validParams();
      33             :   params +=
      34      157572 :       AdvancedOutput::enableOutputTypes("nodal elemental scalar postprocessor reporter input");
      35             : 
      36             :   // Enable sequential file output (do not set default, the use_displace criteria relies on
      37             :   // isParamValid, see Constructor)
      38      315144 :   params.addParam<bool>("sequence",
      39             :                         "Enable/disable sequential file output (enabled by default "
      40             :                         "when 'use_displace = true', otherwise defaults to false");
      41             : 
      42             :   // Select problem dimension for mesh output
      43      472716 :   params.addDeprecatedParam<bool>("use_problem_dimension",
      44             :                                   "Use the problem dimension to the mesh output. "
      45             :                                   "Set to false when outputting lower dimensional "
      46             :                                   "meshes embedded in a higher dimensional space.",
      47             :                                   "Use 'output_dimension = problem_dimension' instead.");
      48             : 
      49      315144 :   MooseEnum output_dimension("default 1 2 3 problem_dimension", "default");
      50             : 
      51      315144 :   params.addParam<MooseEnum>(
      52             :       "output_dimension", output_dimension, "The dimension of the output file");
      53             : 
      54      315144 :   params.addParamNamesToGroup("output_dimension", "Advanced");
      55             : 
      56             :   // Set the default padding to 3
      57      157572 :   params.set<unsigned int>("padding") = 3;
      58             : 
      59             :   // Add description for the Exodus class
      60      157572 :   params.addClassDescription("Object for output data in the Exodus format");
      61             : 
      62             :   // Flag for overwriting at each timestep
      63      236358 :   params.addParam<bool>("overwrite",
      64      157572 :                         false,
      65             :                         "When true the latest timestep will overwrite the "
      66             :                         "existing file, so only a single timestep exists.");
      67             : 
      68             :   // Set outputting of the input to be on by default
      69      157572 :   params.set<ExecFlagEnum>("execute_input_on") = EXEC_INITIAL;
      70             : 
      71             :   // Flag for outputting discontinuous data to Exodus
      72      236358 :   params.addParam<bool>(
      73      157572 :       "discontinuous", false, "Enables discontinuous output format for Exodus files.");
      74             : 
      75             :   // Flag for outputting added side elements (for side-discontinuous data) to Exodus
      76      236358 :   params.addParam<bool>(
      77      157572 :       "side_discontinuous", false, "Enables adding side-discontinuous output in Exodus files.");
      78             : 
      79             :   // Flag for outputting Exodus data in HDF5 format (when libMesh is
      80             :   // configured with HDF5 support).  libMesh wants to do so by default
      81             :   // (for backwards compatibility with libMesh HDF5 users), but we
      82             :   // want to avoid this by default (for backwards compatibility with
      83             :   // most Moose users and to avoid generating regression test gold
      84             :   // files that non-HDF5 Moose builds can't read)
      85      315144 :   params.addParam<bool>("write_hdf5", false, "Enables HDF5 output format for Exodus files.");
      86             : 
      87             :   // Set output of names to be truncated to a certain character count.
      88             :   // libMesh+ExodusII currently supports up to 80, so we would like to
      89             :   // default to that to avoid truncation when possible.
      90             :   //
      91             :   // We used to truncate at 32, so we make this user-configurable to
      92             :   // make it easier to match old gold files.
      93             :   //
      94             :   // We're still defaulting to 32 until our apps in CI start using
      95             :   // this option (and/or re-golding) downstream.
      96             :   //
      97             :   // If someone tries to set truncation at less than 32 they're
      98             :   // probably making a mistake.
      99      393930 :   params.addRangeCheckedParam<unsigned int>("max_output_name_length",
     100      157572 :                                             32,
     101             :                                             "32<=max_output_name_length<=80",
     102             :                                             "Maximum length for names in Exodus file output.");
     103             : 
     104      236358 :   params.addParamNamesToGroup("write_hdf5 max_output_name_length", "Advanced");
     105             : 
     106             :   // Need a layer of geometric ghosting for mesh serialization
     107      236358 :   params.addRelationshipManager("ElementPointNeighborLayers",
     108             :                                 Moose::RelationshipManagerType::GEOMETRIC);
     109             : 
     110             :   // Return the InputParameters
     111      157572 :   return params;
     112       78786 : }
     113             : 
     114       36801 : Exodus::Exodus(const InputParameters & parameters)
     115             :   : SampledOutput(parameters),
     116       36792 :     _exodus_initialized(false),
     117       73584 :     _exodus_mesh_changed(declareRestartableData<bool>("exodus_mesh_changed", true)),
     118      110400 :     _sequence(isParamValid("sequence") ? getParam<bool>("sequence")
     119       36768 :               : _use_displaced         ? true
     120             :                                        : false),
     121       73584 :     _exodus_num(declareRestartableData<unsigned int>("exodus_num", 0)),
     122       36792 :     _recovering(_app.isRecovering()),
     123       73584 :     _overwrite(getParam<bool>("overwrite")),
     124       73584 :     _output_dimension(getParam<MooseEnum>("output_dimension").getEnum<OutputDimension>()),
     125       73584 :     _discontinuous(getParam<bool>("discontinuous")),
     126       73584 :     _side_discontinuous(getParam<bool>("side_discontinuous")),
     127       73584 :     _write_hdf5(getParam<bool>("write_hdf5")),
     128      147177 :     _max_output_name_length(getParam<unsigned int>("max_output_name_length"))
     129             : {
     130      110376 :   if (isParamValid("use_problem_dimension"))
     131             :   {
     132           0 :     auto use_problem_dimension = getParam<bool>("use_problem_dimension");
     133             : 
     134           0 :     if (use_problem_dimension)
     135           0 :       _output_dimension = OutputDimension::PROBLEM_DIMENSION;
     136             :     else
     137           0 :       _output_dimension = OutputDimension::DEFAULT;
     138             :   }
     139             :   // If user sets 'discontinuous = true' and 'elemental_as_nodal = false', issue an error that these
     140             :   // are incompatible states
     141       36864 :   if (_discontinuous && parameters.isParamSetByUser("elemental_as_nodal") && !_elemental_as_nodal)
     142           0 :     mooseError(name(),
     143             :                ": Invalid parameters. 'elemental_as_nodal' set to false while 'discontinuous' set "
     144             :                "to true.");
     145             :   // At this point, if we have discontinuous ouput, we know the user hasn't explicitly set
     146             :   // 'elemental_as_nodal = false', so we can safely default it to true
     147       36792 :   if (_discontinuous)
     148          36 :     _elemental_as_nodal = true;
     149       36792 : }
     150             : 
     151             : void
     152           0 : Exodus::setOutputDimension(unsigned int /*dim*/)
     153             : {
     154           0 :   mooseDeprecated(
     155             :       "This method is no longer needed. We can determine output dimension programmatically");
     156           0 : }
     157             : 
     158             : void
     159       36444 : Exodus::initialSetup()
     160             : {
     161             :   // Call base class setup method
     162       36444 :   SampledOutput::initialSetup();
     163             : 
     164             :   // The libMesh::ExodusII_IO will fail when it is closed if the object is created but
     165             :   // nothing is written to the file. This checks that at least something will be written.
     166       36441 :   if (!hasOutput())
     167           0 :     mooseError("The current settings result in nothing being output to the Exodus file.");
     168             : 
     169             :   // Test that some sort of variable output exists (case when all variables are disabled but input
     170             :   // output is still enabled
     171       36486 :   if (!hasNodalVariableOutput() && !hasElementalVariableOutput() && !hasPostprocessorOutput() &&
     172          45 :       !hasScalarOutput())
     173           3 :     mooseError("The current settings results in only the input file and no variables being output "
     174             :                "to the Exodus file, this is not supported.");
     175       36438 : }
     176             : 
     177             : void
     178        5273 : Exodus::meshChanged()
     179             : {
     180             :   // Maintain Sampled::meshChanged() functionality
     181        5273 :   SampledOutput::meshChanged();
     182             : 
     183             :   // Indicate to the Exodus object that the mesh has changed
     184        5273 :   _exodus_mesh_changed = true;
     185        5273 : }
     186             : 
     187             : void
     188           0 : Exodus::sequence(bool state)
     189             : {
     190           0 :   _sequence = state;
     191           0 : }
     192             : 
     193             : void
     194      163718 : Exodus::outputSetup()
     195             : {
     196      163718 :   if (_exodus_io_ptr)
     197             :   {
     198             :     // Do nothing if the ExodusII_IO objects exists, but has not been initialized
     199      129041 :     if (!_exodus_initialized)
     200      125457 :       return;
     201             : 
     202             :     // Do nothing if the output is using oversampling. In this case the mesh that is being output
     203             :     // has not been changed, so there is no need to create a new ExodusII_IO object
     204      123253 :     if (_use_sampled_output)
     205         293 :       return;
     206             : 
     207             :     // Do nothing if the mesh has not changed and sequential output is not desired
     208      122960 :     if (!_exodus_mesh_changed && !_sequence)
     209      119376 :       return;
     210             :   }
     211             : 
     212       40349 :   auto serialize = [this](auto & moose_mesh)
     213             :   {
     214       40349 :     auto & lm_mesh = moose_mesh.getMesh();
     215             :     // Exodus is serial output so that we have to gather everything to "zero".
     216       40349 :     lm_mesh.gather_to_zero();
     217             :     // This makes the face information out-of-date on process 0 for distributed meshes, e.g.
     218             :     // elements will have neighbors that they didn't previously have
     219       40349 :     if ((this->processor_id() == 0) && !lm_mesh.is_replicated())
     220        4132 :       moose_mesh.markFiniteVolumeInfoDirty();
     221       78610 :   };
     222       38261 :   serialize(_problem_ptr->mesh());
     223             : 
     224             :   // We need to do the same thing for displaced mesh to make them consistent.
     225             :   // In general, it is a good idea to make the reference mesh and the displaced mesh
     226             :   // consistent since some operations or calculations are already based on this assumption.
     227             :   // For example,
     228             :   // FlagElementsThread::onElement(const Elem * elem)
     229             :   //   if (_displaced_problem)
     230             :   //    _displaced_problem->mesh().elemPtr(elem->id())->set_refinement_flag((Elem::RefinementState)marker_value);
     231             :   // Here we assume that the displaced mesh and the reference mesh are identical except
     232             :   // coordinations.
     233       38261 :   if (_problem_ptr->getDisplacedProblem())
     234        2088 :     serialize(_problem_ptr->getDisplacedProblem()->mesh());
     235             : 
     236             :   // Create the ExodusII_IO object
     237       38261 :   _exodus_io_ptr = std::make_unique<ExodusII_IO>(_es_ptr->get_mesh());
     238       38261 :   _exodus_initialized = false;
     239             : 
     240       38261 :   if (_write_hdf5)
     241             :   {
     242             : #ifndef LIBMESH_HAVE_HDF5
     243             :     mooseError("Moose input requested HDF Exodus output, but libMesh was built without HDF5.");
     244             : #endif
     245             : 
     246             :     // This is redundant unless the libMesh default changes
     247           0 :     _exodus_io_ptr->set_hdf5_writing(true);
     248             :   }
     249             :   else
     250             :   {
     251       38261 :     _exodus_io_ptr->set_hdf5_writing(false);
     252             :   }
     253             : 
     254       38261 :   _exodus_io_ptr->set_max_name_length(_max_output_name_length);
     255             : 
     256       38261 :   if (_side_discontinuous)
     257          22 :     _exodus_io_ptr->write_added_sides(true);
     258             : 
     259             :   // Increment file number and set appending status, append if all the following conditions are met:
     260             :   //   (1) If the application is recovering (not restarting)
     261             :   //   (2) The mesh has NOT changed
     262             :   //   (3) An existing Exodus file exists for appending (_exodus_num > 0)
     263             :   //   (4) Sequential output is NOT desired
     264             :   //   (5) Exodus is NOT being output only on FINAL
     265       39080 :   if (_recovering && !_exodus_mesh_changed && _exodus_num > 0 && !_sequence &&
     266         819 :       (getExecuteOnEnum().size() != 1 || !getExecuteOnEnum().contains(EXEC_FINAL)))
     267             :   {
     268             :     // Set the recovering flag to false so that this special case is not triggered again
     269         817 :     _recovering = false;
     270             : 
     271             :     // Set the append flag to true b/c on recover the file is being appended
     272         817 :     _exodus_io_ptr->append(true);
     273             :   }
     274             :   else
     275             :   {
     276             :     // Disable file appending and reset exodus file number count
     277       37444 :     _exodus_io_ptr->append(false);
     278             : 
     279             :     // Customize file output
     280       37444 :     customizeFileOutput();
     281             :   }
     282             : 
     283       38261 :   setOutputDimensionInExodusWriter(*_exodus_io_ptr, *_mesh_ptr, _output_dimension);
     284             : }
     285             : 
     286             : void
     287       37444 : Exodus::customizeFileOutput()
     288             : {
     289       37444 :   if (_exodus_mesh_changed || _sequence)
     290       36630 :     _file_num++;
     291             : 
     292       37444 :   _exodus_num = 1;
     293       37444 : }
     294             : 
     295             : void
     296       41981 : Exodus::setOutputDimensionInExodusWriter(ExodusII_IO & exodus_io,
     297             :                                          const MooseMesh & mesh,
     298             :                                          OutputDimension output_dimension)
     299             : {
     300       41981 :   switch (output_dimension)
     301             :   {
     302       41970 :     case OutputDimension::DEFAULT:
     303             :       // If the mesh_dimension is 1, we need to write out as 3D.
     304             :       //
     305             :       // This works around an issue in Paraview where 1D meshes cannot
     306             :       // not be visualized correctly. Otherwise, write out based on the effectiveSpatialDimension.
     307       41970 :       if (mesh.getMesh().mesh_dimension() == 1)
     308        4342 :         exodus_io.write_as_dimension(3);
     309             :       else
     310       37628 :         exodus_io.write_as_dimension(static_cast<int>(mesh.effectiveSpatialDimension()));
     311       41970 :       break;
     312             : 
     313          11 :     case OutputDimension::ONE:
     314             :     case OutputDimension::TWO:
     315             :     case OutputDimension::THREE:
     316          11 :       exodus_io.write_as_dimension(static_cast<int>(output_dimension));
     317          11 :       break;
     318             : 
     319           0 :     case OutputDimension::PROBLEM_DIMENSION:
     320           0 :       exodus_io.use_mesh_dimension_instead_of_spatial_dimension(true);
     321           0 :       break;
     322             : 
     323           0 :     default:
     324           0 :       ::mooseError("Unknown output_dimension in Exodus writer");
     325             :   }
     326       41981 : }
     327             : 
     328             : void
     329      149921 : Exodus::outputNodalVariables()
     330             : {
     331             :   // Set the output variable to the nodal variables
     332      149921 :   std::vector<std::string> nodal(getNodalVariableOutput().begin(), getNodalVariableOutput().end());
     333      149921 :   _exodus_io_ptr->set_output_variables(nodal);
     334             : 
     335             :   // Check if the mesh is contiguously numbered, because exodus output will renumber to force that
     336      149921 :   const auto & mesh = _problem_ptr->mesh().getMesh();
     337             :   const bool mesh_contiguous_numbering =
     338      149921 :       (mesh.n_nodes() == mesh.max_node_id()) && (mesh.n_elem() == mesh.max_elem_id());
     339             : 
     340             :   // Write the data via libMesh::ExodusII_IO
     341      149921 :   if (_discontinuous)
     342         110 :     _exodus_io_ptr->write_timestep_discontinuous(
     343         110 :         filename(), *_es_ptr, _exodus_num, getOutputTime() + _app.getGlobalTimeOffset());
     344             :   else
     345      299732 :     _exodus_io_ptr->write_timestep(
     346      299732 :         filename(), *_es_ptr, _exodus_num, getOutputTime() + _app.getGlobalTimeOffset());
     347             : 
     348      149921 :   if (!_overwrite)
     349      146063 :     _exodus_num++;
     350             : 
     351      149921 :   if (!mesh_contiguous_numbering)
     352          36 :     handleExodusIOMeshRenumbering();
     353             : 
     354             :   // This satisfies the initialization of the ExodusII_IO object
     355      149921 :   _exodus_initialized = true;
     356      149921 : }
     357             : 
     358             : void
     359       34713 : Exodus::outputElementalVariables()
     360             : {
     361             :   // Make sure the the file is ready for writing of elemental data
     362       34713 :   if (!_exodus_initialized || !hasNodalVariableOutput())
     363        7611 :     outputEmptyTimestep();
     364             : 
     365             :   // Write the elemental data
     366       34713 :   std::vector<std::string> elemental(getElementalVariableOutput().begin(),
     367       69426 :                                      getElementalVariableOutput().end());
     368       34713 :   _exodus_io_ptr->set_output_variables(elemental);
     369       34713 :   _exodus_io_ptr->write_element_data(*_es_ptr);
     370       34713 : }
     371             : 
     372             : void
     373       36090 : Exodus::outputPostprocessors()
     374             : {
     375             :   // List of desired postprocessor outputs
     376       36090 :   const std::set<std::string> & pps = getPostprocessorOutput();
     377             : 
     378             :   // Append the postprocessor data to the global name value parameters; scalar outputs
     379             :   // also append these member variables
     380      101390 :   for (const auto & name : pps)
     381             :   {
     382       65300 :     _global_names.push_back(name);
     383       65300 :     _global_values.push_back(_problem_ptr->getPostprocessorValueByName(name));
     384             :   }
     385       36090 : }
     386             : 
     387             : void
     388         547 : Exodus::outputReporters()
     389             : {
     390        2126 :   for (const auto & combined_name : getReporterOutput())
     391             :   {
     392        1579 :     ReporterName r_name(combined_name);
     393        1784 :     if (_reporter_data.hasReporterValue<Real>(r_name) &&
     394        1784 :         !hasPostprocessorByName(r_name.getObjectName()))
     395             :     {
     396         205 :       const Real & value = _reporter_data.getReporterValue<Real>(r_name);
     397         205 :       _global_names.push_back(r_name.getValueName());
     398         205 :       _global_values.push_back(value);
     399             :     }
     400        1579 :   }
     401         547 : }
     402             : 
     403             : void
     404        3775 : Exodus::outputScalarVariables()
     405             : {
     406             :   // List of desired scalar outputs
     407        3775 :   const std::set<std::string> & out = getScalarOutput();
     408             : 
     409             :   // Append the scalar to the global output lists
     410       10393 :   for (const auto & out_name : out)
     411             :   {
     412             :     // Make sure scalar values are in sync with the solution vector
     413             :     // and are visible on this processor.  See TableOutput.C for
     414             :     // TableOutput::outputScalarVariables() explanatory comments
     415             : 
     416        6618 :     MooseVariableScalar & scalar_var = _problem_ptr->getScalarVariable(0, out_name);
     417        6618 :     scalar_var.reinit();
     418        6618 :     VariableValue value(scalar_var.sln());
     419             : 
     420        6618 :     const std::vector<dof_id_type> & dof_indices = scalar_var.dofIndices();
     421        6618 :     const unsigned int n = dof_indices.size();
     422        6618 :     value.resize(n);
     423             : 
     424        6618 :     const DofMap & dof_map = scalar_var.sys().dofMap();
     425       14237 :     for (unsigned int i = 0; i != n; ++i)
     426             :     {
     427        7619 :       const processor_id_type pid = dof_map.dof_owner(dof_indices[i]);
     428        7619 :       this->comm().broadcast(value[i], pid);
     429             :     }
     430             : 
     431             :     // If the scalar has a single component, output the name directly
     432        6618 :     if (n == 1)
     433             :     {
     434        6104 :       _global_names.push_back(out_name);
     435        6104 :       _global_values.push_back(value[0]);
     436             :     }
     437             : 
     438             :     // If the scalar as many components add indices to the end of the name
     439             :     else
     440             :     {
     441        2029 :       for (unsigned int i = 0; i < n; ++i)
     442             :       {
     443        1515 :         std::ostringstream os;
     444        1515 :         os << out_name << "_" << i;
     445        1515 :         _global_names.push_back(os.str());
     446        1515 :         _global_values.push_back(value[i]);
     447        1515 :       }
     448             :     }
     449        6618 :   }
     450        3775 : }
     451             : 
     452             : void
     453       33608 : Exodus::outputInput()
     454             : {
     455             :   // Format the input file
     456       33608 :   ExodusFormatter syntax_formatter;
     457       33608 :   syntax_formatter.printInputFile(_app.actionWarehouse());
     458       33608 :   syntax_formatter.format();
     459             : 
     460             :   // Store the information
     461       33608 :   _input_record = syntax_formatter.getInputFileRecord();
     462       33608 : }
     463             : 
     464             : void
     465      163718 : Exodus::output()
     466             : {
     467             :   // Prepare the ExodusII_IO object
     468      163718 :   outputSetup();
     469      163718 :   LockFile lf(filename(), processor_id() == 0);
     470             : 
     471             :   // Adjust the position of the output
     472      163718 :   if (_app.hasOutputPosition())
     473        6546 :     _exodus_io_ptr->set_coordinate_offset(_app.getOutputPosition());
     474             : 
     475             :   // Clear the global variables (postprocessors and scalars)
     476      163718 :   _global_names.clear();
     477      163718 :   _global_values.clear();
     478             : 
     479             :   // Call the individual output methods
     480      163718 :   AdvancedOutput::output();
     481             : 
     482             :   // Write the global variables (populated by the output methods)
     483      163718 :   if (!_global_values.empty())
     484             :   {
     485       37071 :     if (!_exodus_initialized)
     486          40 :       outputEmptyTimestep();
     487       37071 :     _exodus_io_ptr->write_global_data(_global_values, _global_names);
     488             :   }
     489             : 
     490             :   // Write the input file record if it exists and the output file is initialized
     491      163718 :   if (!_input_record.empty() && _exodus_initialized)
     492             :   {
     493       33575 :     _exodus_io_ptr->write_information_records(_input_record);
     494       33575 :     _input_record.clear();
     495             :   }
     496             : 
     497             :   // Reset the mesh changed flag
     498      163718 :   _exodus_mesh_changed = false;
     499             : 
     500             :   // It is possible to have an empty file created with the following scenario. By default the
     501             :   // 'execute_on_input' flag is setup to run on INITIAL. If the 'execute_on' is set to FINAL
     502             :   // but the simulation stops early (e.g., --test-checkpoint-half-transient) the Exodus file is
     503             :   // created but there is no data in it, because of the initial call to write the input data seems
     504             :   // to create the file but doesn't actually write the data into the solution/mesh is also supplied
     505             :   // to the IO object. Then if --recover is used this empty file fails to open for appending.
     506             :   //
     507             :   // The code below will delete any empty files that exist. Another solution is to set the
     508             :   // 'execute_on_input' flag to NONE.
     509      163718 :   std::string current = filename();
     510      284441 :   if (processor_id() == 0 && MooseUtils::checkFileReadable(current, false, false) &&
     511      120723 :       (MooseUtils::fileSize(current) == 0))
     512             :   {
     513        3634 :     int err = std::remove(current.c_str());
     514        3634 :     if (err != 0)
     515           0 :       mooseError("MOOSE failed to remove the empty file ", current);
     516             :   }
     517      163718 : }
     518             : 
     519             : std::string
     520      521486 : Exodus::filename()
     521             : {
     522             :   // Append the .e extension on the base file name
     523      521486 :   std::ostringstream output;
     524      521486 :   output << _file_base + ".e";
     525             : 
     526             :   // Add the -s00x extension to the file
     527      521486 :   if (_file_num > 1)
     528       16416 :     output << "-s" << std::setw(_padding) << std::setprecision(0) << std::setfill('0') << std::right
     529       16416 :            << _file_num;
     530             : 
     531     1042972 :   return output.str();
     532      521486 : }
     533             : 
     534             : void
     535        7651 : Exodus::outputEmptyTimestep()
     536             : {
     537             :   // Check if the mesh is contiguously numbered, because exodus output will renumber to force that
     538        7651 :   const auto & mesh = _problem_ptr->mesh().getMesh();
     539             :   const bool mesh_contiguous_numbering =
     540        7651 :       (mesh.n_nodes() == mesh.max_node_id()) && (mesh.n_elem() == mesh.max_elem_id());
     541             : 
     542             :   // Write a timestep with no variables
     543        7651 :   _exodus_io_ptr->set_output_variables(std::vector<std::string>());
     544       15302 :   _exodus_io_ptr->write_timestep(
     545       15302 :       filename(), *_es_ptr, _exodus_num, getOutputTime() + _app.getGlobalTimeOffset());
     546             : 
     547        7651 :   if (!_overwrite)
     548        7585 :     _exodus_num++;
     549             : 
     550        7651 :   if (!mesh_contiguous_numbering)
     551          21 :     handleExodusIOMeshRenumbering();
     552        7651 :   _exodus_initialized = true;
     553        7651 : }
     554             : 
     555             : void
     556        2567 : Exodus::clear()
     557             : {
     558        2567 :   _exodus_io_ptr.reset();
     559        2567 : }
     560             : 
     561             : void
     562          57 : Exodus::handleExodusIOMeshRenumbering()
     563             : {
     564             :   // We renumbered our mesh, so we need the other mesh to do the same
     565          57 :   if (auto * const disp_problem = _problem_ptr->getDisplacedProblem().get(); disp_problem)
     566             :   {
     567          11 :     auto & disp_eq = disp_problem->es();
     568          11 :     auto & other_mesh = &disp_eq == _es_ptr ? _problem_ptr->mesh().getMesh() : disp_eq.get_mesh();
     569             :     mooseAssert(
     570             :         !other_mesh.allow_renumbering(),
     571             :         "The only way we shouldn't have contiguous numbering is if we've disabled renumbering");
     572          11 :     other_mesh.allow_renumbering(true);
     573          11 :     other_mesh.renumber_nodes_and_elements();
     574             :     // Copying over the comment in MeshOutput::write_equation_systems
     575             :     // Not sure what good going back to false will do here, the
     576             :     // renumbering horses have already left the barn...
     577          11 :     other_mesh.allow_renumbering(false);
     578             :   }
     579             : 
     580             :   // Objects that depend on element/node ids are no longer valid
     581          57 :   _problem_ptr->meshChanged(
     582             :       /*intermediate_change=*/false, /*contract_mesh=*/false, /*clean_refinement_flags=*/false);
     583          57 : }

Generated by: LCOV version 1.14