diff --git a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp index 566c74767..49943dc27 100644 --- a/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp +++ b/opm/input/eclipse/EclipseState/Tables/FlatTable.hpp @@ -1,6 +1,11 @@ #ifndef OPM_FLAT_TABLE_HPP #define OPM_FLAT_TABLE_HPP +#include +#include +#include +#include + namespace Opm { class DeckKeyword; @@ -20,6 +25,85 @@ 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; + + double oil_api; + double water_sg; + double gas_sg; + + bool operator==(const GRAVITYRecord& data) const { + return this->oil_api == data.oil_api && + this->water_sg == data.water_sg && + this->gas_sg == data.gas_sg; + } + + template + void serializeOp(Serializer& serializer) + { + serializer(this->oil_api); + serializer(this->water_sg); + serializer(this->gas_sg); + } +}; + +struct GravityTable : public FlatTableWithCopy +{ + GravityTable() = default; + explicit GravityTable(const DeckKeyword& kw); + explicit GravityTable(std::initializer_list records); + + static GravityTable serializeObject() + { + return GravityTable({{1.0, 2.0, 3.0}}); + } + + template + void serializeOp(Serializer& serializer) + { + FlatTableWithCopy::serializeOp(serializer); + } +}; + struct DENSITYRecord { static constexpr std::size_t size = 3; @@ -42,43 +126,22 @@ struct DENSITYRecord { } }; -struct DensityTable : public FlatTable< DENSITYRecord > { - using FlatTable< DENSITYRecord >::FlatTable; +struct DensityTable : public FlatTableWithCopy +{ + DensityTable() = default; + explicit DensityTable(const DeckKeyword& kw); + explicit DensityTable(const GravityTable& gravity); + explicit DensityTable(std::initializer_list records); static DensityTable serializeObject() { return DensityTable({{1.0, 2.0, 3.0}}); } -}; -struct GRAVITYRecord { - static constexpr std::size_t size = 3; - - double oil_api; - double water_sg; - double gas_sg; - - bool operator==(const GRAVITYRecord& data) const { - return this->oil_api == data.oil_api && - this->water_sg == data.water_sg && - this->gas_sg == data.gas_sg; - } - - template + template void serializeOp(Serializer& serializer) { - serializer(this->oil_api); - serializer(this->water_sg); - serializer(this->gas_sg); - } -}; - -struct GravityTable : public FlatTable< GRAVITYRecord > { - using FlatTable< GRAVITYRecord >::FlatTable; - - static GravityTable serializeObject() - { - return GravityTable({{1.0, 2.0, 3.0}}); + FlatTableWithCopy::serializeOp(serializer); } }; @@ -156,13 +219,22 @@ struct PVTWRecord { } }; -struct PvtwTable : public FlatTable< PVTWRecord > { - using FlatTable< PVTWRecord >::FlatTable; +struct PvtwTable : public FlatTableWithCopy +{ + PvtwTable() = default; + explicit PvtwTable(const DeckKeyword& kw); + explicit PvtwTable(std::initializer_list records); static PvtwTable serializeObject() { return PvtwTable({{1.0, 2.0, 3.0, 4.0, 5.0}}); } + + template + void serializeOp(Serializer& serializer) + { + FlatTableWithCopy::serializeOp(serializer); + } }; struct ROCKRecord { 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/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp b/src/opm/input/eclipse/EclipseState/Tables/TableManager.cpp index 75fe4c083..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() ); @@ -1587,12 +1557,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/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp b/src/opm/input/eclipse/EclipseState/Tables/Tables.cpp index 4c0da6c03..f14901795 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 @@ -86,6 +89,17 @@ #include #include +#include + +#include +#include +#include +#include + +#include + +#include + namespace Opm { PvtgTable::PvtgTable( const DeckKeyword& keyword, size_t tableIdx ) : @@ -1547,17 +1561,124 @@ 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 + +// ------------------------------------------------------------------------ + +template +FlatTableWithCopy::FlatTableWithCopy(const DeckKeyword& kw, + std::string_view expect) +{ + if (!expect.empty() && (kw.name() != expect)) { + throw std::invalid_argument { + fmt::format("Keyword {} cannot be used to " + "initialise {} table structures", kw.name(), expect) + }; + } + + this->table_.reserve(kw.size()); + + for (const auto& record : kw) { + if (all_defaulted(record)) { + // 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", + kw.location() + }; + } + + this->table_.push_back(this->table_.back()); + } + else { + this->table_.push_back(flat_get(record, mkseq{})); + } + } +} + +template +FlatTableWithCopy::FlatTableWithCopy(std::initializer_list records) + : table_{ records } +{} + +// ------------------------------------------------------------------------ + +GravityTable::GravityTable(const DeckKeyword& kw) + : FlatTableWithCopy(kw, ParserKeywords::GRAVITY::keywordName) +{} + +GravityTable::GravityTable(std::initializer_list records) + : FlatTableWithCopy(records) +{} + +// ------------------------------------------------------------------------ + +DensityTable::DensityTable(const DeckKeyword& kw) + : FlatTableWithCopy(kw, ParserKeywords::DENSITY::keywordName) +{} + +DensityTable::DensityTable(std::initializer_list records) + : FlatTableWithCopy(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 + }; + }); +} + +// ------------------------------------------------------------------------ + +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 >{} ) ) {} -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..54c59b444 100644 --- a/tests/parser/TableManagerTests.cpp +++ b/tests/parser/TableManagerTests.cpp @@ -38,10 +38,16 @@ #include #include #include +#include #include #include #include #include +#include +#include +#include +#include +#include #include #include @@ -651,8 +657,635 @@ 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(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 . + 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); + } +} + +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