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);
}
Exemple #3
0
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());
}