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] 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 */