FEAT 3
Finite Element Analysis Toolbox
Loading...
Searching...
No Matches
parsed_function.hpp
1// FEAT3: Finite Element Analysis Toolbox, Version 3
2// Copyright (C) 2010 by Stefan Turek & the FEAT group
3// FEAT3 is released under the GNU General Public License version 3,
4// see the file 'copyright.txt' in the top level directory for details.
5
6#pragma once
7
8#include <kernel/analytic/function.hpp>
10
11// The contents of this file require the 'fparser' third-party library.
12#if defined(FEAT_HAVE_FPARSER) || defined(DOXYGEN)
13
14#include <fparser.hh>
15
16#include <array>
17#include <vector>
18#include <map>
19
20namespace FEAT
21{
22 namespace Analytic
23 {
36 {
37 public:
39 explicit ParsedFunctionParseError(const String& msg) : ParseError(msg) {}
40 };
41
51 {
52 public:
54 explicit ParsedFunctionEvalError(const String& msg) : Exception(msg) {}
55 };
56
101 template<int dim_>
103 public Analytic::Function
104 {
105 public:
107 static_assert((dim_ >= 1) && (dim_ <= 3), "unsupported function dimension");
108
110 static constexpr int domain_dim = dim_;
111
114
116 static constexpr bool can_value = true;
117
119 static constexpr bool can_grad = false;
120
122 static constexpr bool can_hess = false;
123
124 private:
126 ::FunctionParser _parser;
128 std::map<String, double> _extra_vars;
129
130 public:
139 _parser(),
141 {
142 // add constants to our parser
143 _parser.AddConstant("pi", Math::pi<double>());
144 _parser.AddConstant("eps", Math::eps<double>());
145 }
146
161 explicit ParsedScalarFunction(const String& function) :
163 {
164 parse(function);
165 }
166
176 void add_constant(const String& name, double value)
177 {
178 _parser.AddConstant(name.c_str(), value);
179 }
180
192 void add_variable(const String& name, double value = 0.0)
193 {
194 XASSERTM(_extra_vars.find(name) == _extra_vars.end(), "extra variable '" + name + "' already added");
195 XASSERTM(name.compare_no_case("x") != 0, "variable name 'x' is reserved and cannot be used as extra variable");
196 XASSERTM(name.compare_no_case("y") != 0, "variable name 'y' is reserved and cannot be used as extra variable");
197 XASSERTM(name.compare_no_case("z") != 0, "variable name 'z' is reserved and cannot be used as extra variable");
198 _extra_vars.emplace(name, value);
199 }
200
210 void set_variable(const String& name, double value)
211 {
212 auto it = _extra_vars.find(name);
213 XASSERTM(it != _extra_vars.end(), "no variable named '" + name + "' found");
214 it->second = value;
215 }
216
229 void parse(const String& function)
230 {
231 // add variables to our parser
232 String vars("x");
233 if(dim_ > 1) vars += ",y";
234 if(dim_ > 2) vars += ",z";
235
236 // add extra variables
237 for(auto& v : _extra_vars)
238 (vars += ",") += v.first;
239
240 // try to parse the function
241 const int ret = _parser.Parse(function.c_str(), vars.c_str());
242 if(ret >= 0)
243 {
244 String msg(_parser.ErrorMsg());
245 msg.append("\n>>> '");
246 msg.append(function);
247 msg.append("'");
248 if(ret < int(function.size()))
249 {
250 // ret contains the index of the first invalid character in the input string
251 // append an additional line to mark the faulty character
252 msg.append("\n>>>");
253 msg.append(String(std::size_t(ret+2), '-'));
254 msg.append("^");
255 }
256 throw ParsedFunctionParseError(msg);
257 }
258
259 // optimize the parsed function
260 _parser.Optimize();
261 }
262
263 template<typename Traits_>
264 class Evaluator :
265 public Analytic::Function::Evaluator<Traits_>
266 {
267 public:
269 typedef typename Traits_::DataType DataType;
271 typedef typename Traits_::PointType PointType;
273 typedef typename Traits_::ValueType ValueType;
274
275 private:
276 // Note: We need to create a copy of the parser rather than just a reference to it,
277 // as the 'Eval' function of the parser is not thread-safe, whereas our Evaluators
278 // are always meant to be so!
280 ::FunctionParser _parser;
282 std::vector<double> _vars;
283
284 public:
285 explicit Evaluator(const ParsedScalarFunction& function) :
286 _parser(function._parser),
287 _vars(std::size_t(dim_), 0.0)
288 {
289 // force deep copy to avoid race conditions in case of multi-threading
290 _parser.ForceDeepCopy();
291 // set extra vars
292 for(const auto& v : function._extra_vars)
293 _vars.push_back(v.second);
294 }
295
296 ValueType value(const PointType& point)
297 {
298 // copy coordinates
299 for(int i(0); i < dim_; ++i)
300 _vars[std::size_t(i)] = double(point[i]);
301
302 // evaluate the parser
303 const double val = _parser.Eval(_vars.data());
304
305 // check for errors
306 switch(_parser.EvalError())
307 {
308 case 0: // no error
309 break;
310
311 case 1: // division by zero
312 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: division by zero");
313
314 case 2: // sqrt error
315 case 3: // log error
316 case 4: // trigonometric error
317 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: illegal input value");
318
319 case 5: // recursion error
320 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: maximum recursion depth reached");
321
322 default: // ???
323 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: unknown error");
324 }
325
326 // return value
327 return ValueType(val);
328 }
329 }; // class ParsedScalarFunction::Evaluator<...>
330 }; // class ParsedScalarFunction
331
333 template<int dim_>
335
336
354 template<int dom_dim_, int img_dim_ = dom_dim_>
356 public Analytic::Function
357 {
358 public:
360 static_assert((dom_dim_ >= 1) && (dom_dim_ <= 3), "unsupported domain dimension");
361 static_assert(img_dim_ >= 1, "unsupported image dimension");
362
364 static constexpr int domain_dim = dom_dim_;
365
368
370 static constexpr bool can_value = true;
371
373 static constexpr bool can_grad = false;
374
376 static constexpr bool can_hess = false;
377
378 private:
380 std::map<String, double> _extra_vars;
381
383 std::array<::FunctionParser, img_dim_> _parsers;
384
385 public:
394 {
395 // add constants to our parsers
396 add_constant("pi", Math::pi<double>());
397 add_constant("eps", Math::eps<double>());
398 }
399
415 explicit ParsedVectorFunction(const String& function) :
417 {
418 parse(function);
419 }
420
430 void add_constant(const String& name, double value)
431 {
432 for(auto& p : _parsers)
433 p.AddConstant(name.c_str(), value);
434 }
435
447 void add_variable(const String& name, double value = 0.0)
448 {
449 XASSERTM(_extra_vars.find(name) == _extra_vars.end(), "extra variable '" + name + "' already added");
450 XASSERTM(name.compare_no_case("x") != 0, "variable name 'x' is reserved and cannot be used as extra variable");
451 XASSERTM(name.compare_no_case("y") != 0, "variable name 'y' is reserved and cannot be used as extra variable");
452 XASSERTM(name.compare_no_case("z") != 0, "variable name 'z' is reserved and cannot be used as extra variable");
453 _extra_vars.emplace(name, value);
454 }
455
465 void set_variable(const String& name, double value)
466 {
467 auto it = _extra_vars.find(name);
468 XASSERTM(it != _extra_vars.end(), "no variable named '" + name + "' found");
469 it->second = value;
470 }
471
485 void parse(const String& function)
486 {
487 // add variables to our parser
488 String vars("x");
489 if(dom_dim_ > 1) vars += ",y";
490 if(dom_dim_ > 2) vars += ",z";
491
492 // add extra variables
493 for(auto& v : _extra_vars)
494 (vars += ",") += v.first;
495
496 String sfunc(function);
497 if(sfunc.starts_with('['))
498 sfunc.pop_front();
499 if(sfunc.ends_with(']'))
500 sfunc.pop_back();
501 sfunc.trim_me();
502
503 // split function string
504 std::deque<String> sfuncs = sfunc.split_by_charset("'");
505 if(sfuncs.size() != _parsers.size())
506 {
507 String msg("Invalid number of formulae: expected ");
508 msg.append(stringify(_parsers.size()));
509 msg.append(" but got ");
510 msg.append(stringify(sfuncs.size()));
511 throw ParsedFunctionParseError(msg);
512 }
513
514 // parse all functions
515 for(std::size_t i(0); i < _parsers.size(); ++i)
516 {
517 // try to parse the function
518 const int ret = _parsers.at(i).Parse(sfuncs.at(i).c_str(), vars.c_str());
519 if(ret >= 0)
520 {
521 String msg(_parsers.at(i).ErrorMsg());
522 msg.append("\n>>> '");
523 msg.append(sfuncs.at(i));
524 msg.append("'");
525 if(ret < int(sfuncs.at(i).size()))
526 {
527 // ret contains the index of the first invalid character in the input string
528 // append an additional line to mark the faulty character
529 msg.append("\n>>>");
530 msg.append(String(std::size_t(ret+2), '-'));
531 msg.append("^");
532 }
533 throw ParsedFunctionParseError(msg);
534 }
535
536 // optimize the parsed function
537 _parsers.at(i).Optimize();
538 }
539 }
540
541 template<typename Traits_>
542 class Evaluator :
543 public Analytic::Function::Evaluator<Traits_>
544 {
545 public:
547 typedef typename Traits_::DataType DataType;
549 typedef typename Traits_::PointType PointType;
551 typedef typename Traits_::ValueType ValueType;
552
553 private:
555 std::array< ::FunctionParser, img_dim_> _parsers;
557 std::vector<double> _vars;
558
559 public:
560 explicit Evaluator(const ParsedVectorFunction& function) :
561 _parsers(function._parsers),
562 _vars(std::size_t(dom_dim_), 0.0)
563 {
564 // force deep copy to avoid race conditions in case of multi-threading
565 for(auto& p : _parsers)
566 p.ForceDeepCopy();
567
568 // set extra vars
569 for(const auto& v : function._extra_vars)
570 _vars.push_back(v.second);
571 }
572
573 ValueType value(const PointType& point)
574 {
575 // copy coordinates
576 for(int i(0); i < dom_dim_; ++i)
577 _vars[std::size_t(i)] = double(point[i]);
578
579 // evaluate the parser
580 ValueType val;
581 for(std::size_t i(0); i < _parsers.size(); ++i)
582 {
583 // evaluate the parser
584 val[int(i)] = DataType(_parsers[i].Eval(_vars.data()));
585
586 // check for errors
587 switch(_parsers[i] .EvalError())
588 {
589 case 0: // no error
590 break;
591
592 case 1: // division by zero
593 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: division by zero");
594
595 case 2: // sqrt error
596 case 3: // log error
597 case 4: // trigonometric error
598 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: illegal input value");
599
600 case 5: // recursion error
601 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: maximum recursion depth reached");
602
603 default: // ???
604 throw ParsedFunctionEvalError("Error in ParsedScalarFunction evaluation: unknown error");
605 }
606 }
607
608 return val;
609 }
610 }; // class ParsedVectorFunction::Evaluator<...>
611 }; // class ParsedVectorFunction
612 } // namespace Analytic
613} // namespace FEAT
614
615#endif // FEAT_HAVE_FPARSER
#define XASSERTM(expr, msg)
Assertion macro definition with custom message.
Definition: assertion.hpp:263
Analytic Function Evaluator base-class template.
Definition: function.hpp:157
Analytic Function interface.
Definition: function.hpp:112
Parsed Function evaluation error.
ParsedFunctionEvalError(const String &msg)
constructor
ParsedFunctionParseError(const String &msg)
constructor
::FunctionParser _parser
our own private copy of the function parser
Traits_::PointType PointType
evaluation point type
Traits_::DataType DataType
coefficient data type
std::vector< double > _vars
a vector for the variable values
Parsed scalar function implementation.
void set_variable(const String &name, double value)
Sets the value of an extra variable.
static constexpr int domain_dim
validate our dimension
::FunctionParser _parser
the actual function parser object
Analytic::Image::Scalar ImageType
This function is always scalar.
static constexpr bool can_grad
we cannot compute gradients
static constexpr bool can_value
we can compute function values
void parse(const String &function)
Parses a function.
ParsedScalarFunction(const String &function)
Constructor.
std::map< String, double > _extra_vars
our extra variable names
void add_constant(const String &name, double value)
Adds a constant to the parser.
ParsedScalarFunction()
Standard constructor.
void add_variable(const String &name, double value=0.0)
Adds an extra variable to the parser.
static constexpr bool can_hess
we cannot compute hessians
Traits_::PointType PointType
evaluation point type
Traits_::DataType DataType
coefficient data type
std::vector< double > _vars
a vector for the variable values
std::array< ::FunctionParser, img_dim_ > _parsers
our own private copy of the function parsers
Parsed vector function implementation.
void set_variable(const String &name, double value)
Sets the value of an extra variable.
static constexpr bool can_grad
we cannot compute gradients
std::map< String, double > _extra_vars
our extra variable names
ParsedVectorFunction(const String &function)
Constructor.
static constexpr int domain_dim
validate our dimensions
static constexpr bool can_value
we can compute function values
void add_variable(const String &name, double value=0.0)
Adds an extra variable to the parser.
static constexpr bool can_hess
we cannot compute hessians
void parse(const String &function)
Parses a function.
void add_constant(const String &name, double value)
Adds a constant to the parser.
Analytic::Image::Vector< img_dim_ > ImageType
This function is always a vector field.
std::array<::FunctionParser, img_dim_ > _parsers
the actual function parser objects
ParsedVectorFunction()
Standard constructor.
Base exception class.
Definition: exception.hpp:27
Class for parser related errors.
Definition: exception.hpp:132
String class implementation.
Definition: string.hpp:46
void pop_front()
Removes the first character from the string.
Definition: string.hpp:239
int compare_no_case(const String &other) const
Compares two strings without regard to case.
Definition: string.hpp:679
FEAT namespace.
Definition: adjactor.hpp:12
String stringify(const T_ &item)
Converts an item into a String.
Definition: string.hpp:944
@ value
specifies whether the space should supply basis function values
Scalar Function Image tag class.
Definition: function.hpp:28
Vector Field Image tag class.
Definition: function.hpp:47