OGS
Function.cpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: Copyright (c) OpenGeoSys Community (opengeosys.org)
2// SPDX-License-Identifier: BSD-3-Clause
3
5
6#ifdef _OPENMP
7#include <omp.h>
8#endif
9
10#include <exprtk.hpp>
11#include <numeric>
12#include <range/v3/range/conversion.hpp>
13#include <range/v3/view/transform.hpp>
14#include <ranges>
15#include <typeinfo>
16#include <unordered_set>
17
18#include "BaseLib/Algorithm.h"
23
24namespace MaterialPropertyLib
25{
26struct CurveWrapper : public exprtk::ifunction<double>
27{
29 : exprtk::ifunction<double>(1), _curve(curve)
30 {
31 exprtk::disable_has_side_effects(*this);
32 }
33
34 double operator()(const double& t) override { return _curve.getValue(t); }
35
36private:
38};
39// Passing symbol table by reference as required by register_symbol_table()
40// call.
41template <typename T>
42static std::vector<exprtk::expression<T>> compileExpressions(
43 exprtk::symbol_table<T>& symbol_table,
44 std::vector<std::string> const& string_expressions)
45{
46 exprtk::parser<T> parser;
47
48 std::vector<exprtk::expression<T>> expressions(string_expressions.size());
49 for (unsigned i = 0; i < string_expressions.size(); ++i)
50 {
51 expressions[i].register_symbol_table(symbol_table);
52 if (!parser.compile(string_expressions[i], expressions[i]))
53 {
54 OGS_FATAL("Error: {:s}\tExpression: {:s}\n",
55 parser.error(),
56 string_expressions[i]);
57 }
58 }
59 return expressions;
60}
61
62template <int D>
64{
65public:
66 using Expression = exprtk::expression<double>;
67
68public:
70 int num_threads,
71 std::vector<std::string> const& variables,
72 std::vector<std::string> const& value_string_expressions,
73 std::vector<std::pair<std::string, std::vector<std::string>>> const&
74 dvalue_string_expressions,
75 std::map<std::string,
76 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
77 curves);
78
79private:
82 exprtk::symbol_table<double> createSymbolTable(
83 std::vector<std::string> const& variables,
84 std::map<std::string,
85 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
86 curves,
87 VariableArray& variable_array);
88
90 std::vector<exprtk::symbol_table<double>> createSymbolTables(
91 int num_threads,
92 std::vector<std::string> const& variables,
93 std::map<std::string,
94 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
95 curves);
96
97public:
100 std::vector<std::vector<Expression>> value_expressions;
101
104 std::vector<std::vector<std::pair<Variable, std::vector<Expression>>>>
106
109 std::vector<VariableArray> variable_arrays;
110
111 std::map<std::string, CurveWrapper> _curve_wrappers;
112
114};
115
116template <int D>
118 std::vector<std::string> const& variables,
119 std::map<std::string,
120 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
121 curves,
122 VariableArray& variable_array)
123{
124 exprtk::symbol_table<double> symbol_table;
125 symbol_table.add_constants();
126
127 std::unordered_set<std::string> curve_names;
128 for (auto const& curve : curves)
129 {
130 curve_names.insert(curve.first);
131 }
132
133 std::unordered_set<std::string> used_curves;
134
135 symbol_table.create_variable("t");
136
137 for (auto const& v : variables)
138 {
139 if (v == "t")
140 {
141 continue;
142 }
143 else if (v == "x" || v == "y" || v == "z")
144 {
145 symbol_table.create_variable("x");
146 symbol_table.create_variable("y");
147 symbol_table.create_variable("z");
149 }
150 else if (curve_names.contains(v))
151 {
152 used_curves.insert(v);
153 }
154 else
155 {
156 auto add_scalar = [&v, &symbol_table](double& value)
157 { symbol_table.add_variable(v, value); };
158
159 auto add_vector =
160 [&v, &symbol_table](double* ptr, std::size_t const size)
161 { symbol_table.add_vector(v, ptr, size); };
162
163 auto add_any_variable = BaseLib::Overloaded{
164 [&add_scalar](VariableArray::Scalar* address)
165 { add_scalar(*address); },
166 [&add_vector](VariableArray::KelvinVector* address)
167 {
168 auto constexpr size =
170 auto& result = address->template emplace<
171 Eigen::Matrix<double, size, 1>>();
172 add_vector(result.data(), size);
173 },
174 [&add_vector](VariableArray::DeformationGradient* address)
175 {
176 auto constexpr size = MathLib::VectorizedTensor::size(D);
177 auto& result = address->template emplace<
178 Eigen::Matrix<double, size, 1>>();
179 add_vector(result.data(), size);
180 }};
181
182 Variable const variable = convertStringToVariable(v);
183 variable_array.visitVariable(add_any_variable, variable);
184 }
185 }
186
187 for (const auto& name : used_curves)
188 {
189 const auto& curve_ptr = curves.at(name);
190 _curve_wrappers.emplace(name, CurveWrapper(*curve_ptr));
191 }
192 for (auto& [name, wrapper] : _curve_wrappers)
193 {
194 symbol_table.add_function(name, wrapper);
195 }
196 return symbol_table;
197}
198template <int D>
199std::vector<exprtk::symbol_table<double>>
201 int num_threads,
202 std::vector<std::string> const& variables,
203 std::map<std::string,
204 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
205 curves)
206{
207 std::vector<exprtk::symbol_table<double>> symbol_tables(num_threads);
208
209 for (int thread_id = 0; thread_id < num_threads; ++thread_id)
210 {
211 symbol_tables[thread_id] =
212 createSymbolTable(variables, curves, variable_arrays[thread_id]);
213 }
214
215 return symbol_tables;
216}
217
218template <int D>
220 int num_threads,
221 std::vector<std::string> const& variables,
222 std::vector<std::string> const& value_string_expressions,
223 std::vector<std::pair<std::string, std::vector<std::string>>> const&
224 dvalue_string_expressions,
225 std::map<std::string,
226 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
227 curves)
228{
229 variable_arrays.resize(num_threads);
230 value_expressions.resize(num_threads);
231 dvalue_expressions.resize(num_threads);
232
233 auto symbol_tables = createSymbolTables(num_threads, variables, curves);
234
235 // value expressions per thread.
236 for (int thread_id = 0; thread_id < num_threads; ++thread_id)
237 {
239 symbol_tables[thread_id], value_string_expressions);
240 }
241
242 // dValue expressions per thread.
243 for (int thread_id = 0; thread_id < num_threads; ++thread_id)
244 {
245 for (auto const& [variable_name, string_expressions] :
246 dvalue_string_expressions)
247 {
248 dvalue_expressions[thread_id].emplace_back(
249 convertStringToVariable(variable_name),
250 compileExpressions(symbol_tables[thread_id],
251 string_expressions));
252 }
253 }
254}
255
256static void updateVariableArrayValues(std::vector<Variable> const& variables,
257 VariableArray const& new_variable_array,
258 VariableArray& variable_array)
259{
260 for (auto const& variable : variables)
261 {
262 auto assign_scalar =
263 [&variable, &new_variable_array](VariableArray::Scalar* address)
264 {
265 double const value = *std::get<VariableArray::Scalar const*>(
266 new_variable_array.address_of(variable));
267
268 if (std::isnan(value))
269 {
270 OGS_FATAL(
271 "Function property: Scalar variable '{:s}' is not "
272 "initialized.",
273 variable_enum_to_string[static_cast<int>(variable)]);
274 }
275
276 *address = value;
277 };
278 auto assign_kelvin_vector = [&variable, &new_variable_array](
280 {
281 auto assign_value = [&destination = *address,
282 &variable]<typename S>(S const& source)
283 {
284 if constexpr (std::is_same_v<S, std::monostate>)
285 {
286 OGS_FATAL(
287 "Function property: Kelvin vector variable '{:s}' is "
288 "not initialized.",
289 variable_enum_to_string[static_cast<int>(variable)]);
290 }
291 else
292 {
293 if (std::holds_alternative<S>(destination))
294 {
295 std::get<S>(destination) = MathLib::KelvinVector::
297 }
298 else
299 {
300 OGS_FATAL(
301 "Function property: Mismatch of Kelvin vector "
302 "sizes for variable {:s}.",
303 variable_enum_to_string[static_cast<int>(
304 variable)]);
305 }
306 }
307 };
308 std::visit(assign_value,
309 *std::get<VariableArray::KelvinVector const*>(
310 new_variable_array.address_of(variable)));
311 };
312 auto assign_deformation_gradient =
313 [&variable,
314 &new_variable_array](VariableArray::DeformationGradient* address)
315 {
316 auto assign_value = [&destination = *address,
317 &variable]<typename S>(S const& source)
318 {
319 if constexpr (std::is_same_v<S, std::monostate>)
320 {
321 OGS_FATAL(
322 "Function property: Vectorized tensor variable '{:s}' "
323 "is not initialized.",
324 variable_enum_to_string[static_cast<int>(variable)]);
325 }
326 else
327 {
328 if (std::holds_alternative<S>(destination))
329 {
330 std::get<S>(destination) = source;
331 }
332 else
333 {
334 OGS_FATAL(
335 "Function property: Mismatch of vectorized tensor "
336 "sizes for variable {:s}.",
337 variable_enum_to_string[static_cast<int>(
338 variable)]);
339 }
340 }
341 };
342 std::visit(assign_value,
343 *std::get<VariableArray::DeformationGradient const*>(
344 new_variable_array.address_of(variable)));
345 };
346
347 variable_array.visitVariable(
348 BaseLib::Overloaded{assign_scalar, assign_kelvin_vector,
349 assign_deformation_gradient},
350 variable);
351 }
352}
353
355 std::vector<Variable> const& variables,
356 VariableArray const& new_variable_array,
357 ParameterLib::SpatialPosition const& pos, double const t,
358 std::vector<exprtk::expression<double>> const& expressions,
359 VariableArray& variable_array, bool const spatial_position_is_required)
360{
361 std::vector<double> result(expressions.size());
362
363 updateVariableArrayValues(variables, new_variable_array, variable_array);
364
365 std::transform(begin(expressions), end(expressions), begin(result),
366 [t, &pos, spatial_position_is_required](auto const& e)
367 {
368 auto& symbol_table = e.get_symbol_table();
369 symbol_table.get_variable("t")->ref() = t;
370
371 if (spatial_position_is_required)
372 {
373 if (!pos.getCoordinates())
374 {
375 OGS_FATAL(
376 "FunctionParameter: The spatial position "
377 "has to be set by "
378 "coordinates.");
379 }
380 auto const coords = pos.getCoordinates().value();
381 symbol_table.get_variable("x")->ref() = coords[0];
382 symbol_table.get_variable("y")->ref() = coords[1];
383 symbol_table.get_variable("z")->ref() = coords[2];
384 }
385
386 return e.value();
387 });
388
389 switch (result.size())
390 {
391 case 1:
392 {
393 return result[0];
394 }
395 case 2:
396 {
397 return Eigen::Vector2d{result[0], result[1]};
398 }
399 case 3:
400 {
401 return Eigen::Vector3d{result[0], result[1], result[2]};
402 }
403 case 4:
404 {
405 Eigen::Matrix<double, 2, 2> m;
406 m = Eigen::Map<Eigen::Matrix<double, 2, 2> const>(result.data(), 2,
407 2);
408 return m;
409 }
410 case 9:
411 {
412 Eigen::Matrix<double, 3, 3> m;
413 m = Eigen::Map<Eigen::Matrix<double, 3, 3> const>(result.data(), 3,
414 3);
415 return m;
416 }
417 }
418 OGS_FATAL("Cannot convert a vector of size {} to a PropertyDataType",
419 result.size());
420}
421
422static std::vector<std::string> collectVariables(
423 std::vector<std::string> const& value_string_expressions,
424 std::vector<std::pair<std::string, std::vector<std::string>>> const&
425 dvalue_string_expressions)
426{
427 std::vector<std::string> variables;
428
429 auto collect_variables = [&](auto string_expressions)
430 {
431 for (auto const& string_expression : string_expressions)
432 {
433 if (!exprtk::collect_variables(string_expression, variables))
434 {
435 OGS_FATAL(
436 "Collecting variables from expression '{}' didn't work.",
437 string_expression);
438 }
439 }
440 };
441
442 collect_variables(value_string_expressions);
443 for (auto const& var_name_string_expression : dvalue_string_expressions)
444 {
445 collect_variables(var_name_string_expression.second);
446 }
447
448 BaseLib::makeVectorUnique(variables);
449 return variables;
450}
451
453 std::string name,
454 std::vector<std::string> const& value_string_expressions,
455 std::vector<std::pair<std::string, std::vector<std::string>>> const&
456 dvalue_string_expressions,
457 std::map<std::string,
458 std::unique_ptr<MathLib::PiecewiseLinearInterpolation>> const&
459 curves)
460{
461 name_ = std::move(name);
462
463 auto const variables =
464 collectVariables(value_string_expressions, dvalue_string_expressions);
465
466 // filter the strings unrelated to time or spatial position
467 static std::unordered_set<std::string> filter_not_variables = {"t", "x",
468 "y", "z"};
469 for (auto const& curve : curves)
470 {
471 filter_not_variables.insert(curve.first);
472 }
473
474 variables_ =
475 variables |
476 std::views::filter([](const std::string& s)
477 { return !filter_not_variables.contains(s); }) |
478 std::views::transform([](std::string const& s)
479 { return convertStringToVariable(s); }) |
480 ranges::to<std::vector>;
481
482 auto const get_number_omp_threads = []()
483 {
484#ifdef _OPENMP
485 return omp_get_max_threads();
486#else
487 return 1;
488#endif
489 };
490
491 int const num_threads = std::max(BaseLib::getNumberOfAssemblyThreads(),
492 get_number_omp_threads());
493
494 impl2_ = std::make_unique<Implementation<2>>(
495 num_threads, variables, value_string_expressions,
496 dvalue_string_expressions, curves);
497 impl3_ = std::make_unique<Implementation<3>>(
498 num_threads, variables, value_string_expressions,
499 dvalue_string_expressions, curves);
500}
501
502std::variant<Function::Implementation<2>*, Function::Implementation<3>*>
504 VariableArray const& variable_array) const
505{
506 if (variable_array.is2D())
507 {
508 return impl2_.get();
509 }
510 if (variable_array.is3D())
511 {
512 return impl3_.get();
513 }
514
515 OGS_FATAL(
516 "Variable array has vectors for 2 and 3 dimensions simultaneously. "
517 "Mixed dimensions cannot be dealt within Function evaluation.");
518}
519
522 double const t, double const /*dt*/) const
523{
524#ifdef _OPENMP
525 int const thread_id = omp_get_thread_num();
526#else
527 int const thread_id = 0;
528#endif
529 return std::visit(
530 [&](auto&& impl_ptr)
531 {
532 if (thread_id >=
533 static_cast<int>(impl_ptr->value_expressions.size()))
534 {
535 OGS_FATAL(
536 "In Function-type property '{:s}' evaluation the "
537 "OMP-thread with id {:d} exceeds the number of allocated "
538 "threads {:d}.",
539 name_, thread_id, impl_ptr->value_expressions.size());
540 }
541 return evaluateExpressions(variables_, variable_array, pos, t,
542 impl_ptr->value_expressions[thread_id],
543 impl_ptr->variable_arrays[thread_id],
544 impl_ptr->spatial_position_is_required);
545 },
547}
548
550 Variable const variable,
552 double const t, double const /*dt*/) const
553{
554#ifdef _OPENMP
555 int const thread_id = omp_get_thread_num();
556#else
557 int const thread_id = 0;
558#endif
559 return std::visit(
560 [&](auto&& impl_ptr)
561 {
562 if (thread_id >=
563 static_cast<int>(impl_ptr->dvalue_expressions.size()))
564 {
565 OGS_FATAL(
566 "In Function-type property '{:s}' evaluation the "
567 "OMP-thread with id {:d} exceeds the number of allocated "
568 "threads {:d}.",
569 name_, thread_id, impl_ptr->value_expressions.size());
570 }
571 auto const it = std::find_if(
572 begin(impl_ptr->dvalue_expressions[thread_id]),
573 end(impl_ptr->dvalue_expressions[thread_id]),
574 [&variable](auto const& v) { return v.first == variable; });
575
576 if (it == end(impl_ptr->dvalue_expressions[thread_id]))
577 {
578 OGS_FATAL(
579 "Requested derivative with respect to the variable {:s} "
580 "not "
581 "provided for Function-type property {:s}.",
582 variable_enum_to_string[static_cast<int>(variable)], name_);
583 }
584
585 return evaluateExpressions(variables_, variable_array, pos, t,
586 it->second,
587 impl_ptr->variable_arrays[thread_id],
588 impl_ptr->spatial_position_is_required);
589 },
591}
592
593Function::~Function() = default;
594
595} // namespace MaterialPropertyLib
#define OGS_FATAL(...)
Definition Error.h:19
std::vector< VariableArray > variable_arrays
Definition Function.cpp:109
std::vector< exprtk::symbol_table< double > > createSymbolTables(int num_threads, std::vector< std::string > const &variables, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves)
Create symbol tables for all threads.
Definition Function.cpp:200
Implementation(int num_threads, std::vector< std::string > const &variables, std::vector< std::string > const &value_string_expressions, std::vector< std::pair< std::string, std::vector< std::string > > > const &dvalue_string_expressions, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves)
Definition Function.cpp:219
exprtk::expression< double > Expression
Definition Function.cpp:66
std::map< std::string, CurveWrapper > _curve_wrappers
Definition Function.cpp:111
std::vector< std::vector< std::pair< Variable, std::vector< Expression > > > > dvalue_expressions
Definition Function.cpp:105
exprtk::symbol_table< double > createSymbolTable(std::vector< std::string > const &variables, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves, VariableArray &variable_array)
Definition Function.cpp:117
std::vector< std::vector< Expression > > value_expressions
Definition Function.cpp:100
std::unique_ptr< Implementation< 3 > > impl3_
Definition Function.h:54
std::vector< Variable > variables_
Variables used in the exprtk expressions.
Definition Function.h:61
std::variant< Function::Implementation< 2 > *, Function::Implementation< 3 > * > getImplementationForDimensionOfVariableArray(VariableArray const &variable_array) const
Definition Function.cpp:503
Function(std::string name, std::vector< std::string > const &value_string_expressions, std::vector< std::pair< std::string, std::vector< std::string > > > const &dvalue_string_expressions, std::map< std::string, std::unique_ptr< MathLib::PiecewiseLinearInterpolation > > const &curves)
Definition Function.cpp:452
PropertyDataType value(VariableArray const &variable_array, ParameterLib::SpatialPosition const &pos, double const t, double const dt) const override
Definition Function.cpp:520
PropertyDataType dValue(VariableArray const &variable_array, Variable const variable, ParameterLib::SpatialPosition const &pos, double const t, double const dt) const override
Definition Function.cpp:549
std::unique_ptr< Implementation< 2 > > impl2_
Definition Function.h:53
virtual PropertyDataType value() const
std::variant< std::monostate, Eigen::Vector< double, 5 >, Eigen::Vector< double, 9 > > DeformationGradient
std::variant< std::monostate, Eigen::Vector< double, 4 >, Eigen::Vector< double, 6 > > KelvinVector
VariablePointerConst address_of(Variable const v) const
auto visitVariable(Visitor &&visitor, Variable const variable)
std::optional< MathLib::Point3d > const getCoordinates() const
int getNumberOfAssemblyThreads()
void makeVectorUnique(std::vector< T > &v)
Definition Algorithm.h:173
static std::vector< exprtk::expression< T > > compileExpressions(exprtk::symbol_table< T > &symbol_table, std::vector< std::string > const &string_expressions)
Definition Function.cpp:42
static std::vector< std::string > collectVariables(std::vector< std::string > const &value_string_expressions, std::vector< std::pair< std::string, std::vector< std::string > > > const &dvalue_string_expressions)
Definition Function.cpp:422
static PropertyDataType evaluateExpressions(std::vector< Variable > const &variables, VariableArray const &new_variable_array, ParameterLib::SpatialPosition const &pos, double const t, std::vector< exprtk::expression< double > > const &expressions, VariableArray &variable_array, bool const spatial_position_is_required)
Definition Function.cpp:354
static void updateVariableArrayValues(std::vector< Variable > const &variables, VariableArray const &new_variable_array, VariableArray &variable_array)
Definition Function.cpp:256
static const std::array< std::string, static_cast< int >(Variable::number_of_variables)> variable_enum_to_string
Variable convertStringToVariable(std::string const &string)
std::variant< double, Eigen::Matrix< double, 2, 1 >, Eigen::Matrix< double, 3, 1 >, Eigen::Matrix< double, 2, 2 >, Eigen::Matrix< double, 3, 3 >, Eigen::Matrix< double, 4, 1 >, Eigen::Matrix< double, 6, 1 >, Eigen::MatrixXd > PropertyDataType
Eigen::Matrix< double, 4, 1 > kelvinVectorToSymmetricTensor(Eigen::Matrix< double, 4, 1, Eigen::ColMajor, 4, 1 > const &v)
constexpr int kelvin_vector_dimensions(int const displacement_dim)
Kelvin vector dimensions for given displacement dimension.
constexpr int size(int const displacement_dim)
Vectorized tensor size for given displacement dimension.
double operator()(const double &t) override
Definition Function.cpp:34
CurveWrapper(const MathLib::PiecewiseLinearInterpolation &curve)
Definition Function.cpp:28
const MathLib::PiecewiseLinearInterpolation & _curve
Definition Function.cpp:37