diff --git a/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp b/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp index 340b43890..8d238c052 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.hpp @@ -23,15 +23,87 @@ #include +#include +#include +#include namespace Opm { class Group2 { public: - explicit Group2(const std::string& group_name); +struct GroupInjectionProperties { + Phase phase = Phase::WATER; + GroupInjection::ControlEnum cmode = GroupInjection::NONE; + double surface_max_rate = 0; + double resv_max_rate = 0; + double target_reinj_fraction = 0; + double target_void_fraction = 0; + + bool operator==(const GroupInjectionProperties& other) const; + bool operator!=(const GroupInjectionProperties& other) const; +}; + + +struct GroupProductionProperties { + GroupProduction::ControlEnum cmode = GroupProduction::NONE; + GroupProductionExceedLimit::ActionEnum exceed_action = GroupProductionExceedLimit::NONE; + double oil_target = 0; + double water_target = 0; + double gas_target = 0; + double liquid_target = 0; + double resv_target = 0; + + bool operator==(const GroupProductionProperties& other) const; + bool operator!=(const GroupProductionProperties& other) const; +}; + + Group2(const std::string& group_name, std::size_t insert_index_arg, std::size_t init_step_arg); + + bool defined(std::size_t timeStep) const; + std::size_t insert_index() const; + const std::string& name() const; + const GroupProductionProperties& productionProperties() const; + const GroupInjectionProperties& injectionProperties() const; + int getGroupNetVFPTable() const; + bool updateNetVFPTable(int vfp_arg); + bool update_gefac(double gefac, bool transfer_gefac); + bool updateInjection(const GroupInjectionProperties& injection); + bool updateProduction(const GroupProductionProperties& production); + bool isProductionGroup() const; + bool isInjectionGroup() const; + void setProductionGroup(); + void setInjectionGroup(); + double getGroupEfficiencyFactor() const; + bool getTransferGroupEfficiencyFactor() const; + + std::size_t numWells() const; + bool addGroup(const std::string& group_name); + bool hasGroup(const std::string& group_name) const; + void delGroup(const std::string& group_name); + bool addWell(const std::string& well_name); + bool hasWell(const std::string& well_name) const; + void delWell(const std::string& well_name); + + const std::vector& wells() const; + const std::vector& groups() const; private: - std::string name; + bool hasType(GroupType gtype) const; + void addType(GroupType new_gtype); + + std::string m_name; + std::size_t m_insert_index; + std::size_t init_step; + GroupType group_type; + double gefac; + bool transfer_gefac; + int vfp_table; + + IOrderSet m_wells; + IOrderSet m_groups; + + GroupInjectionProperties injection_properties; + GroupProductionProperties production_properties; }; } diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index 25ad22d78..d1111bc63 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -148,6 +149,8 @@ namespace Opm bool hasGroup(const std::string& groupName) const; const Group& getGroup(const std::string& groupName) const; Group& getGroup(const std::string& groupName); + const Group2& getGroup2(const std::string& groupName, size_t timeStep) const; + const Tuning& getTuning() const; const MessageLimits& getMessageLimits() const; void invalidNamePattern (const std::string& namePattern, const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& keyword) const; @@ -174,6 +177,7 @@ namespace Opm TimeMap m_timeMap; OrderedMap< std::string, Group > m_groups; OrderedMap< std::string, DynamicState>> wells_static; + OrderedMap< std::string, DynamicState>> groups; DynamicState< GroupTree > m_rootGroupTree; DynamicState< OilVaporizationProperties > m_oilvaporizationproperties; Events m_events; @@ -194,6 +198,8 @@ namespace Opm std::vector< Group* > getGroups(const std::string& groupNamePattern); std::map well_events; + void updateGroup(std::shared_ptr group, size_t reportStep); + bool checkGroups(const ParseContext& parseContext, ErrorGuard& errors); bool updateWellStatus( const std::string& well, size_t reportStep , WellCommon::StatusEnum status); void addWellToGroup( Group& newGroup , const std::string& wellName , size_t timeStep); void iterateScheduleSection(const ParseContext& parseContext , ErrorGuard& errors, const SCHEDULESection& , const EclipseGrid& grid, diff --git a/opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp b/opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp index c68f7ec4b..b67bfaac5 100644 --- a/opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp +++ b/opm/parser/eclipse/EclipseState/Util/IOrderSet.hpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include namespace Opm { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp index 2bafd445d..1c92ae500 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Group/Group2.cpp @@ -22,9 +22,219 @@ #include namespace Opm { - -Group2::Group2(const std::string& group_name) : - name(group_name) +Group2::Group2(const std::string& name, std::size_t insert_index_arg, std::size_t init_step_arg) : + m_name(name), + m_insert_index(insert_index_arg), + init_step(init_step_arg), + group_type(GroupType::NONE), + gefac(1), + transfer_gefac(true), + vfp_table(0) {} +std::size_t Group2::insert_index() const { + return this->m_insert_index; +} + +bool Group2::defined(size_t timeStep) const { + if (timeStep < this->init_step) + return false; + else + return true; +} + +const std::string& Group2::name() const { + return this->m_name; +} + +const Group2::GroupProductionProperties& Group2::productionProperties() const { + return this->production_properties; +} + +const Group2::GroupInjectionProperties& Group2::injectionProperties() const { + return this->injection_properties; +} + +int Group2::getGroupNetVFPTable() const { + return this->vfp_table; +} + +bool Group2::updateNetVFPTable(int vfp_arg) { + if (this->vfp_table != vfp_arg) { + this->vfp_table = vfp_arg; + return true; + } else + return false; +} + +bool Group2::updateInjection(const GroupInjectionProperties& injection) { + bool update = false; + + if (this->injection_properties != injection) { + this->injection_properties = injection; + update = true; + } + + if (!this->hasType(GroupType::INJECTION)) { + this->addType(GroupType::INJECTION); + update = true; + } + + return update; +} + + +bool Group2::updateProduction(const GroupProductionProperties& production) { + bool update = false; + + if (this->production_properties != production) { + this->production_properties = production; + update = true; + } + + if (!this->hasType(GroupType::PRODUCTION)) { + this->addType(GroupType::PRODUCTION); + update = true; + } + + return update; +} + + +bool Group2::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->target_void_fraction == other.target_void_fraction; +} + + +bool Group2::GroupInjectionProperties::operator!=(const GroupInjectionProperties& other) const { + return !(*this == other); +} + + +bool Group2::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.oil_target && + this->gas_target == other.gas_target && + this->liquid_target == other.liquid_target && + this->resv_target == other.resv_target; +} + + +bool Group2::GroupProductionProperties::operator!=(const GroupProductionProperties& other) const { + return !(*this == other); +} + +bool Group2::hasType(GroupType gtype) const { + return ((this->group_type & gtype) == gtype); +} + +void Group2::addType(GroupType new_gtype) { + this->group_type = this->group_type | new_gtype; +} + +bool Group2::isProductionGroup() const { + return this->hasType(GroupType::PRODUCTION); +} + +bool Group2::isInjectionGroup() const { + return this->hasType(GroupType::INJECTION); +} + +void Group2::setProductionGroup() { + this->addType(GroupType::PRODUCTION); +} + +void Group2::setInjectionGroup() { + this->addType(GroupType::INJECTION); +} + + +std::size_t Group2::numWells() const { + return this->m_wells.size(); +} + +const std::vector& Group2::wells() const { + return this->m_wells.data(); +} + +const std::vector& Group2::groups() const { + return this->m_groups.data(); +} + + +bool Group2::addWell(const std::string& well_name) { + if (!this->m_groups.empty()) + throw std::logic_error("Groups can not mix group and well children"); + + if (this->m_wells.count(well_name) == 0) { + this->m_wells.insert(well_name); + return true; + } + return false; +} + +bool Group2::hasWell(const std::string& well_name) const { + return (this->m_wells.count(well_name) == 1); +} + +void Group2::delWell(const std::string& well_name) { + auto rm_count = this->m_wells.erase(well_name); + if (rm_count == 0) + throw std::invalid_argument("Group does not have well: " + well_name); +} + + +bool Group2::addGroup(const std::string& group_name) { + if (!this->m_wells.empty()) + throw std::logic_error("Groups can not mix group and well children"); + + if (this->m_groups.count(group_name) == 0) { + this->m_groups.insert(group_name); + return true; + } + return false; +} + +bool Group2::hasGroup(const std::string& group_name) const { + return (this->m_groups.count(group_name) == 1); +} + +void Group2::delGroup(const std::string& group_name) { + auto rm_count = this->m_groups.erase(group_name); + if (rm_count == 0) + throw std::invalid_argument("Group does not have group: " + group_name); +} + +bool Group2::update_gefac(double gf, bool transfer_gf) { + bool update = false; + if (this->gefac != gf) { + this->gefac = gf; + update = true; + } + + if (this->transfer_gefac != transfer_gf) { + this->transfer_gefac = transfer_gf; + update = true; + } + + return update; +} + +double Group2::getGroupEfficiencyFactor() const { + return this->gefac; +} + +bool Group2::getTransferGroupEfficiencyFactor() const { + return this->transfer_gefac; +} + } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index a80b124a0..753156b46 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -1418,28 +1418,37 @@ namespace { invalidNamePattern(groupNamePattern, parseContext, errors, keyword); for (const auto& group_name : group_names){ - auto& group = this->getGroup(group_name); - { - Phase phase = get_phase( record.getItem("PHASE").getTrimmedString(0) ); - group.setInjectionPhase( currentStep , phase ); - } - { - GroupInjection::ControlEnum controlMode = GroupInjection::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); - group.setInjectionControlMode( currentStep , controlMode ); - } - - Phase wellPhase = get_phase( record.getItem("PHASE").getTrimmedString(0)); - + GroupInjection::ControlEnum controlMode = GroupInjection::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); + Phase phase = get_phase( record.getItem("PHASE").getTrimmedString(0)); double surfaceInjectionRate = record.getItem("SURFACE_TARGET").get< UDAValue >(0).get(); - surfaceInjectionRate = injection::rateToSI(surfaceInjectionRate, wellPhase, section.unitSystem()); double reservoirInjectionRate = record.getItem("RESV_TARGET").get(0).get(); + double reinj_target = record.getItem("REINJ_TARGET").get(0).get(); + double voidage_target = record.getItem("VOIDAGE_TARGET").get(0).get(); - group.setSurfaceMaxRate( currentStep , surfaceInjectionRate); - group.setReservoirMaxRate( currentStep , reservoirInjectionRate); - group.setTargetReinjectFraction( currentStep , record.getItem("REINJ_TARGET").get(0).get()); - group.setTargetVoidReplacementFraction( currentStep , record.getItem("VOIDAGE_TARGET").get(0).get()); + surfaceInjectionRate = injection::rateToSI(surfaceInjectionRate, phase, section.unitSystem()); + { + auto& group = this->getGroup(group_name); + group.setInjectionPhase( currentStep , phase ); + group.setInjectionControlMode( currentStep , controlMode ); + group.setSurfaceMaxRate( currentStep, surfaceInjectionRate); + group.setReservoirMaxRate( currentStep, reservoirInjectionRate); + group.setTargetReinjectFraction( currentStep, reinj_target); + group.setTargetVoidReplacementFraction( currentStep, voidage_target); + group.setInjectionGroup(currentStep); + } + { + auto group_ptr = std::make_shared(this->getGroup2(group_name, currentStep)); + Group2::GroupInjectionProperties injection; + injection.phase = phase; + injection.cmode = controlMode; + injection.surface_max_rate = surfaceInjectionRate; + injection.resv_max_rate = reservoirInjectionRate; + injection.target_reinj_fraction = reinj_target; + injection.target_void_fraction = voidage_target; - group.setInjectionGroup(currentStep); + if (group_ptr->updateInjection(injection)) + this->updateGroup(group_ptr, currentStep); + } } } } @@ -1453,22 +1462,39 @@ namespace { invalidNamePattern(groupNamePattern, parseContext, errors, keyword); for (const auto& group_name : group_names){ - auto& group = this->getGroup(group_name); - { - GroupProduction::ControlEnum controlMode = GroupProduction::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); - group.setProductionControlMode( currentStep , controlMode ); - } - group.setOilTargetRate( currentStep , record.getItem("OIL_TARGET").get(0).get()); - group.setGasTargetRate( currentStep , record.getItem("GAS_TARGET").get(0).get()); - group.setWaterTargetRate( currentStep , record.getItem("WATER_TARGET").get(0).get()); - group.setLiquidTargetRate( currentStep , record.getItem("LIQUID_TARGET").get(0).get()); - group.setReservoirVolumeTargetRate( currentStep , record.getItem("RESERVOIR_FLUID_TARGET").getSIDouble(0)); - { - GroupProductionExceedLimit::ActionEnum exceedAction = GroupProductionExceedLimit::ActionEnumFromString(record.getItem("EXCEED_PROC").getTrimmedString(0) ); - group.setProductionExceedLimitAction( currentStep , exceedAction ); - } + GroupProduction::ControlEnum controlMode = GroupProduction::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); + GroupProductionExceedLimit::ActionEnum exceedAction = GroupProductionExceedLimit::ActionEnumFromString(record.getItem("EXCEED_PROC").getTrimmedString(0) ); + auto oil_target = record.getItem("OIL_TARGET").get(0).get(); + auto gas_target = record.getItem("GAS_TARGET").get(0).get(); + auto water_target = record.getItem("WATER_TARGET").get(0).get(); + auto liquid_target = record.getItem("LIQUID_TARGET").get(0).get(); + auto resv_target = record.getItem("RESERVOIR_FLUID_TARGET").getSIDouble(0); - group.setProductionGroup(currentStep); + { + auto& group = this->getGroup(group_name); + group.setProductionControlMode( currentStep , controlMode ); + group.setOilTargetRate( currentStep , oil_target); + group.setGasTargetRate( currentStep , gas_target); + group.setWaterTargetRate( currentStep , water_target); + group.setLiquidTargetRate( currentStep , liquid_target); + group.setReservoirVolumeTargetRate( currentStep , resv_target); + group.setProductionExceedLimitAction( currentStep , exceedAction ); + group.setProductionGroup(currentStep); + } + { + auto group_ptr = std::make_shared(this->getGroup2(group_name, currentStep)); + Group2::GroupProductionProperties production; + production.cmode = controlMode; + production.oil_target = oil_target; + production.gas_target = gas_target; + production.water_target = water_target; + production.liquid_target = liquid_target; + production.resv_target = resv_target; + production.exceed_action = exceedAction; + + if (group_ptr->updateProduction(production)) + this->updateGroup(group_ptr, currentStep); + } } } } @@ -1483,12 +1509,19 @@ namespace { invalidNamePattern(groupNamePattern, parseContext, errors, keyword); for (const auto& group_name : group_names){ - auto& group = this->getGroup(group_name); - const std::string& transfer_str = record.getItem("TRANSFER_EXT_NET").getTrimmedString(0); - bool transfer = (transfer_str == "YES") ? true : false; + bool transfer = DeckItem::to_bool(record.getItem("TRANSFER_EXT_NET").getTrimmedString(0)); + auto gefac = record.getItem("EFFICIENCY_FACTOR").get< double >(0); + { + auto& group = this->getGroup(group_name); - group.setGroupEfficiencyFactor(currentStep, record.getItem("EFFICIENCY_FACTOR").get< double >(0)); - group.setTransferGroupEfficiencyFactor(currentStep, transfer); + group.setGroupEfficiencyFactor(currentStep, gefac); + group.setTransferGroupEfficiencyFactor(currentStep, transfer); + } + { + auto group_ptr = std::make_shared(this->getGroup2(group_name, currentStep)); + if (group_ptr->update_gefac(gefac, transfer)) + this->updateGroup(group_ptr, currentStep); + } } } } @@ -1770,6 +1803,7 @@ namespace { m_rootGroupTree.update(currentStep, newTree); } + void Schedule::handleGRUPNET( const DeckKeyword& keyword, size_t currentStep) { for( const auto& record : keyword ) { const auto& groupName = record.getItem("NAME").getTrimmedString(0); @@ -1777,9 +1811,16 @@ namespace { if (!hasGroup(groupName)) addGroup(groupName , currentStep); - auto& group = this->m_groups.at( groupName ); int table = record.getItem("VFP_TABLE").get< int >(0); - group.setGroupNetVFPTable(currentStep, table); + { + auto& group = this->m_groups.at( groupName ); + group.setGroupNetVFPTable(currentStep, table); + } + { + auto group_ptr = std::make_shared( this->getGroup2(groupName, currentStep) ); + if (group_ptr->updateNetVFPTable(table)) + this->updateGroup(group_ptr, currentStep); + } } } @@ -2006,6 +2047,22 @@ namespace { return *well_ptr; } + const Group2& Schedule::getGroup2(const std::string& groupName, size_t timeStep) const { + if (this->groups.count(groupName) == 0) + throw std::invalid_argument("No such group: " + groupName); + + const auto& dynamic_state = this->groups.at(groupName); + auto& group_ptr = dynamic_state.get(timeStep); + if (!group_ptr) + throw std::invalid_argument("Group: " + groupName + " not yet defined at step: " + std::to_string(timeStep)); + + return *group_ptr; + } + + void Schedule::updateGroup(std::shared_ptr group, size_t reportStep) { + auto& dynamic_state = this->groups.at(group->name()); + dynamic_state.update(reportStep, group); + } /* There are many SCHEDULE keyword which take a wellname as argument. In @@ -2158,7 +2215,15 @@ namespace { void Schedule::addGroup(const std::string& groupName, size_t timeStep) { const size_t gseqIndex = m_groups.size(); + // Old group m_groups.insert( std::make_pair( groupName, Group { groupName, gseqIndex, m_timeMap, timeStep } )); + + // New group + groups.insert( std::make_pair( groupName, DynamicState>(this->m_timeMap, nullptr))); + auto group_ptr = std::make_shared(groupName, gseqIndex, timeStep); + auto& dynamic_state = this->groups.at(groupName); + dynamic_state.update(timeStep, group_ptr); + m_events.addEvent( ScheduleEvents::NEW_GROUP , timeStep ); } diff --git a/tests/parser/GroupTests.cpp b/tests/parser/GroupTests.cpp index 8fdb108c6..3a69a5a1e 100644 --- a/tests/parser/GroupTests.cpp +++ b/tests/parser/GroupTests.cpp @@ -82,15 +82,6 @@ BOOST_AUTO_TEST_CASE(CreateGroup_SetInjectorProducer_CorrectStatusSet) { group2.setProductionGroup(3); BOOST_CHECK(group2.isProductionGroup(4)); - - group2.setInjectionGroup(4); - BOOST_CHECK(group2.isInjectionGroup(5)); - - - // Testing that a group can be both; that seems slightly dubious - but was the old behavior - group2.setProductionGroup(4); - BOOST_CHECK(group2.isProductionGroup(5)); - BOOST_CHECK(group2.isInjectionGroup(5)); } @@ -435,5 +426,18 @@ BOOST_AUTO_TEST_CASE(createDeckWithGRUPNET) { BOOST_AUTO_TEST_CASE(Group2Create) { - Opm::Group2 group("NAME"); + Opm::Group2 g1("NAME", 1, 1); + Opm::Group2 g2("NAME", 1, 1); + + BOOST_CHECK( g1.addWell("W1") ); + BOOST_CHECK( !g1.addWell("W1") ); + BOOST_CHECK( g1.addWell("W2") ); + + BOOST_CHECK( g2.addGroup("G1") ); + BOOST_CHECK( !g2.addGroup("G1") ); + BOOST_CHECK( g2.addGroup("G2") ); + + // The children must be either all wells - or all groups. + BOOST_CHECK_THROW(g1.addGroup("G1"), std::logic_error); + BOOST_CHECK_THROW(g2.addWell("W1"), std::logic_error); } diff --git a/tests/parser/OrderedMapTests.cpp b/tests/parser/OrderedMapTests.cpp index 7227a7014..9ffb84fee 100644 --- a/tests/parser/OrderedMapTests.cpp +++ b/tests/parser/OrderedMapTests.cpp @@ -137,6 +137,8 @@ BOOST_AUTO_TEST_CASE(test_IOrderSet) { Opm::IOrderSet iset2; + + for (int i=10; i >= 0; i--) iset2.insert(i);