From fd1d777db0aaa04f68cd580ef72f3eacd6bbe965 Mon Sep 17 00:00:00 2001 From: Dmitri Naumov <github@naumov.de> Date: Tue, 15 Dec 2020 11:43:28 +0100 Subject: [PATCH] [T/RM] Unit test for micro porosity computation. --- Tests/CMakeLists.txt | 3 + .../MicroporosityComputation.cpp | 257 ++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index 9348e24e705..23f0e8f5526 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -43,6 +43,9 @@ endif() if(OGS_BUILD_PROCESS_LIE) append_source_files(TEST_SOURCES ProcessLib/LIE) endif() +if(OGS_BUILD_PROCESS_RichardsMechanics) + append_source_files(TEST_SOURCES ProcessLib/RichardsMechanics) +endif() if(OGS_USE_PETSC) list(REMOVE_ITEM TEST_SOURCES NumLib/TestSerialLinearSolver.cpp) diff --git a/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp b/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp new file mode 100644 index 00000000000..25d16f4cb11 --- /dev/null +++ b/Tests/ProcessLib/RichardsMechanics/MicroporosityComputation.cpp @@ -0,0 +1,257 @@ +/** + * \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 <gtest/gtest.h> + +#include <Eigen/Eigen> +#include <cmath> +#include <memory> + +#include "MaterialLib/MPL/Properties/CapillaryPressureSaturation/SaturationVanGenuchten.h" +#include "MaterialLib/MPL/Properties/SaturationDependentSwelling.h" +#include "MaterialLib/MPL/Property.h" +#include "MaterialLib/SolidModels/LinearElasticIsotropic.h" +#include "MaterialLib/SolidModels/MechanicsBase.h" +#include "MathLib/InterpolationAlgorithms/PiecewiseLinearInterpolation.h" +#include "ParameterLib/ConstantParameter.h" +#include "ProcessLib/RichardsMechanics/ComputeMicroPorosity.h" + +using namespace ProcessLib::RichardsMechanics; +namespace MPL = MaterialPropertyLib; + +namespace +{ +template <int DisplacementDim> +MathLib::KelvinVector::KelvinMatrixType<DisplacementDim> +computeElasticTangentStiffness( + MaterialLib::Solids::MechanicsBase<DisplacementDim> const& solid_material, + double const t, ParameterLib::SpatialPosition const& x_position, + double const dt, double const temperature) +{ + MPL::VariableArray variable_array; + MPL::VariableArray variable_array_prev; + + auto const null_state = solid_material.createMaterialStateVariables(); + + using KV = MathLib::KelvinVector::KelvinVectorType<DisplacementDim>; + + variable_array[static_cast<int>(MPL::Variable::stress)].emplace<KV>( + KV::Zero()); + variable_array[static_cast<int>(MPL::Variable::mechanical_strain)] + .emplace<KV>(KV::Zero()); + variable_array[static_cast<int>(MPL::Variable::temperature)] + .emplace<double>(temperature); + + variable_array_prev[static_cast<int>(MPL::Variable::stress)].emplace<KV>( + KV::Zero()); + variable_array_prev[static_cast<int>(MPL::Variable::mechanical_strain)] + .emplace<KV>(KV::Zero()); + variable_array_prev[static_cast<int>(MPL::Variable::temperature)] + .emplace<double>(temperature); + + auto&& solution = solid_material.integrateStress( + variable_array_prev, variable_array, t, x_position, dt, *null_state); + + if (!solution) + { + OGS_FATAL("Computation of elastic tangent stiffness failed."); + } + + MathLib::KelvinVector::KelvinMatrixType<DisplacementDim> C = + std::move(std::get<2>(*solution)); + + return C; +} + +} // namespace + +TEST(RichardsMechanics, computeMicroPorosity) +{ + static constexpr auto eps = 2e-14; + constexpr int DisplacementDim = 2; + // + // Create properties. + // + + // Saturation + constexpr double residual_liquid_saturation = 0; + constexpr double residual_gas_saturation = 0; + constexpr double exponent = 0.4; + constexpr double p_b = 300e6; + MPL::Property const& saturation_micro = MPL::SaturationVanGenuchten{ + "saturation_micro", residual_liquid_saturation, residual_gas_saturation, + exponent, p_b}; + + // Swelling + constexpr std::array swelling_pressures{1e7, 1e7, 1e7}; + constexpr std::array swelling_exponents{1.2, 1.2, 1.2}; + constexpr double lower_saturation_limit = 0; + constexpr double upper_saturation_limit = 1; + MPL::Property const& swelling_stress_rate = + MPL::SaturationDependentSwelling{ + "swelling_stress_rate", swelling_pressures, swelling_exponents, + lower_saturation_limit, upper_saturation_limit, nullptr}; + + // LinearElastic + ParameterLib::ConstantParameter<double> E{"YoungsModulus", 150e6}; + ParameterLib::ConstantParameter<double> nu{"PoissonsRatio", 0.25}; + auto const solid_material = + MaterialLib::Solids::LinearElasticIsotropic<DisplacementDim>{{E, nu}}; + + // + // Populate constants + // + ParameterLib::SpatialPosition const pos; + constexpr double t0 = 0; + constexpr double t_max = 200e3; + double const dt = 100; + double const T = 293.15; + + auto const& identity2 = MathLib::KelvinVector::Invariants< + MathLib::KelvinVector::KelvinVectorDimensions<DisplacementDim>::value>:: + identity2; + auto const C_el = + computeElasticTangentStiffness(solid_material, t0, pos, dt, T); + MathLib::KelvinVector::KelvinVectorType<DisplacementDim> const& + I_2_C_el_inverse = identity2.transpose() * C_el.inverse(); + double const rho_LR_m = 1e3; + double const mu_LR = 1e-3; + double const alpha_bar = 1e-14; + double const alpha_B = 1; + double const phi_M = 0.45; + + auto saturation = [&](double const p_L) { + MPL::VariableArray v; + v[static_cast<int>(MPL::Variable::capillary_pressure)] = -p_L; + return saturation_micro.template value<double>(v, pos, t0, dt); + }; + + // + // Drive the computation by macro pressure. Saturation, steady, and + // desaturation. + // + + // The time coordinates are simultaneously also the test points. + std::vector const t_coords{ + // saturation is quick to rich maximum, + 0 * t_max, 0.05 * t_max, 0.2 * t_max, 0.3 * t_max, + // desaturation takes much longer. + 0.35 * t_max, 0.5 * t_max, t_max}; + std::vector const pressure_M_values{// saturation + -200e6, -200e6, 0., 0., + // desaturation + 0., -200e6, -200e6}; + MathLib::PiecewiseLinearInterpolation const pressure_M{t_coords, + pressure_M_values}; + + // + // Initial state + // + + MicroPorosityStateSpace<DisplacementDim> state_prev = { + 0.45 - 0.25, // phi_m + 0, // e_sw + -200e6, // p_L_m + {0, 0, 0, 0} // sigma_sw + }; + + auto state = state_prev; + + std::vector<MicroPorosityStateSpace<DisplacementDim>> results; + for (double t = t0 + dt; t <= t_max; t += dt) + { + double const p_L = pressure_M.getValue(t); + double const S_L_m_prev = saturation(state_prev.p_L_m); + //auto const [delta_phi_m, delta_e_sw, delta_sigma_sw, delta_p_L_m] = + auto const state_increment = computeMicroPorosity<DisplacementDim>( + I_2_C_el_inverse, + rho_LR_m, // for simplification equal to rho_LR_M + mu_LR, alpha_bar, alpha_B, phi_M, p_L, state_prev.p_L_m, + MaterialPropertyLib::VariableArray{}, S_L_m_prev, state_prev.phi_m, + pos, t, dt, saturation_micro, swelling_stress_rate); + + // push back state + state_prev = state; + + // update + state += state_increment; + + if (std::find_if(begin(t_coords), end(t_coords), [&](auto const value) { + return std::abs(t - value) < eps; + }) != end(t_coords)) + { + results.push_back(state); + /* Keep for possible result updates + std::cout << std::setprecision(17) + << "MicroPorosityStateSpace<DisplacementDim>{ " + << state.phi_m << ", " << state.e_sw << ", " + << state.p_L_m << ", {" << state.sigma_sw[0] << ", " + << state.sigma_sw[1] << ", " << state.sigma_sw[2] << ", " + << state.sigma_sw[3] << " }}\n"; + */ + } + } + + // + // Expected results + // + std::vector expected_results = { + MicroPorosityStateSpace<DisplacementDim>{ + 0.20000000000000001, 0, -200000000, {0, 0, 0, 0}}, + MicroPorosityStateSpace<DisplacementDim>{ + 0.20678794335787565, + 0.012341715196137344, + -87023875.75987792, + {-1234171.5196137347, -1234171.5196137347, -1234171.5196137347, 0}}, + MicroPorosityStateSpace<DisplacementDim>{ + 0.20984996711734061, + 0.017909031122437243, + -4693546.2948520193, + {-1790903.1122437208, -1790903.1122437208, -1790903.1122437208, 0}}, + MicroPorosityStateSpace<DisplacementDim>{ + 0.20987701765630837, + 0.017958213920560351, + 23234.984113500199, + {-1795821.3920560319, -1795821.3920560319, -1795821.3920560319, 0}}, + MicroPorosityStateSpace<DisplacementDim>{ + 0.20475472126658667, + 0.0086449477574298151, + -123250534.84079438, + {-864494.77574297995, -864494.77574297995, -864494.77574297995, 0}}, + MicroPorosityStateSpace<DisplacementDim>{ + 0.20005534659406693, + 0.0001006301710302317, + -199863745.41244847, + {-10063.017103021764, -10063.017103021764, -10063.017103021764, + 0}}}; + + auto eps_equal = [](double const a, double const b) { + return std::abs(a - b) < eps; + }; + + auto const [mismatch_it_expected, mismatch_it_results] = std::mismatch( + begin(expected_results), end(expected_results), begin(results), + [&](auto const& a, auto const& b) { + EXPECT_TRUE(eps_equal(a.phi_m, b.phi_m)) << "with eps = " << eps; + EXPECT_TRUE(eps_equal(a.e_sw, b.e_sw)) << "with eps = " << eps; + EXPECT_TRUE(eps_equal(a.p_L_m / 1e9, b.p_L_m / 1e9)) + << "with eps = " << eps; + EXPECT_TRUE((a.sigma_sw - b.sigma_sw).norm() / 1e9 < eps) + << "with eps = " << eps; + // Divide pressures and stresses by GPa to get approxmately + // O(1) numbers. + return eps_equal(a.phi_m, b.phi_m) && eps_equal(a.e_sw, b.e_sw) && + eps_equal(a.p_L_m / 1e9, b.p_L_m / 1e9) && + (a.sigma_sw - b.sigma_sw).norm() / 1e9 < eps; + }); + + ASSERT_TRUE(mismatch_it_expected == end(expected_results)) + << "Mismatch for expected result\n\t" << *mismatch_it_expected + << "\n\tfound. Corresponding result is\n\t" << *mismatch_it_results; + ASSERT_TRUE(mismatch_it_results == end(results)); +} -- GitLab