From 675858f1e0458e37ae397c0df1dadc78a188c69a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20Petter=20=C3=98ren=20Hauge?= Date: Thu, 3 May 2018 13:20:04 +0200 Subject: [PATCH] Handle wildcard in group keywords Added function getGroups(pattern) to allow records with wildcard. Included the functionality for GCONPROD, GCONINJE and GEFAC - currently the only group keywords that should accept wildcards. --- .../eclipse/EclipseState/Schedule/Group.hpp | 1 + .../EclipseState/Schedule/Schedule.hpp | 4 +- opm/parser/eclipse/Parser/ParseContext.hpp | 2 +- .../EclipseState/Schedule/Completion.cpp | 2 +- .../eclipse/EclipseState/Schedule/Group.cpp | 5 + .../EclipseState/Schedule/Schedule.cpp | 145 ++++++++++-------- .../parser/eclipse/Parser/ParseContext.cpp | 4 +- tests/parser/ParseContextTests.cpp | 2 +- tests/parser/ScheduleTests.cpp | 61 ++++++++ 9 files changed, 160 insertions(+), 66 deletions(-) diff --git a/opm/parser/eclipse/EclipseState/Schedule/Group.hpp b/opm/parser/eclipse/EclipseState/Schedule/Group.hpp index 5e199cdce..6184d5f0b 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Group.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Group.hpp @@ -120,6 +120,7 @@ namespace Opm { bool getTransferGroupEfficiencyFactor(size_t time_step) const; void setGroupNetVFPTable(size_t time_step, int table); int getGroupNetVFPTable(size_t time_step) const; + static bool groupNameInGroupNamePattern(const std::string& groupName, const std::string& groupNamePattern); /*****************************************************************/ diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index c87d13858..80921f4ca 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -105,7 +105,7 @@ namespace Opm std::vector< const Group* > getGroups() const; const Tuning& getTuning() const; const MessageLimits& getMessageLimits() const; - void InvalidWellPattern (const std::string& wellNamePattern, const ParseContext& parseContext, const DeckKeyword& keyword) const; + void invalidNamePattern (const std::string& namePattern, const ParseContext& parseContext, const DeckKeyword& keyword) const; const Events& getEvents() const; const Deck& getModifierDeck(size_t timeStep) const; @@ -137,6 +137,8 @@ namespace Opm WellProducer::ControlModeEnum m_controlModeWHISTCTL; std::vector< Well* > getWells(const std::string& wellNamePattern); + std::vector< Group* > getGroups(const std::string& groupNamePattern); + void updateWellStatus( Well& well, size_t reportStep , WellCommon::StatusEnum status); void addWellToGroup( Group& newGroup , Well& well , size_t timeStep); void iterateScheduleSection(const ParseContext& parseContext , const SCHEDULESection& , const EclipseGrid& grid, diff --git a/opm/parser/eclipse/Parser/ParseContext.hpp b/opm/parser/eclipse/Parser/ParseContext.hpp index c2d75803a..284128f9d 100644 --- a/opm/parser/eclipse/Parser/ParseContext.hpp +++ b/opm/parser/eclipse/Parser/ParseContext.hpp @@ -248,7 +248,7 @@ namespace Opm { (e.g. COMPDAT) to be able to set control mode (e.g. WCONPROD). A well missing specification and/or completion(s) will throw. */ - const static std::string SCHEDULE_INVALID_WELL; + const static std::string SCHEDULE_INVALID_NAME; private: void initDefault(); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp index d04b49d4b..a730f2726 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Completion.cpp @@ -210,7 +210,7 @@ namespace Opm { const auto& matched_wells = schedule.getWellsMatching(wellNamePattern); if (matched_wells.empty()) - schedule.InvalidWellPattern(wellNamePattern, parseContext, compdatKeyword); + schedule.invalidNamePattern(wellNamePattern, parseContext, compdatKeyword); for (const auto& well : matched_wells){ const auto it_pos = std::find( wells.begin(), wells.end(), well); diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Group.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Group.cpp index 880d92528..4f2252d7c 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Group.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Group.cpp @@ -267,6 +267,11 @@ namespace Opm { return m_groupNetVFPTable.get(time_step); } + bool Group::groupNameInGroupNamePattern(const std::string& groupName, const std::string& groupNamePattern) { + if (util_fnmatch( groupNamePattern.c_str() , groupName.c_str()) == 0) + return true; + return false; + } /*****************************************************************/ bool Group::hasWell(const std::string& wellName , size_t time_step) const { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index e06626b95..29e72b363 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -485,7 +485,7 @@ namespace Opm { auto wells = getWells(wellNamePattern); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells ) { WellProductionProperties properties; @@ -618,7 +618,7 @@ namespace Opm { auto wells = getWells(wellNamePattern); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells) { WellInjector::TypeEnum injectorType = WellInjector::TypeFromString( record.getItem("TYPE").getTrimmedString(0) ); @@ -701,7 +701,7 @@ namespace Opm { const auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells) { WellPolymerProperties properties(well->getPolymerPropertiesCopy(currentStep)); @@ -733,7 +733,7 @@ namespace Opm { const auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells ) { well->setEconProductionLimits(currentStep, econ_production_limits); @@ -748,7 +748,7 @@ namespace Opm { const auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells ) { well->setEfficiencyFactor(currentStep, efficiencyFactor); @@ -764,7 +764,7 @@ namespace Opm { const auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern(wellNamePattern, parseContext, keyword); + invalidNamePattern(wellNamePattern, parseContext, keyword); for( auto* well : wells) { WellInjectionProperties injectionProperties = well->getInjectionProperties( currentStep ); @@ -784,7 +784,7 @@ namespace Opm { auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern( wellNamePattern, parseContext, keyword); + invalidNamePattern( wellNamePattern, parseContext, keyword); for (auto* well : wells) { // TODO: Is this the right approach? Setting the well temperature only @@ -810,7 +810,7 @@ namespace Opm { auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern( wellNamePattern, parseContext, keyword); + invalidNamePattern( wellNamePattern, parseContext, keyword); for (auto* well : wells) { // TODO: Is this the right approach? Setting the well temperature only @@ -842,7 +842,7 @@ namespace Opm { auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern( wellNamePattern, parseContext, keyword); + invalidNamePattern( wellNamePattern, parseContext, keyword); for (auto* well : wells) { updateWellStatus( *well, currentStep, status ); @@ -940,7 +940,7 @@ namespace Opm { auto wells = getWells( wellNamePattern ); if (wells.empty()) - InvalidWellPattern( wellNamePattern, parseContext, keyword); + invalidNamePattern( wellNamePattern, parseContext, keyword); /* if all records are defaulted or just the status is set, only * well status is updated @@ -1039,7 +1039,7 @@ namespace Opm { const auto wells = getWells( wellNamePattern ); if( wells.empty() ) - InvalidWellPattern( wellNamePattern, parseContext, keyword); + invalidNamePattern( wellNamePattern, parseContext, keyword); for( auto* well : wells ) { if(well->isProducer(currentStep)){ @@ -1143,67 +1143,74 @@ namespace Opm { void Schedule::handleGCONINJE( const SCHEDULESection& section, const DeckKeyword& keyword, size_t currentStep) { for( const auto& record : keyword ) { - const std::string& groupName = record.getItem("GROUP").getTrimmedString(0); - auto& group = this->m_groups.at( groupName ); + const std::string& groupNamePattern = record.getItem("GROUP").getTrimmedString(0); + auto groups = getGroups ( groupNamePattern ); - { - Phase phase = get_phase( record.getItem("PHASE").getTrimmedString(0) ); - group.setInjectionPhase( currentStep , phase ); + for (auto* group : groups){ + { + 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)); + + // calculate SI injection rates for the group + double surfaceInjectionRate = record.getItem("SURFACE_TARGET").get< double >(0); + surfaceInjectionRate = convertInjectionRateToSI(surfaceInjectionRate, wellPhase, section.unitSystem()); + double reservoirInjectionRate = record.getItem("RESV_TARGET").getSIDouble(0); + + group->setSurfaceMaxRate( currentStep , surfaceInjectionRate); + group->setReservoirMaxRate( currentStep , reservoirInjectionRate); + group->setTargetReinjectFraction( currentStep , record.getItem("REINJ_TARGET").getSIDouble(0)); + group->setTargetVoidReplacementFraction( currentStep , record.getItem("VOIDAGE_TARGET").getSIDouble(0)); + + group->setInjectionGroup(currentStep, true); } - { - GroupInjection::ControlEnum controlMode = GroupInjection::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); - group.setInjectionControlMode( currentStep , controlMode ); - } - - Phase wellPhase = get_phase( record.getItem("PHASE").getTrimmedString(0)); - - // calculate SI injection rates for the group - double surfaceInjectionRate = record.getItem("SURFACE_TARGET").get< double >(0); - surfaceInjectionRate = convertInjectionRateToSI(surfaceInjectionRate, wellPhase, section.unitSystem()); - double reservoirInjectionRate = record.getItem("RESV_TARGET").getSIDouble(0); - - group.setSurfaceMaxRate( currentStep , surfaceInjectionRate); - group.setReservoirMaxRate( currentStep , reservoirInjectionRate); - group.setTargetReinjectFraction( currentStep , record.getItem("REINJ_TARGET").getSIDouble(0)); - group.setTargetVoidReplacementFraction( currentStep , record.getItem("VOIDAGE_TARGET").getSIDouble(0)); - - group.setInjectionGroup(currentStep, true); } } void Schedule::handleGCONPROD( const DeckKeyword& keyword, size_t currentStep) { for( const auto& record : keyword ) { - const std::string& groupName = record.getItem("GROUP").getTrimmedString(0); - auto& group = this->m_groups.at( groupName ); - { - GroupProduction::ControlEnum controlMode = GroupProduction::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); - group.setProductionControlMode( currentStep , controlMode ); - } - group.setOilTargetRate( currentStep , record.getItem("OIL_TARGET").getSIDouble(0)); - group.setGasTargetRate( currentStep , record.getItem("GAS_TARGET").getSIDouble(0)); - group.setWaterTargetRate( currentStep , record.getItem("WATER_TARGET").getSIDouble(0)); - group.setLiquidTargetRate( currentStep , record.getItem("LIQUID_TARGET").getSIDouble(0)); - 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 ); - } + const std::string& groupNamePattern = record.getItem("GROUP").getTrimmedString(0); + auto groups = getGroups ( groupNamePattern ); - group.setProductionGroup(currentStep, true); + for (auto* group : groups){ + { + GroupProduction::ControlEnum controlMode = GroupProduction::ControlEnumFromString( record.getItem("CONTROL_MODE").getTrimmedString(0) ); + group->setProductionControlMode( currentStep , controlMode ); + } + group->setOilTargetRate( currentStep , record.getItem("OIL_TARGET").getSIDouble(0)); + group->setGasTargetRate( currentStep , record.getItem("GAS_TARGET").getSIDouble(0)); + group->setWaterTargetRate( currentStep , record.getItem("WATER_TARGET").getSIDouble(0)); + group->setLiquidTargetRate( currentStep , record.getItem("LIQUID_TARGET").getSIDouble(0)); + 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 ); + } + + group->setProductionGroup(currentStep, true); + } } } void Schedule::handleGEFAC( const DeckKeyword& keyword, size_t currentStep) { for( const auto& record : keyword ) { - const std::string& groupName = record.getItem("GROUP").getTrimmedString(0); - auto& group = this->m_groups.at( groupName ); + const std::string& groupNamePattern = record.getItem("GROUP").getTrimmedString(0); + auto groups = getGroups ( groupNamePattern ); - group.setGroupEfficiencyFactor(currentStep, record.getItem("EFFICIENCY_FACTOR").get< double >(0)); + for (auto* group : groups){ + group->setGroupEfficiencyFactor(currentStep, record.getItem("EFFICIENCY_FACTOR").get< double >(0)); - const std::string& transfer_str = record.getItem("TRANSFER_EXT_NET").getTrimmedString(0); - bool transfer = (transfer_str == "YES") ? true : false; - group.setTransferGroupEfficiencyFactor(currentStep, transfer); + const std::string& transfer_str = record.getItem("TRANSFER_EXT_NET").getTrimmedString(0); + bool transfer = (transfer_str == "YES") ? true : false; + group->setTransferGroupEfficiencyFactor(currentStep, transfer); + } } } @@ -1491,10 +1498,10 @@ namespace Opm { } } - void Schedule::InvalidWellPattern( const std::string& wellNamePattern, const ParseContext& parseContext, const DeckKeyword& keyword ) const { - std::string msg = "Error when handling " + keyword.name() +". No wells match " + - wellNamePattern; - parseContext.handleError( ParseContext::SCHEDULE_INVALID_WELL, msg ); + void Schedule::invalidNamePattern( const std::string& namePattern, const ParseContext& parseContext, const DeckKeyword& keyword ) const { + std::string msg = "Error when handling " + keyword.name() +". No names match " + + namePattern; + parseContext.handleError( ParseContext::SCHEDULE_INVALID_NAME, msg ); } const TimeMap& Schedule::getTimeMap() const { @@ -1680,6 +1687,24 @@ namespace Opm { return groups; } + std::vector< Group* > Schedule::getGroups(const std::string& groupNamePattern) { + size_t wildcard_pos = groupNamePattern.find("*"); + + if( wildcard_pos != groupNamePattern.length()-1 ) { + if( !m_groups.hasKey( groupNamePattern ) ) return {}; + return { std::addressof( m_groups.get( groupNamePattern ) ) }; + } + + std::vector< Group* > groups; + for( auto& group : this->m_groups ) { + if( Group::groupNameInGroupNamePattern( group.name(), groupNamePattern ) ) { + groups.push_back( std::addressof( group ) ); + } + } + + return groups; + } + void Schedule::addWellToGroup( Group& newGroup, Well& well , size_t timeStep) { const std::string currentGroupName = well.getGroupName(timeStep); if (currentGroupName != "") { diff --git a/src/opm/parser/eclipse/Parser/ParseContext.cpp b/src/opm/parser/eclipse/Parser/ParseContext.cpp index 62be4f150..8fb06c8cb 100644 --- a/src/opm/parser/eclipse/Parser/ParseContext.cpp +++ b/src/opm/parser/eclipse/Parser/ParseContext.cpp @@ -85,7 +85,7 @@ namespace Opm { addKey(SUMMARY_UNKNOWN_WELL, InputError::THROW_EXCEPTION); addKey(SUMMARY_UNKNOWN_GROUP, InputError::THROW_EXCEPTION); - addKey(SCHEDULE_INVALID_WELL, InputError::THROW_EXCEPTION); + addKey(SCHEDULE_INVALID_NAME, InputError::THROW_EXCEPTION); } void ParseContext::initEnv() { @@ -259,7 +259,7 @@ namespace Opm { const std::string ParseContext::SUMMARY_UNKNOWN_WELL = "SUMMARY_UNKNOWN_WELL"; const std::string ParseContext::SUMMARY_UNKNOWN_GROUP = "SUMMARY_UNKNOWN_GROUP"; - const std::string ParseContext::SCHEDULE_INVALID_WELL = "SCHEDULE_INVALID_WELL"; + const std::string ParseContext::SCHEDULE_INVALID_NAME = "SCHEDULE_INVALID_NAME"; } diff --git a/tests/parser/ParseContextTests.cpp b/tests/parser/ParseContextTests.cpp index c4e2c9987..dc58807bc 100644 --- a/tests/parser/ParseContextTests.cpp +++ b/tests/parser/ParseContextTests.cpp @@ -502,7 +502,7 @@ BOOST_AUTO_TEST_CASE( test_invalid_wtemplate_config ) { ParseContext parseContext; Parser parser; - parseContext.update(ParseContext::SCHEDULE_INVALID_WELL , InputError::THROW_EXCEPTION ); + parseContext.update(ParseContext::SCHEDULE_INVALID_NAME , InputError::THROW_EXCEPTION ); std::vector < std::string > testSamples; std::string testSample; diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index fdffef5d5..8af16f8e6 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -1022,6 +1022,67 @@ BOOST_AUTO_TEST_CASE(createDeckWithWPIMULT) { } } +BOOST_AUTO_TEST_CASE(createDeckModifyMultipleGCONPROD) { + Opm::Parser parser; + const std::string input = R"( + START -- 0 + 10 'JAN' 2000 / + RUNSPEC + DIMENS + 10 10 10 / + GRID + DX + 1000*0.25 / + DY + 1000*0.25 / + DZ + 1000*0.25 / + TOPS + 100*0.25 / + SCHEDULE + DATES -- 1 + 10 OKT 2008 / + / + WELSPECS + 'PROD1' 'G1' 1 1 10 'OIL' / + 'PROD2' 'G2' 2 2 10 'OIL' / + 'PROD3' 'H1' 3 3 10 'OIL' / + / + GCONPROD + 'G1' 'ORAT' 1000 / + / + DATES -- 2 + 10 NOV 2008 / + / + GCONPROD + 'G*' 'ORAT' 2000 / + / + )"; + + Opm::ParseContext parseContext; + auto deck = parser.parseString(input, parseContext); + EclipseGrid grid( deck ); + TableManager table ( deck ); + Eclipse3DProperties eclipseProperties ( deck , table, grid); + Opm::Schedule schedule(deck, grid, eclipseProperties, Opm::Phases(true, true, true) , parseContext); + + Opm::UnitSystem unitSystem = deck.getActiveUnitSystem(); + double siFactorL = unitSystem.parse("LiquidSurfaceVolume/Time").getSIScaling(); + + auto g_g1 = schedule.getGroup("G1"); + BOOST_CHECK_EQUAL(g_g1.getOilTargetRate(1), 1000 * siFactorL); + BOOST_CHECK_EQUAL(g_g1.getOilTargetRate(2), 2000 * siFactorL); + + auto g_g2 = schedule.getGroup("G2"); + BOOST_CHECK_EQUAL(g_g2.getOilTargetRate(1), -999e100); // Invalid group rate - default + BOOST_CHECK_EQUAL(g_g2.getOilTargetRate(2), 2000 * siFactorL); + + auto g_h1 = schedule.getGroup("H1"); + BOOST_CHECK_EQUAL(g_h1.getOilTargetRate(0), -999e100); + BOOST_CHECK_EQUAL(g_h1.getOilTargetRate(1), -999e100); + BOOST_CHECK_EQUAL(g_h1.getOilTargetRate(2), -999e100); +} + BOOST_AUTO_TEST_CASE(createDeckWithDRSDT) { Opm::Parser parser; std::string input =