From 6284b0f60eacce2f5e5ba35444a5b71ee8246ced Mon Sep 17 00:00:00 2001 From: "Joshua E. Hansel" Date: Thu, 17 Aug 2023 20:38:57 -0500 Subject: [PATCH] Added ParsedFunctorMaterial Refs #24380 --- .../functormaterials/ParsedFunctorMaterial.h | 64 +++++++ .../functormaterials/ParsedFunctorMaterial.C | 157 ++++++++++++++++++ framework/src/utils/FunctionParserUtils.C | 8 +- .../parsed_functor_material.i | 57 +++++++ 4 files changed, 282 insertions(+), 4 deletions(-) create mode 100644 framework/include/functormaterials/ParsedFunctorMaterial.h create mode 100644 framework/src/functormaterials/ParsedFunctorMaterial.C create mode 100644 test/tests/functormaterials/parsed_functor_material/parsed_functor_material.i diff --git a/framework/include/functormaterials/ParsedFunctorMaterial.h b/framework/include/functormaterials/ParsedFunctorMaterial.h new file mode 100644 index 000000000000..cb233f2a4cf1 --- /dev/null +++ b/framework/include/functormaterials/ParsedFunctorMaterial.h @@ -0,0 +1,64 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#pragma once + +#include "FunctorMaterial.h" +#include "FunctionParserUtils.h" + +/** + * Computes a functor material from a parsed expression of other functors. + */ +template +class ParsedFunctorMaterialTempl : public FunctorMaterial, public FunctionParserUtils +{ +public: + static InputParameters validParams(); + + ParsedFunctorMaterialTempl(const InputParameters & parameters); + +protected: + usingFunctionParserUtilsMembers(is_ad); + + /** + * Builds the parsed function + */ + void buildParsedFunction(); + + // TODO: This function was duplicated from Function::getTime. Should it go + // into a common place like FunctorInterface? + /** + * @return The time associated with the requested \p state + */ + Real getTime(const Moose::StateArg & state) const; + + /// Expression to parse for the new functor material + const std::string & _expression; + + /// Functors to use in the parsed expression + const std::vector & _functor_names; + + /// Number of functors + const unsigned int _n_functors; + + /// Symbolic name to use for each functor + std::vector _functor_symbols; + + /// Name to give the new functor material property + const std::string & _property_name; + + /// The parsed function + SymFunctionPtr _parsed_function; + + /// Functors + std::vector> *> _functors; +}; + +typedef ParsedFunctorMaterialTempl ParsedFunctorMaterial; +typedef ParsedFunctorMaterialTempl ADParsedFunctorMaterial; diff --git a/framework/src/functormaterials/ParsedFunctorMaterial.C b/framework/src/functormaterials/ParsedFunctorMaterial.C new file mode 100644 index 000000000000..5eba83ed80bf --- /dev/null +++ b/framework/src/functormaterials/ParsedFunctorMaterial.C @@ -0,0 +1,157 @@ +//* This file is part of the MOOSE framework +//* https://www.mooseframework.org +//* +//* All rights reserved, see COPYRIGHT for full restrictions +//* https://github.com/idaholab/moose/blob/master/COPYRIGHT +//* +//* Licensed under LGPL 2.1, please see LICENSE for details +//* https://www.gnu.org/licenses/lgpl-2.1.html + +#include "ParsedFunctorMaterial.h" + +registerMooseObject("MooseApp", ParsedFunctorMaterial); +registerMooseObject("MooseApp", ADParsedFunctorMaterial); + +template +InputParameters +ParsedFunctorMaterialTempl::validParams() +{ + InputParameters params = FunctorMaterial::validParams(); + params += FunctionParserUtils::validParams(); + + params.addClassDescription( + "Computes a functor material from a parsed expression of other functors."); + + params.addRequiredCustomTypeParam( + "expression", "FunctionExpression", "Expression to parse for the new functor material"); + params.addParam>("functor_names", + "Functors to use in the parsed expression"); + params.addParam>( + "functor_symbols", + "Symbolic name to use for each functor in 'functor_names' in the parsed expression. If not " + "provided, then the actual functor names must be used in the parsed expression."); + params.addRequiredParam("property_name", + "Name to give the new functor material property"); + + return params; +} + +template +ParsedFunctorMaterialTempl::ParsedFunctorMaterialTempl(const InputParameters & parameters) + : FunctorMaterial(parameters), + FunctionParserUtils(parameters), + _expression(getParam("expression")), + _functor_names(getParam>("functor_names")), + _n_functors(_functor_names.size()), + _functor_symbols(getParam>("functor_symbols")), + _property_name(getParam("property_name")) +{ + // Check/modify 'functor_symbols' + if (_functor_symbols.size() != _n_functors) + { + if (_functor_symbols.size() == 0) + _functor_symbols = _functor_names; + else + paramError("functor_symbols", + "The number of entries must be equal to either zero or the number of entries in " + "'functor_names'."); + } + + // Get the functors + _functors.resize(_n_functors); + for (const auto i : index_range(_functor_names)) + _functors[i] = &getFunctor>(_functor_names[i]); + + // Build the parsed function + buildParsedFunction(); + + // Add the functor material property + addFunctorProperty>( + _property_name, + [this](const auto & r, const auto & t) -> GenericReal + { + // Store the functor values + for (const auto i : index_range(_functors)) + _func_params[i] = (*_functors[i])(r, t); + + // Store the space and time values + // TODO: find some way to extract x,y,z from r. getPoint(r) does not exist yet. + // const auto r_point = getPoint(r); + _func_params[_n_functors] = 0; +#if LIBMESH_DIM > 1 + _func_params[_n_functors + 1] = 0; +#endif +#if LIBMESH_DIM > 2 + _func_params[_n_functors + 2] = 0; +#endif + _func_params[_n_functors + LIBMESH_DIM] = getTime(t); + + // Evaluate the parsed function + return evaluate(_parsed_function, _name); + }); +} + +template +void +ParsedFunctorMaterialTempl::buildParsedFunction() +{ + _parsed_function = std::make_shared(); + + setParserFeatureFlags(_parsed_function); + + // Add constants + _parsed_function->AddConstant("pi", std::acos(Real(-1))); + _parsed_function->AddConstant("e", std::exp(Real(1))); + + // Collect the symbols corresponding to the _func_params values + std::vector symbols(_functor_symbols); + std::string symbols_str = Moose::stringify(symbols); + symbols_str += ",x"; +#if LIBMESH_DIM > 1 + symbols_str += ",y"; +#endif +#if LIBMESH_DIM > 2 + symbols_str += ",z"; +#endif + symbols_str += ",t"; + + // Parse the expression + if (_parsed_function->Parse(_expression, symbols_str) >= 0) + mooseError("The expression\n'", + _expression, + "'\nwith symbols\n'", + symbols_str, + "'\ncould not be parsed:\n", + _parsed_function->ErrorMsg()); + + // Resize the values vector + _func_params.resize(_n_functors + LIBMESH_DIM + 1); + + // TODO: Do the optimize stuff +} + +// TODO: Make this some common utility function: duplicated from Function::getTime +template +Real +ParsedFunctorMaterialTempl::getTime(const Moose::StateArg & state) const +{ + if (state.iteration_type != Moose::SolutionIterationType::Time) + // If we are any iteration type other than time (e.g. nonlinear), then temporally we are still + // in the present time + return miProblem().time(); + + switch (state.state) + { + case 0: + return miProblem().time(); + + case 1: + return miProblem().timeOld(); + + default: + mooseError("Unhandled state ", state.state, " in ParsedFunctorMaterial::getTime"); + } +} + +template class ParsedFunctorMaterialTempl; +template class ParsedFunctorMaterialTempl; diff --git a/framework/src/utils/FunctionParserUtils.C b/framework/src/utils/FunctionParserUtils.C index b3f9f14486e9..206ad5b70c74 100644 --- a/framework/src/utils/FunctionParserUtils.C +++ b/framework/src/utils/FunctionParserUtils.C @@ -28,7 +28,7 @@ FunctionParserUtils::validParams() #endif "Enable just-in-time compilation of function expressions for faster evaluation"); params.addParam( - "enable_ad_cache", true, "Enable cacheing of function derivatives for faster startup time"); + "enable_ad_cache", true, "Enable caching of function derivatives for faster startup time"); params.addParam( "enable_auto_optimize", true, "Enable automatic immediate optimization of derivatives"); params.addParam( @@ -108,20 +108,20 @@ FunctionParserUtils::evaluate(SymFunctionPtr & parser, const std::string case FailureMethod::nan_warning: mooseWarning("In ", name, - ": DerivativeParsedMaterial function evaluation encountered an error: ", + ": Parsed function evaluation encountered an error: ", _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code]); return _quiet_nan; case FailureMethod::error: mooseError("In ", name, - ": DerivativeParsedMaterial function evaluation encountered an error: ", + ": Parsed function evaluation encountered an error: ", _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code]); case FailureMethod::exception: mooseException("In ", name, - ": DerivativeParsedMaterial function evaluation encountered an error: ", + ": Parsed function evaluation encountered an error: ", _eval_error_msg[(error_code < 0 || error_code > 5) ? 0 : error_code], "\n Cutting timestep"); } diff --git a/test/tests/functormaterials/parsed_functor_material/parsed_functor_material.i b/test/tests/functormaterials/parsed_functor_material/parsed_functor_material.i new file mode 100644 index 000000000000..07bdf4ea0a24 --- /dev/null +++ b/test/tests/functormaterials/parsed_functor_material/parsed_functor_material.i @@ -0,0 +1,57 @@ +[Mesh] + type = GeneratedMesh + dim = 3 + nx = 2 + ny = 2 + nz = 2 + xmin = 0.0 + xmax = 4.0 + ymin = 0.0 + ymax = 6.0 + zmin = 0.0 + zmax = 10.0 +[] + +[Functions] + [fn1] + type = ConstantFunction + value = 5 + [] + [fn2] + type = ConstantFunction + value = 3 + [] +[] + +[FunctorMaterials] + [parsed_fmat] + type = ParsedFunctorMaterial + expression = 'A * B^2 + 2 + pi + e + t + x + y + z' + functor_names = 'fn1 fn2' + functor_symbols = 'A B' + property_name = 'prop1' + [] +[] + +[Postprocessors] + # The value should be: + # 5 + 3^2 + 2 + pi + e + 4 + 3 + 4.5 + 7.5 = 40.85987448204884 + [get_prop1] + type = ElementExtremeFunctorValue + functor = prop1 + value_type = max + [] +[] + +[Problem] + solve = false +[] + +[Executioner] + type = Steady + time = 4.0 +[] + +[Outputs] + csv = true +[]