diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 8ec94c75e..116d395c8 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -127,6 +127,7 @@ if(ENABLE_ECL_INPUT) src/opm/parser/eclipse/EclipseState/Schedule/RPTConfig.cpp src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp + src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp src/opm/parser/eclipse/EclipseState/Schedule/ScheduleTypes.cpp src/opm/parser/eclipse/EclipseState/Schedule/SummaryState.cpp src/opm/parser/eclipse/EclipseState/Schedule/TimeMap.cpp @@ -743,6 +744,7 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/Schedule/RPTConfig.hpp opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp + opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp opm/parser/eclipse/EclipseState/Schedule/ScheduleTypes.hpp opm/parser/eclipse/EclipseState/Schedule/Tuning.hpp opm/parser/eclipse/EclipseState/Schedule/Group/GPMaint.hpp diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 8042b78c5..670a88b4d 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include @@ -293,6 +294,13 @@ namespace Opm bool operator==(const Schedule& data) const; std::shared_ptr python() const; + + const ScheduleState& operator[](std::size_t index) const; + std::vector::const_iterator begin() const; + std::vector::const_iterator end() const; + ScheduleState& create_next(const ScheduleBlock& block); + + /* The cmp() function compares two schedule instances in a context aware manner. Floating point numbers are compared with a tolerance. The @@ -335,7 +343,6 @@ namespace Opm m_actions.serializeOp(serializer); m_network.serializeOp(serializer); m_glo.serializeOp(serializer); - m_pavg.serializeOp(serializer); rft_config.serializeOp(serializer); m_nupcol.template serializeOp(serializer); restart_config.serializeOp(serializer); @@ -347,6 +354,7 @@ namespace Opm reconstructDynMap(splitvfpinj.first, splitvfpinj.second, vfpinj_tables); } unit_system.serializeOp(serializer); + serializer.vector(snapshots); } private: @@ -375,14 +383,16 @@ namespace Opm DynamicState> m_actions; DynamicState> m_network; DynamicState> m_glo; - DynamicState> m_pavg; RFTConfig rft_config; DynamicState m_nupcol; RestartConfig restart_config; UnitSystem unit_system; std::optional exit_status; - std::map wellgroup_events; + DynamicState> rpt_config; + std::vector snapshots; + + void load_rst(const RestartIO::RstState& rst, const EclipseGrid& grid, const FieldPropsManager& fp); @@ -402,7 +412,6 @@ namespace Opm Connection::Order wellConnectionOrder); bool updateWPAVE(const std::string& wname, std::size_t report_step, const PAvg& pavg); - DynamicState> rpt_config; void updateNetwork(std::shared_ptr network, std::size_t report_step); void updateGuideRateModel(const GuideRateModel& new_model, std::size_t report_step); diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp index 74cb19cb0..f7f6fb03b 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp @@ -54,7 +54,7 @@ namespace Opm { void push_back(const DeckKeyword& keyword); std::optional get(const std::string& kw) const; const std::chrono::system_clock::time_point& start_time() const; - const std::chrono::system_clock::time_point& end_time() const; + const std::optional& end_time() const; void end_time(const std::chrono::system_clock::time_point& t); ScheduleTimeType time_type() const; const KeywordLocation& location() const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp new file mode 100644 index 000000000..de8a87046 --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -0,0 +1,74 @@ +/* + Copyright 2021 Equinor 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 . +*/ + +#ifndef SCHEDULE_TSTEP_HPP +#define SCHEDULE_TSTEP_HPP + +#include +#include +#include + +#include + +namespace Opm { + + /* + The purpose of the ScheduleState class is to hold the entire Schedule + information, i.e. wells and groups and so on, at exactly one point in + time. The ScheduleState class itself has no dynamic behavior, the dynamics + is handled by the Schedule instance owning the ScheduleState instance. + */ + + + class ScheduleState { + public: + ScheduleState() = default; + explicit ScheduleState(const std::chrono::system_clock::time_point& start_time); + ScheduleState(const std::chrono::system_clock::time_point& start_time, const std::chrono::system_clock::time_point& end_time); + ScheduleState(const ScheduleState& src, const std::chrono::system_clock::time_point& start_time); + ScheduleState(const ScheduleState& src, const std::chrono::system_clock::time_point& start_time, const std::chrono::system_clock::time_point& end_time); + + + std::chrono::system_clock::time_point start_time() const; + std::chrono::system_clock::time_point end_time() const; + ScheduleState next(const std::chrono::system_clock::time_point& next_start); + + bool operator==(const ScheduleState& other) const; + static ScheduleState serializeObject(); + + void pavg(PAvg pavg); + const PAvg& pavg() const; + + + template + void serializeOp(Serializer& serializer) { + serializer(m_start_time); + serializer(m_end_time); + serializer(m_pavg); + } + + private: + std::chrono::system_clock::time_point m_start_time; + std::optional m_end_time; + + std::shared_ptr m_pavg; + }; +} + +#endif diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 8d0dac339..c1d5398a1 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -1821,11 +1821,12 @@ namespace { } void Schedule::handleWPAVE(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { - auto wpave = std::make_shared( handlerContext.keyword.getRecord(0) ); + auto wpave = PAvg( handlerContext.keyword.getRecord(0) ); for (const auto& wname : this->wellNames(handlerContext.currentStep)) - this->updateWPAVE(wname, handlerContext.currentStep, *wpave ); + this->updateWPAVE(wname, handlerContext.currentStep, wpave ); - this->m_pavg.update( handlerContext.currentStep, std::move(wpave) ); + auto& sched_state = this->snapshots.back(); + sched_state.pavg(std::move(wpave)); } void Schedule::handleWWPAVE(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 34f919962..8a7584fcc 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -128,7 +128,6 @@ namespace { m_actions(this->m_timeMap, std::make_shared()), m_network(this->m_timeMap, std::make_shared()), m_glo(this->m_timeMap, std::make_shared()), - m_pavg(this->m_timeMap, std::make_shared()), rft_config(this->m_timeMap), m_nupcol(this->m_timeMap, runspec.nupcol()), restart_config(m_timeMap, deck, parseContext, errors), @@ -154,6 +153,33 @@ namespace { } this->iterateScheduleSection( python, deck.getInputPath(), parseContext, errors, grid, fp); + /* + This is temporary "integration test" for the time arguments in the + Schedule class. + */ + if (this->size() > 0) { + for (std::size_t report_step = 0; report_step < this->size() - 1; report_step++) { + const auto& this_block = this->m_sched_deck[report_step]; + if (this_block.start_time() != std::chrono::system_clock::from_time_t(this->m_timeMap[report_step])) { + auto msg = fmt::format("Block: Bug in start_time for report_step: {} ", report_step); + throw std::logic_error(msg); + } + + const auto& next_block = this->m_sched_deck[report_step + 1]; + if (this_block.end_time() != next_block.start_time()) + throw std::logic_error("Block: Internal bug in sched_block start / end inconsistent"); + + const auto& this_step = this->operator[](report_step); + if (this_step.start_time() != std::chrono::system_clock::from_time_t(this->m_timeMap[report_step])) { + auto msg = fmt::format("Bug in start_time for report_step: {} ", report_step); + throw std::logic_error(msg); + } + + const auto& next_step = this->operator[](report_step + 1); + if (this_step.end_time() != next_step.start_time()) + throw std::logic_error("Internal bug in sched_step start / end inconsistent"); + } + } } catch (const OpmInputError& opm_error) { throw; @@ -264,6 +290,7 @@ namespace { result.restart_config = RestartConfig::serializeObject(); result.wellgroup_events = {{"test", Events::serializeObject()}}; result.unit_system = UnitSystem::newFIELD(); + result.snapshots = { ScheduleState::serializeObject() }; return result; } @@ -360,7 +387,7 @@ private: std::vector > rftProperties; std::string time_unit = this->unit_system.name(UnitSystem::measure::time); - auto convert_time = [this](double seconds) { return this->unit_system.from_si(UnitSystem::measure::time, seconds); }; + auto deck_time = [this](double seconds) { return this->unit_system.from_si(UnitSystem::measure::time, seconds); }; std::string current_file; const auto& time_map = this->m_timeMap; /* @@ -400,7 +427,7 @@ private: currentStep + 1, this->size(), Schedule::formatDate(this->getStartTime()), - convert_time(time_map.getTimePassedUntil(currentStep)), + deck_time(time_map.getTimePassedUntil(currentStep)), time_unit, location.lineno)); } @@ -410,8 +437,8 @@ private: auto time_type = block.time_type(); if (time_type == ScheduleTimeType::DATES || time_type == ScheduleTimeType::TSTEP) { const auto& start_date = Schedule::formatDate(std::chrono::system_clock::to_time_t(block.start_time())); - const auto& days = convert_time(this->stepLength(currentStep - 1)); - const auto& days_total = convert_time(time_map.getTimePassedUntil(currentStep)); + const auto& days = deck_time(this->stepLength(currentStep - 1)); + const auto& days_total = deck_time(time_map.getTimePassedUntil(currentStep)); logger.complete_step(fmt::format("Complete report step {0} ({1} {2}) at {3} ({4} {2})", currentStep, days, @@ -427,7 +454,7 @@ private: time_unit, block.location().lineno)); } - + this->create_next(block); while (true) { if (keyword_index == block.size()) @@ -477,6 +504,7 @@ private: rftProperties); keyword_index++; } + checkIfAllConnectionsIsShut(currentStep); currentStep += 1; } @@ -902,8 +930,8 @@ private: this->addWell( std::move(well), timeStep ); - const auto& pavg_ptr = this->m_pavg.get(timeStep); - this->updateWPAVE( wellName, timeStep, *pavg_ptr ); + const auto& ts = this->operator[](timeStep); + this->updateWPAVE( wellName, timeStep, ts.pavg() ); } @@ -2032,5 +2060,35 @@ bool Schedule::cmp(const Schedule& sched1, const Schedule& sched2, std::size_t r return (count == 0); } +const ScheduleState& Schedule::operator[](std::size_t index) const { + return this->snapshots.at(index); +} + +std::vector::const_iterator Schedule::begin() const { + return this->snapshots.begin(); +} + +std::vector::const_iterator Schedule::end() const { + return this->snapshots.end(); +} + +ScheduleState& Schedule::create_next(const ScheduleBlock& block) { + const auto& start_time = block.start_time(); + const auto& end_time = block.end_time(); + + if (this->snapshots.empty()) { + if (end_time.has_value()) + this->snapshots.emplace_back( start_time, end_time.value() ); + else + this->snapshots.emplace_back(start_time); + } else { + const auto& last = this->snapshots.back(); + if (end_time.has_value()) + this->snapshots.emplace_back( last, start_time, end_time.value() ); + else + this->snapshots.emplace_back( last, start_time ); + } + return this->snapshots.back(); +} } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp index 5c88332ba..307869f51 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp @@ -61,8 +61,8 @@ const std::chrono::system_clock::time_point& ScheduleBlock::start_time() const { return this->m_start_time; } -const std::chrono::system_clock::time_point& ScheduleBlock::end_time() const { - return this->m_end_time.value(); +const std::optional& ScheduleBlock::end_time() const { + return this->m_end_time; } ScheduleTimeType ScheduleBlock::time_type() const { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp new file mode 100644 index 000000000..d998da1ea --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp @@ -0,0 +1,80 @@ +/* + Copyright 2021 Equinor 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 + +namespace Opm { + +ScheduleState::ScheduleState(const std::chrono::system_clock::time_point& t1): + m_start_time(t1), + m_pavg( std::make_shared()) +{ +} + +ScheduleState::ScheduleState(const std::chrono::system_clock::time_point& start_time, const std::chrono::system_clock::time_point& end_time) : + ScheduleState(start_time) +{ + this->m_end_time = end_time; +} + +ScheduleState::ScheduleState(const ScheduleState& src, const std::chrono::system_clock::time_point& start_time) : + ScheduleState(src) +{ + this->m_start_time = start_time; + this->m_end_time = std::nullopt; +} + +ScheduleState::ScheduleState(const ScheduleState& src, const std::chrono::system_clock::time_point& start_time, const std::chrono::system_clock::time_point& end_time) : + ScheduleState(src) +{ + this->m_start_time = start_time; + this->m_end_time = end_time; +} + + +std::chrono::system_clock::time_point ScheduleState::start_time() const { + return this->m_start_time; +} + +std::chrono::system_clock::time_point ScheduleState::end_time() const { + return this->m_end_time.value(); +} + + +void ScheduleState::pavg(PAvg arg) { + this->m_pavg = std::make_shared( std::move(arg) ); +} + +const PAvg& ScheduleState::pavg() const { + return *this->m_pavg; +} + + +bool ScheduleState::operator==(const ScheduleState& other) const { + return this->m_start_time == other.m_start_time && + this->m_end_time == other.m_end_time; +} + +ScheduleState ScheduleState::serializeObject() { + auto t1 = std::chrono::system_clock::now(); + auto t2 = t1 + std::chrono::hours(48); + ScheduleState ts(t1, t2); + return ts; +} +} diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 778a11eb6..573d230ca 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -4489,6 +4489,42 @@ std::string dates_msg(const std::chrono::system_clock::time_point& t, std::array } +BOOST_AUTO_TEST_CASE(ScheduleStateDatesTest) { + const auto& sched = make_schedule(createDeckWTEST()); + BOOST_CHECK_EQUAL(sched.size(), 6); + BOOST_CHECK( compare_dates(sched[0].start_time(), 2007, 5, 10 )); + BOOST_CHECK( compare_dates(sched[0].end_time(), 2007, 6, 10 )); + + BOOST_CHECK( compare_dates(sched[1].start_time(), 2007, 6, 10)); + BOOST_CHECK( compare_dates(sched[1].end_time(), 2007, 7, 10 )); + + BOOST_CHECK( compare_dates(sched[2].start_time(), 2007, 7, 10)); + BOOST_CHECK( compare_dates(sched[2].end_time(), 2007, 8, 10 )); + + BOOST_CHECK( compare_dates(sched[3].start_time(), 2007, 8, 10)); + BOOST_CHECK( compare_dates(sched[3].end_time(), 2007, 9, 10 )); + + BOOST_CHECK( compare_dates(sched[4].start_time(), 2007, 9, 10)); + BOOST_CHECK( compare_dates(sched[4].end_time(), 2007, 11, 10 )); + + BOOST_CHECK( compare_dates(sched[5].start_time(), 2007, 11, 10)); + BOOST_CHECK_THROW( sched[5].end_time(), std::exception ); +} + + +BOOST_AUTO_TEST_CASE(ScheduleStateTest) { + auto t1 = std::chrono::system_clock::now(); + auto t2 = t1 + std::chrono::hours(48); + + ScheduleState ts1(t1); + BOOST_CHECK( t1 == ts1.start_time() ); + BOOST_CHECK_THROW( ts1.end_time(), std::exception ); + + ScheduleState ts2(t1, t2); + BOOST_CHECK( t1 == ts2.start_time() ); + BOOST_CHECK( t2 == ts2.end_time() ); +} + BOOST_AUTO_TEST_CASE(ScheduleDeckTest) { { ScheduleDeck sched_deck;