diff --git a/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/c_SaturationBrooksCorey.md b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/c_SaturationBrooksCorey.md new file mode 100644 index 0000000000000000000000000000000000000000..e0be5d6c8b45ec729e063d38f48a355f68e68df9 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/c_SaturationBrooksCorey.md @@ -0,0 +1 @@ +Saturation model presented by Brooks&Corey (1964) \cite BrooksCorey1964. diff --git a/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_entry_pressure.md b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_entry_pressure.md new file mode 100644 index 0000000000000000000000000000000000000000..e3d246498cefcd2402e2df304f0047534317a04a --- /dev/null +++ b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_entry_pressure.md @@ -0,0 +1 @@ +The required pressure for a non-wetting fluid to enter a porous medium (the capillary pressure at full saturation). diff --git a/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_lambda.md b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_lambda.md new file mode 100644 index 0000000000000000000000000000000000000000..6bb97b683fc7ba9dc1ee03065912d88f39128217 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_lambda.md @@ -0,0 +1 @@ +The exponent of the Brooks&Corey \cite BrooksCorey1964 saturation function. diff --git a/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_gas_saturation.md b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_gas_saturation.md new file mode 100644 index 0000000000000000000000000000000000000000..d8631eb27f4627345443d3cb426330a84c325db1 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_gas_saturation.md @@ -0,0 +1 @@ +The smallest degree of saturation of the gas (or non-wetting) phase in the medium. diff --git a/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_liquid_saturation.md b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_liquid_saturation.md new file mode 100644 index 0000000000000000000000000000000000000000..95ce0ed4b319db3ecc0cfc87b42c611dcd572210 --- /dev/null +++ b/Documentation/ProjectFile/properties/property/SaturationBrooksCorey/t_residual_liquid_saturation.md @@ -0,0 +1 @@ +The smallest degree of saturation of the liquid (or wetting) phase in the medium. diff --git a/MaterialLib/MPL/CreateProperty.cpp b/MaterialLib/MPL/CreateProperty.cpp index 4d6eb09f375a007d9084b4b7985675d623c1a045..02646eddf901dc4a1e81856cb37c3287cdee0f23 100644 --- a/MaterialLib/MPL/CreateProperty.cpp +++ b/MaterialLib/MPL/CreateProperty.cpp @@ -18,6 +18,7 @@ #include "BaseLib/ConfigTree.h" #include "Properties/CreateProperties.h" + #include "Properties/Properties.h" #include "Component.h" @@ -58,12 +59,11 @@ std::unique_ptr<MaterialPropertyLib::Property> createProperty( { return createIdealGasLaw(config); } - /* TODO Additional properties go here, for example: - if (boost::iequals(property_type, "BilinearTemperaturePressure")) + + if (boost::iequals(property_type, "SaturationBrooksCorey")) { - return createBilinearTemperaturePressure(config, material_type); + return createSaturationBrooksCorey(config); } - */ // If none of the above property types are found, OGS throws an error. OGS_FATAL("The specified component property type '%s' was not recognized", diff --git a/MaterialLib/MPL/Properties/CreateProperties.h b/MaterialLib/MPL/Properties/CreateProperties.h index 160408503fa0ddafaf007d94ec333a0dba18e730..646a6e2213ab21729599674913def917a1b292cb 100644 --- a/MaterialLib/MPL/Properties/CreateProperties.h +++ b/MaterialLib/MPL/Properties/CreateProperties.h @@ -16,4 +16,5 @@ #include "CreateExponentialProperty.h" #include "CreateIdealGasLaw.h" #include "CreateLinearProperty.h" -#include "CreateParameterProperty.h" \ No newline at end of file +#include "CreateParameterProperty.h" +#include "CreateSaturationBrooksCorey.h" \ No newline at end of file diff --git a/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.cpp b/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3bfcf096c69599d4b9924783a01e71ff294f8015 --- /dev/null +++ b/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.cpp @@ -0,0 +1,42 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2019, 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 "SaturationBrooksCorey.h" + +namespace MaterialPropertyLib +{ +std::unique_ptr<SaturationBrooksCorey> createSaturationBrooksCorey( + BaseLib::ConfigTree const& config) +{ + // check is reading the parameter, not peeking it... + //! \ogs_file_param{prj__media__medium__properties__property__SaturationBrooksCorey} + config.checkConfigParameter("type", "SaturationBrooksCorey"); + + DBUG("Create SaturationBrooksCorey medium property"); + + auto const residual_liquid_saturation = + //! \ogs_file_param{prj__media__medium__properties__property__SaturationBrooksCorey__residual_liquid_saturation} + config.getConfigParameter<double>("residual_liquid_saturation"); + auto const residual_gas_saturation = + //! \ogs_file_param{prj__media__medium__properties__property__SaturationBrooksCorey__residual_gas_saturation} + config.getConfigParameter<double>("residual_gas_saturation"); + auto const exponent = + //! \ogs_file_param{prj__media__medium__properties__property__SaturationBrooksCorey__lambda} + config.getConfigParameter<double>("lambda"); + auto const entry_pressure = + //! \ogs_file_param{prj__media__medium__properties__property__SaturationBrooksCorey__entry_pressure} + config.getConfigParameter<double>("entry_pressure"); + + return std::make_unique<SaturationBrooksCorey>(residual_liquid_saturation, + residual_gas_saturation, + exponent, entry_pressure); +} +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.h b/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.h new file mode 100644 index 0000000000000000000000000000000000000000..42a8286b8d54914bfc7f9ba8012820cee7134156 --- /dev/null +++ b/MaterialLib/MPL/Properties/CreateSaturationBrooksCorey.h @@ -0,0 +1,27 @@ +/** + * \file + * \copyright + * Copyright (c) 2012-2019, 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 + +namespace BaseLib +{ +class ConfigTree; +} + +namespace MaterialPropertyLib +{ +class SaturationBrooksCorey; +} + +namespace MaterialPropertyLib +{ +std::unique_ptr<SaturationBrooksCorey> createSaturationBrooksCorey( + BaseLib::ConfigTree const& config); +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/Properties.h b/MaterialLib/MPL/Properties/Properties.h index f8ca051af51b98fed3182f812161212dcb057abf..bd4b40595de10470c7f9ee174806a5f92839190d 100644 --- a/MaterialLib/MPL/Properties/Properties.h +++ b/MaterialLib/MPL/Properties/Properties.h @@ -17,3 +17,4 @@ #include "IdealGasLaw.h" #include "LinearProperty.h" #include "ParameterProperty.h" +#include "SaturationBrooksCorey.h" diff --git a/MaterialLib/MPL/Properties/SaturationBrooksCorey.cpp b/MaterialLib/MPL/Properties/SaturationBrooksCorey.cpp new file mode 100644 index 0000000000000000000000000000000000000000..54f87cacfc5165eda475ba99e602f24b52971a74 --- /dev/null +++ b/MaterialLib/MPL/Properties/SaturationBrooksCorey.cpp @@ -0,0 +1,109 @@ +/** + * \file + * \author Norbert Grunwald + * \date 27.06.2018 + * \brief + * + * \copyright + * Copyright (c) 2012-2019, 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 "SaturationBrooksCorey.h" + +#include <algorithm> +#include <cmath> + +#include "MaterialLib/MPL/Medium.h" +#include "MathLib/MathTools.h" + +namespace MaterialPropertyLib +{ +SaturationBrooksCorey::SaturationBrooksCorey( + const double residual_liquid_saturation, + const double residual_gas_saturation, + const double exponent, + const double entry_pressure) + : _residual_liquid_saturation(residual_liquid_saturation), + _residual_gas_saturation(residual_gas_saturation), + _exponent(exponent), + _entry_pressure(entry_pressure){}; + +PropertyDataType SaturationBrooksCorey::value( + VariableArray const& variable_array, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/) const +{ + const double p_cap = std::get<double>( + variable_array[static_cast<int>(Variable::capillary_pressure)]); + + const double s_L_res = _residual_liquid_saturation; + const double s_L_max = 1.0 - _residual_gas_saturation; + const double lambda = _exponent; + const double p_b = _entry_pressure; + + if (p_cap <= p_b) + return s_L_max; + + const double s_eff = std::pow(p_b / p_cap, lambda); + return s_eff * (s_L_max - s_L_res) + s_L_res; +} + +PropertyDataType SaturationBrooksCorey::dValue( + VariableArray const& variable_array, Variable const primary_variable, + ParameterLib::SpatialPosition const& pos, double const t) const +{ + (void)primary_variable; + assert((primary_variable == Variable::capillary_pressure) && + "SaturationBrooksCorey::dValue is implemented for " + " derivatives with respect to capillary pressure only."); + + const double s_L_res = _residual_liquid_saturation; + const double s_L_max = 1.0 - _residual_gas_saturation; + const double p_b = _entry_pressure; + const double p_cap = std::get<double>( + variable_array[static_cast<int>(Variable::capillary_pressure)]); + + if (p_cap <= p_b) + return 0.; + + auto const s_L = _medium->property(PropertyType::saturation) + .template value<double>(variable_array, pos, t); + + const double lambda = _exponent; + const double ds_L_d_s_eff = 1. / (s_L_max - s_L_res); + + return -lambda / p_cap * s_L * ds_L_d_s_eff; +} + +PropertyDataType SaturationBrooksCorey::d2Value( + VariableArray const& variable_array, Variable const primary_variable1, + Variable const primary_variable2, + ParameterLib::SpatialPosition const& /*pos*/, double const /*t*/) const +{ + (void)primary_variable1; + (void)primary_variable2; + assert((primary_variable1 == Variable::capillary_pressure) && + (primary_variable2 == Variable::capillary_pressure) && + "SaturationBrooksCorey::d2Value is implemented for " + " derivatives with respect to capillary pressure only."); + + const double p_b = _entry_pressure; + const double p_cap = std::max( + p_b, + std::get<double>( + variable_array[static_cast<int>(Variable::capillary_pressure)])); + + const double s_L_res = _residual_liquid_saturation; + const double s_L_max = 1.0 - _residual_gas_saturation; + + const double lambda = _exponent; + + return lambda * (lambda + 1) * std::pow(p_b / p_cap, lambda) / + (p_cap * p_cap) * (s_L_max - s_L_res); +} + +} // namespace MaterialPropertyLib diff --git a/MaterialLib/MPL/Properties/SaturationBrooksCorey.h b/MaterialLib/MPL/Properties/SaturationBrooksCorey.h new file mode 100644 index 0000000000000000000000000000000000000000..2ee2c3c2c1d6cc1ec4efe543c905c16783cfff0a --- /dev/null +++ b/MaterialLib/MPL/Properties/SaturationBrooksCorey.h @@ -0,0 +1,72 @@ +/** + * \file + * \author Norbert Grunwald + * \date 27.06.2018 + * \brief + * + * \copyright + * Copyright (c) 2012-2019, 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 <limits> +#include "MaterialLib/MPL/Property.h" + +namespace MaterialPropertyLib +{ +class Medium; +class Phase; +class Component; +/** + * \brief A well known soil characteristics function + * \details This property must be a medium property, it + * computes the saturation of the wetting phase as function + * of capillary pressure. + */ +class SaturationBrooksCorey final : public Property +{ +private: + Medium* _medium = nullptr; + const double _residual_liquid_saturation; + const double _residual_gas_saturation; + const double _exponent; + const double _entry_pressure; + +public: + SaturationBrooksCorey(const double residual_liquid_saturation, + const double residual_gas_saturation, + const double exponent, + const double entry_pressure); + + void setScale( + std::variant<Medium*, Phase*, Component*> scale_pointer) override + { + if (!std::holds_alternative<Medium*>(scale_pointer)) + { + OGS_FATAL( + "The property 'SaturationBrooksCorey' is implemented on the " + "'media' scale only."); + } + _medium = std::get<Medium*>(scale_pointer); + } + + /// Those methods override the base class implementations and + /// actually compute and set the property _values and _dValues. + PropertyDataType value(VariableArray const& variable_array, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/) const override; + PropertyDataType dValue(VariableArray const& variable_array, + Variable const variable, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/) const override; + PropertyDataType d2Value(VariableArray const& variable_array, + Variable const variable1, + Variable const variable2, + ParameterLib::SpatialPosition const& /*pos*/, + double const /*t*/) const override; +}; +} // namespace MaterialPropertyLib diff --git a/Tests/MaterialLib/TestMPLSaturationBrooksCorey.cpp b/Tests/MaterialLib/TestMPLSaturationBrooksCorey.cpp new file mode 100644 index 0000000000000000000000000000000000000000..45700e5fdc8b9dccd15d6d9554a606a4121fa8b1 --- /dev/null +++ b/Tests/MaterialLib/TestMPLSaturationBrooksCorey.cpp @@ -0,0 +1,97 @@ +/** + * \file + * + * \copyright + * Copyright (c) 2012-2019, 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 <sstream> + +#include "TestMPL.h" +#include "Tests/TestTools.h" + +#include "MaterialLib/MPL/Medium.h" +#include "MaterialLib/MPL/Properties/SaturationBrooksCorey.h" + +TEST(MaterialPropertyLib, SaturationBrooksCorey) +{ + const double ref_lambda = 2.5; + const double ref_residual_liquid_saturation = 0.12; + const double ref_residual_gas_saturation = 0.06; + const double ref_entry_pressure = 5678.54; + + const double max_saturation = 1. - ref_residual_gas_saturation; + + std::stringstream m; + m << "<medium>\n"; + m << "<phases></phases>\n"; + m << "<properties>\n"; + m << " <property>\n"; + m << " <name>saturation</name>\n"; + m << " <type>SaturationBrooksCorey</type>\n"; + m << " <residual_liquid_saturation>" << ref_residual_liquid_saturation + << "</residual_liquid_saturation>\n"; + m << " <residual_gas_saturation>" << ref_residual_gas_saturation + << "</residual_gas_saturation>\n"; + m << " <lambda>" << ref_lambda << "</lambda>\n"; + m << " <entry_pressure>" << ref_entry_pressure << "</entry_pressure>\n"; + m << " </property> \n"; + m << "</properties>\n"; + m << "</medium>\n"; + + auto const& medium = createTestMaterial(m.str()); + + MaterialPropertyLib::VariableArray variable_array; + ParameterLib::SpatialPosition const pos; + double const time = std::numeric_limits<double>::quiet_NaN(); + + variable_array[static_cast<int>( + MaterialPropertyLib::Variable::capillary_pressure)] = 0.0; + + auto s_L = medium->property(MaterialPropertyLib::PropertyType::saturation) + .template value<double>(variable_array, pos, time); + auto ds_L_dp_cap = + medium->property(MaterialPropertyLib::PropertyType::saturation) + .template dValue<double>( + variable_array, + MaterialPropertyLib::Variable::capillary_pressure, + pos, + time); + + ASSERT_EQ(s_L, max_saturation); + ASSERT_EQ(ds_L_dp_cap, 0.); + + for (double p_cap = 1.0; p_cap < 1.0e10; p_cap *= 1.5) + { + variable_array[static_cast<int>( + MaterialPropertyLib::Variable::capillary_pressure)] = p_cap; + + s_L = medium->property(MaterialPropertyLib::PropertyType::saturation) + .template value<double>(variable_array, pos, time); + ds_L_dp_cap = + medium->property(MaterialPropertyLib::PropertyType::saturation) + .template dValue<double>( + variable_array, + MaterialPropertyLib::Variable::capillary_pressure, + pos, + time); + + const double s_eff = + std::pow(ref_entry_pressure / std::max(p_cap, ref_entry_pressure), + ref_lambda); + const double s_ref = + s_eff * (max_saturation - ref_residual_liquid_saturation) + + ref_residual_liquid_saturation; + const double ds_eff_dpc = + (p_cap <= ref_entry_pressure) ? 0. : -ref_lambda / p_cap * s_ref; + const double ds_L_ds_eff = 1. / (max_saturation - ref_residual_liquid_saturation); + const double ds_L_dpc = ds_L_ds_eff * ds_eff_dpc; + + ASSERT_NEAR(s_L, s_ref, 1.e-10); + ASSERT_NEAR(ds_L_dp_cap, ds_L_dpc, 1.e-10); + } +} \ No newline at end of file