diff --git a/CMakeLists.txt b/CMakeLists.txt index f0646fc41ef..1a9701ce71b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -413,7 +413,7 @@ if(dune-common_FOUND) test_blackoilfluidsystem_nonstatic test_fluidmatrixinteractions test_fluidsystems test_tabulation test_h2brinepvt test_co2brinepvt test_eclblackoilfluidsystem test_eclblackoilfluidsystemnonstatic - test_eclblackoilpvt) + test_eclblackoilpvt test_materialstates) target_link_libraries(${tst} dunecommon) endforeach() endif() diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 5c3c69e122a..33bbe2d7d8f 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -505,6 +505,7 @@ list (APPEND TEST_SOURCE_FILES tests/material/test_binarycoefficients.cpp tests/material/test_fluidmatrixinteractions.cpp tests/material/test_fluidsystems.cpp + tests/material/test_materialstates.cpp tests/material/test_spline.cpp tests/material/test_tabulation.cpp tests/test_Visitor.cpp @@ -1085,6 +1086,7 @@ list( APPEND PUBLIC_HEADER_FILES opm/material/fluidmatrixinteractions/BrooksCoreyParams.hpp opm/material/fluidmatrixinteractions/BrooksCorey.hpp opm/material/fluidmatrixinteractions/PiecewiseLinearTwoPhaseMaterial.hpp + opm/material/materialstates/MaterialStateTPSA.hpp opm/material/checkFluidSystem.hpp opm/material/viscositymodels/LBC.hpp opm/material/viscositymodels/LBCco2rich.hpp diff --git a/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp b/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp index ccefdab168c..a08cea4b8eb 100644 --- a/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp +++ b/opm/input/eclipse/EclipseState/Grid/FieldProps.hpp @@ -153,6 +153,8 @@ static const std::unordered_map> double_keywor {"THCGAS", keyword_info{}.unit_string("Energy/AbsoluteTemperature*Length*Time")}, {"THCWATER",keyword_info{}.unit_string("Energy/AbsoluteTemperature*Length*Time")}, {"YMODULE", keyword_info{}.unit_string("Ymodule")}, + {"SMODULUS",keyword_info{}.unit_string("Ymodule")}, + {"LAME", keyword_info{}.unit_string("Ymodule")}, {"CSTRESS", keyword_info{}.unit_string("1")}, {"PRATIO", keyword_info{}.unit_string("1")}, {"BIOTCOEF", keyword_info{}.unit_string("1")}, diff --git a/opm/input/eclipse/EclipseState/Runspec.cpp b/opm/input/eclipse/EclipseState/Runspec.cpp index e572cb8744c..057c029a485 100644 --- a/opm/input/eclipse/EclipseState/Runspec.cpp +++ b/opm/input/eclipse/EclipseState/Runspec.cpp @@ -587,6 +587,71 @@ bool Nupcol::operator==(const Nupcol& data) const { } +Tpsa::Tpsa(const Deck& deck) +{ + using TPSA = ParserKeywords::TPSA; + if (deck.hasKeyword()) { + // Get record + const auto& keyword = deck.get().back(); + const auto& record = keyword[0]; + + // Set coupling scheme + const auto& scheme = record.getItem().get(0); + if (scheme == "LAGGED") { + this->m_coupling = Tpsa::CouplingScheme::Lagged; + } + else if (scheme == "FIXED-STRESS") { + this->m_coupling = Tpsa::CouplingScheme::FixedStress; + } + else { + const std::string msg = fmt::format("TPSA item 1 = {} not valid!", scheme); + OpmLog::error(msg); + throw std::runtime_error(msg); + } + + // Set fixed stress iteration range + this->m_fixed_stress_min_iter = record.getItem().get(0); + this->m_fixed_stress_max_iter = record.getItem().get(0); + + if (this->fixedStressScheme() && + (this->m_fixed_stress_min_iter > this->m_fixed_stress_max_iter)) { + this->m_fixed_stress_max_iter = this->m_fixed_stress_min_iter; + OpmLog::warning(fmt::format("TPSA item 2 (={}) is larger than item 3 (={}).\n" + "Maximum fixed-stress iterations set equal to minimum!", + this->m_fixed_stress_min_iter, + this->m_fixed_stress_max_iter)); + } + + // Turn on TPSA + this->m_active = true; + } +} + +bool Tpsa::operator==(const Tpsa& other) const +{ + return + this->m_coupling == other.m_coupling && + this->m_fixed_stress_min_iter == other.m_fixed_stress_min_iter && + this->m_fixed_stress_max_iter == other.m_fixed_stress_max_iter && + this->m_active == other.m_active; +} + +Tpsa Tpsa::serializationTestObject() +{ + Tpsa tpsa; + tpsa.m_coupling = CouplingScheme::Lagged; + tpsa.m_fixed_stress_min_iter = 2; + tpsa.m_fixed_stress_max_iter = 8; + tpsa.m_active = true; + + return tpsa; +} + +const Tpsa& Runspec::tpsa() const +{ + return this->m_tpsa; +} + bool Tracers::operator==(const Tracers& other) const { return this->m_oil_tracers == other.m_oil_tracers && @@ -666,6 +731,7 @@ Runspec::Runspec(const Deck& deck) , m_actdims (deck) , m_sfuncctrl (deck) , m_nupcol () + , m_tpsa (deck) , m_tracers (deck) , m_co2storage (false) , m_co2sol (false) diff --git a/opm/input/eclipse/EclipseState/Runspec.hpp b/opm/input/eclipse/EclipseState/Runspec.hpp index 26b5aa8d150..9e62405bebc 100644 --- a/opm/input/eclipse/EclipseState/Runspec.hpp +++ b/opm/input/eclipse/EclipseState/Runspec.hpp @@ -452,6 +452,61 @@ class Nupcol { }; +class Tpsa +{ +public: + enum class CouplingScheme { + Lagged, + FixedStress + }; + + Tpsa() = default; + explicit Tpsa(const Deck&); + bool operator==(const Tpsa& data) const; + static Tpsa serializationTestObject(); + + template + void serializeOp(Serializer& serializer) + { + serializer(this->m_coupling); + serializer(this->m_fixed_stress_min_iter); + serializer(this->m_fixed_stress_max_iter); + serializer(this->m_active); + } + + bool laggedScheme() const + { + return this->m_coupling == CouplingScheme::Lagged; + } + + bool fixedStressScheme() const + { + return this->m_coupling == CouplingScheme::FixedStress; + } + + int fixedStressMinIter() const + { + return this->m_fixed_stress_min_iter; + } + + int fixedStressMaxIter() const + { + return this->m_fixed_stress_max_iter; + } + + bool active() const + { + return this->m_active; + } + +private: + CouplingScheme m_coupling = CouplingScheme::Lagged; + int m_fixed_stress_min_iter{}; + int m_fixed_stress_max_iter{}; + bool m_active{false}; +}; + + class Tracers { public: Tracers() = default; @@ -510,6 +565,7 @@ class Runspec { const Actdims& actdims() const noexcept; const SatFuncControls& saturationFunctionControls() const noexcept; const Nupcol& nupcol() const noexcept; + const Tpsa& tpsa() const; const Tracers& tracers() const; bool compositionalMode() const; size_t numComps() const; @@ -554,6 +610,7 @@ class Runspec { serializer(m_mech); serializer(m_frac); serializer(m_temp); + serializer(m_tpsa); serializer(m_biof); } @@ -572,6 +629,7 @@ class Runspec { Actdims m_actdims{}; SatFuncControls m_sfuncctrl{}; Nupcol m_nupcol{}; + Tpsa m_tpsa{}; Tracers m_tracers{}; size_t m_comps = 0; bool m_co2storage{false}; diff --git a/opm/input/eclipse/share/keywords/900_OPM/L/LAME b/opm/input/eclipse/share/keywords/900_OPM/L/LAME new file mode 100644 index 00000000000..29e06142655 --- /dev/null +++ b/opm/input/eclipse/share/keywords/900_OPM/L/LAME @@ -0,0 +1,11 @@ +{ + "name": "LAME", + "sections": [ + "GRID" + ], + "description": "The LAME item is used to set Lame's first parameter for a cell in mechanics models.", + "data": { + "value_type": "DOUBLE", + "dimension": "Ymodule" + } +} diff --git a/opm/input/eclipse/share/keywords/900_OPM/S/SMODULUS b/opm/input/eclipse/share/keywords/900_OPM/S/SMODULUS new file mode 100644 index 00000000000..922a346cdb9 --- /dev/null +++ b/opm/input/eclipse/share/keywords/900_OPM/S/SMODULUS @@ -0,0 +1,11 @@ +{ + "name": "SMODULUS", + "sections": [ + "GRID" + ], + "description": "The SMODULUS item is used to set the shear modulus (Lame's second parameter) for a cell in mechanics models.", + "data": { + "value_type": "DOUBLE", + "dimension": "Ymodule" + } +} diff --git a/opm/input/eclipse/share/keywords/900_OPM/T/TPSA b/opm/input/eclipse/share/keywords/900_OPM/T/TPSA new file mode 100644 index 00000000000..c0e2be41902 --- /dev/null +++ b/opm/input/eclipse/share/keywords/900_OPM/T/TPSA @@ -0,0 +1,30 @@ +{ + "name": "TPSA", + "sections": [ + "RUNSPEC" + ], + "requires": [ + "MECH" + ], + "size": 1, + "items": [ + { + "item": 1, + "name": "COUPLING", + "value_type": "STRING", + "default": "LAGGED" + }, + { + "item": 2, + "name": "FIXED_STRESS_MIN_ITER", + "value_type": "INT", + "default": 1 + }, + { + "item": 3, + "name": "FIXED_STRESS_MAX_ITER", + "value_type": "INT", + "default": 5 + } + ] +} diff --git a/opm/input/eclipse/share/keywords/keyword_list.cmake b/opm/input/eclipse/share/keywords/keyword_list.cmake index 10623f00643..b29efdf6161 100644 --- a/opm/input/eclipse/share/keywords/keyword_list.cmake +++ b/opm/input/eclipse/share/keywords/keyword_list.cmake @@ -1141,6 +1141,7 @@ set( keywords 900_OPM/G/GROUP_PROBE_OPM 900_OPM/H/H2SOL 900_OPM/H/H2STORE + 900_OPM/L/LAME 900_OPM/M/MECH 900_OPM/M/MICP 900_OPM/M/MINNPCOL @@ -1178,6 +1179,7 @@ set( keywords 900_OPM/S/SKPRPOLY 900_OPM/S/SKPRWAT 900_OPM/S/SMICR + 900_OPM/S/SMODULUS 900_OPM/S/SOURCE 900_OPM/S/SOXYG 900_OPM/S/SPIDER @@ -1190,6 +1192,7 @@ set( keywords 900_OPM/T/THELCOEF 900_OPM/T/THERMEXR 900_OPM/T/TLPMIXPA + 900_OPM/T/TPSA 900_OPM/V/VAPWAT 900_OPM/Y/YMODULE 900_OPM/W/WATJT diff --git a/opm/material/materialstates/MaterialStateTPSA.hpp b/opm/material/materialstates/MaterialStateTPSA.hpp new file mode 100644 index 00000000000..45a856ece0b --- /dev/null +++ b/opm/material/materialstates/MaterialStateTPSA.hpp @@ -0,0 +1,155 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/* + Copyright 2025 NORCE AS + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . + + Consult the COPYING file in the top-level source directory of this + module for the precise wording of the license and the list of + copyright holders. +*/ +#ifndef TPSA_MATERIAL_STATE_HPP +#define TPSA_MATERIAL_STATE_HPP + +#include +#include + +#include + + +namespace Opm { + +template +class MaterialStateTPSA +{ +public: + /*! + * \brief Constructor + */ + MaterialStateTPSA() + { + Opm::Valgrind::SetUndefined(displacement_); + Opm::Valgrind::SetUndefined(rotation_); + Opm::Valgrind::SetUndefined(solidPressure_); + } + + /*! + * \brief Return direction (x-, y- or z-) component of displacement + * + * \param dirIdx Direction component index + */ + const Scalar displacement(unsigned dirIdx) const + { + return displacement_[dirIdx]; + } + + /*! + * \brief Return direction (x-, y- or z-) component of rotation + * + * \param dirIdx Direction component index + */ + const Scalar rotation(unsigned dirIdx) const + { + return rotation_[dirIdx]; + } + + /*! + * \brief Return solid pressure + */ + const Scalar solidPressure() const + { + return solidPressure_; + } + + /*! + * \brief Set a direction (x-, y- or z-) component of displacement + * + * \param dirIdx Direction component index + * \param value Displacement value + */ + void setDisplacement(unsigned dirIdx, const Scalar value) + { + Opm::Valgrind::CheckDefined(value); + Opm::Valgrind::SetUndefined(displacement_[dirIdx]); + + displacement_[dirIdx] = value; + } + + /*! + * \brief Set a direction (x-, y- or z-) component of rotation + * + * \param dirIdx Direction component index + * \param value Rotation value + */ + void setRotation(unsigned dirIdx, const Scalar value) + { + Opm::Valgrind::CheckDefined(value); + Opm::Valgrind::SetUndefined(rotation_[dirIdx]); + + rotation_[dirIdx] = value; + } + + /*! + * \brief Set solid pressure + * + * \param value Solid pressure value + */ + void setSolidPressure(const Scalar value) + { + Opm::Valgrind::CheckDefined(value); + Opm::Valgrind::SetUndefined(solidPressure_); + + solidPressure_ = value; + } + + /*! + * \brief Assign from another material state container + * + * \param ms Incoming material state container + */ + template + void assign(const MaterialState& ms) + { + // Assign displacement and rotation + for (unsigned dirIdx = 0; dirIdx < 3; ++dirIdx) { + displacement_[dirIdx] = Opm::decay(ms.displacement(dirIdx)); + rotation_[dirIdx] = Opm::decay(ms.rotation(dirIdx)); + } + + // Assign solid pressure + solidPressure_ = Opm::decay(ms.solidPressure()); + } + + /*! + * \brief Instruct Valgrind to check the definedness of all attributes of this class + */ + void checkDefined() const + { + Opm::Valgrind::CheckDefined(displacement_); + Opm::Valgrind::CheckDefined(rotation_); + Opm::Valgrind::CheckDefined(solidPressure_); + } + +protected: + std::array displacement_{}; + std::array rotation_{}; + Scalar solidPressure_{}; +}; // class MaterialState + +} // namespace Opm + +#endif diff --git a/opm/output/eclipse/WriteInit.cpp b/opm/output/eclipse/WriteInit.cpp index 9c8cfac41d5..66c77528f83 100644 --- a/opm/output/eclipse/WriteInit.cpp +++ b/opm/output/eclipse/WriteInit.cpp @@ -667,8 +667,10 @@ namespace { {"BIOTCOEF" , ::Opm::UnitSystem::measure::identity}, {"CSTRESS" , ::Opm::UnitSystem::measure::identity}, {"DISPERC" , ::Opm::UnitSystem::measure::length}, + {"LAME" , ::Opm::UnitSystem::measure::ymodule}, {"POELCOEF" , ::Opm::UnitSystem::measure::identity}, {"PRATIO" , ::Opm::UnitSystem::measure::identity}, + {"SMODULUS" , ::Opm::UnitSystem::measure::ymodule}, {"THERMEXR" , ::Opm::UnitSystem::measure::identity}, // 1/(temperature difference) {"THELCOEF" , ::Opm::UnitSystem::measure::identity}, // Pressure/Temperature {"YMODULE" , ::Opm::UnitSystem::measure::ymodule}, diff --git a/tests/material/test_materialstates.cpp b/tests/material/test_materialstates.cpp new file mode 100644 index 00000000000..3fba177c2bf --- /dev/null +++ b/tests/material/test_materialstates.cpp @@ -0,0 +1,63 @@ +// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- +// vi: set et ts=4 sw=4 sts=4: +/* + Copyright 2025 NORCE AS + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . + + Consult the COPYING file in the top-level source directory of this + module for the precise wording of the license and the list of + copyright holders. +*/ +#include "config.h" + +#define BOOST_TEST_MODULE MaterialStates +#include + +#include + +#include +#include + +#include + + +// Scalar and evaluation types to check +using EvalTypes = boost::mpl::list, + Opm::DenseAd::Evaluation>; + +BOOST_AUTO_TEST_CASE_TEMPLATE(TpsaMaterialState, Eval, EvalTypes) +{ + using MaterialState = Opm::MaterialStateTPSA; + + // Instantiate Material state + MaterialState ms; + + // Check if copyable + [[maybe_unused]] MaterialState tmpMs(ms); + + // Valgrind checkDefined interface + ms.checkDefined(); + + // Check methods + while (false) { + std::ignore = ms.displacement(/*dirIdx=*/0); + std::ignore = ms.rotation(/*dirIdx=*/0); + std::ignore = ms.solidPressure(); + } +} diff --git a/tests/parser/RunspecTests.cpp b/tests/parser/RunspecTests.cpp index 18e8f5cf7d3..23f1677aea9 100644 --- a/tests/parser/RunspecTests.cpp +++ b/tests/parser/RunspecTests.cpp @@ -1136,6 +1136,44 @@ BOOST_AUTO_TEST_CASE(Mech) { BOOST_CHECK( runspec.mech() ); } +BOOST_AUTO_TEST_CASE(TpsaLaggedTest) { + const std::string input = R"( + MECH + TPSA + LAGGED / + )"; + + Parser parser; + + auto deck = parser.parseString(input); + Runspec runspec(deck); + const auto& tpsa = runspec.tpsa(); + + BOOST_CHECK(tpsa.active()); + BOOST_CHECK(tpsa.laggedScheme()); + BOOST_CHECK(!tpsa.fixedStressScheme()); +} + +BOOST_AUTO_TEST_CASE(TpsaFixedStressTest) { + const std::string input = R"( + MECH + TPSA + FIXED-STRESS 3 7 / + )"; + + Parser parser; + + auto deck = parser.parseString(input); + Runspec runspec(deck); + const auto& tpsa = runspec.tpsa(); + + BOOST_CHECK(tpsa.active()); + BOOST_CHECK(tpsa.fixedStressScheme()); + BOOST_CHECK_EQUAL(tpsa.fixedStressMinIter(), 3); + BOOST_CHECK_EQUAL(tpsa.fixedStressMaxIter(), 7); + BOOST_CHECK(!tpsa.laggedScheme()); +} + BOOST_AUTO_TEST_CASE(NetworkDims_no_network) { {