LCOV - code coverage report
Current view: top level - src/materials - DerivativeParsedMaterialHelper.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 909fe5 Lines: 116 122 95.1 %
Date: 2025-08-29 20:01:24 Functions: 14 16 87.5 %
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 "DerivativeParsedMaterialHelper.h"
      11             : #include "Conversion.h"
      12             : 
      13             : #include <deque>
      14             : 
      15             : #include "libmesh/quadrature.h"
      16             : 
      17             : template <bool is_ad>
      18             : InputParameters
      19       31944 : DerivativeParsedMaterialHelperTempl<is_ad>::validParams()
      20             : {
      21       31944 :   InputParameters params = ParsedMaterialHelper<is_ad>::validParams();
      22       63888 :   params.addClassDescription("Parsed Function Material with automatic derivatives.");
      23      127776 :   params.addParam<unsigned int>("derivative_order", 3, "Maximum order of derivatives taken");
      24       95832 :   params.addParam<std::vector<SymbolName>>(
      25             :       "additional_derivative_symbols",
      26             :       {},
      27             :       "A list of additional (non-variable) symbols (such as material property or postprocessor "
      28             :       "names) to take derivatives w.r.t.");
      29       31944 :   return params;
      30           0 : }
      31             : 
      32             : template <bool is_ad>
      33        2619 : DerivativeParsedMaterialHelperTempl<is_ad>::DerivativeParsedMaterialHelperTempl(
      34             :     const InputParameters & parameters,
      35             :     const VariableNameMappingMode map_mode,
      36             :     const std::optional<std::string> & function_param_name /* = {} */)
      37             :   : ParsedMaterialHelper<is_ad>(parameters, map_mode, function_param_name),
      38        2619 :     _derivative_order(this->template getParam<unsigned int>("derivative_order")),
      39        5238 :     _dmatvar_base("matpropautoderiv"),
      40        7857 :     _dmatvar_index(0)
      41             : {
      42        2619 : }
      43             : 
      44             : template <bool is_ad>
      45             : void
      46        2619 : DerivativeParsedMaterialHelperTempl<is_ad>::functionsPostParse()
      47             : {
      48             :   // set up the list of all FParser symbols to derive w.r.t.
      49        5238 :   auto additional_derivative_symbols =
      50             :       this->template getParam<std::vector<SymbolName>>("additional_derivative_symbols");
      51             : 
      52             :   // build a list of indices into _symbol_table for all symbols we want to derive w.r.t.
      53             :   // we start with all MOOSE variables, which we _always_ take the derivatives w.r.t.
      54        2619 :   _derivative_symbol_table.reserve(_nargs + additional_derivative_symbols.size());
      55        4824 :   for (unsigned i = 0; i < _nargs; ++i)
      56        2205 :     _derivative_symbol_table.push_back(i);
      57             : 
      58             :   // then we add the additional derivative symbols
      59        2703 :   for (auto & ads : additional_derivative_symbols)
      60             :   {
      61             :     // find the index of the symbol if it exists
      62          96 :     auto it = std::find(_symbol_names.begin(), _symbol_names.end(), ads);
      63          96 :     if (it == _symbol_names.end())
      64           8 :       this->paramError("additional_derivative_symbols", "Invalid symbol name '", ads, "'.");
      65          92 :     auto idx = std::distance(_symbol_names.begin(), it);
      66             : 
      67             :     // make sure it is not a MOOSE variable (we already added those) - their index is < nargs
      68          92 :     if (idx < _nargs)
      69           8 :       this->paramError(
      70             :           "additional_derivative_symbols",
      71             :           "Symbol name '",
      72             :           ads,
      73             :           "' is a coupled variable. Derivatives w.r.t. coupled variables are always taken "
      74             :           "and they must not be specified again.");
      75             : 
      76             :     // check that the user did not specify any duplicates in the additional_derivative_symbols
      77             :     // parameter
      78          88 :     if (std::count(
      79          88 :             additional_derivative_symbols.begin(), additional_derivative_symbols.end(), ads) > 1)
      80           8 :       this->paramError(
      81             :           "additional_derivative_symbols", "Symbol name '", ads, "' was given more than once.");
      82             : 
      83             :     // if all checks passed, add the symbol index to the list
      84          84 :     _derivative_symbol_table.push_back(idx);
      85             :   }
      86             : 
      87             :   // optimize base function
      88        2607 :   ParsedMaterialHelper<is_ad>::functionsOptimize(_func_F);
      89             : 
      90             :   // generate derivatives
      91        2607 :   assembleDerivatives();
      92             : 
      93             :   // force a value update to get the property at least once and register it for the dependencies
      94        5739 :   for (auto & mpd : _mat_prop_descriptors)
      95        3132 :     mpd.value();
      96             : 
      97             :   // batch compilation
      98        2607 : }
      99             : 
     100             : template <bool is_ad>
     101             : void
     102        9957 : DerivativeParsedMaterialHelperTempl<is_ad>::recurseMatProps(
     103             :     unsigned int var, unsigned int order, const MatPropDescriptorList & parent_mpd_list)
     104             : {
     105             :   // quit if we have exceeded the requested derivative order
     106        9957 :   if (order > _derivative_order)
     107        3303 :     return;
     108             : 
     109             :   // FParser symbol we are deriving w.r.t.
     110        6654 :   auto derivative_var = _derivative_symbol_table[var];
     111        6654 :   auto derivative_symbol = _symbol_names[derivative_var];
     112             : 
     113             :   // generate parent material property descriptors derivatives
     114        6654 :   MatPropDescriptorList mpd_list;
     115       11109 :   for (const auto & parent_mpd : parent_mpd_list)
     116             :   {
     117             :     // if this material property does not depend on the variable we are deriving w.r.t. skip it
     118        2637 :     if (!parent_mpd.dependsOn(derivative_symbol))
     119         819 :       continue;
     120             : 
     121             :     // otherwise add it to _mat_prop_descriptors
     122        1818 :     FunctionMaterialPropertyDescriptor<is_ad> mpd(parent_mpd);
     123        1818 :     mpd.addDerivative(derivative_symbol);
     124             : 
     125             :     // create a new symbol name for it
     126        1818 :     std::string newvarname = _dmatvar_base + Moose::stringify(_dmatvar_index++);
     127        1818 :     mpd.setSymbolName(newvarname);
     128             : 
     129             :     // add the new mpd and register it as the current variable derivative of the parent mpd
     130        1818 :     _bulk_rules.emplace_back(parent_mpd.getSymbolName(), derivative_symbol, newvarname);
     131             : 
     132             :     // append to list
     133        1818 :     mpd_list.push_back(mpd);
     134             :   }
     135             : 
     136             :   // append material property descriptors
     137        8472 :   for (const auto & mpd : mpd_list)
     138        1818 :     _mat_prop_descriptors.push_back(mpd);
     139             : 
     140             :   // go one order deeper
     141       14517 :   for (unsigned int i = var; i < _derivative_symbol_table.size(); ++i)
     142        7863 :     recurseMatProps(i, order + 1, mpd_list);
     143        6654 : }
     144             : 
     145             : template <bool is_ad>
     146             : void
     147        9672 : DerivativeParsedMaterialHelperTempl<is_ad>::recurseDerivative(unsigned int var,
     148             :                                                               unsigned int order,
     149             :                                                               const Derivative & parent_derivative)
     150             : {
     151             :   // quit if we have exceeded the requested derivative order
     152        9672 :   if (order > _derivative_order)
     153        3096 :     return;
     154             : 
     155             :   // FParser symbol we are deriving w.r.t.
     156        6576 :   auto derivative_var = _derivative_symbol_table[var];
     157        6576 :   auto derivative_symbol = _symbol_names[derivative_var];
     158             : 
     159             :   // current derivative starts off of the parent function
     160        6576 :   Derivative current;
     161        6576 :   current._darg_names = parent_derivative._darg_names;
     162             : 
     163             :   // the moose variable name goes into the derivative property name
     164        6576 :   current._darg_names.push_back(derivative_var < _nargs ? _arg_names[derivative_var]
     165             :                                                         : derivative_symbol);
     166             : 
     167        6576 :   current._F = std::make_shared<SymFunction>(*parent_derivative._F);
     168             : 
     169             :   // execute derivative
     170        6576 :   if (current._F->AutoDiff(derivative_symbol) != -1)
     171           0 :     mooseError("Failed to take order ", order, " derivative in material ", _name);
     172             : 
     173             :   // optimize
     174        6576 :   if (!_disable_fpoptimizer)
     175        6576 :     current._F->Optimize();
     176             : 
     177             :   // proceed only if the derivative is not zero
     178        6576 :   if (!current._F->isZero() || is_ad)
     179             :   {
     180             :     // compile
     181        6369 :     if (_enable_jit && !current._F->JITCompile())
     182           0 :       mooseInfo("Failed to JIT compile expression, falling back to byte code interpretation.");
     183             : 
     184             :     // go one order deeper
     185       13947 :     for (unsigned int i = var; i < _derivative_symbol_table.size(); ++i)
     186        7578 :       recurseDerivative(i, order + 1, current);
     187             : 
     188             :     // set up a material property for the derivative
     189        6369 :     current._mat_prop =
     190        6369 :         &this->template declarePropertyDerivative<Real, is_ad>(_F_name, current._darg_names);
     191             : 
     192             :     // save off current derivative
     193        6369 :     _derivatives.push_back(current);
     194             :   }
     195        6576 : }
     196             : 
     197             : /**
     198             :  * Perform construction of all requested derivatives.
     199             :  */
     200             : template <bool is_ad>
     201             : void
     202        2607 : DerivativeParsedMaterialHelperTempl<is_ad>::assembleDerivatives()
     203             : {
     204             :   // need to check for zero derivatives here, otherwise at least one order is generated
     205        2607 :   if (_derivative_order < 1)
     206         222 :     return;
     207             : 
     208             :   // if we are not on thread 0 we fetch all data from the thread 0 copy that already did all the
     209             :   // work
     210        2607 :   if (_tid > 0)
     211             :   {
     212             :     // get the master object from thread 0
     213         222 :     const MaterialWarehouse & material_warehouse = this->_fe_problem.getMaterialWarehouse();
     214         222 :     const MooseObjectWarehouse<MaterialBase> & warehouse = material_warehouse[_material_data_type];
     215             : 
     216         222 :     MooseSharedPointer<DerivativeParsedMaterialHelperTempl> master =
     217             :         MooseSharedNamespace::dynamic_pointer_cast<DerivativeParsedMaterialHelperTempl>(
     218             :             warehouse.getActiveObject(name()));
     219             : 
     220             :     // copy parsers and declare properties
     221         759 :     for (const auto & D : master->_derivatives)
     222             :     {
     223         537 :       Derivative newderivative;
     224         537 :       newderivative._mat_prop =
     225         537 :           &this->template declarePropertyDerivative<Real, is_ad>(_F_name, D._darg_names);
     226         537 :       newderivative._F = std::make_shared<SymFunction>(*D._F);
     227         537 :       _derivatives.push_back(newderivative);
     228             :     }
     229             : 
     230             :     // copy coupled material properties
     231         222 :     auto start = _mat_prop_descriptors.size();
     232         384 :     for (MooseIndex(master->_mat_prop_descriptors) i = start;
     233         384 :          i < master->_mat_prop_descriptors.size();
     234             :          ++i)
     235             :     {
     236         162 :       FunctionMaterialPropertyDescriptor<is_ad> newdescriptor(master->_mat_prop_descriptors[i],
     237             :                                                               this);
     238         162 :       _mat_prop_descriptors.push_back(newdescriptor);
     239             :     }
     240             : 
     241             :     // size parameter buffer
     242         222 :     _func_params.resize(master->_func_params.size());
     243         222 :     return;
     244         222 :   }
     245             : 
     246             :   // generate all coupled material property mappings
     247        4479 :   for (unsigned int i = 0; i < _derivative_symbol_table.size(); ++i)
     248        2094 :     recurseMatProps(i, 1, _mat_prop_descriptors);
     249             : 
     250             :   // bulk register material property derivative rules to avoid repeated calls
     251             :   // to the (slow) AddVariable method
     252        2385 :   if (!_bulk_rules.empty())
     253             :   {
     254         606 :     std::string vars = _bulk_rules[0]._child;
     255        1818 :     for (MooseIndex(_bulk_rules) i = 1; i < _bulk_rules.size(); ++i)
     256        1212 :       vars += ',' + _bulk_rules[i]._child;
     257         606 :     _func_F->AddVariable(vars);
     258             : 
     259        2424 :     for (const auto & rule : _bulk_rules)
     260        1818 :       _func_F->RegisterDerivative(rule._parent, rule._var, rule._child);
     261         606 :   }
     262             : 
     263             :   // generate all derivatives
     264        2385 :   Derivative root;
     265        2385 :   root._F = _func_F;
     266        2385 :   root._mat_prop = nullptr;
     267        4479 :   for (unsigned int i = 0; i < _derivative_symbol_table.size(); ++i)
     268        2094 :     recurseDerivative(i, 1, root);
     269             : 
     270             :   // increase the parameter buffer to provide storage for the material property derivatives
     271        4770 :   _func_params.resize(_nargs + _mat_prop_descriptors.size() + _postprocessor_values.size() +
     272        2385 :                       _extra_symbols.size() + _functors.size());
     273        2385 : }
     274             : 
     275             : template <bool is_ad>
     276             : void
     277           0 : DerivativeParsedMaterialHelperTempl<is_ad>::initQpStatefulProperties()
     278             : {
     279           0 :   computeQpProperties();
     280           0 : }
     281             : 
     282             : template <bool is_ad>
     283             : void
     284     2419713 : DerivativeParsedMaterialHelperTempl<is_ad>::computeQpProperties()
     285             : {
     286     2419713 :   ParsedMaterialHelper<is_ad>::computeQpProperties();
     287             : 
     288             :   // set derivatives
     289     9753186 :   for (auto & D : _derivatives)
     290     7333506 :     (*D._mat_prop)[_qp] = evaluate(D._F, _name);
     291     2419680 : }
     292             : 
     293             : // explicit instantiation
     294             : template class DerivativeParsedMaterialHelperTempl<false>;
     295             : template class DerivativeParsedMaterialHelperTempl<true>;

Generated by: LCOV version 1.14