mirror of
https://github.com/OPM/opm-simulators.git
synced 2024-12-28 18:21:00 -06:00
456 lines
19 KiB
C++
456 lines
19 KiB
C++
/*
|
|
Copyright 2018 SINTEF Digital, Mathematics and Cybernetics.
|
|
Copyright 2018 Equinor.
|
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#define BOOST_TEST_MODULE TestLogOutputHelper
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
|
|
#include <opm/common/OpmLog/OpmLog.hpp>
|
|
#include <opm/common/OpmLog/StreamLog.hpp>
|
|
#include <opm/common/utility/String.hpp>
|
|
|
|
#include <opm/input/eclipse/Deck/Deck.hpp>
|
|
#include <opm/input/eclipse/EclipseState/EclipseState.hpp>
|
|
#include <opm/input/eclipse/Parser/Parser.hpp>
|
|
#include <opm/input/eclipse/Python/Python.hpp>
|
|
#include <opm/input/eclipse/Schedule/Schedule.hpp>
|
|
#include <opm/input/eclipse/Schedule/SummaryState.hpp>
|
|
|
|
#include <opm/simulators/flow/LogOutputHelper.hpp>
|
|
|
|
#include <memory>
|
|
#include <string>
|
|
|
|
template<int type>
|
|
struct LogFixture
|
|
{
|
|
LogFixture() {
|
|
Opm::OpmLog::addBackend("stream",
|
|
std::make_shared<Opm::StreamLog>(str, type));
|
|
}
|
|
~LogFixture() {
|
|
Opm::OpmLog::removeBackend("stream");
|
|
}
|
|
|
|
std::stringstream str;
|
|
};
|
|
/*
|
|
template<>
|
|
std::stringstream LogFixture<Opm::Log::MessageType::Warning>::str;
|
|
template<>
|
|
std::stringstream LogFixture<Opm::Log::MessageType::Note>::str;
|
|
*/
|
|
using LogNoteFixture = LogFixture<Opm::Log::MessageType::Note>;
|
|
using LogWarningFixture = LogFixture<Opm::Log::MessageType::Warning>;
|
|
|
|
namespace {
|
|
|
|
const std::string input = R"(
|
|
RUNSPEC
|
|
FIELD
|
|
DIMENS
|
|
10 10 3 /
|
|
DX
|
|
300*1000 /
|
|
DY
|
|
300*1000 /
|
|
DZ
|
|
100*20 100*30 100*50 /
|
|
TOPS
|
|
100*8325 /
|
|
START -- 0
|
|
31 AUG 1993 /
|
|
SCHEDULE
|
|
WELSPECS
|
|
-- Item #: 1 2 3 4 5 6
|
|
'PROD' 'G1' 10 10 8400 'OIL' /
|
|
'INJ' 'G1' 1 1 8335 'GAS' /
|
|
/
|
|
WCONPROD
|
|
-- Item #:1 2 3 4 5 9
|
|
'PROD' 'OPEN' 'ORAT' 20000 4* 1000 /
|
|
/
|
|
WCONINJE
|
|
-- Item #:1 2 3 4 5 6 7
|
|
'INJ' 'GAS' 'OPEN' 'RATE' 100000 1* 9014 /
|
|
/)";
|
|
|
|
|
|
|
|
std::string trimStream(std::stringstream& str)
|
|
{
|
|
char buffer[1024];
|
|
std::string data;
|
|
do {
|
|
str.getline(buffer, 1024, '\n');
|
|
std::string tmp(buffer);
|
|
if (!tmp.empty()) {
|
|
tmp = Opm::trim_copy(tmp);
|
|
data += tmp;
|
|
data += '\n';
|
|
}
|
|
} while (!str.eof());
|
|
|
|
return data;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_CASE(Cumulative, LogNoteFixture)
|
|
{
|
|
const std::string reference = R"(=================================================== CUMULATIVE PRODUCTION/INJECTION REPORT =========================================
|
|
: WELL : LOCATION : WELL :CTRL: OIL : WATER : GAS : Prod : OIL : WATER : GAS : INJ :
|
|
: NAME : (I,J,K) : TYPE :MODE: PROD : PROD : PROD : RES.VOL. : INJ : INJ : INJ : RES.VOL. :
|
|
: : : : : MSTB : MSTB : MMSCF : MRB : MSTB : MSTB : MMSCF : MRB :
|
|
====================================================================================================================================
|
|
: FIELD: : : : 1.0: 2.0: 3.0: 4.0: 5.0: 6.0: 7.0: 8.0:
|
|
: G1: : : : 9.0: 10.0: 11.0: 12.0: 13.0: 14.0: 15.0: 15.0:
|
|
: PROD: 10, 10: PROD:ORAT: 16.0: 17.0: 18.0: 19.0: 20.0: 21.0: 22.0: 23.0:
|
|
: INJ: 1, 1: INJ:GRAT: 24.0: 25.0: 26.0: 27.0: 28.0: 29.0: 30.0: 31.0:
|
|
:--------:-----------:--------:----:-----------:-----------:-----------:-----------:-----------:-----------:-----------:-----------:
|
|
)";
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
|
|
Opm::EclipseState eclState(deck);
|
|
Opm::SummaryState st;
|
|
|
|
// Note: Cumulative gas values--e.g., FGPT--multiplied by an additional
|
|
// factor of 1000, for a total multiplicative factor of one million, in
|
|
// order to produce the expected balance sheet output in MM* units.
|
|
constexpr auto fields = std::array {
|
|
std::pair{"FOPT", 1.0},
|
|
std::pair{"FWPT", 2.0},
|
|
std::pair{"FGPT", 3.0e3},
|
|
std::pair{"FVPT", 4.0},
|
|
std::pair{"FOIT", 5.0},
|
|
std::pair{"FWIT", 6.0},
|
|
std::pair{"FGIT", 7.0e3},
|
|
std::pair{"FVIT", 8.0},
|
|
std::pair{"GOPT:G1", 9.0},
|
|
std::pair{"GWPT:G1", 10.0},
|
|
std::pair{"GGPT:G1", 11.0e3},
|
|
std::pair{"GVPT:G1", 12.0},
|
|
std::pair{"GOIT:G1", 13.0},
|
|
std::pair{"GWIT:G1", 14.0},
|
|
std::pair{"GGIT:G1", 15.0e3},
|
|
std::pair{"GVIT:G1", 15.0},
|
|
std::pair{"WOPT:PROD", 16.0},
|
|
std::pair{"WWPT:PROD", 17.0},
|
|
std::pair{"WGPT:PROD", 18.0e3},
|
|
std::pair{"WVPT:PROD", 19.0},
|
|
std::pair{"WOIT:PROD", 20.0},
|
|
std::pair{"WWIT:PROD", 21.0},
|
|
std::pair{"WGIT:PROD", 22.0e3},
|
|
std::pair{"WVIT:PROD", 23.0},
|
|
std::pair{"WOPT:INJ", 24.0},
|
|
std::pair{"WWPT:INJ", 25.0},
|
|
std::pair{"WGPT:INJ", 26.0e3},
|
|
std::pair{"WVPT:INJ", 27.0},
|
|
std::pair{"WOIT:INJ", 28.0},
|
|
std::pair{"WWIT:INJ", 29.0},
|
|
std::pair{"WGIT:INJ", 30.0e3},
|
|
std::pair{"WVIT:INJ", 31.0},
|
|
};
|
|
for (const auto& p : fields) {
|
|
st.set(p.first, p.second * 1e3);
|
|
}
|
|
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
helper.cumulative(0);
|
|
std::string data = trimStream(str);
|
|
BOOST_CHECK_EQUAL(data, reference);
|
|
}
|
|
|
|
|
|
BOOST_FIXTURE_TEST_CASE(Error, LogWarningFixture)
|
|
{
|
|
const std::string reference = R"(Finding the bubble point pressure failed for 3 cells [(2,1,1), (1,3,1), (1,4,1)]
|
|
Finding the dew point pressure failed for 3 cells [(5,1,1), (6,1,1), (7,1,1)]
|
|
)";
|
|
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
Opm::EclipseState eclState(deck);
|
|
|
|
Opm::SummaryState st;
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
|
|
str.str(""); // clear out parser errors
|
|
helper.error({1,20,30}, {4,5,6});
|
|
std::string data = trimStream(str);
|
|
BOOST_CHECK_EQUAL(data, reference);
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_CASE(Fip, LogNoteFixture)
|
|
{
|
|
const std::string reference = R"(
|
|
==================================================
|
|
: FIELD TOTALS :
|
|
: PAV = 0 PSIA :
|
|
: PORV= 157 RB :
|
|
: Pressure is weighted by hydrocarbon pore volume:
|
|
: Pore volumes are taken at reference conditions :
|
|
:--------------- OIL STB ----------------:-- WAT STB --:--------------- GAS MSCF ----------------:
|
|
: LIQUID VAPOUR TOTAL : TOTAL : FREE DISSOLVED TOTAL :
|
|
:-------------------------:-------------------------------------------:----------------:-------------------------------------------:
|
|
:CURRENTLY IN PLACE : 132 138 120: 113 : 1 1 1:
|
|
:-------------------------:-------------------------------------------:----------------:-------------------------------------------:
|
|
:ORIGINALLY IN PLACE : 25 31 13: 6 : 0 0 0:
|
|
====================================================================================================================================
|
|
|
|
|
|
|
|
==================================================
|
|
: FIPNUM REPORT REGION 1 :
|
|
: PAV = 0 PSIA :
|
|
: PORV= 371 RB :
|
|
:--------------- OIL STB ----------------:-- WAT STB --:--------------- GAS MSCF ----------------:
|
|
: LIQUID VAPOUR TOTAL : TOTAL : FREE DISSOLVED TOTAL :
|
|
:-------------------------:-------------------------------------------:----------------:-------------------------------------------:
|
|
:CURRENTLY IN PLACE : 346 352 333: 327 : 2 2 2:
|
|
:-------------------------:-------------------------------------------:----------------:-------------------------------------------:
|
|
:ORIGINALLY IN PLACE : 239 245 226: 220 : 1 1 1:
|
|
:-------------------------:-------------------------------------------:----------------:-------------------------------------------:
|
|
====================================================================================================================================
|
|
|
|
|
|
)";
|
|
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
|
|
Opm::EclipseState eclState(deck);
|
|
Opm::SummaryState st;
|
|
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
Opm::Inplace initial, current;
|
|
const auto& phases = current.phases();
|
|
int offset = 17;
|
|
double j = 1.0;
|
|
for (const auto& phase : phases) {
|
|
initial.add(phase, j);
|
|
initial.add("FIPNUM", phase, 0, j + 2*offset);
|
|
initial.add("FIPNUM", phase, 1, j + 2*offset);
|
|
current.add(phase, j + offset);
|
|
current.add("FIPNUM", phase, 0, j + 3*offset);
|
|
current.add("FIPNUM", phase, 1, j + 3*offset);
|
|
++j;
|
|
}
|
|
|
|
initial.add(Opm::Inplace::Phase::PressureHydroCarbonPV, 1.0);
|
|
initial.add(Opm::Inplace::Phase::HydroCarbonPV, 2.0);
|
|
initial.add(Opm::Inplace::Phase::PressurePV, 3.0);
|
|
initial.add(Opm::Inplace::Phase::DynamicPoreVolume, 4.0);
|
|
|
|
current.add(Opm::Inplace::Phase::PressureHydroCarbonPV, 2.0);
|
|
current.add(Opm::Inplace::Phase::HydroCarbonPV, 4.0);
|
|
current.add(Opm::Inplace::Phase::PressurePV, 6.0);
|
|
current.add(Opm::Inplace::Phase::DynamicPoreVolume, 8.0);
|
|
current.add("FIPNUM", Opm::Inplace::Phase::PressureHydroCarbonPV, 1, 2.0);
|
|
current.add("FIPNUM", Opm::Inplace::Phase::HydroCarbonPV, 1, 4.0);
|
|
current.add("FIPNUM", Opm::Inplace::Phase::PressurePV, 1, 6.0);
|
|
current.add("FIPNUM", Opm::Inplace::Phase::DynamicPoreVolume, 1, 8.0);
|
|
|
|
helper.fip(current, initial, "");
|
|
helper.fip(current, initial, "FIPNUM");
|
|
|
|
BOOST_CHECK_EQUAL(str.str(), reference);
|
|
}
|
|
|
|
BOOST_FIXTURE_TEST_CASE(FipResv, LogNoteFixture)
|
|
{
|
|
const std::string reference = R"(
|
|
===================================
|
|
: RESERVOIR VOLUMES RB :
|
|
:---------:---------------:---------------:---------------:---------------:---------------:
|
|
: REGION : TOTAL PORE : PORE VOLUME : PORE VOLUME : PORE VOLUME : PORE VOLUME :
|
|
: : VOLUME : CONTAINING : CONTAINING : CONTAINING : CONTAINING :
|
|
: : : OIL : WATER : GAS : HYDRO-CARBON :
|
|
:---------:---------------:---------------:---------------:---------------:---------------
|
|
: FIELD : 176: 13: 19: 25: 38:
|
|
: 1 : 176: 170: 164: 176: 346:
|
|
===========================================================================================
|
|
)";
|
|
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
|
|
Opm::EclipseState eclState(deck);
|
|
Opm::SummaryState st;
|
|
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
Opm::Inplace current;
|
|
const auto& phases = current.phases();
|
|
int offset = 17;
|
|
double j = 1.0;
|
|
for (const auto& phase : phases) {
|
|
current.add(phase, offset);
|
|
current.add("FIPNUM", phase, 1, j + offset);
|
|
++j;
|
|
}
|
|
|
|
current.add(Opm::Inplace::Phase::DynamicPoreVolume, 1.0);
|
|
current.add(Opm::Inplace::Phase::OilResVolume, 2.0);
|
|
current.add(Opm::Inplace::Phase::WaterResVolume, 3.0);
|
|
current.add(Opm::Inplace::Phase::GasResVolume, 4.0);
|
|
current.add("FIPNUM", Opm::Inplace::Phase::DynamicPoreVolume, 1, 11.0 + offset);
|
|
|
|
helper.fipResv(current, "FIPNUM");
|
|
BOOST_CHECK_EQUAL(str.str(), reference);
|
|
}
|
|
|
|
|
|
BOOST_FIXTURE_TEST_CASE(Injection, LogNoteFixture)
|
|
{
|
|
const std::string reference = R"(=================================================== INJECTION REPORT ========================================
|
|
: WELL : LOCATION : CTRL : CTRL : CTRL : OIL : WATER : GAS : FLUID : BHP OR : THP OR :
|
|
: NAME : (I,J,K) : MODE : MODE : MODE : RATE : RATE : RATE : RES.VOL. : CON.PR.: BLK.PR.:
|
|
: : : OIL : WAT : GAS : STB/DAY : STB/DAY : MSCF/DAY : RB/DAY : PSIA : PSIA :
|
|
=============================================================================================================
|
|
: FIELD: : : : : 1.0: 2.0: 3.0: 4.0: : :
|
|
: G1: : : : : 5.0: 6.0: 7.0: 8.0: : :
|
|
: INJ: 1, 1: : : GRAT: 9.0: 10.0: 11.0: 12.0: 13.0: 14.0:
|
|
:--------:-----------:------:------:------:-----------:-----------:-----------:-----------:--------:--------:
|
|
)";
|
|
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
|
|
Opm::EclipseState eclState(deck);
|
|
Opm::SummaryState st;
|
|
constexpr auto fields = std::array {
|
|
std::pair{"FOIR", 1.0},
|
|
std::pair{"FWIR", 2.0},
|
|
std::pair{"FGIR", 3.0},
|
|
std::pair{"FVIR", 4.0},
|
|
std::pair{"GOIR:G1", 5.0},
|
|
std::pair{"GWIR:G1", 6.0},
|
|
std::pair{"GGIR:G1", 7.0},
|
|
std::pair{"GVIR:G1", 8.0},
|
|
std::pair{"WOIR:INJ", 9.0},
|
|
std::pair{"WWIR:INJ", 10.0},
|
|
std::pair{"WGIR:INJ", 11.0},
|
|
std::pair{"WVIR:INJ", 12.0},
|
|
std::pair{"WBHP:INJ", 13.0},
|
|
std::pair{"WTHP:INJ", 14.0},
|
|
};
|
|
for (const auto& p : fields) {
|
|
st.set(p.first, p.second);
|
|
}
|
|
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
helper.injection(0);
|
|
std::string data = trimStream(str);
|
|
BOOST_CHECK_EQUAL(data, reference);
|
|
}
|
|
|
|
|
|
BOOST_FIXTURE_TEST_CASE(Production, LogNoteFixture)
|
|
{
|
|
const std::string reference = R"(======================================================= PRODUCTION REPORT =======================================================
|
|
: WELL : LOCATION :CTRL: OIL : WATER : GAS : FLUID : WATER : GAS/OIL : WAT/GAS : BHP OR : THP OR :
|
|
: NAME : (I,J,K) :MODE: RATE : RATE : RATE : RES.VOL. : CUT : RATIO : RATIO : CON.PR.: BLK.PR.:
|
|
: : : : STB/DAY : STB/DAY : MSCF/DAY : RB/DAY : : MSCF/STB : STB/MSCF : PSIA : PSIA :
|
|
=================================================================================================================================
|
|
: FIELD: : : 1.0: 2.0: 3.0: 4.0: 5.000: 6.00: 0.6667: : :
|
|
: G1: : : 7.0: 8.0: 9.0: 10.0: 11.000: 12.00: 0.8889: : :
|
|
: PROD: 10, 10:ORAT: 13.0: 14.0: 15.0: 16.0: 17.000: 18.00: 0.9333: 19.0: 20.0:
|
|
:--------:-----------:----:-----------:-----------:-----------:-----------:-----------:----------:------------:--------:--------:
|
|
)";
|
|
|
|
|
|
Opm::Parser parser;
|
|
auto python = std::make_shared<Opm::Python>();
|
|
auto deck = parser.parseString(input);
|
|
Opm::EclipseGrid grid(10,10,3);
|
|
Opm::TableManager table ( deck );
|
|
Opm::FieldPropsManager fp( deck, Opm::Phases{true, true, true}, grid, table);
|
|
Opm::Runspec runspec (deck );
|
|
Opm::Schedule schedule(deck, grid, fp, runspec, python);
|
|
|
|
Opm::EclipseState eclState(deck);
|
|
Opm::SummaryState st;
|
|
constexpr auto fields = std::array {
|
|
std::pair{"FOPR", 1.0},
|
|
std::pair{"FWPR", 2.0},
|
|
std::pair{"FGPR", 3.0},
|
|
std::pair{"FVPR", 4.0},
|
|
std::pair{"FWCT", 5.0},
|
|
std::pair{"FGOR", 6.0},
|
|
std::pair{"GOPR:G1", 7.0},
|
|
std::pair{"GWPR:G1", 8.0},
|
|
std::pair{"GGPR:G1", 9.0},
|
|
std::pair{"GVPR:G1", 10.0},
|
|
std::pair{"GWCT:G1", 11.0},
|
|
std::pair{"GGOR:G1", 12.0},
|
|
std::pair{"WOPR:PROD", 13.0},
|
|
std::pair{"WWPR:PROD", 14.0},
|
|
std::pair{"WGPR:PROD", 15.0},
|
|
std::pair{"WVPR:PROD", 16.0},
|
|
std::pair{"WWCT:PROD", 17.0},
|
|
std::pair{"WGOR:PROD", 18.0},
|
|
std::pair{"WBHP:PROD", 19.0},
|
|
std::pair{"WTHP:PROD", 20.0},
|
|
};
|
|
for (const auto& p : fields) {
|
|
st.set(p.first, p.second);
|
|
}
|
|
|
|
Opm::LogOutputHelper<double> helper(eclState, schedule, st, "dummy version");
|
|
helper.production(0);
|
|
std::string data = trimStream(str);
|
|
BOOST_CHECK_EQUAL(data, reference);
|
|
}
|
|
|