diff --git a/MeshLib/Utils/IntegrationPointWriter.h b/MeshLib/Utils/IntegrationPointWriter.h index 10df6f5fd86ad2f617c0f4b85c663fae9f10263a..e4eae288c6240074c0bfe4bcac56acc41ad05fa0 100644 --- a/MeshLib/Utils/IntegrationPointWriter.h +++ b/MeshLib/Utils/IntegrationPointWriter.h @@ -22,6 +22,10 @@ class Properties; struct IntegrationPointWriter final { + /// Constructor taking a member function of the \c LocalAssemblerInterface + /// as a callback. + /// + /// The callback \c getIpData must return a \c std::vector<double>. template <typename LocalAssemblerInterface, typename... Args> IntegrationPointWriter( std::string const& name, @@ -53,6 +57,40 @@ struct IntegrationPointWriter final return result; }; } + + /// Constructor taking an \c accessor function as a callback. + /// + /// The accessor function must take a const reference to a + /// \c LocalAssemblerInterface object as its only argument. + /// The callback \c accessor must return a \c std::vector<double>. + template <typename LocalAssemblerInterface, typename Accessor> + IntegrationPointWriter( + std::string const& name, + int const n_components, + int const integration_order, + std::vector<std::unique_ptr<LocalAssemblerInterface>> const& + local_assemblers, + Accessor accessor) + : _name(name), + _n_components(n_components), + _integration_order(integration_order) + { + _callback = [&local_assemblers, accessor] + { + // Result containing integration point data for each local + // assembler. + std::vector<std::vector<double>> result; + result.reserve(local_assemblers.size()); + + std::transform(begin(local_assemblers), end(local_assemblers), + std::back_inserter(result), + [&accessor](auto const& la) + { return accessor(*la); }); + + return result; + }; + } + int numberOfComponents() const { return _n_components; } int integrationOrder() const { return _integration_order; } std::string name() const { return _name; } diff --git a/ProcessLib/CMakeLists.txt b/ProcessLib/CMakeLists.txt index 97f952ea02e1f591b11cc20e321cd446d19461d1..100d9684a49782916e4cb5133389d14b6fed0e59 100644 --- a/ProcessLib/CMakeLists.txt +++ b/ProcessLib/CMakeLists.txt @@ -13,6 +13,7 @@ append_source_files(SOURCES BoundaryConditionAndSourceTerm/Utils) append_source_files(SOURCES SurfaceFlux) append_source_files(SOURCES Output) append_source_files(SOURCES Utils) +append_source_files(SOURCES Reflection) ogs_add_library(ProcessLib GENERATE_EXPORT_HEADER ${SOURCES}) @@ -29,6 +30,7 @@ target_link_libraries( MeshGeoToolsLib MeshLib NumLib + Boost::mp11 range-v3 $<$<TARGET_EXISTS:ProcessLibBoundaryConditionAndSourceTermPython>:ProcessLibBoundaryConditionAndSourceTermPython> $<$<TARGET_EXISTS:petsc>:petsc> diff --git a/ProcessLib/Output/SecondaryVariable.h b/ProcessLib/Output/SecondaryVariable.h index 3448165720fa4c914210f2b0529643a2894e9772..25b21e199381aa69849628bbb7100b8fa8783c0f 100644 --- a/ProcessLib/Output/SecondaryVariable.h +++ b/ProcessLib/Output/SecondaryVariable.h @@ -13,6 +13,7 @@ #include "BaseLib/Algorithm.h" #include "NumLib/Extrapolation/ExtrapolatableElementCollection.h" #include "NumLib/Extrapolation/Extrapolator.h" +#include "ProcessLib/Utils/TransposeInPlace.h" namespace NumLib { @@ -174,8 +175,9 @@ SecondaryVariableFunctions makeExtrapolator( const double t, std::vector<GlobalVector*> const& x, std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_table, - std::unique_ptr<GlobalVector> & /*result_cache*/ - ) -> GlobalVector const& { + std::unique_ptr<GlobalVector>& /*result_cache*/ + ) -> GlobalVector const& + { auto const extrapolatables = NumLib::makeExtrapolatable( local_assemblers, integration_point_values_method); extrapolator.extrapolate(num_components, extrapolatables, t, x, @@ -189,8 +191,9 @@ SecondaryVariableFunctions makeExtrapolator( const double t, std::vector<GlobalVector*> const& x, std::vector<NumLib::LocalToGlobalIndexMap const*> const& dof_table, - std::unique_ptr<GlobalVector> & /*result_cache*/ - ) -> GlobalVector const& { + std::unique_ptr<GlobalVector>& /*result_cache*/ + ) -> GlobalVector const& + { auto const extrapolatables = NumLib::makeExtrapolatable( local_assemblers, integration_point_values_method); extrapolator.calculateResiduals(num_components, extrapolatables, t, x, @@ -200,4 +203,57 @@ SecondaryVariableFunctions makeExtrapolator( return {num_components, eval_field, eval_residuals}; } +//! A variant that takes an \c accessor function as a callback. +//! +//! The \c accessor must take a local assembler by const reference as its only +//! argument and must return a <tt>std::vector\<double\></tt> of integration +//! point data. The data returned by the accessor must be in "integration point +//! writer order", which is transposed compared to "extrapolator order". +template <typename LocalAssemblerCollection, typename IPDataAccessor> +SecondaryVariableFunctions makeExtrapolator2( + const unsigned num_components, + NumLib::Extrapolator& extrapolator, + LocalAssemblerCollection const& local_assemblers, + IPDataAccessor&& accessor) +{ + using LocalAssemblerInterface = std::remove_cvref_t< + decltype(*std::declval<LocalAssemblerCollection>()[0])>; + static_assert(std::is_invocable_r_v<std::vector<double>, IPDataAccessor, + LocalAssemblerInterface const&>); + + if (num_components == 1) + { + auto method_wrapped = + [accessor]( + LocalAssemblerInterface const& loc_asm, const double /*t*/, + std::vector<GlobalVector*> const& /*x*/, + std::vector< + NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, + std::vector<double>& cache) -> std::vector<double> const& + { + cache = accessor(loc_asm); + return cache; + }; + + return makeExtrapolator(num_components, extrapolator, local_assemblers, + method_wrapped); + } + + auto method_wrapped = + [accessor, num_components]( + LocalAssemblerInterface const& loc_asm, const double /*t*/, + std::vector<GlobalVector*> const& /*x*/, + std::vector< + NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, + std::vector<double>& cache) -> std::vector<double> const& + { + cache = accessor(loc_asm); + transposeInPlace(cache, cache.size() / num_components); + return cache; + }; + + return makeExtrapolator(num_components, extrapolator, local_assemblers, + method_wrapped); +} + } // namespace ProcessLib diff --git a/ProcessLib/Reflection/ReflectionData.h b/ProcessLib/Reflection/ReflectionData.h new file mode 100644 index 0000000000000000000000000000000000000000..eecdc2b39d533e62cf22a557dc64d13115b2c2e8 --- /dev/null +++ b/ProcessLib/Reflection/ReflectionData.h @@ -0,0 +1,44 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, 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 <string> +#include <tuple> + +namespace ProcessLib::Reflection +{ +template <typename Class, typename Member> +struct ReflectionData +{ + ReflectionData(std::string name, Member Class::*field) + : name(std::move(name)), field(field) + { + } + + explicit ReflectionData(Member Class::*field) : field(field) {} + + std::string name; + Member Class::*field; +}; + +template <typename Class, typename Member> +std::tuple<ReflectionData<Class, Member>> reflectWithName( + std::string const& name, Member Class::*field) +{ + return {{name, field}}; +} + +template <typename Class, typename... Members> +auto reflectWithoutName(Members Class::*... members) +{ + return std::tuple{ReflectionData<Class, Members>{"", members}...}; +} +} // namespace ProcessLib::Reflection diff --git a/ProcessLib/Reflection/ReflectionForExtrapolation.h b/ProcessLib/Reflection/ReflectionForExtrapolation.h new file mode 100644 index 0000000000000000000000000000000000000000..a8dac205cd1db2329791c999a86b78d2eb062ad3 --- /dev/null +++ b/ProcessLib/Reflection/ReflectionForExtrapolation.h @@ -0,0 +1,40 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, 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 "ProcessLib/Output/SecondaryVariable.h" +#include "ReflectionIPData.h" + +namespace ProcessLib::Reflection +{ +/// Adds secondary variables for all IP data obtained recursively from the given +/// \c reflection_data to the given secondary variable collection. +template <int Dim, typename LocAsmIF, typename ReflData> +void addReflectedSecondaryVariables( + ReflData const& reflection_data, + SecondaryVariableCollection& secondary_variables, + NumLib::Extrapolator& extrapolator, + std::vector<std::unique_ptr<LocAsmIF>> const& local_assemblers) +{ + forEachReflectedFlattenedIPDataAccessor<Dim, LocAsmIF>( + reflection_data, + [&secondary_variables, &local_assemblers, &extrapolator]( + std::string const& name, + unsigned const num_comp, + auto&& flattened_ip_data_accessor) + { + secondary_variables.addSecondaryVariable( + name, + makeExtrapolator2(num_comp, extrapolator, local_assemblers, + flattened_ip_data_accessor)); + }); +} +} // namespace ProcessLib::Reflection diff --git a/ProcessLib/Reflection/ReflectionForIPWriters.h b/ProcessLib/Reflection/ReflectionForIPWriters.h new file mode 100644 index 0000000000000000000000000000000000000000..dc7a202b2a09ec849630040cc09bd39d1ed24bee --- /dev/null +++ b/ProcessLib/Reflection/ReflectionForIPWriters.h @@ -0,0 +1,42 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, 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 "MeshLib/Utils/IntegrationPointWriter.h" +#include "ReflectionIPData.h" + +namespace ProcessLib::Reflection +{ +/// Adds IP data writers for all IP data obtained recursively from the given +/// \c reflection_data to the given IP writer vector. +template <int Dim, typename LocAsmIF, typename ReflData> +void addReflectedIntegrationPointWriters( + ReflData const& reflection_data, + std::vector<std::unique_ptr<MeshLib::IntegrationPointWriter>>& + integration_point_writers, + unsigned const integration_order, + std::vector<std::unique_ptr<LocAsmIF>> const& local_assemblers) +{ + forEachReflectedFlattenedIPDataAccessor<Dim, LocAsmIF>( + reflection_data, + [&integration_point_writers, integration_order, &local_assemblers]( + std::string const& name, + unsigned const num_comp, + auto&& flattened_ip_data_accessor) + { + // TODO check if writer with such a name already exists. + integration_point_writers.emplace_back( + std::make_unique<MeshLib::IntegrationPointWriter>( + name + "_ip", num_comp, integration_order, local_assemblers, + flattened_ip_data_accessor)); + }); +} +} // namespace ProcessLib::Reflection diff --git a/ProcessLib/Reflection/ReflectionIPData.h b/ProcessLib/Reflection/ReflectionIPData.h new file mode 100644 index 0000000000000000000000000000000000000000..a71ce46fd9ed542691b22d1b29cfd79d59d9a6bb --- /dev/null +++ b/ProcessLib/Reflection/ReflectionIPData.h @@ -0,0 +1,310 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, 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 <boost/mp11.hpp> + +#include "MathLib/KelvinVector.h" +#include "ReflectionData.h" + +namespace ProcessLib::Reflection +{ +namespace detail +{ +// Used in metaprogramming to check if the type T has a member "reflect". +template <typename T> +constexpr bool has_reflect = requires +{ + T::reflect(); +}; + +template <typename T> +struct NumberOfComponents; + +template <> +struct NumberOfComponents<double> : std::integral_constant<unsigned, 1> +{ +}; + +template <int N> +struct NumberOfComponents<Eigen::Matrix<double, N, 1, Eigen::ColMajor, N, 1>> + : std::integral_constant<unsigned, N> +{ +}; + +/** A function object taking a local assembler as its argument and returning a + * <tt>std::vector\<double\></tt> of some specific "flattened" integration point + * (IP) data. + * + * \tparam Dim the space dimension + * \tparam Accessor_IPDataVecInLocAsm see below + * \tparam Accessor_CurrentLevelFromIPDataVecElement see below + * + * In OGS IP data is usually stored in the local assembler in the following way: + * + * \code + * struct LocAsm + * { + * std::vector<IPData1> ip_data1; + * std::vector<IPData2> ip_data2; + * }; + * \endcode + * + * The types \c IPData1 and \c IPData2 might directly contain the IP data or + * might have \c struct members who contain the IP data, e.g.: + * + * \code + * struct IPData1 + * { + * double scalar1; // IPData1 directly holds IP data + * Eigen::Vector<double, 3> vector1; + * }; + * + * struct IPData_Level2 + * { + * double scalar2; + * Eigen::Vector<double, 6> kelvin2; + * }; + * + * struct IPData2 + * { + * IPData_Level2 level2; // IPData2 does not directly hold IP data + * }; + * \endcode + * + * \c Accessor_IPDataVecInLocAsm is a function object with signature + * <tt>LocAsm const\& -\> std::vector\<IPData\> const\&</tt>. + * + * \c Accessor_CurrentLevelFromIPDataVecElement is a function object with + * signature <tt>IPData const\& -\> double (or Eigen::Vector)</tt>, + * where \c IPData is the "top level" struct contained in the + * <tt>std::vector\<IPData\></tt>. + * + * I.e. the first accessor takes us from the local assembler to the IP data + * vector and the second accessor takes us from an IP data vector element to the + * final IP data of type \c double or <tt>Eigen::Vector</tt>. + * + * \note This function object transforms Kelvin vector typed IP data to a + * ParaView compatible symmetric tensor representation. + */ +template <int Dim, typename Accessor_IPDataVecInLocAsm, + typename Accessor_CurrentLevelFromIPDataVecElement> +struct GetFlattenedIPDataFromLocAsm +{ + static_assert(!std::is_reference_v<Accessor_IPDataVecInLocAsm>); + static_assert( + !std::is_reference_v<Accessor_CurrentLevelFromIPDataVecElement>); + + Accessor_IPDataVecInLocAsm accessor_ip_data_vec_in_loc_asm; + Accessor_CurrentLevelFromIPDataVecElement + accessor_current_level_from_ip_data_vec_element; + + template <typename LocAsm> + std::vector<double> operator()(LocAsm const& loc_asm) const + { + using IPDataVector = std::remove_cvref_t< + std::invoke_result_t<Accessor_IPDataVecInLocAsm, LocAsm const&>>; + using IPDataVectorElement = typename IPDataVector::value_type; + + // the concrete IP data, e.g. double or Eigen::Vector + using ConcreteIPData = std::remove_cvref_t< + std::invoke_result_t<Accessor_CurrentLevelFromIPDataVecElement, + IPDataVectorElement const&>>; + + constexpr unsigned num_comp = NumberOfComponents<ConcreteIPData>::value; + auto const& ip_data_vector = accessor_ip_data_vec_in_loc_asm(loc_asm); + auto const num_ips = ip_data_vector.size(); + + std::vector<double> result(num_comp * num_ips); + + for (std::size_t ip = 0; ip < num_ips; ++ip) + { + auto const& ip_data_vector_element = ip_data_vector[ip]; + auto const& ip_data = + accessor_current_level_from_ip_data_vec_element( + ip_data_vector_element); + + if constexpr (num_comp == 1) + { + // TODO optimization if nothing needs to be copied? + result[ip] = ip_data; + } + else if constexpr (num_comp == + MathLib::KelvinVector::kelvin_vector_dimensions( + Dim)) + { + auto const converted = + MathLib::KelvinVector::kelvinVectorToSymmetricTensor( + ip_data); + + for (unsigned comp = 0; comp < num_comp; ++comp) + { + result[ip * num_comp + comp] = converted[comp]; + } + } + else + { + for (unsigned comp = 0; comp < num_comp; ++comp) + { + result[ip * num_comp + comp] = ip_data[comp]; + } + } + } + return result; + } +}; + +// Convenience function for template argument deduction with +// GetFlattenedIPDataFromLocAsm +template <int Dim, typename Accessor_IPDataVecInLocAsm, + typename Accessor_CurrentLevelFromIPDataVecElement> +GetFlattenedIPDataFromLocAsm< + Dim, std::remove_cvref_t<Accessor_IPDataVecInLocAsm>, + std::remove_cvref_t<Accessor_CurrentLevelFromIPDataVecElement>> +getFlattenedIPDataFromLocAsm( + Accessor_IPDataVecInLocAsm accessor_ip_data_vec_in_loc_asm, + Accessor_CurrentLevelFromIPDataVecElement + accessor_current_level_from_ip_data_vec_element) +{ + return {std::forward<Accessor_IPDataVecInLocAsm>( + accessor_ip_data_vec_in_loc_asm), + std::forward<Accessor_CurrentLevelFromIPDataVecElement>( + accessor_current_level_from_ip_data_vec_element)}; +} + +// Convenience function for template argument deduction with +// GetFlattenedIPDataFromLocAsm. Overload of the function above with less +// arguments. +template <int Dim, typename Accessor_IPDataVecInLocAsm> +auto getFlattenedIPDataFromLocAsm( + Accessor_IPDataVecInLocAsm&& accessor_ip_data_vec_in_loc_asm) +{ + return getFlattenedIPDataFromLocAsm<Dim>( + std::forward<Accessor_IPDataVecInLocAsm>( + accessor_ip_data_vec_in_loc_asm), + std::identity{}); +} + +/// Calls the given \c callback for each flattened IP data accessor obtained +/// recursively from the given \c reflection data. +/// +/// The \c callback function must take (i) the name of the IP data (\c +/// std::string), (ii) their number of components (\c unsigned) and a flattened +/// IP data accessor of type +/// ProcessLib::Reflection::detail::GetFlattenedIPDataFromLocAsm as arguments. +/// +/// \see ProcessLib::Reflection::detail::GetFlattenedIPDataFromLocAsm for +/// details, also on the template parameters. +template <int Dim, typename Callback, typename ReflectionDataTuple, + typename Accessor_IPDataVecInLocAsm, + typename Accessor_CurrentLevelFromIPDataVecElement> +void forEachReflectedFlattenedIPDataAccessor( + Callback const& callback, ReflectionDataTuple const& reflection_data, + Accessor_IPDataVecInLocAsm const& accessor_ip_data_vec_in_loc_asm, + Accessor_CurrentLevelFromIPDataVecElement const& + accessor_current_level_from_ip_data_vec_element) +{ + boost::mp11::tuple_for_each( + reflection_data, + [&accessor_ip_data_vec_in_loc_asm, + &accessor_current_level_from_ip_data_vec_element, + &callback]<typename Class, typename Member>( + ReflectionData<Class, Member> const& refl_data) + { + auto accessor_member_from_ip_data_vec_element = + [level = refl_data.field, + accessor_current_level_from_ip_data_vec_element]( + auto const& ip_data_vec_element) -> Member const& + { + return accessor_current_level_from_ip_data_vec_element( + ip_data_vec_element).* + level; + }; + + if constexpr (has_reflect<Member>) + { + forEachReflectedFlattenedIPDataAccessor<Dim>( + callback, Member::reflect(), + accessor_ip_data_vec_in_loc_asm, + accessor_member_from_ip_data_vec_element); + } + else + { + constexpr unsigned num_comp = NumberOfComponents<Member>::value; + + callback(refl_data.name, num_comp, + getFlattenedIPDataFromLocAsm<Dim>( + accessor_ip_data_vec_in_loc_asm, + accessor_member_from_ip_data_vec_element)); + } + }); +} + +// Overload of the function above with less arguments +template <int Dim, typename Callback, typename ReflectionDataTuple, + typename Accessor_IPDataVecInLocAsm> +void forEachReflectedFlattenedIPDataAccessor( + Callback const& callback, + ReflectionDataTuple const& reflection_data, + Accessor_IPDataVecInLocAsm const& accessor_ip_data_vec_in_loc_asm) +{ + forEachReflectedFlattenedIPDataAccessor<Dim>( + callback, reflection_data, accessor_ip_data_vec_in_loc_asm, + std::identity{}); +} + +} // namespace detail + +/// Calls the passed \c callback for each IP data accessor for the given +/// \c LocAsmIF class. +/// +/// The IP data accessors are obtained via reflection (i.e., via the static +/// \c reflect() method. +/// +/// The IP data accessors provide IP data as a flat \c std::vector<double>. +/// +/// The \c callback must accept name, number of components and a function object +/// with signature <tt>LocAsmIF const\& -\> std::vector\<double\></tt> as its +/// arguments. +template <int Dim, typename LocAsmIF, typename Callback, typename ReflData> +void forEachReflectedFlattenedIPDataAccessor(ReflData const& reflection_data, + Callback const& callback) +{ + boost::mp11::tuple_for_each( + reflection_data, + [&callback]<typename Class, typename Member>( + ReflectionData<Class, std::vector<Member>> const& refl_data) + { + auto accessor_ip_data_vec_in_loc_asm = [ip_data_vector = + refl_data.field]( + LocAsmIF const& loc_asm) -> auto const& + { + return loc_asm.*ip_data_vector; + }; + + if constexpr (detail::has_reflect<Member>) + { + detail::forEachReflectedFlattenedIPDataAccessor<Dim>( + callback, Member::reflect(), + accessor_ip_data_vec_in_loc_asm); + } + else + { + constexpr unsigned num_comp = + detail::NumberOfComponents<Member>::value; + + callback(refl_data.name, num_comp, + detail::getFlattenedIPDataFromLocAsm<Dim>( + accessor_ip_data_vec_in_loc_asm)); + } + }); +} +} // namespace ProcessLib::Reflection diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Base.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/Base.h index 60d229558fe9fca170c7be472a9d23b88bcc394a..10a8ffc179aca6860aab1e6ffa47e28be079ffda 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Base.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Base.h @@ -13,6 +13,7 @@ #include "MaterialLib/MPL/Medium.h" #include "MathLib/KelvinVector.h" #include "ParameterLib/SpatialPosition.h" +#include "ProcessLib/Reflection/ReflectionData.h" namespace ProcessLib::ThermoRichardsMechanics { @@ -116,6 +117,13 @@ struct StrainData { // TODO Move initialization to the local assembler. KelvinVector<DisplacementDim> eps = KVzero<DisplacementDim>(); + + static auto reflect() + { + using Self = StrainData<DisplacementDim>; + + return ProcessLib::Reflection::reflectWithName("epsilon", &Self::eps); + } }; } // namespace ProcessLib::ThermoRichardsMechanics diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/DarcyLaw.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/DarcyLaw.h index 74508febd7f33b641add3116b440d6ee21589d7f..1fc24f14182eafb0ef1034f51f1d7d1a955b062f 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/DarcyLaw.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/DarcyLaw.h @@ -11,7 +11,6 @@ #pragma once #include "LiquidDensity.h" -#include "LiquidViscosity.h" #include "Permeability.h" #include "ThermoOsmosis.h" @@ -21,6 +20,14 @@ template <int DisplacementDim> struct DarcyLawData { Eigen::Vector<double, DisplacementDim> v_darcy; + + static auto reflect() + { + using Self = DarcyLawData<DisplacementDim>; + + return ProcessLib::Reflection::reflectWithName("velocity", + &Self::v_darcy); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidDensity.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidDensity.h index c4de0275b4bdee8d0f073aa57cf2d411074f93e2..9ca326252dba2d2ac459802ca3346367890e419e 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidDensity.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidDensity.h @@ -19,6 +19,12 @@ struct LiquidDensityData double rho_LR; double drho_LR_dp; double drho_LR_dT; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName( + "liquid_density", &LiquidDensityData::rho_LR); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.cpp b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.cpp index 7c6e0e7ff5ac09b1da5d51a19474657a7a9cf52a..d8997365742f74a168f7c42bc7fb5bcf3d643679 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.cpp +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.cpp @@ -10,8 +10,6 @@ #include "LiquidViscosity.h" -#include "MaterialLib/MPL/Utils/FormEigenTensor.h" - namespace ProcessLib::ThermoRichardsMechanics { template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.h index 2f60ad5aaf7a1a6241dc0762dc5388560942c494..6ccdcd370ac95548118a175fa6e2ea0737153291 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/LiquidViscosity.h @@ -10,13 +10,19 @@ #pragma once -#include "Porosity.h" +#include "ProcessLib/ThermoRichardsMechanics/Constitutive/Base.h" namespace ProcessLib::ThermoRichardsMechanics { struct LiquidViscosityData { double viscosity; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName( + "viscosity", &LiquidViscosityData::viscosity); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.cpp b/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.cpp index cb7494151c2f9fd83c4dafc70cf529415ab68ab5..03f4229158df7866cfa3cdb7c6f56efa9bdf7f99 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.cpp +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.cpp @@ -23,8 +23,8 @@ void PermeabilityModel<DisplacementDim>::eval( CapillaryPressureData<DisplacementDim> const& p_cap_data, TemperatureData<DisplacementDim> const& T_data, PorosityData const& poro_data, LiquidViscosityData const& mu_L_data, - PorosityData& transport_poro_data, - PorosityData const& transport_poro_data_prev, + TransportPorosityData& transport_poro_data, + TransportPorosityData const& transport_poro_data_prev, SolidMechanicsDataStateless<DisplacementDim> const& s_mech_data, StrainData<DisplacementDim> const& eps_data, StrainData<DisplacementDim> const& eps_prev_data, diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.h index e3a252b373d9d19a731bb92623ed1928454253fd..ae3eb3c5b52e33337595fdd38498e81b5781b94b 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Permeability.h @@ -36,8 +36,8 @@ struct PermeabilityModel PorosityData const& poro_data, LiquidViscosityData const& mu_L_data, // TODO evaluate transport porosity evolution separately - PorosityData& transport_poro_data, - PorosityData const& transport_poro_data_prev, + TransportPorosityData& transport_poro_data, + TransportPorosityData const& transport_poro_data_prev, SolidMechanicsDataStateless<DisplacementDim> const& s_mech_data, StrainData<DisplacementDim> const& eps_data, StrainData<DisplacementDim> const& eps_prev_data, diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Porosity.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/Porosity.h index 42dfa5c813fab638336cb292d44b539295082605..7bafb13b290f185e1137eb8b106046d33bee9b55 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Porosity.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Porosity.h @@ -11,7 +11,6 @@ #pragma once #include "Bishops.h" -#include "MathLib/KelvinVector.h" #include "Saturation.h" #include "SolidCompressibility.h" @@ -20,6 +19,23 @@ namespace ProcessLib::ThermoRichardsMechanics struct PorosityData { double phi; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName("porosity", + &PorosityData::phi); + } +}; + +struct TransportPorosityData +{ + double phi; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName( + "transport_porosity", &TransportPorosityData::phi); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Saturation.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/Saturation.h index eb1c1229a9b72649cae70226555d2b525f31e42b..589c87ecb4cc417c28b8669d8a6b688ea9c7fb45 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Saturation.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Saturation.h @@ -17,6 +17,12 @@ namespace ProcessLib::ThermoRichardsMechanics struct SaturationData { double S_L; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName("saturation", + &SaturationData::S_L); + } }; struct SaturationDataDeriv diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidDensity.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidDensity.h index 4ea8bedb59f72f7014af5c5ba04c2f934ccdd181..fb13c344fa11b36143bcba33171881c159705d4f 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidDensity.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidDensity.h @@ -19,6 +19,12 @@ struct SolidDensityData { double rho_SR; double dry_density_solid; + + static auto reflect() + { + return ProcessLib::Reflection::reflectWithName( + "dry_density_solid", &SolidDensityData::dry_density_solid); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidMechanics.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidMechanics.h index 3def597ab16aba9fba969076280c2ddf463eb608..ec591359a8b72c10606a7dd359f4f8ad090a5d20 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidMechanics.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/SolidMechanics.h @@ -25,6 +25,15 @@ struct SolidMechanicsDataStateful // TODO it seems fragile that some data have to be initialized that way. KelvinVector<DisplacementDim> sigma_eff = KVzero<DisplacementDim>(); KelvinVector<DisplacementDim> eps_m = KVzero<DisplacementDim>(); + + static auto reflect() + { + using Self = SolidMechanicsDataStateful<DisplacementDim>; + + // TODO add eps_m? + return ProcessLib::Reflection::reflectWithName("sigma", + &Self::sigma_eff); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/Constitutive/Swelling.h b/ProcessLib/ThermoRichardsMechanics/Constitutive/Swelling.h index b3a14ad19160fccac81194e450d06f75c98b2a20..54091d39898e7329a46e1322bbf4c384924035b3 100644 --- a/ProcessLib/ThermoRichardsMechanics/Constitutive/Swelling.h +++ b/ProcessLib/ThermoRichardsMechanics/Constitutive/Swelling.h @@ -20,6 +20,14 @@ template <int DisplacementDim> struct SwellingDataStateful { KelvinVector<DisplacementDim> sigma_sw = KVzero<DisplacementDim>(); + + static auto reflect() + { + using Self = SwellingDataStateful<DisplacementDim>; + + return ProcessLib::Reflection::reflectWithName("swelling_stress", + &Self::sigma_sw); + } }; template <int DisplacementDim> diff --git a/ProcessLib/ThermoRichardsMechanics/ConstitutiveSetting.h b/ProcessLib/ThermoRichardsMechanics/ConstitutiveSetting.h index 77307c26c2863431794cd491d85799d5e80d463e..2df2974fdbaea1d4fa505a0833f13f7c2a1ef3c0 100644 --- a/ProcessLib/ThermoRichardsMechanics/ConstitutiveSetting.h +++ b/ProcessLib/ThermoRichardsMechanics/ConstitutiveSetting.h @@ -30,16 +30,30 @@ namespace ProcessLib::ThermoRichardsMechanics { + /// Data whose state must be tracked by the TRM process. template <int DisplacementDim> struct StatefulData { SaturationData S_L_data; PorosityData poro_data; - PorosityData transport_poro_data; + TransportPorosityData transport_poro_data; StrainData<DisplacementDim> eps_data; SwellingDataStateful<DisplacementDim> swelling_data; SolidMechanicsDataStateful<DisplacementDim> s_mech_data; + + static auto reflect() + { + using Self = StatefulData<DisplacementDim>; + + return ProcessLib::Reflection::reflectWithoutName( + &Self::S_L_data, + &Self::poro_data, + &Self::transport_poro_data, + &Self::eps_data, + &Self::swelling_data, + &Self::s_mech_data); + } }; /// Data that is needed for output purposes, but not directly for the assembly. @@ -50,6 +64,16 @@ struct OutputData LiquidDensityData rho_L_data; LiquidViscosityData mu_L_data; SolidDensityData rho_S_data; + + static auto reflect() + { + using Self = OutputData<DisplacementDim>; + + return ProcessLib::Reflection::reflectWithoutName(&Self::darcy_data, + &Self::rho_L_data, + &Self::mu_L_data, + &Self::rho_S_data); + } }; /// Data that is needed for the equation system assembly. diff --git a/ProcessLib/ThermoRichardsMechanics/LocalAssemblerInterface.h b/ProcessLib/ThermoRichardsMechanics/LocalAssemblerInterface.h index 7e87c3ab59923d33fc66e3f187a273c6e42a8cd3..965c03a15ea422fb0c30a040228e4ec8607c78da 100644 --- a/ProcessLib/ThermoRichardsMechanics/LocalAssemblerInterface.h +++ b/ProcessLib/ThermoRichardsMechanics/LocalAssemblerInterface.h @@ -18,7 +18,6 @@ #include "ProcessLib/ThermoRichardsMechanics/ConstitutiveSetting.h" #include "ProcessLib/ThermoRichardsMechanics/ThermoRichardsMechanicsProcessData.h" #include "ProcessLib/Utils/SetOrGetIntegrationPointData.h" -#include "ProcessLib/Utils/TransposeInPlace.h" namespace ProcessLib::ThermoRichardsMechanics { @@ -119,212 +118,6 @@ struct LocalAssemblerInterface : public ProcessLib::LocalAssemblerInterface, return 0; } -private: - std::vector<double> getSigma() const - { - constexpr int kelvin_vector_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - - return transposeInPlace<kelvin_vector_size>( - [this](std::vector<double>& values) - { return getIntPtSigma(0, {}, {}, values); }); - } - - std::vector<double> const& getIntPtSigma( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointKelvinVectorData<DisplacementDim>( - current_states_, [](auto const& cs) -> auto const& { - return cs.s_mech_data.sigma_eff; - }, - cache); - } - - std::vector<double> getSwellingStress() const - { - constexpr int kelvin_vector_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - - return transposeInPlace<kelvin_vector_size>( - [this](std::vector<double>& values) - { return getIntPtSwellingStress(0, {}, {}, values); }); - } - - std::vector<double> const& getIntPtSwellingStress( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - constexpr int kelvin_vector_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - auto const n_integration_points = current_states_.size(); - - cache.clear(); - auto cache_mat = MathLib::createZeroedMatrix<Eigen::Matrix< - double, kelvin_vector_size, Eigen::Dynamic, Eigen::RowMajor>>( - cache, kelvin_vector_size, n_integration_points); - - for (unsigned ip = 0; ip < n_integration_points; ++ip) - { - auto const& sigma_sw = current_states_[ip].swelling_data.sigma_sw; - cache_mat.col(ip) = - MathLib::KelvinVector::kelvinVectorToSymmetricTensor(sigma_sw); - } - - return cache; - } - - std::vector<double> getEpsilon() const - { - constexpr int kelvin_vector_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - - return transposeInPlace<kelvin_vector_size>( - [this](std::vector<double>& values) - { return getIntPtEpsilon(0, {}, {}, values); }); - } - - std::vector<double> const& getIntPtEpsilon( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointKelvinVectorData<DisplacementDim>( - current_states_, - [](auto const& cs) -> auto const& { return cs.eps_data.eps; }, - cache); - } - - std::vector<double> const& getIntPtDarcyVelocity( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - unsigned const n_integration_points = - integration_method_.getNumberOfPoints(); - - cache.clear(); - auto cache_matrix = MathLib::createZeroedMatrix<Eigen::Matrix< - double, DisplacementDim, Eigen::Dynamic, Eigen::RowMajor>>( - cache, DisplacementDim, n_integration_points); - - for (unsigned ip = 0; ip < n_integration_points; ip++) - { - cache_matrix.col(ip).noalias() = - output_data_[ip].darcy_data.v_darcy; - } - - return cache; - } - - std::vector<double> getSaturation() const - { - std::vector<double> result; - getIntPtSaturation(0, {}, {}, result); - return result; - } - - std::vector<double> const& getIntPtSaturation( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - current_states_, - [](auto const& state) -> auto const& { return state.S_L_data.S_L; }, - cache); - } - - std::vector<double> getPorosity() const - { - std::vector<double> result; - getIntPtPorosity(0, {}, {}, result); - return result; - } - - std::vector<double> const& getIntPtPorosity( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - current_states_, [](auto const& state) -> auto const& { - return state.poro_data.phi; - }, - cache); - } - - std::vector<double> getTransportPorosity() const - { - std::vector<double> result; - getIntPtTransportPorosity(0, {}, {}, result); - return result; - } - - std::vector<double> const& getIntPtTransportPorosity( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - current_states_, - [](auto const& state) -> auto const& { - return state.transport_poro_data.phi; - }, - cache); - } - - std::vector<double> const& getIntPtDryDensitySolid( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - output_data_, - [](auto const& out) -> auto const& { - return out.rho_S_data.dry_density_solid; - }, - cache); - } - - std::vector<double> const& getIntPtLiquidDensity( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - output_data_, - [](auto const& out) -> auto const& { - return out.rho_L_data.rho_LR; - }, - cache); - } - - std::vector<double> const& getIntPtViscosity( - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector<NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& cache) const - { - return ProcessLib::getIntegrationPointScalarData( - output_data_, [](auto const& out) -> auto const& { - return out.mu_L_data.viscosity; - }, - cache); - } - -public: // TODO move to NumLib::ExtrapolatableElement unsigned getNumberOfIntegrationPoints() const { @@ -354,65 +147,12 @@ public: prev_states_ = current_states_; } - struct IPDataAccessorForExtrapolation - { - std::string name; - unsigned num_comp; - std::function<std::vector<double> const&( - LocalAssemblerInterface<DisplacementDim> const&, - const double /*t*/, - std::vector<GlobalVector*> const& /*x*/, - std::vector< - NumLib::LocalToGlobalIndexMap const*> const& /*dof_table*/, - std::vector<double>& /*cache*/)> - ip_data_accessor; - }; - - static std::vector<IPDataAccessorForExtrapolation> - getIPDataAccessorsForExtrapolation() - { - using Self = LocalAssemblerInterface<DisplacementDim>; - constexpr auto kv_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - - return {{"sigma", kv_size, &Self::getIntPtSigma}, - {"swelling_stress", kv_size, &Self::getIntPtSwellingStress}, - {"epsilon", kv_size, &Self::getIntPtEpsilon}, - {"velocity", DisplacementDim, &Self::getIntPtDarcyVelocity}, - {"saturation", 1, &Self::getIntPtSaturation}, - {"porosity", 1, &Self::getIntPtPorosity}, - {"transport_porosity", 1, &Self::getIntPtTransportPorosity}, - {"dry_density_solid", 1, &Self::getIntPtDryDensitySolid}, - {"liquid_density", 1, &Self::getIntPtLiquidDensity}, - {"viscosity", 1, &Self::getIntPtViscosity}}; - } - - struct IPDataAccessorForIPWriter - { - std::string name; - unsigned num_comp; - std::vector<double> (LocalAssemblerInterface<DisplacementDim>::* - ip_data_accessor)() const; - }; - - static std::vector<IPDataAccessorForIPWriter> - getIPDataAccessorsForIPWriter() + static auto getReflectionDataForOutput() { using Self = LocalAssemblerInterface<DisplacementDim>; - constexpr auto kv_size = - MathLib::KelvinVector::kelvin_vector_dimensions(DisplacementDim); - // TODO (naumov) remove ip suffix. Probably needs modification of the - // mesh properties, s.t. there is no "overlapping" with cell/point data. - // See getOrCreateMeshProperty. - return { - {"sigma_ip", kv_size, &Self::getSigma}, - {"saturation_ip", 1, &Self::getSaturation}, - {"porosity_ip", 1, &Self::getPorosity}, - {"transport_porosity_ip", 1, &Self::getTransportPorosity}, - {"swelling_stress_ip", kv_size, &Self::getSwellingStress}, - {"epsilon_ip", kv_size, &Self::getEpsilon}, - }; + return ProcessLib::Reflection::reflectWithoutName( + &Self::current_states_, &Self::output_data_); } protected: diff --git a/ProcessLib/ThermoRichardsMechanics/ThermoRichardsMechanicsProcess.cpp b/ProcessLib/ThermoRichardsMechanics/ThermoRichardsMechanicsProcess.cpp index fc1d1b0dbd1c8567249042589f7f8cef084cdaca..439a664a56c5cc73a74e6cb3fdc9cab4ef9bc986 100644 --- a/ProcessLib/ThermoRichardsMechanics/ThermoRichardsMechanicsProcess.cpp +++ b/ProcessLib/ThermoRichardsMechanics/ThermoRichardsMechanicsProcess.cpp @@ -17,6 +17,8 @@ #include "NumLib/DOF/DOFTableUtil.h" #include "ProcessLib/Deformation/SolidMaterialInternalToSecondaryVariables.h" #include "ProcessLib/Process.h" +#include "ProcessLib/Reflection/ReflectionForExtrapolation.h" +#include "ProcessLib/Reflection/ReflectionForIPWriters.h" #include "ProcessLib/Utils/CreateLocalAssemblersTaylorHood.h" #include "ProcessLib/Utils/SetIPDataInitialConditions.h" #include "ThermoRichardsMechanicsFEM.h" @@ -51,14 +53,10 @@ ThermoRichardsMechanicsProcess<DisplacementDim>::ThermoRichardsMechanicsProcess( heat_flux_ = MeshLib::getOrCreateMeshProperty<double>( mesh, "HeatFlowRate", MeshLib::MeshItemType::Node, 1); - for (auto& [name, num_comp, ip_data_accessor] : - LocalAssemblerIF::getIPDataAccessorsForIPWriter()) - { - _integration_point_writer.emplace_back( - std::make_unique<MeshLib::IntegrationPointWriter>( - name, num_comp, integration_order, local_assemblers_, - ip_data_accessor)); - } + ProcessLib::Reflection::addReflectedIntegrationPointWriters< + DisplacementDim>(LocalAssemblerIF::getReflectionDataForOutput(), + _integration_point_writer, integration_order, + local_assemblers_); } template <int DisplacementDim> @@ -143,11 +141,9 @@ void ThermoRichardsMechanicsProcess<DisplacementDim>::initializeConcreteProcess( std::move(get_ip_values_function))); }; - for (auto& [name, num_comp, fct] : LocalAssemblerInterface< - DisplacementDim>::getIPDataAccessorsForExtrapolation()) - { - add_secondary_variable(name, num_comp, fct); - }; + ProcessLib::Reflection::addReflectedSecondaryVariables<DisplacementDim>( + LocalAssemblerIF::getReflectionDataForOutput(), _secondary_variables, + getExtrapolator(), local_assemblers_); // // enable output of internal variables defined by material models diff --git a/ProcessLib/Utils/TransposeInPlace.h b/ProcessLib/Utils/TransposeInPlace.h index 163ebf9713f2800ae344744777a130f1d04926b3..6b8b338ef5a58f1d940a9ca8885465aee3397336 100644 --- a/ProcessLib/Utils/TransposeInPlace.h +++ b/ProcessLib/Utils/TransposeInPlace.h @@ -37,4 +37,32 @@ std::vector<double> transposeInPlace( return result; } +template <int Components> +void transposeInPlace(std::vector<double>& values) +{ + MathLib::toMatrix< + Eigen::Matrix<double, Eigen::Dynamic, Components, Eigen::RowMajor>>( + values, values.size() / Components, Components) = + MathLib::toMatrix< + Eigen::Matrix<double, Components, Eigen::Dynamic, Eigen::RowMajor>>( + values, Components, values.size() / Components) + .transpose() + .eval(); +} + +inline void transposeInPlace(std::vector<double>& values, + unsigned const num_components) +{ + MathLib::toMatrix< + Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>( + values, values.size() / num_components, num_components) = + MathLib::toMatrix<Eigen::Matrix<double, + Eigen::Dynamic, + Eigen::Dynamic, + Eigen::RowMajor>>( + values, num_components, values.size() / num_components) + .transpose() + .eval(); +} + } // namespace ProcessLib diff --git a/Tests/ProcessLib/TestReflectIPData.cpp b/Tests/ProcessLib/TestReflectIPData.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3dd1f8b47c7bcc5a40e32dacf39c3891451267f8 --- /dev/null +++ b/Tests/ProcessLib/TestReflectIPData.cpp @@ -0,0 +1,487 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2022, 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 <gmock/gmock-matchers.h> +#include <gtest/gtest.h> + +#include <numeric> +#include <random> + +#include "ProcessLib/Reflection/ReflectionIPData.h" + +template <int Dim> +struct Level3 +{ + MathLib::KelvinVector::KelvinVectorType<Dim> kelvin3; + Eigen::Vector<double, Dim> vector3; + double scalar3; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"kelvin3", &Level3::kelvin3}, + ReflectionData{"vector3", &Level3::vector3}, + ReflectionData{"scalar3", &Level3::scalar3}}; + } +}; + +template <int Dim> +struct Level3b +{ + double scalar3b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"scalar3b", &Level3b::scalar3b}}; + } +}; + +template <int Dim> +struct Level2 +{ + Level3<Dim> level3; + Level3b<Dim> level3b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{&Level2::level3}, + ReflectionData{&Level2::level3b}}; + } +}; + +template <int Dim> +struct Level2b +{ + double scalar2b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"scalar2b", &Level2b::scalar2b}}; + } +}; + +template <int Dim> +struct Level1 +{ + MathLib::KelvinVector::KelvinVectorType<Dim> kelvin1; + Eigen::Vector<double, Dim> vector1; + double scalar1; + Level2<Dim> level2; + Level2b<Dim> level2b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"kelvin1", &Level1::kelvin1}, + ReflectionData{"vector1", &Level1::vector1}, + ReflectionData{"scalar1", &Level1::scalar1}, + ReflectionData{&Level1::level2}, + ReflectionData{&Level1::level2b}}; + } +}; + +template <int Dim> +struct Level1b +{ + double scalar1b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"scalar1b", &Level1b::scalar1b}}; + } +}; + +template <int Dim> +struct LocAsmIF +{ + explicit LocAsmIF(unsigned const num_ips) + : ip_data_scalar(num_ips), + ip_data_vector(num_ips), + ip_data_kelvin(num_ips), + ip_data_level1(num_ips), + ip_data_level1b(num_ips) + { + } + + std::size_t numIPs() const { return ip_data_scalar.size(); } + + std::vector<double> ip_data_scalar; + std::vector<Eigen::Vector<double, Dim>> ip_data_vector; + std::vector<MathLib::KelvinVector::KelvinVectorType<Dim>> ip_data_kelvin; + std::vector<Level1<Dim>> ip_data_level1; + std::vector<Level1b<Dim>> ip_data_level1b; + + static auto reflect() + { + using namespace ProcessLib::Reflection; + return std::tuple{ReflectionData{"scalar", &LocAsmIF::ip_data_scalar}, + ReflectionData{"vector", &LocAsmIF::ip_data_vector}, + ReflectionData{"kelvin", &LocAsmIF::ip_data_kelvin}, + ReflectionData{&LocAsmIF::ip_data_level1}, + ReflectionData{&LocAsmIF::ip_data_level1b}}; + } +}; + +template <int dim> +struct NumCompAndFunction +{ + unsigned num_comp; + std::function<std::vector<double>(LocAsmIF<dim> const&)> function; +}; + +// Prepares scalar IP data for the passed local assembler. +// +// The IP data are a sequence of double values starting at the passed start +// value and incremented by one for each integration point. +// +// The location of the prepared data is specified by the IP data accessor +// callback function. +// +// Returns the expected data for use in unit test checks. +template <int dim> +std::vector<double> initScalar(LocAsmIF<dim>& loc_asm, + double const start_value, + auto const ip_data_accessor, + bool const for_read_test) +{ + auto const num_int_pts = loc_asm.numIPs(); + + // init ip data in the local assembler + if (for_read_test) + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = start_value + ip; + } + } + else + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = + std::numeric_limits<double>::quiet_NaN(); + } + } + + // prepare reference data + std::vector<double> scalar_expected(num_int_pts); + iota(begin(scalar_expected), end(scalar_expected), start_value); + return scalar_expected; +} + +// Prepares vector valued IP data for the passed local assembler. +// +// The IP data are a sequence of double values starting at the passed start +// value and incremented by one for each integration point and vector +// component. +// +// The location of the prepared data is specified by the IP data accessor +// callback function. +// +// Returns the expected data for use in unit test checks. +template <int dim> +std::vector<double> initVector(LocAsmIF<dim>& loc_asm, + double const start_value, + auto const ip_data_accessor, + bool const for_read_test) +{ + auto const num_int_pts = loc_asm.numIPs(); + + // init ip data in the local assembler + if (for_read_test) + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = + Eigen::Vector<double, dim>::LinSpaced( + dim, ip * dim + start_value, + ip * dim + start_value - 1 + dim); + } + } + else + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = + Eigen::Vector<double, dim>::Constant( + std::numeric_limits<double>::quiet_NaN()); + } + } + + // prepare reference data + std::vector<double> vector_expected(num_int_pts * dim); + iota(begin(vector_expected), end(vector_expected), start_value); + return vector_expected; +} + +// Prepares Kelvin vector valued IP data for the passed local assembler. +// +// The IP data are a sequence of double values starting at the passed start +// value and incremented by one for each integration point and Kelvin vector +// component. +// +// The location of the prepared data is specified by the IP data accessor +// callback function. +// +// Returns the expected data for use in unit test checks. +template <int dim> +std::vector<double> initKelvin(LocAsmIF<dim>& loc_asm, + double const start_value, + auto const ip_data_accessor, + bool const for_read_test) +{ + auto constexpr kv_size = + MathLib::KelvinVector::kelvin_vector_dimensions(dim); + + auto const num_int_pts = loc_asm.numIPs(); + + // init ip data in the local assembler + if (for_read_test) + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = + MathLib::KelvinVector::symmetricTensorToKelvinVector( + Eigen::Vector<double, kv_size>::LinSpaced( + kv_size, ip * kv_size + start_value, + ip * kv_size + start_value - 1 + kv_size)); + } + } + else + { + for (std::size_t ip = 0; ip < num_int_pts; ++ip) + { + ip_data_accessor(loc_asm, ip) = + Eigen::Vector<double, kv_size>::Constant( + std::numeric_limits<double>::quiet_NaN()); + } + } + + // prepare reference data + std::vector<double> vector_expected(num_int_pts * kv_size); + iota(begin(vector_expected), end(vector_expected), start_value); + return vector_expected; +} + +template <int dim> +struct ReferenceData +{ + std::vector<double> scalar; + std::vector<double> vector; + std::vector<double> kelvin; + + std::vector<double> scalar1; + std::vector<double> vector1; + std::vector<double> kelvin1; + + std::vector<double> scalar3; + std::vector<double> vector3; + std::vector<double> kelvin3; + + std::vector<double> scalar1b; + std::vector<double> scalar2b; + std::vector<double> scalar3b; + + static ReferenceData<dim> create(LocAsmIF<dim>& loc_asm, + bool const for_read_test) + { + std::random_device ran_dev; + std::mt19937 ran_gen(ran_dev()); + std::uniform_real_distribution<> ran_dist(1.0, 2.0); + auto start_value = [&]() { return ran_dist(ran_gen); }; + + ReferenceData<dim> ref; + + // level 0 - data preparation ////////////////////////////////////////// + + ref.scalar = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_scalar[ip]; + }, + for_read_test); + + ref.vector = initVector( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_vector[ip]; + }, + for_read_test); + + ref.kelvin = initKelvin( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_kelvin[ip]; + }, + for_read_test); + + // level 1 - data preparation ////////////////////////////////////////// + + ref.scalar1 = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].scalar1; + }, + for_read_test); + + ref.vector1 = initVector( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].vector1; + }, + for_read_test); + + ref.kelvin1 = initKelvin( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].kelvin1; + }, + for_read_test); + + // level 3 - data preparation ////////////////////////////////////////// + + ref.scalar3 = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].level2.level3.scalar3; + }, + for_read_test); + + ref.vector3 = initVector( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].level2.level3.vector3; + }, + for_read_test); + + ref.kelvin3 = initKelvin( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].level2.level3.kelvin3; + }, + for_read_test); + + // b levels - data preparation ///////////////////////////////////////// + // b levels test that the reflection implementation recurses on multiple + // members, not only on one. + + ref.scalar1b = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1b[ip].scalar1b; + }, + for_read_test); + + ref.scalar2b = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].level2b.scalar2b; + }, + for_read_test); + + ref.scalar3b = initScalar( + loc_asm, start_value(), + [](auto& loc_asm, unsigned const ip) -> auto& { + return loc_asm.ip_data_level1[ip].level2.level3b.scalar3b; + }, + for_read_test); + + return ref; + } +}; + +template <class Dim> +struct ProcessLib_ReflectIPData : ::testing::Test +{ + static constexpr auto dim = Dim::value; +}; + +using ProcessLib_ReflectIPData_TestCases = + ::testing::Types<std::integral_constant<int, 2>, + std::integral_constant<int, 3>>; + +TYPED_TEST_SUITE(ProcessLib_ReflectIPData, ProcessLib_ReflectIPData_TestCases); + +TYPED_TEST(ProcessLib_ReflectIPData, ReadTest) +{ + constexpr int dim = TypeParam::value; + auto constexpr kv_size = + MathLib::KelvinVector::kelvin_vector_dimensions(dim); + + using LocAsm = LocAsmIF<dim>; + + std::size_t const num_int_pts = 8; + LocAsm loc_asm(num_int_pts); + + auto const ref = ReferenceData<dim>::create(loc_asm, true); + + // function under test ///////////////////////////////////////////////////// + + std::map<std::string, NumCompAndFunction<dim>> + map_name_to_num_comp_and_function; + + ProcessLib::Reflection::forEachReflectedFlattenedIPDataAccessor<dim, + LocAsm>( + LocAsm::reflect(), + [&map_name_to_num_comp_and_function](std::string const& name, + unsigned const num_comp, + auto&& double_vec_from_loc_asm) + { + EXPECT_FALSE(map_name_to_num_comp_and_function.contains(name)); + map_name_to_num_comp_and_function[name] = { + num_comp, std::move(double_vec_from_loc_asm)}; + }); + + // checks ////////////////////////////////////////////////////////////////// + + auto check = [&map_name_to_num_comp_and_function, &loc_asm]( + std::string const& name, + unsigned const num_comp_expected, + std::vector<double> const& values_expected) + { + auto const it = map_name_to_num_comp_and_function.find(name); + + ASSERT_NE(map_name_to_num_comp_and_function.end(), it) + << "No accessor found for ip data with name '" << name << "'"; + + auto const& [num_comp, fct] = it->second; + + EXPECT_EQ(num_comp_expected, num_comp) + << "Number of components differs for ip data with name '" << name + << "'"; + EXPECT_THAT(fct(loc_asm), + testing::Pointwise(testing::DoubleEq(), values_expected)) + << "Values differ for ip data with name '" << name << "'"; + }; + + // level 0 + check("scalar", 1, ref.scalar); + check("vector", dim, ref.vector); + check("kelvin", kv_size, ref.kelvin); + + // level 1 + check("scalar1", 1, ref.scalar1); + check("vector1", dim, ref.vector1); + check("kelvin1", kv_size, ref.kelvin1); + + // level 3 + check("scalar3", 1, ref.scalar3); + check("vector3", dim, ref.vector3); + check("kelvin3", kv_size, ref.kelvin3); + + // b levels + check("scalar1b", 1, ref.scalar1b); + check("scalar2b", 1, ref.scalar2b); + check("scalar3b", 1, ref.scalar3b); +} diff --git a/Tests/ProcessLib/TestTransposeInplace.cpp b/Tests/ProcessLib/TestTransposeInplace.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d35bc7abe6e9ccf84fbc8e8f52c1ff399d772acc --- /dev/null +++ b/Tests/ProcessLib/TestTransposeInplace.cpp @@ -0,0 +1,59 @@ +/** + * \copyright + * Copyright (c) 2012-2022, 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 <gmock/gmock.h> +#include <gtest/gtest.h> + +#include "ProcessLib/Utils/TransposeInPlace.h" + +TEST(ProcessLib, TransposeInplaceFixed) +{ + std::vector<double> const values_in{ + 1, 2, 3, 4, // + 5, 6, 7, 8, // + 9, 10, 11, 12 // + }; + constexpr unsigned num_rows = 3; + + std::vector<double> const values_out_expected{ + 1, 5, 9, // + 2, 6, 10, // + 3, 7, 11, // + 4, 8, 12 // + }; + + std::vector<double> values_out_actual(values_in); + + ProcessLib::transposeInPlace<num_rows>(values_out_actual); + + ASSERT_THAT(values_out_actual, testing::ContainerEq(values_out_expected)); +} + +TEST(ProcessLib, TransposeInplaceDynamic) +{ + std::vector<double> const values_in{ + 1, 2, 3, 4, // + 5, 6, 7, 8, // + 9, 10, 11, 12 // + }; + unsigned const num_rows = 3; + + std::vector<double> const values_out_expected{ + 1, 5, 9, // + 2, 6, 10, // + 3, 7, 11, // + 4, 8, 12 // + }; + + std::vector<double> values_out_actual(values_in); + + ProcessLib::transposeInPlace(values_out_actual, num_rows); + + ASSERT_THAT(values_out_actual, testing::ContainerEq(values_out_expected)); +}