diff --git a/CMakeLists_files.cmake b/CMakeLists_files.cmake index 6f5ebfbd5f7..957ffda54c5 100644 --- a/CMakeLists_files.cmake +++ b/CMakeLists_files.cmake @@ -250,6 +250,7 @@ if(ENABLE_ECL_INPUT) opm/input/eclipse/Schedule/Group/GConSale.cpp opm/input/eclipse/Schedule/Group/GConSump.cpp opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.cpp + opm/input/eclipse/Schedule/Group/GSatProd.cpp opm/input/eclipse/Schedule/Group/GTNode.cpp opm/input/eclipse/Schedule/MSW/AICD.cpp opm/input/eclipse/Schedule/MSW/Compsegs.cpp @@ -1349,6 +1350,7 @@ if(ENABLE_ECL_INPUT) opm/input/eclipse/Schedule/Group/GuideRate.hpp opm/input/eclipse/Schedule/Group/GConSale.hpp opm/input/eclipse/Schedule/Group/GConSump.hpp + opm/input/eclipse/Schedule/Group/GSatProd.hpp opm/input/eclipse/Schedule/Group/GroupEconProductionLimits.hpp opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp opm/input/eclipse/Schedule/Group/GuideRateModel.hpp diff --git a/opm/input/eclipse/Schedule/Group/GSatProd.cpp b/opm/input/eclipse/Schedule/Group/GSatProd.cpp new file mode 100644 index 00000000000..f24cb55afa7 --- /dev/null +++ b/opm/input/eclipse/Schedule/Group/GSatProd.cpp @@ -0,0 +1,69 @@ +/* + Copyright 2019 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 + +namespace Opm { + +GSatProd GSatProd::serializationTestObject() +{ + GSatProd result; + result.groups = { {"test1", {1.0,2.0,3.0,4.0,5.0} } }; + + return result; +} + +bool GSatProd::has(const std::string& name) const { + return (groups.find(name) != groups.end()); +} + + +const GSatProd::GSatProdGroup& GSatProd::get(const std::string& name) const { + + auto it = groups.find(name); + if (it == groups.end()) + throw std::invalid_argument("Current GSatPRod obj. does not contain '" + name + "'."); + else + return it->second; +} + +void GSatProd::add(const std::string& name, const double& oil_rate, + const double& gas_rate, const double& water_rate, + const double& resv_rate, const double& glift_rate) { + + GSatProd::GSatProdGroup& group = groups[name]; + + group.oil_rate = oil_rate; + group.gas_rate = gas_rate; + group.water_rate = water_rate; + group.resv_rate = resv_rate; + group.glift_rate = glift_rate; +} + + +size_t GSatProd::size() const { + return groups.size(); +} + +bool GSatProd::operator==(const GSatProd& data) const { + return this->groups == data.groups; +} + +} diff --git a/opm/input/eclipse/Schedule/Group/GSatProd.hpp b/opm/input/eclipse/Schedule/Group/GSatProd.hpp new file mode 100644 index 00000000000..e80ad44e63f --- /dev/null +++ b/opm/input/eclipse/Schedule/Group/GSatProd.hpp @@ -0,0 +1,81 @@ +/* + Copyright 2024 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 GSATPROD_H +#define GSATPROD_H + +#include +#include + +namespace Opm { + + class SummaryState; + + class GSatProd { + public: + struct GSatProdGroup { + double oil_rate; + double gas_rate; + double water_rate; + double resv_rate; + double glift_rate; + + bool operator==(const GSatProdGroup& data) const { + return oil_rate == data.oil_rate && + gas_rate == data.gas_rate && + water_rate == data.water_rate && + resv_rate == data.resv_rate && + glift_rate == data.glift_rate; + } + + template + void serializeOp(Serializer& serializer) + { + serializer(oil_rate); + serializer(gas_rate); + serializer(water_rate); + serializer(resv_rate); + serializer(glift_rate); + } + }; + + static GSatProd serializationTestObject(); + + bool has(const std::string& name) const; + const GSatProdGroup& get(const std::string& name) const; + void add(const std::string& name, const double& oil_rate, + const double& gas_rate, const double& water_rate, + const double& resv_rate, const double& glift_rate); + size_t size() const; + + bool operator==(const GSatProd& data) const; + + template + void serializeOp(Serializer& serializer) + { + serializer(groups); + } + + private: + std::map groups; + }; + +} + +#endif diff --git a/opm/input/eclipse/Schedule/Group/GroupKeywordHandlers.cpp b/opm/input/eclipse/Schedule/Group/GroupKeywordHandlers.cpp index 8a8fd4b1ed2..61798b7d8b3 100644 --- a/opm/input/eclipse/Schedule/Group/GroupKeywordHandlers.cpp +++ b/opm/input/eclipse/Schedule/Group/GroupKeywordHandlers.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -308,6 +309,39 @@ void handleGCONPROD(HandlerContext& handlerContext) } } +void handleGSATPROD(HandlerContext& handlerContext) +{ + const auto& keyword = handlerContext.keyword; + auto new_gsatprod = handlerContext.state().gsatprod.get(); + + for (const auto& record : keyword) { + const std::string& groupNamePattern = record.getItem("SATELLITE_GROUP_NAME_OR_GROUP_NAME_ROOT").getTrimmedString(0); + const auto group_names = handlerContext.groupNames(groupNamePattern); + if (group_names.empty()) { + handlerContext.invalidNamePattern(groupNamePattern); + } + const auto oil_rate = record.getItem("OIL_PRODUCTION_RATE").getSIDouble(0); + const auto gas_rate = record.getItem("GAS_PRODUCTION_RATE").getSIDouble(0); + const auto water_rate = record.getItem("WATER_PRODUCTION_RATE").getSIDouble(0); + const auto resv_rate = record.getItem("RES_FLUID_VOL_PRODUCTION_RATE").getSIDouble(0); + const auto glift_rate = record.getItem("LIFT_GAS_SUPPLY_RATE").getSIDouble(0); + + for (const auto& group_name : group_names) { + const bool is_field { group_name == "FIELD" } ; + if (is_field) { + std::string msg_fmt = "Problem with {keyword}\n" + "In {file} line {line}\n" + "GSATPROD group cannot be named FIELD "; + const auto& parseContext = handlerContext.parseContext; + auto& errors = handlerContext.errors; + parseContext.handleError(ParseContext::SCHEDULE_GROUP_ERROR, msg_fmt, keyword.location(), errors); + } + new_gsatprod.add(group_name, oil_rate, gas_rate, water_rate, resv_rate, glift_rate); + } + } + handlerContext.state().gsatprod.update( std::move(new_gsatprod) ); +} + void handleGCONSALE(HandlerContext& handlerContext) { auto new_gconsale = handlerContext.state().gconsale.get(); @@ -452,6 +486,7 @@ getGroupHandlers() { "GCONPROD", &handleGCONPROD }, { "GCONSALE", &handleGCONSALE }, { "GCONSUMP", &handleGCONSUMP }, + { "GSATPROD", &handleGSATPROD }, { "GECON" , &handleGECON }, { "GEFAC" , &handleGEFAC }, { "GPMAINT" , &handleGPMAINT }, diff --git a/opm/input/eclipse/Schedule/Schedule.cpp b/opm/input/eclipse/Schedule/Schedule.cpp index 1b6277f04d1..6af19405195 100644 --- a/opm/input/eclipse/Schedule/Schedule.cpp +++ b/opm/input/eclipse/Schedule/Schedule.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -2374,6 +2375,7 @@ void Schedule::create_first(const time_point& start_time, const std::optionaltemplate pack_unpack(serializer); this->template pack_unpack(serializer); this->template pack_unpack(serializer); + this->template pack_unpack(serializer); this->template pack_unpack(serializer); this->template pack_unpack(serializer); this->template pack_unpack(serializer); diff --git a/opm/input/eclipse/Schedule/ScheduleState.cpp b/opm/input/eclipse/Schedule/ScheduleState.cpp index 893ad347001..28954ad9f74 100644 --- a/opm/input/eclipse/Schedule/ScheduleState.cpp +++ b/opm/input/eclipse/Schedule/ScheduleState.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -307,6 +308,7 @@ bool ScheduleState::operator==(const ScheduleState& other) const { this->group_order.get() == other.group_order.get() && this->gconsale.get() == other.gconsale.get() && this->gconsump.get() == other.gconsump.get() && + this->gsatprod.get() == other.gsatprod.get() && this->wlist_manager.get() == other.wlist_manager.get() && this->rpt_config.get() == other.rpt_config.get() && this->actions.get() == other.actions.get() && @@ -353,6 +355,7 @@ ScheduleState ScheduleState::serializationTestObject() { ts.wtest_config.update( WellTestConfig::serializationTestObject() ); ts.gconsump.update( GConSump::serializationTestObject() ); ts.gconsale.update( GConSale::serializationTestObject() ); + ts.gsatprod.update( GSatProd::serializationTestObject() ); ts.gecon.update( GroupEconProductionLimits::serializationTestObject() ); ts.wlist_manager.update( WListManager::serializationTestObject() ); ts.rpt_config.update( RPTConfig::serializationTestObject() ); diff --git a/opm/input/eclipse/Schedule/ScheduleState.hpp b/opm/input/eclipse/Schedule/ScheduleState.hpp index 9691ec9daba..a815a557556 100644 --- a/opm/input/eclipse/Schedule/ScheduleState.hpp +++ b/opm/input/eclipse/Schedule/ScheduleState.hpp @@ -65,6 +65,7 @@ namespace Opm { class GasLiftOpt; class GConSale; class GConSump; + class GSatProd; class GroupEconProductionLimits; class GroupOrder; class GuideRateConfig; @@ -385,6 +386,7 @@ namespace Opm { ptr_member gconsale; ptr_member gconsump; + ptr_member gsatprod; ptr_member gecon; ptr_member guide_rate; @@ -428,6 +430,8 @@ namespace Opm { return this->gconsale; else if constexpr ( std::is_same_v ) return this->gconsump; + else if constexpr ( std::is_same_v ) + return this->gsatprod; else if constexpr ( std::is_same_v ) return this->gecon; else if constexpr ( std::is_same_v ) diff --git a/opm/input/eclipse/share/keywords/000_Eclipse100/G/GSATPROD b/opm/input/eclipse/share/keywords/000_Eclipse100/G/GSATPROD index 72bcb46b7e8..b4613797218 100644 --- a/opm/input/eclipse/share/keywords/000_Eclipse100/G/GSATPROD +++ b/opm/input/eclipse/share/keywords/000_Eclipse100/G/GSATPROD @@ -11,26 +11,31 @@ { "name": "OIL_PRODUCTION_RATE", "value_type": "DOUBLE", + "dimension": "LiquidSurfaceVolume/Time", "default": 0 }, { "name": "WATER_PRODUCTION_RATE", "value_type": "DOUBLE", + "dimension": "LiquidSurfaceVolume/Time", "default": 0 }, { "name": "GAS_PRODUCTION_RATE", "value_type": "DOUBLE", + "dimension": "GasSurfaceVolume/Time", "default": 0 }, { "name": "RES_FLUID_VOL_PRODUCTION_RATE", "value_type": "DOUBLE", + "dimension": "ReservoirVolume/Time", "default": 0 }, { "name": "LIFT_GAS_SUPPLY_RATE", "value_type": "DOUBLE", + "dimension": "GasSurfaceVolume/Time", "default": 0 }, { diff --git a/opm/output/eclipse/Summary.cpp b/opm/output/eclipse/Summary.cpp index a880e10ba10..e09965b31d1 100644 --- a/opm/output/eclipse/Summary.cpp +++ b/opm/output/eclipse/Summary.cpp @@ -37,6 +37,7 @@ #include #include +#include #include #include #include @@ -655,6 +656,29 @@ alq_type(const Opm::ScheduleState& sched_state, return sched_state.vfpprod(vfp_table_number).getALQType(); } +inline double accum_groups(const rt phase, const Opm::Schedule& schedule, const size_t sim_step, const std::string& gr_name) +{ + double sum = 0.0; + if (!schedule.hasGroup(gr_name, sim_step)) { + return sum; + } + const auto& top_group = schedule.getGroup(gr_name, sim_step); + for (const auto& child : top_group.groups()) { + sum += accum_groups(phase, schedule, sim_step, child); + } + const auto& gsatprod = schedule[sim_step].gsatprod.get(); + if (gsatprod.has(gr_name)) { + const auto& gs = gsatprod.get(gr_name); + if (phase == rt::oil) + sum += gs.oil_rate; + if (phase == rt::gas) + sum += gs.gas_rate; + if (phase == rt::wat) + sum += gs.water_rate; + } + return sum; +} + inline quantity artificial_lift_quantity( const fn_args& args ) { // Note: This function is intentionally supported only at the well level // (meaning there's no loop over args.schedule_wells by intention). Its @@ -773,6 +797,11 @@ inline quantity rate( const fn_args& args ) { sum *= -1.0; } + const auto& gsatprod = args.schedule[args.sim_step].gsatprod.get(); + if (!injection && gsatprod.size() > 0 && !args.group_name.empty()) { + sum += accum_groups(phase, args.schedule, args.sim_step, args.group_name); + } + if (phase == rt::polymer || phase == rt::brine) { return { sum, measure::mass_rate }; } @@ -780,6 +809,7 @@ inline quantity rate( const fn_args& args ) { return { sum, rate_unit< phase >() }; } + template inline quantity filtrate_connection_quantities( const fn_args& args ) { @@ -3253,8 +3283,9 @@ namespace Evaluator { (this->node_.category == Cat::Group) || (this->node_.category == Cat::Node); + const auto def_gr_name = this->node_.category == Cat::Field ? std::string{"FIELD"} : std::string{""}; return need_grp_name - ? this->node_.wgname : std::string{""}; + ? this->node_.wgname : def_gr_name; } bool use_number() const diff --git a/tests/parser/GroupTests.cpp b/tests/parser/GroupTests.cpp index 000fa11622b..d9242b42387 100644 --- a/tests/parser/GroupTests.cpp +++ b/tests/parser/GroupTests.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -380,6 +381,39 @@ TSTEP GuideRate gr(schedule); } +BOOST_AUTO_TEST_CASE(TESTGSatProd) { + const std::string input = R"( +START -- 0 +31 AUG 1993 / +SCHEDULE + +GRUPTREE + 'G1' 'FIELD' / + 'G2' 'FIELD' / +/ + +GCONPROD + 'G1' 'ORAT' 10000 / +/ + +GSATPROD + 'G2' 1000 / +/ + +TSTEP + 1 /)"; + + auto schedule = create_schedule(input); + double metric_to_si = 1.0 / (24.0 * 3600.0); //cubic meters / day + const auto& gsatprod = schedule[0].gsatprod.get(); + BOOST_CHECK_EQUAL(gsatprod.size(), 1U); + BOOST_CHECK(!gsatprod.has("G1")); + BOOST_CHECK(gsatprod.has("G2")); + const GSatProd::GSatProdGroup& group = gsatprod.get("G2"); + BOOST_CHECK_EQUAL(group.oil_rate, 1000*metric_to_si); + BOOST_CHECK_EQUAL(group.water_rate, 0.0); +} + BOOST_AUTO_TEST_CASE(TESTGCONSALE) { const std::string input = R"( START -- 0 diff --git a/tests/parser/ScheduleSerializeTest.cpp b/tests/parser/ScheduleSerializeTest.cpp index bbf6bb7d5f6..4609dd9e704 100644 --- a/tests/parser/ScheduleSerializeTest.cpp +++ b/tests/parser/ScheduleSerializeTest.cpp @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -222,6 +223,8 @@ SCHEDULE GRUPTREE 'G1' 'FIELD' / 'G2' 'FIELD' / + 'G4' 'FIELD' / + 'G5' 'FIELD' / / GCONSALE @@ -238,6 +241,10 @@ GCONSUMP 'G2' 30 60 / / +GSATPROD +'G4' 20 / +'G5' 30 / +/ DATES -- 1 10 JUN 2007 / / @@ -263,6 +270,11 @@ GCONSUMP 'G2' 10 77 / / +GSATPROD +'G4' 40 / +'G5' 60 / +/ + DATES -- 4 10 JUL 2007 / / @@ -454,6 +466,28 @@ BOOST_AUTO_TEST_CASE(SerializeGCONSUMP) { BOOST_CHECK( gconsump2 == sched0[5].gconsump()); } +BOOST_AUTO_TEST_CASE(SerializeGSatProd) { + auto sched = make_schedule(GCONSALE_deck); + auto sched0 = make_schedule(deck0); + auto gsatprod1 = sched[0].gsatprod.get(); + auto gsatprod2 = sched[3].gsatprod.get(); + + { + std::vector value_list; + std::vector index_list; + sched.pack_state( value_list, index_list ); + BOOST_CHECK_EQUAL( value_list.size(), 2 ); + sched0.unpack_state( value_list, index_list ); + } + + BOOST_CHECK( gsatprod1 == sched0[0].gsatprod()); + BOOST_CHECK( gsatprod1 == sched0[1].gsatprod()); + BOOST_CHECK( gsatprod1 == sched0[2].gsatprod()); + + BOOST_CHECK( gsatprod2 == sched0[3].gsatprod()); + BOOST_CHECK( gsatprod2 == sched0[4].gsatprod()); + BOOST_CHECK( gsatprod2 == sched0[5].gsatprod()); +} BOOST_AUTO_TEST_CASE(SerializeVFP) { auto sched = make_schedule(VFP_deck1); @@ -488,7 +522,7 @@ BOOST_AUTO_TEST_CASE(SerializeGROUPS) { std::vector value_list; std::vector index_list; sched.pack_map( value_list, index_list ); - BOOST_CHECK_EQUAL( value_list.size(), 5 ); + BOOST_CHECK_EQUAL( value_list.size(), 7 ); sched0.unpack_map( value_list, index_list ); } diff --git a/tests/test_Serialization.cpp b/tests/test_Serialization.cpp index 94b9debeb3c..fb3f735c004 100644 --- a/tests/test_Serialization.cpp +++ b/tests/test_Serialization.cpp @@ -93,6 +93,7 @@ #include #include #include +#include #include #include #include @@ -255,6 +256,7 @@ TEST_FOR_TYPE(FoamConfig) TEST_FOR_TYPE(FoamData) TEST_FOR_TYPE(GConSale) TEST_FOR_TYPE(GConSump) +TEST_FOR_TYPE(GSatProd) TEST_FOR_TYPE(GroupEconProductionLimits) TEST_FOR_TYPE(GridDims) TEST_FOR_TYPE(Group)