diff --git a/framework/doc/content/source/auxkernels/ParsedAux.md b/framework/doc/content/source/auxkernels/ParsedAux.md index 2580573b670a..371ac5fc6998 100644 --- a/framework/doc/content/source/auxkernels/ParsedAux.md +++ b/framework/doc/content/source/auxkernels/ParsedAux.md @@ -6,6 +6,8 @@ The parsed expression may contain: - variables (`coupled_variables` parameter) +- functors (`functor_names` or `functor_symbols` parameter) + - coordinates in space and time (`use_xyzt` parameter) - constants (`constant_names` for their name in the expression and `constant_expressions` for their values) diff --git a/framework/include/auxkernels/ParsedAux.h b/framework/include/auxkernels/ParsedAux.h index 97a5f3c830bd..47365fb35394 100644 --- a/framework/include/auxkernels/ParsedAux.h +++ b/framework/include/auxkernels/ParsedAux.h @@ -25,6 +25,21 @@ class ParsedAux : public AuxKernel, public FunctionParserUtils protected: virtual Real computeValue() override; + /// Function to validate the symbols in _functor_symbols + void validateFunctorSymbols(); + + /// Function to validate the names in _functor_names + void validateFunctorNames(); + + /** + * Function to ensure vector entries (names) do not overlap with xyzt or coupled variable names. + * @param names_vec Vector containing names to compare to xyzt and coupled variables names. + * @param param_name Name of the parameter corresponding to names_vec. This will be the paremeter + * errored on, if applicable. + */ + template + void validateGenericVectorNames(const std::vector & names_vec, const std::string & param_name); + /// function expression std::string _function; @@ -35,8 +50,50 @@ class ParsedAux : public AuxKernel, public FunctionParserUtils /// import coordinates and time const bool _use_xyzt; + /// coordinate and time variable names + const std::vector _xyzt; + /// function parser object for the resudual and on-diagonal Jacobian SymFunctionPtr _func_F; usingFunctionParserUtilsMembers(false); + + /// 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 + const std::vector _functor_symbols; + + /// Vector of pointers to functors + std::vector *> _functors; + + /// Vector of coupled variable names + std::vector _coupled_variable_names; }; + +template +void +ParsedAux::validateGenericVectorNames(const std::vector & names_vec, + const std::string & param_name) +{ + for (const auto & name : names_vec) + { + // Make sure symbol is not x, y, z, or t + if (_use_xyzt && (std::find(_xyzt.begin(), _xyzt.end(), name) != _xyzt.end())) + paramError( + param_name, + "x, y, z, and t cannot be used in '" + param_name + "' when use_xyzt=true." + + (param_name == "functor_names" ? " Use 'functor_symbols' to disambiguate." : "")); + // Make sure symbol is not a coupled variable name + if (_coupled_variable_names.size() && + (std::find(_coupled_variable_names.begin(), _coupled_variable_names.end(), name) != + _coupled_variable_names.end())) + paramError( + param_name, + "Values in '" + param_name + "' cannot overlap with coupled variable names." + + (param_name == "functor_names" ? " Use 'functor_symbols' to disambiguate." : "")); + } +} diff --git a/framework/src/auxkernels/ParsedAux.C b/framework/src/auxkernels/ParsedAux.C index 911ddbd879d0..2c89b6e1b9d5 100644 --- a/framework/src/auxkernels/ParsedAux.C +++ b/framework/src/auxkernels/ParsedAux.C @@ -8,7 +8,6 @@ //* https://www.gnu.org/licenses/lgpl-2.1.html #include "ParsedAux.h" -#include "MooseApp.h" registerMooseObject("MooseApp", ParsedAux); @@ -38,6 +37,13 @@ ParsedAux::validParams() "constant_expressions", {}, "Vector of values for the constants in constant_names (can be an FParser expression)"); + 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 will be used in the parsed expression."); return params; } @@ -48,19 +54,41 @@ ParsedAux::ParsedAux(const InputParameters & parameters) _function(getParam("expression")), _nargs(coupledComponents("coupled_variables")), _args(coupledValues("coupled_variables")), - _use_xyzt(getParam("use_xyzt")) + _use_xyzt(getParam("use_xyzt")), + _xyzt({"x", "y", "z", "t"}), + _functor_names(getParam>("functor_names")), + _n_functors(_functor_names.size()), + _functor_symbols(getParam>("functor_symbols")) { + + for (const auto i : make_range(_nargs)) + _coupled_variable_names.push_back(getFieldVar("coupled_variables", i)->name()); + + // sanity checks + if (!_functor_symbols.empty() && (_functor_symbols.size() != _n_functors)) + paramError("functor_symbols", "functor_symbols must be the same length as functor_names."); + + validateFunctorSymbols(); + validateFunctorNames(); + // build variables argument std::string variables; // coupled field variables - for (std::size_t i = 0; i < _nargs; ++i) - variables += (i == 0 ? "" : ",") + getFieldVar("coupled_variables", i)->name(); + for (const auto i : index_range(_coupled_variable_names)) + variables += (i == 0 ? "" : ",") + _coupled_variable_names[i]; + + // adding functors to the expression + if (_functor_symbols.size()) + for (const auto & symbol : _functor_symbols) + variables += (variables.empty() ? "" : ",") + symbol; + else + for (const auto & name : _functor_names) + variables += (variables.empty() ? "" : ",") + name; // "system" variables - const std::vector xyzt = {"x", "y", "z", "t"}; if (_use_xyzt) - for (auto & v : xyzt) + for (auto & v : _xyzt) variables += (variables.empty() ? "" : ",") + v; // base function object @@ -98,21 +126,50 @@ ParsedAux::ParsedAux(const InputParameters & parameters) } // reserve storage for parameter passing buffer - _func_params.resize(_nargs + (_use_xyzt ? 4 : 0)); + _func_params.resize(_nargs + _n_functors + (_use_xyzt ? 4 : 0)); + + for (const auto & name : _functor_names) + _functors.push_back(&getFunctor(name)); } Real ParsedAux::computeValue() { - for (std::size_t j = 0; j < _nargs; ++j) + for (const auto j : make_range(_nargs)) _func_params[j] = (*_args[j])[_qp]; + const auto & state = determineState(); + if (isNodal()) + { + const Moose::NodeArg node_arg = {_current_node, Moose::INVALID_BLOCK_ID}; + for (const auto i : index_range(_functors)) + _func_params[_nargs + i] = (*_functors[i])(node_arg, state); + } + else + { + const Moose::ElemQpArg qp_arg = {_current_elem, _qp, _qrule, _q_point[_qp]}; + for (const auto i : index_range(_functors)) + _func_params[_nargs + i] = (*_functors[i])(qp_arg, state); + } + if (_use_xyzt) { - for (std::size_t j = 0; j < LIBMESH_DIM; ++j) - _func_params[_nargs + j] = isNodal() ? (*_current_node)(j) : _q_point[_qp](j); - _func_params[_nargs + 3] = _t; + for (const auto j : make_range(LIBMESH_DIM)) + _func_params[_nargs + _n_functors + j] = isNodal() ? (*_current_node)(j) : _q_point[_qp](j); + _func_params[_nargs + _n_functors + 3] = _t; } return evaluate(_func_F); } + +void +ParsedAux::validateFunctorSymbols() +{ + validateGenericVectorNames(_functor_symbols, "functor_symbols"); +} + +void +ParsedAux::validateFunctorNames() +{ + validateGenericVectorNames(_functor_names, "functor_names"); +} diff --git a/test/tests/auxkernels/parsed_aux/gold/parsed_aux_functors_test_out.e b/test/tests/auxkernels/parsed_aux/gold/parsed_aux_functors_test_out.e new file mode 120000 index 000000000000..8a7e8cf34329 --- /dev/null +++ b/test/tests/auxkernels/parsed_aux/gold/parsed_aux_functors_test_out.e @@ -0,0 +1 @@ +out.e \ No newline at end of file diff --git a/test/tests/auxkernels/parsed_aux/parsed_aux_functors_test.i b/test/tests/auxkernels/parsed_aux/parsed_aux_functors_test.i new file mode 100644 index 000000000000..ba44a446c2df --- /dev/null +++ b/test/tests/auxkernels/parsed_aux/parsed_aux_functors_test.i @@ -0,0 +1,95 @@ +[Mesh] + type = GeneratedMesh + + dim = 2 + + xmin = 0 + xmax = 1 + + ymin = 0 + ymax = 1 + + nx = 10 + ny = 10 +[] + +[Variables] + [u] + order = FIRST + family = LAGRANGE + [] + + [v] + order = FIRST + family = LAGRANGE + [] +[] + +[AuxVariables] + [parsed] + order = FIRST + family = LAGRANGE + [] +[] + +[Kernels] + [diff_u] + type = Diffusion + variable = u + [] + + [diff_v] + type = Diffusion + variable = v + [] +[] + +[BCs] + [left_u] + type = DirichletBC + variable = u + boundary = top + value = 0 + [] + + [right_u] + type = DirichletBC + variable = u + boundary = bottom + value = 1 + [] + + [left_v] + type = DirichletBC + variable = v + boundary = left + value = 0 + [] + + [right_v] + type = DirichletBC + variable = v + boundary = right + value = 1 + [] +[] + +[AuxKernels] + [set_parsed] + type = ParsedAux + variable = parsed + functor_names = 'u v' + functor_symbols = 'u v' + expression = '(u-0.5)^3*v' + [] +[] + +[Executioner] + type = Steady + + solve_type = 'PJFNK' +[] + +[Outputs] + exodus = true +[] diff --git a/test/tests/auxkernels/parsed_aux/tests b/test/tests/auxkernels/parsed_aux/tests index 9293ed89a63d..19714918a542 100644 --- a/test/tests/auxkernels/parsed_aux/tests +++ b/test/tests/auxkernels/parsed_aux/tests @@ -16,4 +16,58 @@ issues = '#15877' requirement = "The parsed expression AuxKernel in The system shall expose quadrature/nodal point coordinates and time if requested by the user." [../] + [./functor_test] + type = 'Exodiff' + input = 'parsed_aux_functors_test.i' + exodiff = 'parsed_aux_functors_test_out.e' + scale_refine = 3 + issues = '#21244' + requirement = "The system shall be capable of computing values from a functor." + [../] + [errors] + issues = '#21244' + requirement = 'The system shall report an error if' + [functor_symbol_length] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/functor_names='u v' AuxKernels/set_parsed/functor_symbols='u v w'" + expect_err = "functor_symbols must be the same length as functor_names." + detail = 'functor symbols parameter does not have the same length as functor names parameter' + [../] + [invalid_functor_symbol] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/functor_names='u v' AuxKernels/set_parsed/functor_symbols='x v' AuxKernels/set_parsed/use_xyzt=true" + expect_err = "x, y, z, and t cannot be used in 'functor_symbols' when use_xyzt=true." + detail = "functor symbols parameter contains 'x', 'y', 'z', or 't' when coordinates and time parameters are already in use" + [../] + [functor_symbol_variable_name_overlap] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/functor_symbols='u v' AuxKernels/set_parsed/coupled_variables='u v'" + expect_err = "Values in 'functor_symbols' cannot overlap with coupled variable names." + detail = "functor symbols cannot overlap with coupled variable names" + [../] + [invalid_functor_name] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/functor_names='x v' AuxKernels/set_parsed/functor_symbols='' AuxKernels/set_parsed/use_xyzt=true" + expect_err = "x, y, z, and t cannot be used in 'functor_names' when use_xyzt=true. Use 'functor_symbols' to disambiguate." + detail = "functor names parameter contains 'x', 'y', 'z', or 't' when coordinates and time parameters are already in use" + [../] + [functor_name_variable_name_overlap] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/functor_names='u v' AuxKernels/set_parsed/coupled_variables='u v' AuxKernels/set_parsed/functor_symbols=''" + expect_err = "Values in 'functor_names' cannot overlap with coupled variable names. Use 'functor_symbols' to disambiguate." + detail = "functor names cannot overlap with coupled variable names" + [../] + [expression] + type = RunException + input = 'parsed_aux_functors_test.i' + cli_args = "AuxKernels/set_parsed/expression='l'" + expect_err = "Invalid function" + detail = "an invalid function is provided." + [../] + [] []