diff --git a/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp b/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp index 8894026c1..66102a05f 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Group/Group.hpp @@ -114,6 +114,7 @@ struct GroupInjectionProperties { UDAValue target_void_fraction; std::string reinj_group; std::string voidage_group; + bool available_group_control; static GroupInjectionProperties serializeObject(); @@ -133,6 +134,7 @@ struct GroupInjectionProperties { serializer(reinj_group); serializer(voidage_group); serializer(injection_controls); + serializer(available_group_control); } }; @@ -159,7 +161,7 @@ struct GroupProductionProperties { double guide_rate; GuideRateTarget guide_rate_def; double resv_target = 0; - + bool available_group_control = true; static GroupProductionProperties serializeObject(); int production_controls = 0; @@ -178,10 +180,12 @@ struct GroupProductionProperties { serializer(guide_rate); serializer(guide_rate_def); serializer(resv_target); + serializer(available_group_control); serializer(production_controls); } }; + struct ProductionControls { ProductionCMode cmode; ExceedAction exceed_action; @@ -224,8 +228,6 @@ struct ProductionControls { void setInjectionGroup(); double getGroupEfficiencyFactor() const; bool getTransferGroupEfficiencyFactor() const; - bool isAvailableForGroupControl() const; - void setAvailableForGroupControl(const bool available); std::size_t numWells() const; bool addGroup(const std::string& group_name); @@ -249,6 +251,8 @@ struct ProductionControls { Phase injection_phase() const; bool has_control(ProductionCMode control) const; bool has_control(InjectionCMode control) const; + bool productionGroupControlAvailable() const; + bool injectionGroupControlAvailable(const Phase phase) const; bool operator==(const Group& data) const; const Phase& topup_phase() const; @@ -265,7 +269,6 @@ struct ProductionControls { serializer(group_type); serializer(gefac); serializer(transfer_gefac); - serializer(available_for_group_control); serializer(vfp_table); serializer(parent_group); m_wells.serializeOp(serializer); @@ -287,7 +290,6 @@ private: GroupType group_type; double gefac; bool transfer_gefac; - bool available_for_group_control; int vfp_table; std::string parent_group; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp index 0331f710f..3ac662c66 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group.cpp @@ -39,7 +39,6 @@ Group::Group(const std::string& name, std::size_t insert_index_arg, std::size_t group_type(GroupType::NONE), gefac(1), transfer_gefac(true), - available_for_group_control(name == "FIELD" ? false : true), vfp_table(0) { // All groups are initially created as children of the "FIELD" group. @@ -58,7 +57,6 @@ Group Group::serializeObject() result.group_type = GroupType::PRODUCTION; result.gefac = 4.0; result.transfer_gefac = true; - result.available_for_group_control = false; result.vfp_table = 5; result.parent_group = "test2"; result.m_wells = {{"test3", "test4"}, {"test5", "test6"}}; @@ -186,15 +184,16 @@ Group::GroupInjectionProperties Group::GroupInjectionProperties::serializeObject bool Group::GroupInjectionProperties::operator==(const GroupInjectionProperties& other) const { return - this->phase == other.phase && - this->cmode == other.cmode && - this->surface_max_rate == other.surface_max_rate && - this->resv_max_rate == other.resv_max_rate && - this->target_reinj_fraction == other.target_reinj_fraction && - this->injection_controls == other.injection_controls && - this->target_void_fraction == other.target_void_fraction && - this->reinj_group == other.reinj_group && - this->voidage_group == other.voidage_group; + this->phase == other.phase && + this->cmode == other.cmode && + this->surface_max_rate == other.surface_max_rate && + this->resv_max_rate == other.resv_max_rate && + this->target_reinj_fraction == other.target_reinj_fraction && + this->injection_controls == other.injection_controls && + this->target_void_fraction == other.target_void_fraction && + this->reinj_group == other.reinj_group && + this->available_group_control == other.available_group_control && + this->voidage_group == other.voidage_group; } @@ -223,18 +222,35 @@ Group::GroupProductionProperties Group::GroupProductionProperties::serializeObje bool Group::GroupProductionProperties::operator==(const GroupProductionProperties& other) const { return - this->cmode == other.cmode && - this->exceed_action == other.exceed_action && - this->oil_target == other.oil_target && - this->water_target == other.water_target && - this->gas_target == other.gas_target && - this->liquid_target == other.liquid_target && - this->guide_rate == other.guide_rate && - this->guide_rate_def == other.guide_rate_def && - this->production_controls == other.production_controls && - this->resv_target == other.resv_target; + this->cmode == other.cmode && + this->exceed_action == other.exceed_action && + this->oil_target == other.oil_target && + this->water_target == other.water_target && + this->gas_target == other.gas_target && + this->liquid_target == other.liquid_target && + this->guide_rate == other.guide_rate && + this->guide_rate_def == other.guide_rate_def && + this->production_controls == other.production_controls && + this->available_group_control == other.available_group_control && + this->resv_target == other.resv_target; } +bool Group::productionGroupControlAvailable() const { + if (this->m_name == "FIELD") + return false; + return this->production_properties.available_group_control; +} + +bool Group::injectionGroupControlAvailable(const Phase phase) const { + if (this->m_name == "FIELD") + return false; + + auto inj_iter = this->injection_properties.find(phase); + if (inj_iter == this->injection_properties.end()) + return true; + + return inj_iter->second.available_group_control; +} bool Group::GroupProductionProperties::operator!=(const GroupProductionProperties& other) const { return !(*this == other); @@ -348,14 +364,6 @@ bool Group::getTransferGroupEfficiencyFactor() const { return this->transfer_gefac; } -bool Group::isAvailableForGroupControl() const { - return this->available_for_group_control; -} - -void Group::setAvailableForGroupControl(const bool available) { - this->available_for_group_control = available; -} - const std::string& Group::parent() const { return this->parent_group; } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index f20aa77ec..30a13b2ea 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1676,6 +1676,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext& injection.injection_controls = 0; injection.reinj_group = reinj_group; injection.voidage_group = voidage_group; + injection.available_group_control = availableForGroupControl; if (!record.getItem("SURFACE_TARGET").defaultApplied(0)) injection.injection_controls += static_cast(Group::InjectionCMode::RATE); @@ -1689,9 +1690,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext& if (!record.getItem("VOIDAGE_TARGET").defaultApplied(0)) injection.injection_controls += static_cast(Group::InjectionCMode::VREP); - const bool must_update_avail = group_ptr->isAvailableForGroupControl() != availableForGroupControl; - if (group_ptr->updateInjection(injection) || must_update_avail) { - group_ptr->setAvailableForGroupControl(availableForGroupControl); + if (group_ptr->updateInjection(injection)) { this->updateGroup(std::move(group_ptr), currentStep); m_events.addEvent( ScheduleEvents::GROUP_INJECTION_UPDATE , currentStep); this->addWellGroupEvent(group_name, ScheduleEvents::GROUP_INJECTION_UPDATE, currentStep); @@ -1750,6 +1749,8 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext& production.guide_rate = guide_rate; production.guide_rate_def = guide_rate_def; production.resv_target = resv_target; + production.available_group_control = availableForGroupControl; + if ((production.cmode == Group::ProductionCMode::ORAT) || (production.cmode == Group::ProductionCMode::WRAT) || (production.cmode == Group::ProductionCMode::GRAT) || @@ -1775,9 +1776,7 @@ Schedule::Schedule(const Deck& deck, const EclipseState& es, const ParseContext& if (!record.getItem("RESERVOIR_FLUID_TARGET").defaultApplied(0)) production.production_controls += static_cast(Group::ProductionCMode::RESV); - const bool must_update_avail = group_ptr->isAvailableForGroupControl() != availableForGroupControl; - if (group_ptr->updateProduction(production) || must_update_avail) { - group_ptr->setAvailableForGroupControl(availableForGroupControl); + if (group_ptr->updateProduction(production)) { auto new_config = std::make_shared( this->guideRateConfig(currentStep) ); new_config->update_group(*group_ptr); this->guide_rate_config.update( currentStep, std::move(new_config) ); diff --git a/tests/parser/GroupTests.cpp b/tests/parser/GroupTests.cpp index 88a5ce304..943cf8dd4 100644 --- a/tests/parser/GroupTests.cpp +++ b/tests/parser/GroupTests.cpp @@ -45,6 +45,17 @@ using namespace Opm; +Opm::Schedule create_schedule(const std::string& deck_string) { + Opm::Parser parser; + auto python = std::make_shared(); + auto deck = parser.parseString(deck_string); + EclipseGrid grid(10,10,10); + TableManager table ( deck ); + FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); + Runspec runspec (deck ); + return Opm::Schedule(deck, grid, fp, runspec, python); +} + BOOST_AUTO_TEST_CASE(CreateGroup_CorrectNameAndDefaultValues) { Opm::Group group("G1" , 1, 0, 0, UnitSystem::newMETRIC()); @@ -92,7 +103,6 @@ BOOST_AUTO_TEST_CASE(GroupDoesNotHaveWell) { BOOST_AUTO_TEST_CASE(createDeckWithGEFAC) { - Opm::Parser parser; std::string input = "START -- 0 \n" "19 JUN 2007 / \n" @@ -112,13 +122,7 @@ BOOST_AUTO_TEST_CASE(createDeckWithGEFAC) { " 'PRODUC' 0.85 / \n" "/\n"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - Runspec runspec (deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Opm::Schedule schedule(deck, grid, fp, runspec, python); + auto schedule = create_schedule(input); auto group_names = schedule.groupNames("PRODUC"); BOOST_CHECK_EQUAL(group_names.size(), 1); @@ -136,8 +140,6 @@ BOOST_AUTO_TEST_CASE(createDeckWithWGRUPCONandWCONPROD) { /* Test deck with well guide rates for group control: GRUPCON (well guide rates for group control) WCONPROD (conrol data for production wells) with GRUP control mode */ - - Opm::Parser parser; std::string input = "START -- 0 \n" "19 JUN 2007 / \n" @@ -166,13 +168,7 @@ BOOST_AUTO_TEST_CASE(createDeckWithWGRUPCONandWCONPROD) { - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Opm::Schedule schedule(deck, grid, fp, runspec, python); + auto schedule = create_schedule(input); const auto& currentWell = schedule.getWell("B-37T2", 0); const Opm::Well::WellProductionProperties& wellProductionProperties = currentWell.getProductionProperties(); BOOST_CHECK(wellProductionProperties.controlMode == Opm::Well::ProducerCMode::GRUP); @@ -181,8 +177,6 @@ BOOST_AUTO_TEST_CASE(createDeckWithWGRUPCONandWCONPROD) { BOOST_CHECK_EQUAL(currentWell.getGuideRate(), 30); BOOST_CHECK(currentWell.getGuideRatePhase() == Opm::Well::GuideRateTarget::OIL); BOOST_CHECK_EQUAL(currentWell.getGuideRateScalingFactor(), 1.0); - - } @@ -190,8 +184,7 @@ BOOST_AUTO_TEST_CASE(createDeckWithWGRUPCONandWCONPROD) { BOOST_AUTO_TEST_CASE(createDeckWithGRUPNET) { - Opm::Parser parser; - std::string input = + std::string input = "START -- 0 \n" "31 AUG 1993 / \n" "SCHEDULE\n" @@ -211,20 +204,15 @@ BOOST_AUTO_TEST_CASE(createDeckWithGRUPNET) { " 'MANI-E2' 1* 9 4* / \n" "/\n"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Opm::Schedule schedule(deck, grid, fp, runspec, python); - const auto& group1 = schedule.getGroup("PROD", 0); - const auto& group2 = schedule.getGroup("MANI-E2", 0); - const auto& group3 = schedule.getGroup("MANI-K1", 0); - BOOST_CHECK_EQUAL(group1.getGroupNetVFPTable(), 0); - BOOST_CHECK_EQUAL(group2.getGroupNetVFPTable(), 9); - BOOST_CHECK_EQUAL(group3.getGroupNetVFPTable(), 9999); + auto schedule = create_schedule(input); + + const auto& group1 = schedule.getGroup("PROD", 0); + const auto& group2 = schedule.getGroup("MANI-E2", 0); + const auto& group3 = schedule.getGroup("MANI-K1", 0); + BOOST_CHECK_EQUAL(group1.getGroupNetVFPTable(), 0); + BOOST_CHECK_EQUAL(group2.getGroupNetVFPTable(), 9); + BOOST_CHECK_EQUAL(group3.getGroupNetVFPTable(), 9999); } @@ -254,7 +242,6 @@ BOOST_AUTO_TEST_CASE(GroupCreate) { } BOOST_AUTO_TEST_CASE(createDeckWithGCONPROD) { - Opm::Parser parser; std::string input = R"( START -- 0 31 AUG 1993 / @@ -270,13 +257,7 @@ BOOST_AUTO_TEST_CASE(createDeckWithGCONPROD) { 'G2' 'RESV' 10000 3* 'CON' / /)"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Opm::Schedule schedule(deck, grid, fp, runspec, python); + auto schedule = create_schedule(input); SummaryState st(std::chrono::system_clock::now()); const auto& group1 = schedule.getGroup("G1", 0); @@ -300,7 +281,6 @@ BOOST_AUTO_TEST_CASE(TESTGuideRateModel) { } BOOST_AUTO_TEST_CASE(TESTGuideRateLINCOM) { - Parser parser; std::string input = R"( START -- 0 31 AUG 1993 / @@ -324,19 +304,12 @@ BOOST_AUTO_TEST_CASE(TESTGuideRateLINCOM) { )"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); /* The 'COMB' target mode is not supported */ - BOOST_CHECK_THROW(Opm::Schedule schedule(deck, grid, fp, runspec, python), std::logic_error); + BOOST_CHECK_THROW(create_schedule(input), std::logic_error); } BOOST_AUTO_TEST_CASE(TESTGuideRate) { - Parser parser; std::string input = R"( START -- 0 31 AUG 1993 / @@ -362,19 +335,11 @@ BOOST_AUTO_TEST_CASE(TESTGuideRate) { 1 1 1 1 1 1 1 1 1 1 1 / )"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Schedule schedule(deck, grid, fp, runspec, python); - + auto schedule = create_schedule(input); GuideRate gr(schedule); } BOOST_AUTO_TEST_CASE(TESTGCONSALE) { - Parser parser; std::string input = R"( START -- 0 31 AUG 1993 / @@ -396,14 +361,7 @@ BOOST_AUTO_TEST_CASE(TESTGCONSALE) { )"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck, Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Schedule schedule(deck, grid, fp, runspec, python); - + auto schedule = create_schedule(input); double metric_to_si = 1.0 / (24.0 * 3600.0); //cubic meters / day const auto& gconsale = schedule.gConSale(0); @@ -438,7 +396,6 @@ BOOST_AUTO_TEST_CASE(TESTGCONSALE) { } BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { - Parser parser; std::string input = R"( START -- 0 31 AUG 1993 / @@ -466,13 +423,7 @@ BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { )"; - auto deck = parser.parseString(input); - auto python = std::make_shared(); - EclipseGrid grid(10,10,10); - TableManager table ( deck ); - FieldPropsManager fp( deck , Phases{true, true, true}, grid, table); - Runspec runspec (deck ); - Schedule schedule(deck, grid, fp, runspec, python); + auto schedule = create_schedule(input); SummaryState st(std::chrono::system_clock::now()); // Step 0 { @@ -480,7 +431,10 @@ BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { BOOST_CHECK( g1.hasInjectionControl(Phase::WATER)); BOOST_CHECK( g1.hasInjectionControl(Phase::GAS)); BOOST_CHECK( !g1.hasInjectionControl(Phase::OIL)); - BOOST_CHECK( g1.isAvailableForGroupControl() ); + + BOOST_CHECK( g1.injectionGroupControlAvailable(Phase::WATER) ); + BOOST_CHECK( g1.injectionGroupControlAvailable(Phase::GAS) ); + BOOST_CHECK( g1.productionGroupControlAvailable() ); g1.injectionControls(Phase::WATER, st); g1.injectionControls(Phase::GAS, st); @@ -493,7 +447,7 @@ BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { const auto& g2 = schedule.getGroup("G2", 0); BOOST_CHECK(!g2.has_topup_phase()); BOOST_CHECK_THROW(g2.topup_phase(), std::logic_error); - BOOST_CHECK( g2.isAvailableForGroupControl() ); + BOOST_CHECK( g2.injectionGroupControlAvailable(Phase::WATER) ); } // Step 1 { @@ -501,7 +455,7 @@ BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { BOOST_CHECK( g2.hasInjectionControl(Phase::WATER)); BOOST_CHECK( g2.hasInjectionControl(Phase::GAS)); BOOST_CHECK( !g2.hasInjectionControl(Phase::OIL)); - BOOST_CHECK( !g2.isAvailableForGroupControl() ); + BOOST_CHECK( !g2.injectionGroupControlAvailable(Phase::GAS) ); g2.injectionControls(Phase::WATER, st); g2.injectionControls(Phase::GAS, st); @@ -514,6 +468,72 @@ BOOST_AUTO_TEST_CASE(GCONINJE_MULTIPLE_PHASES) { const auto& g1 = schedule.getGroup("G1", 1); BOOST_CHECK(!g1.has_topup_phase()); BOOST_CHECK_THROW(g1.topup_phase(), std::logic_error); - BOOST_CHECK( g1.isAvailableForGroupControl() ); + } +} + +BOOST_AUTO_TEST_CASE(GCONINJE_GCONPROD) { + std::string input = R"( + START -- 0 + 31 AUG 1993 / + SCHEDULE + + GRUPTREE + 'G1' 'FIELD' / + 'G2' 'FIELD' / + / + + GCONPROD + 'G1' 'ORAT' 10000 3* 'CON' 'NO'/ + 'G2' 'ORAT' 10000 3* 'CON' / + / + + GCONINJE + 'G1' 'WATER' 1* 1000 / + 'G2' 'WATER' 1* 1* 2000 1* 1* 'NO'/ + / + + + TSTEP + 1 / + + GCONPROD + 'G1' 'ORAT' 10000 3* 'CON' / + 'G2' 'ORAT' 10000 3* 'CON' 'NO'/ + / + + GCONINJE + 'G1' 'WATER' 1* 1000 3* 'NO' / + 'G2' 'WATER' 1* 1* 2000 / + / + + )"; + + auto schedule = create_schedule(input); + { + const auto& f = schedule.getGroup("FIELD", 0); + const auto& g1 = schedule.getGroup("G1", 0); + const auto& g2 = schedule.getGroup("G2", 0); + + BOOST_CHECK(!f.productionGroupControlAvailable() ); + BOOST_CHECK(!f.injectionGroupControlAvailable(Phase::WATER)); + BOOST_CHECK(!f.injectionGroupControlAvailable(Phase::GAS)); + + BOOST_CHECK(!g1.productionGroupControlAvailable() ); + BOOST_CHECK( g2.productionGroupControlAvailable() ); + BOOST_CHECK( g1.injectionGroupControlAvailable(Phase::WATER)); + BOOST_CHECK(!g2.injectionGroupControlAvailable(Phase::WATER)); + BOOST_CHECK( g1.injectionGroupControlAvailable(Phase::GAS)); + BOOST_CHECK( g2.injectionGroupControlAvailable(Phase::GAS)); + } + { + const auto& g1 = schedule.getGroup("G1", 1); + const auto& g2 = schedule.getGroup("G2", 1); + + BOOST_CHECK( g1.productionGroupControlAvailable() ); + BOOST_CHECK(!g2.productionGroupControlAvailable() ); + BOOST_CHECK(!g1.injectionGroupControlAvailable(Phase::WATER)); + BOOST_CHECK( g2.injectionGroupControlAvailable(Phase::WATER)); + BOOST_CHECK( g1.injectionGroupControlAvailable(Phase::GAS)); + BOOST_CHECK( g2.injectionGroupControlAvailable(Phase::GAS)); } }