https://mooseframework.inl.gov
FunctionParserUtils.C
Go to the documentation of this file.
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 "FunctionParserUtils.h"
11 
12 // MOOSE includes
13 #include "InputParameters.h"
14 #include "MooseEnum.h"
15 
16 template <bool is_ad>
19 {
21 
22  params.addParam<bool>(
23  "enable_jit",
24 #ifdef LIBMESH_HAVE_FPARSER_JIT
25  true,
26 #else
27  false,
28 #endif
29  "Enable just-in-time compilation of function expressions for faster evaluation");
30  params.addParam<bool>(
31  "enable_ad_cache", true, "Enable caching of function derivatives for faster startup time");
32  params.addParam<bool>(
33  "enable_auto_optimize", true, "Enable automatic immediate optimization of derivatives");
34  params.addParam<bool>(
35  "disable_fpoptimizer", false, "Disable the function parser algebraic optimizer");
36  MooseEnum evalerror("nan nan_warning error exception", "nan");
37  params.addParam<MooseEnum>("evalerror_behavior",
38  evalerror,
39  "What to do if evaluation error occurs. Options are to pass a nan, "
40  "pass a nan with a warning, throw a error, or throw an exception");
41 
42  params.addParamNamesToGroup(
43  "enable_jit enable_ad_cache enable_auto_optimize disable_fpoptimizer evalerror_behavior",
44  "Parsed expression advanced");
45  params.addParam<Real>("epsilon", 0, "Fuzzy comparison tolerance");
46  return params;
47 }
48 
49 template <bool is_ad>
51  "Unknown",
52  "Division by zero",
53  "Square root of a negative value",
54  "Logarithm of negative value",
55  "Trigonometric error (asin or acos of illegal value)",
56  "Maximum recursion level reached"};
57 
58 template <bool is_ad>
60  : _enable_jit(parameters.isParamValid("enable_jit") && parameters.get<bool>("enable_jit")),
61  _enable_ad_cache(parameters.get<bool>("enable_ad_cache")),
62  _disable_fpoptimizer(parameters.get<bool>("disable_fpoptimizer")),
63  _enable_auto_optimize(parameters.get<bool>("enable_auto_optimize") && !_disable_fpoptimizer),
64  _evalerror_behavior(parameters.get<MooseEnum>("evalerror_behavior").getEnum<FailureMethod>()),
65  _quiet_nan(std::numeric_limits<Real>::quiet_NaN()),
66  _epsilon(parameters.get<Real>("epsilon"))
67 {
68 #ifndef LIBMESH_HAVE_FPARSER_JIT
69  if (_enable_jit)
70  {
71  mooseWarning("Tried to enable FParser JIT but libmesh does not have it compiled in.");
72  _enable_jit = false;
73  }
74 #endif
75 }
76 
77 template <bool is_ad>
78 void
80 {
81  parser->SetADFlags(SymFunction::ADCacheDerivatives, _enable_ad_cache);
82  parser->SetADFlags(SymFunction::ADAutoOptimize, _enable_auto_optimize);
83 }
84 
85 template <bool is_ad>
87 FunctionParserUtils<is_ad>::evaluate(SymFunctionPtr & parser, const std::string & name)
88 {
89  return evaluate(parser, _func_params, name);
90 }
91 
92 template <bool is_ad>
95  const std::vector<GenericReal<is_ad>> & func_params,
96  const std::string & name)
97 {
98  // null pointer is a shortcut for vanishing derivatives, see functionsOptimize()
99  if (parser == NULL)
100  return 0.0;
101 
102  // set desired epsilon
103  auto tmp_eps = parser->epsilon();
104  parser->setEpsilon(_epsilon);
105 
106  // evaluate expression
107  auto result = parser->Eval(func_params.data());
108 
109  // restore epsilon
110  parser->setEpsilon(tmp_eps);
111 
112  // fetch fparser evaluation error (set to unknown if the JIT result is nan)
113  int error_code = _enable_jit ? (std::isnan(result) ? -1 : 0) : parser->EvalError();
114 
115  // no error
116  if (error_code == 0)
117  return result;
118 
119  // hard fail or return not a number
120  switch (_evalerror_behavior)
121  {
122  case FailureMethod::nan:
123  return _quiet_nan;
124 
125  case FailureMethod::nan_warning:
126  mooseWarning("In ",
127  name,
128  ": Parsed function evaluation encountered an error: ",
129  _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code]);
130  return _quiet_nan;
131 
132  case FailureMethod::error:
133  mooseError("In ",
134  name,
135  ": Parsed function evaluation encountered an error: ",
136  _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code]);
137 
138  case FailureMethod::exception:
139  mooseException("In ",
140  name,
141  ": Parsed function evaluation encountered an error: ",
142  _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code],
143  "\n Cutting timestep");
144  }
145 
146  return _quiet_nan;
147 }
148 
149 template <bool is_ad>
150 void
152  SymFunctionPtr & parser,
153  const std::vector<std::string> & constant_names,
154  const std::vector<std::string> & constant_expressions) const
155 {
156  // check constant vectors
157  unsigned int nconst = constant_expressions.size();
158  if (nconst != constant_names.size())
159  mooseError("The parameter vectors constant_names (size " +
160  std::to_string(constant_names.size()) + ") and constant_expressions (size " +
161  std::to_string(nconst) + ") must have equal length.");
162 
163  // previously evaluated constant_expressions may be used in following constant_expressions
164  std::vector<Real> constant_values(nconst);
165 
166  for (unsigned int i = 0; i < nconst; ++i)
167  {
168  // no need to use dual numbers for the constant expressions
169  auto expression = std::make_shared<FunctionParserADBase<Real>>();
170 
171  // add previously evaluated constants
172  for (unsigned int j = 0; j < i; ++j)
173  if (!expression->AddConstant(constant_names[j], constant_values[j]))
174  mooseError("Invalid constant name: ", constant_names[j], " and value ", constant_values[j]);
175 
176  // build the temporary constant expression function
177  if (expression->Parse(constant_expressions[i], "") >= 0)
178  mooseError("Invalid constant expression\n",
179  constant_expressions[i],
180  "\n in parsed function object.\n",
181  expression->ErrorMsg());
182 
183  constant_values[i] = expression->Eval(NULL);
184 
185  if (!parser->AddConstant(constant_names[i], constant_values[i]))
186  mooseError("Invalid constant name in parsed function object");
187  }
188 }
189 
190 template <>
191 void
192 FunctionParserUtils<false>::functionsOptimize(SymFunctionPtr & parsed_function)
193 {
194  // set desired epsilon for optimization!
195  auto tmp_eps = parsed_function->epsilon();
196  parsed_function->setEpsilon(_epsilon);
197 
198  // base function
200  parsed_function->Optimize();
201  if (_enable_jit && !parsed_function->JITCompile())
202  mooseInfo("Failed to JIT compile expression, falling back to byte code interpretation.");
203 
204  parsed_function->setEpsilon(tmp_eps);
205 }
206 
207 template <>
208 void
210 {
211  // set desired epsilon for optimization!
212  auto tmp_eps = parsed_function->epsilon();
213  parsed_function->setEpsilon(_epsilon);
214 
215  // base function
216  if (!_disable_fpoptimizer)
217  parsed_function->Optimize();
218  if (!_enable_jit || !parsed_function->JITCompile())
219  mooseError("AD parsed objects require JIT compilation to be enabled and working.");
220 
221  parsed_function->setEpsilon(tmp_eps);
222 }
223 
224 template <bool is_ad>
225 void
227  SymFunctionPtr & function,
228  const std::string & expression,
229  const std::string & variables,
230  const std::vector<std::string> & constant_names,
231  const std::vector<std::string> & constant_expressions,
232  const libMesh::Parallel::Communicator & comm) const
233 {
234  // set FParser internal feature flags
235  setParserFeatureFlags(function);
236 
237  // add the constant expressions
238  addFParserConstants(function, constant_names, constant_expressions);
239 
240  // parse function
241  if (function->Parse(expression, variables) >= 0)
242  mooseError("Invalid function\n", expression, "\nError:\n", function->ErrorMsg());
243 
244  // optimize
245  if (!_disable_fpoptimizer)
246  function->Optimize();
247 
248  // just-in-time compile
249  if (_enable_jit)
250  {
251  // let rank 0 do the JIT compilation first
252  if (comm.rank() != 0)
253  comm.barrier();
254 
255  function->JITCompile();
256 
257  // wait for ranks > 0 to catch up
258  if (comm.rank() == 0)
259  comm.barrier();
260  }
261 }
262 
263 // explicit instantiation
264 template class FunctionParserUtils<false>;
265 template class FunctionParserUtils<true>;
std::string name(const ElemQuality q)
GenericReal< is_ad > evaluate(SymFunctionPtr &, const std::string &object_name="")
Evaluate FParser object and check EvalError.
void addFParserConstants(SymFunctionPtr &parser, const std::vector< std::string > &constant_names, const std::vector< std::string > &constant_expressions) const
add constants (which can be complex expressions) to the parser object
Moose::GenericType< Real, is_ad > GenericReal
Definition: MooseTypes.h:648
std::shared_ptr< SymFunction > SymFunctionPtr
Shorthand for an smart pointer to an autodiff function parser object.
void mooseError(Args &&... args)
Emit an error message with the given stringified, concatenated args and terminate the application...
Definition: MooseError.h:302
FunctionParserUtils(const InputParameters &parameters)
void mooseWarning(Args &&... args)
Emit a warning message with the given stringified, concatenated args.
Definition: MooseError.h:336
T * get(const std::unique_ptr< T > &u)
The MooseUtils::get() specializations are used to support making forwards-compatible code changes fro...
Definition: MooseUtils.h:1146
virtual void functionsOptimize(SymFunctionPtr &parsed_function)
run FPOptimizer on the parsed function
processor_id_type rank() const
The main MOOSE class responsible for handling user-defined parameters in almost every MOOSE system...
InputParameters emptyInputParameters()
void mooseInfo(Args &&... args)
Emit an informational message with the given stringified, concatenated args.
Definition: MooseError.h:369
bool _enable_jit
feature flags
FailureMethod
Enum for failure method.
This is a "smart" enum class intended to replace many of the shortcomings in the C++ enum type It sho...
Definition: MooseEnum.h:33
void parsedFunctionSetup(SymFunctionPtr &function, const std::string &expression, const std::string &variables, const std::vector< std::string > &constant_names, const std::vector< std::string > &constant_expressions, const libMesh::Parallel::Communicator &comm) const
Performs setup steps on a SymFunction.
T evaluate(Real, const Point &)
The general evaluation method is not defined.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
static InputParameters validParams()
void addParam(const std::string &name, const S &value, const std::string &doc_string)
These methods add an optional parameter and a documentation string to the InputParameters object...
const Real _epsilon
fuzzy comparison tolerance
void setParserFeatureFlags(SymFunctionPtr &) const
apply input parameters to internal feature flags of the parser object
void addParamNamesToGroup(const std::string &space_delim_names, const std::string group_name)
This method takes a space delimited list of parameter names and adds them to the specified group name...