LCOV - code coverage report
Current view: top level - src/neml2/actions - NEML2Action.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: fa5e60 Lines: 233 323 72.1 %
Date: 2026-06-24 08:03:36 Functions: 19 22 86.4 %
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 "NEML2Action.h"
      11             : #include "NEML2ActionCommon.h"
      12             : #include "FEProblem.h"
      13             : #include "Factory.h"
      14             : #include "NEML2Utils.h"
      15             : #include "InputParameterWarehouse.h"
      16             : 
      17             : #ifdef NEML2_ENABLED
      18             : #include "neml2/neml2.h"
      19             : #include "neml2/base/Settings.h"
      20             : #include "neml2/models/VariableBase.h"
      21             : #endif
      22             : 
      23             : registerMooseAction("MooseApp", NEML2Action, "parse_neml2");
      24             : registerMooseAction("MooseApp", NEML2Action, "add_material");
      25             : registerMooseAction("MooseApp", NEML2Action, "add_user_object");
      26             : 
      27             : #ifdef NEML2_ENABLED
      28             : // NEML2 variable type --> MOOSE type
      29             : const std::map<neml2::TensorType, std::string> tensor_type_map = {
      30             :     {neml2::TensorType::kScalar, "Real"},
      31             :     {neml2::TensorType::kSR2, "SymmetricRankTwoTensor"},
      32             :     {neml2::TensorType::kR2, "RankTwoTensor"},
      33             :     {neml2::TensorType::kSSR4, "SymmetricRankFourTensor"},
      34             :     {neml2::TensorType::kR4, "RankFourTensor"},
      35             :     {neml2::TensorType::kRot, "RealVectorValue"}};
      36             : // NEML2 (output, input) type --> NEML2 derivative type
      37             : const std::map<std::pair<neml2::TensorType, neml2::TensorType>, neml2::TensorType> deriv_type_map =
      38             :     {
      39             :         {{neml2::TensorType::kScalar, neml2::TensorType::kScalar}, neml2::TensorType::kScalar},
      40             :         {{neml2::TensorType::kSR2, neml2::TensorType::kSR2}, neml2::TensorType::kSSR4},
      41             :         {{neml2::TensorType::kSR2, neml2::TensorType::kScalar}, neml2::TensorType::kSR2},
      42             :         {{neml2::TensorType::kScalar, neml2::TensorType::kSR2}, neml2::TensorType::kSR2},
      43             :         {{neml2::TensorType::kR2, neml2::TensorType::kR2}, neml2::TensorType::kR4},
      44             :         {{neml2::TensorType::kR2, neml2::TensorType::kScalar}, neml2::TensorType::kR2},
      45             :         {{neml2::TensorType::kScalar, neml2::TensorType::kR2}, neml2::TensorType::kR2},
      46             : };
      47             : #endif
      48             : 
      49             : InputParameters
      50          67 : NEML2Action::validParams()
      51             : {
      52          67 :   InputParameters params = NEML2ActionCommon::commonParams();
      53         134 :   params.addClassDescription("Set up the NEML2 material model");
      54         268 :   params.addParam<std::string>("executor_name",
      55             :                                "Name of the NEML2ModelExecutor user object. The default name is "
      56             :                                "'neml2_<model-name>_<block-name>' where <model-name> is the NEML2 "
      57             :                                "model's name, and <block-name> is this action sub-block's name.");
      58         268 :   params.addParam<std::string>(
      59             :       "batch_index_generator_name",
      60             :       "Name of the NEML2BatchIndexGenerator user object. The default name is "
      61             :       "'neml2_index_<model-name>_<block-name>' where <model-name> is the NEML2 model's name, and "
      62             :       "<block-name> is this action sub-block's name.");
      63         201 :   params.addParam<std::vector<SubdomainName>>(
      64             :       "block", {}, "List of blocks (subdomains) where the material model is defined");
      65          67 :   return params;
      66           0 : }
      67             : 
      68          25 : NEML2Action::NEML2Action(const InputParameters & params)
      69             :   : Action(params),
      70          75 :     _executor_name(isParamValid("executor_name")
      71         100 :                        ? getParam<std::string>("executor_name")
      72         100 :                        : "neml2_" + getParam<std::string>("model") + "_" + name()),
      73          75 :     _idx_generator_name(isParamValid("batch_index_generator_name")
      74         100 :                             ? getParam<std::string>("batch_index_generator_name")
      75         100 :                             : "neml2_index_" + getParam<std::string>("model") + "_" + name()),
      76          50 :     _block(getParam<std::vector<SubdomainName>>("block")),
      77         100 :     _skip_input_variables(getParam<std::vector<std::string>>("skip_input_variables"))
      78             : {
      79          25 :   NEML2Utils::assertNEML2Enabled();
      80             : 
      81             :   // Apply parameters under the common area, i.e., under [NEML2]
      82          25 :   const auto & all_params = _app.getInputParameterWarehouse().getInputParameters();
      83          25 :   auto & sub_block_params = *(all_params.find(uniqueActionName())->second.get());
      84          25 :   const auto & common_action = getCommonAction();
      85          25 :   sub_block_params.applyParameters(common_action.parameters());
      86             : 
      87             :   // Set up optional output variable initialization
      88          75 :   auto init_vars = getParam<std::vector<MaterialPropertyName>>("initialize_outputs");
      89          50 :   auto init_vals = getParam<std::vector<MaterialPropertyName>>("initialize_output_values");
      90          25 :   if (init_vars.size() != init_vals.size())
      91           0 :     paramError("initialize_outputs",
      92             :                "initialize_outputs should have the same length as initialize_output_values");
      93          25 :   for (auto i : index_range(init_vars))
      94           0 :     _initialize_output_values[init_vars[i]] = init_vals[i];
      95             : 
      96             :   // Set up additional outputs for each requested material property
      97          50 :   auto outputs = getParam<std::vector<MaterialPropertyName>>("export_outputs");
      98          50 :   auto output_targets = getParam<std::vector<std::vector<OutputName>>>("export_output_targets");
      99          25 :   if (outputs.size() != output_targets.size())
     100           0 :     paramError("export_outputs",
     101             :                "export_outputs should have the same length as export_output_targets");
     102          85 :   for (auto i : index_range(outputs))
     103          60 :     _export_output_targets[outputs[i]] = output_targets[i];
     104             : 
     105             : #ifdef NEML2_ENABLED
     106             :   // File name and CLI args
     107          50 :   _fname = getParam<DataFileName>("input");
     108          75 :   _cli_args = getParam<std::vector<std::string>>("cli_args");
     109             : 
     110             :   // Load input file
     111          25 :   auto factory = neml2::load_input(std::string(_fname), neml2::utils::join(_cli_args, " "));
     112          50 :   _model = NEML2Utils::getModel(*factory, getParam<std::string>("model"));
     113             : #endif
     114          25 : }
     115             : 
     116             : const NEML2ActionCommon &
     117          25 : NEML2Action::getCommonAction() const
     118             : {
     119          25 :   auto common_block = _awh.getActions<NEML2ActionCommon>();
     120             :   mooseAssert(common_block.size() == 1, "There must exist one and only one common NEML2 action.");
     121          50 :   return *common_block[0];
     122          25 : }
     123             : 
     124             : #ifndef NEML2_ENABLED
     125             : 
     126             : void
     127           0 : NEML2Action::act()
     128             : {
     129           0 : }
     130             : 
     131             : #else
     132             : 
     133             : static std::string
     134         104 : obscureObjectName(const std::string & name,
     135             :                   const std::string & prefix,
     136             :                   const std::string & suffix,
     137             :                   const std::string & block)
     138             : {
     139         104 :   return "__" + prefix + "_" + name + "_" + suffix + "_" + block + "__";
     140             : }
     141             : 
     142             : void
     143          74 : NEML2Action::act()
     144             : {
     145          74 :   if (_current_task == "parse_neml2")
     146             :   {
     147          25 :     if (_app.parameters().have_parameter<bool>("parse_neml2_only"))
     148          25 :       if (!_app.parameters().get<bool>("parse_neml2_only"))
     149          25 :         return;
     150           0 :     printSummary();
     151             :   }
     152             : 
     153             :   // Look up the MOOSE tensor type string for a NEML2 tensor type, or error
     154         104 :   auto mooseType = [this](neml2::TensorType type) -> const std::string &
     155             :   {
     156         104 :     auto it = tensor_type_map.find(type);
     157         104 :     if (it == tensor_type_map.end())
     158           0 :       mooseError("NEML2 type ", type, " not yet mapped to MOOSE");
     159         208 :     return it->second;
     160          49 :   };
     161             : 
     162          49 :   if (_current_task == "add_user_object")
     163             :   {
     164          25 :     setupInputMappings(*_model);
     165          24 :     setupParameterMappings(*_model);
     166          24 :     setupOutputMappings(*_model);
     167          24 :     setupDerivativeMappings(*_model);
     168          24 :     setupParameterDerivativeMappings(*_model);
     169             : 
     170          24 :     printSummary();
     171             : 
     172             :     // Create and register a MOOSEToNEML2 gatherer user object, returning its name
     173          41 :     auto addGatherer = [&](const std::string & moose_name,
     174             :                            const std::string & neml2_name,
     175             :                            NEML2Utils::MOOSEIOType moose_type,
     176             :                            neml2::TensorType neml2_type,
     177             :                            const std::string & suffix,
     178             :                            const std::string & type_prefix = "")
     179             :     {
     180          82 :       auto obj_name = obscureObjectName(moose_name, "moose_to_neml2", suffix, name());
     181          41 :       auto obj_type = "MOOSE" + type_prefix + mooseType(neml2_type) + "ToNEML2";
     182          41 :       auto obj_params = _factory.getValidParams(obj_type);
     183          82 :       obj_params.set<std::string>("from_moose") = moose_name;
     184          82 :       obj_params.set<std::string>("to_neml2") = neml2_name;
     185          41 :       obj_params.set<MooseEnum>("quantity_type").assign(static_cast<int>(moose_type));
     186          82 :       obj_params.set<std::vector<SubdomainName>>("block") = _block;
     187          41 :       _problem->addUserObject(obj_type, obj_name, obj_params);
     188          82 :       return obj_name;
     189          41 :     };
     190             : 
     191             :     // MOOSEToNEML2 input gatherers
     192          24 :     std::vector<UserObjectName> gatherers;
     193          24 :     const auto sep = _model->settings().history_separator();
     194          63 :     for (const auto & input : _inputs)
     195         117 :       gatherers.push_back(addGatherer(input.name,
     196          39 :                                       neml2::history_name(input.name, input.history_order, sep),
     197          39 :                                       input.moose_type,
     198          39 :                                       input.neml2_type,
     199          78 :                                       std::to_string(input.history_order),
     200          39 :                                       input.history_order == 1 ? "Old" : ""));
     201             : 
     202             :     // Additional NEML2Kernels that provide input data
     203          72 :     for (const auto & kernel_name : getParam<std::vector<std::string>>("input_kernels"))
     204             :     {
     205           0 :       if (!_model->input_variables().count(kernel_name))
     206           0 :         paramError("input_kernels",
     207             :                    "The NEML2 kernel ",
     208             :                    kernel_name,
     209             :                    " name does not match any NEML2 input variable.");
     210           0 :       gatherers.push_back(kernel_name);
     211             :     }
     212             : 
     213             :     // MOOSEToNEML2 parameter gatherers
     214          24 :     std::vector<UserObjectName> param_gatherers;
     215          26 :     for (const auto & param : _params)
     216           2 :       param_gatherers.push_back(
     217          10 :           addGatherer(param.name, param.name, param.moose_type, param.neml2_type, ""));
     218             : 
     219             :     // The index generator UO
     220             :     {
     221          24 :       auto type = "NEML2BatchIndexGenerator";
     222          48 :       auto params = _factory.getValidParams(type);
     223          24 :       params.applyParameters(parameters());
     224          48 :       _problem->addUserObject(type, _idx_generator_name, params);
     225          24 :     }
     226             : 
     227             :     // The Executor UO
     228             :     {
     229          24 :       auto type = "NEML2ModelExecutor";
     230          48 :       auto params = _factory.getValidParams(type);
     231          24 :       params.applyParameters(parameters());
     232          72 :       params.set<UserObjectName>("batch_index_generator") = _idx_generator_name;
     233          48 :       params.set<std::vector<UserObjectName>>("gatherers") = gatherers;
     234          24 :       params.set<std::vector<UserObjectName>>("param_gatherers") = param_gatherers;
     235          48 :       _problem->addUserObject(type, _executor_name, params);
     236          24 :     }
     237          24 :   }
     238             : 
     239          48 :   if (_current_task == "add_material")
     240             :   {
     241             :     // Look up the NEML2 derivative tensor type for (y, x) variables, or error
     242             :     auto derivTensorType =
     243          24 :         [this](neml2::TensorType y_type, neml2::TensorType x_type, const std::string & deriv_name)
     244             :     {
     245          24 :       auto it = deriv_type_map.find({y_type, x_type});
     246          24 :       if (it == deriv_type_map.end())
     247           0 :         mooseError("NEML2 derivative type for ", deriv_name, " not yet mapped to MOOSE");
     248          48 :       return it->second;
     249          24 :     };
     250             : 
     251             :     // Create and register a NEML2ToMOOSE material property retriever; `extra` lets
     252             :     // each caller add the bits that are unique to outputs vs. (parameter) derivatives
     253          63 :     auto addRetriever = [&](const std::string & moose_name,
     254             :                             const std::string & neml2_var,
     255             :                             neml2::TensorType tensor_type,
     256             :                             auto && extra)
     257             :     {
     258         252 :       auto obj_name = obscureObjectName(moose_name, "neml2_to_moose", "", name());
     259          63 :       auto obj_type = "NEML2ToMOOSE" + mooseType(tensor_type) + "MaterialProperty";
     260          63 :       auto obj_params = _factory.getValidParams(obj_type);
     261         126 :       obj_params.set<UserObjectName>("neml2_executor") = _executor_name;
     262         189 :       obj_params.set<MaterialPropertyName>("to_moose") = moose_name;
     263          63 :       obj_params.set<std::string>("from_neml2") = neml2_var;
     264         126 :       obj_params.set<std::vector<SubdomainName>>("block") = _block;
     265          63 :       if (_export_output_targets.count(moose_name))
     266         144 :         obj_params.set<std::vector<OutputName>>("outputs") = _export_output_targets[moose_name];
     267          63 :       extra(obj_params);
     268          63 :       _problem->addMaterial(obj_type, obj_name, obj_params);
     269          87 :     };
     270             : 
     271             :     // NEML2ToMOOSE output retrievers
     272          63 :     for (const auto & output : _outputs)
     273             :     {
     274          39 :       if (output.moose_type != NEML2Utils::MOOSEIOType::MATERIAL)
     275           0 :         paramError("moose_output_types",
     276             :                    "Unsupported type corresponding to the moose output ",
     277           0 :                    output.name);
     278          39 :       addRetriever(output.name,
     279          39 :                    output.name,
     280          39 :                    output.neml2_type,
     281          39 :                    [&](InputParameters & p)
     282             :                    {
     283          39 :                      if (_initialize_output_values.count(output.name))
     284           0 :                        p.set<MaterialPropertyName>("moose_material_property_init") =
     285           0 :                            _initialize_output_values[output.name];
     286          39 :                    });
     287             :     }
     288             : 
     289             :     // NEML2ToMOOSE derivative retrievers
     290          48 :     for (const auto & deriv : _derivs)
     291             :     {
     292          24 :       auto type = derivTensorType(_model->output_variable(deriv.y).type(),
     293          24 :                                   _model->input_variable(deriv.x).type(),
     294          24 :                                   deriv.name);
     295          24 :       addRetriever(deriv.name,
     296          24 :                    deriv.y,
     297             :                    type,
     298          24 :                    [&](InputParameters & p)
     299          72 :                    { p.set<std::string>("neml2_input_derivative") = deriv.x; });
     300             :     }
     301             : 
     302             :     // NEML2ToMOOSE parameter derivative retrievers
     303          24 :     for (const auto & param_deriv : _param_derivs)
     304             :     {
     305           0 :       auto type = derivTensorType(_model->output_variable(param_deriv.y).type(),
     306           0 :                                   _model->get_parameter(param_deriv.x).type(),
     307           0 :                                   param_deriv.name);
     308           0 :       addRetriever(param_deriv.name,
     309           0 :                    param_deriv.y,
     310             :                    type,
     311           0 :                    [&](InputParameters & p)
     312           0 :                    { p.set<std::string>("neml2_parameter_derivative") = param_deriv.x; });
     313             :     }
     314             :   }
     315             : }
     316             : 
     317             : NEML2Utils::MOOSEIOType
     318          42 : NEML2Action::inferMOOSEIOType(const neml2::VariableName & name,
     319             :                               const neml2::TensorType & type) const
     320             : {
     321             :   // neml2::kScalar can only come from scalar variable, function, or variable
     322          42 :   if (type == neml2::TensorType::kScalar)
     323             :   {
     324          42 :     bool is_time = _problem->isTransient() && (name == "t" || name == "time");
     325          42 :     bool has_scalar = _problem->hasScalarVariable(name);
     326          42 :     bool has_func = _problem->hasFunction(name);
     327          42 :     bool has_var = _problem->hasVariable(name);
     328          42 :     if (int(is_time) + int(has_scalar) + int(has_func) + int(has_var) > 1)
     329           2 :       mooseError("Trying to infer MOOSE data type for NEML2 variable ",
     330             :                  name,
     331             :                  ". The name matches multiple types (",
     332           1 :                  (is_time ? "time " : ""),
     333           1 :                  (has_scalar ? "scalar variable " : ""),
     334           1 :                  (has_func ? "function " : ""),
     335           1 :                  (has_var ? "variable " : ""),
     336             :                  "). To avoid ambiguity, please explicitly specify the type in the NEML2 action.");
     337          41 :     if (is_time)
     338           0 :       return NEML2Utils::MOOSEIOType::TIME;
     339          41 :     if (has_scalar)
     340           0 :       return NEML2Utils::MOOSEIOType::SCALAR;
     341          41 :     if (has_func)
     342           0 :       return NEML2Utils::MOOSEIOType::FUNCTION;
     343          41 :     if (has_var)
     344          25 :       return NEML2Utils::MOOSEIOType::VARIABLE;
     345             :     // if neither function nor variable exists, let's assume it's a material property
     346             :     // note that we can't explicitly check if a material property with the given name exists,
     347             :     // because materials are added _after_ user objects (see Moose.C)
     348          16 :     if (!has_func && !has_var)
     349          16 :       return NEML2Utils::MOOSEIOType::MATERIAL;
     350             :   }
     351             : 
     352             :   // non-scalar can only come from material properties
     353           0 :   return NEML2Utils::MOOSEIOType::MATERIAL;
     354             : }
     355             : 
     356             : void
     357          25 : NEML2Action::setupInputMappings(const neml2::Model & model)
     358             : {
     359          50 :   const auto & kernels = getParam<std::vector<std::string>>("input_kernels");
     360             : 
     361             :   // Default mapping
     362          64 :   for (const auto & [vname, var] : model.input_variables())
     363             :   {
     364             :     // user requested to skip
     365          40 :     if (std::find(_skip_input_variables.begin(), _skip_input_variables.end(), var->base_name()) !=
     366          80 :         _skip_input_variables.end())
     367           0 :       continue;
     368             : 
     369             :     // skip if the input is directly provided by a custom MOOSEToNEML2 object
     370          40 :     bool gathered_by_kernel = false;
     371          40 :     for (const auto & kernel_name : kernels)
     372           0 :       if (vname == kernel_name)
     373             :       {
     374           0 :         gathered_by_kernel = true;
     375           0 :         break;
     376             :       }
     377          40 :     if (gathered_by_kernel)
     378           0 :       continue;
     379             : 
     380             :     // skip if manage_state_advance is true and the variable is stateful (history_order > 0),
     381             :     // because in that case we will gather the variable on the compute device and do not need to set
     382             :     // up a gatherer for it
     383         120 :     if (getParam<bool>("manage_state_advance") && var->history_order() > 0)
     384           0 :       continue;
     385             : 
     386          79 :     _inputs.push_back({var->base_name(),
     387          79 :                        inferMOOSEIOType(var->base_name(), var->type()),
     388          39 :                        var->type(),
     389          39 :                        var->history_order()});
     390             :   }
     391             : 
     392             :   // User-specified mapping (overrides default mapping)
     393          24 :   const auto [input_types, inputs] =
     394          96 :       getInputParameterMapping<NEML2Utils::MOOSEIOType, std::string>("input_types", "inputs");
     395             : 
     396          24 :   for (auto i : index_range(inputs))
     397             :   {
     398             :     // Check if the input variable also appears in skip_input_variables
     399             :     const auto itr =
     400           0 :         std::find(_skip_input_variables.begin(), _skip_input_variables.end(), inputs[i]);
     401           0 :     if (itr != _skip_input_variables.end())
     402           0 :       paramError("skip_input_variables",
     403             :                  "The input variable ",
     404           0 :                  inputs[i],
     405             :                  " is listed in skip_input_variables, but it also appears in inputs. Please "
     406             :                  "remove it from either list.");
     407             :     // Check if the input variable exists in the NEML2 model
     408           0 :     if (model.input_variables().count(inputs[i]) == 0)
     409           0 :       paramError("inputs", "The neml2 input variable ", inputs[i], " does not exist.");
     410             :     // Check if the input variable is already gathered by a custom MOOSEToNEML2 object
     411           0 :     bool gathered_by_kernel = false;
     412           0 :     for (const auto & kernel_name : kernels)
     413           0 :       if (inputs[i] == kernel_name)
     414             :       {
     415           0 :         gathered_by_kernel = true;
     416           0 :         break;
     417             :       }
     418           0 :     if (gathered_by_kernel)
     419           0 :       paramError("inputs",
     420             :                  "The input variable ",
     421           0 :                  inputs[i],
     422             :                  " is listed in inputs, but it also appears in input_kernels. Please "
     423             :                  "remove it from either list.");
     424             :     // Check if the input variable is stateful and manage_state_advance is true
     425           0 :     if (getParam<bool>("manage_state_advance"))
     426           0 :       if (model.input_variable(inputs[i]).history_order() > 0)
     427           0 :         paramError(
     428             :             "inputs",
     429             :             "The input variable ",
     430           0 :             inputs[i],
     431             :             " is listed in inputs, but it is stateful (history_order > 0) and manage_state_advance "
     432             :             "is true. Please remove it from inputs, or set manage_state_advance to false.");
     433             : 
     434             :     // Get the existing mapping for this neml2 input variable and override it
     435           0 :     for (auto & input : _inputs)
     436           0 :       if (input.name == inputs[i])
     437             :       {
     438           0 :         input.moose_type = input_types[i];
     439           0 :         break;
     440             :       }
     441             :   }
     442         181 : }
     443             : 
     444             : void
     445          24 : NEML2Action::setupOutputMappings(const neml2::Model & model)
     446             : {
     447          72 :   if (!getParam<bool>("auto_output"))
     448           0 :     return;
     449             : 
     450             :   // Outputs
     451          63 :   for (const auto & [name, var] : model.output_variables())
     452          39 :     _outputs.push_back({name,
     453             :                         NEML2Utils::MOOSEIOType::MATERIAL,
     454          39 :                         var->type(),
     455             :                         /*history_order=*/0});
     456          78 : }
     457             : 
     458             : void
     459          24 : NEML2Action::setupParameterMappings(const neml2::Model & model)
     460             : {
     461             :   // User-specified mapping
     462          24 :   const auto [param_types, params] = getInputParameterMapping<NEML2Utils::MOOSEIOType, std::string>(
     463          96 :       "parameter_types", "parameters");
     464             : 
     465          26 :   for (auto i : index_range(params))
     466             :   {
     467           2 :     if (model.named_parameters().count(params[i]) == 0)
     468           0 :       paramError("parameters", "The neml2 parameter ", params[i], " does not exist.");
     469           2 :     const auto & param = model.get_parameter(params[i]);
     470           6 :     _params.push_back({params[i], inferMOOSEIOType(params[i], param.type()), param.type()});
     471             :   }
     472          28 : }
     473             : 
     474             : void
     475          24 : NEML2Action::setupDerivativeMappings(const neml2::Model & model)
     476             : {
     477          48 :   const auto derivs = getParam<std::vector<std::vector<std::string>>>("derivatives");
     478             : 
     479          48 :   for (auto i : index_range(derivs))
     480             :   {
     481          24 :     if (derivs[i].size() != 2)
     482           0 :       paramError("derivatives", "The length of each pair in derivatives must be 2.");
     483          24 :     if (model.output_variables().count(derivs[i][0]) == 0)
     484           0 :       paramError("derivatives", "The NEML2 output variable ", derivs[i][0], " does not exist.");
     485          24 :     if (model.input_variables().count(derivs[i][1]) == 0)
     486           0 :       paramError("derivatives", "The NEML2 input variable ", derivs[i][1], " does not exist.");
     487             : 
     488          24 :     const auto & y = derivs[i][0];
     489          24 :     const auto & x = derivs[i][1];
     490          24 :     const auto deriv_name = derivativePropertyNameFirst(y, x);
     491          24 :     _derivs.push_back({deriv_name, y, x});
     492          24 :   }
     493          48 : }
     494             : 
     495             : void
     496          24 : NEML2Action::setupParameterDerivativeMappings(const neml2::Model & model)
     497             : {
     498          48 :   const auto derivs = getParam<std::vector<std::vector<std::string>>>("parameter_derivatives");
     499             : 
     500          24 :   for (auto i : index_range(derivs))
     501             :   {
     502           0 :     if (derivs[i].size() != 2)
     503           0 :       paramError("parameter_derivatives",
     504             :                  "The length of each pair in parameter_derivatives must be 2.");
     505           0 :     if (model.output_variables().count(derivs[i][0]) == 0)
     506           0 :       paramError(
     507           0 :           "parameter_derivatives", "The NEML2 output variable ", derivs[i][0], " does not exist.");
     508           0 :     if (model.named_parameters().count(derivs[i][1]) == 0)
     509           0 :       paramError("parameter_derivatives", "The NEML2 parameter ", derivs[i][1], " does not exist.");
     510             : 
     511           0 :     const auto & y = derivs[i][0];
     512           0 :     const auto & x = derivs[i][1];
     513           0 :     const auto deriv_name = derivativePropertyNameFirst(y, x);
     514           0 :     _param_derivs.push_back({deriv_name, y, x});
     515           0 :   }
     516          24 : }
     517             : 
     518             : void
     519          24 : NEML2Action::printSummary() const
     520             : {
     521          24 :   if (!_app.parameters().have_parameter<bool>("parse_neml2_only"))
     522           0 :     return;
     523             : 
     524             :   // Save formatting of the output stream so that we can restore it later
     525          24 :   const auto flags = _console.flags();
     526             : 
     527             :   // Default width for the summary
     528          24 :   const int width = 79;
     529             : 
     530          24 :   _console << std::endl;
     531          24 :   _console << COLOR_CYAN << std::setw(width) << std::setfill('*') << std::left
     532          24 :            << "NEML2 MATERIAL MODEL SUMMARY BEGIN " << std::setfill(' ') << COLOR_DEFAULT
     533          24 :            << std::endl;
     534             : 
     535             :   // Metadata
     536          24 :   _console << "NEML2 input file location: " << fname() << std::endl;
     537          24 :   _console << "NEML2 action path:         " << parameters().blockFullpath() << std::endl;
     538             : 
     539             :   // List inputs, outputs, and parameters of the model
     540          24 :   _console << COLOR_CYAN << std::setw(width) << std::setfill('-') << std::left
     541          24 :            << "Material model structure " << std::setfill(' ') << COLOR_DEFAULT << std::endl;
     542          24 :   _console << *_model;
     543             : 
     544             :   // List transfer between MOOSE and NEML2
     545          24 :   if (!_app.parameters().get<bool>("parse_neml2_only"))
     546             :   {
     547          24 :     _console << COLOR_CYAN << std::setw(width) << std::setfill('-') << std::left
     548          24 :              << "Transfer between MOOSE and NEML2 " << std::setfill(' ') << COLOR_DEFAULT
     549          24 :              << std::endl;
     550             : 
     551          24 :     _console << "MOOSE --> NEML2" << std::endl;
     552             : 
     553             :     // List input transfer, MOOSE -> NEML2
     554          63 :     for (const auto & input : _inputs)
     555          78 :       _console << "  - " << (input.history_order > 0 ? ("(old) " + input.name) : input.name) << " ("
     556          39 :                << NEML2Utils::stringify(input.moose_type) << ")" << std::endl;
     557             : 
     558             :     // List parameter transfer, MOOSE -> NEML2
     559          26 :     for (const auto & param : _params)
     560           4 :       _console << "  - " << param.name << " (" << NEML2Utils::stringify(param.moose_type) << " --> "
     561           2 :                << param.neml2_type << ")" << std::endl;
     562             : 
     563          24 :     _console << "MOOSE <-- NEML2" << std::endl;
     564             : 
     565             :     // List output transfer, NEML2 -> MOOSE
     566          63 :     for (const auto & output : _outputs)
     567          39 :       _console << "  - " << output.name << std::endl;
     568             : 
     569             :     // List derivative transfer, NEML2 -> MOOSE
     570          48 :     for (const auto & deriv : _derivs)
     571          24 :       _console << "  - " << deriv.name << std::endl;
     572             : 
     573             :     // List parameter derivative transfer, NEML2 -> MOOSE
     574          24 :     for (const auto & param_deriv : _param_derivs)
     575           0 :       _console << "  - " << param_deriv.name << std::endl;
     576             :   }
     577             : 
     578          24 :   _console << COLOR_CYAN << std::setw(width) << std::setfill('*') << std::left
     579          24 :            << "NEML2 MATERIAL MODEL SUMMARY END " << std::setfill(' ') << COLOR_DEFAULT << std::endl
     580          24 :            << std::endl;
     581             : 
     582             :   // Restore the formatting of the output stream
     583          24 :   _console.flags(flags);
     584             : }
     585             : 
     586             : std::size_t
     587           0 : NEML2Action::getLongestMOOSEName() const
     588             : {
     589           0 :   std::size_t max_moose_name_length = 0;
     590           0 :   for (const auto & input : _inputs)
     591             :   {
     592           0 :     auto n = input.name.size();
     593           0 :     if (input.history_order > 0)
     594           0 :       n += 6; // 6 is the length of "(old) "
     595           0 :     max_moose_name_length = std::max(max_moose_name_length, n);
     596             :   }
     597           0 :   for (const auto & param : _params)
     598           0 :     max_moose_name_length = std::max(max_moose_name_length, param.name.size());
     599           0 :   for (const auto & output : _outputs)
     600           0 :     max_moose_name_length = std::max(max_moose_name_length, output.name.size());
     601           0 :   for (const auto & deriv : _derivs)
     602           0 :     max_moose_name_length = std::max(max_moose_name_length, deriv.name.size());
     603           0 :   for (const auto & param_deriv : _param_derivs)
     604           0 :     max_moose_name_length = std::max(max_moose_name_length, param_deriv.name.size());
     605           0 :   return max_moose_name_length;
     606             : }
     607             : #endif // NEML2_ENABLED

Generated by: LCOV version 1.14