void FunctionParserUtils::addFParserConstants(ADFunctionPtr & parser, const std::vector<std::string> & constant_names, const std::vector<std::string> & constant_expressions) { // check constant vectors unsigned int nconst = constant_expressions.size(); if (nconst != constant_expressions.size()) mooseError("The parameter vectors constant_names and constant_values must have equal length."); // previously evaluated constant_expressions may be used in following constant_expressions std::vector<Real> constant_values(nconst); for (unsigned int i = 0; i < nconst; ++i) { ADFunctionPtr expression = ADFunctionPtr(new ADFunction()); // set FParser internal feature flags setParserFeatureFlags(expression); // add previously evaluated constants for (unsigned int j = 0; j < i; ++j) if (!expression->AddConstant(constant_names[j], constant_values[j])) mooseError("Invalid constant name in ParsedMaterialHelper"); // build the temporary comnstant expression function if (expression->Parse(constant_expressions[i], "") >= 0) mooseError("Invalid constant expression\n", constant_expressions[i], "\n in parsed function object.\n", expression->ErrorMsg()); constant_values[i] = expression->Eval(NULL); if (!parser->AddConstant(constant_names[i], constant_values[i])) mooseError("Invalid constant name in parsed function object"); } }
ParsedGenerateSideset::ParsedGenerateSideset(const InputParameters & parameters) : SideSetsGeneratorBase(parameters), FunctionParserUtils(parameters), _input(getMesh("input")), _function(parameters.get<std::string>("combinatorial_geometry")), _sideset_name(getParam<BoundaryName>("new_sideset_name")), _check_subdomains(isParamValid("included_subdomain_ids")), _check_normal(parameters.isParamSetByUser("normal")), _included_ids(_check_subdomains ? parameters.get<std::vector<SubdomainID>>("included_subdomain_ids") : std::vector<SubdomainID>()), _normal(getParam<Point>("normal")) { if (typeid(_input).name() == typeid(std::unique_ptr<DistributedMesh>).name()) mooseError("GenerateAllSideSetsByNormals only works with ReplicatedMesh."); // base function object _func_F = ADFunctionPtr(new ADFunction()); // set FParser internal feature flags setParserFeatureFlags(_func_F); // add the constant expressions addFParserConstants(_func_F, getParam<std::vector<std::string>>("constant_names"), getParam<std::vector<std::string>>("constant_expressions")); // parse function if (_func_F->Parse(_function, "x,y,z") >= 0) mooseError("Invalid function\n", _function, "\nin ParsedAddSideset ", name(), ".\n", _func_F->ErrorMsg()); _func_params.resize(3); }
ParsedODEKernel::ParsedODEKernel(const InputParameters & parameters) : ODEKernel(parameters), FunctionParserUtils(parameters), _function(getParam<std::string>("function")), _nargs(coupledScalarComponents("args")), _args(_nargs), _arg_names(_nargs), _func_dFdarg(_nargs), _number_of_nl_variables(_sys.nVariables()), _arg_index(_number_of_nl_variables, -1) { // build variables argument (start with variable the kernel is operating on) std::string variables = _var.name(); // add additional coupled variables for (unsigned int i = 0; i < _nargs; ++i) { _arg_names[i] = getScalarVar("args", i)->name(); variables += "," + _arg_names[i]; _args[i] = &coupledScalarValue("args", i); // populate number -> arg index lookup table skipping aux variables unsigned int number = coupledScalar("args", i); if (number < _number_of_nl_variables) _arg_index[number] = i; } // base function object _func_F = ADFunctionPtr(new ADFunction()); // set FParser interneal feature flags setParserFeatureFlags(_func_F); // add the constant expressions addFParserConstants(_func_F, getParam<std::vector<std::string>>("constant_names"), getParam<std::vector<std::string>>("constant_expressions")); // parse function if (_func_F->Parse(_function, variables) >= 0) mooseError("Invalid function\n", _function, "\nin ParsedODEKernel ", name(), ".\n", _func_F->ErrorMsg()); // on-diagonal derivative _func_dFdu = ADFunctionPtr(new ADFunction(*_func_F)); if (_func_dFdu->AutoDiff(_var.name()) != -1) mooseError("Failed to take first derivative w.r.t. ", _var.name()); // off-diagonal derivatives for (unsigned int i = 0; i < _nargs; ++i) { _func_dFdarg[i] = ADFunctionPtr(new ADFunction(*_func_F)); if (_func_dFdarg[i]->AutoDiff(_arg_names[i]) != -1) mooseError("Failed to take first derivative w.r.t. ", _arg_names[i]); } // optimize if (!_disable_fpoptimizer) { _func_F->Optimize(); _func_dFdu->Optimize(); for (unsigned int i = 0; i < _nargs; ++i) _func_dFdarg[i]->Optimize(); } // just-in-time compile if (_enable_jit) { _func_F->JITCompile(); _func_dFdu->JITCompile(); for (unsigned int i = 0; i < _nargs; ++i) _func_dFdarg[i]->JITCompile(); } // reserve storage for parameter passing buffer _func_params.resize(_nargs + 1); }
void ParsedMaterialHelper::functionParse(const std::string & function_expression, const std::vector<std::string> & constant_names, const std::vector<std::string> & constant_expressions, const std::vector<std::string> & mat_prop_expressions, const std::vector<std::string> & tol_names, const std::vector<Real> & tol_values) { // build base function object _func_F = ADFunctionPtr(new ADFunction()); // initialize constants addFParserConstants(_func_F, constant_names, constant_expressions); // add further constants coming from default value coupling for (std::vector<std::string>::iterator it = _arg_constant_defaults.begin(); it != _arg_constant_defaults.end(); ++it) if (!_func_F->AddConstant(*it, _pars.defaultCoupledValue(*it))) mooseError("Invalid constant name in parsed function object"); // set variable names based on map_mode switch (_map_mode) { case USE_MOOSE_NAMES: for (unsigned i = 0; i < _nargs; ++i) _variable_names[i] = _arg_names[i]; break; case USE_PARAM_NAMES: // we do not allow vector coupling in this mode if (!_mapping_is_unique) mooseError("Derivative parsed materials must couple exactly one non-linear variable per coupled variable input parameter."); for (unsigned i = 0; i < _nargs; ++i) _variable_names[i] = _arg_param_names[i]; break; default: mooseError("Unnknown variable mapping mode."); } // tolerance vectors if (tol_names.size() != tol_values.size()) mooseError("The parameter vectors tol_names and tol_values must have equal length."); // set tolerances _tol.resize(_nargs); for (unsigned int i = 0; i < _nargs; ++i) { _tol[i] = -1.0; // for every argument look throug the entire tolerance vector to find a match for (unsigned int j = 0; j < tol_names.size(); ++j) if (_variable_names[i] == tol_names[j]) { _tol[i] = tol_values[j]; break; } } // build 'variables' argument for fparser std::string variables; for (unsigned i = 0; i < _nargs; ++i) variables += "," + _variable_names[i]; // get all material properties unsigned int nmat_props = mat_prop_expressions.size(); _mat_prop_descriptors.resize(nmat_props); for (unsigned int i = 0; i < nmat_props; ++i) { // parse the material property parameter entry into a FunctionMaterialPropertyDescriptor _mat_prop_descriptors[i] = FunctionMaterialPropertyDescriptor(mat_prop_expressions[i], this); // get the fparser symbol name for the new material property variables += "," + _mat_prop_descriptors[i].getSymbolName(); } // erase leading comma variables.erase(0,1); // build the base function if (_func_F->Parse(function_expression, variables) >= 0) mooseError("Invalid function\n" << function_expression << '\n' << variables << "\nin ParsedMaterialHelper.\n" << _func_F->ErrorMsg()); // create parameter passing buffer _func_params.resize(_nargs + nmat_props); // perform next steps (either optimize or take derivatives and then optimize) functionsPostParse(); }
/** * Perform a breadth first construction of all requested derivatives. */ void DerivativeParsedMaterialHelper::assembleDerivatives() { // need to check for zero derivatives here, otherwise at least one order is generated if (_derivative_order < 1) return; // if we are not on thread 0 we fetch all data from the thread 0 copy that already did all the work if (_tid > 0) { // get the master object from thread 0 const MaterialWarehouse<Material> & material_warehouse = _fe_problem.getMaterialWarehouse(); const ExecuteMooseObjectWarehouse<Material> & warehouse = material_warehouse[_material_data_type]; MooseSharedPointer<DerivativeParsedMaterialHelper> master = MooseSharedNamespace::dynamic_pointer_cast<DerivativeParsedMaterialHelper>(warehouse.getActiveObject(name())); // copy parsers and declare properties for (unsigned int i = 0; i < master->_derivatives.size(); ++i) { Derivative newderivative; newderivative.first = &declarePropertyDerivative<Real>(_F_name, master->_derivatives[i].darg_names); newderivative.second = ADFunctionPtr(new ADFunction(*master->_derivatives[i].second)); _derivatives.push_back(newderivative); } // copy coupled material properties for (unsigned int i = 0; i < master->_mat_prop_descriptors.size(); ++i) { FunctionMaterialPropertyDescriptor newdescriptor(master->_mat_prop_descriptors[i]); _mat_prop_descriptors.push_back(newdescriptor); } // size parameter buffer _func_params.resize(master->_func_params.size()); } // set up job queue. We need a deque here to be able to iterate over the currently queued items. std::deque<QueueItem> queue; queue.push_back(QueueItem(_func_F)); // generate derivatives until the queue is exhausted while (!queue.empty()) { QueueItem current = queue.front(); // all permutations of one set of derivatives are equal, so we make sure to generate only one each unsigned int first = current._dargs.empty() ? 0 : current._dargs.back(); // add necessary derivative steps for (unsigned int i = first; i < _nargs; ++i) { // go through list of material properties and check if derivatives are needed unsigned int ndesc = _mat_prop_descriptors.size(); for (unsigned int jj = 0; jj < ndesc; ++jj) { FunctionMaterialPropertyDescriptor * j = &_mat_prop_descriptors[jj]; // take a property descriptor and check if it depends on the current derivative variable if (j->dependsOn(_arg_names[i])) { FunctionMaterialPropertyDescriptor matderivative(*j); matderivative.addDerivative(_arg_names[i]); // search if this new derivative is not yet in the list of material properties MatPropDescriptorList::iterator m = findMatPropDerivative(matderivative); if (m == _mat_prop_descriptors.end()) { // construct new variable name for the material property derivative as base name + number std::string newvarname = _dmatvar_base + Moose::stringify(_dmatvar_index++); matderivative.setSymbolName(newvarname); // loop over all queue items to register the new dmatvar variable (includes 'current' which is popped below) for (std::deque<QueueItem>::iterator k = queue.begin(); k != queue.end(); ++k) { k->_F->AddVariable(newvarname); k->_F->RegisterDerivative(j->getSymbolName(), _arg_names[i], newvarname); } _mat_prop_descriptors.push_back(matderivative); } } } // construct new derivative QueueItem newitem = current; newitem._dargs.push_back(i); // build derivative newitem._F = ADFunctionPtr(new ADFunction(*current._F)); if (newitem._F->AutoDiff(_variable_names[i]) != -1) mooseError("Failed to take order " << newitem._dargs.size() << " derivative in material " << _name); // optimize and compile if (!_disable_fpoptimizer) newitem._F->Optimize(); if (_enable_jit && !newitem._F->JITCompile()) mooseWarning("Failed to JIT compile expression, falling back to byte code interpretation."); // generate material property argument vector std::vector<VariableName> darg_names(0); for (unsigned int j = 0; j < newitem._dargs.size(); ++j) darg_names.push_back(_arg_names[newitem._dargs[j]]); // append to list of derivatives if the derivative is non-vanishing if (!newitem._F->isZero()) { Derivative newderivative; newderivative.first = &declarePropertyDerivative<Real>(_F_name, darg_names); newderivative.second = newitem._F; newderivative.darg_names = darg_names; _derivatives.push_back(newderivative); } // push item to queue if further differentiation is required if (newitem._dargs.size() < _derivative_order) queue.push_back(newitem); } // remove the 'current' element from the queue queue.pop_front(); } // increase the parameter buffer to provide storage for the material property derivatives _func_params.resize(_nargs + _mat_prop_descriptors.size()); }