diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index e6455ecca..1dea43993 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -497,6 +497,7 @@ namespace Opm // Keyword Handlers void handlePYACTION (std::shared_ptr python, const std::string& input_path, const DeckKeyword&, std::size_t currentStep); void handleGCONPROD(const DeckKeyword& keyword, std::size_t current_step, const ParseContext& parseContext, ErrorGuard& errors); + void handleGLIFTOPT(const DeckKeyword& keyword, std::size_t report_step, const ParseContext& parseContext, ErrorGuard& errors); // Normal keyword handlers -- in KeywordHandlers.cpp void handleBRANPROP (const HandlerContext&, const ParseContext&, ErrorGuard&); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.cpp index 71e4cdf2e..0d22d0de3 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Action/ActionX.cpp @@ -33,7 +33,7 @@ namespace Action { bool ActionX::valid_keyword(const std::string& keyword) { - static std::unordered_set actionx_allowed_list = {"EXIT", "GCONPROD", "WELSPECS","WELOPEN", "UDQ"}; + static std::unordered_set actionx_allowed_list = {"EXIT", "GCONPROD", "GLIFTOPT", "WELSPECS","WELOPEN", "UDQ"}; return (actionx_allowed_list.find(keyword) != actionx_allowed_list.end()); } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index afe5ad689..de3aa5ce9 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -505,14 +505,18 @@ namespace { } } - void Schedule::handleGLIFTOPT(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard&errors) { - auto glo = std::make_shared( this->glo(handlerContext.currentStep) ); + void Schedule::handleGLIFTOPT(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { + this->handleGLIFTOPT(handlerContext.keyword, handlerContext.currentStep, parseContext, errors); + } - for (const auto& record : handlerContext.keyword) { + void Schedule::handleGLIFTOPT(const DeckKeyword& keyword, std::size_t report_step, const ParseContext& parseContext, ErrorGuard&errors) { + auto glo = std::make_shared( this->glo(report_step) ); + + for (const auto& record : keyword) { const std::string& groupNamePattern = record.getItem().getTrimmedString(0); const auto group_names = this->groupNames(groupNamePattern); if (group_names.empty()) - invalidNamePattern(groupNamePattern, handlerContext.currentStep, parseContext, errors, handlerContext.keyword); + invalidNamePattern(groupNamePattern, report_step, parseContext, errors, keyword); const auto& max_gas_item = record.getItem(); const double max_lift_gas_value = max_gas_item.hasValue(0) @@ -533,7 +537,7 @@ namespace { } } - this->m_glo.update(handlerContext.currentStep, std::move(glo)); + this->m_glo.update(report_step, std::move(glo)); } void Schedule::handleGPMAINT(const HandlerContext& handlerContext, const ParseContext& parseContext, ErrorGuard& errors) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index d426cfcd3..1133ecee4 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1499,6 +1499,9 @@ private: if (keyword.name() == "GCONPROD") this->handleGCONPROD(keyword, reportStep, parseContext, errors); + + if (keyword.name() == "GLIFTOPT") + this->handleGLIFTOPT(keyword, reportStep, parseContext, errors); } } diff --git a/src/opm/parser/eclipse/share/keywords/000_Eclipse100/G/GLIFTOPT b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/G/GLIFTOPT index dfb14715f..250287c80 100644 --- a/src/opm/parser/eclipse/share/keywords/000_Eclipse100/G/GLIFTOPT +++ b/src/opm/parser/eclipse/share/keywords/000_Eclipse100/G/GLIFTOPT @@ -3,6 +3,7 @@ "sections": [ "SCHEDULE" ], + "requires" : ["LIFTOPT"], "items": [ { "name": "GROUP_NAME", diff --git a/tests/parser/ACTIONX.cpp b/tests/parser/ACTIONX.cpp index d011e67b3..6a7e041a2 100644 --- a/tests/parser/ACTIONX.cpp +++ b/tests/parser/ACTIONX.cpp @@ -53,6 +53,17 @@ using namespace Opm; +Schedule make_schedule(const std::string& deck_string, const ParseContext& parseContext = {}) { + ErrorGuard errors; + Opm::Parser parser; + auto deck = parser.parseString(deck_string); + EclipseGrid grid1(10,10,10); + TableManager table ( deck ); + FieldPropsManager fp( deck, Phases{true, true, true}, grid1, table); + auto python = std::make_shared(); + Runspec runspec (deck); + return Schedule(deck, grid1, fp, runspec, parseContext, errors, python); +} BOOST_AUTO_TEST_CASE(Create) { @@ -128,27 +139,15 @@ ENDACTIO TSTEP 10 / )"}; - auto python = std::make_shared(); - Opm::Parser parser; - auto deck1 = parser.parseString(MISSING_END); - auto deck2 = parser.parseString(WITH_WELSPECS); - auto deck3 = parser.parseString(WITH_GRID); - EclipseGrid grid1(10,10,10); - TableManager table ( deck1 ); - FieldPropsManager fp( deck1, Phases{true, true, true}, grid1, table); - Runspec runspec (deck1); + BOOST_CHECK_THROW(make_schedule(MISSING_END), OpmInputError); - // The ACTIONX keyword has no matching 'ENDACTIO' -> exception - BOOST_CHECK_THROW(Schedule(deck1, grid1, fp, runspec, python), OpmInputError); - - Schedule sched(deck2, grid1, fp, runspec, python); + Schedule sched = make_schedule(WITH_WELSPECS); BOOST_CHECK( !sched.hasWell("W1") ); BOOST_CHECK( sched.hasWell("W2")); // The deck3 contains the 'GRID' keyword in the ACTIONX block - that is not a whitelisted keyword. ParseContext parseContext( {{ParseContext::ACTIONX_ILLEGAL_KEYWORD, InputError::THROW_EXCEPTION}} ); - ErrorGuard errors; - BOOST_CHECK_THROW(Schedule(deck3, grid1, fp, runspec, parseContext, errors, python), OpmInputError); + BOOST_CHECK_THROW( make_schedule(WITH_GRID, parseContext), OpmInputError ); } @@ -964,16 +963,8 @@ TSTEP )"}; auto unit_system = UnitSystem::newMETRIC(); - Opm::Parser parser; - auto deck = parser.parseString(deck_string); - EclipseGrid grid1(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid1, table); - auto python = std::make_shared(); const auto st = SummaryState{ std::chrono::system_clock::now() }; - - Runspec runspec (deck); - Schedule sched(deck, grid1, fp, runspec, python); + Schedule sched = make_schedule(deck_string); const auto& action1 = sched.actions(0).get("A"); { const auto& group = sched.getGroup("G1", 0); @@ -990,3 +981,62 @@ TSTEP BOOST_CHECK_CLOSE( prod.oil_target , unit_system.to_si(UnitSystem::measure::liquid_surface_rate, 200), 1e-5 ); } } + +BOOST_AUTO_TEST_CASE(GASLIFT_OPT_DECK) { + const auto input = R"(-- Turns on gas lift optimization +RUNSPEC +LIFTOPT +/ + +SCHEDULE + +GRUPTREE + 'PROD' 'FIELD' / + + 'M5S' 'PLAT-A' / + 'M5N' 'PLAT-A' / + + 'C1' 'M5N' / + 'F1' 'M5N' / + 'B1' 'M5S' / + 'G1' 'M5S' / + / + +ACTIONX +'A' / +WWCT 'OPX' > 0.75 AND / +FPR < 100 / +/ + +GLIFTOPT + 'PLAT-A' 200000 / -- +/ + +ENDACTIO + +TSTEP +10 / + + +)"; + + Opm::UnitSystem unitSystem = UnitSystem( UnitSystem::UnitType::UNIT_TYPE_METRIC ); + auto sched = make_schedule(input); + const auto& action1 = sched.actions(0).get("A"); + { + const auto& glo = sched.glo(0); + BOOST_CHECK(!glo.has_group("PLAT-A")); + } + + Action::Result action_result(true); + sched.applyAction(0, action1, action_result); + + { + const auto& glo = sched.glo(0); + BOOST_CHECK(glo.has_group("PLAT-A")); + const auto& plat_group = glo.group("PLAT-A"); + BOOST_CHECK_EQUAL( *plat_group.max_lift_gas(), unitSystem.to_si( UnitSystem::measure::gas_surface_rate, 200000)); + BOOST_CHECK(!plat_group.max_total_gas().has_value()); + } + +}