17 #include <ATen/ATen.h> 18 #include "libmesh/id_types.h" 19 #include "neml2/tensors/functions/jacrev.h" 20 #include "neml2/dispatchers/ValueMapLoader.h" 21 #include "neml2/misc/string_utils.h" 22 #include "neml2/base/Settings.h" 31 params.addParam<
bool>(
32 "manage_state_advance",
34 "Keep state and forces on the device and advance it to old_state and old_forces without a " 35 "roundtrip through MOOSE materials. This is only recommended for explicit time integration " 36 "or when absolutely no restepping occurs (e.g. failed timesteps).");
37 params.addParam<
bool>(
38 "debug_inputs_on_failure",
40 "When a NEML2 solve fails, append a detailed dump of input tensors (defined/missing, " 41 "shapes, and devices) to the error message.");
50 params.addClassDescription(
"Execute the specified NEML2 model");
52 params.addRequiredParam<UserObjectName>(
53 "batch_index_generator",
54 "The NEML2BatchIndexGenerator used to generate the element-to-batch-index map.");
55 params.addParam<std::vector<UserObjectName>>(
58 "List of MOOSE*ToNEML2 user objects gathering MOOSE data as NEML2 input variables");
59 params.addParam<std::vector<UserObjectName>>(
62 "List of MOOSE*ToNEML2 user objects gathering MOOSE data as NEML2 model parameters");
69 params.set<
ExecFlagEnum>(
"execute_on") = execute_options;
79 _manage_state_advance(getParam<bool>(
"manage_state_advance")),
80 _debug_inputs_on_failure(getParam<bool>(
"debug_inputs_on_failure")),
89 for (
const auto & gatherer_name :
getParam<std::vector<UserObjectName>>(
"gatherers"))
91 for (
const auto & gatherer_name :
getParam<std::vector<UserObjectName>>(
"param_gatherers"))
101 for (
const auto & gatherer_name :
getParam<std::vector<UserObjectName>>(
"gatherers"))
105 const auto & uo = getUserObjectByName<MOOSEToNEML2>(gatherer_name,
false);
108 auto sep =
model().settings().history_separator();
109 auto [base_name, history_order] = neml2::parse_history(uo.NEML2Name(), sep);
112 "The gatherer for history variable `",
114 "` is not needed when `manage_state_advance = true`.");
121 for (
const auto & gatherer_name :
getParam<std::vector<UserObjectName>>(
"param_gatherers"))
125 const auto & uo = getUserObjectByName<MOOSEToNEML2>(gatherer_name,
false);
131 for (
const auto & [iname, ivar] :
model().input_variables())
137 paramError(
"gatherers",
"The required model input `", iname,
"` is not gathered");
142 for (
const auto & [iname, ivar] :
model().input_variables())
143 if (ivar->history_order() > 0)
155 const neml2::VariableName & var)
159 "The NEML2 input variable `",
161 "` gathered by UO '",
163 "' is already gathered by another gatherer.");
169 const std::string & param)
173 "The NEML2 model parameter `",
175 "` gathered by UO '",
177 "' is already gathered by another gatherer.");
200 mooseError(
"The mesh changed while `manage_state_advance = true` for NEML2 model executor '",
202 "'. This mode requires a fixed mesh because state history is cached on the device.");
226 auto success =
solve();
248 for (
auto & [var, val] :
_in)
259 for (
const auto & [p, tensor] : dy)
260 model().get_parameter(p).requires_grad_(
true);
262 catch (std::exception & e)
264 mooseError(
"An error occurred while filling inputs for the NEML2 model. Error message:\n",
274 std::vector<neml2::Tensor>
defined;
275 for (
const auto & [key,
value] :
_in)
277 const auto s = neml2::utils::broadcast_dynamic_sizes(
defined);
281 if (
value.dynamic_sizes() != s)
282 _in[key] =
value.dynamic_unsqueeze(0).dynamic_expand(s);
293 auto sep =
model().settings().history_separator();
294 auto [base_name, order] = neml2::parse_history(
name, sep);
295 mooseAssert(order > 0,
"Invalid history order");
298 auto curr_name = order == 1 ? base_name : base_name + sep + std::to_string(order - 1);
299 if (
_out.count(curr_name))
301 else if (
_in.count(curr_name))
304 mooseError(
"Failed to find cached value for history variable: ",
name);
314 TIME_SECTION(
"NEML2 solve", 3,
"Solving NEML2 material model");
317 auto prev_dtype = neml2::get_default_dtype();
318 neml2::set_default_dtype(neml2::kFloat64);
324 neml2::ValueMapLoader loader(
_in, 0);
333 neml2::set_default_dtype(prev_dtype);
335 catch (std::exception & e)
341 auto shape_to_string = [](
const neml2::TensorShapeRef & shape) -> std::string
343 std::ostringstream
os;
345 for (std::size_t i = 0; i < shape.size(); ++i)
355 std::ostringstream
os;
356 os <<
"\nNEML2 input variables:\n";
357 for (
const auto & [var, val] :
model().input_variables())
359 os <<
" - " << var <<
": ";
360 const auto it =
_in.find(var);
363 else if (!it->second.defined())
367 const auto & val = it->second;
368 const auto & v =
model().input_variable(var);
369 neml2::TensorShape expected;
370 const auto & intmd_sizes = v.intmd_sizes();
371 expected.insert(expected.end(), intmd_sizes.begin(), intmd_sizes.end());
372 const auto & base_sizes = v.base_sizes();
373 expected.insert(expected.end(), base_sizes.begin(), base_sizes.end());
375 os <<
"device=" << val.device() <<
" dtype=" << val.scalar_type()
376 <<
" sizes=" << shape_to_string(val.sizes())
377 <<
" batch=" << shape_to_string(val.batch_sizes().concrete())
378 <<
" expected_base=" << shape_to_string(expected);
382 auto cpu = val.detach().to(val.options().device(at::kCPU));
383 auto flat = cpu.reshape({-1});
384 auto min = flat.min().item<
double>();
385 auto max = flat.max().item<
double>();
386 auto mean = flat.mean().item<
double>();
387 auto has_nan = at::isnan(flat).any().item<
bool>();
388 auto has_inf = at::isinf(flat).any().item<
bool>();
389 os <<
" min=" <<
min <<
" max=" <<
max <<
" mean=" << mean
390 <<
" nan=" << (has_nan ?
"true" :
"false")
391 <<
" inf=" << (has_inf ?
"true" :
"false");
400 os <<
"NEML2 stateful variables:\n";
403 os <<
" - " << var <<
": ";
404 const auto it_out =
_out.find(var);
405 const auto it_in =
_in.find(var);
406 if (it_out ==
_out.end() || it_in ==
_in.end())
410 const auto it = it_out !=
_out.end() ? it_out : it_in;
411 const auto & val = it->second;
412 os <<
"device=" << val.device() <<
" dtype=" << val.scalar_type()
413 <<
" sizes=" << shape_to_string(val.sizes())
414 <<
" batch=" << shape_to_string(val.batch_sizes().concrete());
418 auto cpu = val.detach().to(val.options().device(at::kCPU));
419 auto flat = cpu.reshape({-1});
420 auto min = flat.min().item<
double>();
421 auto max = flat.max().item<
double>();
422 auto mean = flat.mean().item<
double>();
423 auto has_nan = at::isnan(flat).any().item<
bool>();
424 auto has_inf = at::isinf(flat).any().item<
bool>();
425 os <<
" min=" <<
min <<
" max=" <<
max <<
" mean=" << mean
426 <<
" nan=" << (has_nan ?
"true" :
"false")
427 <<
" inf=" << (has_inf ?
"true" :
"false");
454 for (
auto & [p, target] : dy)
455 target = neml2::jacrev(
_out[y],
456 model().get_parameter(p),
468 for (
auto & [x, target] : dy)
471 if (source.defined())
472 target = source.to(
output_device()).dynamic_expand({neml2::Size(
N)});
478 catch (std::exception & e)
480 mooseError(
"An error occurred while retrieving outputs from the NEML2 model. Error message:\n",
502 std::string msg =
"NEML2 model execution failed on at least one processor with ID " +
503 std::to_string(pid) +
". Error message:\n";
506 msg +=
"\nTo recover, the solution will fail and then be re-attempted with a reduced time " 508 _console << COLOR_YELLOW << msg << COLOR_DEFAULT << std::endl;
520 mooseError(
"NEML2 output variables and derivatives must be retrieved during object " 521 "construction. This is a code problem.");
524 const neml2::Tensor &
529 if (!
model().output_variables().count(output_name))
530 mooseError(
"Trying to retrieve a non-existent NEML2 output variable '", output_name,
"'.");
535 const neml2::Tensor &
537 const neml2::VariableName & input_name)
const 541 if (!
model().output_variables().count(output_name))
542 mooseError(
"Trying to retrieve the derivative of NEML2 output variable '",
544 "' with respect to NEML2 input variable '",
546 "', but the NEML2 output variable does not exist.");
548 if (!
model().input_variables().count(input_name))
549 mooseError(
"Trying to retrieve the derivative of NEML2 output variable '",
551 "' with respect to NEML2 input variable '",
553 "', but the NEML2 input variable does not exist.");
558 const neml2::Tensor &
560 const std::string & parameter_name)
const 564 if (!
model().output_variables().count(output_name))
565 mooseError(
"Trying to retrieve the derivative of NEML2 output variable '",
567 "' with respect to NEML2 model parameter '",
569 "', but the NEML2 output variable does not exist.");
571 if (
model().named_parameters().count(parameter_name) != 1)
572 mooseError(
"Trying to retrieve the derivative of NEML2 output variable '",
574 "' with respect to NEML2 model parameter '",
576 "', but the NEML2 model parameter does not exist.");
virtual void addGatheredVariable(const UserObjectName &, const neml2::VariableName &)
Register a NEML2 input variable gathered by a gatherer.
neml2::WorkScheduler * scheduler()
Get the work scheduler.
const bool _debug_inputs_on_failure
Dump input tensor info on failure to aid debugging.
std::vector< const MOOSEToNEML2 * > _gatherers
MOOSE data gathering user objects.
A MultiMooseEnum object to hold "execute_on" flags.
static InputParameters validParams()
void paramError(const std::string ¶m, Args... args) const
Emits an error prefixed with the file and line number of the given param (from the input file) along ...
const T & getParam(const std::string &name) const
Retrieve a parameter for the object.
void execute() override
Execute method.
neml2::DerivMap _retrieved_derivatives
set of derivatives that were retrieved (by other objects)
bool shouldCompute(const SubProblem &)
Determine whether the NEML2 material model should be evaluated.
std::vector< const MOOSEToNEML2 * > _param_gatherers
Interface class to provide common input parameters, members, and methods for MOOSEObjects that use NE...
const ExecFlagType & _current_execute_flag
Reference to FEProblemBase.
virtual bool solve()
Perform the material update.
neml2::Model & model() const
Get the NEML2 model.
bool _error
Whether an error was encountered.
const neml2::Device & device() const
Get the target compute device.
processor_id_type rank() const
std::set< neml2::VariableName > _gathered_variable_names
neml2::ValueMap _out
The output variables of the material model.
static std::string NEML2_help_message
const Parallel::Communicator & _communicator
const ExecFlagType EXEC_TIMESTEP_END
std::set< std::string > _depend_uo
Depend UserObjects that to be used both for determining user object sorting and by AuxKernel for find...
const std::unique_ptr< DispatcherType > & dispatcher() const
Get the work dispatcher.
std::basic_ostream< charT, traits > * os
virtual void extractOutputs()
Extract output derivatives with respect to input variables and model parameters.
registerMooseObject("MooseApp", NEML2ModelExecutor)
void setFailNextNonlinearConvergenceCheck()
Skip further residual evaluations and fail the next nonlinear convergence check(s) ...
auto max(const L &left, const R &right)
bool isEmpty() const
Whether the batch is empty.
ExecFlagEnum getDefaultExecFlagEnum()
std::set< std::string > _gathered_parameter_names
void initialSetup() override
Gets called at the beginning of the simulation before this object is asked to do its job...
const NEML2BatchIndexGenerator & _batch_index_generator
The NEML2BatchIndexGenerator used to generate the element-to-batch-index map.
const neml2::Tensor & getOutputDerivative(const neml2::VariableName &output_name, const neml2::VariableName &input_name) const
Get a reference(!) to the requested output derivative view.
std::string _error_message
Error message.
FEProblemBase & _fe_problem
Reference to the FEProblemBase for this user object.
uint8_t processor_id_type
const std::string & name() const
Get the name of the class.
int & _t_step
The number of the time step.
const bool _manage_state_advance
Advance state on device (rather than via MOSOE material properties)
Real value(unsigned n, unsigned alpha, unsigned beta, Real x)
std::map< neml2::VariableName, std::map< std::string, neml2::Tensor > > _retrieved_parameter_derivatives
set of parameter derivatives that were retrieved (by other objects)
virtual void fillInputs()
Fill input variables and model parameters using the gatherers.
void initialize() override
Called before execute() is ever called so that data can be cleared.
void maxloc(T &r, unsigned int &max_id) const
const ExecFlagType EXEC_LINEAR
virtual bool solverSystemConverged(const unsigned int solver_sys_num) override
virtual void checkExecutionStage() const final
Prevent output and derivative retrieval after construction.
neml2::ValueMap _state_vars
Cached variables from the last successful step (for on-device advance)
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
static InputParameters actionParams()
Parameters that can be specified under the NEML2Action common area.
const ExecFlagType EXEC_NONLINEAR
void advanceState()
Save stateful variables for on-device state advance.
const neml2::Tensor & getOutput(const neml2::VariableName &output_name) const
Get a reference(!) to the requested output view.
std::map< std::string, neml2::Tensor > _model_params
The model parameters to update (gathered from MOOSE)
const neml2::Device & output_device() const
Get the target output device.
NEML2ModelExecutor executes a NEML2 model.
bool _output_ready
flag that indicates if output data has been fully computed
neml2::DerivMap _dout_din
The derivative of the output variables w.r.t. the input variables.
virtual void addGatheredParameter(const UserObjectName &, const std::string &)
Register a NEML2 model parameter gathered by a gatherer.
void meshChanged() override
Called on this object when the mesh changes.
void mooseError(Args &&... args) const
Emits an error prefixed with object name and type and optionally a file path to the top-level block p...
static InputParameters validParams()
const neml2::Tensor & getOutputParameterDerivative(const neml2::VariableName &output_name, const std::string ¶meter_name) const
Get a reference(!) to the requested output parameter derivative view.
void finalize() override
Finalize.
const ConsoleStream _console
An instance of helper class to write streams to the Console objects.
virtual void validateModel() const
Validate the NEML2 material model.
virtual bool isTransient() const override
auto min(const L &left, const R &right)
virtual void expandInputs()
Expand tensor shapes if necessary to conformal sizes.
std::size_t getBatchIndex() const
Get the current batch index (in almost all cases this is the total batch size)
class infix_ostream_iterator if defined(__GNUC__) &&!defined(__clang__) &&(__GNUC__<
GCC9 currently hits a "no type named 'value_type'" error during build if this is removed and iterator...
std::size_t getBatchIndex(dof_id_type elem_id) const
Get the batch index for the given element ID.
neml2::ValueMap _in
The input variables of the material model.
neml2::ValueMap _retrieved_outputs
set of output variables that were retrieved (by other objects)
NEML2ModelExecutor(const InputParameters ¶ms)
virtual bool startedInitialSetup()
Returns true if we are in or beyond the initialSetup stage.
NEML2BatchIndexGenerator iterates over the mesh and generates a map from element ID to batch index wh...
const ExecFlagType EXEC_INITIAL