Line data Source code
1 : //* This file is part of the MOOSE framework 2 : //* https://www.mooseframework.org 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 "CauchyStressFromNEML2.h" 11 : #include "NEML2Utils.h" 12 : #include "NonlinearSystem.h" 13 : #include "libmesh/nonlinear_solver.h" 14 : 15 : InputParameters 16 0 : CauchyStressFromNEML2::validParams() 17 : { 18 : InputParameters params = 19 0 : NEML2SolidMechanicsInterface<ComputeLagrangianObjectiveStress>::validParams(); 20 0 : NEML2Utils::addClassDescription( 21 : params, "Perform the objective stress update using a NEML2 material model"); 22 0 : params.addCoupledVar("temperature", "Coupled temperature"); 23 0 : return params; 24 0 : } 25 : 26 : #ifndef NEML2_ENABLED 27 0 : CauchyStressFromNEML2::CauchyStressFromNEML2(const InputParameters & parameters) 28 0 : : NEML2SolidMechanicsInterface<ComputeLagrangianObjectiveStress>(parameters) 29 : { 30 0 : NEML2Utils::libraryNotEnabledError(parameters); 31 0 : } 32 : #endif 33 : 34 : registerMooseObject("TensorMechanicsApp", CauchyStressFromNEML2); 35 : 36 : #ifdef NEML2_ENABLED 37 : 38 : CauchyStressFromNEML2::CauchyStressFromNEML2(const InputParameters & parameters) 39 : : NEML2SolidMechanicsInterface<ComputeLagrangianObjectiveStress>(parameters), 40 : // Inputs to the constitutive model 41 : _mechanical_strain_old(nullptr), 42 : _temperature(nullptr), 43 : _temperature_old(nullptr) 44 : { 45 : validateModel(); 46 : 47 : // Get the old mechanical strain if the NEML2 model need it 48 : _mechanical_strain_old = 49 : model().input().has_variable(strainOld()) 50 : ? &getMaterialPropertyOld<RankTwoTensor>(_base_name + "mechanical_strain") 51 : : nullptr; 52 : 53 : // Get the temperature if the NEML2 model need it 54 : _temperature = 55 : model().input().has_variable(temperature()) ? &coupledValue("temperature") : nullptr; 56 : 57 : // Get the old temperature if the NEML2 model need it 58 : _temperature = 59 : model().input().has_variable(temperatureOld()) ? &coupledValueOld("temperature") : nullptr; 60 : 61 : // Find the stateful state variables. 62 : // A state variable is said to be stateful if it exists on the input's old_state axis as well as 63 : // on the output's state axis. 64 : if (model().input().has_subaxis("old_state")) 65 : for (auto var : model().input().subaxis("old_state").variable_accessors(/*recursive=*/true)) 66 : { 67 : _state_vars[var.on("state")] = &declareProperty<std::vector<Real>>(Moose::stringify(var)); 68 : _state_vars_old[var.on("old_state")] = 69 : &getMaterialPropertyOld<std::vector<Real>>(Moose::stringify(var)); 70 : } 71 : } 72 : 73 : void 74 : CauchyStressFromNEML2::initialSetup() 75 : { 76 : auto solver = _fe_problem.getNonlinearSystem(/*nl_sys_num=*/0).nonlinearSolver(); 77 : if (solver->residual_object || solver->jacobian || !solver->residual_and_jacobian_object) 78 : mooseWarning("NEML2 material models are designed to be used together with " 79 : "residual_and_jacobian_together = true."); 80 : } 81 : 82 : void 83 : CauchyStressFromNEML2::initQpStatefulProperties() 84 : { 85 : ComputeLagrangianObjectiveStress::initQpStatefulProperties(); 86 : 87 : // TODO: provide a mechanism to initialize the internal variables to non-zeros 88 : for (auto && [var, prop] : _state_vars) 89 : (*prop)[_qp] = std::vector<Real>(model().output().storage_size(var), 0); 90 : } 91 : 92 : void 93 : CauchyStressFromNEML2::computeProperties() 94 : { 95 : try 96 : { 97 : // Allocate input 98 : _in = neml2::LabeledVector::zeros(_qrule->n_points(), {&model().input()}); 99 : 100 : advanceStep(); 101 : 102 : updateForces(); 103 : 104 : if (model().implicit()) 105 : applyPredictor(); 106 : 107 : solve(); 108 : } 109 : catch (neml2::NEMLException & e) 110 : { 111 : mooseException("An error occurred during the evaluation of a NEML2 model:\n", 112 : e.what(), 113 : NEML2Utils::NEML2_help_message); 114 : } 115 : catch (std::runtime_error & e) 116 : { 117 : mooseException("An unknown error occurred during the evaluation of a NEML2 model:\n", 118 : e.what(), 119 : "\nIt is possible that this error is related to NEML2.", 120 : NEML2Utils::NEML2_help_message); 121 : } 122 : 123 : ComputeLagrangianObjectiveStress::computeProperties(); 124 : } 125 : 126 : void 127 : CauchyStressFromNEML2::computeQpSmallStress() 128 : { 129 : neml2::TorchSize qp = _qp; 130 : 131 : // Set the results into the corresponding MOOSE material properties 132 : _small_stress[_qp] = 133 : NEML2Utils::toMOOSE<SymmetricRankTwoTensor>(_out(stress()).batch_index({qp})); 134 : 135 : _small_jacobian[_qp] = RankFourTensor(NEML2Utils::toMOOSE<SymmetricRankFourTensor>( 136 : _dout_din(stress(), strain()).batch_index({qp}))); 137 : 138 : for (auto && [var, prop] : _state_vars) 139 : (*prop)[_qp] = NEML2Utils::toMOOSE<std::vector<Real>>(_out(var).batch_index({qp})); 140 : } 141 : 142 : void 143 : CauchyStressFromNEML2::advanceStep() 144 : { 145 : // Set old state variables 146 : for (auto && [var, prop] : _state_vars_old) 147 : NEML2Utils::setBatched(_in, {var}, prop); 148 : 149 : // Set old forces 150 : NEML2Utils::setBatched( 151 : // The input LabeledVector 152 : _in, 153 : // NEML2 variable accessors 154 : {strainOld(), temperatureOld()}, 155 : // Pointer to the batched data 156 : _mechanical_strain_old, 157 : _temperature_old); 158 : 159 : // TransientInterface doesn't provide _t_old... 160 : auto t_old = _t - _dt; 161 : NEML2Utils::set( 162 : // The input LabeledVector 163 : _in, 164 : // NEML2 variable accessors 165 : {timeOld()}, 166 : // Pointer to the unbatched data 167 : model().input().has_variable(timeOld()) ? &t_old : nullptr); 168 : } 169 : 170 : void 171 : CauchyStressFromNEML2::updateForces() 172 : { 173 : NEML2Utils::setBatched( 174 : // The input LabeledVector 175 : _in, 176 : // NEML2 variable accessors 177 : {strain(), temperature()}, 178 : // Pointer to the batched data 179 : &_mechanical_strain, 180 : _temperature); 181 : 182 : NEML2Utils::set( 183 : // The input LabeledVector 184 : _in, 185 : // NEML2 variable accessors 186 : {time()}, 187 : // Pointer to the unbatched data 188 : model().input().has_variable(time()) ? &_t : nullptr); 189 : } 190 : 191 : void 192 : CauchyStressFromNEML2::applyPredictor() 193 : { 194 : // Set trial state variables (i.e., initial guesses). 195 : // Right now we hard-code to use the old state as the trial state. 196 : // TODO: implement other predictors 197 : _in.slice("state").fill(_in.slice("old_state")); 198 : } 199 : 200 : void 201 : CauchyStressFromNEML2::solve() 202 : { 203 : // 1. Sync input onto the model's device 204 : // 2. Evaluate the NEML2 material model 205 : // 3. Sync output back onto CPU 206 : auto res = model().value_and_dvalue(_in.to(device())); 207 : _out = std::get<0>(res).to(torch::kCPU); 208 : _dout_din = std::get<1>(res).to(torch::kCPU); 209 : } 210 : 211 : #endif // NEML2_ENABLED