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 1/5] 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 From a066d2b95f00ec61b57d7fb2f4325909dd8975fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 20 Jun 2022 14:25:29 +0200 Subject: [PATCH 2/5] Implement Defaulted Table Copy for PVDO and PVDG --- .../EclipseState/Tables/TableManager.cpp | 12 ++ tests/parser/TableManagerTests.cpp | 146 ++++++++++++++++++ 2 files changed, 158 insertions(+) diff --git a/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp b/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp index 75fe4c083..33906cae8 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp @@ -1587,12 +1587,24 @@ DensityTable make_density_table(const GravityTable& gravity) { return; } + auto lastComplete = 0 * numTables; const auto& tableKeyword = deck[keywordName].back(); for (size_t tableIdx = 0; tableIdx < tableKeyword.size(); ++tableIdx) { const auto& dataItem = tableKeyword.getRecord( tableIdx ).getItem("DATA"); if (dataItem.data_size() > 0) { std::shared_ptr table = std::make_shared( dataItem, tableIdx ); container.addTable( tableIdx , table ); + lastComplete = tableIdx; + } + else if (tableIdx > static_cast(0)) { + const auto& item = tableKeyword.getRecord(lastComplete).getItem("DATA"); + container.addTable(tableIdx, std::make_shared(item, tableIdx)); + } + else { + throw OpmInputError { + fmt::format("Cannot default region {}'s table data", tableIdx + 1), + tableKeyword.location() + }; } } } diff --git a/tests/parser/TableManagerTests.cpp b/tests/parser/TableManagerTests.cpp index 70af15abf..0f568e200 100644 --- a/tests/parser/TableManagerTests.cpp +++ b/tests/parser/TableManagerTests.cpp @@ -43,6 +43,8 @@ #include #include #include +#include +#include #include #include @@ -652,6 +654,150 @@ BOOST_AUTO_TEST_CASE(FoammobTable_Tests) { } } +BOOST_AUTO_TEST_CASE(PvdoTable_Tests) { + // PVDO tables from opm-tests/model6/0_BASE_MODEL6.DATA . + 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 / +PVDO + 23.0 1.10770 52.630 + 27.5 1.08610 53.660 + 32.1 1.06460 54.730 + 50.0 1.06350 58.940 +/ +/ -- Copied from table 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& pvdo = tmgr.getPvdoTables(); + BOOST_REQUIRE_EQUAL(pvdo.size(), std::size_t{2}); + + { + const auto& t1 = pvdo.getTable(0); + + const auto& p = t1.getPressureColumn(); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(p[0], 2.3e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 2.75e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 3.21e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[3], 5.0e6, 1.0e-8); + + const auto& B = t1.getFormationFactorColumn(); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(B[0], 1.10770, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.08610, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.06460, 1.0e-8); + BOOST_CHECK_CLOSE(B[3], 1.06350, 1.0e-8); + + const auto& mu = t1.getViscosityColumn(); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(mu[0], 52.630e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 53.660e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 54.730e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[3], 58.940e-3, 1.0e-8); + } + + { + const auto& t2 = pvdo.getTable(1); + + const auto& p = t2.getPressureColumn(); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(p[0], 2.3e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 2.75e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 3.21e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[3], 5.0e6, 1.0e-8); + + const auto& B = t2.getFormationFactorColumn(); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(B[0], 1.10770, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.08610, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.06460, 1.0e-8); + BOOST_CHECK_CLOSE(B[3], 1.06350, 1.0e-8); + + const auto& mu = t2.getViscosityColumn(); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{4}); + BOOST_CHECK_CLOSE(mu[0], 52.630e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 53.660e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 54.730e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[3], 58.940e-3, 1.0e-8); + } +} + +BOOST_AUTO_TEST_CASE(PvdgTable_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 / +PVDG +-- Table number: 1 + 10.0000 0.266161 0.0108 + 15.0000 0.127259 0.0116 + 25.0000 0.062022 0.0123 / +/ -- Copied from table 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& pvdg = tmgr.getPvdgTables(); + BOOST_REQUIRE_EQUAL(pvdg.size(), std::size_t{2}); + + { + const auto& t1 = pvdg.getTable(0); + + const auto& p = t1.getPressureColumn(); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 2.5e6, 1.0e-8); + + const auto& B = t1.getFormationFactorColumn(); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.266161, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.127259, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.062022, 1.0e-8); + + const auto& mu = t1.getViscosityColumn(); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.0108e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.0116e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.0123e-3, 1.0e-8); + } + + { + const auto& t2 = pvdg.getTable(1); + + const auto& p = t2.getPressureColumn(); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 2.5e6, 1.0e-8); + + const auto& B = t2.getFormationFactorColumn(); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.266161, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.127259, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.062022, 1.0e-8); + + const auto& mu = t2.getViscosityColumn(); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.0108e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.0116e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.0123e-3, 1.0e-8); + } +} BOOST_AUTO_TEST_CASE(PvtwTable_Tests) { From 54ba23d71bda4f650ca0040ccfc311816f6e2624 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 20 Jun 2022 14:28:20 +0200 Subject: [PATCH 3/5] Implement Defaulted Table Copy for PVTO and PVTG The existing mechanism did not handle this case sufficiently well. It is likely that we will have to polish this approach, but for now use an explicit table copy to ensure that we have structurally correct backing table data. --- .../eclipse/EclipseState/Tables/PvtxTable.cpp | 45 ++- tests/parser/TableManagerTests.cpp | 366 ++++++++++++++++++ 2 files changed, 405 insertions(+), 6 deletions(-) diff --git a/src/opm/input/eclipse/EclipseState/Tables/PvtxTable.cpp b/src/opm/input/eclipse/EclipseState/Tables/PvtxTable.cpp index e83b7c672..a6875d234 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/PvtxTable.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/PvtxTable.cpp @@ -17,13 +17,25 @@ along with OPM. If not, see . */ +#include + +#include +#include +#include + #include #include #include -#include -#include -#include -#include + +#include + +#include +#include +#include + +#include + +#include namespace Opm { @@ -52,11 +64,32 @@ namespace Opm { have been explicitly set before calling this method. */ - void PvtxTable::init( const DeckKeyword& keyword, size_t tableIdx) { - auto ranges = recordRanges( keyword ); + void PvtxTable::init( const DeckKeyword& keyword, const size_t tableIdx0) { + const auto ranges = recordRanges( keyword ); + auto tableIdx = tableIdx0; + if (tableIdx >= ranges.size()) throw std::invalid_argument("Asked for table: " + std::to_string( tableIdx ) + " in keyword + " + keyword.name() + " which only has " + std::to_string( ranges.size() ) + " tables"); + auto isempty = [&ranges](const size_t ix) + { + const auto& [begin, end] = ranges[ix]; + return begin == end; + }; + + if ((tableIdx == size_t{0}) && isempty(tableIdx)) { + throw OpmInputError { + "Cannot default region 1's table data", + keyword.location() + }; + } + + // Locate source table for this region. Last non-empty table up to + // and including 'tableIdx0'. + while ((tableIdx > size_t{0}) && isempty(tableIdx)) { + --tableIdx; + } + { auto range = ranges[ tableIdx ]; for (size_t rowIdx = range.first; rowIdx < range.second; rowIdx++) { diff --git a/tests/parser/TableManagerTests.cpp b/tests/parser/TableManagerTests.cpp index 0f568e200..4ef6299e2 100644 --- a/tests/parser/TableManagerTests.cpp +++ b/tests/parser/TableManagerTests.cpp @@ -45,6 +45,9 @@ #include #include #include +#include +#include +#include #include #include @@ -799,6 +802,369 @@ END } } +BOOST_AUTO_TEST_CASE(PvtoTable_Tests) { + // PVT tables from opm-tests/model5/include/pvt_live_oil_dgas.ecl . + const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC +OIL +GAS +TABDIMS +1 2 / +PROPS +DENSITY + 924.1 1026.0 1.03446 / + 924.1 1026.0 1.03446 / +PVTO +-- Table number: 1 + 3.9140 10.000 1.102358 2.8625 + 15.000 1.101766 2.9007 + 25.000 1.100611 2.9695 / + + 7.0500 15.000 1.112540 2.6589 + 25.000 1.111313 2.7221 + 45.000 1.108952 2.8374 / +/ +/ -- Copied from region 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& pvto = tmgr.getPvtoTables(); + BOOST_REQUIRE_EQUAL(pvto.size(), std::size_t{2}); + + { + const auto& t1 = pvto[0]; + + BOOST_REQUIRE_EQUAL(t1.size(), std::size_t{2}); + + const auto& satTbl = t1.getSaturatedTable(); + { + BOOST_REQUIRE_EQUAL(satTbl.numRows(), std::size_t{2}); + BOOST_REQUIRE_EQUAL(satTbl.numColumns(), std::size_t{4}); + + const auto& rs = satTbl.getColumn(0); + BOOST_CHECK_CLOSE(rs[0], 3.914, 1.0e-8); + BOOST_CHECK_CLOSE(rs[1], 7.05, 1.0e-8); + + const auto& p = satTbl.getColumn(1); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + + const auto& B = satTbl.getColumn(2); + BOOST_CHECK_CLOSE(B[0], 1.102358, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.11254, 1.0e-8); + + const auto& mu = satTbl.getColumn(3); + BOOST_CHECK_CLOSE(mu[0], 2.8625e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.6589e-3, 1.0e-8); + } + + { + const auto& u1 = t1.getUnderSaturatedTable(0); + BOOST_REQUIRE_EQUAL(u1.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u1.numColumns(), std::size_t{3}); + + const auto& p = u1.getColumn(0); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 2.5e6, 1.0e-8); + + const auto& B = u1.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 1.102358, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.101766, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.100611, 1.0e-8); + + const auto& mu = u1.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 2.8625e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.9007e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 2.9695e-3, 1.0e-8); + } + + { + const auto& u2 = t1.getUnderSaturatedTable(1); + BOOST_REQUIRE_EQUAL(u2.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u2.numColumns(), std::size_t{3}); + + const auto& p = u2.getColumn(0); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 2.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 4.5e6, 1.0e-8); + + const auto& B = u2.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 1.112540, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.111313, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.108952, 1.0e-8); + + const auto& mu = u2.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 2.6589e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.7221e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 2.8374e-3, 1.0e-8); + } + } + + { + const auto& t2 = pvto[1]; + + BOOST_REQUIRE_EQUAL(t2.size(), std::size_t{2}); + + const auto& satTbl = t2.getSaturatedTable(); + { + BOOST_REQUIRE_EQUAL(satTbl.numRows(), std::size_t{2}); + BOOST_REQUIRE_EQUAL(satTbl.numColumns(), std::size_t{4}); + + const auto& rs = satTbl.getColumn(0); + BOOST_CHECK_CLOSE(rs[0], 3.914, 1.0e-8); + BOOST_CHECK_CLOSE(rs[1], 7.05, 1.0e-8); + + const auto& p = satTbl.getColumn(1); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + + const auto& B = satTbl.getColumn(2); + BOOST_CHECK_CLOSE(B[0], 1.102358, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.11254, 1.0e-8); + + const auto& mu = satTbl.getColumn(3); + BOOST_CHECK_CLOSE(mu[0], 2.8625e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.6589e-3, 1.0e-8); + } + + { + const auto& u1 = t2.getUnderSaturatedTable(0); + BOOST_REQUIRE_EQUAL(u1.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u1.numColumns(), std::size_t{3}); + + const auto& p = u1.getColumn(0); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 2.5e6, 1.0e-8); + + const auto& B = u1.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 1.102358, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.101766, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.100611, 1.0e-8); + + const auto& mu = u1.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 2.8625e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.9007e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 2.9695e-3, 1.0e-8); + } + + { + const auto& u2 = t2.getUnderSaturatedTable(1); + BOOST_REQUIRE_EQUAL(u2.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u2.numColumns(), std::size_t{3}); + + const auto& p = u2.getColumn(0); + BOOST_REQUIRE_EQUAL(p.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(p[0], 1.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 2.5e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[2], 4.5e6, 1.0e-8); + + const auto& B = u2.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 1.112540, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 1.111313, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 1.108952, 1.0e-8); + + const auto& mu = u2.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 2.6589e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 2.7221e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 2.8374e-3, 1.0e-8); + } + } +} + +BOOST_AUTO_TEST_CASE(PvtgTable_Tests) { + // PVT tables from opm-tests/norne/INCLUDE/PVT/PVT-WET-GAS.INC . + const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC +OIL +GAS +TABDIMS +1 2 / +PROPS +DENSITY + 924.1 1026.0 1.03446 / + 924.1 1026.0 1.03446 / +PVTG +-- Table number: 1 + 50.00 0.00000497 0.024958 0.01441 + 0.00000248 0.024958 0.01440 + 0.00000000 0.024958 0.01440 / + + 70.00 0.00000521 0.017639 0.01491 + 0.00000261 0.017641 0.01490 + 0.00000000 0.017643 0.01490 / +/ +/ -- Copied from region 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& pvtg = tmgr.getPvtgTables(); + BOOST_REQUIRE_EQUAL(pvtg.size(), std::size_t{2}); + + { + const auto& t1 = pvtg[0]; + + BOOST_REQUIRE_EQUAL(t1.size(), std::size_t{2}); + + const auto& satTbl = t1.getSaturatedTable(); + { + BOOST_REQUIRE_EQUAL(satTbl.numRows(), std::size_t{2}); + BOOST_REQUIRE_EQUAL(satTbl.numColumns(), std::size_t{4}); + + const auto& p = satTbl.getColumn(0); + BOOST_CHECK_CLOSE(p[0], 5.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 7.0e6, 1.0e-8); + + const auto& rv = satTbl.getColumn(1); + BOOST_CHECK_CLOSE(rv[0], 0.00000497, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000521, 1.0e-8); + + const auto& B = satTbl.getColumn(2); + BOOST_CHECK_CLOSE(B[0], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.017639, 1.0e-8); + + const auto& mu = satTbl.getColumn(3); + BOOST_CHECK_CLOSE(mu[0], 0.01441e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01491e-3, 1.0e-8); + } + + { + const auto& u1 = t1.getUnderSaturatedTable(0); + BOOST_REQUIRE_EQUAL(u1.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u1.numColumns(), std::size_t{3}); + + const auto& rv = u1.getColumn(0); + BOOST_REQUIRE_EQUAL(rv.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(rv[0], 0.00000497, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000248, 1.0e-8); + BOOST_CHECK_CLOSE(rv[2], 0.00000000, 1.0e-8); + + const auto& B = u1.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.024958, 1.0e-8); + + const auto& mu = u1.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.01441e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01440e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.01440e-3, 1.0e-8); + } + + { + const auto& u2 = t1.getUnderSaturatedTable(1); + BOOST_REQUIRE_EQUAL(u2.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u2.numColumns(), std::size_t{3}); + + const auto& rv = u2.getColumn(0); + BOOST_REQUIRE_EQUAL(rv.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(rv[0], 0.00000521, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000261, 1.0e-8); + BOOST_CHECK_CLOSE(rv[2], 0.00000000, 1.0e-8); + + const auto& B = u2.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.017639, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.017641, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.017643, 1.0e-8); + + const auto& mu = u2.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.01491e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01490e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.01490e-3, 1.0e-8); + } + } + + { + const auto& t2 = pvtg[1]; + + BOOST_REQUIRE_EQUAL(t2.size(), std::size_t{2}); + + const auto& satTbl = t2.getSaturatedTable(); + { + BOOST_REQUIRE_EQUAL(satTbl.numRows(), std::size_t{2}); + BOOST_REQUIRE_EQUAL(satTbl.numColumns(), std::size_t{4}); + + const auto& p = satTbl.getColumn(0); + BOOST_CHECK_CLOSE(p[0], 5.0e6, 1.0e-8); + BOOST_CHECK_CLOSE(p[1], 7.0e6, 1.0e-8); + + const auto& rv = satTbl.getColumn(1); + BOOST_CHECK_CLOSE(rv[0], 0.00000497, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000521, 1.0e-8); + + const auto& B = satTbl.getColumn(2); + BOOST_CHECK_CLOSE(B[0], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.017639, 1.0e-8); + + const auto& mu = satTbl.getColumn(3); + BOOST_CHECK_CLOSE(mu[0], 0.01441e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01491e-3, 1.0e-8); + } + + { + const auto& u1 = t2.getUnderSaturatedTable(0); + BOOST_REQUIRE_EQUAL(u1.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u1.numColumns(), std::size_t{3}); + + const auto& rv = u1.getColumn(0); + BOOST_REQUIRE_EQUAL(rv.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(rv[0], 0.00000497, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000248, 1.0e-8); + BOOST_CHECK_CLOSE(rv[2], 0.00000000, 1.0e-8); + + const auto& B = u1.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.024958, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.024958, 1.0e-8); + + const auto& mu = u1.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.01441e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01440e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.01440e-3, 1.0e-8); + } + + { + const auto& u2 = t2.getUnderSaturatedTable(1); + BOOST_REQUIRE_EQUAL(u2.numRows(), std::size_t{3}); + BOOST_REQUIRE_EQUAL(u2.numColumns(), std::size_t{3}); + + const auto& rv = u2.getColumn(0); + BOOST_REQUIRE_EQUAL(rv.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(rv[0], 0.00000521, 1.0e-8); + BOOST_CHECK_CLOSE(rv[1], 0.00000261, 1.0e-8); + BOOST_CHECK_CLOSE(rv[2], 0.00000000, 1.0e-8); + + const auto& B = u2.getColumn(1); + BOOST_REQUIRE_EQUAL(B.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(B[0], 0.017639, 1.0e-8); + BOOST_CHECK_CLOSE(B[1], 0.017641, 1.0e-8); + BOOST_CHECK_CLOSE(B[2], 0.017643, 1.0e-8); + + const auto& mu = u2.getColumn(2); + BOOST_REQUIRE_EQUAL(mu.size(), std::size_t{3}); + BOOST_CHECK_CLOSE(mu[0], 0.01491e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[1], 0.01490e-3, 1.0e-8); + BOOST_CHECK_CLOSE(mu[2], 0.01490e-3, 1.0e-8); + } + } +} BOOST_AUTO_TEST_CASE(PvtwTable_Tests) { // PVT tables from opm-tests/model5/include/pvt_live_oil_dgas.ecl . From d35abfddb6881f782cd45ef13defe81d289c86e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Mon, 20 Jun 2022 16:29:48 +0200 Subject: [PATCH 4/5] Implement Defaulted Table Copy for DENSITY and GRAVITY Use the same targeted strategy as for PVTW. --- .../eclipse/EclipseState/Tables/FlatTable.hpp | 133 +++++++++++++----- .../EclipseState/Tables/TableManager.cpp | 32 +---- .../eclipse/EclipseState/Tables/Tables.cpp | 109 +++++++++++++- tests/parser/TableManagerTests.cpp | 64 +++++++++ 4 files changed, 272 insertions(+), 66 deletions(-) diff --git a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp index fa021c18e..81e372983 100644 --- a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp +++ b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp @@ -24,37 +24,6 @@ struct FlatTable : public std::vector< T > { } }; -struct DENSITYRecord { - static constexpr std::size_t size = 3; - - double oil; - double water; - double gas; - - bool operator==(const DENSITYRecord& data) const { - return oil == data.oil && - water == data.water && - gas == data.gas; - } - - template - void serializeOp(Serializer& serializer) - { - serializer(oil); - serializer(water); - serializer(gas); - } -}; - -struct DensityTable : public FlatTable< DENSITYRecord > { - using FlatTable< DENSITYRecord >::FlatTable; - - static DensityTable serializeObject() - { - return DensityTable({{1.0, 2.0, 3.0}}); - } -}; - struct GRAVITYRecord { static constexpr std::size_t size = 3; @@ -77,13 +46,111 @@ struct GRAVITYRecord { } }; -struct GravityTable : public FlatTable< GRAVITYRecord > { - using FlatTable< GRAVITYRecord >::FlatTable; +class GravityTable +{ +public: + GravityTable() = default; + explicit GravityTable(const DeckKeyword& kw); + explicit GravityTable(std::initializer_list records); + + auto size() const { return this->table_.size(); } + bool empty() const { return this->table_.empty(); } + auto begin() const { return this->table_.begin(); } + auto end() const { return this->table_.end(); } + + const GRAVITYRecord& operator[](const std::size_t tableID) const + { + return this->table_[tableID]; + } + + const GRAVITYRecord& at(const std::size_t tableID) const + { + return this->table_.at(tableID); + } + + bool operator==(const GravityTable& other) const + { + return this->table_ == other.table_; + } static GravityTable serializeObject() { return GravityTable({{1.0, 2.0, 3.0}}); } + + template + void serializeOp(Serializer& serializer) + { + serializer.vector(this->table_); + } + +private: + std::vector table_{}; +}; + +struct DENSITYRecord { + static constexpr std::size_t size = 3; + + double oil; + double water; + double gas; + + bool operator==(const DENSITYRecord& data) const { + return oil == data.oil && + water == data.water && + gas == data.gas; + } + + template + void serializeOp(Serializer& serializer) + { + serializer(oil); + serializer(water); + serializer(gas); + } +}; + +class DensityTable +{ +public: + DensityTable() = default; + explicit DensityTable(const DeckKeyword& kw); + explicit DensityTable(std::initializer_list records); + explicit DensityTable(const GravityTable& gravity); + + auto size() const { return this->table_.size(); } + bool empty() const { return this->table_.empty(); } + auto begin() const { return this->table_.begin(); } + auto end() const { return this->table_.end(); } + + const DENSITYRecord& operator[](const std::size_t tableID) const + { + return this->table_[tableID]; + } + + const DENSITYRecord& at(const std::size_t tableID) const + { + return this->table_.at(tableID); + } + + bool operator==(const DensityTable& other) const + { + return this->table_ == other.table_; + } + + static DensityTable serializeObject() + { + return DensityTable({{1.0, 2.0, 3.0}}); + } + + template + void serializeOp(Serializer& serializer) + { + serializer.vector(this->table_); + } + +private: + std::vector table_{}; }; struct DiffCoeffRecord { diff --git a/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp b/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp index 33906cae8..7db4588cf 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp @@ -123,36 +123,6 @@ std::optional make_jfunc(const Deck& deck) { return JFunc(deck); } -DensityTable make_density_table(const GravityTable& gravity) { - auto rho = DensityTable{}; - rho.reserve(gravity.size()); - - constexpr auto default_air_density = - 1.22 * unit::kilogram / unit::cubic(unit::meter); - - constexpr auto default_water_density = - 1000.0 * unit::kilogram / unit::cubic(unit::meter); - - // Degrees API defined as - // - // API = (141.5 / SG) - 131.5 - // - // with SG being the specific gravity of oil relative to pure water. - - std::transform(gravity.begin(), gravity.end(), - std::back_inserter(rho), - [](const GRAVITYRecord& record) - { - return DENSITYRecord { - (141.5 / (record.oil_api + 131.5)) * default_water_density, - record.water_sg * default_water_density, - record.gas_sg * default_air_density - }; - }); - - return rho; -} - } @@ -197,7 +167,7 @@ DensityTable make_density_table(const GravityTable& gravity) { this->m_densityTable = DensityTable( deck["DENSITY"].back() ); else if( deck.hasKeyword( "GRAVITY" ) ) - this->m_densityTable = make_density_table( GravityTable ( deck["GRAVITY"].back() ) ); + this->m_densityTable = DensityTable( GravityTable ( deck["GRAVITY"].back() ) ); if( deck.hasKeyword( "DIFFC" ) ) this->m_diffCoeffTable = DiffCoeffTable( deck["DIFFC"].back() ); diff --git a/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp index 67c54060c..0a9a77c30 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp @@ -24,10 +24,13 @@ #include #include #include +#include +#include #include #include #include #include +#include #include #include @@ -1609,13 +1612,115 @@ PvtwTable::PvtwTable(std::initializer_list records) // ------------------------------------------------------------------------ +GravityTable::GravityTable(const DeckKeyword& kw) +{ + if (kw.name() != ParserKeywords::GRAVITY::keywordName) { + throw std::invalid_argument { + fmt::format("Keyword {} cannot be used to " + "initialise {} table structures", kw.name(), + ParserKeywords::GRAVITY::keywordName) + }; + } + + this->table_.reserve(kw.size()); + + for (const auto& record : kw) { + if (all_defaulted(record)) { + // All-defaulted records imply GRAVITY in region R is equal to + // GRAVITY in region R-1. GRAVITY 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{})); + } + } +} + +GravityTable::GravityTable(std::initializer_list records) + : table_(records) +{} + +// ------------------------------------------------------------------------ + +DensityTable::DensityTable(const DeckKeyword& kw) +{ + if (kw.name() != ParserKeywords::DENSITY::keywordName) { + throw std::invalid_argument { + fmt::format("Keyword {} cannot be used to " + "initialise {} table structures", kw.name(), + ParserKeywords::DENSITY::keywordName) + }; + } + + this->table_.reserve(kw.size()); + + for (const auto& record : kw) { + if (all_defaulted(record)) { + // All-defaulted records imply DENSITY in region R is equal to + // DENSITY in region R-1. DENSITY 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{})); + } + } +} + +DensityTable::DensityTable(std::initializer_list records) + : table_(records) +{} + +DensityTable::DensityTable(const GravityTable& gravity) +{ + this->table_.reserve(gravity.size()); + + constexpr auto default_air_density = + 1.22 * unit::kilogram / unit::cubic(unit::meter); + + constexpr auto default_water_density = + 1000.0 * unit::kilogram / unit::cubic(unit::meter); + + // Degrees API defined as + // + // API = (141.5 / SG) - 131.5 + // + // with SG being the specific gravity of oil relative to pure water. + + std::transform(gravity.begin(), gravity.end(), + std::back_inserter(this->table_), + [](const GRAVITYRecord& record) + { + return DENSITYRecord { + (141.5 / (record.oil_api + 131.5)) * default_water_density, + record.water_sg * default_water_density, + record.gas_sg * default_air_density + }; + }); +} + +// ------------------------------------------------------------------------ + template< typename T > FlatTable< T >::FlatTable( const DeckKeyword& kw ) : std::vector< T >( flat_records< T >( kw, mkseq< T::size >{} ) ) {} -template FlatTable< DENSITYRecord >::FlatTable( const DeckKeyword& ); -template FlatTable< GRAVITYRecord >::FlatTable( const DeckKeyword& ); template FlatTable< DiffCoeffRecord >::FlatTable( const DeckKeyword& ); template FlatTable< PVCDORecord >::FlatTable( const DeckKeyword& ); template FlatTable< ROCKRecord >::FlatTable( const DeckKeyword& ); diff --git a/tests/parser/TableManagerTests.cpp b/tests/parser/TableManagerTests.cpp index 4ef6299e2..54c59b444 100644 --- a/tests/parser/TableManagerTests.cpp +++ b/tests/parser/TableManagerTests.cpp @@ -1223,6 +1223,70 @@ END } } +BOOST_AUTO_TEST_CASE(DensityTable_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 / +/ -- Copied from region 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + 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); + } +} + +BOOST_AUTO_TEST_CASE(GravityTable_Tests) { + const auto deck = Opm::Parser{}.parseString(R"(RUNSPEC +OIL +WATER +TABDIMS +1 2 / +GRAVITY + 12.34 1.2 1.21 / +/ -- Copied from region 1 +END +)"); + + const auto tmgr = Opm::TableManager { deck }; + const auto& dens = tmgr.getDensityTable(); + BOOST_REQUIRE_EQUAL(dens.size(), std::size_t{2}); + + { + const auto& t1 = dens[0]; + BOOST_CHECK_CLOSE( 983.731924360, t1.oil , 1.0e-8 ); + BOOST_CHECK_CLOSE( 1200.0 , t1.water, 1.0e-8 ); + BOOST_CHECK_CLOSE( 1.4762 , t1.gas , 1.0e-8 ); + } + + { + const auto& t2 = dens[1]; + BOOST_CHECK_CLOSE( 983.731924360, t2.oil , 1.0e-8 ); + BOOST_CHECK_CLOSE( 1200.0 , t2.water, 1.0e-8 ); + BOOST_CHECK_CLOSE( 1.4762 , t2.gas , 1.0e-8 ); + } +} + /** * Tests "happy path" for a VFPPROD table, i.e., when everything goes well */ From 254b2e28625d8d8e31c94972c643e144fee4e495 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 24 Jun 2022 12:56:08 +0200 Subject: [PATCH 5/5] Extract Common Base Class For Flat Tables With Record Copying This commit introduces a new helper template class, FlatTableWithCopy which wraps a vector, provides constructors from DeckKeyword and initializer_list and handles copying tables for all-defaulted records. We reimplement the PVTW, DENSITY, and GRAVITY tables in terms of this helper class to reduce duplication. If the copy behaviour is generally useful/needed we expect to replace the existing FlatTable mechanism with the new helper. Suggested by: Markus Blatt --- .../eclipse/EclipseState/Tables/FlatTable.hpp | 124 +++++++----------- .../eclipse/EclipseState/Tables/Tables.cpp | 101 ++++---------- 2 files changed, 76 insertions(+), 149 deletions(-) diff --git a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp index 81e372983..49943dc27 100644 --- a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp +++ b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp @@ -3,6 +3,7 @@ #include #include +#include #include namespace Opm { @@ -24,6 +25,45 @@ struct FlatTable : public std::vector< T > { } }; +template +class FlatTableWithCopy +{ +public: + FlatTableWithCopy() = default; + explicit FlatTableWithCopy(const DeckKeyword& kw, + std::string_view expect = ""); + explicit FlatTableWithCopy(std::initializer_list records); + + auto size() const { return this->table_.size(); } + bool empty() const { return this->table_.empty(); } + auto begin() const { return this->table_.begin(); } + auto end() const { return this->table_.end(); } + + const RecordType& operator[](const std::size_t tableID) const + { + return this->table_[tableID]; + } + + const RecordType& at(const std::size_t tableID) const + { + return this->table_.at(tableID); + } + + bool operator==(const FlatTableWithCopy& other) const + { + return this->table_ == other.table_; + } + + template + void serializeOp(Serializer& serializer) + { + serializer.vector(this->table_); + } + +protected: + std::vector table_{}; +}; + struct GRAVITYRecord { static constexpr std::size_t size = 3; @@ -46,33 +86,12 @@ struct GRAVITYRecord { } }; -class GravityTable +struct GravityTable : public FlatTableWithCopy { -public: GravityTable() = default; explicit GravityTable(const DeckKeyword& kw); explicit GravityTable(std::initializer_list records); - auto size() const { return this->table_.size(); } - bool empty() const { return this->table_.empty(); } - auto begin() const { return this->table_.begin(); } - auto end() const { return this->table_.end(); } - - const GRAVITYRecord& operator[](const std::size_t tableID) const - { - return this->table_[tableID]; - } - - const GRAVITYRecord& at(const std::size_t tableID) const - { - return this->table_.at(tableID); - } - - bool operator==(const GravityTable& other) const - { - return this->table_ == other.table_; - } - static GravityTable serializeObject() { return GravityTable({{1.0, 2.0, 3.0}}); @@ -81,11 +100,8 @@ public: template void serializeOp(Serializer& serializer) { - serializer.vector(this->table_); + FlatTableWithCopy::serializeOp(serializer); } - -private: - std::vector table_{}; }; struct DENSITYRecord { @@ -110,33 +126,12 @@ struct DENSITYRecord { } }; -class DensityTable +struct DensityTable : public FlatTableWithCopy { -public: DensityTable() = default; explicit DensityTable(const DeckKeyword& kw); - explicit DensityTable(std::initializer_list records); explicit DensityTable(const GravityTable& gravity); - - auto size() const { return this->table_.size(); } - bool empty() const { return this->table_.empty(); } - auto begin() const { return this->table_.begin(); } - auto end() const { return this->table_.end(); } - - const DENSITYRecord& operator[](const std::size_t tableID) const - { - return this->table_[tableID]; - } - - const DENSITYRecord& at(const std::size_t tableID) const - { - return this->table_.at(tableID); - } - - bool operator==(const DensityTable& other) const - { - return this->table_ == other.table_; - } + explicit DensityTable(std::initializer_list records); static DensityTable serializeObject() { @@ -146,11 +141,8 @@ public: template void serializeOp(Serializer& serializer) { - serializer.vector(this->table_); + FlatTableWithCopy::serializeOp(serializer); } - -private: - std::vector table_{}; }; struct DiffCoeffRecord { @@ -227,31 +219,12 @@ struct PVTWRecord { } }; -class PvtwTable +struct PvtwTable : public FlatTableWithCopy { -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}}); @@ -260,11 +233,8 @@ public: template void serializeOp(Serializer& serializer) { - serializer.vector(this->table_); + FlatTableWithCopy::serializeOp(serializer); } - -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 0a9a77c30..f14901795 100644 --- a/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp +++ b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp @@ -94,6 +94,7 @@ #include #include #include +#include #include @@ -1574,13 +1575,14 @@ bool all_defaulted(const DeckRecord& record) // ------------------------------------------------------------------------ -PvtwTable::PvtwTable(const DeckKeyword& kw) +template +FlatTableWithCopy::FlatTableWithCopy(const DeckKeyword& kw, + std::string_view expect) { - if (kw.name() != ParserKeywords::PVTW::keywordName) { + if (!expect.empty() && (kw.name() != expect)) { throw std::invalid_argument { fmt::format("Keyword {} cannot be used to " - "initialise {} table structures", kw.name(), - ParserKeywords::PVTW::keywordName) + "initialise {} table structures", kw.name(), expect) }; } @@ -1588,9 +1590,9 @@ PvtwTable::PvtwTable(const DeckKeyword& kw) 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). + // All-defaulted records imply table in region R is equal to + // table in region R-1. Table must not be defaulted in region 1 + // (i.e., when PVTNUM=1). if (this->table_.empty()) { throw OpmInputError { "First record cannot be defaulted", @@ -1601,89 +1603,34 @@ PvtwTable::PvtwTable(const DeckKeyword& kw) this->table_.push_back(this->table_.back()); } else { - this->table_.push_back(flat_get(record, mkseq{})); + this->table_.push_back(flat_get(record, mkseq{})); } } } -PvtwTable::PvtwTable(std::initializer_list records) - : table_(records) +template +FlatTableWithCopy::FlatTableWithCopy(std::initializer_list records) + : table_{ records } {} // ------------------------------------------------------------------------ GravityTable::GravityTable(const DeckKeyword& kw) -{ - if (kw.name() != ParserKeywords::GRAVITY::keywordName) { - throw std::invalid_argument { - fmt::format("Keyword {} cannot be used to " - "initialise {} table structures", kw.name(), - ParserKeywords::GRAVITY::keywordName) - }; - } - - this->table_.reserve(kw.size()); - - for (const auto& record : kw) { - if (all_defaulted(record)) { - // All-defaulted records imply GRAVITY in region R is equal to - // GRAVITY in region R-1. GRAVITY 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{})); - } - } -} + : FlatTableWithCopy(kw, ParserKeywords::GRAVITY::keywordName) +{} GravityTable::GravityTable(std::initializer_list records) - : table_(records) + : FlatTableWithCopy(records) {} // ------------------------------------------------------------------------ DensityTable::DensityTable(const DeckKeyword& kw) -{ - if (kw.name() != ParserKeywords::DENSITY::keywordName) { - throw std::invalid_argument { - fmt::format("Keyword {} cannot be used to " - "initialise {} table structures", kw.name(), - ParserKeywords::DENSITY::keywordName) - }; - } - - this->table_.reserve(kw.size()); - - for (const auto& record : kw) { - if (all_defaulted(record)) { - // All-defaulted records imply DENSITY in region R is equal to - // DENSITY in region R-1. DENSITY 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{})); - } - } -} + : FlatTableWithCopy(kw, ParserKeywords::DENSITY::keywordName) +{} DensityTable::DensityTable(std::initializer_list records) - : table_(records) + : FlatTableWithCopy(records) {} DensityTable::DensityTable(const GravityTable& gravity) @@ -1716,6 +1663,16 @@ DensityTable::DensityTable(const GravityTable& gravity) // ------------------------------------------------------------------------ +PvtwTable::PvtwTable(const DeckKeyword& kw) + : FlatTableWithCopy(kw, ParserKeywords::PVTW::keywordName) +{} + +PvtwTable::PvtwTable(std::initializer_list records) + : FlatTableWithCopy(records) +{} + +// ------------------------------------------------------------------------ + template< typename T > FlatTable< T >::FlatTable( const DeckKeyword& kw ) : std::vector< T >( flat_records< T >( kw, mkseq< T::size >{} ) )