GCC Code Coverage Report


Directory: ../../../builds/dumux-repositories/
File: /builds/dumux-repositories/dumux/dumux/common/functionfromstringexpression.hh
Date: 2024-09-21 20:52:54
Exec Total Coverage
Lines: 31 33 93.9%
Functions: 10 12 83.3%
Branches: 53 174 30.5%

Line Branch Exec Source
1 // -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 // vi: set et ts=4 sw=4 sts=4:
3 //
4 // SPDX-FileCopyrightInfo: Copyright © DuMux Project contributors, see AUTHORS.md in root folder
5 // SPDX-License-Identifier: GPL-3.0-or-later
6 //
7 /*!
8 * \file
9 * \ingroup Core
10 * \brief Evaluating string math expressions containing named variables
11 */
12 #ifndef DUMUX_COMMON_FUNCTION_FROM_STRING_EXPRESSION_HH
13 #define DUMUX_COMMON_FUNCTION_FROM_STRING_EXPRESSION_HH
14
15 #include <array>
16 #include <mutex>
17 #include <string>
18 #include <string_view>
19 #include <type_traits>
20 #include <iostream>
21
22 #include <dune/common/exceptions.hh>
23 #include <dune/common/fvector.hh>
24
25 #include <dumux/io/format.hh>
26 #include <dumux/io/expression/exprtk.hpp>
27
28 namespace Dumux {
29
30 /*!
31 * \ingroup Core
32 * \brief Evaluating string math expressions containing named variables
33 * \tparam numVars number of variables in the expression; number of function arguments of the call operator
34 * \tparam Scalar type of numerical values in the expression
35 *
36 * Example usage
37 \code{.cpp}
38 // Create a callable f(x,t) from a string expression containing variable literals.
39 // The constructor compiles the expression in the constructor making calls efficient.
40 // The constructor throws a Dune::IOError with detailed info if parsing fails.
41 std::string expr = getParam("Problem.Function"); // e.g. "5*x + x*sin(x*t)"
42 FunctionFromStringExpression<2> f(expr, "xt"); // variables "x" and "t"
43
44 // evaluate function, result is double (the default scalar type)
45 const double x = 1.0, t = 2.0;
46 const double result = f(x, t);
47 \endcode
48 *
49 * For variables with several characters construct
50 \code{.cpp}
51 // variables "pos" and "time"
52 FunctionFromStringExpression<2> f(expr, std::array<std::string, 2>{{"pos", "time"}});
53 \endcode
54 *
55 */
56 template<std::size_t numVars, class Scalar = double>
57 class FunctionFromStringExpression
58 {
59 using SymbolTable = exprtk::symbol_table<Scalar>;
60 using Expression = exprtk::expression<Scalar>;
61 using Parser = exprtk::parser<Scalar>;
62
63 public:
64 static constexpr std::size_t numVariables = numVars;
65
66 //! \brief Constructor from math expression and array of variable names
67 10 FunctionFromStringExpression(const std::string& expression, const std::array<std::string, numVars>& variableNames)
68
6/8
✓ Branch 2 taken 5 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 5 times.
✗ Branch 7 not taken.
✓ Branch 10 taken 4 times.
✓ Branch 11 taken 1 times.
✓ Branch 13 taken 4 times.
✓ Branch 14 taken 1 times.
24 { initialize_(expression, variableNames); }
69
70 //! \brief Delegating constructor using all characters of a string as variables
71 //! \note Calling FunctionFromStringExpression(expr, "xt") uses "x" and "t" as variable names
72 6 FunctionFromStringExpression(const std::string& expression, std::string_view variableNames)
73
1/2
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
12 : FunctionFromStringExpression(expression, extractVariableNames_(variableNames, std::make_index_sequence<numVars>{})) {}
74
75 template<class S, std::enable_if_t<std::is_convertible_v<Scalar, S>, int> = 0>
76 Scalar operator() (const std::array<S, numVars>& params) const
77 { return evalRandomAcessImpl_(params); }
78
79 template<class S, std::enable_if_t<std::is_convertible_v<Scalar, S>, int> = 0>
80 Scalar operator() (const Dune::FieldVector<S, numVars>& params) const
81 { return evalRandomAcessImpl_(params); }
82
83 template<class ...Params, std::enable_if_t<(sizeof...(Params) == numVars) && (std::is_convertible_v<Scalar, std::decay_t<Params>> && ...), int> = 0>
84 Scalar operator() (Params&&... params) const
85
4/56
✗ Branch 1 not taken.
✓ Branch 2 taken 100 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 10 not taken.
✓ Branch 11 taken 100 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 19 not taken.
✓ Branch 20 taken 100 times.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 28 not taken.
✓ Branch 29 taken 100 times.
✗ Branch 31 not taken.
✗ Branch 32 not taken.
✗ Branch 34 not taken.
✗ Branch 35 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
✗ Branch 40 not taken.
✗ Branch 42 not taken.
✗ Branch 43 not taken.
✗ Branch 45 not taken.
✗ Branch 46 not taken.
✗ Branch 48 not taken.
✗ Branch 49 not taken.
✗ Branch 50 not taken.
✗ Branch 51 not taken.
✗ Branch 53 not taken.
✗ Branch 54 not taken.
✗ Branch 56 not taken.
✗ Branch 57 not taken.
✗ Branch 59 not taken.
✗ Branch 60 not taken.
✗ Branch 61 not taken.
✗ Branch 62 not taken.
✗ Branch 64 not taken.
✗ Branch 65 not taken.
✗ Branch 67 not taken.
✗ Branch 68 not taken.
✗ Branch 70 not taken.
✗ Branch 71 not taken.
✗ Branch 72 not taken.
✗ Branch 73 not taken.
✗ Branch 75 not taken.
✗ Branch 76 not taken.
✗ Branch 78 not taken.
✗ Branch 79 not taken.
400 { return evalRandomAcessImpl_(std::array<Scalar, numVars>{ std::forward<Params>(params)... }); }
86
87 void setVerbosity(unsigned int v)
88 { verbosity_ = v; }
89
90 private:
91 template<class RandomAccessContainer>
92 800 Scalar evalRandomAcessImpl_(const RandomAccessContainer& params) const
93 {
94
0/2
✗ Branch 1 not taken.
✗ Branch 2 not taken.
800 std::lock_guard lock(evalMutex_);
95
2/2
✓ Branch 0 taken 700 times.
✓ Branch 1 taken 400 times.
2200 for (std::size_t i = 0; i < numVars; ++i)
96 4200 variables_[i] = params[i];
97
2/4
✓ Branch 1 taken 400 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 400 times.
✗ Branch 4 not taken.
1600 return expression_.value();
98 }
99
100 template<std::size_t... I>
101 6 std::array<std::string, numVars> extractVariableNames_(std::string_view names, std::index_sequence<I...>) const
102 {
103 static_assert(numVars == sizeof...(I), "Number of variables has to match size of index set.");
104
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
6 if (names.size() != numVars)
105 DUNE_THROW(Dune::IOError, "Number of variables in '"
106 << names << "' does not match number of function arguments: " << numVars);
107
4/8
✓ Branch 1 taken 2 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 2 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 2 times.
✗ Branch 8 not taken.
✓ Branch 10 taken 2 times.
✗ Branch 11 not taken.
12 return { std::string(1, names.at(I))... };
108 }
109
110 //! Parse the math expression and throw detailed error message when this fails
111 10 void initialize_(const std::string& expression, const std::array<std::string, numVars>& variableNames)
112 {
113
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 5 times.
28 for (std::size_t i = 0; i < numVars; ++i)
114
2/6
✓ Branch 4 taken 9 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 9 times.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
54 symbolTable_.add_variable(std::string{variableNames[i]}, variables_[i]);
115 10 symbolTable_.add_constants();
116 10 expression_.register_symbol_table(symbolTable_);
117
118
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 4 times.
10 if (!parser_.compile(expression, expression_))
119 {
120 2 std::stringstream ss;
121
2/6
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
6 ss << Fmt::format("Parsing expression '{}' failed.\n", expression);
122
123
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
2 if (verbosity_ >= 1)
124 {
125
4/4
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
✓ Branch 3 taken 1 times.
4 for (std::size_t i = 0; i < parser_.error_count(); ++i)
126 {
127
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 const auto error = parser_.get_error(i);
128
129
5/14
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 1 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 1 times.
✗ Branch 9 not taken.
✓ Branch 10 taken 1 times.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
4 ss << Fmt::format(
130 "-- error (position: {:02d}, type: {}): {}\n",
131 2 static_cast<unsigned int>(error.token.position),
132
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
2 exprtk::parser_error::to_str(error.mode).c_str(),
133 error.diagnostic.c_str()
134 );
135 }
136 }
137
138
10/24
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✓ Branch 13 taken 1 times.
✗ Branch 14 not taken.
✓ Branch 17 taken 1 times.
✗ Branch 18 not taken.
✓ Branch 20 taken 1 times.
✗ Branch 21 not taken.
✓ Branch 22 taken 1 times.
✗ Branch 23 not taken.
✓ Branch 25 taken 1 times.
✗ Branch 26 not taken.
✓ Branch 28 taken 1 times.
✗ Branch 29 not taken.
✓ Branch 30 taken 1 times.
✗ Branch 31 not taken.
✗ Branch 33 not taken.
✓ Branch 34 taken 1 times.
✗ Branch 36 not taken.
✗ Branch 37 not taken.
✗ Branch 38 not taken.
✗ Branch 39 not taken.
24 DUNE_THROW(Dune::IOError, ss.str());
139 }
140
1/2
✓ Branch 0 taken 4 times.
✗ Branch 1 not taken.
8 else if (verbosity_ >= 2)
141 {
142
2/6
✓ Branch 3 taken 4 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 4 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
24 std::cout << Fmt::format(
143 "Successfully parsed math expression '{}'\n",
144 expression
145 );
146 }
147 8 }
148
149 unsigned int verbosity_ = 2;
150 SymbolTable symbolTable_;
151 Expression expression_;
152 Parser parser_;
153 mutable std::array<Scalar, numVars> variables_;
154 mutable std::mutex evalMutex_;
155 };
156
157 } // end namespace Dumux
158
159 #endif
160