deal.II version GIT relicensing-6834-g5b78e6bcdf 2026-10-01 11:20:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
mu_parser_internal.cc
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2019 - 2026 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
16
17#include <cmath>
18#include <complex>
19#include <ctime>
20#include <limits>
21#include <map>
22#include <mutex>
23#include <random>
24#include <vector>
25
26#ifdef DEAL_II_WITH_MUPARSER
27# include <muParser.h>
28#endif
29
31
32namespace internal
33{
34 namespace FunctionParser
35 {
36 int
37 mu_round(const double val)
38 {
39 return static_cast<int>(val + ((val >= 0.0) ? 0.5 : -0.5));
40 }
41
42
43
44 double
45 mu_if(const double condition,
46 const double thenvalue,
47 const double elsevalue)
48 {
49 if (mu_round(condition) != 0)
50 return thenvalue;
51 else
52 return elsevalue;
53 }
54
55
56
57 double
58 mu_or(const double left, const double right)
59 {
60 return static_cast<double>((mu_round(left) != 0) ||
61 (mu_round(right) != 0));
62 }
63
64
65
66 double
67 mu_and(const double left, const double right)
68 {
69 return static_cast<double>((mu_round(left) != 0) &&
70 (mu_round(right) != 0));
71 }
72
73
74
75 double
76 mu_int(const double value)
77 {
78 return static_cast<double>(mu_round(value));
79 }
80
81
82
83 double
84 mu_ceil(const double value)
85 {
86 return std::ceil(value);
87 }
88
89
90
91 double
92 mu_floor(const double value)
93 {
94 return std::floor(value);
95 }
96
97
98
99 double
100 mu_cot(const double value)
101 {
102 return 1.0 / std::tan(value);
103 }
104
105
106
107 double
108 mu_csc(const double value)
109 {
110 return 1.0 / std::sin(value);
111 }
112
113
114
115 double
116 mu_sec(const double value)
117 {
118 return 1.0 / std::cos(value);
119 }
120
121
122
123 double
124 mu_log(const double value)
125 {
126 return std::log(value);
127 }
128
129
130
131 double
132 mu_pow(const double a, const double b)
133 {
134 return std::pow(a, b);
135 }
136
137
138
139 double
140 mu_erf(const double value)
141 {
142 return std::erf(value);
143 }
144
145
146
147 double
148 mu_erfc(const double value)
149 {
150 return std::erfc(value);
151 }
152
153
154
155 // Returns a random value in the range [0,1], after initializing the
156 // generator with the given seed
157 double
158 mu_rand_seed(const double seed)
159 {
160 static std::mutex rand_mutex;
161 std::scoped_lock lock(rand_mutex);
162
163 std::uniform_real_distribution<> uniform_distribution(0., 1.);
164
165 // for each seed a unique random number generator is created,
166 // which is initialized with the seed itself
167 static std::map<double, std::mt19937> rng_map;
169 return uniform_distribution(
170 rng_map.try_emplace(seed, std::mt19937(static_cast<unsigned int>(seed)))
171 .first->second);
172 }
173
174
175 // Returns a random value in the range [0,1]
176 double
179 static std::mutex rand_mutex;
180 std::scoped_lock lock(rand_mutex);
181 std::uniform_real_distribution<> uniform_distribution(0., 1.);
182 const unsigned int seed = static_cast<unsigned long>(std::time(nullptr));
183 static std::mt19937 rng(seed);
184 return uniform_distribution(rng);
185 }
186
187
188
189 std::vector<std::string>
191 {
192 return {// functions predefined by muparser
193 "sin",
194 "cos",
195 "tan",
196 "asin",
197 "acos",
198 "atan",
199 "sinh",
200 "cosh",
201 "tanh",
202 "asinh",
203 "acosh",
204 "atanh",
205 "atan2",
206 "log2",
207 "log10",
208 "log",
209 "ln",
210 "exp",
211 "sqrt",
212 "sign",
213 "rint",
214 "abs",
215 "min",
216 "max",
217 "sum",
218 "avg",
219 // functions we define ourselves above
220 "if",
221 "int",
222 "ceil",
223 "cot",
224 "csc",
225 "floor",
226 "sec",
227 "pow",
228 "erf",
229 "erfc",
230 "rand",
231 "rand_seed"};
232 }
233
234#ifdef DEAL_II_WITH_MUPARSER
238 class Parser : public muParserBase
239 {
240 public:
241 operator mu::Parser &()
242 {
243 return parser;
244 }
245
246 operator const mu::Parser &() const
247 {
248 return parser;
249 }
250
251 protected:
252 mu::Parser parser;
253 };
254#endif
255
256
257
258 template <int dim, typename Number>
260 : initialized(false)
261 , n_vars(0)
262 {}
263
264
265
266 template <int dim, typename Number>
267 void
269 const std::string &variables,
270 const std::vector<std::string> &expressions,
271 const std::map<std::string, double> &constants,
272 const bool time_dependent)
273 {
274 this->parser_data.clear(); // this will reset all thread-local objects
275
276 this->constants = constants;
277 this->var_names = Utilities::split_string_list(variables, ',');
278 this->expressions = expressions;
279 AssertThrow(((time_dependent) ? dim + 1 : dim) == this->var_names.size(),
280 ExcMessage("Wrong number of variables"));
281
282 // Now we define how many variables we expect to read in. We distinguish
283 // between two cases: Time dependent problems, and not time dependent
284 // problems. In the first case the number of variables is given by the
285 // dimension plus one. In the other case, the number of variables is equal
286 // to the dimension. Once we parsed the variables string, if none of this
287 // is the case, then an exception is thrown.
288 if (time_dependent)
289 this->n_vars = dim + 1;
290 else
291 this->n_vars = dim;
292
293 // create a parser object for the current thread we can then query in
294 // value() and vector_value(). this is not strictly necessary because a
295 // user may never call these functions on the current thread, but it gets
296 // us error messages about wrong formulas right away
297 this->init_muparser();
298 this->initialized = true;
299 }
300
301
302
303 template <int dim, typename Number>
304 void
306 {
307#ifdef DEAL_II_WITH_MUPARSER
308 // check that we have not already initialized the parser on the
309 // current thread, i.e., that the current function is only called
310 // once per thread
311 ParserData &data = this->parser_data.get();
312 Assert(data.parsers.empty() && data.vars.empty(), ExcInternalError());
313 const unsigned int n_components = expressions.size();
314
315 // initialize the objects for the current thread
316 data.parsers.reserve(n_components);
317 data.vars.resize(this->var_names.size());
318 for (unsigned int component = 0; component < n_components; ++component)
319 {
320 data.parsers.emplace_back(std::make_unique<Parser>());
321 mu::Parser &parser = dynamic_cast<Parser &>(*data.parsers.back());
322
323 for (const auto &constant : this->constants)
324 parser.DefineConst(constant.first, constant.second);
325
326 for (unsigned int iv = 0; iv < this->var_names.size(); ++iv)
327 parser.DefineVar(this->var_names[iv], &data.vars[iv]);
328
329 // define some compatibility functions:
330 parser.DefineFun("if", mu_if, true);
331 parser.DefineOprt("|", mu_or, 1);
332 parser.DefineOprt("&", mu_and, 2);
333 parser.DefineFun("int", mu_int, true);
334 parser.DefineFun("ceil", mu_ceil, true);
335 parser.DefineFun("cot", mu_cot, true);
336 parser.DefineFun("csc", mu_csc, true);
337 parser.DefineFun("floor", mu_floor, true);
338 parser.DefineFun("sec", mu_sec, true);
339 parser.DefineFun("log", mu_log, true);
340 parser.DefineFun("pow", mu_pow, true);
341 parser.DefineFun("erfc", mu_erfc, true);
342 // Disable optimizations (by passing false) that assume the functions
343 // will always return the same value:
344 parser.DefineFun("rand_seed", mu_rand_seed, false);
345 parser.DefineFun("rand", mu_rand, false);
346
347 try
348 {
349 // muparser expects that functions have no
350 // space between the name of the function and the opening
351 // parenthesis. this is awkward because it is not backward
352 // compatible to the library we used to use before muparser
353 // (the fparser library) but also makes no real sense.
354 // consequently, in the expressions we set, remove any space
355 // we may find after function names
356 std::string transformed_expression = this->expressions[component];
357
358 for (const auto &current_function_name : get_function_names())
359 {
360 const unsigned int function_name_length =
361 current_function_name.size();
362
363 std::string::size_type pos = 0;
364 while (true)
365 {
366 // try to find any occurrences of the function name
367 pos =
368 transformed_expression.find(current_function_name, pos);
369 if (pos == std::string::npos)
370 break;
371
372 // replace whitespace until there no longer is any
373 while (
374 (pos + function_name_length <
375 transformed_expression.size()) &&
376 ((transformed_expression[pos + function_name_length] ==
377 ' ') ||
378 (transformed_expression[pos + function_name_length] ==
379 '\t')))
380 transformed_expression.erase(
381 transformed_expression.begin() + pos +
382 function_name_length);
383
384 // move the current search position by the size of the
385 // actual function name
386 pos += function_name_length;
387 }
388 }
389
390 // now use the transformed expression
391 parser.SetExpr(transformed_expression);
392 }
393 catch (mu::ParserError &e)
394 {
395 std::cerr << "Message: <" << e.GetMsg() << ">\n";
396 std::cerr << "Formula: <" << e.GetExpr() << ">\n";
397 std::cerr << "Token: <" << e.GetToken() << ">\n";
398 std::cerr << "Position: <" << e.GetPos() << ">\n";
399 std::cerr << "Errc: <" << e.GetCode() << ">" << std::endl;
400 AssertThrow(false, ExcParseError(e.GetCode(), e.GetMsg()));
401 }
402 }
403#else
405#endif
406 }
407
408 template <int dim, typename Number>
409 Number
411 const double time,
412 unsigned int component) const
413 {
414#ifdef DEAL_II_WITH_MUPARSER
415 Assert(this->initialized == true, ExcNotInitialized());
416
417 // initialize the parser if that hasn't happened yet on the current
418 // thread
419 internal::FunctionParser::ParserData &data = this->parser_data.get();
420 if (data.vars.empty())
421 init_muparser();
422
423 for (unsigned int i = 0; i < dim; ++i)
424 data.vars[i] = p[i];
425 if (dim != this->n_vars)
426 data.vars[dim] = time;
427
428 try
429 {
430 Assert(dynamic_cast<Parser *>(data.parsers[component].get()),
432 // NOLINTNEXTLINE don't warn about using static_cast once we check
433 mu::Parser &parser = static_cast<Parser &>(*data.parsers[component]);
434 return parser.Eval();
435 } // try
436 catch (mu::ParserError &e)
437 {
438 std::cerr << "Message: <" << e.GetMsg() << ">\n";
439 std::cerr << "Formula: <" << e.GetExpr() << ">\n";
440 std::cerr << "Token: <" << e.GetToken() << ">\n";
441 std::cerr << "Position: <" << e.GetPos() << ">\n";
442 std::cerr << "Errc: <" << e.GetCode() << ">" << std::endl;
443 AssertThrow(false, ExcParseError(e.GetCode(), e.GetMsg()));
444 } // catch
445
446#else
447 (void)p;
448 (void)time;
449 (void)component;
451#endif
452 return std::numeric_limits<double>::signaling_NaN();
453 }
454
455 template <int dim, typename Number>
456 void
458 const Point<dim> &p,
459 const double time,
460 ArrayView<Number> &values) const
461 {
462#ifdef DEAL_II_WITH_MUPARSER
463 Assert(this->initialized == true, ExcNotInitialized());
464
465 // initialize the parser if that hasn't happened yet on the current
466 // thread
467 internal::FunctionParser::ParserData &data = this->parser_data.get();
468 if (data.vars.empty())
469 init_muparser();
470
471 for (unsigned int i = 0; i < dim; ++i)
472 data.vars[i] = p[i];
473 if (dim != this->n_vars)
474 data.vars[dim] = time;
475
476 AssertDimension(values.size(), data.parsers.size());
477 try
478 {
479 for (unsigned int component = 0; component < data.parsers.size();
480 ++component)
481 {
482 Assert(dynamic_cast<Parser *>(data.parsers[component].get()),
484 mu::Parser &parser =
485 // We just checked that the pointer is valid so suppress the
486 // clang-tidy check
487 static_cast<Parser &>(*data.parsers[component]); // NOLINT
488 values[component] = parser.Eval();
489 }
490 } // try
491 catch (mu::ParserError &e)
492 {
493 std::cerr << "Message: <" << e.GetMsg() << ">\n";
494 std::cerr << "Formula: <" << e.GetExpr() << ">\n";
495 std::cerr << "Token: <" << e.GetToken() << ">\n";
496 std::cerr << "Position: <" << e.GetPos() << ">\n";
497 std::cerr << "Errc: <" << e.GetCode() << ">" << std::endl;
498 AssertThrow(false, ExcParseError(e.GetCode(), e.GetMsg()));
499 } // catch
500#else
501 (void)p;
502 (void)time;
503 (void)values;
505#endif
506 }
507
508// explicit instantiations
509#include "base/mu_parser_internal.inst"
510
511 } // namespace FunctionParser
512} // namespace internal
513
Definition point.h:111
Number do_value(const Point< dim > &p, const double time, unsigned int component) const
void do_all_values(const Point< dim > &p, const double time, ArrayView< Number > &values) const
virtual void initialize(const std::string &vars, const std::vector< std::string > &expressions, const std::map< std::string, double > &constants, const bool time_dependent=false)
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
static ::ExceptionBase & ExcNeedsFunctionparser()
#define Assert(cond, exc)
static ::ExceptionBase & ExcParseError(int arg1, std::string arg2)
#define AssertDimension(dim1, dim2)
static ::ExceptionBase & ExcInternalError()
static ::ExceptionBase & ExcNotInitialized()
static ::ExceptionBase & ExcMessage(std::string arg1)
#define AssertThrow(cond, exc)
std::vector< index_type > data
Definition mpi.cc:734
std::vector< std::string > split_string_list(const std::string &s, const std::string &delimiter=",")
Definition utilities.cc:695
double mu_if(double condition, double thenvalue, double elsevalue)
double mu_erf(double value)
double mu_sec(double value)
double mu_floor(double value)
double mu_csc(double value)
double mu_pow(double a, double b)
double mu_log(double value)
double mu_rand_seed(double seed)
double mu_ceil(double value)
std::vector< std::string > get_function_names()
double mu_cot(double value)
double mu_int(double value)
double mu_or(double left, double right)
double mu_and(double left, double right)
double mu_erfc(double value)
::VectorizedArray< Number, width > log(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > tan(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > cos(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > sin(const ::VectorizedArray< Number, width > &)
::VectorizedArray< Number, width > pow(const ::VectorizedArray< Number, width > &, const Number p)