From 433cc4d6497741344dfd4de169be6ae997d42d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 17 Jun 2022 17:52:46 +0200 Subject: [PATCH] Treat All-Defaulted PVTW Record as Copy of Previous A simulation model may choose to give PVTW data as PVTW 1.0 1.0 1.0e-5 0.2 0.0 / / -- record 2 (copied from record 1) if, for instance, the oil and/or gas tables are different in regions 1 and 2, but the water is the same. In this case we must properly copy record 1 into record 2 and essentially recreate the table. To this end, decouple the 'PvtwTable' from the 'FlatTable' machinery and make the former into an independent type containing vector<> instead of inheriting from vector<>. Implement the default->copy behaviour in the new PvtwTable::PvtwTable(const DeckKeyword&) constructor. --- .../eclipse/EclipseState/Tables/FlatTable.hpp | 39 +++++++++++- .../eclipse/EclipseState/Tables/Tables.cpp | 61 ++++++++++++++++++- tests/parser/TableManagerTests.cpp | 57 +++++++++++++++++ 3 files changed, 154 insertions(+), 3 deletions(-) diff --git a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp index 566c74767..fa021c18e 100644 --- a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp +++ b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp @@ -1,6 +1,10 @@ #ifndef OPM_FLAT_TABLE_HPP #define OPM_FLAT_TABLE_HPP +#include +#include +#include + namespace Opm { class DeckKeyword; @@ -156,13 +160,44 @@ struct PVTWRecord { } }; -struct PvtwTable : public FlatTable< PVTWRecord > { - using FlatTable< PVTWRecord >::FlatTable; +class PvtwTable +{ +public: + PvtwTable() = default; + explicit PvtwTable(const DeckKeyword& kw); + explicit PvtwTable(std::initializer_list records); + + auto size() const { return this->table_.size(); } + bool empty() const { return this->table_.empty(); } + + const PVTWRecord& operator[](const std::size_t tableID) const + { + return this->table_[tableID]; + } + + const PVTWRecord& at(const std::size_t tableID) const + { + return this->table_.at(tableID); + } + + bool operator==(const PvtwTable& other) const + { + return this->table_ == other.table_; + } static PvtwTable serializeObject() { return PvtwTable({{1.0, 2.0, 3.0, 4.0, 5.0}}); } + + template + void serializeOp(Serializer& serializer) + { + serializer.vector(this->table_); + } + +private: + std::vector table_{}; }; struct ROCKRecord { diff --git a/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp index 4c0da6c03..67c54060c 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp @@ -86,6 +86,16 @@ #include #include +#include + +#include +#include +#include + +#include + +#include + namespace Opm { PvtgTable::PvtgTable( const DeckKeyword& keyword, size_t tableIdx ) : @@ -1547,8 +1557,58 @@ struct flat_props< PVCDORecord, N > { } }; +bool all_defaulted(const DeckRecord& record) +{ + return std::all_of(record.begin(), record.end(), + [](const DeckItem& item) + { + const auto& vstat = item.getValueStatus(); + return std::all_of(vstat.begin(), vstat.end(), &value::defaulted); + }); } +} // Anonymous namespace + +// ------------------------------------------------------------------------ + +PvtwTable::PvtwTable(const DeckKeyword& kw) +{ + if (kw.name() != ParserKeywords::PVTW::keywordName) { + throw std::invalid_argument { + fmt::format("Keyword {} cannot be used to " + "initialise {} table structures", kw.name(), + ParserKeywords::PVTW::keywordName) + }; + } + + this->table_.reserve(kw.size()); + + for (const auto& record : kw) { + if (all_defaulted(record)) { + // All-defaulted records imply PVTW in region R is equal to PVTW + // in region R-1. PVTW must not be defaulted in region 1 (i.e., + // when PVTNUM=1). + if (this->table_.empty()) { + throw OpmInputError { + "First record cannot be defaulted", + kw.location() + }; + } + + this->table_.push_back(this->table_.back()); + } + else { + this->table_.push_back(flat_get(record, mkseq{})); + } + } +} + +PvtwTable::PvtwTable(std::initializer_list records) + : table_(records) +{} + +// ------------------------------------------------------------------------ + template< typename T > FlatTable< T >::FlatTable( const DeckKeyword& kw ) : std::vector< T >( flat_records< T >( kw, mkseq< T::size >{} ) ) @@ -1557,7 +1617,6 @@ FlatTable< T >::FlatTable( const DeckKeyword& kw ) : template FlatTable< DENSITYRecord >::FlatTable( const DeckKeyword& ); template FlatTable< GRAVITYRecord >::FlatTable( const DeckKeyword& ); template FlatTable< DiffCoeffRecord >::FlatTable( const DeckKeyword& ); -template FlatTable< PVTWRecord >::FlatTable( const DeckKeyword& ); template FlatTable< PVCDORecord >::FlatTable( const DeckKeyword& ); template FlatTable< ROCKRecord >::FlatTable( const DeckKeyword& ); template FlatTable< PlyvmhRecord >::FlatTable( const DeckKeyword& ); diff --git a/tests/parser/TableManagerTests.cpp b/tests/parser/TableManagerTests.cpp index 54934ac25..70af15abf 100644 --- a/tests/parser/TableManagerTests.cpp +++ b/tests/parser/TableManagerTests.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -653,6 +654,62 @@ BOOST_AUTO_TEST_CASE(FoammobTable_Tests) { +BOOST_AUTO_TEST_CASE(PvtwTable_Tests) { + // PVT tables from opm-tests/model5/include/pvt_live_oil_dgas.ecl . + const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC +OIL +WATER +TABDIMS +1 2 / +PROPS +DENSITY + 924.1 1026.0 1.03446 / + 924.1 1026.0 1.03446 / +PVTW + 79.0 1.02643 0.37876E-04 0.39831 0.74714E-04 / +/ +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& pvtw = tmgr.getPvtwTable(); + BOOST_REQUIRE_EQUAL(pvtw.size(), std::size_t{2}); + + { + const auto& t1 = pvtw[0]; + BOOST_CHECK_CLOSE(t1.reference_pressure, 7.9e6, 1.0e-8); + BOOST_CHECK_CLOSE(t1.volume_factor, 1.02643, 1.0e-8); + BOOST_CHECK_CLOSE(t1.compressibility, 0.37876e-9, 1.0e-8); + BOOST_CHECK_CLOSE(t1.viscosity, 0.39831e-3, 1.0e-8); + BOOST_CHECK_CLOSE(t1.viscosibility, 0.74714e-9, 1.0e-9); + } + + { + const auto& t2 = pvtw[1]; + BOOST_CHECK_CLOSE(t2.reference_pressure, 7.9e6, 1.0e-8); + BOOST_CHECK_CLOSE(t2.volume_factor, 1.02643, 1.0e-8); + BOOST_CHECK_CLOSE(t2.compressibility, 0.37876e-9, 1.0e-8); + BOOST_CHECK_CLOSE(t2.viscosity, 0.39831e-3, 1.0e-8); + BOOST_CHECK_CLOSE(t2.viscosibility, 0.74714e-9, 1.0e-9); + } + + const auto& dens = tmgr.getDensityTable(); + BOOST_REQUIRE_EQUAL(dens.size(), std::size_t{2}); + + { + const auto& t1 = dens[0]; + BOOST_CHECK_CLOSE(t1.oil, 924.1, 1.0e-8); + BOOST_CHECK_CLOSE(t1.gas, 1.03446, 1.0e-8); + BOOST_CHECK_CLOSE(t1.water, 1026.0, 1.0e-8); + } + + { + const auto& t2 = dens[1]; + BOOST_CHECK_CLOSE(t2.oil, 924.1, 1.0e-8); + BOOST_CHECK_CLOSE(t2.gas, 1.03446, 1.0e-8); + BOOST_CHECK_CLOSE(t2.water, 1026.0, 1.0e-8); + } +} /** * Tests "happy path" for a VFPPROD table, i.e., when everything goes well