LCOV - code coverage report
Current view: top level - src/scalarkernels - ParsedODEKernel.C (source / functions) Hit Total Coverage
Test: idaholab/moose framework: 2bf808 Lines: 77 84 91.7 %
Date: 2025-07-17 01:28:37 Functions: 6 6 100.0 %
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 "ParsedODEKernel.h"
      11             : 
      12             : // MOOSE includes
      13             : #include "MooseVariableScalar.h"
      14             : #include "SystemBase.h"
      15             : #include "MooseApp.h"
      16             : 
      17             : #include "libmesh/fparser_ad.hh"
      18             : 
      19             : registerMooseObject("MooseApp", ParsedODEKernel);
      20             : 
      21             : InputParameters
      22       15339 : ParsedODEKernel::validParams()
      23             : {
      24       15339 :   InputParameters params = ODEKernel::validParams();
      25       15339 :   params += FunctionParserUtils<false>::validParams();
      26       15339 :   params.addClassDescription("Parsed expression ODE kernel.");
      27             : 
      28       15339 :   params.addRequiredCustomTypeParam<std::string>(
      29             :       "function", "FunctionExpression", "function expression");
      30       15339 :   params.deprecateParam("function", "expression", "02/07/2024");
      31       15339 :   params.addCoupledVar("args", "Scalar variables coupled in the parsed expression.");
      32       15339 :   params.deprecateCoupledVar("args", "coupled_variables", "02/07/2024");
      33       15339 :   params.addParam<std::vector<std::string>>(
      34             :       "constant_names", {}, "Vector of constants used in the parsed expression");
      35       15339 :   params.addParam<std::vector<std::string>>(
      36             :       "constant_expressions",
      37             :       {},
      38             :       "Vector of values for the constants in constant_names (can be an FParser expression)");
      39       15339 :   params.addParam<std::vector<PostprocessorName>>(
      40             :       "postprocessors", {}, "Vector of postprocessor names used in the function expression");
      41             : 
      42       15339 :   return params;
      43           0 : }
      44             : 
      45         533 : ParsedODEKernel::ParsedODEKernel(const InputParameters & parameters)
      46             :   : ODEKernel(parameters),
      47             :     FunctionParserUtils(parameters),
      48         533 :     _function(getParam<std::string>("expression")),
      49         533 :     _nargs(isCoupledScalar("coupled_variables") ? coupledScalarComponents("coupled_variables") : 0),
      50         533 :     _args(_nargs),
      51         533 :     _arg_names(_nargs),
      52         533 :     _func_dFdarg(_nargs),
      53         533 :     _number_of_nl_variables(_sys.nVariables()),
      54        1599 :     _arg_index(_number_of_nl_variables, -1)
      55             : {
      56             :   // build variables argument (start with variable the kernel is operating on)
      57         533 :   std::string variables = _var.name();
      58             : 
      59             :   // add additional coupled variables
      60         761 :   for (unsigned int i = 0; i < _nargs; ++i)
      61             :   {
      62         228 :     _arg_names[i] = getScalarVar("coupled_variables", i)->name();
      63         228 :     variables += "," + _arg_names[i];
      64         228 :     _args[i] = &coupledScalarValue("coupled_variables", i);
      65             : 
      66             :     // populate number -> arg index lookup table skipping aux variables
      67         228 :     unsigned int number = coupledScalar("coupled_variables", i);
      68         228 :     if (number < _number_of_nl_variables)
      69         228 :       _arg_index[number] = i;
      70             :   }
      71             : 
      72             :   // add postprocessors
      73         533 :   auto pp_names = getParam<std::vector<PostprocessorName>>("postprocessors");
      74         533 :   _pp.resize(pp_names.size());
      75         545 :   for (unsigned int i = 0; i < pp_names.size(); ++i)
      76             :   {
      77          12 :     variables += "," + pp_names[i];
      78          12 :     _pp[i] = &getPostprocessorValueByName(pp_names[i]);
      79             :   }
      80             : 
      81             :   // Note: we do not use the FunctionParsedUtils::parsedFunctionSetup because we are building
      82             :   // multiple expressions, and we can share the MPI barriers by keeping the code here.
      83             :   // base function object
      84         533 :   _func_F = std::make_shared<SymFunction>();
      85             : 
      86             :   // set FParser interneal feature flags
      87         533 :   setParserFeatureFlags(_func_F);
      88             : 
      89             :   // add the constant expressions
      90         533 :   addFParserConstants(_func_F,
      91             :                       getParam<std::vector<std::string>>("constant_names"),
      92             :                       getParam<std::vector<std::string>>("constant_expressions"));
      93             : 
      94             :   // parse function
      95         533 :   if (_func_F->Parse(_function, variables) >= 0)
      96           0 :     mooseError("Invalid function\n",
      97           0 :                _function,
      98             :                "\nin ParsedODEKernel ",
      99           0 :                name(),
     100             :                ".\n",
     101           0 :                _func_F->ErrorMsg());
     102             : 
     103             :   // on-diagonal derivative
     104         533 :   _func_dFdu = std::make_shared<SymFunction>(*_func_F);
     105             : 
     106             :   // let rank 0 do the work first (diff and JIT) to populate caches
     107         533 :   if (_communicator.rank() != 0)
     108         153 :     _communicator.barrier();
     109             : 
     110         533 :   if (_func_dFdu->AutoDiff(_var.name()) != -1)
     111           0 :     mooseError("Failed to take first derivative w.r.t. ", _var.name());
     112             : 
     113             :   // off-diagonal derivatives
     114         761 :   for (unsigned int i = 0; i < _nargs; ++i)
     115             :   {
     116         228 :     _func_dFdarg[i] = std::make_shared<SymFunction>(*_func_F);
     117             : 
     118         228 :     if (_func_dFdarg[i]->AutoDiff(_arg_names[i]) != -1)
     119           0 :       mooseError("Failed to take first derivative w.r.t. ", _arg_names[i]);
     120             :   }
     121             : 
     122             :   // optimize
     123         533 :   if (!_disable_fpoptimizer)
     124             :   {
     125         533 :     _func_F->Optimize();
     126         533 :     _func_dFdu->Optimize();
     127         761 :     for (unsigned int i = 0; i < _nargs; ++i)
     128         228 :       _func_dFdarg[i]->Optimize();
     129             :   }
     130             : 
     131             :   // just-in-time compile
     132         533 :   if (_enable_jit)
     133             :   {
     134         533 :     _func_F->JITCompile();
     135         533 :     _func_dFdu->JITCompile();
     136         761 :     for (unsigned int i = 0; i < _nargs; ++i)
     137         228 :       _func_dFdarg[i]->JITCompile();
     138             :   }
     139             : 
     140             :   // wait for ranks > 0 to catch up
     141         533 :   if (_communicator.rank() == 0)
     142         380 :     _communicator.barrier();
     143             : 
     144             :   // reserve storage for parameter passing buffer
     145         533 :   _func_params.resize(1 + _nargs + _pp.size());
     146         533 : }
     147             : 
     148             : void
     149       74506 : ParsedODEKernel::updateParams()
     150             : {
     151       74506 :   _func_params[0] = _u[_i];
     152             : 
     153       99142 :   for (unsigned int j = 0; j < _nargs; ++j)
     154       24636 :     _func_params[j + 1] = (*_args[j])[_i];
     155       74746 :   for (unsigned int j = 0; j < _pp.size(); ++j)
     156         240 :     _func_params[j + 1 + _nargs] = *_pp[j];
     157       74506 : }
     158             : 
     159             : Real
     160       56785 : ParsedODEKernel::computeQpResidual()
     161             : {
     162       56785 :   updateParams();
     163       56785 :   return evaluate(_func_F);
     164             : }
     165             : 
     166             : Real
     167       14020 : ParsedODEKernel::computeQpJacobian()
     168             : {
     169       14020 :   updateParams();
     170       14020 :   return evaluate(_func_dFdu);
     171             : }
     172             : 
     173             : Real
     174        4685 : ParsedODEKernel::computeQpOffDiagJacobianScalar(unsigned int jvar)
     175             : {
     176        4685 :   int i = _arg_index[jvar];
     177        4685 :   if (i < 0)
     178         984 :     return 0.0;
     179             : 
     180        3701 :   updateParams();
     181        3701 :   return evaluate(_func_dFdarg[i]);
     182             : }

Generated by: LCOV version 1.14