LCOV - code coverage report
Current view: top level - src/userobjects - SolutionUserObjectBase.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 441 603 73.1 %
Date: 2025-07-17 01:28:37 Functions: 29 34 85.3 %
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 "SolutionUserObjectBase.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "ConsoleUtils.h"
      14             : #include "MooseError.h"
      15             : #include "MooseMesh.h"
      16             : #include "MooseUtils.h"
      17             : #include "MooseVariableFE.h"
      18             : #include "RotationMatrix.h"
      19             : #include "Function.h"
      20             : 
      21             : // libMesh includes
      22             : #include "libmesh/equation_systems.h"
      23             : #include "libmesh/mesh_function.h"
      24             : #include "libmesh/numeric_vector.h"
      25             : #include "libmesh/nonlinear_implicit_system.h"
      26             : #include "libmesh/transient_system.h"
      27             : #include "libmesh/parallel_mesh.h"
      28             : #include "libmesh/serial_mesh.h"
      29             : #include "libmesh/exodusII_io.h"
      30             : #include "libmesh/exodusII_io_helper.h"
      31             : #include "libmesh/enum_xdr_mode.h"
      32             : #include "libmesh/string_to_enum.h"
      33             : 
      34             : InputParameters
      35       15181 : SolutionUserObjectBase::validParams()
      36             : {
      37             :   // Get the input parameters from the parent class
      38       15181 :   InputParameters params = GeneralUserObject::validParams();
      39             : 
      40             :   // Add required parameters
      41       15181 :   params.addRequiredParam<MeshFileName>(
      42             :       "mesh", "The name of the mesh file (must be xda/xdr or exodusII file).");
      43       45543 :   params.addParam<std::vector<std::string>>(
      44             :       "system_variables",
      45       30362 :       std::vector<std::string>(),
      46             :       "The name of the nodal and elemental variables from the file you want to use for values");
      47             : 
      48             :   // When using XDA/XDR files the following must be defined
      49       15181 :   params.addParam<FileName>(
      50             :       "es",
      51             :       "<not supplied>",
      52             :       "The name of the file holding the equation system info in xda/xdr format (xda/xdr only).");
      53       15181 :   params.addParam<std::string>(
      54             :       "system",
      55             :       "nl0",
      56             :       "The name of the system to pull values out of (xda/xdr only). The default name for the "
      57             :       "nonlinear system is 'nl0', auxiliary system is 'aux0'");
      58             : 
      59             :   // When using ExodusII a specific time is extracted
      60       15181 :   params.addParam<std::string>("timestep",
      61             :                                "Index of the single timestep used or \"LATEST\" for "
      62             :                                "the last timestep (exodusII only).  If not supplied, "
      63             :                                "time interpolation will occur.");
      64             : 
      65             :   // Choose the interpolation order for incoming nodal variables
      66       45543 :   params.addParam<MooseEnum>("nodal_variable_order",
      67       30362 :                              MooseEnum("FIRST SECOND", "FIRST"),
      68             :                              "Specifies the order of the nodal solution data.");
      69             : 
      70             :   // Add ability to perform coordinate transformation: scale, factor
      71       45543 :   params.addParam<std::vector<Real>>(
      72       30362 :       "scale", std::vector<Real>(LIBMESH_DIM, 1), "Scale factor for points in the simulation");
      73       45543 :   params.addParam<std::vector<Real>>("scale_multiplier",
      74       30362 :                                      std::vector<Real>(LIBMESH_DIM, 1),
      75             :                                      "Scale multiplying factor for points in the simulation");
      76       45543 :   params.addParam<std::vector<Real>>("translation",
      77       30362 :                                      std::vector<Real>(LIBMESH_DIM, 0),
      78             :                                      "Translation factors for x,y,z coordinates of the simulation");
      79       45543 :   params.addParam<RealVectorValue>("rotation0_vector",
      80       30362 :                                    RealVectorValue(0, 0, 1),
      81             :                                    "Vector about which to rotate points of the simulation.");
      82       45543 :   params.addParam<Real>(
      83             :       "rotation0_angle",
      84       30362 :       0.0,
      85             :       "Anticlockwise rotation angle (in degrees) to use for rotation about rotation0_vector.");
      86       45543 :   params.addParam<RealVectorValue>("rotation1_vector",
      87       30362 :                                    RealVectorValue(0, 0, 1),
      88             :                                    "Vector about which to rotate points of the simulation.");
      89       45543 :   params.addParam<Real>(
      90             :       "rotation1_angle",
      91       30362 :       0.0,
      92             :       "Anticlockwise rotation angle (in degrees) to use for rotation about rotation1_vector.");
      93             : 
      94             :   // following lines build the default_transformation_order
      95             :   MultiMooseEnum default_transformation_order(
      96       15181 :       "rotation0 translation scale rotation1 scale_multiplier", "translation scale");
      97       15181 :   params.addParam<MultiMooseEnum>(
      98             :       "transformation_order",
      99             :       default_transformation_order,
     100             :       "The order to perform the operations in.  Define R0 to be the rotation matrix encoded by "
     101             :       "rotation0_vector and rotation0_angle.  Similarly for R1.  Denote the scale by s, the "
     102             :       "scale_multiplier by m, and the translation by t.  Then, given a point x in the simulation, "
     103             :       "if transformation_order = 'rotation0 scale_multiplier translation scale rotation1' then "
     104             :       "form p = R1*(R0*x*m - t)/s.  Then the values provided by the SolutionUserObjectBase at "
     105             :       "point x "
     106             :       "in the simulation are the variable values at point p in the mesh.");
     107       15181 :   params.addParamNamesToGroup("scale scale_multiplier translation rotation0_vector rotation0_angle "
     108             :                               "rotation1_angle transformation_order",
     109             :                               "Coordinate system transformation");
     110       15181 :   params.addClassDescription("Reads a variable from a mesh in one simulation to another");
     111       30362 :   return params;
     112       15181 : }
     113             : 
     114             : // Static mutex definition
     115             : Threads::spin_mutex SolutionUserObjectBase::_solution_user_object_mutex;
     116             : 
     117         458 : SolutionUserObjectBase::SolutionUserObjectBase(const InputParameters & parameters)
     118             :   : GeneralUserObject(parameters),
     119         458 :     _file_type(MooseEnum("xda=0 exodusII=1 xdr=2")),
     120         458 :     _mesh_file(getParam<MeshFileName>("mesh")),
     121         458 :     _es_file(getParam<FileName>("es")),
     122         458 :     _system_name(getParam<std::string>("system")),
     123         458 :     _system_variables(getParam<std::vector<std::string>>("system_variables")),
     124         458 :     _exodus_time_index(-1),
     125         458 :     _interpolate_times(false),
     126         458 :     _system(nullptr),
     127         458 :     _system2(nullptr),
     128         458 :     _interpolation_time(0.0),
     129         458 :     _interpolation_factor(0.0),
     130         458 :     _exodus_times(nullptr),
     131         458 :     _exodus_index1(-1),
     132         458 :     _exodus_index2(-1),
     133         458 :     _nodal_variable_order(getParam<MooseEnum>("nodal_variable_order")),
     134         458 :     _scale(getParam<std::vector<Real>>("scale")),
     135         458 :     _scale_multiplier(getParam<std::vector<Real>>("scale_multiplier")),
     136         458 :     _translation(getParam<std::vector<Real>>("translation")),
     137         458 :     _rotation0_vector(getParam<RealVectorValue>("rotation0_vector")),
     138         458 :     _rotation0_angle(getParam<Real>("rotation0_angle")),
     139         458 :     _r0(RealTensorValue()),
     140         458 :     _rotation1_vector(getParam<RealVectorValue>("rotation1_vector")),
     141         458 :     _rotation1_angle(getParam<Real>("rotation1_angle")),
     142         458 :     _r1(RealTensorValue()),
     143         458 :     _transformation_order(getParam<MultiMooseEnum>("transformation_order")),
     144        1374 :     _initialized(false)
     145             : {
     146             :   // form rotation matrices with the specified angles
     147         458 :   Real halfPi = libMesh::pi / 2.0;
     148             :   Real a;
     149             :   Real b;
     150             : 
     151         458 :   a = std::cos(halfPi * -_rotation0_angle / 90.0);
     152         458 :   b = std::sin(halfPi * -_rotation0_angle / 90.0);
     153             :   // the following is an anticlockwise rotation about z
     154         458 :   RealTensorValue rot0_z(a, -b, 0, b, a, 0, 0, 0, 1);
     155             :   // form the rotation matrix that will take rotation0_vector to the z axis
     156         458 :   RealTensorValue vec0_to_z = RotationMatrix::rotVecToZ(_rotation0_vector);
     157             :   // _r0 is then: rotate points so vec0 lies along z; then rotate about angle0; then rotate points
     158             :   // back
     159         458 :   _r0 = vec0_to_z.transpose() * (rot0_z * vec0_to_z);
     160             : 
     161         458 :   a = std::cos(halfPi * -_rotation1_angle / 90.0);
     162         458 :   b = std::sin(halfPi * -_rotation1_angle / 90.0);
     163             :   // the following is an anticlockwise rotation about z
     164         458 :   RealTensorValue rot1_z(a, -b, 0, b, a, 0, 0, 0, 1);
     165             :   // form the rotation matrix that will take rotation1_vector to the z axis
     166         458 :   RealTensorValue vec1_to_z = RotationMatrix::rotVecToZ(_rotation1_vector);
     167             :   // _r1 is then: rotate points so vec1 lies along z; then rotate about angle1; then rotate points
     168             :   // back
     169         458 :   _r1 = vec1_to_z.transpose() * (rot1_z * vec1_to_z);
     170             : 
     171         458 :   if (isParamValid("timestep") && getParam<std::string>("timestep") == "-1")
     172           0 :     mooseError("A \"timestep\" of -1 is no longer supported for interpolation. Instead simply "
     173             :                "remove this parameter altogether for interpolation");
     174             : 
     175             :   // check for possible inconsistencies between mesh and input data
     176         458 :   if (_fe_problem.mesh().hasSecondOrderElements())
     177          24 :     mooseInfo(
     178             :         "The mesh has second-order elements, be sure to set 'nodal_variable_order' if needed.");
     179         458 : }
     180             : 
     181             : void
     182         112 : SolutionUserObjectBase::readXda()
     183             : {
     184         112 :   if (!isParamSetByUser("es"))
     185           0 :     paramError("es", "Equation system file (.xda or .xdr) should have been specified");
     186             : 
     187             :   // Check that the required files exist
     188         112 :   MooseUtils::checkFileReadable(_es_file);
     189         112 :   MooseUtils::checkFileReadable(_mesh_file);
     190             : 
     191             :   // Read the libmesh::mesh from the xda file
     192         112 :   _mesh->read(_mesh_file);
     193             : 
     194             :   // Create the libmesh::EquationSystems
     195         112 :   _es = std::make_unique<EquationSystems>(*_mesh);
     196             : 
     197             :   // Use new read syntax (binary)
     198         112 :   if (_file_type == "xdr")
     199           0 :     _es->read(_es_file,
     200             :               libMesh::DECODE,
     201             :               EquationSystems::READ_HEADER | EquationSystems::READ_DATA |
     202             :                   EquationSystems::READ_ADDITIONAL_DATA);
     203             : 
     204             :   // Use new read syntax
     205         112 :   else if (_file_type == "xda")
     206         112 :     _es->read(_es_file,
     207             :               libMesh::READ,
     208             :               EquationSystems::READ_HEADER | EquationSystems::READ_DATA |
     209             :                   EquationSystems::READ_ADDITIONAL_DATA);
     210             : 
     211             :   // This should never occur, just in case produce an error
     212             :   else
     213           0 :     mooseError("Failed to determine proper read method for XDA/XDR equation system file: ",
     214           0 :                _es_file);
     215             : 
     216             :   // Update and store the EquationSystems name locally
     217         112 :   _es->update();
     218         112 :   _system = &_es->get_system(_system_name);
     219         112 : }
     220             : 
     221             : void
     222         346 : SolutionUserObjectBase::readExodusII()
     223             : {
     224             :   // Define a default system name
     225         346 :   if (_system_name == "")
     226           0 :     _system_name = "SolutionUserObjectSystem";
     227             : 
     228             :   // Read the Exodus file
     229         346 :   _exodusII_io = std::make_unique<libMesh::ExodusII_IO>(*_mesh);
     230         346 :   _exodusII_io->read(_mesh_file);
     231         346 :   readBlockIdMapFromExodusII();
     232         346 :   _exodus_times = &_exodusII_io->get_time_steps();
     233             : 
     234         346 :   if (isParamValid("timestep"))
     235             :   {
     236         204 :     std::string s_timestep = getParam<std::string>("timestep");
     237         204 :     int n_steps = _exodusII_io->get_num_time_steps();
     238         204 :     if (s_timestep == "LATEST")
     239          56 :       _exodus_time_index = n_steps;
     240             :     else
     241             :     {
     242         148 :       std::istringstream ss(s_timestep);
     243         148 :       if (!((ss >> _exodus_time_index) && ss.eof()) || _exodus_time_index > n_steps)
     244           0 :         mooseError("Invalid value passed as \"timestep\". Expected \"LATEST\" or a valid integer "
     245             :                    "less than ",
     246             :                    n_steps,
     247             :                    ", received ",
     248             :                    s_timestep);
     249         148 :     }
     250         204 :   }
     251             :   else
     252             :     // Interpolate between times rather than using values from a set timestep
     253         142 :     _interpolate_times = true;
     254             : 
     255             :   // Check that the number of time steps is valid
     256         346 :   int num_exo_times = _exodus_times->size();
     257         346 :   if (num_exo_times == 0)
     258           0 :     mooseError("In SolutionUserObjectBase, exodus file contains no timesteps.");
     259             : 
     260             :   // Account for parallel mesh
     261         346 :   if (dynamic_cast<DistributedMesh *>(_mesh.get()))
     262             :   {
     263           0 :     _mesh->allow_renumbering(true);
     264           0 :     _mesh->prepare_for_use();
     265             :   }
     266             :   else
     267             :   {
     268         346 :     _mesh->allow_renumbering(false);
     269         346 :     _mesh->prepare_for_use();
     270             :   }
     271             : 
     272             :   // Create EquationSystems object for solution
     273         346 :   _es = std::make_unique<EquationSystems>(*_mesh);
     274         346 :   _es->add_system<libMesh::ExplicitSystem>(_system_name);
     275         346 :   _system = &_es->get_system(_system_name);
     276             : 
     277             :   // Get the variable name lists as set; these need to be sets to perform set_intersection
     278         346 :   const std::vector<std::string> & all_nodal(_exodusII_io->get_nodal_var_names());
     279         346 :   const std::vector<std::string> & all_elemental(_exodusII_io->get_elem_var_names());
     280         346 :   const std::vector<std::string> & all_scalar(_exodusII_io->get_global_var_names());
     281             : 
     282             :   // Build nodal/elemental variable lists, limit to variables listed in 'system_variables', if
     283             :   // provided
     284             :   // This function could be called more than once, so clear the member variables so we don't keep
     285             :   // adding to the vectors
     286         346 :   _nodal_variables.clear();
     287         346 :   _elemental_variables.clear();
     288         346 :   _scalar_variables.clear();
     289         346 :   if (!_system_variables.empty())
     290             :   {
     291         720 :     for (const auto & var_name : _system_variables)
     292             :     {
     293         422 :       if (std::find(all_nodal.begin(), all_nodal.end(), var_name) != all_nodal.end())
     294         318 :         _nodal_variables.push_back(var_name);
     295         422 :       if (std::find(all_elemental.begin(), all_elemental.end(), var_name) != all_elemental.end())
     296         104 :         _elemental_variables.push_back(var_name);
     297         422 :       if (std::find(all_scalar.begin(), all_scalar.end(), var_name) != all_scalar.end())
     298             :         // Check if the scalar matches any field variables, and ignore the var if it does. This
     299             :         // means its a Postprocessor.
     300           0 :         if (std::find(begin(_nodal_variables), end(_nodal_variables), var_name) ==
     301           0 :                 _nodal_variables.end() &&
     302           0 :             std::find(begin(_elemental_variables), end(_elemental_variables), var_name) ==
     303           0 :                 _elemental_variables.end())
     304           0 :           _scalar_variables.push_back(var_name);
     305             :     }
     306             :   }
     307             :   else
     308             :   {
     309          48 :     _nodal_variables = all_nodal;
     310          48 :     _elemental_variables = all_elemental;
     311             : 
     312          84 :     for (auto var_name : all_scalar)
     313             :       // Check if the scalar matches any field variables, and ignore the var if it does. This means
     314             :       // its a Postprocessor.
     315          36 :       if (std::find(begin(_nodal_variables), end(_nodal_variables), var_name) ==
     316          96 :               _nodal_variables.end() &&
     317          24 :           std::find(begin(_elemental_variables), end(_elemental_variables), var_name) ==
     318          60 :               _elemental_variables.end())
     319          36 :         _scalar_variables.push_back(var_name);
     320             :   }
     321             : 
     322             :   // Add the variables to the system
     323         736 :   for (const auto & var_name : _nodal_variables)
     324         390 :     _system->add_variable(var_name, Utility::string_to_enum<Order>(_nodal_variable_order));
     325             : 
     326         462 :   for (const auto & var_name : _elemental_variables)
     327         116 :     _system->add_variable(var_name, CONSTANT, MONOMIAL);
     328             : 
     329         370 :   for (const auto & var_name : _scalar_variables)
     330          24 :     _system->add_variable(var_name, FIRST, SCALAR);
     331             : 
     332             :   // Initialize the equations systems
     333         346 :   _es->init();
     334             : 
     335             :   // Interpolate times
     336         346 :   if (_interpolate_times)
     337             :   {
     338             :     // Create a second equation system
     339         142 :     _es2 = std::make_unique<EquationSystems>(*_mesh);
     340         142 :     _es2->add_system<libMesh::ExplicitSystem>(_system_name);
     341         142 :     _system2 = &_es2->get_system(_system_name);
     342             : 
     343             :     // Add the variables to the system
     344         308 :     for (const auto & var_name : _nodal_variables)
     345         166 :       _system2->add_variable(var_name, Utility::string_to_enum<Order>(_nodal_variable_order));
     346             : 
     347         166 :     for (const auto & var_name : _elemental_variables)
     348          24 :       _system2->add_variable(var_name, CONSTANT, MONOMIAL);
     349             : 
     350         166 :     for (const auto & var_name : _scalar_variables)
     351          24 :       _system2->add_variable(var_name, FIRST, SCALAR);
     352             : 
     353             :     // Initialize
     354         142 :     _es2->init();
     355             : 
     356             :     // Update the times for interpolation (initially start at 0)
     357         142 :     updateExodusBracketingTimeIndices();
     358             : 
     359             :     // Copy the solutions from the first system
     360         308 :     for (const auto & var_name : _nodal_variables)
     361             :     {
     362         166 :       _exodusII_io->copy_nodal_solution(*_system, var_name, var_name, _exodus_index1 + 1);
     363         166 :       _exodusII_io->copy_nodal_solution(*_system2, var_name, var_name, _exodus_index2 + 1);
     364             :     }
     365             : 
     366         166 :     for (const auto & var_name : _elemental_variables)
     367             :     {
     368          24 :       _exodusII_io->copy_elemental_solution(*_system, var_name, var_name, _exodus_index1 + 1);
     369          24 :       _exodusII_io->copy_elemental_solution(*_system2, var_name, var_name, _exodus_index2 + 1);
     370             :     }
     371             : 
     372         142 :     if (_scalar_variables.size() > 0)
     373             :     {
     374          48 :       _exodusII_io->copy_scalar_solution(
     375          24 :           *_system, _scalar_variables, _scalar_variables, _exodus_index1 + 1);
     376          48 :       _exodusII_io->copy_scalar_solution(
     377          24 :           *_system2, _scalar_variables, _scalar_variables, _exodus_index2 + 1);
     378             :     }
     379             : 
     380             :     // Update the systems
     381         142 :     _system->update();
     382         142 :     _es->update();
     383         142 :     _system2->update();
     384         142 :     _es2->update();
     385             :   }
     386             : 
     387             :   // Non-interpolated times
     388             :   else
     389             :   {
     390         204 :     if (_exodus_time_index > num_exo_times)
     391           0 :       mooseError("In SolutionUserObjectBase, timestep = ",
     392           0 :                  _exodus_time_index,
     393             :                  ", but there are only ",
     394             :                  num_exo_times,
     395             :                  " time steps.");
     396             : 
     397             :     // Copy the values from the ExodusII file
     398         428 :     for (const auto & var_name : _nodal_variables)
     399         224 :       _exodusII_io->copy_nodal_solution(*_system, var_name, var_name, _exodus_time_index);
     400             : 
     401         296 :     for (const auto & var_name : _elemental_variables)
     402          92 :       _exodusII_io->copy_elemental_solution(*_system, var_name, var_name, _exodus_time_index);
     403             : 
     404         204 :     if (!_scalar_variables.empty())
     405           0 :       _exodusII_io->copy_scalar_solution(
     406           0 :           *_system, _scalar_variables, _scalar_variables, _exodus_time_index);
     407             : 
     408             :     // Update the equations systems
     409         204 :     _system->update();
     410         204 :     _es->update();
     411             :   }
     412         346 : }
     413             : 
     414             : Real
     415       40072 : SolutionUserObjectBase::directValue(const Node * node, const std::string & var_name) const
     416             : {
     417             :   // Get the libmesh variable and system numbers
     418       40072 :   unsigned int var_num = _system->variable_number(var_name);
     419       40072 :   unsigned int sys_num = _system->number();
     420             : 
     421             :   // Get the node id and associated dof
     422       40072 :   dof_id_type node_id = node->id();
     423       40072 :   const Node & sys_node = _system->get_mesh().node_ref(node_id);
     424             :   mooseAssert(sys_node.n_dofs(sys_num, var_num) > 0,
     425             :               "Variable " << var_name << " has no DoFs on node " << sys_node.id());
     426       40072 :   dof_id_type dof_id = sys_node.dof_number(sys_num, var_num, 0);
     427             : 
     428             :   // Return the desired value for the dof
     429       40072 :   return directValue(dof_id);
     430             : }
     431             : 
     432             : Real
     433           0 : SolutionUserObjectBase::directValue(const Elem * elem, const std::string & var_name) const
     434             : {
     435             :   // Get the libmesh variable and system numbers
     436           0 :   unsigned int var_num = _system->variable_number(var_name);
     437           0 :   unsigned int sys_num = _system->number();
     438             : 
     439             :   // Get the element id and associated dof
     440           0 :   dof_id_type elem_id = elem->id();
     441           0 :   const Elem & sys_elem = _system->get_mesh().elem_ref(elem_id);
     442             :   mooseAssert(sys_elem.n_dofs(sys_num, var_num) > 0,
     443             :               "Variable " << var_name << " has no DoFs on element " << sys_elem.id());
     444           0 :   dof_id_type dof_id = sys_elem.dof_number(sys_num, var_num, 0);
     445             : 
     446             :   // Return the desired value
     447           0 :   return directValue(dof_id);
     448             : }
     449             : 
     450             : void
     451        1623 : SolutionUserObjectBase::initialize()
     452             : {
     453        1623 : }
     454             : 
     455             : void
     456        1623 : SolutionUserObjectBase::finalize()
     457             : {
     458        1623 : }
     459             : 
     460             : void
     461        1656 : SolutionUserObjectBase::timestepSetup()
     462             : {
     463             :   // Update time interpolation for ExodusII solution
     464        1656 :   if (_file_type == 1 && _interpolate_times)
     465         644 :     updateExodusTimeInterpolation();
     466        1656 : }
     467             : 
     468             : void
     469        1623 : SolutionUserObjectBase::execute()
     470             : {
     471        1623 : }
     472             : 
     473             : void
     474         458 : SolutionUserObjectBase::initialSetup()
     475             : {
     476             : 
     477             :   // Make sure this only happens once
     478         458 :   if (_initialized)
     479           0 :     return;
     480             : 
     481             :   // Create a libmesh::Mesh object for storing the loaded data.
     482             :   // Several aspects of SolutionUserObjectBase won't work with a DistributedMesh:
     483             :   // .) ExodusII_IO::copy_nodal_solution() doesn't work in parallel.
     484             :   // .) We don't know if directValue will be used, which may request
     485             :   //    a value on a Node we don't have.
     486             :   // We force the Mesh used here to be a ReplicatedMesh.
     487         458 :   _mesh = std::make_unique<ReplicatedMesh>(_communicator);
     488             : 
     489             :   // ExodusII mesh file supplied
     490        1028 :   if (MooseUtils::hasExtension(_mesh_file, "e", /*strip_exodus_ext =*/true) ||
     491         570 :       MooseUtils::hasExtension(_mesh_file, "exo", true))
     492             :   {
     493         346 :     _file_type = "exodusII";
     494         346 :     readExodusII();
     495             :   }
     496             : 
     497             :   // XDA mesh file supplied
     498         112 :   else if (MooseUtils::hasExtension(_mesh_file, "xda"))
     499             :   {
     500         112 :     _file_type = "xda";
     501         112 :     readXda();
     502             :   }
     503             : 
     504             :   // XDR mesh file supplied
     505           0 :   else if (MooseUtils::hasExtension(_mesh_file, "xdr"))
     506             :   {
     507           0 :     _file_type = "xdr";
     508           0 :     readXda();
     509             :   }
     510             : 
     511             :   // Produce an error for an unknown file type
     512             :   else
     513           0 :     mooseError(
     514             :         "In SolutionUserObjectBase, invalid file type: only .xda, .xdr, .exo and .e supported");
     515             : 
     516             :   // Initialize the serial solution vector
     517         458 :   _serialized_solution = NumericVector<Number>::build(_communicator);
     518         458 :   _serialized_solution->init(_system->n_dofs(), false, libMesh::SERIAL);
     519             : 
     520             :   // Pull down a full copy of this vector on every processor so we can get values in parallel
     521         458 :   _system->solution->localize(*_serialized_solution);
     522             : 
     523             :   // Vector of variable numbers to apply the MeshFunction to
     524         458 :   std::vector<unsigned int> var_nums;
     525             : 
     526             :   // If no variables were given, use all of them
     527         458 :   if (_system_variables.empty())
     528             :   {
     529          48 :     _system->get_all_variable_numbers(var_nums);
     530         156 :     for (const auto & var_num : var_nums)
     531         108 :       _system_variables.push_back(_system->variable_name(var_num));
     532             :   }
     533             : 
     534             :   // Otherwise, gather the numbers for the variables given
     535             :   else
     536             :   {
     537         944 :     for (const auto & var_name : _system_variables)
     538         534 :       var_nums.push_back(_system->variable_number(var_name));
     539             :   }
     540             : 
     541             :   // Create the MeshFunction for working with the solution data
     542         916 :   _mesh_function = std::make_unique<libMesh::MeshFunction>(
     543         916 :       *_es, *_serialized_solution, _system->get_dof_map(), var_nums);
     544         458 :   _mesh_function->init();
     545             : 
     546             :   // Tell the MeshFunctions that we might be querying them outside the
     547             :   // mesh, so we can handle any errors at the MOOSE rather than at the
     548             :   // libMesh level.
     549         458 :   DenseVector<Number> default_values;
     550         458 :   _mesh_function->enable_out_of_mesh_mode(default_values);
     551             : 
     552             :   // Build second MeshFunction for interpolation
     553         458 :   if (_interpolate_times)
     554             :   {
     555             :     // Need to pull down a full copy of this vector on every processor so we can get values in
     556             :     // parallel
     557         142 :     _serialized_solution2 = NumericVector<Number>::build(_communicator);
     558         142 :     _serialized_solution2->init(_system2->n_dofs(), false, libMesh::SERIAL);
     559         142 :     _system2->solution->localize(*_serialized_solution2);
     560             : 
     561             :     // Create the MeshFunction for the second copy of the data
     562         284 :     _mesh_function2 = std::make_unique<libMesh::MeshFunction>(
     563         284 :         *_es2, *_serialized_solution2, _system2->get_dof_map(), var_nums);
     564         142 :     _mesh_function2->init();
     565         142 :     _mesh_function2->enable_out_of_mesh_mode(default_values);
     566             :   }
     567             : 
     568             :   // Populate the MeshFunction variable index
     569        1100 :   for (unsigned int i = 0; i < _system_variables.size(); ++i)
     570             :   {
     571         642 :     std::string name = _system_variables[i];
     572         642 :     _local_variable_index[name] = i;
     573         642 :   }
     574             : 
     575             :   // Set initialization flag
     576         458 :   _initialized = true;
     577         458 : }
     578             : 
     579             : MooseEnum
     580         196 : SolutionUserObjectBase::getSolutionFileType() const
     581             : {
     582         196 :   return _file_type;
     583             : }
     584             : 
     585             : void
     586         644 : SolutionUserObjectBase::updateExodusTimeInterpolation()
     587             : {
     588         644 :   if (_t != _interpolation_time)
     589             :   {
     590         600 :     if (updateExodusBracketingTimeIndices())
     591             :     {
     592             : 
     593         469 :       for (const auto & var_name : _nodal_variables)
     594         240 :         _exodusII_io->copy_nodal_solution(*_system, var_name, var_name, _exodus_index1 + 1);
     595         229 :       for (const auto & var_name : _elemental_variables)
     596           0 :         _exodusII_io->copy_elemental_solution(*_system, var_name, var_name, _exodus_index1 + 1);
     597         229 :       if (_scalar_variables.size() > 0)
     598          44 :         _exodusII_io->copy_scalar_solution(
     599          22 :             *_system, _scalar_variables, _scalar_variables, _exodus_index1 + 1);
     600             : 
     601         229 :       _system->update();
     602         229 :       _es->update();
     603         229 :       _system->solution->localize(*_serialized_solution);
     604             : 
     605         469 :       for (const auto & var_name : _nodal_variables)
     606         240 :         _exodusII_io->copy_nodal_solution(*_system2, var_name, var_name, _exodus_index2 + 1);
     607         229 :       for (const auto & var_name : _elemental_variables)
     608           0 :         _exodusII_io->copy_elemental_solution(*_system2, var_name, var_name, _exodus_index2 + 1);
     609         229 :       if (_scalar_variables.size() > 0)
     610          44 :         _exodusII_io->copy_scalar_solution(
     611          22 :             *_system2, _scalar_variables, _scalar_variables, _exodus_index2 + 1);
     612             : 
     613         229 :       _system2->update();
     614         229 :       _es2->update();
     615         229 :       _system2->solution->localize(*_serialized_solution2);
     616             :     }
     617         600 :     _interpolation_time = _t;
     618             :   }
     619         644 : }
     620             : 
     621             : bool
     622         742 : SolutionUserObjectBase::updateExodusBracketingTimeIndices()
     623             : {
     624         742 :   if (_file_type != 1)
     625           0 :     mooseError("In SolutionUserObjectBase, getTimeInterpolationData only applicable for exodusII "
     626             :                "file type");
     627             : 
     628         742 :   int old_index1 = _exodus_index1;
     629         742 :   int old_index2 = _exodus_index2;
     630             : 
     631         742 :   int num_exo_times = _exodus_times->size();
     632             : 
     633         742 :   const auto solution_time = solutionSampleTime();
     634             : 
     635         742 :   if (solution_time < (*_exodus_times)[0])
     636             :   {
     637           0 :     _exodus_index1 = 0;
     638           0 :     _exodus_index2 = 0;
     639           0 :     _interpolation_factor = 0.0;
     640             :   }
     641             :   else
     642             :   {
     643        1765 :     for (int i = 0; i < num_exo_times - 1; ++i)
     644             :     {
     645        1765 :       if (solution_time <= (*_exodus_times)[i + 1])
     646             :       {
     647         742 :         _exodus_index1 = i;
     648         742 :         _exodus_index2 = i + 1;
     649         742 :         _interpolation_factor =
     650         742 :             (solution_time - (*_exodus_times)[i]) / ((*_exodus_times)[i + 1] - (*_exodus_times)[i]);
     651         742 :         break;
     652             :       }
     653        1023 :       else if (i == num_exo_times - 2)
     654             :       {
     655           0 :         _exodus_index1 = num_exo_times - 1;
     656           0 :         _exodus_index2 = num_exo_times - 1;
     657           0 :         _interpolation_factor = 1.0;
     658           0 :         break;
     659             :       }
     660             :     }
     661             :   }
     662             : 
     663         742 :   bool indices_modified(false);
     664             : 
     665         742 :   if (_exodus_index1 != old_index1 || _exodus_index2 != old_index2)
     666         371 :     indices_modified = true;
     667             : 
     668         742 :   return indices_modified;
     669             : }
     670             : 
     671             : unsigned int
     672      573378 : SolutionUserObjectBase::getLocalVarIndex(const std::string & var_name) const
     673             : {
     674             :   // Extract the variable index for the MeshFunction(s)
     675      573378 :   std::map<std::string, unsigned int>::const_iterator it = _local_variable_index.find(var_name);
     676      573378 :   if (it == _local_variable_index.end())
     677          12 :     mooseError("Value requested for nonexistent variable '",
     678             :                var_name,
     679             :                "' in the '",
     680           4 :                name(),
     681             :                "' SolutionUserObjectBase.\nSystem selected: ",
     682           4 :                _system_name,
     683             :                "\nAvailable variables:\n",
     684           4 :                ConsoleUtils::formatString(Moose::stringify(_system_variables), ""));
     685      573374 :   return it->second;
     686             : }
     687             : 
     688             : Real
     689           0 : SolutionUserObjectBase::pointValueWrapper(Real t,
     690             :                                           const Point & p,
     691             :                                           const std::string & var_name,
     692             :                                           const MooseEnum & weighting_type,
     693             :                                           const std::set<subdomain_id_type> * subdomain_ids) const
     694             : {
     695             :   // first check if the FE type is continuous because in that case the value is
     696             :   // unique and we can take a short cut, the default weighting_type found_first also
     697             :   // shortcuts out
     698             :   auto family =
     699           0 :       _fe_problem
     700           0 :           .getVariable(
     701           0 :               _tid, var_name, Moose::VarKindType::VAR_ANY, Moose::VarFieldType::VAR_FIELD_STANDARD)
     702           0 :           .feType()
     703           0 :           .family;
     704             : 
     705           0 :   if (weighting_type == 1 ||
     706           0 :       (family != L2_LAGRANGE && family != MONOMIAL && family != L2_HIERARCHIC))
     707           0 :     return pointValue(t, p, var_name, subdomain_ids);
     708             : 
     709             :   // the shape function is discontinuous so we need to compute a suitable unique value
     710           0 :   std::map<const Elem *, Real> values = discontinuousPointValue(t, p, var_name);
     711           0 :   switch (weighting_type)
     712             :   {
     713           0 :     case 2:
     714             :     {
     715           0 :       Real average = 0.0;
     716           0 :       for (auto & v : values)
     717           0 :         average += v.second;
     718           0 :       return average / Real(values.size());
     719             :     }
     720           0 :     case 4:
     721             :     {
     722           0 :       Real smallest_elem_id_value = std::numeric_limits<Real>::max();
     723           0 :       dof_id_type smallest_elem_id = _fe_problem.mesh().maxElemId();
     724           0 :       for (auto & v : values)
     725           0 :         if (v.first->id() < smallest_elem_id)
     726             :         {
     727           0 :           smallest_elem_id = v.first->id();
     728           0 :           smallest_elem_id_value = v.second;
     729             :         }
     730           0 :       return smallest_elem_id_value;
     731             :     }
     732           0 :     case 8:
     733             :     {
     734           0 :       Real largest_elem_id_value = std::numeric_limits<Real>::lowest();
     735           0 :       dof_id_type largest_elem_id = 0;
     736           0 :       for (auto & v : values)
     737           0 :         if (v.first->id() > largest_elem_id)
     738             :         {
     739           0 :           largest_elem_id = v.first->id();
     740           0 :           largest_elem_id_value = v.second;
     741             :         }
     742           0 :       return largest_elem_id_value;
     743             :     }
     744             :   }
     745             : 
     746           0 :   mooseError("SolutionUserObjectBase::pointValueWrapper reaches line that it should not be able to "
     747             :              "reach.");
     748             :   return 0.0;
     749           0 : }
     750             : 
     751             : Real
     752      573062 : SolutionUserObjectBase::pointValue(Real t,
     753             :                                    const Point & p,
     754             :                                    const std::string & var_name,
     755             :                                    const std::set<subdomain_id_type> * subdomain_ids) const
     756             : {
     757      573062 :   const unsigned int local_var_index = getLocalVarIndex(var_name);
     758      573062 :   return pointValue(t, p, local_var_index, subdomain_ids);
     759             : }
     760             : 
     761             : Real
     762      786182 : SolutionUserObjectBase::pointValue(Real libmesh_dbg_var(t),
     763             :                                    const Point & p,
     764             :                                    const unsigned int local_var_index,
     765             :                                    const std::set<subdomain_id_type> * subdomain_ids) const
     766             : {
     767             :   // Create copy of point
     768      786182 :   Point pt(p);
     769             : 
     770             :   // do the transformations
     771     2354514 :   for (unsigned int trans_num = 0; trans_num < _transformation_order.size(); ++trans_num)
     772             :   {
     773     1568332 :     if (_transformation_order[trans_num] == "rotation0")
     774        5472 :       pt = _r0 * pt;
     775     1562860 :     else if (_transformation_order[trans_num] == "translation")
     776     3121688 :       for (const auto i : make_range(Moose::dim))
     777     2341266 :         pt(i) -= _translation[i];
     778      782438 :     else if (_transformation_order[trans_num] == "scale")
     779     3121688 :       for (const auto i : make_range(Moose::dim))
     780     2341266 :         pt(i) /= _scale[i];
     781        2016 :     else if (_transformation_order[trans_num] == "scale_multiplier")
     782        1152 :       for (const auto i : make_range(Moose::dim))
     783         864 :         pt(i) *= _scale_multiplier[i];
     784        1728 :     else if (_transformation_order[trans_num] == "rotation1")
     785        1728 :       pt = _r1 * pt;
     786             :   }
     787             : 
     788             :   // Extract the value at the current point
     789      786182 :   Real val = evalMeshFunction(pt, local_var_index, 1, subdomain_ids);
     790             : 
     791             :   // Interpolate
     792      786178 :   if (_file_type == 1 && _interpolate_times)
     793             :   {
     794             :     mooseAssert(t == _interpolation_time,
     795             :                 "Time passed into value() must match time at last call to timestepSetup()");
     796      176654 :     Real val2 = evalMeshFunction(pt, local_var_index, 2, subdomain_ids);
     797      176654 :     val = val + (val2 - val) * _interpolation_factor;
     798             :   }
     799             : 
     800      786178 :   return val;
     801             : }
     802             : 
     803             : std::map<const Elem *, Real>
     804          66 : SolutionUserObjectBase::discontinuousPointValue(
     805             :     Real t,
     806             :     const Point & p,
     807             :     const std::string & var_name,
     808             :     const std::set<subdomain_id_type> * subdomain_ids) const
     809             : {
     810          66 :   const unsigned int local_var_index = getLocalVarIndex(var_name);
     811          66 :   return discontinuousPointValue(t, p, local_var_index, subdomain_ids);
     812             : }
     813             : 
     814             : std::map<const Elem *, Real>
     815          66 : SolutionUserObjectBase::discontinuousPointValue(
     816             :     Real libmesh_dbg_var(t),
     817             :     Point pt,
     818             :     const unsigned int local_var_index,
     819             :     const std::set<subdomain_id_type> * subdomain_ids) const
     820             : {
     821             :   // do the transformations
     822         198 :   for (unsigned int trans_num = 0; trans_num < _transformation_order.size(); ++trans_num)
     823             :   {
     824         132 :     if (_transformation_order[trans_num] == "rotation0")
     825           0 :       pt = _r0 * pt;
     826         132 :     else if (_transformation_order[trans_num] == "translation")
     827         264 :       for (const auto i : make_range(Moose::dim))
     828         198 :         pt(i) -= _translation[i];
     829          66 :     else if (_transformation_order[trans_num] == "scale")
     830         264 :       for (const auto i : make_range(Moose::dim))
     831         198 :         pt(i) /= _scale[i];
     832           0 :     else if (_transformation_order[trans_num] == "scale_multiplier")
     833           0 :       for (const auto i : make_range(Moose::dim))
     834           0 :         pt(i) *= _scale_multiplier[i];
     835           0 :     else if (_transformation_order[trans_num] == "rotation1")
     836           0 :       pt = _r1 * pt;
     837             :   }
     838             : 
     839             :   // Extract the value at the current point
     840             :   std::map<const Elem *, Real> map =
     841          66 :       evalMultiValuedMeshFunction(pt, local_var_index, 1, subdomain_ids);
     842             : 
     843             :   // Interpolate
     844          66 :   if (_file_type == 1 && _interpolate_times)
     845             :   {
     846             :     mooseAssert(t == _interpolation_time,
     847             :                 "Time passed into value() must match time at last call to timestepSetup()");
     848          66 :     std::map<const Elem *, Real> map2 = evalMultiValuedMeshFunction(pt, local_var_index, 2);
     849             : 
     850          66 :     if (map.size() != map2.size())
     851           0 :       mooseError(
     852             :           "In SolutionUserObjectBase::discontinuousPointValue map and map2 have different size");
     853             : 
     854             :     // construct the interpolated map
     855         154 :     for (auto & k : map)
     856             :     {
     857          88 :       if (map2.find(k.first) == map2.end())
     858           0 :         mooseError(
     859             :             "In SolutionUserObjectBase::discontinuousPointValue map and map2 have differing keys");
     860          88 :       Real val = k.second;
     861          88 :       Real val2 = map2[k.first];
     862          88 :       map[k.first] = val + (val2 - val) * _interpolation_factor;
     863             :     }
     864          66 :   }
     865             : 
     866          66 :   return map;
     867           0 : }
     868             : 
     869             : RealGradient
     870           0 : SolutionUserObjectBase::pointValueGradientWrapper(
     871             :     Real t,
     872             :     const Point & p,
     873             :     const std::string & var_name,
     874             :     const MooseEnum & weighting_type,
     875             :     const std::set<subdomain_id_type> * subdomain_ids) const
     876             : {
     877             :   // the default weighting_type found_first shortcuts out
     878           0 :   if (weighting_type == 1)
     879           0 :     return pointValueGradient(t, p, var_name, subdomain_ids);
     880             : 
     881             :   // the shape function is discontinuous so we need to compute a suitable unique value
     882             :   std::map<const Elem *, RealGradient> values =
     883           0 :       discontinuousPointValueGradient(t, p, var_name, subdomain_ids);
     884           0 :   switch (weighting_type)
     885             :   {
     886           0 :     case 2:
     887             :     {
     888           0 :       RealGradient average = RealGradient(0.0, 0.0, 0.0);
     889           0 :       for (auto & v : values)
     890           0 :         average += v.second;
     891           0 :       return average / Real(values.size());
     892             :     }
     893           0 :     case 4:
     894             :     {
     895           0 :       RealGradient smallest_elem_id_value;
     896           0 :       dof_id_type smallest_elem_id = _fe_problem.mesh().maxElemId();
     897           0 :       for (auto & v : values)
     898           0 :         if (v.first->id() < smallest_elem_id)
     899             :         {
     900           0 :           smallest_elem_id = v.first->id();
     901           0 :           smallest_elem_id_value = v.second;
     902             :         }
     903           0 :       return smallest_elem_id_value;
     904             :     }
     905           0 :     case 8:
     906             :     {
     907           0 :       RealGradient largest_elem_id_value;
     908           0 :       dof_id_type largest_elem_id = 0;
     909           0 :       for (auto & v : values)
     910           0 :         if (v.first->id() > largest_elem_id)
     911             :         {
     912           0 :           largest_elem_id = v.first->id();
     913           0 :           largest_elem_id_value = v.second;
     914             :         }
     915           0 :       return largest_elem_id_value;
     916             :     }
     917             :   }
     918             : 
     919           0 :   mooseError("SolutionUserObjectBase::pointValueGradientWrapper reaches line that it should not be "
     920             :              "able to reach.");
     921             :   return RealGradient(0.0, 0.0, 0.0);
     922           0 : }
     923             : 
     924             : RealGradient
     925           0 : SolutionUserObjectBase::pointValueGradient(Real t,
     926             :                                            const Point & p,
     927             :                                            const std::string & var_name,
     928             :                                            const std::set<subdomain_id_type> * subdomain_ids) const
     929             : {
     930           0 :   const unsigned int local_var_index = getLocalVarIndex(var_name);
     931           0 :   return pointValueGradient(t, p, local_var_index, subdomain_ids);
     932             : }
     933             : 
     934             : RealGradient
     935       56628 : SolutionUserObjectBase::pointValueGradient(Real libmesh_dbg_var(t),
     936             :                                            Point pt,
     937             :                                            const unsigned int local_var_index,
     938             :                                            const std::set<subdomain_id_type> * subdomain_ids) const
     939             : {
     940             :   // do the transformations
     941      169884 :   for (unsigned int trans_num = 0; trans_num < _transformation_order.size(); ++trans_num)
     942             :   {
     943      113256 :     if (_transformation_order[trans_num] == "rotation0")
     944           0 :       pt = _r0 * pt;
     945      113256 :     else if (_transformation_order[trans_num] == "translation")
     946      226512 :       for (const auto i : make_range(Moose::dim))
     947      169884 :         pt(i) -= _translation[i];
     948       56628 :     else if (_transformation_order[trans_num] == "scale")
     949      226512 :       for (const auto i : make_range(Moose::dim))
     950      169884 :         pt(i) /= _scale[i];
     951           0 :     else if (_transformation_order[trans_num] == "scale_multiplier")
     952           0 :       for (const auto i : make_range(Moose::dim))
     953           0 :         pt(i) *= _scale_multiplier[i];
     954           0 :     else if (_transformation_order[trans_num] == "rotation1")
     955           0 :       pt = _r1 * pt;
     956             :   }
     957             : 
     958             :   // Extract the value at the current point
     959       56628 :   RealGradient val = evalMeshFunctionGradient(pt, local_var_index, 1, subdomain_ids);
     960             : 
     961             :   // Interpolate
     962       56628 :   if (_file_type == 1 && _interpolate_times)
     963             :   {
     964             :     mooseAssert(t == _interpolation_time,
     965             :                 "Time passed into value() must match time at last call to timestepSetup()");
     966       56628 :     RealGradient val2 = evalMeshFunctionGradient(pt, local_var_index, 2, subdomain_ids);
     967       56628 :     val = val + (val2 - val) * _interpolation_factor;
     968             :   }
     969             : 
     970       56628 :   return val;
     971             : }
     972             : 
     973             : std::map<const Elem *, RealGradient>
     974          66 : SolutionUserObjectBase::discontinuousPointValueGradient(
     975             :     Real t,
     976             :     const Point & p,
     977             :     const std::string & var_name,
     978             :     const std::set<subdomain_id_type> * subdomain_ids) const
     979             : {
     980          66 :   const unsigned int local_var_index = getLocalVarIndex(var_name);
     981          66 :   return discontinuousPointValueGradient(t, p, local_var_index, subdomain_ids);
     982             : }
     983             : 
     984             : std::map<const Elem *, RealGradient>
     985          66 : SolutionUserObjectBase::discontinuousPointValueGradient(
     986             :     Real libmesh_dbg_var(t),
     987             :     Point pt,
     988             :     const unsigned int local_var_index,
     989             :     const std::set<subdomain_id_type> * subdomain_ids) const
     990             : {
     991             :   // do the transformations
     992         198 :   for (unsigned int trans_num = 0; trans_num < _transformation_order.size(); ++trans_num)
     993             :   {
     994         132 :     if (_transformation_order[trans_num] == "rotation0")
     995           0 :       pt = _r0 * pt;
     996         132 :     else if (_transformation_order[trans_num] == "translation")
     997         264 :       for (const auto i : make_range(Moose::dim))
     998         198 :         pt(i) -= _translation[i];
     999          66 :     else if (_transformation_order[trans_num] == "scale")
    1000         264 :       for (const auto i : make_range(Moose::dim))
    1001         198 :         pt(i) /= _scale[i];
    1002           0 :     else if (_transformation_order[trans_num] == "scale_multiplier")
    1003           0 :       for (const auto i : make_range(Moose::dim))
    1004           0 :         pt(i) *= _scale_multiplier[i];
    1005           0 :     else if (_transformation_order[trans_num] == "rotation1")
    1006           0 :       pt = _r1 * pt;
    1007             :   }
    1008             : 
    1009             :   // Extract the value at the current point
    1010             :   std::map<const Elem *, RealGradient> map =
    1011          66 :       evalMultiValuedMeshFunctionGradient(pt, local_var_index, 1, subdomain_ids);
    1012             : 
    1013             :   // Interpolate
    1014          66 :   if (_file_type == 1 && _interpolate_times)
    1015             :   {
    1016             :     mooseAssert(t == _interpolation_time,
    1017             :                 "Time passed into value() must match time at last call to timestepSetup()");
    1018             :     std::map<const Elem *, RealGradient> map2 =
    1019          66 :         evalMultiValuedMeshFunctionGradient(pt, local_var_index, 1, subdomain_ids);
    1020             : 
    1021          66 :     if (map.size() != map2.size())
    1022           0 :       mooseError(
    1023             :           "In SolutionUserObjectBase::discontinuousPointValue map and map2 have different size");
    1024             : 
    1025             :     // construct the interpolated map
    1026         154 :     for (auto & k : map)
    1027             :     {
    1028          88 :       if (map2.find(k.first) == map2.end())
    1029           0 :         mooseError(
    1030             :             "In SolutionUserObjectBase::discontinuousPointValue map and map2 have differing keys");
    1031          88 :       RealGradient val = k.second;
    1032          88 :       RealGradient val2 = map2[k.first];
    1033          88 :       map[k.first] = val + (val2 - val) * _interpolation_factor;
    1034             :     }
    1035          66 :   }
    1036             : 
    1037          66 :   return map;
    1038           0 : }
    1039             : 
    1040             : Real
    1041       40132 : SolutionUserObjectBase::directValue(dof_id_type dof_index) const
    1042             : {
    1043       40132 :   Real val = (*_serialized_solution)(dof_index);
    1044       40132 :   if (_file_type == 1 && _interpolate_times)
    1045             :   {
    1046       20060 :     Real val2 = (*_serialized_solution2)(dof_index);
    1047       20060 :     val = val + (val2 - val) * _interpolation_factor;
    1048             :   }
    1049       40132 :   return val;
    1050             : }
    1051             : 
    1052             : Real
    1053      962836 : SolutionUserObjectBase::evalMeshFunction(const Point & p,
    1054             :                                          const unsigned int local_var_index,
    1055             :                                          unsigned int func_num,
    1056             :                                          const std::set<subdomain_id_type> * subdomain_ids) const
    1057             : {
    1058             :   // Storage for mesh function output
    1059      962836 :   DenseVector<Number> output;
    1060             : 
    1061             :   // Extract a value from the _mesh_function
    1062             :   {
    1063      962836 :     Threads::spin_mutex::scoped_lock lock(_solution_user_object_mutex);
    1064      962836 :     if (func_num == 1)
    1065      786182 :       (*_mesh_function)(p, 0.0, output, subdomain_ids);
    1066             : 
    1067             :     // Extract a value from _mesh_function2
    1068      176654 :     else if (func_num == 2)
    1069      176654 :       (*_mesh_function2)(p, 0.0, output, subdomain_ids);
    1070             : 
    1071             :     else
    1072           0 :       mooseError("The func_num must be 1 or 2");
    1073      962836 :   }
    1074             : 
    1075             :   // Error if the data is out-of-range, which will be the case if the mesh functions are evaluated
    1076             :   // outside the domain
    1077      962836 :   if (output.size() == 0)
    1078             :   {
    1079           4 :     std::ostringstream oss;
    1080           4 :     p.print(oss);
    1081           4 :     mooseError("Failed to access the data for variable '",
    1082           4 :                _system_variables[local_var_index],
    1083             :                "' at point ",
    1084           0 :                oss.str(),
    1085             :                " in the '",
    1086           4 :                name(),
    1087             :                "' SolutionUserObjectBase");
    1088           0 :   }
    1089     1925664 :   return output(local_var_index);
    1090      962832 : }
    1091             : 
    1092             : std::map<const Elem *, Real>
    1093         132 : SolutionUserObjectBase::evalMultiValuedMeshFunction(
    1094             :     const Point & p,
    1095             :     const unsigned int local_var_index,
    1096             :     unsigned int func_num,
    1097             :     const std::set<subdomain_id_type> * subdomain_ids) const
    1098             : {
    1099             :   // Storage for mesh function output
    1100         132 :   std::map<const Elem *, DenseVector<Number>> temporary_output;
    1101             : 
    1102             :   // Extract a value from the _mesh_function
    1103             :   {
    1104         132 :     Threads::spin_mutex::scoped_lock lock(_solution_user_object_mutex);
    1105         132 :     if (func_num == 1)
    1106          66 :       _mesh_function->discontinuous_value(p, 0.0, temporary_output, subdomain_ids);
    1107             : 
    1108             :     // Extract a value from _mesh_function2
    1109          66 :     else if (func_num == 2)
    1110          66 :       _mesh_function2->discontinuous_value(p, 0.0, temporary_output, subdomain_ids);
    1111             : 
    1112             :     else
    1113           0 :       mooseError("The func_num must be 1 or 2");
    1114         132 :   }
    1115             : 
    1116             :   // Error if the data is out-of-range, which will be the case if the mesh functions are evaluated
    1117             :   // outside the domain
    1118         132 :   if (temporary_output.size() == 0)
    1119             :   {
    1120           0 :     std::ostringstream oss;
    1121           0 :     p.print(oss);
    1122           0 :     mooseError("Failed to access the data for variable '",
    1123           0 :                _system_variables[local_var_index],
    1124             :                "' at point ",
    1125           0 :                oss.str(),
    1126             :                " in the '",
    1127           0 :                name(),
    1128             :                "' SolutionUserObjectBase");
    1129           0 :   }
    1130             : 
    1131             :   // Fill the actual map that is returned
    1132         132 :   std::map<const Elem *, Real> output;
    1133         308 :   for (auto & k : temporary_output)
    1134             :   {
    1135             :     mooseAssert(
    1136             :         k.second.size() > local_var_index,
    1137             :         "In SolutionUserObjectBase::evalMultiValuedMeshFunction variable with local_var_index "
    1138             :             << local_var_index << " does not exist");
    1139         176 :     output[k.first] = k.second(local_var_index);
    1140             :   }
    1141             : 
    1142         264 :   return output;
    1143         132 : }
    1144             : 
    1145             : RealGradient
    1146      113256 : SolutionUserObjectBase::evalMeshFunctionGradient(
    1147             :     const Point & p,
    1148             :     const unsigned int local_var_index,
    1149             :     unsigned int func_num,
    1150             :     const std::set<subdomain_id_type> * subdomain_ids) const
    1151             : {
    1152             :   // Storage for mesh function output
    1153      113256 :   std::vector<Gradient> output;
    1154             : 
    1155             :   // Extract a value from the _mesh_function
    1156             :   {
    1157      113256 :     Threads::spin_mutex::scoped_lock lock(_solution_user_object_mutex);
    1158      113256 :     if (func_num == 1)
    1159       56628 :       _mesh_function->gradient(p, 0.0, output, subdomain_ids);
    1160             : 
    1161             :     // Extract a value from _mesh_function2
    1162       56628 :     else if (func_num == 2)
    1163       56628 :       _mesh_function2->gradient(p, 0.0, output, subdomain_ids);
    1164             : 
    1165             :     else
    1166           0 :       mooseError("The func_num must be 1 or 2");
    1167      113256 :   }
    1168             : 
    1169             :   // Error if the data is out-of-range, which will be the case if the mesh functions are evaluated
    1170             :   // outside the domain
    1171      113256 :   if (output.size() == 0)
    1172             :   {
    1173           0 :     std::ostringstream oss;
    1174           0 :     p.print(oss);
    1175           0 :     mooseError("Failed to access the data for variable '",
    1176           0 :                _system_variables[local_var_index],
    1177             :                "' at point ",
    1178           0 :                oss.str(),
    1179             :                " in the '",
    1180           0 :                name(),
    1181             :                "' SolutionUserObjectBase");
    1182           0 :   }
    1183      226512 :   return output[local_var_index];
    1184      113256 : }
    1185             : 
    1186             : std::map<const Elem *, RealGradient>
    1187         132 : SolutionUserObjectBase::evalMultiValuedMeshFunctionGradient(
    1188             :     const Point & p,
    1189             :     const unsigned int local_var_index,
    1190             :     unsigned int func_num,
    1191             :     const std::set<subdomain_id_type> * subdomain_ids) const
    1192             : {
    1193             :   // Storage for mesh function output
    1194         132 :   std::map<const Elem *, std::vector<Gradient>> temporary_output;
    1195             : 
    1196             :   // Extract a value from the _mesh_function
    1197             :   {
    1198         132 :     Threads::spin_mutex::scoped_lock lock(_solution_user_object_mutex);
    1199         132 :     if (func_num == 1)
    1200         132 :       _mesh_function->discontinuous_gradient(p, 0.0, temporary_output, subdomain_ids);
    1201             : 
    1202             :     // Extract a value from _mesh_function2
    1203           0 :     else if (func_num == 2)
    1204           0 :       _mesh_function2->discontinuous_gradient(p, 0.0, temporary_output, subdomain_ids);
    1205             : 
    1206             :     else
    1207           0 :       mooseError("The func_num must be 1 or 2");
    1208         132 :   }
    1209             : 
    1210             :   // Error if the data is out-of-range, which will be the case if the mesh functions are evaluated
    1211             :   // outside the domain
    1212         132 :   if (temporary_output.size() == 0)
    1213             :   {
    1214           0 :     std::ostringstream oss;
    1215           0 :     p.print(oss);
    1216           0 :     mooseError("Failed to access the data for variable '",
    1217           0 :                _system_variables[local_var_index],
    1218             :                "' at point ",
    1219           0 :                oss.str(),
    1220             :                " in the '",
    1221           0 :                name(),
    1222             :                "' SolutionUserObjectBase");
    1223           0 :   }
    1224             : 
    1225             :   // Fill the actual map that is returned
    1226         132 :   std::map<const Elem *, RealGradient> output;
    1227         308 :   for (auto & k : temporary_output)
    1228             :   {
    1229             :     mooseAssert(
    1230             :         k.second.size() > local_var_index,
    1231             :         "In SolutionUserObjectBase::evalMultiValuedMeshFunction variable with local_var_index "
    1232             :             << local_var_index << " does not exist");
    1233         176 :     output[k.first] = k.second[local_var_index];
    1234             :   }
    1235             : 
    1236         264 :   return output;
    1237         132 : }
    1238             : 
    1239             : const std::vector<std::string> &
    1240         303 : SolutionUserObjectBase::variableNames() const
    1241             : {
    1242         303 :   return _system_variables;
    1243             : }
    1244             : 
    1245             : bool
    1246           0 : SolutionUserObjectBase::isVariableNodal(const std::string & var_name) const
    1247             : {
    1248           0 :   return std::find(_nodal_variables.begin(), _nodal_variables.end(), var_name) !=
    1249           0 :          _nodal_variables.end();
    1250             : }
    1251             : 
    1252             : Real
    1253          60 : SolutionUserObjectBase::scalarValue(Real /*t*/, const std::string & var_name) const
    1254             : {
    1255          60 :   unsigned int var_num = _system->variable_number(var_name);
    1256          60 :   const DofMap & dof_map = _system->get_dof_map();
    1257          60 :   std::vector<dof_id_type> dofs;
    1258          60 :   dof_map.SCALAR_dof_indices(dofs, var_num);
    1259             :   // We can handle only FIRST order scalar variables
    1260         120 :   return directValue(dofs[0]);
    1261          60 : }
    1262             : 
    1263             : void
    1264         346 : SolutionUserObjectBase::readBlockIdMapFromExodusII()
    1265             : {
    1266             : #ifdef LIBMESH_HAVE_EXODUS_API
    1267         346 :   libMesh::ExodusII_IO_Helper & exio_helper = _exodusII_io->get_exio_helper();
    1268         346 :   const auto & id_to_block = exio_helper.id_to_block_names;
    1269         346 :   _block_name_to_id.clear();
    1270         346 :   _block_id_to_name.clear();
    1271         728 :   for (const auto & it : id_to_block)
    1272             :   {
    1273         382 :     _block_name_to_id[it.second] = it.first;
    1274         382 :     _block_id_to_name[it.first] = it.second;
    1275             :   }
    1276             : #endif
    1277         346 : }

Generated by: LCOV version 1.14