From 546523e229c15a815c8a3456a4c4e3e7b5120807 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Wed, 24 Mar 2021 17:24:42 +0100 Subject: [PATCH 1/4] Add new class RSTConfig for configuration of restart. --- CMakeLists_files.cmake | 2 + examples/opmhash.cpp | 2 +- .../EclipseState/Schedule/RSTConfig.hpp | 235 ++++++++ .../EclipseState/Schedule/Schedule.hpp | 24 +- .../EclipseState/Schedule/ScheduleState.hpp | 17 +- .../EclipseState/Schedule/KeywordHandlers.cpp | 23 +- .../EclipseState/Schedule/RSTConfig.cpp | 533 ++++++++++++++++++ .../EclipseState/Schedule/Schedule.cpp | 32 +- .../EclipseState/Schedule/ScheduleState.cpp | 75 ++- tests/parser/RestartConfigTests.cpp | 155 ++++- .../integration_tests/IOConfig/RPT_TEST2.DATA | 3 + .../integration/IOConfigIntegrationTest.cpp | 51 ++ 12 files changed, 1084 insertions(+), 68 deletions(-) create mode 100644 opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp create mode 100644 src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 7719a83b0..fa44bcfe3 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -130,6 +130,7 @@ if(ENABLE_ECL_INPUT) src/opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.cpp src/opm/parser/eclipse/EclipseState/Schedule/RFTConfig.cpp src/opm/parser/eclipse/EclipseState/Schedule/RPTConfig.cpp + src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.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 @@ -758,6 +759,7 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/Schedule/SummaryState.hpp opm/parser/eclipse/EclipseState/Schedule/RFTConfig.hpp opm/parser/eclipse/EclipseState/Schedule/RPTConfig.hpp + opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp opm/parser/eclipse/EclipseState/Schedule/ScheduleDeck.hpp opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp diff --git a/examples/opmhash.cpp b/examples/opmhash.cpp index 030e8122d..d86b1f6bb 100644 --- a/examples/opmhash.cpp +++ b/examples/opmhash.cpp @@ -19,7 +19,7 @@ #include #include - +#include #include #include diff --git a/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp b/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp new file mode 100644 index 000000000..da298c12b --- /dev/null +++ b/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.hpp @@ -0,0 +1,235 @@ +/* + 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 OPM_RST_CONFIG_HPP +#define OPM_RST_CONFIG_HPP + + +/* + The RestartConfig class internalizes information of when (at which + report steps) we should save restart files, and which properties + should be included in the restart files. The configuration of this + immensely complex, and this code is unfortunately also way too + complex. + + The most basic question to disentangle is the "When to write restart + files" versus "What data to store write in the restart file". As + expressed in the deck keywords this completely entangled, in this + implementation we have tried to disentangle it: + + Keywords involved + ----------------- + + RPTRST: This is the main keyword for configuring restart output; it + can be used to configure bothe when to write the files and which + properties should be included in the restart files. + + + RPTSCHED: The main purpose of the RPTSCHED keyword is to configure + output from the SCHEDULE section to the PRINT file. However the + mneomnic RESTART=n can be used to turn writing of restart files + on, and also for values > 2 to some configuration of what is + written to the restart file: + + RESTART=1 : As RPTRST,BASIC=1 + RESTART>1 : As RPTRST,BASIC=2 + RESTART>2 : Flow is added to restart file + RESTART>3 : Fluid in place is added to restart file + RESTART=6 : Restart file for every timestep. + + + + RPTSOL: The RPTSOL keyword is very similar to the RPTCHED keyword, + it configures output from the SOLUTION section to the PRINT file, + but just as the RPTSCHED keyword it accepts a RESTART=n mnenonic + which can be used similarly to the BASIC=n mnenonic of the RPTRST + keyword. In particular the writing of an initial restart files + with initial equilibrium solution is controlled by the RPTSOL + keyword. If the restart mneonic is greater than 2 that can be + used to configure FLOWS and FIP keywords in the restart file. + + RESTART=1 : As RPTRST,BASIC=1 + RESTART>1 : As RPTRST,BASIC=2 + RESTART>2 : Flow is added to restart file + RESTART>3 : Fluid in place is added to restart file + + + The basic rule in ECLIPSE is generally that the 'last keyword wins', + but for the RPTRST RPTSHCED combination a BASIC setting with n >= 3 + will override consecutive RESTART=n settings from RPTSCHED. + + + When to write restart files: + ---------------------------- + + When to write the restart file is governed by the BASIC=n setting in + the RPTRST keyword and the RESTART=n settings in the RPTSOL and + RPTSCHED keywords. The most common setting is 'ON' - i.e. BASIC=2 + which means write a restart file for every report step, that can be + turned off again with BASIC=0. For BASIC>2 there are varietes of + every n'th report step, and the first report step in every month and + every year. + + + Old style / new style + --------------------- + + All of the relevant keywords can be specified using a new style + based on string mneomnics and alternatively an old style represented + with a *strictly ordered* list of integers. For instance both of + these keywords request restart files written for every report step; + in addition to the fields required to actually restart the files + should contain the relative permeabilities KRO, KRW, KRG: + + RPTRST + BASIC=2 KRG KRW KRO / + + + RPTRST + 2 9*0 3*1 17*0 + + Integer controls and string mneomnics can not be mixed in the same + keyword, but they can be mixed in the same deck - and that is + actually quite common. + + + What is written to the restart file + ----------------------------------- + + The BASIC=n mneonics request the writing of a restart file which + should contain 'all properties required to restart', in addition you + can configure extra keywords to be added to the restart file. This + is configured by just adding a list as: + + RPTRST + BASIC=2 KRG KRW KRO / + + It is really *not clear* what is the correct persistence semantics + for these keywords, consider for insance the following series of keywords: + + -- Request restart file at every report step, the restart files + -- should contain additional properties KRO, KRG and KRW. + RPTRST + BASIC=2 KRG KRW KRO / + + -- Advance the simulator forward with TSTEP / DATES + TSTEP / DATES / WCONxxx + + -- Turn writing of restart files OFF using integer controls. + RPTRST + 0 / + + -- Advance the simulator forward with TSTEP / DATES + TSTEP / DATES / WCONxxx + + -- Turn writing of restart files ON using integer controls. + RPTRST + 2 / + + When writing of restart files is turned on again with the last + RPTRST keyword, should still the relative permeabilites KRO, KRW and + KRG be added to the restart files? The model we have implemented is: + + - The list of keywords written to the restart file is persisted + independtly of the BASIC=n setting. + + - Using string based mnonics you can *only add* kewyords to be + written to the files. To stop writing a keyword you must use an + integer control with value 0. + + Based on this best guess heuristic the final restart files will + still contain KRO, KRW and KRG. + + + + What is required to restart? + ---------------------------- + + A restart capable files is requested with the 'BASIC' mneomnic, but + exactly which properties the 'BASIC' keyword is expanded to is the + responsability of the simulator; i.e. for a black oil simulation you + will at the very least need the expansion: + + BASIC -> PRESSURE, SWAT, SGAS, RS, RV + + But this class just carries the boolean information: Yes - restart + is requested - expanding as illustrated is the responsability of the + simulator. + + + + + What is not supported? + ---------------------- + + The SAVE keyword is not supported in OPM at all, this implies that + the SAVE and SFREQ mneomics are not supported. +*/ + +#include +#include +#include + +#include + +namespace Opm { + +class ParseContext; +class ErrorGuard; + +class RSTConfig { +public: + RSTConfig() = default; + RSTConfig(const SOLUTIONSection& solution_section, const ParseContext& parseContext, ErrorGuard& errors); + void update(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors); + void init_next(); + static RSTConfig first(const RSTConfig& src); + static RSTConfig serializeObject(); + + template + void serializeOp(Serializer& serializer) { + serializer(write_rst_file); + serializer.template map, false>(keywords); + serializer(basic); + serializer(freq); + serializer(save); + } + + bool operator==(const RSTConfig& other) const; + + std::optional write_rst_file; + std::map keywords; + std::optional basic; + std::optional freq; + bool save = false; + +private: + void handleRPTSOL(const DeckKeyword& keyword); + void handleRPTRST(const DeckKeyword& keyword, const ParseContext& parse_context, ErrorGuard& errors); + void handleRPTSCHED(const DeckKeyword& keyword, const ParseContext& parse_context, ErrorGuard& errors); + void update_schedule(const std::pair, std::optional>& basic_freq); +}; + + + +} //namespace Opm + + + +#endif diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 20f8defda..55cee567d 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -24,9 +24,9 @@ #include #include +#include #include #include -#include #include #include #include @@ -71,6 +71,7 @@ namespace Opm MessageLimits m_deck_message_limits; UnitSystem m_unit_system; Runspec m_runspec; + RSTConfig rst_config; ScheduleStatic() = default; @@ -80,13 +81,17 @@ namespace Opm ScheduleStatic(std::shared_ptr python_handle, const Deck& deck, - const Runspec& runspec) : + const Runspec& runspec, + const ParseContext& parseContext, + ErrorGuard& errors): m_python_handle(python_handle), m_input_path(deck.getInputPath()), m_deck_message_limits( deck ), m_unit_system( deck.getActiveUnitSystem() ), - m_runspec( runspec ) - {} + m_runspec( runspec ), + rst_config( SOLUTIONSection(deck), parseContext, errors ) + { + } template void serializeOp(Serializer& serializer) @@ -95,6 +100,7 @@ namespace Opm m_runspec.serializeOp(serializer); m_unit_system.serializeOp(serializer); serializer(this->m_input_path); + rst_config.serializeOp(serializer); } @@ -105,6 +111,7 @@ namespace Opm st.m_runspec = Runspec::serializeObject(); st.m_unit_system = UnitSystem::newFIELD(); st.m_input_path = "Some/funny/path"; + st.rst_config = RSTConfig::serializeObject(); return st; } @@ -112,6 +119,7 @@ namespace Opm return this->m_input_path == other.m_input_path && this->m_deck_message_limits == other.m_deck_message_limits && this->m_unit_system == other.m_unit_system && + this->rst_config == other.rst_config && this->m_runspec == other.m_runspec; } }; @@ -287,7 +295,6 @@ namespace Opm void serializeOp(Serializer& serializer) { m_sched_deck.serializeOp(serializer); - restart_config.serializeOp(serializer); serializer.vector(snapshots); m_static.serializeOp(serializer); serializer(m_restart_info); @@ -307,6 +314,7 @@ namespace Opm pack_unpack(serializer); pack_unpack(serializer); pack_unpack(serializer); + pack_unpack(serializer); pack_unpack_map(serializer); pack_unpack_map(serializer); @@ -451,13 +459,9 @@ namespace Opm ScheduleStatic m_static; std::pair m_restart_info; ScheduleDeck m_sched_deck; - RestartConfig restart_config; std::optional exit_status; std::vector snapshots; - RestartConfig& restart(); - const RestartConfig& restart() const; - void load_rst(const RestartIO::RstState& rst, const EclipseGrid& grid, const FieldPropsManager& fp); @@ -599,8 +603,10 @@ namespace Opm void handleMXUNSUPP (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleNODEPROP (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleNUPCOL (const HandlerContext&, const ParseContext&, ErrorGuard&); + void handleRPTRST (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleRPTSCHED (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleTUNING (const HandlerContext&, const ParseContext&, ErrorGuard&); + void handleSAVE (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleUDQ (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleVAPPARS (const HandlerContext&, const ParseContext&, ErrorGuard&); void handleVFPINJ (const HandlerContext&, const ParseContext&, ErrorGuard&); diff --git a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp index 686789f66..d9d7e9daa 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.hpp @@ -49,6 +49,7 @@ #include #include #include +#include namespace { @@ -316,6 +317,10 @@ namespace Opm { Well::ProducerCMode whistctl() const; void update_whistctl(Well::ProducerCMode whistctl); + bool rst_file(const RSTConfig& rst_config) const; + void update_date(const time_point& prev_time); + void handleSAVE(); + /*********************************************************************/ ptr_member pavg; @@ -333,6 +338,7 @@ namespace Opm { ptr_member glo; ptr_member guide_rate; ptr_member rft_config; + ptr_member rst_config; template struct always_false1 : std::false_type {}; @@ -368,6 +374,8 @@ namespace Opm { return this->guide_rate; else if constexpr ( std::is_same_v ) return this->rft_config; + else if constexpr ( std::is_same_v ) + return this->rst_config; else static_assert(always_false1::value, "Template type not supported in get()"); } @@ -404,6 +412,8 @@ namespace Opm { return this->guide_rate; else if constexpr ( std::is_same_v ) return this->rft_config; + else if constexpr ( std::is_same_v ) + return this->rst_config; else static_assert(always_false1::value, "Template type not supported in get()"); } @@ -441,6 +451,7 @@ namespace Opm { serializer(m_year_num); serializer(m_first_in_year); serializer(m_first_in_month); + serializer(m_save_step); m_tuning.serializeOp(serializer); serializer(m_nupcol); m_oilvap.serializeOp(serializer); @@ -460,8 +471,10 @@ namespace Opm { std::size_t m_sim_step = 0; std::size_t m_month_num = 0; std::size_t m_year_num = 0; - bool m_first_in_month = true; - bool m_first_in_year = true; + bool m_first_in_month; + bool m_first_in_year; + std::optional m_save_step; + Tuning m_tuning; int m_nupcol; OilVaporizationProperties m_oilvap; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index 1bc9faa19..b9215ccd9 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -790,8 +790,27 @@ namespace { this->snapshots.back().update_nupcol(nupcol); } - void Schedule::handleRPTSCHED(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { + void Schedule::handleRPTSCHED(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { this->snapshots.back().rpt_config.update( RPTConfig(handlerContext.keyword )); + auto rst_config = this->snapshots.back().rst_config(); + rst_config.update(handlerContext.keyword, parseContext, errors); + this->snapshots.back().rst_config.update(std::move(rst_config)); + } + + void Schedule::handleRPTRST(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { + auto rst_config = this->snapshots.back().rst_config(); + rst_config.update(handlerContext.keyword, parseContext, errors); + this->snapshots.back().rst_config.update(std::move(rst_config)); + } + + /* + We do not really handle the SAVE keyword, we just interpret it as: Write a + normal restart file at this report step. + */ + void Schedule::handleSAVE(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { + auto rst_config = this->snapshots.back().rst_config(); + rst_config.save = true; + this->snapshots.back().rst_config.update(std::move(rst_config)); } void Schedule::handleTUNING(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { @@ -1972,7 +1991,9 @@ namespace { { "MULTZ-" , &Schedule::handleMXUNSUPP }, { "NODEPROP", &Schedule::handleNODEPROP }, { "NUPCOL" , &Schedule::handleNUPCOL }, + { "RPTRST" , &Schedule::handleRPTRST }, { "RPTSCHED", &Schedule::handleRPTSCHED }, + { "SAVE" , &Schedule::handleSAVE }, { "TUNING" , &Schedule::handleTUNING }, { "UDQ" , &Schedule::handleUDQ }, { "VAPPARS" , &Schedule::handleVAPPARS }, diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.cpp new file mode 100644 index 000000000..bdf0d8c0a --- /dev/null +++ b/src/opm/parser/eclipse/EclipseState/Schedule/RSTConfig.cpp @@ -0,0 +1,533 @@ +/* + 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 +#include + +#include +#include +#include +#include +#include +#include + + +namespace Opm { + +namespace { + +inline bool is_int( const std::string& x ) { + auto is_digit = []( char c ) { return std::isdigit( c ); }; + + return !x.empty() + && ( x.front() == '-' || is_digit( x.front() ) ) + && std::all_of( x.begin() + 1, x.end(), is_digit ); +} + +constexpr const char* SCHEDIntegerKeywords[] = { "PRES", // 1 + "SOIL", // 2 + "SWAT", // 3 + "SGAS", // 4 + "RS", // 5 + "RV", // 6 + "RESTART", // 7 + "FIP", // 8 + "WELLS", // 9 + "VFPPROD", // 10 + "SUMMARY", // 11 + "CPU", // 12 + "AQUCT", // 13 + "WELSPECS",// 14 + "NEWTON", // 15 + "POILD", // 16 + "PWAT", // 17 + "PWATD", // 18 + "PGAS", // 19 + "PGASD", // 20 + "FIPVE", // 21 + "WOC", // 22 + "GOC", // 23 + "WOCDIFF", // 24 + "GOCDIFF", // 25 + "WOCGOC", // 26 + "ODGAS", // 27 + "ODWAT", // 28 + "GDOWAT", // 29 + "WDOGAS", // 30 + "OILAPI", // 31 + "FIPITR", // 32 + "TBLK", // 33 + "PBLK", // 34 + "SALT", // 35 + "PLYADS", // 36 + "RK", // 37 + "FIPSALT", // 38 + "TUNING", // 39 + "GI", // 40 + "ROCKC", // 41 + "SPENWAT", // 42 + "FIPSOL", // 43 + "SURFBLK", // 44 + "SURFADS", // 45 + "FIPSURF", // 46 + "TRADS", // 47 + "VOIL", // 48 + "VWAT", // 49 + "VGAS", // 50 + "DENO", // 51 + "DENW", // 52 + "DENG", // 53 + "GASCONC", // 54 + "PB", // 55 + "PD", // 56 + "KRW", // 57 + "KRO", // 58 + "KRG", // 59 + "MULT", // 60 + "UNKNOWN", // 61 61 and 62 are not listed in the manual + "UNKNOWN", // 62 + "FOAM", // 63 + "FIPFOAM", // 64 + "TEMP", // 65 + "FIPTEMP", // 66 + "POTC", // 67 + "FOAMADS", // 68 + "FOAMDCY", // 69 + "FOAMMOB", // 70 + "RECOV", // 71 + "FLOOIL", // 72 + "FLOWAT", // 73 + "FLOGAS", // 74 + "SGTRAP", // 75 + "FIPRESV", // 76 + "FLOSOL", // 77 + "KRN", // 78 + "GRAD", // 79 + }; +constexpr const char* RSTIntegerKeywords[] = { "BASIC", // 1 + "FLOWS", // 2 + "FIP", // 3 + "POT", // 4 + "PBPD", // 5 + "FREQ", // 6 + "PRES", // 7 + "VISC", // 8 + "DEN", // 9 + "DRAIN", // 10 + "KRO", // 11 + "KRW", // 12 + "KRG", // 13 + "PORO", // 14 + "NOGRAD", // 15 + "NORST", // 16 NORST - not supported + "SAVE", // 17 + "SFREQ", // 18 SFREQ=?? - not supported + "ALLPROPS", // 19 + "ROCKC", // 20 + "SGTRAP", // 21 + "", // 22 - Blank - ignored. + "RSSAT", // 23 + "RVSAT", // 24 + "GIMULT", // 25 + "SURFBLK", // 26 + "", // 27 - PCOW, PCOG, special cased + "STREAM", // 28 STREAM=?? - not supported + "RK", // 29 + "VELOCITY", // 30 + "COMPRESS" }; // 31 + +bool is_RPTRST_mnemonic( const std::string& kw ) { + /* all eclipse 100 keywords we want to not simply ignore. The list is + * sorted, so we can use binary_search for log(n) lookup. It is important + * that the list is sorted, but these are all the keywords listed in the + * manual and unlikely to change at all + */ + static constexpr const char* valid[] = { + "ACIP", "ACIS", "ALLPROPS", "BASIC", "BG", "BO", + "BW", "CELLINDX", "COMPRESS", "CONV", "DEN", "DRAIN", + "DRAINAGE", "DYNREG", "FIP", "FLORES", "FLOWS", "FREQ", + "GIMULT", "HYDH", "HYDHFW", "KRG", "KRO", "KRW", + "NOGRAD", "NORST", "NPMREB", "PBPD", "PCOG", "PCOW", + "PERMREDN", "POIS", "PORO", "PORV", "POT", "PRES", + "RFIP", "RK", "ROCKC", "RPORV", "RSSAT", "RVSAT", + "SAVE", "SDENO", "SFIP", "SFREQ", "SGTRAP", "SIGM_MOD", + "STREAM", "SURFBLK", "TRAS", "VELGAS", "VELOCITY", "VELOIL", + "VELWAT", "VISC", + }; + + return std::binary_search( std::begin( valid ), std::end( valid ), kw ); +} + + +bool is_RPTSCHED_mnemonic( const std::string& kw ) { + static constexpr const char* valid[] = { + "ALKALINE", "ANIONS", "AQUCT", "AQUFET", "AQUFETP", "BFORG", + "CATIONS", "CPU", "DENG", "DENO", "DENW", "ESALPLY", + "ESALSUR", "FFORG", "FIP", "FIPFOAM", "FIPHEAT", "FIPRESV", + "FIPSALT", "FIPSOL", "FIPSURF", "FIPTEMP", "FIPTR", "FIPVE", + "FLOGAS", "FLOOIL", "FLOSOL", "FLOWAT", "FMISC", "FOAM", + "FOAMADS", "FOAMCNM", "FOAMDCY", "FOAMMOB", "GASCONC", "GASSATC", + "GDOWAT", "GI", "GOC", "GOCDIFF", "GRAD", "KRG", + "KRN", "KRO", "KRW", "MULT", "NEWTON", "NOTHING", + "NPMREB", "ODGAS", "ODWAT", "OILAPI", "PB", "PBLK", + "PBU", "PD", "PDEW", "PGAS", "PGASD", "PLYADS", + "POIL", "POILD", "POLYMER", "POTC", "POTG", "POTO", + "POTW", "PRES", "PRESSURE", "PWAT", "PWATD", "RECOV", + "RESTART", "ROCKC", "RS", "RSSAT", "RV", "RVSAT", + "SALT", "SGAS", "SGTRAP", "SIGM_MOD", "SOIL", "SSOL", + "SUMMARY", "SURFADS", "SURFBLK", "SWAT", "TBLK", "TEMP", + "TRACER", "TRADS", "TRDCY", "TUNING", "VFPPROD", "VGAS", + "VOIL", "VWAT", "WDOGAS", "WELLS", "WELSPECL", "WELSPECS", + "WOC", "WOCDIFF", "WOCGOC", + }; + + return std::binary_search( std::begin( valid ), std::end( valid ), kw ); +} + +inline std::map< std::string, int > +RPTSCHED_integer( const std::vector< int >& ints ) { + const size_t size = std::min( ints.size(), sizeof( SCHEDIntegerKeywords ) ); + + std::map< std::string, int > mnemonics; + for( size_t i = 0; i < size; ++i ) + mnemonics[ SCHEDIntegerKeywords[ i ] ] = ints[ i ]; + + return mnemonics; +} + +inline std::map< std::string, int > +RPTRST_integer( const std::vector< int >& ints ) { + const size_t PCO_index = 26; + const size_t BASIC_index = 0; + + std::map< std::string, int > mnemonics; + const size_t size = std::min( ints.size(), sizeof( RSTIntegerKeywords ) ); + + /* fun with special cases. Eclipse seems to ignore the BASIC=0, interpreting + * it as sort-of "don't modify". Handle this by *not* adding/updating the + * integer list sourced BASIC mnemonic, should it be zero. I'm not sure if + * this applies to other mnemonics, but the eclipse manual indicates that + * any zero here should disable the output. + * + * See https://github.com/OPM/opm-parser/issues/886 for reference + * + * The current treatment of a mix on RPTRST and RPTSCHED integer keywords is + * probably not correct, but it is extremely difficult to comprehend exactly + * how it should be. Current code is a rather arbitrary hack to get through + * the tests. + */ + + if (size >= 26) { + for( size_t i = 0; i < std::min( size, PCO_index ); ++i ) + mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ]; + } else { + if( size > 0 && ints[ BASIC_index ] != 0) + mnemonics[ RSTIntegerKeywords[ BASIC_index ] ] = ints[ BASIC_index ]; + + for( size_t i = 1; i < std::min( size, PCO_index ); ++i ) + mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ]; + } + + for( size_t i = PCO_index + 1; i < size; ++i ) + mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ]; + + /* item 27 (index 26) sets both PCOW and PCOG, so we special case it */ + if( ints.size() >= PCO_index ) { + mnemonics[ "PCOW" ] = ints[ PCO_index ]; + mnemonics[ "PCOG" ] = ints[ PCO_index ]; + } + + return mnemonics; +} + + +template< typename F, typename G > +inline std::map< std::string, int > RPT( const DeckKeyword& keyword, + const ParseContext& parseContext, + ErrorGuard& errors, + F is_mnemonic, + G integer_mnemonic ) { + + std::vector items; + const auto& deck_items = keyword.getStringData(); + const auto ints = std::any_of( deck_items.begin(), deck_items.end(), is_int ); + const auto strs = !std::all_of( deck_items.begin(), deck_items.end(), is_int ); + + /* if any of the values are pure integers we assume this is meant to be the + * slash-terminated list of integers way of configuring. If integers and + * non-integers are mixed, this is an error; however if the error mode + * RPT_MIXED_STYLE is permissive we try some desperate heuristics to + * interpret this as list of mnemonics. See the the documentation of the + * RPT_MIXED_STYLE error handler for more details. + */ + auto stoi = []( const std::string& str ) { return std::stoi( str ); }; + if( !strs ) + return integer_mnemonic( fun::map( stoi, deck_items ) ); + + + if (ints && strs) { + const auto& location = keyword.location(); + std::string msg = "Error in keyword {keyword}, mixing mnemonics and integers is not allowed\n" + "In {file} line {line}."; + parseContext.handleError(ParseContext::RPT_MIXED_STYLE, msg, location, errors); + + std::vector stack; + for (size_t index=0; index < deck_items.size(); index++) { + if (is_int(deck_items[index])) { + + if (stack.size() < 2) + throw OpmInputError("Problem processing {keyword}\nIn {file} line {line}.", location); + + if (stack.back() == "=") { + stack.pop_back(); + std::string mnemonic = stack.back(); + stack.pop_back(); + + items.insert(items.begin(), stack.begin(), stack.end()); + stack.clear(); + items.push_back( mnemonic + "=" + deck_items[index]); + } else + throw OpmInputError("Problem processing {keyword}\nIn {file} line {line}.", location); + + } else + stack.push_back(deck_items[index]); + } + items.insert(items.begin(), stack.begin(), stack.end()); + } else + items = deck_items; + + std::map< std::string, int > mnemonics; + for( const auto& mnemonic : items ) { + const auto sep_pos = mnemonic.find_first_of( "= " ); + + std::string base = mnemonic.substr( 0, sep_pos ); + if( !is_mnemonic( base ) ) { + std::string msg_fmt = fmt::format("Error in keyword {{keyword}}, unrecognized mnemonic {}\nIn {{file}} line {{line}}.", base); + parseContext.handleError(ParseContext::RPT_UNKNOWN_MNEMONIC, msg_fmt, keyword.location(), errors); + continue; + } + + int val = 1; + if (sep_pos != std::string::npos) { + const auto value_pos = mnemonic.find_first_not_of("= ", sep_pos); + if (value_pos != std::string::npos) + val = std::stoi(mnemonic.substr(value_pos)); + } + + mnemonics.emplace( base, val ); + } + + return mnemonics; +} + +inline void expand_RPTRST_mnemonics(std::map< std::string, int >& mnemonics) { + const auto allprops_iter = mnemonics.find( "ALLPROPS"); + if (allprops_iter != mnemonics.end()) { + const auto value = allprops_iter->second; + mnemonics.erase( allprops_iter ); + + for (const auto& kw : {"BG","BO","BW","KRG","KRO","KRW","VOIL","VGAS","VWAT","DEN"}) + mnemonics[kw] = value; + } +} + +std::optional extract(std::map& mnemonics, const std::string& key) { + auto iter = mnemonics.find(key); + if (iter == mnemonics.end()) + return {}; + + int value = iter->second; + mnemonics.erase(iter); + return value; +} + + +inline std::pair< std::map< std::string, int >, std::pair, std::optional>> +RPTRST( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) { + auto mnemonics = RPT( keyword, parseContext, errors, is_RPTRST_mnemonic, RPTRST_integer ); + std::optional basic = extract(mnemonics, "BASIC"); + std::optional freq = extract(mnemonics, "FREQ"); + + expand_RPTRST_mnemonics( mnemonics ); + return {mnemonics, { basic, freq }}; +} + + +template +void update_optional(std::optional& target, const std::optional& src) { + if (src.has_value()) + target = src; +} + + +} + +// The handleRPTSOL() function is only invoked from the constructor which uses +// the SOLUTION section, and the only information actually extracted is whether +// to write the initial restart file. + +void RSTConfig::handleRPTSOL( const DeckKeyword& keyword) { + const auto& record = keyword.getRecord(0); + const auto& item = record.getItem(0); + for (const auto& mnemonic : item.getData()) { + auto mnemonic_RESTART_pos = mnemonic.find("RESTART="); + if (mnemonic_RESTART_pos != std::string::npos) { + std::string restart_no = mnemonic.substr(mnemonic_RESTART_pos + 8, mnemonic.size()); + auto restart = std::strtoul(restart_no.c_str(), nullptr, 10); + this->write_rst_file = (restart > 1); + return; + } + } + + + /* If no RESTART mnemonic is found, either it is not present or we might + have an old data set containing integer controls instead of mnemonics. + Restart integer switch is integer control nr 7 */ + + if (item.data_size() >= 7) { + const std::string& integer_control = item.get(6); + auto restart = std::strtoul(integer_control.c_str(), nullptr, 10); + this->write_rst_file = (restart > 1); + return; + } +} + +bool RSTConfig::operator==(const RSTConfig& other) const { + return this->write_rst_file == other.write_rst_file && + this->keywords == other.keywords && + this->basic == other.basic && + this->freq == other.freq && + this->save == other.save; +} + + +void RSTConfig::update_schedule(const std::pair, std::optional>& basic_freq) { + update_optional(this->basic, basic_freq.first); + update_optional(this->freq, basic_freq.second); + if (this->basic.has_value()) { + auto basic_value = this->basic.value(); + if (basic_value == 0) + this->write_rst_file = false; + else if (basic_value == 1 || basic_value == 2) + this->write_rst_file = true; + else + this->write_rst_file = {}; + } +} + + +void RSTConfig::handleRPTRST(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) { + const auto& [mnemonics, basic_freq] = RPTRST(keyword, parseContext, errors); + this->update_schedule(basic_freq); + for (const auto& [kw,num] : mnemonics) + this->keywords[kw] = num; +} + +void RSTConfig::handleRPTSCHED(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) { + auto mnemonics = RPT( keyword, parseContext, errors, is_RPTSCHED_mnemonic, RPTSCHED_integer ); + auto nothing = extract(mnemonics, "NOTHING"); + if (nothing.has_value()) { + this->basic = {}; + this->keywords.clear(); + } + + if (this->basic.value_or(2) <= 2) { + auto restart = extract(mnemonics, "RESTART"); + if (restart.has_value()) { + auto basic_value = std::min(2, restart.value()); + this->update_schedule({basic_value , 1}); + } + } + + for (const auto& [kw,num] : mnemonics) + this->keywords[kw] = num; +} + + +RSTConfig::RSTConfig(const SOLUTIONSection& solution_section, const ParseContext& parseContext, ErrorGuard& errors) +{ + this->write_rst_file = false; + if (solution_section.hasKeyword()) { + const auto& keyword = solution_section.getKeyword(); + this->handleRPTRST(keyword, parseContext, errors); + + // Guessing on eclipse rules for write of initial RESTART file (at time 0): + // Write of initial restart file is (due to the eclipse reference manual) + // governed by RPTSOL RESTART in solution section, + // if RPTSOL RESTART > 1 initial restart file is written. + // but - due to initial restart file written from Eclipse + // for data where RPTSOL RESTART not set - guessing that + // when RPTRST is set in SOLUTION (no basic though...) -> write inital restart. + this->write_rst_file = true; + } + + if (solution_section.hasKeyword()) { + const auto& keyword = solution_section.getKeyword(); + this->handleRPTSOL(keyword); + } +} + + +void RSTConfig::update(const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors) { + if (keyword.name() == ParserKeywords::RPTRST::keywordName) + this->handleRPTRST(keyword, parseContext, errors); + else if (keyword.name() == ParserKeywords::RPTSCHED::keywordName) { + this->handleRPTSCHED(keyword, parseContext, errors); + } else + throw std::logic_error("The RSTConfig object can only use RPTRST and RPTSCHED keywords"); +} + + +RSTConfig RSTConfig::serializeObject() { + RSTConfig rst_config; + rst_config.basic = 10; + rst_config.freq = {}; + rst_config.write_rst_file = true; + rst_config.save = true; + rst_config.keywords = {{"S1", 1}, {"S2", 2}}; + return rst_config; +} + +/* + The RPTRST keyword is treated differently in the SOLUTION section and in the + SCHEDULE section. This function takes a RSTConfig object created from the + solution section and creates a transformed copy suitable as the first + RSTConfig to represent the Schedule section. +*/ +RSTConfig RSTConfig::first(const RSTConfig& solution_config ) { + RSTConfig rst_config(solution_config); + auto basic = rst_config.basic; + if (!basic.has_value()) { + rst_config.write_rst_file = false; + return rst_config; + } + + auto basic_value = basic.value(); + if (basic_value == 0) + rst_config.write_rst_file = false; + else if (basic_value == 1 || basic_value == 2) + rst_config.write_rst_file = true; + else if (basic_value >= 3) + rst_config.write_rst_file = {}; + + return rst_config; +} + +} + + diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index fb3e100d6..5435a4564 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -108,10 +108,9 @@ namespace { const std::optional& output_interval, const RestartIO::RstState * rst) try : - m_static( python, deck, runspec ), + m_static( python, deck, runspec, parseContext, errors ), m_restart_info( restart_info(rst)), - m_sched_deck(deck, m_restart_info ), - restart_config(deck, m_restart_info, output_interval, parseContext, errors) + m_sched_deck(deck, m_restart_info ) { if (rst) { auto restart_step = this->m_restart_info.second; @@ -211,7 +210,6 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const std::optional Schedule result; result.m_static = ScheduleStatic::serializeObject(); - result.restart_config = RestartConfig::serializeObject(); result.snapshots = { ScheduleState::serializeObject() }; return result; @@ -1206,28 +1204,28 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e } } - RestartConfig& Schedule::restart() { - return this->restart_config; - } + bool Schedule::write_rst_file(std::size_t report_step, bool ) const { + if (report_step == 0) + return this->m_static.rst_config.write_rst_file.value(); - const RestartConfig& Schedule::restart() const { - return this->restart_config; - } - -bool Schedule::write_rst_file(std::size_t report_step, bool log) const { - return this->restart_config.getWriteRestartFile(report_step, log); + const auto& rst_config = this->snapshots[report_step - 1].rst_config(); + const auto& state = this->snapshots[report_step]; + return state.rst_file(rst_config); } - const std::map< std::string, int >& Schedule::rst_keywords( size_t timestep ) const { - return this->restart_config.getRestartKeywords(timestep); + const std::map< std::string, int >& Schedule::rst_keywords( size_t report_step ) const { + if (report_step == 0) + return this->m_static.rst_config.keywords; + + const auto& keywords = this->snapshots[report_step - 1].rst_config().keywords; + return keywords; } bool Schedule::operator==(const Schedule& data) const { return this->m_restart_info == data.m_restart_info && this->m_static == data.m_static && - this->restart_config == data.restart_config && this->snapshots == data.snapshots; } @@ -1647,6 +1645,8 @@ void Schedule::create_first(const time_point& start_time, const std::optionalm_static.rst_config ) ); + //sched_state.update_date( start_time ); this->addGroup("FIELD", 0); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp index fb59e5d80..a2ca63618 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/ScheduleState.cpp @@ -40,7 +40,7 @@ time_point clamp_time(time_point t) { return TimeService::from_time_t( TimeService::to_time_t( t ) ); } -std::pair date_diff(time_point t2, time_point t1) { +std::pair date_diff(const time_point& t2, const time_point& t1) { auto ts1 = TimeStampUTC(TimeService::to_time_t(t1)); auto ts2 = TimeStampUTC(TimeService::to_time_t(t2)); auto year_diff = ts2.year() - ts1.year(); @@ -54,8 +54,12 @@ std::pair date_diff(time_point t2, time_point t1) { ScheduleState::ScheduleState(const time_point& t1): - m_start_time(clamp_time(t1)) + m_start_time(clamp_time(t1)), + m_first_in_month(true), + m_first_in_year(true) { + auto ts1 = TimeStampUTC(TimeService::to_time_t(this->m_start_time)); + this->m_month_num = ts1.month() - 1; } ScheduleState::ScheduleState(const time_point& start_time, const time_point& end_time) : @@ -64,6 +68,19 @@ ScheduleState::ScheduleState(const time_point& start_time, const time_point& end this->m_end_time = clamp_time(end_time); } +void ScheduleState::update_date(const time_point& prev_time) { + auto [year_diff, month_diff] = date_diff(this->m_start_time, prev_time); + this->m_year_num += year_diff; + this->m_first_in_month = (month_diff > 0); + this->m_first_in_year = (year_diff > 0); + + auto ts1 = TimeStampUTC(TimeService::to_time_t(this->m_start_time)); + this->m_month_num = ts1.month() - 1; +} + + + + ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time) : ScheduleState(src) { @@ -79,14 +96,15 @@ ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_t if (next_rft.has_value()) this->rft_config.update( std::move(*next_rft) ); - auto [year_diff, month_diff] = date_diff(this->m_start_time, src.m_start_time); - this->m_year_num += year_diff; - this->m_month_num += month_diff; - - this->m_first_in_month = (this->m_month_num > src.m_month_num); - this->m_first_in_year = (this->m_year_num > src.m_year_num); + this->update_date(src.m_start_time); + if (this->rst_config().save) { + auto new_rst = this->rst_config(); + new_rst.save = false; + this->rst_config.update( std::move(new_rst) ); + } } + ScheduleState::ScheduleState(const ScheduleState& src, const time_point& start_time, const time_point& end_time) : ScheduleState(src, start_time) { @@ -244,6 +262,7 @@ ScheduleState ScheduleState::serializeObject() { ts.guide_rate.update( GuideRateConfig::serializeObject() ); ts.glo.update( GasLiftOpt::serializeObject() ); ts.rft_config.update( RFTConfig::serializeObject() ); + ts.rst_config.update( RSTConfig::serializeObject() ); return ts; } @@ -285,4 +304,44 @@ const WellGroupEvents& ScheduleState::wellgroup_events() const { return this->m_wellgroup_events; } + +/* + Observe that the decision to write a restart file will typically be a + combination of the RST configuration from the previous report step, and the + first_in_year++ attributes of this report step. That is the reason the + function takes a RSTConfig argument - instead of using the rst_config member. + +*/ + +bool ScheduleState::rst_file(const RSTConfig& rst) const { + if (rst.save) + return true; + + if (rst.write_rst_file.has_value()) + return rst.write_rst_file.value(); + + auto freq = rst.freq.value_or(1); + auto basic = rst.basic.value(); + + if (basic == 3) + return (this->sim_step() % freq) == 0; + + if (basic == 4) { + if (!this->first_in_year()) + return false; + + return (this->m_year_num % freq) == 0; + } + + if (basic == 5) { + if (!this->first_in_month()) + return false; + + return (this->m_month_num % freq) == 0; + } + + throw std::logic_error(fmt::format("Unsupported BASIC={} value", basic)); +} + + } diff --git a/tests/parser/RestartConfigTests.cpp b/tests/parser/RestartConfigTests.cpp index 3bc882220..497290ee2 100644 --- a/tests/parser/RestartConfigTests.cpp +++ b/tests/parser/RestartConfigTests.cpp @@ -202,14 +202,13 @@ RESTART=1 } - BOOST_AUTO_TEST_CASE(RPTRST_AND_RPTSOL_SOLUTION) { const auto input = std::string { R"(RUNSPEC DIMENS 10 10 10 / START - 6 JLY 2020 / + 6 JUN 2020 / GRID DXV @@ -259,27 +258,45 @@ END auto sched = make_schedule(input, false); - for (const std::size_t stepID : { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18 }) { + for (const std::size_t stepID : { 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 13, 15, 16, 18 }) { BOOST_CHECK_MESSAGE(! sched.write_rst_file(stepID), "Must not write restart information for excluded step " << stepID); } - for (const std::size_t stepID : { 0, 11, 14, 17 }) { + for (const std::size_t stepID : { 0, 1, 11, 14, 17 }) { BOOST_CHECK_MESSAGE(sched.write_rst_file(stepID), "Must write restart information for included step " << stepID); } - std::vector first_in_month{true, false, false, false, false, true, false, true, true, true, true, true, true, true, true, true, false, true, false}; - std::vector month_num{ 0,0,0,0,0, 1, 1, 2, 3, 4, 5, 6, 7, 10, 12, 17, 17, 18, 18 }; + std::vector> expected = {{true , true , TimeStampUTC(2020, 6, 6)}, // 0 + {true , false, TimeStampUTC(2020, 7, 7)}, // 1 + {false, false, TimeStampUTC(2020, 7, 10)}, // 2 + {false, false, TimeStampUTC(2020, 7, 20)}, // 3 + {false, false, TimeStampUTC(2020, 7, 30)}, // 4 + {true , false, TimeStampUTC(2020, 8, 5)}, // 5 + {false, false, TimeStampUTC(2020, 8, 20)}, // 6 + {true , false, TimeStampUTC(2020, 9, 5)}, // 7 + {true , false, TimeStampUTC(2020, 10, 1)}, // 8 + {true , false, TimeStampUTC(2020, 11, 1)}, // 9 + {true , false, TimeStampUTC(2020, 12, 1)}, // 10 + {true , true , TimeStampUTC(2021, 1, 5)}, // 11 + {true , false, TimeStampUTC(2021, 2, 1)}, // 12 + {true , false, TimeStampUTC(2021, 5, 17)}, // 13 + {true , false, TimeStampUTC(2021, 7, 6)}, // 14 + {true , false, TimeStampUTC(2021, 12, 1)}, // 15 + {false, false, TimeStampUTC(2021, 12, 31)}, // 16 + {true, true , TimeStampUTC(2022, 1, 21)}, // 17 + {false, false, TimeStampUTC(2022, 1, 31)}}; // 18 + for (std::size_t index = 0; index < sched.size(); index++) { const auto& state = sched[index]; - BOOST_CHECK_EQUAL( state.month_num(), month_num[index] ); - BOOST_CHECK_EQUAL( state.first_in_month(), first_in_month[index] ); + const auto& [first_in_month, first_in_year, ts] = expected[index]; - if (index == 0 || index == 11 || index == 17) - BOOST_CHECK( state.first_in_year()); - else - BOOST_CHECK(!state.first_in_year()); + printf("index: %ld \n", index); + BOOST_CHECK_EQUAL( state.month_num(), ts.month() - 1); + BOOST_CHECK_EQUAL( state.first_in_month(), first_in_month ); + BOOST_CHECK_EQUAL( state.first_in_year(), first_in_year); + BOOST_CHECK( ts == TimeStampUTC( TimeService::to_time_t(state.start_time() ))); } } @@ -416,23 +433,25 @@ PORO 1000*0.25 / SOLUTION RPTRST -- PRES,DEN,PCOW,PCOG,RK,VELOCITY,COMPRESS - 6*0 1 0 1 9*0 1 7*0 1 0 3*1 / + 6*0 1 0 1 9*0 1 7*0 1 0 3*1 / -- Static + SCHEDULE +-- 0 DATES -- 1 10 OKT 2008 / / -RPTSCHED +RPTSCHED -- 1 RESTART=1 / DATES -- 2 20 JAN 2010 / / -RPTRST -- RK,VELOCITY,COMPRESS +RPTRST -- RK,VELOCITY,COMPRESS --2 18*0 0 8*0 / DATES -- 3 20 FEB 2010 / / -RPTSCHED +RPTSCHED -- 3 RESTART=0 / )"; @@ -1256,28 +1275,28 @@ RPTRST BASIC=5 FREQ=2 / DATES - 22 MAY 1981 / - 23 MAY 1981 / - 24 MAY 1981 / - 1 JUN 1981 / - 1 JUL 1981 / -- write - 1 JAN 1982 / -- write - 2 JAN 1982 / - 1 FEB 1982 / - 1 MAR 1982 / -- write - 1 APR 1983 / -- write - 2 JUN 1983 / -- write + 22 MAY 1981 / -- 1 + 23 MAY 1981 / -- 2 + 24 MAY 1981 / -- 3 + 1 JUN 1981 / -- 4 + 1 JUL 1981 / -- 5 Write + 1 JAN 1982 / -- 6 Write + 2 JAN 1982 / -- 7 + 1 FEB 1982 / -- 8 + 1 MAR 1982 / -- 9 Write + 1 APR 1983 / --10 + 2 JUN 1983 / --11 / )"; auto sched = make_schedule(data); /* BASIC=5, restart file is written at the first report step of each month. */ - for( size_t ts : { 1, 2, 3, 4, 7, 8 } ) + for( size_t ts : { 1, 2, 3, 4, 7, 8, 10, 11 } ) BOOST_CHECK( !sched.write_rst_file( ts ) ); - for( size_t ts : { 5, 6, 9, 10, 11 } ) - BOOST_CHECK( sched.write_rst_file( ts ) ); + for( size_t ts : { 5, 6, 9} ) + BOOST_CHECK_MESSAGE( sched.write_rst_file( ts ) , "Restart file expected for step: " << ts); } BOOST_AUTO_TEST_CASE(BASIC_EQ_0) { @@ -1369,7 +1388,7 @@ BASIC=4 FREQ=2 DATES 22 MAY 1981 / / -RPTSCHED // BASIC >2, ignore RPTSCHED RESTART +RPTSCHED -- BASIC >2, ignore RPTSCHED RESTART RESTART=3, FREQ=1 / DATES @@ -1456,3 +1475,77 @@ TSTEP } + +BOOST_AUTO_TEST_CASE(RPTSCHED_INTEGER2) { + + const std::string deckData1 = R"( +RUNSPEC +START -- 0 +19 JUN 2007 / +DIMENS + 10 10 10 / +GRID + +DXV + 10*1 / + +DYV + 10*1 / + +DZV + 10*1 / + +DEPTHZ + 121*1 / + +PORO + 1000*0.25 / +SOLUTION +RPTRST -- PRES,DEN,PCOW,PCOG,RK,VELOCITY,COMPRESS + 1 5*0 1 0 1 9*0 1 7*0 1 0 3*1 / -- Static + +SCHEDULE +-- 0 +DATES -- 1 + 10 OKT 2008 / +/ +RPTSCHED +RESTART=1 +/ +DATES -- 2 + 20 JAN 2010 / +/ +RPTRST -- RK,VELOCITY,COMPRESS + 18*0 0 8*0 / +DATES -- 3 + 20 FEB 2010 / +/ +RPTSCHED +RESTART=0 +/ + +DATES -- 4 +1 MAR 2010 / +/ +)"; + + auto sched = make_schedule(deckData1, false); + + BOOST_CHECK_EQUAL( sched.size(), 5); + BOOST_CHECK( sched.write_rst_file( 0 ) ); + BOOST_CHECK( sched.write_rst_file( 1 ) ); + BOOST_CHECK( sched.write_rst_file( 2 ) ); + BOOST_CHECK( !sched.write_rst_file( 3 ) ); + + + const auto& kw_list1 = filter_keywords(sched.rst_keywords(1)); + const auto expected1 = {"BG","BO","BW","COMPRESS","DEN","KRG","KRO","KRW","PCOG","PCOW","PRES","RK","VELOCITY","VGAS","VOIL","VWAT"}; + BOOST_CHECK_EQUAL_COLLECTIONS( expected1.begin(), expected1.end(), + kw_list1.begin(), kw_list1.end() ); + + const auto& kw_list2 = filter_keywords( sched.rst_keywords(3)); + const auto expected2 = { "COMPRESS", "RK", "VELOCITY" }; + BOOST_CHECK_EQUAL_COLLECTIONS( expected2.begin(), expected2.end(), + kw_list2.begin(), kw_list2.end() ); +} + diff --git a/tests/parser/data/integration_tests/IOConfig/RPT_TEST2.DATA b/tests/parser/data/integration_tests/IOConfig/RPT_TEST2.DATA index f3a908109..043ee154b 100644 --- a/tests/parser/data/integration_tests/IOConfig/RPT_TEST2.DATA +++ b/tests/parser/data/integration_tests/IOConfig/RPT_TEST2.DATA @@ -199,6 +199,9 @@ DATES DATES 21 'AUG' 2001 / / + +SAVE + DATES 24 'AUG' 2001 / / diff --git a/tests/parser/integration/IOConfigIntegrationTest.cpp b/tests/parser/integration/IOConfigIntegrationTest.cpp index 1081e9b18..33bad8654 100644 --- a/tests/parser/integration/IOConfigIntegrationTest.cpp +++ b/tests/parser/integration/IOConfigIntegrationTest.cpp @@ -138,6 +138,7 @@ BOOST_AUTO_TEST_CASE( NorneRestartConfig ) { + BOOST_AUTO_TEST_CASE( RestartConfig2 ) { std::map rptConfig; @@ -145,6 +146,7 @@ BOOST_AUTO_TEST_CASE( RestartConfig2 ) { rptConfig.emplace(8 , boost::gregorian::date(2000,7,1)); rptConfig.emplace(27 , boost::gregorian::date(2001,1,1)); rptConfig.emplace(45 , boost::gregorian::date(2001,7,1)); + rptConfig.emplace(50 , boost::gregorian::date(2001,8,24)); rptConfig.emplace(61 , boost::gregorian::date(2002,1,1)); rptConfig.emplace(79 , boost::gregorian::date(2002,7,1)); rptConfig.emplace(89 , boost::gregorian::date(2003,1,1)); @@ -177,6 +179,55 @@ BOOST_AUTO_TEST_CASE( RestartConfig2 ) { EclipseState state( deck); Schedule schedule(deck, state, python); verifyRestartConfig(schedule, rptConfig); + + auto keywords0 = schedule.rst_keywords(0); + std::map expected0 = {{"BG", 1}, + {"BO", 1}, + {"BW", 1}, + {"KRG", 1}, + {"KRO", 1}, + {"KRW", 1}, + {"VOIL", 1}, + {"VGAS", 1}, + {"VWAT", 1}, + {"DEN", 1}, + {"RVSAT", 1}, + {"RSSAT", 1}, + {"PBPD", 1}, + {"NORST", 1}}; + for (const auto& [kw, num] : expected0) + BOOST_CHECK_EQUAL( keywords0.at(kw), num ); + + auto keywords1 = schedule.rst_keywords(1); + std::map expected1 = {{"BG", 1}, + {"BO", 1}, + {"BW", 1}, + {"KRG", 1}, + {"KRO", 1}, + {"KRW", 1}, + {"VOIL", 1}, + {"VGAS", 1}, + {"VWAT", 1}, + {"DEN", 1}, + {"RVSAT", 1}, + {"RSSAT", 1}, + {"PBPD", 1}, + {"NORST", 1}, + {"FIP", 3}, + {"WELSPECS", 1}, + {"WELLS", 0}, + {"NEWTON", 1}, + {"SUMMARY", 1}, + {"CPU", 1}, + {"CONV", 10}}; + + for (const auto& [kw, num] : expected1) + BOOST_CHECK_EQUAL( keywords1.at(kw), num ); + + BOOST_CHECK_EQUAL(expected1.size(), keywords1.size()); + + auto keywords10 = schedule.rst_keywords(10); + BOOST_CHECK( keywords10 == keywords1 ); } From d14cd52a5e3fffe964769ab11e3761954fcaa5a9 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Thu, 25 Mar 2021 19:33:15 +0100 Subject: [PATCH 2/4] Remove stale functionality RestartConfig / DynamicState --- CMakeLists_files.cmake | 4 - .../EclipseState/IOConfig/RestartConfig.hpp | 390 ---------- .../EclipseState/Schedule/DynamicState.hpp | 162 ---- src/opm/output/eclipse/CreateInteHead.cpp | 1 - .../eclipse/EclipseState/EclipseConfig.cpp | 2 +- .../EclipseState/IOConfig/RestartConfig.cpp | 718 ------------------ tests/parser/DynamicStateTests.cpp | 132 ---- tests/parser/IOConfigTests.cpp | 2 - .../integration/IOConfigIntegrationTest.cpp | 1 - 9 files changed, 1 insertion(+), 1411 deletions(-) delete mode 100644 opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp delete mode 100644 opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp delete mode 100644 src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp delete mode 100644 tests/parser/DynamicStateTests.cpp diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index fa44bcfe3..1fe52e7b1 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -89,7 +89,6 @@ if(ENABLE_ECL_INPUT) src/opm/parser/eclipse/EclipseState/InitConfig/FoamConfig.cpp src/opm/parser/eclipse/EclipseState/InitConfig/InitConfig.cpp src/opm/parser/eclipse/EclipseState/IOConfig/IOConfig.cpp - src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp src/opm/parser/eclipse/EclipseState/Runspec.cpp src/opm/parser/eclipse/EclipseState/TracerConfig.cpp src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionAST.cpp @@ -342,7 +341,6 @@ if(ENABLE_ECL_INPUT) tests/parser/CopyRegTests.cpp tests/parser/DeckValueTests.cpp tests/parser/DeckTests.cpp - tests/parser/DynamicStateTests.cpp tests/parser/EclipseGridTests.cpp tests/parser/EmbeddedPython.cpp tests/parser/EqualRegTests.cpp @@ -776,7 +774,6 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/Schedule/MessageLimits.hpp opm/parser/eclipse/EclipseState/Schedule/Events.hpp opm/parser/eclipse/EclipseState/Schedule/OilVaporizationProperties.hpp - opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp opm/parser/eclipse/EclipseState/Schedule/MSW/icd.hpp opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp opm/parser/eclipse/EclipseState/Schedule/MSW/Segment.hpp @@ -788,7 +785,6 @@ if(ENABLE_ECL_INPUT) opm/parser/eclipse/EclipseState/SimulationConfig/RockConfig.hpp opm/parser/eclipse/EclipseState/SimulationConfig/SimulationConfig.hpp opm/parser/eclipse/EclipseState/Schedule/MSW/Valve.hpp - opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp opm/parser/eclipse/EclipseState/IOConfig/IOConfig.hpp opm/parser/eclipse/EclipseState/checkDeck.hpp opm/parser/eclipse/EclipseState/Runspec.hpp diff --git a/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp b/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp deleted file mode 100644 index 23e7de0b2..000000000 --- a/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.hpp +++ /dev/null @@ -1,390 +0,0 @@ -/* - Copyright 2015 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 . - */ - -#ifndef OPM_RESTART_CONFIG_HPP -#define OPM_RESTART_CONFIG_HPP - -#include -#include -#include -#include -#include - -/* - The RestartConfig class internalizes information of when (at which - report steps) we should save restart files, and which properties - should be included in the restart files. The configuration of this - immensely complex, and this code is unfortunately also way too - complex. - - The most basic question to disentangle is the "When to write restart - files" versus "What data to store write in the restart file". As - expressed in the deck keywords this completely entangled, in this - implementation we have tried to disentangle it: - - Keywords involved - ----------------- - - RPTRST: This is the main keyword for configuring restart output; it - can be used to configure bothe when to write the files and which - properties should be included in the restart files. - - - RPTSCHED: The main purpose of the RPTSCHED keyword is to configure - output from the SCHEDULE section to the PRINT file. However the - mneomnic RESTART=n can be used to turn writing of restart files - on, and also for values > 2 to some configuration of what is - written to the restart file: - - RESTART=1 : As RPTRST,BASIC=1 - RESTART>1 : As RPTRST,BASIC=2 - RESTART>2 : Flow is added to restart file - RESTART>3 : Fluid in place is added to restart file - RESTART=6 : Restart file for every timestep. - - - - RPTSOL: The RPTSOL keyword is very similar to the RPTCHED keyword, - it configures output from the SOLUTION section to the PRINT file, - but just as the RPTSCHED keyword it accepts a RESTART=n mnenonic - which can be used similarly to the BASIC=n mnenonic of the RPTRST - keyword. In particular the writing of an initial restart files - with initial equilibrium solution is controlled by the RPTSOL - keyword. If the restart mneonic is greater than 2 that can be - used to configure FLOWS and FIP keywords in the restart file. - - RESTART=1 : As RPTRST,BASIC=1 - RESTART>1 : As RPTRST,BASIC=2 - RESTART>2 : Flow is added to restart file - RESTART>3 : Fluid in place is added to restart file - - - The basic rule in ECLIPSE is generally that the 'last keyword wins', - but for the RPTRST RPTSHCED combination a BASIC setting with n >= 3 - will override consecutive RESTART=n settings from RPTSCHED. - - - When to write restart files: - ---------------------------- - - When to write the restart file is governed by the BASIC=n setting in - the RPTRST keyword and the RESTART=n settings in the RPTSOL and - RPTSCHED keywords. The most common setting is 'ON' - i.e. BASIC=2 - which means write a restart file for every report step, that can be - turned off again with BASIC=0. For BASIC>2 there are varietes of - every n'th report step, and the first report step in every month and - every year. - - - Old style / new style - --------------------- - - All of the relevant keywords can be specified using a new style - based on string mneomnics and alternatively an old style represented - with a *strictly ordered* list of integers. For instance both of - these keywords request restart files written for every report step; - in addition to the fields required to actually restart the files - should contain the relative permeabilities KRO, KRW, KRG: - - RPTRST - BASIC=2 KRG KRW KRO / - - - RPTRST - 2 9*0 3*1 17*0 - - Integer controls and string mneomnics can not be mixed in the same - keyword, but they can be mixed in the same deck - and that is - actually quite common. - - - What is written to the restart file - ----------------------------------- - - The BASIC=n mneonics request the writing of a restart file which - should contain 'all properties required to restart', in addition you - can configure extra keywords to be added to the restart file. This - is configured by just adding a list as: - - RPTRST - BASIC=2 KRG KRW KRO / - - It is really *not clear* what is the correct persistence semantics - for these keywords, consider for insance the following series of keywords: - - -- Request restart file at every report step, the restart files - -- should contain additional properties KRO, KRG and KRW. - RPTRST - BASIC=2 KRG KRW KRO / - - -- Advance the simulator forward with TSTEP / DATES - TSTEP / DATES / WCONxxx - - -- Turn writing of restart files OFF using integer controls. - RPTRST - 0 / - - -- Advance the simulator forward with TSTEP / DATES - TSTEP / DATES / WCONxxx - - -- Turn writing of restart files ON using integer controls. - RPTRST - 2 / - - When writing of restart files is turned on again with the last - RPTRST keyword, should still the relative permeabilites KRO, KRW and - KRG be added to the restart files? The model we have implemented is: - - - The list of keywords written to the restart file is persisted - independtly of the BASIC=n setting. - - - Using string based mnonics you can *only add* kewyords to be - written to the files. To stop writing a keyword you must use an - integer control with value 0. - - Based on this best guess heuristic the final restart files will - still contain KRO, KRW and KRG. - - - - What is required to restart? - ---------------------------- - - A restart capable files is requested with the 'BASIC' mneomnic, but - exactly which properties the 'BASIC' keyword is expanded to is the - responsability of the simulator; i.e. for a black oil simulation you - will at the very least need the expansion: - - BASIC -> PRESSURE, SWAT, SGAS, RS, RV - - But this class just carries the boolean information: Yes - restart - is requested - expanding as illustrated is the responsability of the - simulator. - - - - - What is not supported? - ---------------------- - - The SAVE keyword is not supported in OPM at all, this implies that - the SAVE and SFREQ mneomics are not supported. -*/ - - - -namespace Opm { - - template< typename > class DynamicState; - - class Deck; - class DeckKeyword; - class RUNSPECSection; - class SCHEDULESection; - class SOLUTIONSection; - class Schedule; - class ParseContext; - class ErrorGuard; - - /*The IOConfig class holds data about input / ouput configurations - - Amongst these configuration settings, a IOConfig object knows if - a restart file should be written for a specific report step - - The write of restart files is governed by several eclipse keywords. - These keywords are all described in the eclipse manual, but some - of them are rather porly described there. - To have equal sets of restart files written from Eclipse and Flow for various - configurations, we have made a qualified guess on the behaviour - for some of the keywords (by running eclipse for different configurations, - and looked at which restart files that have been written). - - - ------ RPTSOL RESTART (solution section) ------ - If RPTSOL RESTART > 1 initial restart file is written. - - - ------ RPTRST (solution section) ------ - Eclipse manual states that the initial restart file is to be written - if RPTSOL RESTART > 1. But - due to that the initial restart file - is written from Eclipse for data where RPTSOL RESTART is not set, - we - have made a guess that when RPTRST is set in SOLUTION (no basic though...), - it means that the initial restart file should be written. - Running of eclipse with different settings have proven this to be a qualified guess. - - - ------ RPTRST BASIC=0 (solution or schedule section) ------ - No restart files are written - - - ------ RPTRST BASIC=1 or RPTRST BASIC=2 (solution or schedule section) ------ - Restart files are written for every timestep, from timestep 1 to number of timesteps. - (Write of inital timestep is governed by a separate setting) - - Notice! Eclipse simulator RPTRST BASIC=1 writes restart files for every - report step, but only keeps the last one written. This functionality is - not supported in Flow; so to compare Eclipse results with Flow results - for every report step, set RPTRST BASIC=2 for the eclipse run - - - ------ RPTRST BASIC=3 FREQ=n (solution or schedule section) ------ - Restart files are created every nth report time. Default frequency is 1 (every report step) - - If a frequency higher than 1 is given: - start_rs = report step the setting was given. - write report step rstep if (rstep >= start_rs) && ((rstep % frequency) == 0). - - - ------ RPTRST BASIC=4 FREQ=n or RPTRST BASIC=5 FREQ=n (solution or schedule section) ------ - For the settings BASIC 4 or BASIC 5, - first report step of every new year(4) or new month(5), - the first report step is compared with report step 0 (start), and then every report step is - compared with the previous one to see if year/month has changed. - - This leaves us with a set of timesteps. - All timesteps in the set that are higher or equal to the timestep the RPTRST keyword was set on is written. - - If in addition FREQUENCY is given (higher than 1), every n'the value of this set are to be written. - - If the setting BASIC=4 or BASIC=5 is set on a timestep that is a member of the set "first timestep of - each year" / "First timestep of each month", then the timestep that is freq-1 timesteps (within the set) from - this start timestep will be written, and then every n'the timestep (within the set) from this one will be written. - - If the setting BASIC=4 or BASIC=5 is set on a timestep that is not a member of the list "first timestep of - each year" / "First timestep of each month", then the list is searched for the closest timestep that are - larger than the timestep that introduced the setting, and then; same as above - the timestep that is freq-1 - timesteps from this one (within the set) will be written, and then every n'the timestep (within the set) from - this one will be written. - - - ------ RPTRST BASIC=6 (solution or schedule section) ------ - Not supported in Flow - - - ------ Default ------ - If no keywords for config of writing restart files have been handled; no restart files are written. - - */ - - //namespace { - - class RestartSchedule { - /* - The content of this struct is logically divided in two; either the - restart behaviour is governed by { timestep , basic , frequency }, or - alternatively by { rptshec_restart_set , rptsched_restart }. - - The former triplet is mainly governed by the RPTRST keyword and the - latter pair by the RPTSCHED keyword. - */ - public: - - RestartSchedule() = default; - explicit RestartSchedule( size_t sched_restart); - RestartSchedule( size_t step, size_t b, size_t freq); - - static RestartSchedule serializeObject(); - - bool writeRestartFile( size_t timestep , const TimeMap& timemap) const; - bool operator!=(const RestartSchedule& rhs) const; - bool operator==( const RestartSchedule& rhs ) const; - - template - void serializeOp(Serializer& serializer) - { - serializer(timestep); - serializer(basic); - serializer(frequency); - serializer(rptsched_restart_set); - serializer(rptsched_restart); - } - - //private: - size_t timestep = 0; - size_t basic = 0; - size_t frequency = 0; - bool rptsched_restart_set = false; - size_t rptsched_restart = 0; - }; - // } - class RestartConfig { - - public: - - RestartConfig() = default; - - template - RestartConfig( const Deck&, const std::pair& restart, const std::optional& output_interval, const ParseContext& parseContext, T&& errors ); - RestartConfig( const Deck&, const std::pair& restart, const std::optional& output_interval, const ParseContext& parseContext, ErrorGuard& errors ); - RestartConfig( const Deck&, const std::pair& restart, const std::optional& output_interval); - - static RestartConfig serializeObject(); - - int getFirstRestartStep() const; - bool getWriteRestartFile(size_t timestep, bool log=true) const; - const std::map< std::string, int >& getRestartKeywords( size_t timestep ) const; - - void handleSolutionSection(const SOLUTIONSection& solutionSection, const ParseContext& parseContext, ErrorGuard& errors); - void setWriteInitialRestartFile(bool writeInitialRestartFile); - - bool operator==(const RestartConfig& data) const; - - template - void serializeOp(Serializer& serializer) - { - m_timemap.serializeOp(serializer); - serializer(m_first_restart_step); - serializer(m_write_initial_RST_file); - restart_schedule.serializeOp(serializer); - restart_keywords.serializeOp(serializer); - serializer(save_keywords); - } - - private: - /// This method will internalize variables with information of - /// the first report step with restart and rft output - /// respectively. This information is important because right - /// at the first output step we must reset the files to size - /// zero, for subsequent output steps we should append. - void initFirstOutput( ); - RestartSchedule getNode( size_t timestep ) const; - void overrideRestartWriteInterval(size_t interval); - - bool getWriteRestartFileFrequency(size_t timestep, - size_t start_timestep, - size_t frequency, - bool years = false, - bool months = false) const; - void handleRPTSOL( const DeckKeyword& keyword); - void handleScheduleSection( const SCHEDULESection& schedule, const ParseContext& parseContext, ErrorGuard& errors); - void update( size_t step, const RestartSchedule& rs); - static RestartSchedule rptsched( const DeckKeyword& ); - - TimeMap m_timemap; - int m_first_restart_step = 1; - bool m_write_initial_RST_file = false; - - DynamicState< RestartSchedule > restart_schedule; - DynamicState< std::map< std::string, int > > restart_keywords; - std::vector< bool > save_keywords; - }; -} //namespace Opm - - - -#endif diff --git a/opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp b/opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp deleted file mode 100644 index c56dfadb7..000000000 --- a/opm/parser/eclipse/EclipseState/Schedule/DynamicState.hpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - 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 . -*/ - - -#ifndef DYNAMICSTATE_HPP_ -#define DYNAMICSTATE_HPP_ - -#include -#include -#include -#include -#include - -#include - - -namespace Opm { - - /** - The DynamicState class is designed to hold information about - properties with the following semantics: - - 1. The property can be updated repeatedly at different - timesteps; observe that the class does not support - operator[] - only updates with weakly increasing timesteps - are supported. - - 2. At any point in the time the previous last set value - applies. - - The class is very much tailored to support the Schedule file of - Eclipse where a control applied at time T will apply - indefinitely, or until explicitly set to a different value. - - The update() method returns true if the updated value is - different from the current value, this implies that the - class must support operator!= - */ - - - -template< class T > -class DynamicState { - public: - typedef typename std::vector< T >::iterator iterator; - - DynamicState() = default; - - DynamicState( const TimeMap& timeMap, T initial ) : - m_data( timeMap.size(), initial ), - initial_range( timeMap.size() ) - {} - - DynamicState(const std::vector& data, - size_t init_range) : - m_data(data), initial_range(init_range) - {} - - void globalReset( T value ) { - this->m_data.assign( this->m_data.size(), value ); - } - - const T& back() const { - return m_data.back(); - } - - const T& get(size_t index) const { - return this->m_data.at( index ); - } - - /** - If the current value has been changed the method will - return true, otherwise it will return false. - */ - bool update( size_t index, T value ) { - if( this->initial_range == this->m_data.size() ) - this->initial_range = index; - - const bool change = (value != this->m_data.at( index )); - - if( !change ) return false; - - std::fill( this->m_data.begin() + index, - this->m_data.end(), - value ); - - return true; - } - - - - - - bool operator==(const DynamicState& data) const { - return m_data == data.m_data && - initial_range == data.initial_range; - } - - // complexType=true if contained type has a serializeOp - template - void serializeOp(Serializer& serializer) - { - std::vector unique; - auto indices = split(unique); - serializer.template vector(unique); - serializer(indices); - if (!serializer.isSerializing()) - reconstruct(unique, indices); - } - - private: - std::vector< T > m_data; - size_t initial_range; - - std::vector split(std::vector& unique) const { - std::vector idxVec; - idxVec.reserve(m_data.size() + 1); - for (const auto& w : m_data) { - auto candidate = std::find(unique.begin(), unique.end(), w); - size_t idx = candidate - unique.begin(); - if (candidate == unique.end()) { - unique.push_back(w); - idx = unique.size() - 1; - } - idxVec.push_back(idx); - } - idxVec.push_back(initial_range); - - return idxVec; - } - - void reconstruct(const std::vector& unique, - const std::vector& idxVec) { - m_data.clear(); - m_data.reserve(idxVec.size() - 1); - for (size_t i = 0; i < idxVec.size() - 1; ++i) - m_data.push_back(unique[idxVec[i]]); - - initial_range = idxVec.back(); - } -}; - -} - -#endif - diff --git a/src/opm/output/eclipse/CreateInteHead.cpp b/src/opm/output/eclipse/CreateInteHead.cpp index eea0a94d7..e21f6a7cf 100755 --- a/src/opm/output/eclipse/CreateInteHead.cpp +++ b/src/opm/output/eclipse/CreateInteHead.cpp @@ -23,7 +23,6 @@ #include #include -#include #include #include #include diff --git a/src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp b/src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp index 959bf9dd0..dbf5f6602 100644 --- a/src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp +++ b/src/opm/parser/eclipse/EclipseState/EclipseConfig.cpp @@ -24,7 +24,7 @@ #include #include #include -#include + namespace Opm { diff --git a/src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp b/src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp deleted file mode 100644 index 04d126877..000000000 --- a/src/opm/parser/eclipse/EclipseState/IOConfig/RestartConfig.cpp +++ /dev/null @@ -1,718 +0,0 @@ -/* - Copyright 2015 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 -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - - - - -namespace Opm { - -namespace { - -inline bool is_int( const std::string& x ) { - auto is_digit = []( char c ) { return std::isdigit( c ); }; - - return !x.empty() - && ( x.front() == '-' || is_digit( x.front() ) ) - && std::all_of( x.begin() + 1, x.end(), is_digit ); -} - -constexpr const char* RSTIntegerKeywords[] = { "BASIC", // 1 - "FLOWS", // 2 - "FIP", // 3 - "POT", // 4 - "PBPD", // 5 - "FREQ", // 6 - "PRES", // 7 - "VISC", // 8 - "DEN", // 9 - "DRAIN", // 10 - "KRO", // 11 - "KRW", // 12 - "KRG", // 13 - "PORO", // 14 - "NOGRAD", // 15 - "NORST", // 16 NORST - not supported - "SAVE", // 17 - "SFREQ", // 18 SFREQ=?? - not supported - "ALLPROPS", // 19 - "ROCKC", // 20 - "SGTRAP", // 21 - "", // 22 - Blank - ignored. - "RSSAT", // 23 - "RVSAT", // 24 - "GIMULT", // 25 - "SURFBLK", // 26 - "", // 27 - PCOW, PCOG, special cased - "STREAM", // 28 STREAM=?? - not supported - "RK", // 29 - "VELOCITY", // 30 - "COMPRESS" }; // 31 - -constexpr const char* SCHEDIntegerKeywords[] = { "PRES", // 1 - "SOIL", // 2 - "SWAT", // 3 - "SGAS", // 4 - "RS", // 5 - "RV", // 6 - "RESTART", // 7 - "FIP", // 8 - "WELLS", // 9 - "VFPPROD", // 10 - "SUMMARY", // 11 - "CPU", // 12 - "AQUCT", // 13 - "WELSPECS",// 14 - "NEWTON", // 15 - "POILD", // 16 - "PWAT", // 17 - "PWATD", // 18 - "PGAS", // 19 - "PGASD", // 20 - "FIPVE", // 21 - "WOC", // 22 - "GOC", // 23 - "WOCDIFF", // 24 - "GOCDIFF", // 25 - "WOCGOC", // 26 - "ODGAS", // 27 - "ODWAT", // 28 - "GDOWAT", // 29 - "WDOGAS", // 30 - "OILAPI", // 31 - "FIPITR", // 32 - "TBLK", // 33 - "PBLK", // 34 - "SALT", // 35 - "PLYADS", // 36 - "RK", // 37 - "FIPSALT", // 38 - "TUNING", // 39 - "GI", // 40 - "ROCKC", // 41 - "SPENWAT", // 42 - "FIPSOL", // 43 - "SURFBLK", // 44 - "SURFADS", // 45 - "FIPSURF", // 46 - "TRADS", // 47 - "VOIL", // 48 - "VWAT", // 49 - "VGAS", // 50 - "DENO", // 51 - "DENW", // 52 - "DENG", // 53 - "GASCONC", // 54 - "PB", // 55 - "PD", // 56 - "KRW", // 57 - "KRO", // 58 - "KRG", // 59 - "MULT", // 60 - "UNKNOWN", // 61 61 and 62 are not listed in the manual - "UNKNOWN", // 62 - "FOAM", // 63 - "FIPFOAM", // 64 - "TEMP", // 65 - "FIPTEMP", // 66 - "POTC", // 67 - "FOAMADS", // 68 - "FOAMDCY", // 69 - "FOAMMOB", // 70 - "RECOV", // 71 - "FLOOIL", // 72 - "FLOWAT", // 73 - "FLOGAS", // 74 - "SGTRAP", // 75 - "FIPRESV", // 76 - "FLOSOL", // 77 - "KRN", // 78 - "GRAD", // 79 - }; - -bool is_RPTRST_mnemonic( const std::string& kw ) { - /* all eclipse 100 keywords we want to not simply ignore. The list is - * sorted, so we can use binary_search for log(n) lookup. It is important - * that the list is sorted, but these are all the keywords listed in the - * manual and unlikely to change at all - */ - static constexpr const char* valid[] = { - "ACIP", "ACIS", "ALLPROPS", "BASIC", "BG", "BO", - "BW", "CELLINDX", "COMPRESS", "CONV", "DEN", "DRAIN", - "DRAINAGE", "DYNREG", "FIP", "FLORES", "FLOWS", "FREQ", - "GIMULT", "HYDH", "HYDHFW", "KRG", "KRO", "KRW", - "NOGRAD", "NORST", "NPMREB", "PBPD", "PCOG", "PCOW", - "PERMREDN", "POIS", "PORO", "PORV", "POT", "PRES", - "RFIP", "RK", "ROCKC", "RPORV", "RSSAT", "RVSAT", - "SAVE", "SDENO", "SFIP", "SFREQ", "SGTRAP", "SIGM_MOD", - "STREAM", "SURFBLK", "TRAS", "VELGAS", "VELOCITY", "VELOIL", - "VELWAT", "VISC", - }; - - return std::binary_search( std::begin( valid ), std::end( valid ), kw ); -} - -bool is_RPTSCHED_mnemonic( const std::string& kw ) { - static constexpr const char* valid[] = { - "ALKALINE", "ANIONS", "AQUCT", "AQUFET", "AQUFETP", "BFORG", - "CATIONS", "CPU", "DENG", "DENO", "DENW", "ESALPLY", - "ESALSUR", "FFORG", "FIP", "FIPFOAM", "FIPHEAT", "FIPRESV", - "FIPSALT", "FIPSOL", "FIPSURF", "FIPTEMP", "FIPTR", "FIPVE", - "FLOGAS", "FLOOIL", "FLOSOL", "FLOWAT", "FMISC", "FOAM", - "FOAMADS", "FOAMCNM", "FOAMDCY", "FOAMMOB", "GASCONC", "GASSATC", - "GDOWAT", "GI", "GOC", "GOCDIFF", "GRAD", "KRG", - "KRN", "KRO", "KRW", "MULT", "NEWTON", "NOTHING", - "NPMREB", "ODGAS", "ODWAT", "OILAPI", "PB", "PBLK", - "PBU", "PD", "PDEW", "PGAS", "PGASD", "PLYADS", - "POIL", "POILD", "POLYMER", "POTC", "POTG", "POTO", - "POTW", "PRES", "PRESSURE", "PWAT", "PWATD", "RECOV", - "RESTART", "ROCKC", "RS", "RSSAT", "RV", "RVSAT", - "SALT", "SGAS", "SGTRAP", "SIGM_MOD", "SOIL", "SSOL", - "SUMMARY", "SURFADS", "SURFBLK", "SWAT", "TBLK", "TEMP", - "TRACER", "TRADS", "TRDCY", "TUNING", "VFPPROD", "VGAS", - "VOIL", "VWAT", "WDOGAS", "WELLS", "WELSPECL", "WELSPECS", - "WOC", "WOCDIFF", "WOCGOC", - }; - - return std::binary_search( std::begin( valid ), std::end( valid ), kw ); -} - -} - - RestartSchedule::RestartSchedule( size_t sched_restart) : - rptsched_restart_set( true ), - rptsched_restart( sched_restart ) - { - } - - RestartSchedule::RestartSchedule( size_t step, size_t b, size_t freq) : - timestep( step ), - basic( b ), - frequency( basic > 2 ? std::max( freq, size_t{ 1 } ) : freq ) - { - /* - * if basic > 2 and freq is default (zero) we're looking at an error - * (every Nth step where N = 0). Instead of throwing we default this to - * 1 so that basic > 2 and freq unset essentially is - * write-every-timestep. It could've just as easily been an exception, - * but to be more robust handling poorly written decks we instead set a - * reasonable default and carry on. - */ - } - - RestartSchedule RestartSchedule::serializeObject() - { - RestartSchedule result(1, 2, 3); - result.rptsched_restart_set = true; - result.rptsched_restart = 4; - - return result; - } - - bool RestartSchedule::operator!=(const RestartSchedule & rhs) const { - return !( *this == rhs ); - } - - bool RestartSchedule::operator==( const RestartSchedule& rhs ) const { - if( this->rptsched_restart_set ) { - return rhs.rptsched_restart_set - && this->rptsched_restart == rhs.rptsched_restart; - } - - return this->timestep == rhs.timestep && - this->basic == rhs.basic && - this->frequency == rhs.frequency; - } - -inline std::map< std::string, int > -RPTRST_integer( const std::vector< int >& ints ) { - const size_t PCO_index = 26; - const size_t BASIC_index = 0; - - std::map< std::string, int > mnemonics; - const size_t size = std::min( ints.size(), sizeof( RSTIntegerKeywords ) ); - - /* fun with special cases. Eclipse seems to ignore the BASIC=0, - * interpreting it as sort-of "don't modify". Handle this by *not* - * adding/updating the integer list sourced BASIC mnemonic, should it be - * zero. I'm not sure if this applies to other mnemonics, but the eclipse - * manual indicates that any zero here should disable the output. - * - * See https://github.com/OPM/opm-parser/issues/886 for reference - */ - if( size > 0 && ints[ BASIC_index ] != 0 ) - mnemonics[ RSTIntegerKeywords[ BASIC_index ] ] = ints[ BASIC_index ]; - - for( size_t i = 1; i < std::min( size, PCO_index ); ++i ) - mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ]; - - for( size_t i = PCO_index + 1; i < size; ++i ) - mnemonics[ RSTIntegerKeywords[ i ] ] = ints[ i ]; - - /* item 27 (index 26) sets both PCOW and PCOG, so we special case it */ - if( ints.size() >= PCO_index ) { - mnemonics[ "PCOW" ] = ints[ PCO_index ]; - mnemonics[ "PCOG" ] = ints[ PCO_index ]; - } - - return mnemonics; -} - -inline std::map< std::string, int > -RPTSCHED_integer( const std::vector< int >& ints ) { - const size_t size = std::min( ints.size(), sizeof( SCHEDIntegerKeywords ) ); - - std::map< std::string, int > mnemonics; - for( size_t i = 0; i < size; ++i ) - mnemonics[ SCHEDIntegerKeywords[ i ] ] = ints[ i ]; - - return mnemonics; -} - -template< typename F, typename G > -inline std::map< std::string, int > RPT( const DeckKeyword& keyword, - const ParseContext& parseContext, - ErrorGuard& errors, - F is_mnemonic, - G integer_mnemonic ) { - - std::vector items; - const auto& deck_items = keyword.getStringData(); - const auto ints = std::any_of( deck_items.begin(), deck_items.end(), is_int ); - const auto strs = !std::all_of( deck_items.begin(), deck_items.end(), is_int ); - - /* if any of the values are pure integers we assume this is meant to be the - * slash-terminated list of integers way of configuring. If integers and - * non-integers are mixed, this is an error; however if the error mode - * RPT_MIXED_STYLE is permissive we try some desperate heuristics to - * interpret this as list of mnemonics. See the the documentation of the - * RPT_MIXED_STYLE error handler for more details. - */ - auto stoi = []( const std::string& str ) { return std::stoi( str ); }; - if( !strs ) - return integer_mnemonic( fun::map( stoi, deck_items ) ); - - - if (ints && strs) { - const auto& location = keyword.location(); - std::string msg = "Error in keyword {keyword}, mixing mnemonics and integers is not allowed\n" - "In {file} line {line}."; - parseContext.handleError(ParseContext::RPT_MIXED_STYLE, msg, location, errors); - - std::vector stack; - for (size_t index=0; index < deck_items.size(); index++) { - if (is_int(deck_items[index])) { - - if (stack.size() < 2) - throw OpmInputError("Problem processing {keyword}\nIn {file} line {line}.", location); - - if (stack.back() == "=") { - stack.pop_back(); - std::string mnemonic = stack.back(); - stack.pop_back(); - - items.insert(items.begin(), stack.begin(), stack.end()); - stack.clear(); - items.push_back( mnemonic + "=" + deck_items[index]); - } else - throw OpmInputError("Problem processing {keyword}\nIn {file} line {line}.", location); - - } else - stack.push_back(deck_items[index]); - } - items.insert(items.begin(), stack.begin(), stack.end()); - } else - items = deck_items; - - std::map< std::string, int > mnemonics; - for( const auto& mnemonic : items ) { - const auto sep_pos = mnemonic.find_first_of( "= " ); - - std::string base = mnemonic.substr( 0, sep_pos ); - if( !is_mnemonic( base ) ) { - std::string msg_fmt = fmt::format("Error in keyword {{keyword}}, unrecognized mnemonic {}\nIn {{file}} line {{line}}.", base); - parseContext.handleError(ParseContext::RPT_UNKNOWN_MNEMONIC, msg_fmt, keyword.location(), errors); - continue; - } - - int val = 1; - if (sep_pos != std::string::npos) { - const auto value_pos = mnemonic.find_first_not_of("= ", sep_pos); - if (value_pos != std::string::npos) - val = std::stoi(mnemonic.substr(value_pos)); - } - - mnemonics.emplace( base, val ); - } - - return mnemonics; -} - -inline void expand_RPTRST_mnemonics(std::map< std::string, int >& mnemonics) { - const auto allprops = mnemonics.find( "ALLPROPS"); - if (allprops != mnemonics.end()) { - const auto value = allprops->second; - mnemonics.erase( "ALLPROPS" ); - - for (const auto& kw : {"BG","BO","BW","KRG","KRO","KRW","VOIL","VGAS","VWAT","DEN"}) - mnemonics[kw] = value; - - } -} - - -inline std::pair< std::map< std::string, int >, RestartSchedule > -RPTRST( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors, RestartSchedule prev, size_t step ) { - auto mnemonics = RPT( keyword, parseContext, errors, is_RPTRST_mnemonic, RPTRST_integer ); - - const bool has_freq = mnemonics.find( "FREQ" ) != mnemonics.end(); - const bool has_basic = mnemonics.find( "BASIC" ) != mnemonics.end(); - - expand_RPTRST_mnemonics( mnemonics ); - - if( !has_freq && !has_basic ) return { std::move( mnemonics ), {} }; - - const auto basic = has_basic ? mnemonics.at( "BASIC" ) : prev.basic; - const auto freq = has_freq ? mnemonics.at( "FREQ" ) : prev.frequency; - - return { std::move( mnemonics ), { step, basic, freq } }; -} - -inline std::pair< std::map< std::string, int >, RestartSchedule > -RPTSCHED( const DeckKeyword& keyword, const ParseContext& parseContext, ErrorGuard& errors ) { - auto mnemonics = RPT( keyword, parseContext, errors, is_RPTSCHED_mnemonic, RPTSCHED_integer ); - - if( mnemonics.count( "NOTHING" ) ) - return { std::move( mnemonics ), { RestartSchedule(0) } }; - - if( mnemonics.count( "RESTART" ) ) - return { std::move( mnemonics ), RestartSchedule( size_t( mnemonics.at( "RESTART" )) ) }; - - return { std::move( mnemonics ), {} }; -} - - - -void RestartConfig::handleScheduleSection(const SCHEDULESection& schedule, const ParseContext& parseContext, ErrorGuard& errors) { - size_t current_step = 1; - RestartSchedule unset; - - auto ignore_RPTSCHED_RESTART = []( decltype( restart_schedule )& x ) { - return x.back().basic > 2; - }; - - for( const auto& keyword : schedule ) { - const auto& name = keyword.name(); - - if( name == "DATES" ) { - current_step += keyword.size(); - continue; - } - - - if( name == "TSTEP" ) { - current_step += keyword.getRecord( 0 ).getItem( 0 ).data_size(); - continue; - } - - if( this->m_timemap.size() <= current_step ) continue; - - if (name == "SAVE") { - this->save_keywords.at(current_step) = true; - } else { - this->save_keywords.at(current_step) = false; - } - - if( !( name == "RPTRST" || name == "RPTSCHED" ) ) continue; - - const bool is_RPTRST = name == "RPTRST"; - const auto& prev_sched = this->restart_schedule.back(); - - auto config = is_RPTRST ? RPTRST( keyword, parseContext, errors, prev_sched, current_step ) - : RPTSCHED( keyword , parseContext, errors); - - /* add the missing entries from the previous step */ - { - auto& mnemonics = config.first; - const auto& prev_mnemonics = this->restart_keywords.back(); - mnemonics.insert( prev_mnemonics.begin(), prev_mnemonics.end() ); - - if( mnemonics.find( "NOTHING" ) != mnemonics.end() ) - mnemonics.clear(); - - this->restart_keywords.update( current_step , mnemonics ); - } - const bool ignore_RESTART = - !is_RPTRST && ignore_RPTSCHED_RESTART( this->restart_schedule ); - - const auto& rs = config.second; - if( rs == unset || ignore_RESTART ) continue; - - if( 6 == rs.rptsched_restart || 6 == rs.basic ) - throw std::runtime_error( - "OPM does not support the RESTART=6 setting " - "(write restart file every timestep)" - ); - - this->restart_schedule.update( current_step, rs ); - } -} - - bool RestartSchedule::writeRestartFile( size_t input_timestep , const TimeMap& timemap) const { - if (this->rptsched_restart_set && (this->rptsched_restart > 0)) - return true; - - switch (this->basic) { - //Do not write restart files - case 0: return false; - - //Write restart file every report time - case 1: return true; - - //Write restart file every report time - case 2: return true; - - //Every n'th report time - case 3: return ((input_timestep % this->frequency) == 0) ? true : false; - - //First reportstep of every year, or if n > 1, n'th years - case 4: return timemap.isTimestepInFirstOfMonthsYearsSequence(input_timestep, true , this->timestep, this->frequency); - - //First reportstep of every month, or if n > 1, n'th months - case 5: return timemap.isTimestepInFirstOfMonthsYearsSequence(input_timestep, false , this->timestep, this->frequency); - - default: return false; - } - } - - template - RestartConfig::RestartConfig( const Deck& deck, const std::pair& restart, const std::optional& output_interval, const ParseContext& parseContext, T&& errors ) : - RestartConfig( deck, restart, output_interval, parseContext, errors) - {} - - RestartConfig::RestartConfig( const Deck& deck, const std::pair& restart, const std::optional& output_interval) : - RestartConfig( deck, restart, output_interval, ParseContext(), ErrorGuard()) - {} - - - RestartConfig::RestartConfig( const Deck& deck, const std::pair& restart, const std::optional& output_interval, const ParseContext& parseContext, ErrorGuard& errors ) : - m_timemap( TimeMap(deck, restart) ), - m_first_restart_step( -1 ), - restart_schedule( m_timemap, {0,0,1}), - restart_keywords( m_timemap, {} ), - save_keywords( m_timemap.size(), false ) - { - handleSolutionSection( SOLUTIONSection(deck), parseContext, errors ); - handleScheduleSection( SCHEDULESection(deck), parseContext, errors ); - initFirstOutput( ); - - if (output_interval.has_value()) - this->overrideRestartWriteInterval(output_interval.value()); - } - - RestartConfig RestartConfig::serializeObject() - { - RestartConfig result; - result.m_timemap = TimeMap::serializeObject(); - result.m_first_restart_step = 2; - result.m_write_initial_RST_file = true; - result.restart_schedule = {{RestartSchedule::serializeObject()}, 2}; - result.restart_keywords = {{{{"test",3}}}, 3}; - result.save_keywords = {false, true}; - - return result; - } - - RestartSchedule RestartConfig::getNode( size_t timestep ) const{ - return restart_schedule.get(timestep); - } - - - bool RestartConfig::getWriteRestartFile(size_t timestep, bool log) const { - if (0 == timestep) - return m_write_initial_RST_file; - - if (save_keywords[timestep]) { - if ( log ) { - std::string logstring = "Fast restart using SAVE is not supported. Standard restart file is written instead"; - Opm::OpmLog::warning("Unhandled output keyword", logstring); - } - return true; - } - - { - RestartSchedule ts_restart_config = getNode( timestep ); - return ts_restart_config.writeRestartFile( timestep , m_timemap ); - } - } - - - const std::map< std::string, int >& RestartConfig::getRestartKeywords( size_t timestep ) const { - return restart_keywords.get( timestep ); - } - - /* - Will initialize the internal variable holding the first report - step when restart output is queried. - - The reason we are interested in this report step is that when we - reach this step the output files should be opened with mode 'w' - - whereas for subsequent steps they should be opened with mode - 'a'. - */ - - void RestartConfig::initFirstOutput( ) { - size_t report_step = 0; - while (true) { - if (getWriteRestartFile(report_step)) { - m_first_restart_step = report_step; - break; - } - report_step++; - if (report_step == m_timemap.size()) - break; - } - } - - - void RestartConfig::handleSolutionSection(const SOLUTIONSection& solutionSection, const ParseContext& parseContext, ErrorGuard& errors) { - using RST = ParserKeywords::RPTRST; - if (solutionSection.hasKeyword()) { - std::size_t step = 1; - const auto& rptrstkeyword = solutionSection.getKeyword(); - - const auto rptrst = RPTRST( rptrstkeyword, parseContext, errors, {}, step ); - this->restart_keywords.update( step, rptrst.first ); - this->restart_schedule.update( step, rptrst.second ); - setWriteInitialRestartFile(true); // Guessing on eclipse rules for write of initial RESTART file (at time 0): - // Write of initial restart file is (due to the eclipse reference manual) - // governed by RPTSOL RESTART in solution section, - // if RPTSOL RESTART > 1 initial restart file is written. - // but - due to initial restart file written from Eclipse - // for data where RPTSOL RESTART not set - guessing that - // when RPTRST is set in SOLUTION (no basic though...) -> write inital restart. - } //RPTRST - - - if (solutionSection.hasKeyword("RPTSOL") && (m_timemap.size() > 0)) { - handleRPTSOL(solutionSection.getKeyword("RPTSOL")); - } //RPTSOL - } - - - void RestartConfig::overrideRestartWriteInterval(size_t interval) { - size_t step = 0; - /* write restart files if the interval is non-zero. The restart - * mnemonic (setting) that governs restart-on-interval is BASIC=3 - */ - size_t basic = interval > 0 ? 3 : 0; - - RestartSchedule rs( step, basic, interval ); - restart_schedule.globalReset( rs ); - - setWriteInitialRestartFile( interval > 0 ); - } - - - void RestartConfig::setWriteInitialRestartFile(bool writeInitialRestartFile) { - m_write_initial_RST_file = writeInitialRestartFile; - } - - - void RestartConfig::handleRPTSOL( const DeckKeyword& keyword) { - const auto& record = keyword.getRecord(0); - - size_t restart = 0; - size_t found_mnemonic_RESTART = 0; - bool handle_RPTSOL_RESTART = false; - - const auto& item = record.getItem(0); - - for (size_t index = 0; index < item.data_size(); ++index) { - const std::string& mnemonic = item.get< std::string >(index); - - found_mnemonic_RESTART = mnemonic.find("RESTART="); - if (found_mnemonic_RESTART != std::string::npos) { - std::string restart_no = mnemonic.substr(found_mnemonic_RESTART+8, mnemonic.size()); - restart = std::strtoul(restart_no.c_str(), nullptr, 10); - handle_RPTSOL_RESTART = true; - } - } - - - /* If no RESTART mnemonic is found, either it is not present or we might - have an old data set containing integer controls instead of mnemonics. - Restart integer switch is integer control nr 7 */ - - if (found_mnemonic_RESTART == std::string::npos) { - if (item.data_size() >= 7) { - const std::string& integer_control = item.get< std::string >(6); - restart = std::strtoul(integer_control.c_str(), nullptr, 10); - if (restart != ULONG_MAX) - handle_RPTSOL_RESTART = true; - } - } - - if (handle_RPTSOL_RESTART) { - if (restart > 1) { - setWriteInitialRestartFile(true); - } else { - setWriteInitialRestartFile(false); - } - } - } - - - int RestartConfig::getFirstRestartStep() const { - return m_first_restart_step; - } - - - bool RestartConfig::operator==(const RestartConfig& data) const { - return this->m_timemap == data.m_timemap && - this->m_first_restart_step == data.m_first_restart_step && - this->m_write_initial_RST_file == data.m_write_initial_RST_file && - this->restart_schedule == data.restart_schedule && - this->restart_keywords == data.restart_keywords && - this->save_keywords == data.save_keywords; - } -} diff --git a/tests/parser/DynamicStateTests.cpp b/tests/parser/DynamicStateTests.cpp deleted file mode 100644 index 54f8db9f0..000000000 --- a/tests/parser/DynamicStateTests.cpp +++ /dev/null @@ -1,132 +0,0 @@ -/* - 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 . - */ - -#include -#include -#include - - -#define BOOST_TEST_MODULE DynamicStateTests -#include -#include - -#include -#include - - -Opm::TimeMap make_timemap(int num) { - std::vector tp; - for (int i = 0; i < num; i++) - tp.push_back( Opm::asTimeT(Opm::TimeStampUTC(2010,1,i+1))); - - Opm::TimeMap timeMap{ tp }; - return timeMap; -} - - - -BOOST_AUTO_TEST_CASE(CreateDynamicTest) { - const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1); - Opm::TimeMap timeMap({ startDate }); - Opm::DynamicState state(timeMap , 9.99); -} - - -BOOST_AUTO_TEST_CASE(DynamicStateGetOutOfRangeThrows) { - const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1); - Opm::TimeMap timeMap({ startDate }); - Opm::DynamicState state(timeMap , 9.99); - BOOST_CHECK_THROW( state.get(1) , std::out_of_range ); -} - - -BOOST_AUTO_TEST_CASE(DynamicStateGetDefault) { - const std::time_t startDate = Opm::TimeMap::mkdate(2010, 1, 1); - Opm::TimeMap timeMap( { startDate } ); - Opm::DynamicState state(timeMap , 137); - BOOST_CHECK_EQUAL( 137 , state.get(0)); - BOOST_CHECK_EQUAL( 137 , state.back() ); -} - - -BOOST_AUTO_TEST_CASE(DynamicStateSetOutOfRangeThrows) { - Opm::TimeMap timeMap = make_timemap(3); - Opm::DynamicState state(timeMap , 137); - - BOOST_CHECK_THROW( state.update(3 , 100) , std::out_of_range ); -} - - -BOOST_AUTO_TEST_CASE(DynamicStateSetOK) { - Opm::TimeMap timeMap = make_timemap(11); - Opm::DynamicState state(timeMap , 137); - - state.update(2 , 23 ); - BOOST_CHECK_EQUAL( 137 , state.get(0)); - BOOST_CHECK_EQUAL( 137 , state.get(1)); - BOOST_CHECK_EQUAL( 23 , state.get(2)); - BOOST_CHECK_EQUAL( 23 , state.get(5)); - - state.update(2 , 17); - BOOST_CHECK_EQUAL( 137 , state.get(0)); - BOOST_CHECK_EQUAL( 137 , state.get(1)); - BOOST_CHECK_EQUAL( 17 , state.get(2)); - BOOST_CHECK_EQUAL( 17 , state.get(5)); - - state.update(6 , 60); - BOOST_CHECK_EQUAL( 17 , state.get(2)); - BOOST_CHECK_EQUAL( 17 , state.get(5)); - BOOST_CHECK_EQUAL( 60 , state.get(6)); - BOOST_CHECK_EQUAL( 60 , state.get(8)); - BOOST_CHECK_EQUAL( 60 , state.get(9)); - BOOST_CHECK_EQUAL( 60 , state.back()); -} - - -BOOST_AUTO_TEST_CASE( ResetGlobal ) { - Opm::TimeMap timeMap = make_timemap(11); - Opm::DynamicState state(timeMap , 137); - - state.update(5 , 100); - BOOST_CHECK_EQUAL( state.get(0) , 137 ); - BOOST_CHECK_EQUAL( state.get(4) , 137 ); - BOOST_CHECK_EQUAL( state.get(5) , 100 ); - BOOST_CHECK_EQUAL( state.get(9) , 100 ); - - - state.globalReset( 88 ); - BOOST_CHECK_EQUAL( state.get(0) , 88 ); - BOOST_CHECK_EQUAL( state.get(4) , 88 ); - BOOST_CHECK_EQUAL( state.get(5) , 88 ); - BOOST_CHECK_EQUAL( state.get(9) , 88 ); -} - - -BOOST_AUTO_TEST_CASE( CheckReturn ) { - Opm::TimeMap timeMap = make_timemap(11); - Opm::DynamicState state(timeMap , 137); - - BOOST_CHECK_EQUAL( false , state.update( 0 , 137 )); - BOOST_CHECK_EQUAL( false , state.update( 3 , 137 )); - BOOST_CHECK_EQUAL( true , state.update( 5 , 200 )); -} - - - - diff --git a/tests/parser/IOConfigTests.cpp b/tests/parser/IOConfigTests.cpp index 38194f381..cc2e3a605 100644 --- a/tests/parser/IOConfigTests.cpp +++ b/tests/parser/IOConfigTests.cpp @@ -28,7 +28,6 @@ #include #include #include -#include using namespace Opm; @@ -196,7 +195,6 @@ DATES auto deck = Parser().parseString( data); IOConfig ioConfig( deck ); - RestartConfig rstConfig( deck, std::make_pair(std::time_t{0}, std::size_t{0}), {}); /*If no GRIDFILE nor NOGGF keywords are specified, default output an EGRID file*/ BOOST_CHECK( ioConfig.getWriteEGRIDFile() ); diff --git a/tests/parser/integration/IOConfigIntegrationTest.cpp b/tests/parser/integration/IOConfigIntegrationTest.cpp index 33bad8654..f620d2708 100644 --- a/tests/parser/integration/IOConfigIntegrationTest.cpp +++ b/tests/parser/integration/IOConfigIntegrationTest.cpp @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include From 0eb1b95fc3a7deccd76f0637189857f6c7090230 Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 26 Mar 2021 07:44:42 +0100 Subject: [PATCH 3/4] Handle commandline argument --output-interval --- opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp | 6 +++++- src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 55cee567d..19b937f4c 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -72,6 +72,7 @@ namespace Opm UnitSystem m_unit_system; Runspec m_runspec; RSTConfig rst_config; + std::optional output_interval; ScheduleStatic() = default; @@ -82,6 +83,7 @@ namespace Opm ScheduleStatic(std::shared_ptr python_handle, const Deck& deck, const Runspec& runspec, + const std::optional& output_interval_, const ParseContext& parseContext, ErrorGuard& errors): m_python_handle(python_handle), @@ -89,7 +91,8 @@ namespace Opm m_deck_message_limits( deck ), m_unit_system( deck.getActiveUnitSystem() ), m_runspec( runspec ), - rst_config( SOLUTIONSection(deck), parseContext, errors ) + rst_config( SOLUTIONSection(deck), parseContext, errors ), + output_interval(output_interval_) { } @@ -101,6 +104,7 @@ namespace Opm m_unit_system.serializeOp(serializer); serializer(this->m_input_path); rst_config.serializeOp(serializer); + serializer(this->output_interval); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 5435a4564..62259599e 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -108,7 +108,7 @@ namespace { const std::optional& output_interval, const RestartIO::RstState * rst) try : - m_static( python, deck, runspec, parseContext, errors ), + m_static( python, deck, runspec, output_interval, parseContext, errors ), m_restart_info( restart_info(rst)), m_sched_deck(deck, m_restart_info ) { @@ -1205,6 +1205,9 @@ void Schedule::iterateScheduleSection(std::size_t load_start, std::size_t load_e } bool Schedule::write_rst_file(std::size_t report_step, bool ) const { + if (this->m_static.output_interval.has_value()) + return this->m_static.output_interval.value() % report_step; + if (report_step == 0) return this->m_static.rst_config.write_rst_file.value(); From e9890a1e13ec0c2007cdee7c974f8c5dd20b0d2b Mon Sep 17 00:00:00 2001 From: Joakim Hove Date: Fri, 26 Mar 2021 08:03:54 +0100 Subject: [PATCH 4/4] Fix warning - unused variable --- tests/test_AggregateConnectionData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_AggregateConnectionData.cpp b/tests/test_AggregateConnectionData.cpp index aa1534904..8d245827f 100644 --- a/tests/test_AggregateConnectionData.cpp +++ b/tests/test_AggregateConnectionData.cpp @@ -701,8 +701,6 @@ BOOST_AUTO_TEST_CASE(Declared_Connection_Data) // XCONN (PROD) + (WINJ) { using Ix = ::Opm::RestartIO::Helpers::VectorItems::XConn::index; - const auto& units = simCase.es.getUnits(); - using M = ::Opm::UnitSystem::measure; const auto& xconn = amconn.getXConn(); // PROD well