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