Reference documentation for deal.II version 9.1.0-pre
function_parser.cc
1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2005 - 2018 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15 
16 
17 #include <deal.II/base/function_parser.h>
18 #include <deal.II/base/patterns.h>
19 #include <deal.II/base/thread_management.h>
20 #include <deal.II/base/utilities.h>
21 
22 #include <deal.II/lac/vector.h>
23 
24 #include <boost/math/special_functions/erf.hpp>
25 #include <boost/random.hpp>
26 
27 #include <cmath>
28 #include <map>
29 
30 #ifdef DEAL_II_WITH_MUPARSER
31 # include <muParser.h>
32 #else
33 
34 
35 
36 namespace fparser
37 {
38  class FunctionParser
39  {};
40 } // namespace fparser
41 #endif
42 
43 DEAL_II_NAMESPACE_OPEN
44 
45 
46 
47 template <int dim>
48 FunctionParser<dim>::FunctionParser(const unsigned int n_components,
49  const double initial_time,
50  const double h)
51  : AutoDerivativeFunction<dim>(h, n_components, initial_time)
52  , initialized(false)
53  , n_vars(0)
54 {}
55 
56 
57 template <int dim>
58 FunctionParser<dim>::FunctionParser(const std::string &expression,
59  const std::string &constants,
60  const std::string &variable_names,
61  const double h)
63  h,
64  Utilities::split_string_list(expression, ';').size())
65  , initialized(false)
66  , n_vars(0)
67 {
69  constants,
72  0,
74  ",",
75  "=")
76  .clone());
77  initialize(variable_names,
78  expression,
79  constants_map,
80  Utilities::split_string_list(variable_names, ",").size() ==
81  dim + 1);
82 }
83 
84 
85 // We deliberately delay the definition of the default destructor
86 // so that we don't need to include the definition of mu::Parser
87 // in the header file.
88 template <int dim>
90 
91 
92 #ifdef DEAL_II_WITH_MUPARSER
93 
94 template <int dim>
95 void
96 FunctionParser<dim>::initialize(const std::string & variables,
97  const std::vector<std::string> &expressions,
98  const std::map<std::string, double> &constants,
99  const bool time_dependent)
100 {
101  this->fp.clear(); // this will reset all thread-local objects
102 
103  this->constants = constants;
104  this->var_names = Utilities::split_string_list(variables, ',');
105  this->expressions = expressions;
106  AssertThrow(((time_dependent) ? dim + 1 : dim) == var_names.size(),
107  ExcMessage("Wrong number of variables"));
108 
109  // We check that the number of
110  // components of this function
111  // matches the number of components
112  // passed in as a vector of
113  // strings.
114  AssertThrow(this->n_components == expressions.size(),
115  ExcInvalidExpressionSize(this->n_components, expressions.size()));
116 
117  // Now we define how many variables
118  // we expect to read in. We
119  // distinguish between two cases:
120  // Time dependent problems, and not
121  // time dependent problems. In the
122  // first case the number of
123  // variables is given by the
124  // dimension plus one. In the other
125  // case, the number of variables is
126  // equal to the dimension. Once we
127  // parsed the variables string, if
128  // none of this is the case, then
129  // an exception is thrown.
130  if (time_dependent)
131  n_vars = dim + 1;
132  else
133  n_vars = dim;
134 
135  // create a parser object for the current thread we can then query
136  // in value() and vector_value(). this is not strictly necessary
137  // because a user may never call these functions on the current
138  // thread, but it gets us error messages about wrong formulas right
139  // away
140  init_muparser();
141 
142  // finally set the initialization bit
143  initialized = true;
144 }
145 
146 
147 
148 namespace internal
149 {
150  // convert double into int
151  int
152  mu_round(double val)
153  {
154  return static_cast<int>(val + ((val >= 0.0) ? 0.5 : -0.5));
155  }
156 
157  double
158  mu_if(double condition, double thenvalue, double elsevalue)
159  {
160  if (mu_round(condition))
161  return thenvalue;
162  else
163  return elsevalue;
164  }
165 
166  double
167  mu_or(double left, double right)
168  {
169  return (mu_round(left)) || (mu_round(right));
170  }
171 
172  double
173  mu_and(double left, double right)
174  {
175  return (mu_round(left)) && (mu_round(right));
176  }
177 
178  double
179  mu_int(double value)
180  {
181  return static_cast<double>(mu_round(value));
182  }
183 
184  double
185  mu_ceil(double value)
186  {
187  return ceil(value);
188  }
189 
190  double
191  mu_floor(double value)
192  {
193  return floor(value);
194  }
195 
196  double
197  mu_cot(double value)
198  {
199  return 1.0 / tan(value);
200  }
201 
202  double
203  mu_csc(double value)
204  {
205  return 1.0 / sin(value);
206  }
207 
208  double
209  mu_sec(double value)
210  {
211  return 1.0 / cos(value);
212  }
213 
214  double
215  mu_log(double value)
216  {
217  return log(value);
218  }
219 
220  double
221  mu_pow(double a, double b)
222  {
223  return std::pow(a, b);
224  }
225 
226  double
227  mu_erfc(double value)
228  {
229  return boost::math::erfc(value);
230  }
231 
232  // returns a random value in the range [0,1] initializing the generator
233  // with the given seed
234  double
235  mu_rand_seed(double seed)
236  {
237  static Threads::Mutex rand_mutex;
238  Threads::Mutex::ScopedLock lock(rand_mutex);
239 
240  static boost::random::uniform_real_distribution<> uniform_distribution(0,
241  1);
242 
243  // for each seed an unique random number generator is created,
244  // which is initialized with the seed itself
245  static std::map<double, boost::random::mt19937> rng_map;
246 
247  if (rng_map.find(seed) == rng_map.end())
248  rng_map[seed] = boost::random::mt19937(static_cast<unsigned int>(seed));
249 
250  return uniform_distribution(rng_map[seed]);
251  }
252 
253  // returns a random value in the range [0,1]
254  double
255  mu_rand()
256  {
257  static Threads::Mutex rand_mutex;
258  Threads::Mutex::ScopedLock lock(rand_mutex);
259  static boost::random::uniform_real_distribution<> uniform_distribution(0,
260  1);
261  static boost::random::mt19937 rng(
262  static_cast<unsigned long>(std::time(nullptr)));
263  return uniform_distribution(rng);
264  }
265 
266 } // namespace internal
267 
268 
269 
270 template <int dim>
271 void
273 {
274  // check that we have not already initialized the parser on the
275  // current thread, i.e., that the current function is only called
276  // once per thread
277  Assert(fp.get().size() == 0, ExcInternalError());
278 
279  // initialize the objects for the current thread (fp.get() and
280  // vars.get())
281  fp.get().reserve(this->n_components);
282  vars.get().resize(var_names.size());
283  for (unsigned int component = 0; component < this->n_components; ++component)
284  {
285  fp.get().emplace_back(new mu::Parser());
286 
287  for (std::map<std::string, double>::const_iterator constant =
288  constants.begin();
289  constant != constants.end();
290  ++constant)
291  {
292  fp.get()[component]->DefineConst(constant->first.c_str(),
293  constant->second);
294  }
295 
296  for (unsigned int iv = 0; iv < var_names.size(); ++iv)
297  fp.get()[component]->DefineVar(var_names[iv].c_str(), &vars.get()[iv]);
298 
299  // define some compatibility functions:
300  fp.get()[component]->DefineFun("if", internal::mu_if, true);
301  fp.get()[component]->DefineOprt("|", internal::mu_or, 1);
302  fp.get()[component]->DefineOprt("&", internal::mu_and, 2);
303  fp.get()[component]->DefineFun("int", internal::mu_int, true);
304  fp.get()[component]->DefineFun("ceil", internal::mu_ceil, true);
305  fp.get()[component]->DefineFun("cot", internal::mu_cot, true);
306  fp.get()[component]->DefineFun("csc", internal::mu_csc, true);
307  fp.get()[component]->DefineFun("floor", internal::mu_floor, true);
308  fp.get()[component]->DefineFun("sec", internal::mu_sec, true);
309  fp.get()[component]->DefineFun("log", internal::mu_log, true);
310  fp.get()[component]->DefineFun("pow", internal::mu_pow, true);
311  fp.get()[component]->DefineFun("erfc", internal::mu_erfc, true);
312  fp.get()[component]->DefineFun("rand_seed", internal::mu_rand_seed, true);
313  fp.get()[component]->DefineFun("rand", internal::mu_rand, true);
314 
315  try
316  {
317  // muparser expects that functions have no
318  // space between the name of the function and the opening
319  // parenthesis. this is awkward because it is not backward
320  // compatible to the library we used to use before muparser
321  // (the fparser library) but also makes no real sense.
322  // consequently, in the expressions we set, remove any space
323  // we may find after function names
324  std::string transformed_expression = expressions[component];
325 
326  const char *function_names[] = {// functions predefined by muparser
327  "sin",
328  "cos",
329  "tan",
330  "asin",
331  "acos",
332  "atan",
333  "sinh",
334  "cosh",
335  "tanh",
336  "asinh",
337  "acosh",
338  "atanh",
339  "atan2",
340  "log2",
341  "log10",
342  "log",
343  "ln",
344  "exp",
345  "sqrt",
346  "sign",
347  "rint",
348  "abs",
349  "min",
350  "max",
351  "sum",
352  "avg",
353  // functions we define ourselves above
354  "if",
355  "int",
356  "ceil",
357  "cot",
358  "csc",
359  "floor",
360  "sec",
361  "pow",
362  "erfc",
363  "rand",
364  "rand_seed"};
365  for (unsigned int f = 0;
366  f < sizeof(function_names) / sizeof(function_names[0]);
367  ++f)
368  {
369  const std::string function_name = function_names[f];
370  const unsigned int function_name_length = function_name.size();
371 
372  std::string::size_type pos = 0;
373  while (true)
374  {
375  // try to find any occurrences of the function name
376  pos = transformed_expression.find(function_name, pos);
377  if (pos == std::string::npos)
378  break;
379 
380  // replace whitespace until there no longer is any
381  while ((pos + function_name_length <
382  transformed_expression.size()) &&
383  ((transformed_expression[pos + function_name_length] ==
384  ' ') ||
385  (transformed_expression[pos + function_name_length] ==
386  '\t')))
387  transformed_expression.erase(
388  transformed_expression.begin() + pos +
389  function_name_length);
390 
391  // move the current search position by the size of the
392  // actual function name
393  pos += function_name_length;
394  }
395  }
396 
397  // now use the transformed expression
398  fp.get()[component]->SetExpr(transformed_expression);
399  }
400  catch (mu::ParserError &e)
401  {
402  std::cerr << "Message: <" << e.GetMsg() << ">\n";
403  std::cerr << "Formula: <" << e.GetExpr() << ">\n";
404  std::cerr << "Token: <" << e.GetToken() << ">\n";
405  std::cerr << "Position: <" << e.GetPos() << ">\n";
406  std::cerr << "Errc: <" << e.GetCode() << ">" << std::endl;
407  AssertThrow(false, ExcParseError(e.GetCode(), e.GetMsg().c_str()));
408  }
409  }
410 }
411 
412 
413 
414 template <int dim>
415 void
417  const std::string & expression,
418  const std::map<std::string, double> &constants,
419  const bool time_dependent)
420 {
421  initialize(vars,
422  Utilities::split_string_list(expression, ';'),
423  constants,
424  time_dependent);
425 }
426 
427 
428 
429 template <int dim>
430 double
432  const unsigned int component) const
433 {
435  Assert(component < this->n_components,
436  ExcIndexRange(component, 0, this->n_components));
437 
438  // initialize the parser if that hasn't happened yet on the current thread
439  if (fp.get().size() == 0)
440  init_muparser();
441 
442  for (unsigned int i = 0; i < dim; ++i)
443  vars.get()[i] = p(i);
444  if (dim != n_vars)
445  vars.get()[dim] = this->get_time();
446 
447  try
448  {
449  return fp.get()[component]->Eval();
450  }
451  catch (mu::ParserError &e)
452  {
453  std::cerr << "Message: <" << e.GetMsg() << ">\n";
454  std::cerr << "Formula: <" << e.GetExpr() << ">\n";
455  std::cerr << "Token: <" << e.GetToken() << ">\n";
456  std::cerr << "Position: <" << e.GetPos() << ">\n";
457  std::cerr << "Errc: <" << e.GetCode() << ">" << std::endl;
458  AssertThrow(false, ExcParseError(e.GetCode(), e.GetMsg().c_str()));
459  return 0.0;
460  }
461 }
462 
463 
464 
465 template <int dim>
466 void
468  Vector<double> & values) const
469 {
471  Assert(values.size() == this->n_components,
472  ExcDimensionMismatch(values.size(), this->n_components));
473 
474 
475  // initialize the parser if that hasn't happened yet on the current thread
476  if (fp.get().size() == 0)
477  init_muparser();
478 
479  for (unsigned int i = 0; i < dim; ++i)
480  vars.get()[i] = p(i);
481  if (dim != n_vars)
482  vars.get()[dim] = this->get_time();
483 
484  for (unsigned int component = 0; component < this->n_components; ++component)
485  values(component) = fp.get()[component]->Eval();
486 }
487 
488 #else
489 
490 
491 template <int dim>
492 void
493 FunctionParser<dim>::initialize(const std::string &,
494  const std::vector<std::string> &,
495  const std::map<std::string, double> &,
496  const bool)
497 {
498  Assert(false, ExcNeedsFunctionparser());
499 }
500 
501 template <int dim>
502 void
503 FunctionParser<dim>::initialize(const std::string &,
504  const std::string &,
505  const std::map<std::string, double> &,
506  const bool)
507 {
508  Assert(false, ExcNeedsFunctionparser());
509 }
510 
511 
512 
513 template <int dim>
514 double
515 FunctionParser<dim>::value(const Point<dim> &, unsigned int) const
516 {
517  Assert(false, ExcNeedsFunctionparser());
518  return 0.;
519 }
520 
521 
522 template <int dim>
523 void
525 {
526  Assert(false, ExcNeedsFunctionparser());
527 }
528 
529 
530 #endif
531 
532 // Explicit Instantiations.
533 
534 template class FunctionParser<1>;
535 template class FunctionParser<2>;
536 template class FunctionParser<3>;
537 
538 DEAL_II_NAMESPACE_CLOSE
std::vector< std::string > split_string_list(const std::string &s, const std::string &delimiter=",")
Definition: utilities.cc:279
static::ExceptionBase & ExcNeedsFunctionparser()
FunctionParser(const unsigned int n_components=1, const double initial_time=0.0, const double h=1e-8)
Threads::ThreadLocalStorage< std::vector< std::shared_ptr< mu::Parser > > > fp
const unsigned int n_components
Definition: function.h:160
static T to_value(const std::string &s, const std::unique_ptr< Patterns::PatternBase > &p=Convert< T >::to_pattern())=delete
size_type size() const
Number get_time() const
static const unsigned int max_int_value
Definition: patterns.h:585
unsigned int n_vars
static::ExceptionBase & ExcParseError(int arg1, std::string arg2)
void init_muparser() const
static::ExceptionBase & ExcNotInitialized()
#define AssertThrow(cond, exc)
Definition: exceptions.h:1329
static::ExceptionBase & ExcIndexRange(int arg1, int arg2, int arg3)
static::ExceptionBase & ExcMessage(std::string arg1)
virtual void vector_value(const Point< dim > &p, Vector< double > &values) const override
#define Assert(cond, exc)
Definition: exceptions.h:1227
static::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
Threads::ThreadLocalStorage< std::vector< double > > vars
static::ExceptionBase & ExcInvalidExpressionSize(int arg1, int arg2)
Definition: cuda.h:32
std::vector< std::string > var_names
void initialize(const std::string &vars, const std::vector< std::string > &expressions, const ConstMap &constants, const bool time_dependent=false)
virtual double value(const Point< dim > &p, const unsigned int component=0) const override
std::vector< std::string > expressions
~FunctionParser() override
std::map< std::string, double > constants
static::ExceptionBase & ExcInternalError()