From 77312b14326f97f1fbc58013f9c6128bbdbb623d Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 8 Jan 2021 10:03:20 +0100 Subject: [PATCH 1/2] Enable rerun of iteration over Schedule keywords --- .../EclipseState/Schedule/Schedule.hpp | 13 +- .../EclipseState/Schedule/ScheduleDeck.hpp | 1 + .../EclipseState/Schedule/ScheduleState.hpp | 1 + .../EclipseState/Schedule/KeywordHandlers.cpp | 3 +- .../EclipseState/Schedule/Schedule.cpp | 141 +++++++++++++++--- .../EclipseState/Schedule/ScheduleDeck.cpp | 5 + .../EclipseState/Schedule/ScheduleState.cpp | 4 + 7 files changed, 139 insertions(+), 29 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 23b585ddb..a2024c51f 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -348,11 +348,13 @@ namespace Opm } unit_system.serializeOp(serializer); serializer.vector(snapshots); + serializer(this->m_input_path); } private: template using Map2 = std::map; std::shared_ptr python_handle; + std::string m_input_path; ScheduleDeck m_sched_deck; TimeMap m_timeMap; WellMap wells_static; @@ -411,7 +413,10 @@ namespace Opm void updateUDQActive( std::size_t timeStep, std::shared_ptr udq ); bool updateWellStatus( const std::string& well, std::size_t reportStep, bool runtime, Well::Status status, std::optional = {}); void addWellToGroup( const std::string& group_name, const std::string& well_name , std::size_t timeStep); - void iterateScheduleSection(std::shared_ptr python, const std::string& input_path, const ParseContext& parseContext , ErrorGuard& errors, const EclipseGrid& grid, + void iterateScheduleSection(std::optional load_offset, + const ParseContext& parseContext, + ErrorGuard& errors, + const EclipseGrid& grid, const FieldPropsManager& fp); void addACTIONX(const Action::ActionX& action, std::size_t currentStep); void addGroupToGroup( const std::string& parent_group, const std::string& child_group, std::size_t timeStep); @@ -421,9 +426,7 @@ namespace Opm void addWell(const std::string& wellName, const DeckRecord& record, std::size_t timeStep, Connection::Order connection_order); void checkIfAllConnectionsIsShut(std::size_t currentStep); void updateUDQ(const DeckKeyword& keyword, std::size_t current_step); - void handleKeyword(std::shared_ptr python, - const std::string& input_path, - std::size_t currentStep, + void handleKeyword(std::size_t currentStep, const ScheduleBlock& block, const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors, @@ -507,7 +510,7 @@ namespace Opm bool handleNormalKeyword(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors); // Keyword Handlers - void handlePYACTION (std::shared_ptr python, const std::string& input_path, const DeckKeyword&, std::size_t currentStep); + void handlePYACTION(const DeckKeyword&, std::size_t currentStep); void handleGCONPROD(const DeckKeyword& keyword, std::size_t current_step, const ParseContext& parseContext, ErrorGuard& errors); void handleGCONINJE(const DeckKeyword& keyword, std::size_t current_step, const ParseContext& parseContext, ErrorGuard& errors); void handleGLIFTOPT(const DeckKeyword& keyword, std::size_t report_step, const ParseContext& parseContext, ErrorGuard& errors); diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp index f7f6fb03b..e1cb4038c 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp @@ -101,6 +101,7 @@ namespace Opm { std::vector::const_iterator begin() const; std::vector::const_iterator end() const; std::size_t size() const; + std::size_t restart_offset() const; const KeywordLocation& location() const; bool operator==(const ScheduleDeck& other) const; diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index 697dd9f4e..18f81ae45 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -58,6 +58,7 @@ namespace Opm { const PAvg& pavg() const; void tuning(Tuning tuning); + Tuning& tuning(); const Tuning& tuning() const; void nupcol(int nupcol); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 6f27c6d42..6c689493a 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -1615,15 +1615,14 @@ namespace { } void Schedule::handleWSEGITER(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { - auto tuning = this->snapshots.back().tuning(); const auto& record = handlerContext.keyword.getRecord(0); + auto& tuning = this->snapshots.back().tuning(); tuning.MXWSIT = record.getItem().get(0); tuning.WSEG_MAX_RESTART = record.getItem().get(0); tuning.WSEG_REDUCTION_FACTOR = record.getItem().get(0); tuning.WSEG_INCREASE_FACTOR = record.getItem().get(0); - this->snapshots.back().tuning(tuning); m_events.addEvent(ScheduleEvents::TUNING_CHANGE, handlerContext.currentStep); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 8f44cc182..73fd6b417 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -109,6 +109,7 @@ namespace { const RestartIO::RstState * rst) try : python_handle(python), + m_input_path(deck.getInputPath()), m_sched_deck(deck, restart_info(rst) ), m_timeMap( deck , restart_info( rst )), m_events( this->m_timeMap ), @@ -149,12 +150,28 @@ namespace { applyMESSAGES(keyword, 0); } - this->iterateScheduleSection( python, deck.getInputPath(), parseContext, errors, grid, fp); + this->iterateScheduleSection( {}, parseContext, errors, grid, fp); + /* - This is temporary "integration test" for the time arguments in the - Schedule class. + The code in the if (integration_test) { ... } is an enforced + integration test to assert the sanity of the ongoing Schedule + refactoring. At the very latest this should be removed when the + Schedule refactoring is complete. */ - if (this->size() > 0) { + const bool integration_test = true; + if (integration_test) { + if (this->size() == 0) + return; + + // Verify that we can safely re-iterate over the Schedule section + if (!rst) + this->iterateScheduleSection( 0, parseContext, errors, grid, fp); + else { + auto restart_offset = this->m_sched_deck.restart_offset(); + this->iterateScheduleSection( restart_offset, parseContext, errors, grid, fp); + } + + // Verify that the time schedule is correct. 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])) { @@ -285,6 +302,7 @@ namespace { result.wellgroup_events = {{"test", Events::serializeObject()}}; result.unit_system = UnitSystem::newFIELD(); result.snapshots = { ScheduleState::serializeObject() }; + result.m_input_path = "Some/funny/path"; return result; } @@ -302,9 +320,7 @@ namespace { } - void Schedule::handleKeyword(std::shared_ptr python, - const std::string& input_path, - std::size_t currentStep, + void Schedule::handleKeyword(std::size_t currentStep, const ScheduleBlock& block, const DeckKeyword& keyword, const ParseContext& parseContext, @@ -325,7 +341,7 @@ namespace { rftProperties.push_back( std::make_pair( &keyword , currentStep )); else if (keyword.name() == "PYACTION") - handlePYACTION(python, input_path, keyword, currentStep); + handlePYACTION(keyword, currentStep); } namespace { @@ -372,18 +388,89 @@ private: } - void Schedule::iterateScheduleSection(std::shared_ptr python, - const std::string& input_path, - const ParseContext& parseContext , - ErrorGuard& errors, - const EclipseGrid& grid, - const FieldPropsManager& fp) { +void Schedule::iterateScheduleSection(std::optional load_offset, + const ParseContext& parseContext , + ErrorGuard& errors, + const EclipseGrid& grid, + const FieldPropsManager& fp) { std::vector > rftProperties; std::string time_unit = this->unit_system.name(UnitSystem::measure::time); 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; + /* + The process of transitioning to Schedule model based on ScheduleState + instances is a gradual one. For the keywords which have been converted + to the ScheduleState implementation the iterateScheduleSection() + function can be called repeatedly, whereas for the keywords which have + not yet been converted that is not safe. The old_style_keywords is a + list of keywords which should be ignored when iterateScheduleSection() + is called repeatedly. + */ + std::unordered_set old_style_keywords = { + "PYACTION", + "GCONPROD", + "GCONINJE", + "GLIFTOPT", + "WELPI", + "BRANPROP", + "COMPDAT", + "COMPLUMP", + "COMPORD", + "COMPSEGS", + "GCONINJE", + "GCONPROD", + "GCONSALE", + "GCONSUMP", + "GEFAC", + "GLIFTOPT", + "GPMAINT", + "GRUPNET", + "GRUPTREE", + "GUIDERAT", + "LIFTOPT", + "LINCOM", + "MESSAGES", + "MULTFLT", + "MXUNSUPP", + "NODEPROP", + "RPTSCHED", + "UDQ", + "VFPINJ", + "VFPPROD", + "WCONHIST", + "WCONINJE", + "WCONINJH", + "WCONPROD", + "WECON", + "WEFAC", + "WELOPEN", + "WELPI", + "WELSEGS", + "WELSPECS", + "WELTARG", + "WFOAM", + "WGRUPCON", + "WHISTCTL", + "WINJTEMP", + "WLIFTOPT", + "WLIST", + "WPAVEDEP", + "WPIMULT", + "WPMITAB", + "WPOLYMER", + "WSALT", + "WSEGSICD", + "WSEGAICD", + "WSEGVALV", + "WSKPTAB", + "WSOLVENT", + "WTEMP", + "WTEST", + "WTRACER" + }; + /* The keywords in the skiprest_whitelist set are loaded from the SCHEDULE section even though the SKIPREST keyword is in action. The @@ -426,6 +513,12 @@ private: location.lineno)); } + if (load_offset.has_value()) { + if (load_offset.value() < this->m_sched_deck.restart_offset()) + throw std::logic_error("BUG: Tried to replay schedule keywords from historical section in restarted run"); + this->snapshots.resize( load_offset.value() ); + } + for (const auto& block : this->m_sched_deck) { std::size_t keyword_index = 0; auto time_type = block.time_type(); @@ -462,6 +555,11 @@ private: current_file = location.filename; } + if (load_offset.has_value() && old_style_keywords.count(keyword.name()) == 1) { + keyword_index += 1; + continue; + } + if (keyword.name() == "ACTIONX") { Action::ActionX action(keyword, this->m_timeMap.getStartTime(currentStep)); while (true) { @@ -487,9 +585,7 @@ private: } logger(fmt::format("Processing keyword {} at line {}", location.keyword, location.lineno)); - this->handleKeyword(python, - input_path, - currentStep, + this->handleKeyword(currentStep, block, keyword, parseContext, @@ -522,8 +618,8 @@ private: this->m_actions.update(currentStep, new_actions); } - void Schedule::handlePYACTION(std::shared_ptr python, const std::string& input_path, const DeckKeyword& keyword, std::size_t currentStep) { - if (!python->enabled()) { + void Schedule::handlePYACTION(const DeckKeyword& keyword, std::size_t currentStep) { + if (!this->python_handle->enabled()) { //Must have a real Python instance here - to ensure that IMPORT works const auto& loc = keyword.location(); OpmLog::warning("This version of flow is built without support for Python. Keyword PYACTION in file: " + loc.filename + " line: " + std::to_string(loc.lineno) + " is ignored."); @@ -534,12 +630,12 @@ private: const auto& run_count = Action::PyAction::from_string( keyword.getRecord(0).getItem().get(0) ); const auto& module_arg = keyword.getRecord(1).getItem().get(0); std::string module; - if (input_path.empty()) + if (this->m_input_path.empty()) module = module_arg; else - module = input_path + "/" + module_arg; + module = this->m_input_path + "/" + module_arg; - Action::PyAction pyaction(python, name, run_count, module); + Action::PyAction pyaction(this->python_handle, name, run_count, module); auto new_actions = std::make_shared( this->actions(currentStep) ); new_actions->add(pyaction); this->m_actions.update(currentStep, new_actions); @@ -1631,6 +1727,7 @@ private: }; return this->m_timeMap == data.m_timeMap && + this->m_input_path == data.m_input_path && compareMap(this->wells_static, data.wells_static) && compareMap(this->groups, data.groups) && this->m_events == data.m_events && diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp index 22ffa24b5..7bf425a57 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.cpp @@ -121,6 +121,11 @@ const KeywordLocation& ScheduleDeck::location() const { } +std::size_t ScheduleDeck::restart_offset() const { + return this->m_restart_offset; +} + + ScheduleDeck::ScheduleDeck(const Deck& deck, const std::pair& restart) { const std::unordered_set skiprest_include = {"VFPPROD", "VFPINJ", "RPTSCHED", "RPTRST", "TUNING", "MESSAGES"}; std::chrono::system_clock::time_point start_time; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp index 0c1791d0d..a06718b38 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp @@ -110,4 +110,8 @@ const Tuning& ScheduleState::tuning() const { return this->m_tuning; } +Tuning& ScheduleState::tuning() { + return this->m_tuning; +} + } From b8ef8767e5c6f66bf5a3236551d110296c4817d3 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 8 Jan 2021 10:55:46 +0100 Subject: [PATCH 2/2] Remove grid and fp members from default HandlerContext construction --- .../eclipse/EclipseState/Schedule/Schedule.hpp | 12 +++++------- .../EclipseState/Schedule/KeywordHandlers.cpp | 12 +++++++++--- .../eclipse/EclipseState/Schedule/Schedule.cpp | 18 ++++++++++++++++-- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index a2024c51f..03909792f 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -474,19 +474,17 @@ namespace Opm const ScheduleBlock& block; const DeckKeyword& keyword; const std::size_t currentStep; - const EclipseGrid& grid; - const FieldPropsManager& fieldPropsManager; + const EclipseGrid* grid_ptr; + const FieldPropsManager* fp_ptr; HandlerContext(const ScheduleBlock& block_, const DeckKeyword& keyword_, - const std::size_t currentStep_, - const EclipseGrid& grid_, - const FieldPropsManager& fieldPropsManager_) : + const std::size_t currentStep_): block(block_), keyword(keyword_), currentStep(currentStep_), - grid(grid_), - fieldPropsManager(fieldPropsManager_) + grid_ptr(nullptr), + fp_ptr(nullptr) {} diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 6c689493a..89a54e736 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -143,6 +143,9 @@ namespace { } void Schedule::handleCOMPDAT(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { + if (!handlerContext.grid_ptr) + throw std::logic_error("BUG: Schedule::handleCOMPDAT() has been called with an invalid grid pointer"); + std::unordered_set wells; for (const auto& record : handlerContext.keyword) { const std::string& wellNamePattern = record.getItem("WELL").getTrimmedString(0); @@ -153,8 +156,8 @@ namespace { for (const auto& name : wellnames) { auto well2 = std::shared_ptr(new Well( this->getWell(name, handlerContext.currentStep))); auto connections = std::shared_ptr( new WellConnections( well2->getConnections())); - connections->loadCOMPDAT(record, handlerContext.grid, handlerContext.fieldPropsManager, name, handlerContext.keyword.location()); - if (well2->updateConnections(connections, handlerContext.currentStep, handlerContext.grid, handlerContext.fieldPropsManager.get_int("PVTNUM"))) { + connections->loadCOMPDAT(record, *handlerContext.grid_ptr, *handlerContext.fp_ptr, name, handlerContext.keyword.location()); + if (well2->updateConnections(connections, handlerContext.currentStep, *handlerContext.grid_ptr, handlerContext.fp_ptr->get_int("PVTNUM"))) { this->updateWell(std::move(well2), handlerContext.currentStep); wells.insert( name ); } @@ -213,6 +216,9 @@ namespace { } void Schedule::handleCOMPSEGS(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { + if (!handlerContext.grid_ptr) + throw std::logic_error("BUG: Schedule::handleCOMPDAT() has been called with an invalid grid pointer"); + const auto& record1 = handlerContext.keyword.getRecord(0); const std::string& well_name = record1.getItem("WELL").getTrimmedString(0); @@ -228,7 +234,7 @@ namespace { return; } - if (well_ptr->handleCOMPSEGS(handlerContext.keyword, handlerContext.currentStep, handlerContext.grid, parseContext, errors)) + if (well_ptr->handleCOMPSEGS(handlerContext.keyword, handlerContext.currentStep, *handlerContext.grid_ptr, parseContext, errors)) this->updateWell(std::move(well_ptr), handlerContext.currentStep); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 73fd6b417..ec1570b64 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -158,7 +158,7 @@ namespace { refactoring. At the very latest this should be removed when the Schedule refactoring is complete. */ - const bool integration_test = true; + const bool integration_test = false; if (integration_test) { if (this->size() == 0) return; @@ -329,8 +329,22 @@ namespace { const FieldPropsManager& fp, std::vector >& rftProperties) { - HandlerContext handlerContext { block, keyword, currentStep, grid, fp }; + static const std::unordered_set require_grid = { + "COMPDAT", + "COMPSEGS" + }; + + HandlerContext handlerContext { block, keyword, currentStep }; + + /* + The grid and fieldProps members create problems for reiterating the + Schedule section. We therefor single them out very clearly here. + */ + if (require_grid.count(keyword.name()) > 0) { + handlerContext.grid_ptr = &grid; + handlerContext.fp_ptr = &fp; + } if (handleNormalKeyword(handlerContext, parseContext, errors)) return;