Files
opm-common/tests/parser/ScheduleRestartTests.cpp

345 lines
12 KiB
C++

/*
Copyright 2013 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 <http://www.gnu.org/licenses/>.
*/
#define BOOST_TEST_MODULE ScheduleTests
#include <boost/test/unit_test.hpp>
#include <opm/common/utility/TimeService.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <opm/common/utility/FileSystem.hpp>
#include <opm/parser/eclipse/Python/Python.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/FieldPropsManager.hpp>
#include <opm/parser/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/WellConnections.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp>
#include <opm/parser/eclipse/Units/UnitSystem.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/UDQ/UDQState.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Action/State.hpp>
#include <opm/parser/eclipse/Deck/DeckValue.hpp>
#include <opm/parser/eclipse/Deck/Deck.hpp>
#include <opm/parser/eclipse/Deck/FileDeck.hpp>
#include <opm/parser/eclipse/Parser/Parser.hpp>
#include <opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp>
#include <opm/io/eclipse/rst/state.hpp>
#include <opm/io/eclipse/ERst.hpp>
#include <opm/io/eclipse/RestartFileView.hpp>
#include <cstddef>
#include <iterator>
#include <memory>
#include <utility>
#include <vector>
namespace fs = Opm::filesystem;
using namespace Opm;
void compare_connections(const RestartIO::RstConnection& rst_conn, const Connection& sched_conn) {
BOOST_CHECK_EQUAL(rst_conn.ijk[0], sched_conn.getI());
BOOST_CHECK_EQUAL(rst_conn.ijk[1], sched_conn.getJ());
BOOST_CHECK_EQUAL(rst_conn.ijk[2], sched_conn.getK());
BOOST_CHECK_EQUAL(rst_conn.segment, sched_conn.segment());
BOOST_CHECK_EQUAL(rst_conn.rst_index, sched_conn.sort_value());
BOOST_CHECK(rst_conn.state == sched_conn.state());
BOOST_CHECK(rst_conn.dir == sched_conn.dir());
BOOST_CHECK_CLOSE( rst_conn.cf, sched_conn.CF() , 1e-6);
}
void compare_wells(const RestartIO::RstWell& rst_well, const Well& sched_well) {
BOOST_CHECK_EQUAL(rst_well.name, sched_well.name());
BOOST_CHECK_EQUAL(rst_well.group, sched_well.groupName());
const auto& sched_connections = sched_well.getConnections();
BOOST_CHECK_EQUAL(sched_connections.size(), rst_well.connections.size());
for (std::size_t ic=0; ic < rst_well.connections.size(); ic++) {
const auto& rst_conn = rst_well.connections[ic];
const auto& sched_conn = sched_connections[ic];
compare_connections(rst_conn, sched_conn);
}
}
BOOST_AUTO_TEST_CASE(LoadRST) {
Parser parser;
auto deck = parser.parseFile("SPE1CASE2.DATA");
auto rst_file = std::make_shared<EclIO::ERst>("SPE1CASE2.X0060");
auto rst_view = std::make_shared<EclIO::RestartFileView>(std::move(rst_file), 60);
auto rst_state = RestartIO::RstState::load(std::move(rst_view), Runspec{}, parser);
BOOST_REQUIRE_THROW( rst_state.get_well("NO_SUCH_WELL"), std::out_of_range);
auto python = std::make_shared<Python>();
EclipseState ecl_state(deck);
Schedule sched(deck, ecl_state, python);
const auto& well_names = sched.wellNames(60);
BOOST_CHECK_EQUAL(well_names.size(), rst_state.wells.size());
for (const auto& wname : well_names) {
const auto& rst_well = rst_state.get_well(wname);
const auto& sched_well = sched.getWell(wname, 60);
compare_wells(rst_well, sched_well);
}
}
std::tuple<Schedule, Schedule, RestartIO::RstState> load_schedule_pair(const std::string& base_deck,
const std::string& rst_deck,
const std::string& rst_fname,
std::size_t restart_step) {
Parser parser;
auto python = std::make_shared<Python>();
auto deck = parser.parseFile(base_deck);
EclipseState ecl_state(deck);
Schedule sched(deck, ecl_state, python);
auto restart_deck = parser.parseFile(rst_deck);
auto rst_file = std::make_shared<EclIO::ERst>(rst_fname);
auto rst_view = std::make_shared<EclIO::RestartFileView>(std::move(rst_file), restart_step);
auto rst_state = RestartIO::RstState::load(std::move(rst_view), ecl_state.runspec(), parser);
EclipseState ecl_state_restart(restart_deck);
Schedule restart_sched(restart_deck, ecl_state_restart, python, {}, &rst_state);
return {sched, restart_sched, rst_state};
}
void compare_sched(const std::string& base_deck,
const std::string& rst_deck,
const std::string& rst_fname,
std::size_t restart_step)
{
const auto& [sched, restart_sched, _] = load_schedule_pair(base_deck, rst_deck, rst_fname, restart_step);
(void) _;
BOOST_CHECK_EQUAL(restart_sched.size(), sched.size());
for (std::size_t report_step=restart_step; report_step < sched.size(); report_step++) {
const auto& base = sched[report_step];
auto rst = restart_sched[report_step];
BOOST_CHECK(base.start_time() == rst.start_time());
if (report_step < sched.size() - 1)
BOOST_CHECK(base.end_time() == rst.end_time());
// Should ideally do a base == rst check here, but for now the members
// wells, rft_config, m_first_in_year and m_first_in_month fail.
// BOOST_CHECK(base == rst);
}
}
BOOST_AUTO_TEST_CASE(LoadRestartSim) {
compare_sched("SPE1CASE2.DATA", "SPE1CASE2_RESTART_SKIPREST.DATA", "SPE1CASE2.X0060", 60);
compare_sched("SPE1CASE2.DATA", "SPE1CASE2_RESTART.DATA", "SPE1CASE2.X0060", 60);
}
BOOST_AUTO_TEST_CASE(LoadUDQRestartSim) {
const auto& [sched, restart_sched, rst_state] = load_schedule_pair("UDQ_WCONPROD.DATA", "UDQ_WCONPROD_RESTART.DATA", "UDQ_WCONPROD.X0006", 6);
std::size_t report_step = 10;
SummaryState st(TimeService::now());
st.update_well_var("OPL02", "WUOPRL", 1);
st.update_well_var("OPL02", "WULPRL", 11);
st.update_well_var("OPU02", "WUOPRU", 111);
st.update_well_var("OPU02", "WULPRU", 1111);
for (const auto& wname : sched.wellNames(report_step)) {
const auto& well = sched.getWell(wname, report_step);
const auto& rst_well = restart_sched.getWell(wname, report_step);
if (well.isProducer()) {
const auto& controls = well.productionControls(st);
auto rst_controls = rst_well.productionControls(st);
/*
The cmode in the base case is the cmode set by the input deck,
whereas the cmode in restart case is what cmode was active when
the restart file was written - these can deviate.
*/
rst_controls.cmode = controls.cmode;
BOOST_CHECK( controls == rst_controls );
}
}
UDQState udq_state(0);
udq_state.load_rst(rst_state);
}
BOOST_AUTO_TEST_CASE(LoadActionRestartSim) {
const auto& [sched, restart_sched, rst_state] = load_schedule_pair("UDQ_ACTIONX.DATA", "UDQ_ACTIONX_RESTART.DATA", "UDQ_ACTIONX.X0007", 7);
const auto& input_actions = sched[7].actions();
const auto& rst_actions = restart_sched[7].actions();
BOOST_CHECK_EQUAL(input_actions.ecl_size(), rst_actions.ecl_size());
for (std::size_t iact = 0; iact < input_actions.ecl_size(); iact++) {
const auto& input_action = input_actions[iact];
const auto& rst_action = rst_actions[iact];
auto input_iter = input_action.begin();
auto rst_iter = rst_action.begin();
BOOST_REQUIRE_EQUAL( std::distance(input_action.begin(), input_action.end()),
std::distance(rst_action.begin(), rst_action.end()) );
while (input_iter != input_action.end()) {
BOOST_CHECK( *input_iter == *rst_iter );
input_iter++;
rst_iter++;
}
}
Action::State action_state;
action_state.load_rst(rst_actions, rst_state);
}
BOOST_AUTO_TEST_CASE(TestFileDeck)
{
Parser parser;
auto python = std::make_shared<Python>();
auto deck = parser.parseFile("UDQ_WCONPROD.DATA");
FileDeck fd(deck);
{
auto index = fd.start();
for (const auto& kw : deck) {
BOOST_CHECK(kw == fd[index]);
index++;
}
}
{
auto index = fd.stop();
for (std::size_t deck_index = deck.size(); deck_index > 0; deck_index--)
BOOST_CHECK(deck[deck_index - 1] == fd[--index]);
}
const auto& index1 = fd.find("RADIAL");
BOOST_CHECK( !index1.has_value() );
const auto& index2 = fd.find("DIMENS");
BOOST_CHECK( index2.has_value());
BOOST_CHECK_EQUAL( index2.value().file_index , 0);
BOOST_CHECK_EQUAL( index2.value().keyword_index, 3);
const auto& index3 = fd.find("COORD");
BOOST_CHECK_EQUAL( index3.value().file_index , 1);
BOOST_CHECK_EQUAL( index3.value().keyword_index, 3);
const auto& index4 = fd.find("SGOF");
BOOST_CHECK_EQUAL( index4.value().file_index , 2);
BOOST_CHECK_EQUAL( index4.value().keyword_index, 2);
fd.erase(index4.value());
const auto& index5 = fd.find("SGOF");
BOOST_CHECK( !index5.has_value() );
const auto& index6 = fd.find("SWOF");
const auto& index7 = fd.find("SOLUTION");
fd.erase(index6.value(), index7.value());
}
BOOST_AUTO_TEST_CASE(RestartTest2)
{
Parser parser;
auto python = std::make_shared<Python>();
auto deck = parser.parseFile("UDQ_WCONPROD.DATA");
FileDeck fd(deck);
fd.rst_solution("RESTART", 77);
fd.insert_skiprest();
auto solution_index = fd.find("SOLUTION").value();
auto restart_index = fd.find("RESTART").value();
auto summary_index = fd.find("SUMMARY").value();
auto schedule_index = fd.find("SCHEDULE").value();
auto skiprest_index = fd.find("SKIPREST").value();
BOOST_CHECK(solution_index < restart_index);
BOOST_CHECK(restart_index < summary_index);
BOOST_CHECK(summary_index < schedule_index);
BOOST_CHECK(schedule_index < skiprest_index);
}
BOOST_AUTO_TEST_CASE(RestartTest)
{
Parser parser;
auto python = std::make_shared<Python>();
auto deck = parser.parseFile("UDQ_WCONPROD.DATA");
FileDeck fd(deck);
const auto solution = fd.find("SOLUTION");
const auto schedule = fd.find("SCHEDULE");
auto index = solution.value();
while (true) {
const auto& keyword = fd[++index];
if (keyword.name() == "EQUIL")
fd.erase(index);
if (index == schedule)
break;
}
UnitSystem units;
std::vector<DeckValue> values{ DeckValue{"RESTART_BASE"}, DeckValue{100} };
DeckKeyword restart(parser.getKeyword("RESTART"), std::vector<std::vector<DeckValue>>{ values }, units, units);
index = solution.value();
fd.insert(++index, restart);
BOOST_CHECK( fd[index] == restart );
// time_point tp = asTimePoint( TimeStampUTC(2018, 1, 11) );
// auto find_date = [tp](const DeckKeyword& keyword)
// {
// if (keyword.name() == "DATES") {
// const auto& record = keyword[0];
// const auto &dayItem = record.getItem(0);
// const auto &monthItem = record.getItem(1);
// const auto &yearItem = record.getItem(2);
// // Accept lower- and mixed-case month names.
// std::string monthname = uppercase(monthItem.get<std::string>(0));
// auto date = asTimePoint( TimeStampUTC( yearItem.get<int>(0), TimeService::eclipseMonthIndices().at(monthname), dayItem.get<int>(0)));
// return date == tp;
// }
// };
}