diff --git a/Documentation/ProjectFile/properties/property/Function/c_Function.md b/Documentation/ProjectFile/properties/property/Function/c_Function.md new file mode 100644 index 0000000000000000000000000000000000000000..5a7b4408a4c2d8414113e3d55e8aa1e6e0b03e41 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/c_Function.md @@ -0,0 +1 @@ +\copydoc MaterialPropertyLib::Function diff --git a/Documentation/ProjectFile/properties/property/Function/dvalue/i_dvalue.md b/Documentation/ProjectFile/properties/property/Function/dvalue/i_dvalue.md new file mode 100644 index 0000000000000000000000000000000000000000..25a872c3e27fa47b051a927dffe7bb4a8f159805 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/dvalue/i_dvalue.md @@ -0,0 +1 @@ +\copydoc MaterialPropertyLib::Function::dvalue_expressions_ diff --git a/Documentation/ProjectFile/properties/property/Function/dvalue/t_expression.md b/Documentation/ProjectFile/properties/property/Function/dvalue/t_expression.md new file mode 100644 index 0000000000000000000000000000000000000000..25a872c3e27fa47b051a927dffe7bb4a8f159805 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/dvalue/t_expression.md @@ -0,0 +1 @@ +\copydoc MaterialPropertyLib::Function::dvalue_expressions_ diff --git a/Documentation/ProjectFile/properties/property/Function/dvalue/t_variable_name.md b/Documentation/ProjectFile/properties/property/Function/dvalue/t_variable_name.md new file mode 100644 index 0000000000000000000000000000000000000000..4a95bbbd5692fee4c6a26126938cc2704a3e8a1f --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/dvalue/t_variable_name.md @@ -0,0 +1,2 @@ +Variable name for the derivative of the function with respect to the named +variable. diff --git a/Documentation/ProjectFile/properties/property/Function/value/i_value.md b/Documentation/ProjectFile/properties/property/Function/value/i_value.md new file mode 100644 index 0000000000000000000000000000000000000000..c3f64a5050683d2969460dc58884223c7820ee95 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/value/i_value.md @@ -0,0 +1 @@ +\copydoc MaterialPropertyLib::Function::value_expressions_ diff --git a/Documentation/ProjectFile/properties/property/Function/value/t_expression.md b/Documentation/ProjectFile/properties/property/Function/value/t_expression.md new file mode 100644 index 0000000000000000000000000000000000000000..c3f64a5050683d2969460dc58884223c7820ee95 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/Function/value/t_expression.md @@ -0,0 +1 @@ +\copydoc MaterialPropertyLib::Function::value_expressions_ diff --git a/MaterialLib/CMakeLists.txt b/MaterialLib/CMakeLists.txt index f44b84266f246111f35353217a9a0d4c765167ed..6767d5f4dd544a0af4ab50a75b06c4e8a4124611 100644 --- a/MaterialLib/CMakeLists.txt +++ b/MaterialLib/CMakeLists.txt @@ -40,5 +40,5 @@ ogs_add_library(MaterialLib ${SOURCES}) target_link_libraries( MaterialLib PUBLIC MaterialLib_SolidModels MaterialLib_FractureModels - PRIVATE MathLib MeshLib ParameterLib spdlog::spdlog + PRIVATE MathLib MeshLib ParameterLib exprtk spdlog::spdlog ) diff --git a/MaterialLib/MPL/CreateProperty.cpp b/MaterialLib/MPL/CreateProperty.cpp index 4ea28761befab9e3fce62a479cc72a9773a4e000..d3dba9812fd12426e3c7812df070a689e37161be 100644 --- a/MaterialLib/MPL/CreateProperty.cpp +++ b/MaterialLib/MPL/CreateProperty.cpp @@ -56,6 +56,10 @@ std::unique_ptr<MaterialPropertyLib::Property> createProperty( { return createExponential(config); } + if (property_type == "Function") + { + return createFunction(config); + } if (property_type == "Parameter") { diff --git a/MaterialLib/MPL/Properties/CreateFunction.cpp b/MaterialLib/MPL/Properties/CreateFunction.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e6048cb4f4e290ada11e962284c41f89db7d04c6 --- /dev/null +++ b/MaterialLib/MPL/Properties/CreateFunction.cpp @@ -0,0 +1,60 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include "BaseLib/ConfigTree.h" +#include "Function.h" + +namespace MaterialPropertyLib +{ +std::unique_ptr<Function> createFunction(BaseLib::ConfigTree const& config) +{ + //! \ogs_file_param{properties__property__type} + config.checkConfigParameter("type", "Function"); + + // Second access for storage. + //! \ogs_file_param{properties__property__name} + auto property_name = config.peekConfigParameter<std::string>("name"); + + DBUG("Create Function property {:s}.", property_name); + + std::vector<std::string> value_expressions; + //! \ogs_file_param{properties__property__Function__value} + auto const& value_config = config.getConfigSubtree("value"); + + //! \ogs_file_param{properties__property__Function__value__expression} + for (auto const& p : value_config.getConfigSubtreeList("expression")) + { + value_expressions.emplace_back(p.getValue<std::string>()); + } + + // For each derivative a name of the variable and the list of expressions. + std::vector<std::pair<std::string, std::vector<std::string>>> + dvalue_expressions; + //! \ogs_file_param{properties__property__Function__dvalue} + for (auto const& dvalue_config : config.getConfigSubtreeList("dvalue")) + { + auto variable_name = + //! \ogs_file_param{properties__property__Function__dvalue__variable_name} + dvalue_config.getConfigParameter<std::string>("variable_name"); + + std::vector<std::string> expressions; + //! \ogs_file_param{properties__property__Function__dvalue__expression} + for (auto const& p : dvalue_config.getConfigSubtreeList("expression")) + { + expressions.emplace_back(p.getValue<std::string>()); + } + dvalue_expressions.emplace_back(std::move(variable_name), + std::move(expressions)); + } + + return std::make_unique<MaterialPropertyLib::Function>( + std::move(property_name), std::move(value_expressions), + std::move(dvalue_expressions)); +} +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/CreateFunction.h b/MaterialLib/MPL/Properties/CreateFunction.h new file mode 100644 index 0000000000000000000000000000000000000000..0f86f3d02b6bf9368178c1a843c32854233087fb --- /dev/null +++ b/MaterialLib/MPL/Properties/CreateFunction.h @@ -0,0 +1,27 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#pragma once + +#include <memory> + +namespace BaseLib +{ +class ConfigTree; +} + +namespace MaterialPropertyLib +{ +class Function; +} + +namespace MaterialPropertyLib +{ +std::unique_ptr<Function> createFunction(BaseLib::ConfigTree const& config); +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/CreateProperties.h b/MaterialLib/MPL/Properties/CreateProperties.h index 7b45b0a11a45babcb05cb7b645c3c792f065a6f4..de51caa288965ead42d885d79c624f2a7c5ad733 100644 --- a/MaterialLib/MPL/Properties/CreateProperties.h +++ b/MaterialLib/MPL/Properties/CreateProperties.h @@ -27,6 +27,7 @@ #include "CreateEffectiveThermalConductivityPorosityMixing.h" #include "CreateEmbeddedFracturePermeability.h" #include "CreateExponential.h" +#include "CreateFunction.h" #include "CreateGasPressureDependentPermeability.h" #include "CreateIdealGasLaw.h" #include "CreateKozenyCarmanModel.h" diff --git a/MaterialLib/MPL/Properties/Function.cpp b/MaterialLib/MPL/Properties/Function.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bbdb668e633c182ae7f20a9bc45388e56b72e629 --- /dev/null +++ b/MaterialLib/MPL/Properties/Function.cpp @@ -0,0 +1,216 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ + +#include "MaterialLib/MPL/Properties/Function.h" + +#include <numeric> + +#include "BaseLib/Algorithm.h" + +namespace MaterialPropertyLib +{ +// Passing symbol table by reference as required by register_symbol_table() +// call. +template <typename T> +static std::vector<exprtk::expression<T>> compileExpressions( + exprtk::symbol_table<T>& symbol_table, + std::vector<std::string> const& string_expressions) +{ + exprtk::parser<T> parser; + + std::vector<exprtk::expression<T>> expressions(string_expressions.size()); + expressions.resize(string_expressions.size()); + for (unsigned i = 0; i < string_expressions.size(); ++i) + { + expressions[i].register_symbol_table(symbol_table); + if (!parser.compile(string_expressions[i], expressions[i])) + { + OGS_FATAL("Error: {:s}\tExpression: {:s}\n", + parser.error(), + string_expressions[i]); + } + } + return expressions; +} + +static void updateVariableValues( + std::vector<std::pair<int, double*>> const& symbol_values, + VariableArray const& variable_array) +{ + for (auto& index_value_ptr_pair : symbol_values) + { + auto const index = index_value_ptr_pair.first; + + double* value_ptr = index_value_ptr_pair.second; + std::visit( + [&value_ptr, &index](auto&& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_same_v<T, std::monostate>) + { + OGS_FATAL( + "Function property: variable {:s} value needed for " + "evaluation of the expression was not set by the " + "caller.", + variable_enum_to_string[index]); + } + else if constexpr (std::is_same_v<T, double>) + { + *value_ptr = v; + } + else + { + OGS_FATAL( + "Function property: not implemented handling for a " + "type {:s} of variable {:s}.", + typeid(T).name(), variable_enum_to_string[index]); + } + }, + variable_array[index]); + } +} + +static PropertyDataType evaluateExpressions( + std::vector<std::pair<int, double*>> const& symbol_values, + VariableArray const& variable_array, + std::vector<exprtk::expression<double>> const& expressions) +{ + updateVariableValues(symbol_values, variable_array); + + std::vector<double> result(expressions.size()); + std::transform(begin(expressions), end(expressions), begin(result), + [](auto const& e) { return e.value(); }); + + switch (result.size()) + { + case 1: + { + return result[0]; + } + case 2: + { + return Eigen::Vector2d{result[0], result[1]}; + } + case 3: + { + return Eigen::Vector3d{result[0], result[1], result[2]}; + } + case 4: + { + Eigen::Matrix<double, 2, 2> m; + m = Eigen::Map<Eigen::Matrix<double, 2, 2> const>(result.data(), 2, + 2); + return m; + } + case 9: + { + Eigen::Matrix<double, 3, 3> m; + m = Eigen::Map<Eigen::Matrix<double, 3, 3> const>(result.data(), 3, + 3); + return m; + } + } + OGS_FATAL("Cannot convert a vector of size {} to a PropertyDataType", + result.size()); +} + +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) +{ + std::vector<std::string> variables; + + auto collect_variables = [&](auto string_expressions) { + for (auto const& string_expression : string_expressions) + { + if (!exprtk::collect_variables(string_expression, variables)) + { + OGS_FATAL("Collecting variables didn't work."); + } + } + }; + + collect_variables(value_string_expressions); + for (auto const& var_name_string_expression : dvalue_string_expressions) + { + collect_variables(var_name_string_expression.second); + } + + BaseLib::makeVectorUnique(variables); + return variables; +} + +Function::Function(std::string name, + std::vector<std::string> + value_string_expressions, + std::vector<std::pair<std::string, std::vector<std::string>>> + dvalue_string_expressions) +{ + name_ = std::move(name); + + // Create symbol table for used variables. + exprtk::symbol_table<double> symbol_table; + + for (auto const& v : + collectVariables(value_string_expressions, dvalue_string_expressions)) + { + symbol_table.create_variable(v); + // Store variables index in the variable array and the pointer to the + // value in the symbol table for fast access later. + int const variable_array_index = + static_cast<int>(convertStringToVariable(v)); + symbol_values_.emplace_back(variable_array_index, + &symbol_table.get_variable(v)->ref()); + } + + // value expressions. + value_expressions_ = + compileExpressions(symbol_table, value_string_expressions); + + // dValue expressions. + for (auto const& [variable_name, string_expressions] : + dvalue_string_expressions) + { + dvalue_expressions_.emplace_back( + convertStringToVariable(variable_name), + compileExpressions(symbol_table, string_expressions)); + } +} + +PropertyDataType Function::value(VariableArray const& variable_array, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/, double const /*dt*/) const +{ + return evaluateExpressions(symbol_values_, variable_array, + value_expressions_); +} + +PropertyDataType Function::dValue(VariableArray const& variable_array, + Variable const primary_variable, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/, double const /*dt*/) const +{ + auto const it = std::find_if(begin(dvalue_expressions_), + end(dvalue_expressions_), + [&primary_variable](auto const& v) { + return v.first == primary_variable; + }); + + if (it == end(dvalue_expressions_)) + { + OGS_FATAL( + "Requested derivative with respect to the variable {:s} not " + "provided for Function-type property {:s}.", + variable_enum_to_string[static_cast<int>(primary_variable)], name_); + } + + return evaluateExpressions(symbol_values_, variable_array, it->second); +} + +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/Function.h b/MaterialLib/MPL/Properties/Function.h new file mode 100644 index 0000000000000000000000000000000000000000..27ad4c1fa2ef1fa693de63df0870da1e9cd77606 --- /dev/null +++ b/MaterialLib/MPL/Properties/Function.h @@ -0,0 +1,59 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2021, OpenGeoSys Community (http://www.opengeosys.org) + * Distributed under a Modified BSD License. + * See accompanying file LICENSE.txt or + * http://www.opengeosys.org/project/license + */ +#pragma once + +#include <exprtk.hpp> +#include <utility> +#include <vector> + +#include "MaterialLib/MPL/Property.h" +#include "MaterialLib/MPL/VariableType.h" + +namespace MaterialPropertyLib +{ +/// A function property defined by mathematical expression. For the evaluation +/// of the expressions the exprtk library is used. In the expressions all +/// variables defined in MaterialPropertyLib::Variable enum can be used. +/// +/// \warning The evaluation calls are not to be used in parallel (openMP), +/// because the values' updates are using the same space. +class Function final : public Property +{ +public: + Function(std::string name, + std::vector<std::string> + value_string_expressions, + std::vector<std::pair<std::string, std::vector<std::string>>> + dvalue_string_expressions); + + PropertyDataType value(VariableArray const& variable_array, + ParameterLib::SpatialPosition const& pos, + double const t, + double const dt) const override; + + PropertyDataType dValue(VariableArray const& variable_array, + Variable const primary_variable, + ParameterLib::SpatialPosition const& pos, + double const t, + double const dt) const override; + +private: + using Expression = exprtk::expression<double>; + + /// Mapping from variable array index to symbol table values. + std::vector<std::pair<int, double*>> symbol_values_; + /// Value expressions. + /// Multiple expressions are representing vector-valued functions. + std::vector<Expression> value_expressions_; + /// Derivative expressions with respect to the variable. + /// Multiple expressions are representing vector-valued functions. + std::vector<std::pair<Variable, std::vector<Expression>>> + dvalue_expressions_; +}; +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/Properties.h b/MaterialLib/MPL/Properties/Properties.h index f22a6061d3cb2521b16c797f66eb45094d62f81b..290a0e43669170910f8b7c85b75302b9e4cefcc7 100644 --- a/MaterialLib/MPL/Properties/Properties.h +++ b/MaterialLib/MPL/Properties/Properties.h @@ -28,6 +28,7 @@ #include "Enthalpy/LinearWaterVapourLatentHeat.h" #include "Enthalpy/WaterVapourLatentHeatWithCriticalTemperature.h" #include "Exponential.h" +#include "Function.h" #include "GasPressureDependentPermeability.h" #include "IdealGasLaw.h" #include "Linear.h"