From bd73bb6c556d639f4416acb527d28bd7caab669a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 11:25:14 +0100 Subject: [PATCH 01/11] Linearised Table: Add Constructor with User-Defined Fill Value Reimplement original constructor in terms of the new constructor. The main purpose of the new constructor is to support PVT tables which use a fill/padding value different from 1.0e+20. --- opm/output/eclipse/LinearisedOutputTable.hpp | 30 +++++++++++++ .../output/eclipse/LinearisedOutputTable.cpp | 32 +++++++++++++- tests/test_LinearisedOutputTable.cpp | 42 +++++++++++++++++++ 3 files changed, 103 insertions(+), 1 deletion(-) diff --git a/opm/output/eclipse/LinearisedOutputTable.hpp b/opm/output/eclipse/LinearisedOutputTable.hpp index 337383794..11a0a9664 100644 --- a/opm/output/eclipse/LinearisedOutputTable.hpp +++ b/opm/output/eclipse/LinearisedOutputTable.hpp @@ -1,4 +1,5 @@ /* + Copyright 2019 Equinor. Copyright 2017 Statoil ASA. This file is part of the Open Porous Media project (OPM). @@ -35,6 +36,8 @@ namespace Opm { public: /// Constructor. /// + /// Padded table entries set to +1.0e+20. + /// /// \param[in] numTables Number of tables managed by internal /// buffer. Typically corresponds to maximum value of a region /// ID vector such as SATNUM, IMBNUM, or PVTNUM. @@ -57,6 +60,33 @@ namespace Opm { const std::size_t numRows, const std::size_t numCols); + /// Constructor. + /// + /// \param[in] numTables Number of tables managed by internal + /// buffer. Typically corresponds to maximum value of a region + /// ID vector such as SATNUM, IMBNUM, or PVTNUM. + /// + /// \param[in] numPrimary Number of primary look-up keys for the + /// tabular data managed by the internal buffer. Mostly relevant + /// (i.e., greater than one) for miscible oil ("PVTO") or + /// miscible gas ("PVTG") tables and typically corresponds to the + /// number of Rs/Rv entries from the TABDIMS keyword. + /// + /// \param[in] numRows Number of rows in each sub-table + /// corresponding to a single primary look-up key. Typically the + /// number of nodes (e.g., NSSFUN or NPPVT) of the corresponding + /// input keyword. + /// + /// \param[in] numCols Number of columns in each sub-table (and main + /// table). Typically 5. + /// + /// \param[in] fillValue Data element value for padded table entries. + LinearisedOutputTable(const std::size_t numTables, + const std::size_t numPrimary, + const std::size_t numRows, + const std::size_t numCols, + const double fillValue); + /// Retrieve iterator to start of \c numRows (contiguous) column /// elements of a particular sub-table of a particular main table. /// diff --git a/src/opm/output/eclipse/LinearisedOutputTable.cpp b/src/opm/output/eclipse/LinearisedOutputTable.cpp index 5f5c4e96a..3ddb7365b 100644 --- a/src/opm/output/eclipse/LinearisedOutputTable.cpp +++ b/src/opm/output/eclipse/LinearisedOutputTable.cpp @@ -1,3 +1,23 @@ +/* + Copyright 2019 Equinor. + Copyright 2017 Statoil ASA. + + This file is part of the Open Porous Media project (OPM). + + OPM is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OPM is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with OPM. If not, see . +*/ + #include #include @@ -10,7 +30,17 @@ LinearisedOutputTable(const std::size_t numTables0, const std::size_t numPrimary0, const std::size_t numRows0, const std::size_t numCols0) - : data (numTables0 * numPrimary0 * numRows0 * numCols0, 1.0e20) + : LinearisedOutputTable(numTables0, numPrimary0, + numRows0, numCols0, 1.0e20) +{} + +Opm::LinearisedOutputTable:: +LinearisedOutputTable(const std::size_t numTables0, + const std::size_t numPrimary0, + const std::size_t numRows0, + const std::size_t numCols0, + const double fillValue) + : data (numTables0 * numPrimary0 * numRows0 * numCols0, fillValue) , numTables (numTables0) , numPrimary(numPrimary0) , numRows (numRows0) diff --git a/tests/test_LinearisedOutputTable.cpp b/tests/test_LinearisedOutputTable.cpp index f8f0c6ddc..20e136eb8 100644 --- a/tests/test_LinearisedOutputTable.cpp +++ b/tests/test_LinearisedOutputTable.cpp @@ -125,6 +125,48 @@ namespace { } } // Anonymous +// --------------------------------------------------------------------- +// Constructors + +BOOST_AUTO_TEST_SUITE (Basic_Operations) + +BOOST_AUTO_TEST_CASE (Construct_Defaulted_FillVal) +{ + const auto numTables = std::size_t{2}; + const auto numPrimary = std::size_t{3}; + const auto numRows = std::size_t{4}; + const auto numCols = std::size_t{5}; + + auto linTable = ::Opm::LinearisedOutputTable { + numTables, numPrimary, numRows, numCols + }; + + const auto expect_initial = std::vector( + numTables * numPrimary * numRows * numCols, 1.0e20); + + check_is_close(linTable.getData(), expect_initial); +} + +BOOST_AUTO_TEST_CASE (Construct_UserDefined_FillVal) +{ + const auto numTables = std::size_t{2}; + const auto numPrimary = std::size_t{3}; + const auto numRows = std::size_t{4}; + const auto numCols = std::size_t{5}; + const auto fillVal = 1.234e-5; + + auto linTable = ::Opm::LinearisedOutputTable { + numTables, numPrimary, numRows, numCols, fillVal + }; + + const auto expect_initial = std::vector( + numTables * numPrimary * numRows * numCols, fillVal); + + check_is_close(linTable.getData(), expect_initial); +} + +BOOST_AUTO_TEST_SUITE_END () + // --------------------------------------------------------------------- // Saturation Functions From c279184229d35ca4f3e4130cb9fa2cab87f74cd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 11:43:27 +0100 Subject: [PATCH 02/11] Tables: Make Table Constructor More Generally Callable In particular, expose the internals of SatFunc::detail::createSatfuncTable() as a new free function, createPropfuncTable(). The latter knows about sub-tables as present in live oil (PVTO) and wet gas (PVTG) input data and will loop over all primary keys of a single table (i.e., region) before advancing to the next table. The primary purpose of createPropfuncTable() is to support creating the normalised (ECLIPSE 100 style) INIT file tables for PVT data. --- src/opm/output/eclipse/Tables.cpp | 122 ++++++++++++++++++++++++------ 1 file changed, 97 insertions(+), 25 deletions(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index e5d0760f8..802f8e862 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -1,4 +1,6 @@ /* + Copyright 2019 Equinor. + Copyright 2017 Statoil ASA. Copyright 2016 Statoil ASA. This file is part of the Open Porous Media project (OPM). @@ -43,6 +45,96 @@ #include #include +namespace { + /// Create linearised, padded TAB vector entries for a collection of + /// tabulated saturation functions corresponding to a single input + /// keyword. + /// + /// \tparam BuildDependent Callable entity that extracts the + /// independent and primary dependent variates of a single + /// saturation function table into a linearised output table. + /// Must implement a function call operator of the form + /// \code + /// std::size_t + /// operator()(const std::size_t tableID, + /// const std::size_t primID, + /// LinearisedOutputTable& table); + /// \endcode + /// that will assign the independent variate of the sub-table + /// primID within the table identified as tableID to column zero + /// of 'table' and all dependent variates to columns one &c of + /// 'table'. The function call operator must return the number + /// of active (used) rows within the sub-table through its return + /// value. + /// + /// \param[in] numTab Number of tables in this table collection. + /// + /// \param[in] numPrim Number of primary look-up keys for each table. + /// Mostly relevant (i.e., greater than one) for miscible oil + /// ("PVTO") or miscible gas ("PVTG") tables and typically + /// corresponds to the number of Rs/Rv entries from the TABDIMS + /// keyword. + /// + /// \param[in] numRows Number of rows to allocate for each padded + /// output table. + /// + /// \param[in] numDep Number of dependent variates (columns) in each + /// table of this collection of input tables. Total number of + /// columns in the result vector will be 1 + 2*numDep to account + /// for the independent variate, the dependent variates and the + /// derivatives of the dependent variates with respect to the + /// independent variate. + /// + /// \param[in] buildDeps Function object that implements the + /// protocol outlined for \code BuildDependent::operator()() + /// \endcode. Typically a lambda expression. + /// + /// \return Linearised, padded TAB vector entries for a collection of + /// tabulated property functions (e.g., saturation functions or PVT + /// functions) corresponding to a single input keyword. Derivatives + /// included as additional columns. + template + std::vector + createPropfuncTable(const std::size_t numTab, + const std::size_t numPrim, + const std::size_t numRows, + const std::size_t numDep, + const double fillVal, + BuildDependent&& buildDeps) + { + const auto numCols = 1 + 2*numDep; + + auto descr = ::Opm::DifferentiateOutputTable::Descriptor{}; + descr.primID = 0 * numPrim; + + auto linTable = ::Opm::LinearisedOutputTable { + numTab, numPrim, numRows, numCols, fillVal + }; + + for (descr.tableID = 0*numTab; + descr.tableID < 1*numTab; ++descr.tableID) + { + for (descr.primID = 0*numPrim; + descr.primID < 1*numPrim; ++descr.primID) + { + descr.numActRows = + buildDeps(descr.tableID, descr.primID, linTable); + + // Derivatives. Use values already stored in linTable to + // take advantage of any unit conversion already applied. + // We don't have to do anything special for the units here. + // + // Note: argument 'descr' implies argument-dependent lookup + // whence we unambiguously invoke function calcSlopes() + // from namespace ::Opm::DifferentiateOutputTable. + calcSlopes(numDep, descr, linTable); + } + } + + return linTable.getDataDestructively(); + } +} // Anonymous + /// Functions to facilitate generating TAB vector entries for tabulated /// saturation functions. namespace { namespace SatFunc { @@ -94,33 +186,13 @@ namespace { namespace SatFunc { const std::size_t numDep, BuildDependent&& buildDeps) { + // Saturation functions do not have sub-tables so the number of + // primary look-up keys is one. const auto numPrim = std::size_t{1}; - const auto numCols = 1 + 2*numDep; + const auto fillVal = 1.0e20; - auto descr = ::Opm::DifferentiateOutputTable::Descriptor{}; - descr.primID = 0 * numPrim; - - auto linTable = ::Opm::LinearisedOutputTable { - numTab, numPrim, numRows, numCols - }; - - for (descr.tableID = 0*numTab; - descr.tableID < 1*numTab; ++descr.tableID) - { - descr.numActRows = - buildDeps(descr.tableID, descr.primID, linTable); - - // Derivatives. Use values already stored in linTable to - // take advantage of any unit conversion already applied. - // We don't have to do anything special for the units here. - // - // Note: argument 'descr' implies argument-dependent lookup - // whence we unambiguously invoke function calcSlopes() - // from namespace ::Opm::DifferentiateOutputTable. - calcSlopes(numDep, descr, linTable); - } - - return linTable.getDataDestructively(); + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + std::forward(buildDeps)); } } // detail From 466341fa0f5e945bc2c6bb5fa5418b2fc3c40c8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 15:46:41 +0100 Subject: [PATCH 03/11] Tables: Add Structurally Correct Water PVT Output This commit adds a new public member function Tables::addPVTTables(const EclipseState&) that calls into a new private member function Tables::addWaterPVTTables(const EclipseState&) to create structurally correct TAB vector entries from the PVTW input table data when water is an active phase in a simulation run. Specifically, the result array has the columns [ Pw, 1/Bw, Cw, 1/(Bw * mu_w), Cw - Cv ] in which 'Cw' denotes the water compressibility and 'Cv' denotes the water viscosibility. Column 4 and 5 follow ECLIPSE 100 conventions. Number of table rows equal to number of PVT regions. This result array differs from the existing Tables::addPVTW() member function in the treatment of viscosity and viscosibility data. We have verified the results with ECLIPSE 100. At present the function is not called by Flow's INIT file writer. Add a unit test to exercise the new member function. --- opm/output/eclipse/Tables.hpp | 19 +++++ src/opm/output/eclipse/Tables.cpp | 117 ++++++++++++++++++++++++++++++ tests/test_Tables.cpp | 109 ++++++++++++++++++++++++++++ 3 files changed, 245 insertions(+) diff --git a/opm/output/eclipse/Tables.hpp b/opm/output/eclipse/Tables.hpp index 18052afcc..f0cb9d1b4 100644 --- a/opm/output/eclipse/Tables.hpp +++ b/opm/output/eclipse/Tables.hpp @@ -1,4 +1,6 @@ /* + Copyright 2019 Equinor. + Copyright 2017 Statoil ASA. Copyright 2016 Statoil ASA. This file is part of the Open Porous Media project (OPM). @@ -42,6 +44,15 @@ namespace Opm { void addPVTW(const PvtwTable& pvtwTable); void addDensity(const DensityTable& density); + /// Add normalised PVT function tables to INIT file's TAB vector. + /// + /// \param[in] es Valid \c EclipseState object with accurate RUNSPEC + /// information on active phases and table dimensions ("TABDIMS"). + /// + /// \param[in] logihead Flag specifications identifying which tables + /// to output. + void addPVTTables(const EclipseState& es); + /// Add normalised saturation function tables to INIT file's TAB /// vector. /// @@ -101,6 +112,14 @@ namespace Opm { const bool gas, const bool oil, const bool wat); + + /// Add water PVT tables (keyword PVTW) to the tabular data (TABDIMS + /// and TAB vectors). + /// + /// \param[in] es Valid \c EclipseState object with accurate table + /// dimensions ("TABDIMS" keyword) and an initialised \c + /// TableManager sub-object. + void addWaterPVTTables(const EclipseState& es); }; /// Emit normalised tabular information (TABDIMS and TAB vectors) to diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 802f8e862..1af39019f 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -27,6 +27,7 @@ #include #include +#include // PVTW #include #include #include @@ -1078,6 +1079,99 @@ namespace { namespace SatFunc { } // Water }} // Anonymous::SatFunc +/// Functions to facilitate generating TAB vector entries for tabulated +/// PVT functions. +namespace { namespace PVTFunc { + + /// Functions to create linearised, padded, and normalised water PVT + /// output tables from input water PVT function keyword. + namespace Water { + /// Create linearised 'TAB' vector entries of normalised water PVT + /// tables for all PVT function regions from PVTW keyword data. + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal), formation volume factors + /// (Rm3/Sm3), compressibility values (1/Pascal), viscosity + /// values (Pa*s), and viscosibility values (1/Pascal) to + /// declared conventions of the run specification. + /// + /// \param[in] pvtw Collection of PVTW tables for all PVT regions. + /// + /// \return Linearised 'TAB' vector values for output water PVT + /// tables. A unit-converted, transformed version of the input + /// table \p pvtw. No derivative information added. + std::vector + fromPVTW(const Opm::UnitSystem& units, + const Opm::PvtwTable& pvtw) + { + // Recall: PvtwTable is essentially vector in which + // + // struct PVTWRecord { + // double reference_pressure; + // double volume_factor; + // double compressibility; + // double viscosity; + // double viscosibility; + // }; + + using M = ::Opm::UnitSystem::measure; + + // Columns [ Pw, 1/Bw, Cw, 1/(Bw*mu_w), Cw - Cv ] + // + // Single row per table. No derivatives. Can't reuse + // createPropfuncTable here, so implement the return value + // directly in terms of LinearisedOutputTable. + + const auto numTab = pvtw.size(); + const auto numPrim = std::size_t{1}; + const auto numRows = std::size_t{1}; + const auto numCols = std::size_t{5}; + + auto lintable = ::Opm::LinearisedOutputTable { + numTab, numPrim, numRows, numCols + }; + + // Note unit hack for compressibility and viscosibility. The + // unit of measurement for these quantities is 1/pressure, but + // the UnitSystem does not define this unit. Work around the + // missing conversion by using *to_si()* rather than *from_si()* + // for those quantities. + + const auto uPress = M::pressure; + const auto uRecipFVF = M::water_inverse_formation_volume_factor; + const auto uVisc = M::viscosity; + + const auto primID = std::size_t{0}; + for (auto tabID = 0*numTab; tabID < numTab; ++tabID) { + const auto& t = pvtw[tabID]; + + auto iPw = lintable.column(tabID, primID, 0); + auto irecipFvf = lintable.column(tabID, primID, 1); + auto iCw = lintable.column(tabID, primID, 2); + auto irecipFvfVisc = lintable.column(tabID, primID, 3); + auto idiffCwCv = lintable.column(tabID, primID, 4); + + *iPw = units.from_si(uPress , t.reference_pressure); + *irecipFvf = units.from_si(uRecipFVF, 1.0 / t.volume_factor); + + // Compressibility unit hack here (*to_si()*) + *iCw = units.to_si(uPress, t.compressibility); + + *irecipFvfVisc = + units.from_si(uRecipFVF, 1.0 / t.volume_factor) + / units.from_si(uVisc, t.viscosity); + + // Viscosibility unit hack here (*to_si()*) + *idiffCwCv = + units.to_si(uPress, t.compressibility - t.viscosibility); + } + + return lintable.getDataDestructively(); + } + } // Water + +}} // Anonymous::PVTFunc + namespace Opm { Tables::Tables(const UnitSystem& units0) @@ -1274,6 +1368,13 @@ namespace Opm { } } + void Tables::addPVTTables(const EclipseState& es) + { + const auto& phases = es.runspec().phases(); + + if (phases.active(Phase::WATER)) { this->addWaterPVTTables(es); } + } + void Tables::addSatFunc(const EclipseState& es) { const auto& tabMgr = es.getTableManager(); @@ -1441,6 +1542,22 @@ namespace Opm { } } + void Tables::addWaterPVTTables(const EclipseState& es) + { + const auto& tabMgr = es.getTableManager(); + + const auto& pvtw = tabMgr.getPvtwTable(); + + if (pvtw.empty()) { + return; + } + + const auto data = PVTFunc::Water::fromPVTW(this->units, pvtw); + + this->addData(TABDIMS_IBPVTW_OFFSET_ITEM, data); + this->m_tabdims[TABDIMS_NTPVTW_ITEM] = pvtw.size(); + } + void fwrite(const Tables& tables, ERT::FortIO& fortio) { diff --git a/tests/test_Tables.cpp b/tests/test_Tables.cpp index 5dea679e6..810cfa07f 100644 --- a/tests/test_Tables.cpp +++ b/tests/test_Tables.cpp @@ -1,4 +1,5 @@ /* + Copyright 2019 Equinor. Copyright 2016 Statoil ASA. This file is part of the Open Porous Media project (OPM). @@ -1608,3 +1609,111 @@ BOOST_AUTO_TEST_CASE (Serialize_Family_Two) } BOOST_AUTO_TEST_SUITE_END () + +// ##################################################################### + +BOOST_AUTO_TEST_SUITE (PVTTables) + +namespace { + Opm::EclipseState parse(const std::string& rspec, + const std::string& props) + { + return { + Opm::Parser{}.parseString(rspec + R"(GRID +INIT + +DXV + 10*200 / + +DYV + 10*200 / + +DZV + 10*25 / + +TOPS + 100*2500 / + +PORO + 1000*0.3 / + +PERMX + 1000*100 / + +COPY + 'PERMX' 'PERMY' / + 'PERMX' 'PERMZ' / +/ + +MULTIPLY + 'PERMZ' 0.1 / +/ + +PROPS +)" + props + R"( +END +)") }; + } +} + +BOOST_AUTO_TEST_SUITE (Water) + +BOOST_AUTO_TEST_CASE (PVTW) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTW Output + +WATER + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 4 1* 3 +/ +)" }; + + const auto props = std::string { R"( +PVTW +-- Pref Bw(Pref) Cw Vw(Pref) Cv + 200 1.23 0.321e-4 0.25 0.654e-3 / + 250 0.987 1.234e-5 0.314 0.9876e-5 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtw = tabdims[ TABDIMS_IBPVTW_OFFSET_ITEM ] - 1; + const auto ntpvtw = tabdims[ TABDIMS_NTPVTW_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(ntpvtw, 2); + + const auto pvtw = std::vector { + &tab[ ibpvtw ] + 0, + &tab[ ibpvtw ] + ntpvtw*ncol + }; + + const auto expect_pvtw = makeTable(5, { + // Pw 1/Bw Cw 1/(Bw*mu_w) Cw - Cv + 2.000000000000000e+02, 8.130081300813008e-01, 3.210000000000000e-05, 3.252032520325203e+00, -6.219000000000000e-04, + 2.500000000000000e+02, 1.013171225937183e+00, 1.234000000000000e-05, 3.226659955213960e+00, 2.464000000000000e-06, + }); + + check_is_close(pvtw, expect_pvtw); +} + +BOOST_AUTO_TEST_SUITE_END () + +// ===================================================================== + +BOOST_AUTO_TEST_SUITE_END () From b2a37d1e54ded91f8b8f3e80f6a8b3541dacc0c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 16:58:03 +0100 Subject: [PATCH 04/11] Tables: Add Structurally Correct Oil PVT Output from PVCDO This commit expands the public member function Tables::addPVTTables(const EclipseState&) to call into a new private member function Tables::addOilPVTTables(const EclipseState&) which will create structurally correct TAB vector entries from the PVCDO input table data when oil is an active phase in a simulation run. Specifically, the result array has the columns [ Po, Bo, Co, mu_o, Cv ] in which 'Co' denotes the oil compressibility and 'Cv' denotes the oil viscosibility. The number of table rows is equal to number of PVT regions times the number of pressure nodes declared in input keyword TABDIMS (NPPVT, Item 4). In other words, the result array is a copy of the input table but expanded to fill NPPVT rows per PVT region. Fill value -1.0e+20. We have verified the results with ECLIPSE 100. At present the function is not called by Flow's INIT file writer. --- opm/output/eclipse/Tables.hpp | 8 ++ src/opm/output/eclipse/Tables.cpp | 123 +++++++++++++++++++++++++++++- 2 files changed, 130 insertions(+), 1 deletion(-) diff --git a/opm/output/eclipse/Tables.hpp b/opm/output/eclipse/Tables.hpp index f0cb9d1b4..5c02a8dca 100644 --- a/opm/output/eclipse/Tables.hpp +++ b/opm/output/eclipse/Tables.hpp @@ -113,6 +113,14 @@ namespace Opm { const bool oil, const bool wat); + /// Add oil PVT tables (keywords PVCDO, PVDO and PVTO) to the + /// tabular data (TABDIMS and TAB vectors). + /// + /// \param[in] es Valid \c EclipseState object with accurate table + /// dimensions ("TABDIMS" keyword) and an initialised \c + /// TableManager sub-object. + void addOilPVTTables(const EclipseState& es); + /// Add water PVT tables (keyword PVTW) to the tabular data (TABDIMS /// and TAB vectors). /// diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 1af39019f..73f9384b1 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -27,7 +27,7 @@ #include #include -#include // PVTW +#include // PVTW, PVCDO #include #include #include @@ -1083,6 +1083,103 @@ namespace { namespace SatFunc { /// PVT functions. namespace { namespace PVTFunc { + /// Functions to create linearised, padded, and normalised oil PVT + /// output tables from various input oil PVT function keywords. + namespace Oil { + /// Create linearised and padded 'TAB' vector entries of normalised + /// oil tables for all PVT function regions from PVCDO (dead oil + /// with constant oil compressibility) keyword data. + /// + /// \param[in] numPressNodes Number of pressure nodes (rows) to + /// allocate in the output vector for each table. Expected to be + /// equal to the number of declared pressure nodes in the + /// simulation run's TABDIMS keyword (NPPVT, Item 4). Note that + /// only the first row of each table contains any actual PVT + /// data. + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal), formation volume factors + /// (Rm3/Sm3), compressibility values (1/Pascal), viscosity + /// values (Pa*s), and viscosibility values (1/Pascal) to + /// declared conventions of the run specification. + /// + /// \param[in] pvcdo Collection of PVCDO tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output oil + /// PVT tables. A unit-converted copy of the input table \p + /// pvcdo. No derivative information added. + std::vector + fromPVCDO(const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const Opm::PvcdoTable& pvcdo) + { + // Recall: PvcdoTable is essentially vector with + // + // struct PVCDORecord { + // double reference_pressure; + // double volume_factor; + // double compressibility; + // double viscosity; + // double viscosibility; + // }; + // + using M = ::Opm::UnitSystem::measure; + + // Columns [ Po, Bo, Co, mu_o, Cv ] + // + // Single active row per table. No derivatives. Can't reuse + // createPropfuncTable here, so implement the return value + // directly in terms of LinearisedOutputTable. + + const auto numTab = pvcdo.size(); + const auto numPrim = std::size_t{1}; + const auto numCols = std::size_t{5}; + + // PVCDO fill value: -1.0e20 + const auto fillVal = -1.0e20; + + auto lintable = ::Opm::LinearisedOutputTable { + numTab, numPrim, numPressNodes, numCols, fillVal + }; + + // Note unit hack for compressibility and viscosibility. The + // unit of measurement for these quantities is 1/pressure, but + // the UnitSystem does not define this unit. Work around the + // missing conversion by using *to_si()* rather than *from_si()* + // for those quantities. + + const auto uPress = M::pressure; + const auto uBo = M::oil_formation_volume_factor; + const auto uVisc = M::viscosity; + + // Single primary key, ID = 0. + const auto primID = std::size_t{0}; + + for (auto tabID = 0*numTab; tabID < numTab; ++tabID) { + const auto& t = pvcdo[tabID]; + + auto iPo = lintable.column(tabID, primID, 0); + auto iBo = lintable.column(tabID, primID, 1); + auto iCo = lintable.column(tabID, primID, 2); + auto imu_o = lintable.column(tabID, primID, 3); + auto iCv = lintable.column(tabID, primID, 4); + + *iPo = units.from_si(uPress, t.reference_pressure); + *iBo = units.from_si(uBo, t.volume_factor); + + // Compressibility unit hack here (*to_si()*) + *iCo = units.to_si(uPress, t.compressibility); + + *imu_o = units.from_si(uVisc, t.viscosity); + + // Viscosibility unit hack here (*to_si()*) + *iCv = units.to_si(uPress, t.viscosibility); + } + + return lintable.getDataDestructively(); + } + } // Oil + /// Functions to create linearised, padded, and normalised water PVT /// output tables from input water PVT function keyword. namespace Water { @@ -1372,6 +1469,7 @@ namespace Opm { { const auto& phases = es.runspec().phases(); + if (phases.active(Phase::OIL)) { this->addOilPVTTables (es); } if (phases.active(Phase::WATER)) { this->addWaterPVTTables(es); } } @@ -1542,6 +1640,29 @@ namespace Opm { } } + void Tables::addOilPVTTables(const EclipseState& es) + { + const auto& tabMgr = es.getTableManager(); + const auto& tabd = es.runspec().tabdims(); + + const auto numPressNodes = tabd.getNumPressureNodes(); + + const auto hasPVCDO = !tabMgr.getPvcdoTable().empty(); + + if (hasPVCDO) { + // Dead oil, constant compressibility. + const auto& pvcdo = tabMgr.getPvcdoTable(); + + const auto numRows = std::max(numPressNodes, pvcdo.size()); + + const auto data = PVTFunc::Oil::fromPVCDO(numRows, this->units, pvcdo); + + this->addData(TABDIMS_IBPVTO_OFFSET_ITEM, data); + this->m_tabdims[TABDIMS_NPPVTO_ITEM] = numRows; + this->m_tabdims[TABDIMS_NTPVTO_ITEM] = pvcdo.size(); + } + } + void Tables::addWaterPVTTables(const EclipseState& es) { const auto& tabMgr = es.getTableManager(); From b4915e7b555168eb937863f5a681d1696698f066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 17:12:33 +0100 Subject: [PATCH 05/11] Tables: Add Structurally Correct Oil PVT Output from PVDO This commit expands the private member function Tables::addOilPVTTables(const EclipseState&) to create structurally correct TAB vector entries from the PVDO (dead oil) input table data when oil is an active phase in a simulation run. Specifically, the result array has the columns [ Po, 1/Bo, 1/(Bo*mu_o), d(1/Bo)/dPo, d(1/(Bo*mu_o))/dPo ] The number of table rows is equal to number of PVT regions times the number of pressure nodes declared in input keyword TABDIMS (NPPVT, Item 4). In other words, the result array is expanded to fill NPPVT rows per PVT region. Fill value +2.0e+20. We have verified the results with ECLIPSE 100. As an OPM extension, we will use the maximum number of active pressure nodes across all PVDO tables if the declared maximum pressure nodes in TABDIMS is too small. This will create a TAB vector that is not compatible with ECLIPSE, but which will nevertheless be useful in the context of ResInsight's flux calculation. --- src/opm/output/eclipse/Tables.cpp | 145 +++++++++++++++++++++++++++++- 1 file changed, 144 insertions(+), 1 deletion(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 73f9384b1..d27b25a26 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -28,6 +28,7 @@ #include #include #include // PVTW, PVCDO +#include #include #include #include @@ -44,6 +45,7 @@ #include #include #include +#include #include namespace { @@ -1178,6 +1180,128 @@ namespace { namespace PVTFunc { return lintable.getDataDestructively(); } + + /// Create linearised and padded 'TAB' vector entries of normalised + /// gas tables for all PVT function regions from PVDO (dead oil) + /// keyword data. + /// + /// \param[in] numPressNodes Number of pressure nodes (rows) to + /// allocate in the output vector for each table. Expected to be + /// equal to the number of declared pressure nodes in the + /// simulation run's TABDIMS keyword (NPPVT, Item 4). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal), formation volume factors + /// (Rm3/Sm3), and viscosity values (Pa*s) to declared + /// conventions of the run specification. + /// + /// \param[in] pvdo Collection of PVDO tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output gas + /// PVT tables. A unit-converted copy of the input table \p pvdo + /// with added derivatives. + std::vector + fromPVDO(const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const Opm::TableContainer& pvdo) + { + // Columns [ Po, 1/Bo, 1/(Bo*mu_o), derivatives ] + using PVDO = ::Opm::PvdoTable; + + const auto numTab = pvdo.size(); + const auto numPrim = std::size_t{1}; // No sub-tables + const auto numRows = numPressNodes; // One row per pressure node. + const auto numDep = std::size_t{2}; // 1/Bo, 1/(Bo*mu_o) + + // PVDO fill value = +2.0e20 + const auto fillVal = +2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvdo](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + const auto& t = pvdo.getTable(tableID); + + auto numActRows = std::size_t{0}; + + // Column 0: Po + { + const auto uPress = ::Opm::UnitSystem::measure::pressure; + + const auto& Po = t.getPressureColumn(); + + numActRows = Po.size(); + + std::transform(std::begin(Po), std::end(Po), + linTable.column(tableID, primID, 0), + [&units](const double p) -> double + { + return units.from_si(uPress, p); + }); + } + + // Column 1: 1/Bo + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + oil_inverse_formation_volume_factor; + + const auto& Bo = t.getFormationFactorColumn(); + std::transform(std::begin(Bo), std::end(Bo), + linTable.column(tableID, primID, 1), + [&units](const double B) -> double + { + return units.from_si(uRecipFVF, 1.0 / B); + }); + } + + // Column 2: 1/(Bo*mu_o) + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + oil_inverse_formation_volume_factor; + + const auto uVisc = ::Opm::UnitSystem::measure::viscosity; + + const auto& Bo = t.getFormationFactorColumn(); + const auto& mu_o = t.getViscosityColumn(); + + std::transform(std::begin(Bo), std::end(Bo), + std::begin(mu_o), + linTable.column(tableID, primID, 2), + [&units](const double B, const double mu) -> double + { + return units.from_si(uRecipFVF, 1.0 / B) + / units.from_si(uVisc , mu); + }); + } + + // Inform createPropfuncTable() of number of active rows in + // this table. Needed to compute slopes of piecewise linear + // interpolants. + return numActRows; + }); + } + + /// Extract maximum effective pressure nodes in PVDO table data + /// + /// \param[in] pvdo Collection of PVDO tables for all PVT regions. + /// + /// \return Maximum number of table rows across all tables of \p + /// pvdo. + std::size_t maxNumPressNodes(const Opm::TableContainer& pvdo) + { + using PVDO = ::Opm::PvdoTable; + auto tabID = std::vector(pvdo.size()); + + std::iota(tabID.begin(), tabID.end(), std::size_t{0}); + + return std::accumulate(tabID.begin(), tabID.end(), std::size_t{0}, + [&pvdo](const std::size_t n, const std::size_t table) + { + return std::max(n, pvdo.getTable(table).numRows()); + }); + } } // Oil /// Functions to create linearised, padded, and normalised water PVT @@ -1647,9 +1771,28 @@ namespace Opm { const auto numPressNodes = tabd.getNumPressureNodes(); + const auto hasPVDO = tabMgr.hasTables("PVDO"); const auto hasPVCDO = !tabMgr.getPvcdoTable().empty(); - if (hasPVCDO) { + if (hasPVDO + hasPVCDO != 1) { + // Unhandled table specification. Maybe throw here? + return; + } + + if (hasPVDO) { + // Dead oil, pressure dependent compressibility. + const auto& pvdo = tabMgr.getPvdoTables(); + + const auto numRows = + std::max(numPressNodes, PVTFunc::Oil::maxNumPressNodes(pvdo)); + + const auto data = PVTFunc::Oil::fromPVDO(numRows, this->units, pvdo); + + this->addData(TABDIMS_IBPVTO_OFFSET_ITEM, data); + this->m_tabdims[TABDIMS_NPPVTO_ITEM] = numRows; + this->m_tabdims[TABDIMS_NTPVTO_ITEM] = pvdo.size(); + } + else { // Dead oil, constant compressibility. const auto& pvcdo = tabMgr.getPvcdoTable(); From c5eca4b1e41e7da185f3694c87256bbcb8bee1e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 17:45:05 +0100 Subject: [PATCH 06/11] Tables: Add Structurally Correct Oil PVT Output from PVTO This commit expands the private member function Tables::addOilPVTTables(const EclipseState&) to create structurally correct TAB vector entries from the PVTO (live oil) input table data when oil is an active phase in a simulation run. Specifically, the main result array has the columns [ Po, 1/Bo, 1/(Bo*mu_o), d(1/Bo)/dPo, d(1/(Bo*mu_o))/dPo ] and the ancillary table (base pointer JBPVTO) holds the composition nodes. Note that while we do create structurally correct output tables, we do not fill in undersaturated states that have been defaulted in the input table. The number of table rows in the main table is equal to number of PVT regions times the number of declared composition nodes (TABDIMS item 6, NRPVT) times the number of declared pressure nodes (TABDIMS item 4, NPPVT). In other words, the main result array is expanded to fill NPPVT rows per Rs node per PVT region. Fill value +2.0e+20. We have verified the results with ECLIPSE 100. The ancillary table is expanded to number of declared composition nodes for each PVT region. Fill value +2.0e+20. As an OPM extension, we will use the maximum number of active pressure and composition nodes across all PVTO tables if the declared maximum pressure nodes in TABDIMS is too small. This will create a TAB vector representations that are not compatible with ECLIPSE, but which will nevertheless be useful in the context of ResInsight's flux calculation. Add unit tests for all supported oil-related PVT tables. --- src/opm/output/eclipse/Tables.cpp | 247 +++++++++- tests/test_Tables.cpp | 740 ++++++++++++++++++++++++++++++ 2 files changed, 984 insertions(+), 3 deletions(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index d27b25a26..525988bd1 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -29,6 +29,7 @@ #include #include // PVTW, PVCDO #include +#include #include #include #include @@ -1283,6 +1284,198 @@ namespace { namespace PVTFunc { }); } + /// Create linearised and padded 'TAB' vector entries of normalised + /// gas tables for all PVT function regions from PVTO (live oil with + /// dissolved gas) keyword data. + /// + /// \param[in] numCompNodes Number of composition nodes (sub-tables) + /// to allocate in the output vector for each oil PVT table. + /// Expected to be equal to the number of declared composition + /// nodes in the simulation run's TABDIMS keyword (NRPVT, Item + /// 6). + /// + /// \param[in] numPressNodes Number of pressure nodes (rows per + /// sub-table) to allocate in the output vector for each oil PVT + /// table. Expected to be equal to the number of declared + /// pressure nodes in the simulation run's TABDIMS keyword + /// (NPPVT, Item 4). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal), formation volume factors + /// (S-volume to R-volume), and viscosity values (Pa*s) to + /// declared conventions of the run specification. + /// + /// \param[in] pvto Collection of PVTO tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output gas + /// PVT tables. A unit-converted copy of the input table \p pvto + /// with added derivatives. + std::vector + fromPVTO(const std::size_t numCompNodes, + const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const std::vector& pvto) + { + // Columns [ Po, 1/Bo, 1/(Bo*mu_o), derivatives ] + const auto numTab = pvto.size(); + const auto numPrim = numCompNodes; + const auto numRows = numPressNodes; + const auto numDep = std::size_t{2}; // 1/Bo, 1/(Bo*mu_o) + + // PVTO fill value = +2.0e20 + const auto fillVal = +2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvto](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + auto numActRows = std::size_t{0}; + + if (primID >= pvto[tableID].size()) { + // Pressure node outside current table's active set. No + // active rows in this subtable. + return numActRows; + } + + const auto& t = pvto[tableID].getUnderSaturatedTable(primID); + + // Column 0: Po + { + const auto uPo = ::Opm::UnitSystem::measure::pressure; + + const auto& Po = t.getColumn(0); + + numActRows = Po.size(); + + std::transform(std::begin(Po), std::end(Po), + linTable.column(tableID, primID, 0), + [&units](const double po) -> double + { + return units.from_si(uPo, po); + }); + } + + // Column 1: 1/Bo + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + oil_inverse_formation_volume_factor; + + const auto& Bo = t.getColumn(1); + + std::transform(std::begin(Bo), std::end(Bo), + linTable.column(tableID, primID, 1), + [&units](const double B) -> double + { + return units.from_si(uRecipFVF, 1.0 / B); + }); + } + + // Column 2: 1/(Bo*mu_o) + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + oil_inverse_formation_volume_factor; + + const auto uVisc = ::Opm::UnitSystem::measure::viscosity; + + const auto& Bo = t.getColumn(1); + const auto& mu_o = t.getColumn(2); + + std::transform(std::begin(Bo), std::end(Bo), + std::begin(mu_o), + linTable.column(tableID, primID, 2), + [&units](const double B, const double mu) -> double + { + return units.from_si(uRecipFVF, 1.0 / B) + / units.from_si(uVisc , mu); + }); + } + + // Inform createPropfuncTable() of number of active rows in + // this table. Needed to compute slopes of piecewise linear + // interpolants. + return numActRows; + }); + } + + /// Create linearised and padded 'TAB' vector entries of normalised + /// composition nodes for all PVT function regions from PVTO (live + /// oil with dissolved gas) keyword data. + /// + /// \param[in] numCompNodes Number of compositoin nodes to allocate + /// in the output vector for each oil PVT table. Expected to be + /// equal to the number of declared composition nodes in the + /// simulation run's TABDIMS keyword (NRPVT, Item 6). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention dissolved gas composition values (Sm3/Sm3) to + /// declared conventions of the run specification. + /// + /// \param[in] pvto Collection of PVTO tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output oil + /// PVT tables. A unit-converted copy of the primary keys in + /// input table \p pvto. + std::vector + compositionNodes(const std::size_t numCompNodes, + const Opm::UnitSystem& units, + const std::vector& pvto) + { + // Columns [ Rs ] + const auto numTab = pvto.size(); + + // One set of composition nodes per table. + const auto numPrim = std::size_t{1}; + const auto numRows = numCompNodes; + + // No dependent variables. + const auto numDep = std::size_t{0}; + + // PVTO fill value = +2.0e20 + const auto fillVal = +2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvto](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + const auto uRs = ::Opm::UnitSystem::measure::gas_oil_ratio; + + const auto& t = pvto[tableID].getSaturatedTable(); + const auto& Rs = t.getColumn(0); + + const auto numActRows = Rs.size(); + + std::transform(std::begin(Rs), std::end(Rs), + linTable.column(tableID, primID, 0), + [&units](const double rs) -> double + { + return units.from_si(uRs, rs); + }); + + return numActRows; + }); + } + + /// Extract maximum effective composition nodes in PVTO table data + /// + /// \param[in] pvto Collection of PVTO tables for all PVT regions. + /// + /// \return Maximum number of active keys across all tables of \p + /// pvto. + std::size_t maxNumCompNodes(const std::vector& pvto) + { + if (pvto.empty()) { return 0; } + + return std::accumulate(std::begin(pvto), std::end(pvto), std::size_t{0}, + [](const std::size_t n, const Opm::PvtoTable& t) -> std::size_t + { + return std::max(n, t.getSaturatedTable().numRows()); + }); + } + /// Extract maximum effective pressure nodes in PVDO table data /// /// \param[in] pvdo Collection of PVDO tables for all PVT regions. @@ -1302,6 +1495,30 @@ namespace { namespace PVTFunc { return std::max(n, pvdo.getTable(table).numRows()); }); } + + /// Extract maximum effective pressure nodes in PVTO table data + /// + /// \param[in] pvto Collection of PVTO tables for all PVT regions. + /// + /// \return Maximum number of active pressure rows across all + /// sub-tables of \p pvto. + std::size_t maxNumPressNodes(const std::vector& pvto) + { + auto max_nppvt = std::size_t{0}; + + for (const auto& table : pvto) { + const auto max_nppvt_tab = + std::accumulate(table.begin(), table.end(), std::size_t{0}, + [](const std::size_t n, const Opm::SimpleTable& t) + { + return std::max(n, t.numRows()); + }); + + max_nppvt = std::max(max_nppvt, max_nppvt_tab); + } + + return max_nppvt; + } } // Oil /// Functions to create linearised, padded, and normalised water PVT @@ -1771,15 +1988,39 @@ namespace Opm { const auto numPressNodes = tabd.getNumPressureNodes(); + const auto hasPVTO = !tabMgr.getPvtoTables().empty(); const auto hasPVDO = tabMgr.hasTables("PVDO"); const auto hasPVCDO = !tabMgr.getPvcdoTable().empty(); - if (hasPVDO + hasPVCDO != 1) { - // Unhandled table specification. Maybe throw here? + if (hasPVTO + hasPVDO + hasPVCDO != 1) { + // Inconsistent table specification. Maybe throw here? return; } - if (hasPVDO) { + if (hasPVTO) { + // Live oil with dissolved gas. + const auto& pvto = tabMgr.getPvtoTables(); + + const auto numCompNodes = + std::max(tabd.getNumRSNodes(), PVTFunc::Oil::maxNumCompNodes(pvto)); + + const auto numRows = + std::max(numPressNodes, PVTFunc::Oil::maxNumPressNodes(pvto)); + + const auto data = PVTFunc::Oil:: + fromPVTO(numCompNodes, numRows, this->units, pvto); + + const auto rsData = PVTFunc::Oil:: + compositionNodes(numCompNodes, this->units, pvto); + + this->addData(TABDIMS_IBPVTO_OFFSET_ITEM, data); + this->addData(TABDIMS_JBPVTO_OFFSET_ITEM, rsData); + + this->m_tabdims[TABDIMS_NPPVTO_ITEM] = numRows; + this->m_tabdims[TABDIMS_NRPVTO_ITEM] = numCompNodes; + this->m_tabdims[TABDIMS_NTPVTO_ITEM] = pvto.size(); + } + else if (hasPVDO) { // Dead oil, pressure dependent compressibility. const auto& pvdo = tabMgr.getPvdoTables(); diff --git a/tests/test_Tables.cpp b/tests/test_Tables.cpp index 810cfa07f..979e0c529 100644 --- a/tests/test_Tables.cpp +++ b/tests/test_Tables.cpp @@ -1656,6 +1656,746 @@ END } } +BOOST_AUTO_TEST_SUITE (Oil) + +BOOST_AUTO_TEST_CASE (PVCDO) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVCDO Output + +OIL +WATER + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 4 1* 3 +/ +)" }; + + const auto props = std::string { R"( +PVCDO +-- Pref Bo(Pref) Co Vo(Pref) Cv + 200 1.23 0.321e-4 0.25 0.654e-3 / + 250 0.987 1.234e-5 0.314 0.9876e-5 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + // Rs table defaulted + BOOST_CHECK_EQUAL(tabdims[TABDIMS_JBPVTO_OFFSET_ITEM], 1); + BOOST_CHECK_EQUAL(tabdims[TABDIMS_NRPVTO_ITEM], 1); + + BOOST_CHECK_EQUAL(nppvto, 4); + BOOST_CHECK_EQUAL(ntpvto, 2); + + const auto pvcdo = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nppvto*ncol + }; + + const auto expect_pvcdo = makeTable(5, { + // Po Bo Co Vo Cv + // + // Table 1 + 2.000000000000000e+02, 1.230000000000000e+00, 3.210000000000000e-05, 2.500000000000000e-01, 6.540000000000001e-04, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + + // =========================================================================================================================== + + // Table 2 + 2.500000000000000e+02, 9.870000000000000e-01, 1.234000000000000e-05, 3.140000000000000e-01, 9.876000000000000e-06, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, -1.000000000000000e+20, + }); + + check_is_close(pvcdo, expect_pvcdo); +} + +BOOST_AUTO_TEST_CASE (PVDO_Regular) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVDO Output + +OIL +WATER + +FIELD + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 8 1* 1* +/ +)" }; + + const auto props = std::string { R"( +PVDO +400 1.012 1.16 +1200 1.0040 1.164 +2000 0.9960 1.167 +2800 0.9880 1.172 +3600 0.9802 1.177 +4400 0.9724 1.181 +5200 0.9646 1.185 +5600 0.9607 1.19 / +800 1.0255 1.14 +1600 1.0172 1.14 +2400 1.0091 1.14 +3200 1.0011 1.14 +4000 0.9931 1.14 +4800 0.9852 1.14 +5600 0.9774 1.14 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + // Rs table defaulted + BOOST_CHECK_EQUAL(tabdims[TABDIMS_JBPVTO_OFFSET_ITEM], 1); + BOOST_CHECK_EQUAL(tabdims[TABDIMS_NRPVTO_ITEM], 1); + + BOOST_CHECK_EQUAL(nppvto, 8); + BOOST_CHECK_EQUAL(ntpvto, 2); + + const auto pvdo = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nppvto*ncol + }; + + const auto expect_pvdo = makeTable(5, { + // Po 1/Bo 1/(Bo*mu_o) d(1/Bo)/dPo d(1/(Bo*mu_o))/dPo + // + // Table 1 (8 pressure nodes) + 4.000000000000000e+02, 9.881422924901185e-01, 8.518468038707919e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.200000000000000e+03, 9.960159362549801e-01, 8.556837940334882e-01, 9.842054706076935e-06, 4.796237703370287e-06, + 2.000000000000000e+03, 1.004016064257028e+00, 8.603393866812581e-01, 1.000016000256010e-05, 5.819490809712421e-06, + 2.800000000000000e+03, 1.012145748987854e+00, 8.636055878735959e-01, 1.016210591353262e-05, 4.082751490422227e-06, + 3.600000000000000e+03, 1.020199959192002e+00, 8.667799143517431e-01, 1.006776275518428e-05, 3.967908097683990e-06, + 4.400000000000000e+03, 1.028383381324558e+00, 8.707733965491598e-01, 1.022927766569509e-05, 4.991852746770858e-06, + 5.199999999999999e+03, 1.036699149906697e+00, 8.748516032967908e-01, 1.039471072767418e-05, 5.097758434538807e-06, + 5.599999999999999e+03, 1.040907671489539e+00, 8.747123289828058e-01, 1.052130395710449e-05, -3.481857849624292e-07, + + // =========================================================================================================================== + + // Table 2 (7 pressure nodes) + 8.000000000000000e+02, 9.751340809361286e-01, 8.553807727509901e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.600000000000000e+03, 9.830908375933936e-01, 8.623603838538541e-01, 9.945945821581148e-06, 8.724513878579920e-06, + 2.400000000000000e+03, 9.909820632246555e-01, 8.692825116005751e-01, 9.864032039077486e-06, 8.652659683401343e-06, + 3.200000000000000e+03, 9.989012086704624e-01, 8.762291304126864e-01, 9.898931807258565e-06, 8.683273515139035e-06, + 4.000000000000000e+03, 1.006947940791461e+00, 8.832876673609308e-01, 1.005841515124826e-05, 8.823171185305545e-06, + 4.800000000000000e+03, 1.015022330491271e+00, 8.903704653432202e-01, 1.009298712476236e-05, 8.853497477861732e-06, + 5.599999999999999e+03, 1.023122570083896e+00, 8.974759386700842e-01, 1.012529949078135e-05, 8.881841658580091e-06, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + check_is_close(pvdo, expect_pvdo); +} + +BOOST_AUTO_TEST_CASE (PVDO_Exceed_Tabdims_Limits) +{ + // This checks that Flow's extended table allocation scheme does what + // it's supposed to do. Normally, one would announce the maximum sizes + // in the RUNSPEC keyword 'TABDIMS'. Note that the maximum sizes need + // to be big enough in order to run the case in ECLIPSE. Other than + // NTPVT, Flow does not really care about these declared maximum sizes. + + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVDO Output + +OIL +WATER + +FIELD + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 2 1* 1* +/ +)" }; + + const auto props = std::string { R"( +PVDO +400 1.012 1.16 +1200 1.0040 1.164 +2000 0.9960 1.167 +2800 0.9880 1.172 +3600 0.9802 1.177 +4000 0.9752 1.179 +4400 0.9724 1.181 +5200 0.9646 1.185 +5400 0.9630 1.187 +5600 0.9607 1.19 / +800 1.0255 1.14 +1600 1.0172 1.14 +5600 0.9774 1.14 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + // Rs table defaulted + BOOST_CHECK_EQUAL(tabdims[TABDIMS_JBPVTO_OFFSET_ITEM], 1); + BOOST_CHECK_EQUAL(tabdims[TABDIMS_NRPVTO_ITEM], 1); + + BOOST_CHECK_EQUAL(nppvto, 10); // Table 1 + BOOST_CHECK_EQUAL(ntpvto, 2); + + // Verify declared TABDIMS from input + { + const auto& tabd = es.runspec().tabdims(); + + BOOST_CHECK_EQUAL(tabd.getNumPVTTables(), 2); + BOOST_CHECK_EQUAL(tabd.getNumPressureNodes(), 2); + } + + const auto pvdo = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nppvto*ncol + }; + + const auto expect_pvdo = makeTable(5, { + // Po 1/Bo 1/(Bo*mu_o) d(1/Bo)/dPo d(1/(Bo*mu_o))/dPo + // + // Table 1 (10 pressure nodes) + 4.000000000000000e+02, 9.881422924901185e-01, 8.518468038707919e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.200000000000000e+03, 9.960159362549801e-01, 8.556837940334882e-01, 9.842054706076935e-06, 4.796237703370287e-06, + 2.000000000000000e+03, 1.004016064257028e+00, 8.603393866812581e-01, 1.000016000256010e-05, 5.819490809712421e-06, + 2.800000000000000e+03, 1.012145748987854e+00, 8.636055878735959e-01, 1.016210591353262e-05, 4.082751490422227e-06, + 3.600000000000000e+03, 1.020199959192002e+00, 8.667799143517431e-01, 1.006776275518428e-05, 3.967908097683990e-06, + 4.000000000000000e+03, 1.025430680885972e+00, 8.697461245852181e-01, 1.307680423492609e-05, 7.415525583687474e-06, + 4.400000000000000e+03, 1.028383381324558e+00, 8.707733965491598e-01, 7.381751096464098e-06, 2.568179909854249e-06, + 5.199999999999999e+03, 1.036699149906697e+00, 8.748516032967908e-01, 1.039471072767418e-05, 5.097758434538807e-06, + 5.400000000000000e+03, 1.038421599169263e+00, 8.748286429395642e-01, 8.612246312827937e-06, -1.148017861329892e-07, + 5.599999999999999e+03, 1.040907671489539e+00, 8.747123289828058e-01, 1.243036160138106e-05, -5.815697837918713e-07, + + // =========================================================================================================================== + + // Table 2 (3 pressure nodes) + 8.000000000000000e+02, 9.751340809361286e-01, 8.553807727509901e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.600000000000000e+03, 9.830908375933936e-01, 8.623603838538541e-01, 9.945945821581148e-06, 8.724513878579920e-06, + 5.599999999999999e+03, 1.023122570083896e+00, 8.974759386700842e-01, 1.000793312262560e-05, 8.778888704057549e-06, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + check_is_close(pvdo, expect_pvdo); +} + +BOOST_AUTO_TEST_CASE (PVTO_SPE1_USat_Defaulted) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTO Output + +OIL +WATER + +FIELD + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 4 1* 3 +/ +)" }; + + const auto props = std::string { R"( +PVTO +0.3710 1014.7 1.2950 0.8300 / +0.9300 3014.7 1.5650 0.5940 / +1.6180 5014.7 1.8270 0.4490 + 9014.7 1.7370 0.6310 / +/ +0.0010 14.7 1.0620 1.0400 / +1.2700 4014.7 1.6950 0.5100 + 9014.7 1.5790 0.7400 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto jbpvto = tabdims[ TABDIMS_JBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto nrpvto = tabdims[ TABDIMS_NRPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(nppvto, 4); + BOOST_CHECK_EQUAL(nrpvto, 3); + BOOST_CHECK_EQUAL(ntpvto, 2); + + const auto pvto = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nrpvto*nppvto*ncol + }; + + const auto rs = std::vector { + &tab[ jbpvto ] + 0, + &tab[ jbpvto ] + ntpvto*nrpvto + }; + + const auto expect_pvto = makeTable(5, { + // Po 1/Bo 1/(Bo*mu_o) d(1/Bo)/dPo d(1/(Bo*mu_o))/dPo + // + // Table 1 (Comp 1) + 1.014700000000000e+03, 7.722007722007722e-01, 9.303623761455088e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 1 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 3.014700000000000e+03, 6.389776357827476e-01, 1.075719925560181e+00, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 1 (Comp 3) ---------------------------------------------------------------------------------------------------------- + + 5.014700000000000e+03, 5.473453749315819e-01, 1.219032015437822e+00, 2.000000000000000e+20, 2.000000000000000e+20, + 9.014699999999999e+03, 5.757052389176741e-01, 9.123696337839526e-01, 7.089965996523064e-06, -7.666559541346725e-05, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // ========================================================================================================================== + + // Table 2 (Comp 1) + 1.470000000000000e+01, 9.416195856873822e-01, 9.054034477763291e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 4.014699999999999e+03, 5.899705014749262e-01, 1.156804904852797e+00, 2.000000000000000e+20, 2.000000000000000e+20, + 9.014699999999999e+03, 6.333122229259025e-01, 8.558273282782466e-01, 8.668344290195251e-06, -6.019551531491001e-05, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 3, unused) -------------------------------------------------------------------------------------------------- + + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + const auto expect_rs = makeTable(1, { + // Table 1 + 0.3710, 0.9300, 1.6180, + + // Table 2 + 0.0010, 1.2700, 2.0e+20, + }); + + check_is_close(pvto, expect_pvto); + check_is_close(rs , expect_rs ); +} + +BOOST_AUTO_TEST_CASE (PVTO_USat_Full) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTO Output + +OIL +WATER + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 5 1* 4 +/ +)" }; + + const auto props = std::string { R"( +PVTO +20.59 50.00 1.10615 1.180 + 75.00 1.10164 1.247 + 100.00 1.09744 1.315 + 125.00 1.09351 1.384 + 150.00 1.08984 1.453 / +28.19 70.00 1.12522 1.066 + 95.00 1.12047 1.124 + 120.00 1.11604 1.182 + 145.00 1.11191 1.241 + 170.00 1.10804 1.300 / +36.01 90.00 1.14458 0.964 + 115.00 1.13959 1.014 + 140.00 1.13494 1.064 + 165.00 1.13060 1.115 + 190.00 1.12653 1.166 / +44.09 110.00 1.16437 0.880 + 135.00 1.15915 0.924 + 160.00 1.15428 0.968 + 185.00 1.14973 1.012 + 210.00 1.14547 1.056 / +/ +32.91 80.00 1.13304 1.04537 + 114.00 1.12837 1.10009 + 148.00 1.12401 1.15521 + 182.00 1.11994 1.21063 / +40.99 100.00 1.15276 0.97219 + 134.00 1.14786 1.02086 + 168.00 1.14328 1.06983 + 202.00 1.13900 1.11901 / +49.36 120.00 1.17297 0.91124 + 154.00 1.16783 0.95496 + 188.00 1.16303 0.99891 + 222.00 1.15854 1.04301 / +58.04 140.00 1.19374 0.85942 + 174.00 1.18836 0.89902 + 208.00 1.18334 0.93878 + 242.00 1.17864 0.97864 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto jbpvto = tabdims[ TABDIMS_JBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto nrpvto = tabdims[ TABDIMS_NRPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(nppvto, 5); + BOOST_CHECK_EQUAL(nrpvto, 4); + BOOST_CHECK_EQUAL(ntpvto, 2); + + const auto pvto = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nrpvto*nppvto*ncol + }; + + const auto rs = std::vector { + &tab[ jbpvto ] + 0, + &tab[ jbpvto ] + ntpvto*nrpvto + }; + + const auto expect_pvto = makeTable(5, { + // Po 1/Bo 1/(Bo*mu_o) d(1/Bo)/dPo d(1/(Bo*mu_o))/dPo + // + // Table 1 (Comp 1) + 5.000000000000001e+01, 9.040365230755323e-01, 7.661326466741798e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 7.500000000000000e+01, 9.077375549181221e-01, 7.279370929575959e-01, 1.480412737035941e-04, -1.527822148663356e-03, + 1.000000000000000e+02, 9.112115468727220e-01, 6.929365375457962e-01, 1.389596781839940e-04, -1.400022216471987e-03, + 1.250000000000000e+02, 9.144863787253888e-01, 6.607560539923329e-01, 1.309932741066744e-04, -1.287219342138530e-03, + 1.500000000000000e+02, 9.175658812302724e-01, 6.314975094496025e-01, 1.231801001953415e-04, -1.170341781709219e-03, + + // Table 1 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 7.000000000000000e+01, 8.887150957146157e-01, 8.336914593945738e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 9.500000000000001e+01, 8.924826189009969e-01, 7.940236822962605e-01, 1.507009274552472e-04, -1.586711083932530e-03, + 1.200000000000000e+02, 8.960252320705352e-01, 7.580585719716880e-01, 1.417045267815320e-04, -1.438604412982900e-03, + 1.450000000000000e+02, 8.993533649306149e-01, 7.247005358022682e-01, 1.331253144031886e-04, -1.334321446776793e-03, + 1.700000000000000e+02, 9.024944947835819e-01, 6.942265344489090e-01, 1.256451941186798e-04, -1.218960054134368e-03, + + // Table 1 (Comp 3) ---------------------------------------------------------------------------------------------------------- + + 9.000000000000001e+01, 8.736829229935872e-01, 9.063100860929328e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.150000000000000e+02, 8.775085776463464e-01, 8.653930746019195e-01, 1.530261861103677e-04, -1.636680459640534e-03, + 1.400000000000000e+02, 8.811038468993955e-01, 8.281051192663491e-01, 1.438107701219638e-04, -1.491518213422816e-03, + 1.650000000000000e+02, 8.844861135680170e-01, 7.932610884018088e-01, 1.352906667448606e-04, -1.393761234581614e-03, + 1.900000000000000e+02, 8.876816418559648e-01, 7.613050101680658e-01, 1.278211315179111e-04, -1.278243129349715e-03, + + // Table 1 (Comp 4) ---------------------------------------------------------------------------------------------------------- + + 1.100000000000000e+02, 8.588335322964350e-01, 9.759471957914034e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.350000000000000e+02, 8.627011171979468e-01, 9.336592177466957e-01, 1.547033960604739e-04, -1.691519121788311e-03, + 1.600000000000000e+02, 8.663409224798143e-01, 8.949802918179900e-01, 1.455922112746988e-04, -1.547157037148228e-03, + 1.850000000000000e+02, 8.697694241256644e-01, 8.594559526933443e-01, 1.371400658340026e-04, -1.420973564985826e-03, + 2.100000000000000e+02, 8.730040943892027e-01, 8.267084227170479e-01, 1.293868105415319e-04, -1.309901199051855e-03, + + // =========================================================================================================================== + + // Table 2 (Comp 1) + 8.000000000000000e+01, 8.825813740026830e-01, 8.442765470624592e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.140000000000000e+02, 8.862341253312299e-01, 8.056014738168967e-01, 1.074338626043212e-04, -1.137502154281250e-03, + 1.480000000000000e+02, 8.896718000729531e-01, 7.701385895836714e-01, 1.011080806389172e-04, -1.043026006859568e-03, + 1.820000000000000e+02, 8.929049770523422e-01, 7.375539818543586e-01, 9.509344057026681e-05, -9.583708155680233e-04, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 1.000000000000000e+02, 8.674832575731288e-01, 8.922980668111468e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.340000000000000e+02, 8.711863816144825e-01, 8.533847752037326e-01, 1.089154129809896e-04, -1.144508576688653e-03, + 1.680000000000000e+02, 8.746763697431950e-01, 8.175844477563677e-01, 1.026467096680142e-04, -1.052950807275436e-03, + 2.020000000000000e+02, 8.779631255487269e-01, 7.845891685943172e-01, 9.666928839799841e-05, -9.704493871191340e-04, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 3) ---------------------------------------------------------------------------------------------------------- + + 1.200000000000000e+02, 8.525367230193440e-01, 9.355786873044905e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.540000000000000e+02, 8.562890146682309e-01, 8.966752687738030e-01, 1.103615190849091e-04, -1.144218192079043e-03, + 1.880000000000000e+02, 8.598230484166358e-01, 8.607612782098846e-01, 1.039421690707318e-04, -1.056293840115247e-03, + 2.220000000000000e+02, 8.631553507000190e-01, 8.275619128292337e-01, 9.800889068774162e-05, -9.764519229603183e-04, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 4) ---------------------------------------------------------------------------------------------------------- + + 1.400000000000000e+02, 8.377033524888167e-01, 9.747310424342193e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.740000000000000e+02, 8.414958430105355e-01, 9.360145970173471e-01, 1.115438388740827e-04, -1.138718982849180e-03, + 2.080000000000000e+02, 8.450656616019064e-01, 9.001743343508666e-01, 1.049946644520869e-04, -1.054125372543546e-03, + 2.420000000000000e+02, 8.484354849657233e-01, 8.669536141642721e-01, 9.911245187696645e-05, -9.770800054880735e-04, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + const auto expect_rs = makeTable(1, { + // Table 1 + 20.59, 28.19, 36.01, 44.09, + + // Table 2 + 32.91, 40.99, 49.36, 58.04, + }); + + check_is_close(pvto, expect_pvto); + check_is_close(rs , expect_rs ); +} + +BOOST_AUTO_TEST_CASE (PVTO_Exceed_Tabdims_Limits) +{ + // This checks that Flow's extended table allocation scheme does what + // it's supposed to do. Normally, one would announce the maximum sizes + // in the RUNSPEC keyword 'TABDIMS'. Note that the maximum sizes need + // to be big enough in order to run the case in ECLIPSE. Other than + // NTPVT, Flow does not really care about these declared maximum sizes. + + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTO Output + +OIL +WATER + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 2 1* 1 +/ +)" }; + + const auto props = std::string { R"( +PVTO +20.59 50.00 1.10615 1.180 + 75.00 1.10164 1.247 / +28.19 70.00 1.12522 1.066 / +44.09 110.00 1.16437 0.880 + 135.00 1.15915 0.924 + 160.00 1.15428 0.968 + 185.00 1.14973 1.012 + 210.00 1.14547 1.056 / +/ +32.91 80.00 1.13304 1.04537 + 114.00 1.12837 1.10009 / +40.99 100.00 1.15276 0.97219 + 134.00 1.14786 1.02086 + 168.00 1.14328 1.06983 / +49.36 120.00 1.17297 0.91124 + 154.00 1.16783 0.95496 / +58.04 140.00 1.19374 0.85942 + 174.00 1.18836 0.89902 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvto = tabdims[ TABDIMS_IBPVTO_OFFSET_ITEM ] - 1; + const auto jbpvto = tabdims[ TABDIMS_JBPVTO_OFFSET_ITEM ] - 1; + const auto nppvto = tabdims[ TABDIMS_NPPVTO_ITEM ]; + const auto nrpvto = tabdims[ TABDIMS_NRPVTO_ITEM ]; + const auto ntpvto = tabdims[ TABDIMS_NTPVTO_ITEM ]; + const auto ncol = 5; + + // Verify declared TABDIMS from input + { + const auto& tabd = es.runspec().tabdims(); + + BOOST_CHECK_EQUAL(tabd.getNumPVTTables(), 2); + BOOST_CHECK_EQUAL(tabd.getNumRSNodes(), 1); + BOOST_CHECK_EQUAL(tabd.getNumPressureNodes(), 2); + } + + // Verify active TABDIMS from dynamic sizes + BOOST_CHECK_EQUAL(nppvto, 5); // Table 1, Comp 3 + BOOST_CHECK_EQUAL(nrpvto, 4); // Table 2 + BOOST_CHECK_EQUAL(ntpvto, 2); + + const auto pvto = std::vector { + &tab[ ibpvto ] + 0, + &tab[ ibpvto ] + ntpvto*nrpvto*nppvto*ncol + }; + + const auto rs = std::vector { + &tab[ jbpvto ] + 0, + &tab[ jbpvto ] + ntpvto*nrpvto + }; + + const auto expect_pvto = makeTable(5, { + // Po 1/Bo 1/(Bo*mu_o) d(1/Bo)/dPo d(1/(Bo*mu_o))/dPo + // + // Table 1 (Comp 1) + 5.000000000000001e+01, 9.040365230755323e-01, 7.661326466741798e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 7.500000000000000e+01, 9.077375549181221e-01, 7.279370929575959e-01, 1.480412737035941e-04, -1.527822148663356e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 1 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 7.000000000000000e+01, 8.887150957146157e-01, 8.336914593945738e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 1 (Comp 3) ---------------------------------------------------------------------------------------------------------- + + 1.100000000000000e+02, 8.588335322964350e-01, 9.759471957914034e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.350000000000000e+02, 8.627011171979468e-01, 9.336592177466957e-01, 1.547033960604739e-04, -1.691519121788311e-03, + 1.600000000000000e+02, 8.663409224798143e-01, 8.949802918179900e-01, 1.455922112746988e-04, -1.547157037148228e-03, + 1.850000000000000e+02, 8.697694241256644e-01, 8.594559526933443e-01, 1.371400658340026e-04, -1.420973564985826e-03, + 2.100000000000000e+02, 8.730040943892027e-01, 8.267084227170479e-01, 1.293868105415319e-04, -1.309901199051855e-03, + + // Table 1 (Comp 4 -- unused) ------------------------------------------------------------------------------------------------ + + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // =========================================================================================================================== + + // Table 2 (Comp 1) + 8.000000000000000e+01, 8.825813740026830e-01, 8.442765470624592e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.140000000000000e+02, 8.862341253312299e-01, 8.056014738168967e-01, 1.074338626043212e-04, -1.137502154281250e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 2) ---------------------------------------------------------------------------------------------------------- + + 1.000000000000000e+02, 8.674832575731288e-01, 8.922980668111468e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.340000000000000e+02, 8.711863816144825e-01, 8.533847752037326e-01, 1.089154129809896e-04, -1.144508576688653e-03, + 1.680000000000000e+02, 8.746763697431950e-01, 8.175844477563677e-01, 1.026467096680142e-04, -1.052950807275436e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 3) ---------------------------------------------------------------------------------------------------------- + + 1.200000000000000e+02, 8.525367230193440e-01, 9.355786873044905e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.540000000000000e+02, 8.562890146682309e-01, 8.966752687738030e-01, 1.103615190849091e-04, -1.144218192079043e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // Table 2 (Comp 4) ---------------------------------------------------------------------------------------------------------- + + 1.400000000000000e+02, 8.377033524888167e-01, 9.747310424342193e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 1.740000000000000e+02, 8.414958430105355e-01, 9.360145970173471e-01, 1.115438388740827e-04, -1.138718982849180e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + const auto expect_rs = makeTable(1, { + // Table 1 + 20.59, 28.19, 44.09, +2.0e20, + + // Table 2 + 32.91, 40.99, 49.36, 58.04, + }); + + check_is_close(pvto, expect_pvto); + check_is_close(rs , expect_rs ); +} + +BOOST_AUTO_TEST_SUITE_END () + +// ===================================================================== + BOOST_AUTO_TEST_SUITE (Water) BOOST_AUTO_TEST_CASE (PVTW) From 877ff028be6f5deb31f89d4ece014c184415eed7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 18:42:03 +0100 Subject: [PATCH 07/11] Tables: Add Structurally Correct Gas PVT Output from PVDG This commit expands the public member function Tables::addPVTTables(const EclipseState&) to call into a new private member function Tables::addGasPVTTables(const EclipseState&) which will create structurally correct TAB vector entries from the PVDG (dry gas) input table data when gas is an active phase in a simulation run. Specifically, the result array has the columns [ Pg, 1/Bg, 1/(Bg*mu_g), d(1/Bg)/dPg, d(1/(Bg*mu_g))/dPg ] The number of table rows is equal to number of PVT regions times the number of pressure nodes declared in input keyword TABDIMS (NPPVT, Item 4). In other words, the result array is expanded to fill NPPVT rows per PVT region. Fill value +2.0e+20. We have verified the results with ECLIPSE 100. As an OPM extension, we will use the maximum number of active pressure nodes across all PVDG tables if the declared maximum pressure nodes in TABDIMS is too small. This will create a TAB vector that is not compatible with ECLIPSE, but which will nevertheless be useful in the context of ResInsight's flux calculation. --- opm/output/eclipse/Tables.hpp | 8 ++ src/opm/output/eclipse/Tables.cpp | 153 ++++++++++++++++++++++++++++++ 2 files changed, 161 insertions(+) diff --git a/opm/output/eclipse/Tables.hpp b/opm/output/eclipse/Tables.hpp index 5c02a8dca..12e67ae13 100644 --- a/opm/output/eclipse/Tables.hpp +++ b/opm/output/eclipse/Tables.hpp @@ -113,6 +113,14 @@ namespace Opm { const bool oil, const bool wat); + /// Add gas PVT tables (keywords PVDG and PVTG) to the tabular data + /// (TABDIMS and TAB vectors). + /// + /// \param[in] es Valid \c EclipseState object with accurate table + /// dimensions ("TABDIMS" keyword) and an initialised \c + /// TableManager sub-object. + void addGasPVTTables(const EclipseState& es); + /// Add oil PVT tables (keywords PVCDO, PVDO and PVTO) to the /// tabular data (TABDIMS and TAB vectors). /// diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 525988bd1..952a9dcaa 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -28,6 +28,7 @@ #include #include #include // PVTW, PVCDO +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include namespace { @@ -1086,6 +1088,132 @@ namespace { namespace SatFunc { /// PVT functions. namespace { namespace PVTFunc { + /// Functions to create linearised, padded, and normalised gas PVT + /// output tables from various input gas PVT function keywords. + namespace Gas { + /// Create linearised and padded 'TAB' vector entries of normalised + /// gas tables for all PVT function regions from PVDG (dry gas) + /// keyword data. + /// + /// \param[in] numPressNodes Number of pressure nodes (rows) to + /// allocate in the output vector for each table. Expected to be + /// equal to the number of declared pressure nodes in the + /// simulation run's TABDIMS keyword (NPPVT, Item 4). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal), formation volume factors + /// (Rm3/Sm3), and viscosity values (Pascal seconds) to declared + /// conventions of the run specification. + /// + /// \param[in] pvdg Collection of PVDG tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output gas + /// PVT tables. A unit-converted copy of the input table \p pvdg + /// with added derivatives. + std::vector + fromPVDG(const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const Opm::TableContainer& pvdg) + { + // Columns [ Pg, 1/Bg, 1/(Bg*mu_g), derivatives ] + using PVDG = ::Opm::PvdgTable; + + const auto numTab = pvdg.size(); + const auto numPrim = std::size_t{1}; // No sub-tables + const auto numRows = numPressNodes; // One row per pressure node + const auto numDep = std::size_t{2}; // 1/Bg, 1/(Bg*mu_g) + + // PVDG fill value = +2.0e20 + const auto fillVal = +2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvdg](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + const auto& t = pvdg.getTable(tableID); + + auto numActRows = std::size_t{0}; + + // Column 0: Pg + { + const auto uPress = ::Opm::UnitSystem::measure::pressure; + + const auto& Pg = t.getPressureColumn(); + + numActRows = Pg.size(); + + std::transform(std::begin(Pg), std::end(Pg), + linTable.column(tableID, primID, 0), + [&units](const double p) -> double + { + return units.from_si(uPress, p); + }); + } + + // Column 1: 1/Bg + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + gas_inverse_formation_volume_factor; + + const auto& Bg = t.getFormationFactorColumn(); + std::transform(std::begin(Bg), std::end(Bg), + linTable.column(tableID, primID, 1), + [&units](const double B) -> double + { + return units.from_si(uRecipFVF, 1.0 / B); + }); + } + + // Column 2: 1/(Bg*mu_g) + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + gas_inverse_formation_volume_factor; + + const auto uVisc = ::Opm::UnitSystem::measure::viscosity; + + const auto& Bg = t.getFormationFactorColumn(); + const auto& mu_g = t.getViscosityColumn(); + + std::transform(std::begin(Bg), std::end(Bg), + std::begin(mu_g), + linTable.column(tableID, primID, 2), + [&units](const double B, const double mu) -> double + { + return units.from_si(uRecipFVF, 1.0 / B) + / units.from_si(uVisc , mu); + }); + } + + // Inform createPropfuncTable() of number of active rows in + // this table. Needed to compute slopes of piecewise linear + // interpolants. + return numActRows; + }); + } + + /// Extract maximum effective pressure nodes in PVDG table data + /// + /// \param[in] pvdg Collection of PVDG tables for all PVT regions. + /// + /// \return Maximum number of table rows across all tables of \p + /// pvdo. + std::size_t maxNumPressNodes(const Opm::TableContainer& pvdg) + { + using PVDG = ::Opm::PvdgTable; + auto tabID = std::vector(pvdg.size()); + + std::iota(tabID.begin(), tabID.end(), std::size_t{0}); + + return std::accumulate(tabID.begin(), tabID.end(), std::size_t{0}, + [&pvdg](const std::size_t n, const std::size_t table) + { + return std::max(n, pvdg.getTable(table).numRows()); + }); + } + } // Gas + /// Functions to create linearised, padded, and normalised oil PVT /// output tables from various input oil PVT function keywords. namespace Oil { @@ -1810,6 +1938,7 @@ namespace Opm { { const auto& phases = es.runspec().phases(); + if (phases.active(Phase::GAS)) { this->addGasPVTTables (es); } if (phases.active(Phase::OIL)) { this->addOilPVTTables (es); } if (phases.active(Phase::WATER)) { this->addWaterPVTTables(es); } } @@ -1981,6 +2110,30 @@ namespace Opm { } } + void Tables::addGasPVTTables(const EclipseState& es) + { + const auto& tabMgr = es.getTableManager(); + const auto& tabd = es.runspec().tabdims(); + + const auto numPressNodes = tabd.getNumPressureNodes(); + + const auto hasPVDG = tabMgr.hasTables("PVDG"); + + if (hasPVDG) { + // Dry gas, pressure dependent compressibility. + const auto& pvdg = tabMgr.getPvdgTables(); + + const auto numRows = + std::max(numPressNodes, PVTFunc::Gas::maxNumPressNodes(pvdg)); + + const auto data = PVTFunc::Gas::fromPVDG(numRows, this->units, pvdg); + + this->addData(TABDIMS_IBPVTG_OFFSET_ITEM, data); + this->m_tabdims[TABDIMS_NPPVTG_ITEM] = numRows; + this->m_tabdims[TABDIMS_NTPVTG_ITEM] = pvdg.size(); + } + } + void Tables::addOilPVTTables(const EclipseState& es) { const auto& tabMgr = es.getTableManager(); From 25d7b538aac1ba74bf53d6bc610ee28e779d516b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 28 Mar 2019 19:30:31 +0100 Subject: [PATCH 08/11] Tables: Add Structurally Correct Gas PVT Output from PVTG This commit expands the private member function Tables::addGasPVTTables(const EclipseState&) to create structurally correct TAB vector entries from the PVTG (wet gas) input table data when gas is an active phase in a simulation run. Specifically, the main result array has the columns [ Rv, 1/Bg, 1/(Bg*mu_g), d(1/Bg)/dRv, d(1/(Bg*mu_g))/dRv ] and the ancillary table (base pointer JBPVTG) holds the gas pressure nodes. Note that while we do create structurally correct output tables, we do not fill in undersaturated states that have been defaulted in the input table. The number of table rows in the main table is equal to number of PVT regions times the number of declared pressure nodes (TABDIMS item 4, NPPVT) times the number of declared composition nodes (TABDIMS item 6, NRPVT). In other words, the main result array is expanded to fill NRPVT rows per gas pressure node per PVT region. Fill value -2.0e+20. We have verified the results with ECLIPSE 100. The ancillary table is expanded to the number of declared pressure nodes for each PVT region. Here the fill value is +2.0e+20. As an OPM extension, we will use the maximum number of active composition and pressure nodes across all PVTG tables if the declared maximum pressure nodes in TABDIMS is too small. This will create TAB vector representations that are not compatible with ECLIPSE, but which will nevertheless be useful in the context of ResInsight's flux calculation. Add unit tests for all gas-related PVT tables. --- src/opm/output/eclipse/Tables.cpp | 248 ++++++++++- tests/test_Tables.cpp | 691 ++++++++++++++++++++++++++++++ 2 files changed, 938 insertions(+), 1 deletion(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 952a9dcaa..216da0cdb 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -30,6 +30,7 @@ #include // PVTW, PVCDO #include #include +#include #include #include #include @@ -1193,6 +1194,205 @@ namespace { namespace PVTFunc { }); } + /// Create linearised and padded 'TAB' vector entries of normalised + /// gas tables for all PVT function regions from PVTG (wet gas with + /// volatile/vaporised oil) keyword data. + /// + /// \param[in] numCompNodes Number of composition nodes (rows per + /// sub-table) to allocate in the output vector for each table. + /// Expected to be equal to the number of declared composition + /// nodes in the simulation run's TABDIMS keyword (NRPVT, Item + /// 6). + /// + /// \param[in] numPressNodes Number of pressure nodes (sub-tables) + /// to allocate in the output vector for each gas PVT table. + /// Expected to be equal to the number of declared pressure nodes + /// in the simulation run's TABDIMS keyword (NPPVT, Item 4). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention vaporised oil composition values (Sm3/Sm3), + /// formation volume factors (Rm3/Sm3), and viscosity values + /// (Pa*s) to declared conventions of the run specification. + /// + /// \param[in] pvtg Collection of PVTG tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output gas + /// PVT tables. A unit-converted copy of the input table \p pvtg + /// with added derivatives. + std::vector + fromPVTG(const std::size_t numCompNodes, + const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const std::vector& pvtg) + { + // Columns [ Rv, 1/Bg, 1/(Bg*mu_g), derivatives ] + const auto numTab = pvtg.size(); + const auto numPrim = numPressNodes; + const auto numRows = numCompNodes; + const auto numDep = std::size_t{2}; // 1/Bg, 1/(Bg*mu_g) + + // PVTG fill value = -2.0e20 + const auto fillVal = -2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvtg](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + auto numActRows = std::size_t{0}; + + if (primID >= pvtg[tableID].size()) { + // Composition node outside current table's active set. + // No active rows in this subtable. + return numActRows; + } + + const auto& t = pvtg[tableID].getUnderSaturatedTable(primID); + + // Column 0: Rv + { + const auto uRv = ::Opm::UnitSystem::measure::oil_gas_ratio; + + const auto& Rv = t.getColumn(0); + + numActRows = Rv.size(); + + std::transform(std::begin(Rv), std::end(Rv), + linTable.column(tableID, primID, 0), + [&units](const double rv) -> double + { + return units.from_si(uRv, rv); + }); + } + + // Column 1: 1/Bg + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + gas_inverse_formation_volume_factor; + + const auto& Bg = t.getColumn(1); + + std::transform(std::begin(Bg), std::end(Bg), + linTable.column(tableID, primID, 1), + [&units](const double B) -> double + { + return units.from_si(uRecipFVF, 1.0 / B); + }); + } + + // Column 2: 1/(Bg*mu_g) + { + const auto uRecipFVF = ::Opm::UnitSystem::measure:: + gas_inverse_formation_volume_factor; + + const auto uVisc = ::Opm::UnitSystem::measure::viscosity; + + const auto& Bg = t.getColumn(1); + const auto& mu_g = t.getColumn(2); + + std::transform(std::begin(Bg), std::end(Bg), + std::begin(mu_g), + linTable.column(tableID, primID, 2), + [&units](const double B, const double mu) -> double + { + return units.from_si(uRecipFVF, 1.0 / B) + / units.from_si(uVisc , mu); + }); + } + + // Inform createPropfuncTable() of number of active rows in + // this table. Needed to compute slopes of piecewise linear + // interpolants. + return numActRows; + }); + } + + /// Create linearised and padded 'TAB' vector entries of normalised + /// gas pressure nodes for all PVT function regions from PVTG (wet + /// gas with volatile/vaporised oil) keyword data. + /// + /// \param[in] numPressNodes Number of pressure nodes to allocate in + /// the output vector for each gas PVT table. Expected to be + /// equal to the number of declared pressure nodes in the + /// simulation run's TABDIMS keyword (NPPVT, Item 4). + /// + /// \param[in] units Active unit system. Needed to convert SI + /// convention pressure values (Pascal) to declared conventions + /// of the run specification. + /// + /// \param[in] pvtg Collection of PVTG tables for all PVT regions. + /// + /// \return Linearised and padded 'TAB' vector values for output gas + /// PVT tables. A unit-converted copy of the primary keys in + /// input table \p pvtg. + std::vector + pressureNodes(const std::size_t numPressNodes, + const Opm::UnitSystem& units, + const std::vector& pvtg) + { + // Columns [ Pg ] + const auto numTab = pvtg.size(); + + // One set of pressure nodes per table. + const auto numPrim = std::size_t{1}; + + const auto numRows = numPressNodes; + + // No dependent variables. + const auto numDep = std::size_t{0}; + + // Pressure node fill value = +2.0e20 + const auto fillVal = +2.0e20; + + return createPropfuncTable(numTab, numPrim, numRows, numDep, fillVal, + [&units, &pvtg](const std::size_t tableID, + const std::size_t primID, + Opm::LinearisedOutputTable& linTable) + -> std::size_t + { + const auto uPress = ::Opm::UnitSystem::measure::pressure; + + const auto& t = pvtg[tableID].getSaturatedTable(); + const auto& Pg = t.getColumn(0); + + const auto numActRows = Pg.size(); + + std::transform(std::begin(Pg), std::end(Pg), + linTable.column(tableID, primID, 0), + [&units](const double p) -> double + { + return units.from_si(uPress, p); + }); + + return numActRows; + }); + } + + /// Extract maximum effective composition nodes in PVTG table data + /// + /// \param[in] pvtg Collection of PVTG tables for all PVT regions. + /// + /// \return Maximum number of active rows across all sub-tables of \p + /// pvtg. + std::size_t maxNumCompNodes(const std::vector& pvtg) + { + auto max_nrpvt = std::size_t{0}; + + for (const auto& table : pvtg) { + const auto max_nrpvt_tab = + std::accumulate(table.begin(), table.end(), std::size_t{0}, + [](const std::size_t n, const Opm::SimpleTable& t) + { + return std::max(n, t.numRows()); + }); + + max_nrpvt = std::max(max_nrpvt, max_nrpvt_tab); + } + + return max_nrpvt; + } + /// Extract maximum effective pressure nodes in PVDG table data /// /// \param[in] pvdg Collection of PVDG tables for all PVT regions. @@ -1212,6 +1412,23 @@ namespace { namespace PVTFunc { return std::max(n, pvdg.getTable(table).numRows()); }); } + + /// Extract maximum effective pressure nodes in PVTG table data + /// + /// \param[in] pvtg Collection of PVTG tables for all PVT regions. + /// + /// \return Maximum number of active keys across all tables of \p + /// pvtg. + std::size_t maxNumPressNodes(const std::vector& pvtg) + { + if (pvtg.empty()) { return 0; } + + return std::accumulate(std::begin(pvtg), std::end(pvtg), std::size_t{0}, + [](const std::size_t n, const Opm::PvtgTable& t) -> std::size_t + { + return std::max(n, t.getSaturatedTable().numRows()); + }); + } } // Gas /// Functions to create linearised, padded, and normalised oil PVT @@ -2117,9 +2334,38 @@ namespace Opm { const auto numPressNodes = tabd.getNumPressureNodes(); + const auto hasPVTG = !tabMgr.getPvtgTables().empty(); const auto hasPVDG = tabMgr.hasTables("PVDG"); - if (hasPVDG) { + if (hasPVTG + hasPVDG != 1) { + // Inconsistent table specification. Maybe throw here? + return; + } + + if (hasPVTG) { + // Wet gas with vaporised/volatile oil. + const auto& pvtg = tabMgr.getPvtgTables(); + + const auto numCompNodes = + std::max(tabd.getNumRSNodes(), PVTFunc::Gas::maxNumCompNodes(pvtg)); + + const auto numPrimary = + std::max(numPressNodes, PVTFunc::Gas::maxNumPressNodes(pvtg)); + + const auto data = PVTFunc::Gas:: + fromPVTG(numCompNodes, numPrimary, this->units, pvtg); + + const auto pressData = PVTFunc::Gas:: + pressureNodes(numPrimary, this->units, pvtg); + + this->addData(TABDIMS_IBPVTG_OFFSET_ITEM, data); + this->addData(TABDIMS_JBPVTG_OFFSET_ITEM, pressData); + + this->m_tabdims[TABDIMS_NPPVTG_ITEM] = numPrimary; + this->m_tabdims[TABDIMS_NRPVTG_ITEM] = numCompNodes; + this->m_tabdims[TABDIMS_NTPVTG_ITEM] = pvtg.size(); + } + else { // Dry gas, pressure dependent compressibility. const auto& pvdg = tabMgr.getPvdgTables(); diff --git a/tests/test_Tables.cpp b/tests/test_Tables.cpp index 979e0c529..6349f3ae0 100644 --- a/tests/test_Tables.cpp +++ b/tests/test_Tables.cpp @@ -1656,6 +1656,697 @@ END } } +BOOST_AUTO_TEST_SUITE (Gas) + +BOOST_AUTO_TEST_CASE (PVDG_Regular) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVDG Output + +GAS + +FIELD + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 16 1* 1* +/ +)" }; + + const auto props = std::string { R"( +PVDG + 14.7 166.666 0.0080 + 264.7 12.093 0.0096 + 514.7 6.274 0.0112 +1014.7 3.197 0.0140 +2014.7 1.614 0.0189 +2514.7 1.294 0.0208 +3014.7 1.080 0.0228 +4014.7 0.811 0.0268 +5014.7 0.649 0.0309 +9014.7 0.386 0.0470 / + 400.0 5.900 0.0130 + 800.0 2.950 0.0135 +1200.0 1.960 0.0140 +1600.0 1.470 0.0145 +2000.0 1.180 0.0150 +2400.0 0.980 0.0155 +2800.0 0.840 0.0160 +3200.0 0.740 0.0165 +3600.0 0.650 0.0170 +4000.0 0.590 0.0175 +4400.0 0.540 0.0180 +4800.0 0.490 0.0185 +5200.0 0.450 0.0190 +5600.0 0.420 0.0195 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtg = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1; + const auto nppvtg = tabdims[ TABDIMS_NPPVTG_ITEM ]; + const auto ntpvtg = tabdims[ TABDIMS_NTPVTG_ITEM ]; + const auto ncol = 5; + + // Pg table defaulted + BOOST_CHECK_EQUAL(tabdims[TABDIMS_JBPVTG_OFFSET_ITEM], 1); + BOOST_CHECK_EQUAL(tabdims[TABDIMS_NRPVTG_ITEM], 1); + + BOOST_CHECK_EQUAL(nppvtg, 16); + BOOST_CHECK_EQUAL(ntpvtg, 2); + + const auto pvdg = std::vector { + &tab[ ibpvtg ] + 0, + &tab[ ibpvtg ] + ntpvtg*nppvtg*ncol + }; + + const auto expect_pvdg = makeTable(5, { + // Pg 1/Bg 1/(Bg*mu_g) d(1/Bg)/dPg d(1/(Bg*mu_g))/dPg + // + // Table 1 (10 pressure nodes) + 1.470000000000000e+01, 6.000024000096002e-03, 7.500030000120003e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 2.647000000000000e+02, 8.269246671628215e-02, 8.613798616279391e+00, 3.067697708647446e-04, 3.145518246506956e-02, + 5.147000000000000e+02, 1.593879502709595e-01, 1.423106698847853e+01, 3.067819342187094e-04, 2.246907348879655e-02, + 1.014700000000000e+03, 3.127932436659369e-01, 2.234237454756692e+01, 3.068105867899547e-04, 1.622261511817678e-02, + 2.014700000000000e+03, 6.195786864931847e-01, 3.278194108429548e+01, 3.067854428272479e-04, 1.043956653672856e-02, + 2.514700000000000e+03, 7.727975270479135e-01, 3.715372726191892e+01, 3.064376811094576e-04, 8.743572355246868e-03, + 3.014700000000000e+03, 9.259259259259259e-01, 4.061078622482131e+01, 3.062567977560249e-04, 6.914117925804789e-03, + 4.014699999999999e+03, 1.233045622688040e+00, 4.600916502567312e+01, 3.071196967621139e-04, 5.398378800851812e-03, + 5.014700000000000e+03, 1.540832049306626e+00, 4.986511486429210e+01, 3.077864266185862e-04, 3.855949838618981e-03, + 9.014699999999999e+03, 2.590673575129534e+00, 5.512071436445817e+01, 2.624603814557271e-04, 1.313899875041518e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // =========================================================================================================================== + + // Table 2 (14 pressure nodes) + 4.000000000000000e+02, 1.694915254237289e-01, 1.303780964797914e+01, 2.000000000000000e+20, 2.000000000000000e+20, + 8.000000000000000e+02, 3.389830508474577e-01, 2.510985561833020e+01, 4.237288135593221e-04, 3.018011492587764e-02, + 1.200000000000000e+03, 5.102040816326532e-01, 3.644314868804666e+01, 4.280525769629887e-04, 2.833323267429114e-02, + 1.600000000000000e+03, 6.802721088435375e-01, 4.691531785127844e+01, 4.251700680272108e-04, 2.618042290807946e-02, + 2.000000000000000e+03, 8.474576271186441e-01, 5.649717514124294e+01, 4.179637956877669e-04, 2.395464322491127e-02, + 2.400000000000000e+03, 1.020408163265306e+00, 6.583278472679396e+01, 4.323763403666553e-04, 2.333902396387753e-02, + 2.800000000000000e+03, 1.190476190476191e+00, 7.440476190476191e+01, 4.251700680272113e-04, 2.142994294491990e-02, + 3.200000000000000e+03, 1.351351351351352e+00, 8.190008190008190e+01, 4.021879021879017e-04, 1.873829998829995e-02, + 3.600000000000000e+03, 1.538461538461539e+00, 9.049773755656106e+01, 4.677754677754675e-04, 2.149413914119791e-02, + 4.000000000000000e+03, 1.694915254237288e+00, 9.685230024213075e+01, 3.911342894393748e-04, 1.588640671392424e-02, + 4.400000000000000e+03, 1.851851851851852e+00, 1.028806584362140e+02, 3.923414940364085e-04, 1.507089548520804e-02, + 4.800000000000000e+03, 2.040816326530613e+00, 1.103143960286818e+02, 4.724111866969022e-04, 1.858434398116948e-02, + 5.199999999999999e+03, 2.222222222222222e+00, 1.169590643274854e+02, 4.535147392290250e-04, 1.661167074700910e-02, + 5.599999999999999e+03, 2.380952380952381e+00, 1.221001221001221e+02, 3.968253968253976e-04, 1.285264443159182e-02, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + }); + + check_is_close(pvdg, expect_pvdg); +} + +BOOST_AUTO_TEST_CASE (PVDG_Exceed_Tabdims_Limits) +{ + // This checks that Flow's extended table allocation scheme does what + // it's supposed to do. Normally, one would announce the maximum sizes + // in the RUNSPEC keyword 'TABDIMS'. Note that the maximum sizes need + // to be big enough in order to run the case in ECLIPSE. Other than + // NTPVT, Flow does not really care about these declared maximum sizes. + + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVDG Output + +GAS + +FIELD + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 2 1* 1* +/ +)" }; + + const auto props = std::string { R"( +PVDG + 14.7 166.666 0.0080 + 264.7 12.093 0.0096 + 514.7 6.274 0.0112 +1014.7 3.197 0.0140 +2014.7 1.614 0.0189 +2514.7 1.294 0.0208 +3014.7 1.080 0.0228 +4014.7 0.811 0.0268 +5014.7 0.649 0.0309 +9014.7 0.386 0.0470 / + 400.0 5.900 0.0130 + 800.0 2.950 0.0135 +1200.0 1.960 0.0140 +1600.0 1.470 0.0145 +2000.0 1.180 0.0150 +2400.0 0.980 0.0155 +2800.0 0.840 0.0160 +3200.0 0.740 0.0165 +3600.0 0.650 0.0170 +4000.0 0.590 0.0175 +4400.0 0.540 0.0180 +4800.0 0.490 0.0185 +5200.0 0.450 0.0190 +5600.0 0.420 0.0195 / +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtg = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1; + const auto nppvtg = tabdims[ TABDIMS_NPPVTG_ITEM ]; + const auto ntpvtg = tabdims[ TABDIMS_NTPVTG_ITEM ]; + const auto ncol = 5; + + // Pg table defaulted + BOOST_CHECK_EQUAL(tabdims[TABDIMS_JBPVTG_OFFSET_ITEM], 1); + BOOST_CHECK_EQUAL(tabdims[TABDIMS_NRPVTG_ITEM], 1); + + BOOST_CHECK_EQUAL(nppvtg, 14); // Table 2 + BOOST_CHECK_EQUAL(ntpvtg, 2); + + const auto pvdg = std::vector { + &tab[ ibpvtg ] + 0, + &tab[ ibpvtg ] + ntpvtg*nppvtg*ncol + }; + + const auto expect_pvdg = makeTable(5, { + // Pg 1/Bg 1/(Bg*mu_g) d(1/Bg)/dPg d(1/(Bg*mu_g))/dPg + // + // Table 1 (10 pressure nodes) + 1.470000000000000e+01, 6.000024000096002e-03, 7.500030000120003e-01, 2.000000000000000e+20, 2.000000000000000e+20, + 2.647000000000000e+02, 8.269246671628215e-02, 8.613798616279391e+00, 3.067697708647446e-04, 3.145518246506956e-02, + 5.147000000000000e+02, 1.593879502709595e-01, 1.423106698847853e+01, 3.067819342187094e-04, 2.246907348879655e-02, + 1.014700000000000e+03, 3.127932436659369e-01, 2.234237454756692e+01, 3.068105867899547e-04, 1.622261511817678e-02, + 2.014700000000000e+03, 6.195786864931847e-01, 3.278194108429548e+01, 3.067854428272479e-04, 1.043956653672856e-02, + 2.514700000000000e+03, 7.727975270479135e-01, 3.715372726191892e+01, 3.064376811094576e-04, 8.743572355246868e-03, + 3.014700000000000e+03, 9.259259259259259e-01, 4.061078622482131e+01, 3.062567977560249e-04, 6.914117925804789e-03, + 4.014699999999999e+03, 1.233045622688040e+00, 4.600916502567312e+01, 3.071196967621139e-04, 5.398378800851812e-03, + 5.014700000000000e+03, 1.540832049306626e+00, 4.986511486429210e+01, 3.077864266185862e-04, 3.855949838618981e-03, + 9.014699999999999e+03, 2.590673575129534e+00, 5.512071436445817e+01, 2.624603814557271e-04, 1.313899875041518e-03, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, 2.000000000000000e+20, + + // =========================================================================================================================== + + // Table 2 (14 pressure nodes) + 4.000000000000000e+02, 1.694915254237289e-01, 1.303780964797914e+01, 2.000000000000000e+20, 2.000000000000000e+20, + 8.000000000000000e+02, 3.389830508474577e-01, 2.510985561833020e+01, 4.237288135593221e-04, 3.018011492587764e-02, + 1.200000000000000e+03, 5.102040816326532e-01, 3.644314868804666e+01, 4.280525769629887e-04, 2.833323267429114e-02, + 1.600000000000000e+03, 6.802721088435375e-01, 4.691531785127844e+01, 4.251700680272108e-04, 2.618042290807946e-02, + 2.000000000000000e+03, 8.474576271186441e-01, 5.649717514124294e+01, 4.179637956877669e-04, 2.395464322491127e-02, + 2.400000000000000e+03, 1.020408163265306e+00, 6.583278472679396e+01, 4.323763403666553e-04, 2.333902396387753e-02, + 2.800000000000000e+03, 1.190476190476191e+00, 7.440476190476191e+01, 4.251700680272113e-04, 2.142994294491990e-02, + 3.200000000000000e+03, 1.351351351351352e+00, 8.190008190008190e+01, 4.021879021879017e-04, 1.873829998829995e-02, + 3.600000000000000e+03, 1.538461538461539e+00, 9.049773755656106e+01, 4.677754677754675e-04, 2.149413914119791e-02, + 4.000000000000000e+03, 1.694915254237288e+00, 9.685230024213075e+01, 3.911342894393748e-04, 1.588640671392424e-02, + 4.400000000000000e+03, 1.851851851851852e+00, 1.028806584362140e+02, 3.923414940364085e-04, 1.507089548520804e-02, + 4.800000000000000e+03, 2.040816326530613e+00, 1.103143960286818e+02, 4.724111866969022e-04, 1.858434398116948e-02, + 5.199999999999999e+03, 2.222222222222222e+00, 1.169590643274854e+02, 4.535147392290250e-04, 1.661167074700910e-02, + 5.599999999999999e+03, 2.380952380952381e+00, 1.221001221001221e+02, 3.968253968253976e-04, 1.285264443159182e-02, + }); + + check_is_close(pvdg, expect_pvdg); +} + +BOOST_AUTO_TEST_CASE (PVTG_USat_Defaulted) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTG Output + +GAS + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 7 1* 2 +/ +)" }; + + const auto props = std::string { R"( +PVTG +30 0.00014 0.0523 0.0234 + 0 0.0521 0.0238 / +90 0.00012 0.0132 0.0252 + 0 0.0131 0.0253 / +150 0.00015 0.00877 0.0281 + 0 0.00861 0.0275 / +210 0.00019 0.00554 0.0318 + 0 0.00555 0.0302 / +270 0.00029 0.00417 0.0355 + 0 0.00421 0.0330 / +330 0.00049 0.00357 0.0392 + 0 0.00361 0.0358 / +530 0.00060 0.00356 0.0393 + 0 0.00360 0.0359 / +/ +60 0.00014 0.0523 0.0234 / +120 0.00012 0.0132 0.0252 / +180 0.00015 0.00877 0.0281 / +240 0.00019 0.00554 0.0318 / +300 0.00029 0.00417 0.0355 / +360 0.00049 0.00357 0.0392 / +560 0.00060 0.00356 0.0393 + 0 0.00360 0.0359 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtg = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1; + const auto jbpvtg = tabdims[ TABDIMS_JBPVTG_OFFSET_ITEM ] - 1; + const auto nppvtg = tabdims[ TABDIMS_NPPVTG_ITEM ]; + const auto nrpvtg = tabdims[ TABDIMS_NRPVTG_ITEM ]; + const auto ntpvtg = tabdims[ TABDIMS_NTPVTG_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(nppvtg, 7); + BOOST_CHECK_EQUAL(nrpvtg, 2); + BOOST_CHECK_EQUAL(ntpvtg, 2); + + const auto pvtg = std::vector { + &tab[ ibpvtg ] + 0, + &tab[ ibpvtg ] + ntpvtg*nppvtg*nrpvtg*ncol + }; + + const auto pg = std::vector { + &tab[ jbpvtg ] + 0, + &tab[ jbpvtg ] + ntpvtg*nppvtg + }; + + const auto expect_pvtg = makeTable(5, { + // Rv 1/Bg 1/(Bg*mu_g) d(1/Bg)/dRv d(1/(Bg*mu_g))/dRv + // + // Table 1 (Pg 1) + 1.400000000000000e-04, 1.912045889101339e+01, 8.171136278210848e+02, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 1.919385796545106e+01, 8.064646203971031e+02, -5.242791031262176e+02, 7.606433874272674e+04, + + // Table 1 (Pg 2) ------------------------------------------------------------------------------------------------------------ + + 1.200000000000000e-04, 7.575757575757576e+01, 3.006253006253006e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 7.633587786259541e+01, 3.017228374015629e+03, -4.819184208497044e+03, -9.146139802185188e+04, + + // Table 1 (Pg 3) ------------------------------------------------------------------------------------------------------------ + + 1.500000000000000e-04, 1.140250855188141e+02, 4.057832224868830e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 1.161440185830430e+02, 4.223418857565199e+03, -1.412622042819227e+04, -1.103910884642458e+06, + + // Table 1 (Pg 4) ------------------------------------------------------------------------------------------------------------ + + 1.900000000000000e-04, 1.805054151624549e+02, 5.676270917058329e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 1.801801801801802e+02, 5.966231131794045e+03, 1.711763064603725e+03, -1.526106393345871e+06, + + // Table 1 (Pg 5) ------------------------------------------------------------------------------------------------------------ + + 2.900000000000000e-04, 2.398081534772182e+02, 6.755159252879387e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 2.375296912114014e+02, 7.197869430648528e+03, 7.856766433850982e+03, -1.526586819893588e+06, + + // Table 1 (Pg 6) ------------------------------------------------------------------------------------------------------------ + + 4.900000000000000e-04, 2.801120448179272e+02, 7.145715429028755e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 2.770083102493075e+02, 7.737662297466691e+03, 6.334152180856531e+03, -1.208054833546807e+06, + + // Table 1 (Pg 7) ------------------------------------------------------------------------------------------------------------ + + 5.999999999999999e-04, 2.808988764044944e+02, 7.147554106984589e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 2.777777777777778e+02, 7.737542556484059e+03, 5.201831044527695e+03, -9.833140824991158e+05, + + // ========================================================================================================================== + + // Table 2 (Pg 1) + 1.400000000000000e-04, 1.912045889101339e+01, 8.171136278210848e+02, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 2) ------------------------------------------------------------------------------------------------------------ + + 1.200000000000000e-04, 7.575757575757576e+01, 3.006253006253006e+03, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 3) ------------------------------------------------------------------------------------------------------------ + + 1.500000000000000e-04, 1.140250855188141e+02, 4.057832224868830e+03, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 4) ------------------------------------------------------------------------------------------------------------ + + 1.900000000000000e-04, 1.805054151624549e+02, 5.676270917058329e+03, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 5) ------------------------------------------------------------------------------------------------------------ + + 2.900000000000000e-04, 2.398081534772182e+02, 6.755159252879387e+03, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 6) ------------------------------------------------------------------------------------------------------------ + + 4.900000000000000e-04, 2.801120448179272e+02, 7.145715429028755e+03, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 7) ------------------------------------------------------------------------------------------------------------ + + 5.999999999999999e-04, 2.808988764044944e+02, 7.147554106984589e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 0, 2.777777777777778e+02, 7.737542556484059e+03, 5.201831044527695e+03, -9.833140824991158e+05, + }); + + const auto expect_pg = makeTable(1, { + // Table 1 + 30, 90, 150, 210, 270, 330, 530, + + // Table 2 + 60, 120, 180, 240, 300, 360, 560, + }); + + check_is_close(pvtg, expect_pvtg); + check_is_close(pg , expect_pg ); +} + +BOOST_AUTO_TEST_CASE (PVTG_USat_Full_Large_Tabdims) +{ + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTG Output + +GAS + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 1 1* 7 1* 5 +/ +)" }; + + const auto props = std::string { R"( +PVTG + 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 / + 90.00 0.00000627 0.013608 0.01547 + 0.00000313 0.013611 0.01546 + 0.00000000 0.013615 0.01544 / +110.00 0.00000798 0.011072 0.01609 + 0.00000399 0.011076 0.01607 + 0.00000000 0.011081 0.01605 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtg = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1; + const auto jbpvtg = tabdims[ TABDIMS_JBPVTG_OFFSET_ITEM ] - 1; + const auto nppvtg = tabdims[ TABDIMS_NPPVTG_ITEM ]; + const auto nrpvtg = tabdims[ TABDIMS_NRPVTG_ITEM ]; + const auto ntpvtg = tabdims[ TABDIMS_NTPVTG_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(nppvtg, 7); + BOOST_CHECK_EQUAL(nrpvtg, 5); + BOOST_CHECK_EQUAL(ntpvtg, 1); + + const auto pvtg = std::vector { + &tab[ ibpvtg ] + 0, + &tab[ ibpvtg ] + ntpvtg*nppvtg*nrpvtg*ncol + }; + + const auto pg = std::vector { + &tab[ jbpvtg ] + 0, + &tab[ jbpvtg ] + ntpvtg*nppvtg + }; + + const auto expect_pvtg = makeTable(5, { + // Rv 1/Bg 1/(Bg*mu_g) d(1/Bg)/dRv d(1/(Bg*mu_g))/dRv + // + // Table 1 (Pg 1) + 4.970000000000000e-06, 4.006731308598445e+01, 2.780521380012800e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 2.480000000000000e-06, 4.006731308598445e+01, 2.782452297637809e+03, 0, -7.754689257064208e+05, + 0, 4.006731308598445e+01, 2.782452297637809e+03, 0, 0, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 2) ------------------------------------------------------------------------------------------------------------ + + 5.210000000000000e-06, 5.669255626736210e+01, 3.802317657100074e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 2.610000000000000e-06, 5.668612890425712e+01, 3.804438181493767e+03, 2.472062732683009e+03, -8.155863052665014e+05, + 0, 5.667970299835629e+01, 3.804006912641362e+03, 2.462032912196776e+03, 1.652371082011811e+05, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 3) ------------------------------------------------------------------------------------------------------------ + + 6.270000000000000e-06, 7.348618459729570e+01, 4.750238176942192e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 3.130000000000000e-06, 7.346998751010213e+01, 4.752263098971676e+03, 5.158308023431543e+03, -6.448796272243677e+05, + 0, 7.344840249724568e+01, 4.757020887127311e+03, 6.896170241676778e+03, -1.520060113621228e+06, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 4) ------------------------------------------------------------------------------------------------------------ + + 7.980000000000000e-06, 9.031791907514450e+01, 5.613295156938751e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 3.990000000000000e-06, 9.028530155290719e+01, 5.618251496758381e+03, 8.174817603337499e+03, -1.242190430985102e+06, + 0, 9.024456276509339e+01, 5.622714190971551e+03, 1.021022250972279e+04, -1.118469727611510e+06, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 5) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 6) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 1 (Pg 7) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + }); + + const auto expect_pg = makeTable(1, { + // Table 1 + 50.00, 70.00, 90.00, 110.00, 2.0e20, 2.0e20, 2.0e20, + }); + + check_is_close(pvtg, expect_pvtg); + check_is_close(pg , expect_pg ); +} + +BOOST_AUTO_TEST_CASE (PVTG_Exceed_Tabdims_Limits) +{ + // This checks that Flow's extended table allocation scheme does what + // it's supposed to do. Normally, one would announce the maximum sizes + // in the RUNSPEC keyword 'TABDIMS'. Note that the maximum sizes need + // to be big enough in order to run the case in ECLIPSE. Other than + // NTPVT, Flow does not really care about these declared maximum sizes. + + const auto rspec = std::string { R"(RUNSPEC +DIMENS + 10 10 10 / + +TITLE + Test PVTG Output + +GAS + +METRIC + +TABDIMS +-- NTSFUN NTPVT NSSFUN NPPVT NTFIP NRPVT + 1* 2 1* 1 1* 1 +/ +)" }; + + const auto props = std::string { R"( +PVTG + 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 / + 90.00 0.00000627 0.013608 0.01547 + 0.00000313 0.013611 0.01546 + 0.00000000 0.013615 0.01544 / +110.00 0.00000798 0.011072 0.01609 + 0.00000399 0.011076 0.01607 + 0.00000000 0.011081 0.01605 / +130.00 0.00001041 0.009340 0.01677 + 0.00000520 0.009346 0.01674 + 0.00000000 0.009352 0.01671 / +/ + 50.00 0.00000497 0.024958 0.01441 + 0.00000248 0.024958 0.01440 + 0.00000000 0.024958 0.01440 / +/ +)" }; + + const auto es = parse(rspec, props); + + auto tables = ::Opm::Tables(es.getUnits()); + tables.addPVTTables(es); + + const auto& tabdims = tables.tabdims(); + const auto& tab = tables.tab(); + + const auto ibpvtg = tabdims[ TABDIMS_IBPVTG_OFFSET_ITEM ] - 1; + const auto jbpvtg = tabdims[ TABDIMS_JBPVTG_OFFSET_ITEM ] - 1; + const auto nppvtg = tabdims[ TABDIMS_NPPVTG_ITEM ]; + const auto nrpvtg = tabdims[ TABDIMS_NRPVTG_ITEM ]; + const auto ntpvtg = tabdims[ TABDIMS_NTPVTG_ITEM ]; + const auto ncol = 5; + + BOOST_CHECK_EQUAL(nppvtg, 5); // Table 1 + BOOST_CHECK_EQUAL(nrpvtg, 3); // Table 1, Pg 1 + BOOST_CHECK_EQUAL(ntpvtg, 2); // TABDIMS + + const auto pvtg = std::vector { + &tab[ ibpvtg ] + 0, + &tab[ ibpvtg ] + ntpvtg*nppvtg*nrpvtg*ncol + }; + + const auto pg = std::vector { + &tab[ jbpvtg ] + 0, + &tab[ jbpvtg ] + ntpvtg*nppvtg + }; + + const auto expect_pvtg = makeTable(5, { + // Rv 1/Bg 1/(Bg*mu_g) d(1/Bg)/dRv d(1/(Bg*mu_g))/dRv + // + // Table 1 (Pg 1) + 4.970000000000000e-06, 4.006731308598445e+01, 2.780521380012800e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 2.480000000000000e-06, 4.006731308598445e+01, 2.782452297637809e+03, 0, -7.754689257064208e+05, + 0, 4.006731308598445e+01, 2.782452297637809e+03, 0, 0, + + // Table 1 (Pg 2) ------------------------------------------------------------------------------------------------------------ + + 5.210000000000000e-06, 5.669255626736210e+01, 3.802317657100074e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 2.610000000000000e-06, 5.668612890425712e+01, 3.804438181493767e+03, 2.472062732683009e+03, -8.155863052665014e+05, + 0, 5.667970299835629e+01, 3.804006912641362e+03, 2.462032912196776e+03, 1.652371082011811e+05, + + // Table 1 (Pg 3) ------------------------------------------------------------------------------------------------------------ + + 6.270000000000000e-06, 7.348618459729570e+01, 4.750238176942192e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 3.130000000000000e-06, 7.346998751010213e+01, 4.752263098971676e+03, 5.158308023431543e+03, -6.448796272243677e+05, + 0, 7.344840249724568e+01, 4.757020887127311e+03, 6.896170241676778e+03, -1.520060113621228e+06, + + // Table 1 (Pg 4) ------------------------------------------------------------------------------------------------------------ + + 7.980000000000000e-06, 9.031791907514450e+01, 5.613295156938751e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 3.990000000000000e-06, 9.028530155290719e+01, 5.618251496758381e+03, 8.174817603337499e+03, -1.242190430985102e+06, + 0, 9.024456276509339e+01, 5.622714190971551e+03, 1.021022250972279e+04, -1.118469727611510e+06, + + // Table 1 (Pg 5) ------------------------------------------------------------------------------------------------------------ + + 1.041000000000000e-05, 1.070663811563169e+02, 6.384399591909179e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 5.200000000000000e-06, 1.069976460517869e+02, 6.391735128541628e+03, 1.319291833590461e+04, -1.407972482235984e+06, + 0, 1.069289991445680e+02, 6.399102282738958e+03, 1.320132831131706e+04, -1.416760422563444e+06, + + // ========================================================================================================================== + + // Table 2 (Pg 1) + 4.970000000000000e-06, 4.006731308598445e+01, 2.780521380012800e+03, -2.000000000000000e+20, -2.000000000000000e+20, + 2.480000000000000e-06, 4.006731308598445e+01, 2.782452297637809e+03, 0, -7.754689257064208e+05, + 0, 4.006731308598445e+01, 2.782452297637809e+03, 0, 0, + + // Table 2 (Pg 2) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 3) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 4) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + + // Table 2 (Pg 5) ------------------------------------------------------------------------------------------------------------ + + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, -2.000000000000000e+20, + }); + + const auto expect_pg = makeTable(1, { + // Table 1 + 50.00, 70.00, 90.00, 110.00, 130.0, + + // Table 2 + 50.00, 2.0e20, 2.0e20, 2.0e20, 2.0e20, + }); + + check_is_close(pvtg, expect_pvtg); + check_is_close(pg , expect_pg ); +} + +BOOST_AUTO_TEST_SUITE_END () + +// ===================================================================== + BOOST_AUTO_TEST_SUITE (Oil) BOOST_AUTO_TEST_CASE (PVCDO) From 8e49c35c183c0738ddb9b216be3c7725be5d3888 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 29 Mar 2019 14:22:34 +0100 Subject: [PATCH 09/11] Create Prop-Func Table: Prune Vacuous Assignment Left over from when the function was extracted from the saturation function code. While here, fix an incorrect comment that mistakenly implied that the primary key of PVTO is pressure rather than the dissolved gas/oil ratio (Rs), and correct a type in 'composition'. --- src/opm/output/eclipse/Tables.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 216da0cdb..550fd46ee 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -112,7 +112,6 @@ namespace { const auto numCols = 1 + 2*numDep; auto descr = ::Opm::DifferentiateOutputTable::Descriptor{}; - descr.primID = 0 * numPrim; auto linTable = ::Opm::LinearisedOutputTable { numTab, numPrim, numRows, numCols, fillVal @@ -1679,8 +1678,8 @@ namespace { namespace PVTFunc { auto numActRows = std::size_t{0}; if (primID >= pvto[tableID].size()) { - // Pressure node outside current table's active set. No - // active rows in this subtable. + // Composition node outside current table's active set. + // No active rows in this sub-table. return numActRows; } @@ -1748,7 +1747,7 @@ namespace { namespace PVTFunc { /// composition nodes for all PVT function regions from PVTO (live /// oil with dissolved gas) keyword data. /// - /// \param[in] numCompNodes Number of compositoin nodes to allocate + /// \param[in] numCompNodes Number of composition nodes to allocate /// in the output vector for each oil PVT table. Expected to be /// equal to the number of declared composition nodes in the /// simulation run's TABDIMS keyword (NRPVT, Item 6). From 4dafe9456a7d173c9cb26d6e81e3b35862154138 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 29 Mar 2019 15:11:33 +0100 Subject: [PATCH 10/11] Create Prop-Func Table: Switch to std::function This commit switches to using the explicit type std::function rather than an implied callable entity whose interface is defined only in a comment. That adds compiler type checking at the cost of (possibly) introducing virtual function calls. The size overhead in the object file is mostly negligible in Release mode. Suggested by: [at]joakim-hove --- src/opm/output/eclipse/Tables.cpp | 39 ++++++++++++++----------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index 550fd46ee..ffa56cc22 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -47,33 +47,31 @@ #include #include #include +#include #include #include #include #include namespace { + /// Convenience type alias for a callable entity that extracts the + /// independent and primary dependent variates of a single property + /// function table into a linearised output table. Calling the function + /// will assign the independent variate of the sub-table primID within + /// the table identified as tableID to column zero of 'table' and all + /// dependent variates to columns one &c of 'table'. The function call + /// must return the number of active (used) rows within the sub-table + /// through its return value. + using BuildDep = std::function< + std::size_t(const std::size_t tableID, + const std::size_t primID, + ::Opm::LinearisedOutputTable& table) + >; + /// Create linearised, padded TAB vector entries for a collection of /// tabulated saturation functions corresponding to a single input /// keyword. /// - /// \tparam BuildDependent Callable entity that extracts the - /// independent and primary dependent variates of a single - /// saturation function table into a linearised output table. - /// Must implement a function call operator of the form - /// \code - /// std::size_t - /// operator()(const std::size_t tableID, - /// const std::size_t primID, - /// LinearisedOutputTable& table); - /// \endcode - /// that will assign the independent variate of the sub-table - /// primID within the table identified as tableID to column zero - /// of 'table' and all dependent variates to columns one &c of - /// 'table'. The function call operator must return the number - /// of active (used) rows within the sub-table through its return - /// value. - /// /// \param[in] numTab Number of tables in this table collection. /// /// \param[in] numPrim Number of primary look-up keys for each table. @@ -93,21 +91,20 @@ namespace { /// independent variate. /// /// \param[in] buildDeps Function object that implements the - /// protocol outlined for \code BuildDependent::operator()() - /// \endcode. Typically a lambda expression. + /// protocol outlined for \code BuildDep::operator()() + /// \endcode. Typically initialised from a lambda expression. /// /// \return Linearised, padded TAB vector entries for a collection of /// tabulated property functions (e.g., saturation functions or PVT /// functions) corresponding to a single input keyword. Derivatives /// included as additional columns. - template std::vector createPropfuncTable(const std::size_t numTab, const std::size_t numPrim, const std::size_t numRows, const std::size_t numDep, const double fillVal, - BuildDependent&& buildDeps) + const BuildDep& buildDeps) { const auto numCols = 1 + 2*numDep; From 68a254ffdc8f043f2d7f8df758f93d9f4169fa90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Fri, 29 Mar 2019 15:37:23 +0100 Subject: [PATCH 11/11] Create Prop-Func Table: Elucidate Motives for Unusual Loop Counters Thanks to my colleague Francesca Watson for wording assistance. --- src/opm/output/eclipse/Tables.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/opm/output/eclipse/Tables.cpp b/src/opm/output/eclipse/Tables.cpp index ffa56cc22..e1cfda42e 100644 --- a/src/opm/output/eclipse/Tables.cpp +++ b/src/opm/output/eclipse/Tables.cpp @@ -114,6 +114,13 @@ namespace { numTab, numPrim, numRows, numCols, fillVal }; + // Note unusual loop counters here. Specifically, we mutate the + // members of 'descr' rather than using separate counters, which is + // the custom construction. The reason for this is that these + // members are also used in calcSlopes() to indicate which table + // subsection to analyse and compute derivatives for. With separate + // loop counters, we would need to form a new table descriptor for + // each call to calcSlopes(). for (descr.tableID = 0*numTab; descr.tableID < 1*numTab; ++descr.tableID) {