diff --git a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp index df7095693..af84e57ff 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Schedule.hpp @@ -194,7 +194,6 @@ namespace Opm DynamicState> udq_config; RFTConfig rft_config; - WellProducer::ControlModeEnum m_controlModeWHISTCTL; Actions m_actions; std::vector< Well* > getWells(const std::string& wellNamePattern, const std::vector& matching_wells = {}); @@ -248,7 +247,7 @@ namespace Opm void handleDRVDTR( const DeckKeyword& keyword, size_t currentStep); void handleVAPPARS( const DeckKeyword& keyword, size_t currentStep); void handleWECON( const DeckKeyword& keyword, size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); - void handleWHISTCTL(const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& keyword); + void handleWHISTCTL(const DeckKeyword& keyword, std::size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors); void handleMESSAGES(const DeckKeyword& keyword, size_t currentStep); void handleVFPPROD(const DeckKeyword& vfpprodKeyword, const UnitSystem& unit_system, size_t currentStep); void handleVFPINJ(const DeckKeyword& vfpprodKeyword, const UnitSystem& unit_system, size_t currentStep); diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp index 02e11441f..01f270823 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.hpp @@ -48,19 +48,12 @@ namespace Opm { int VFPTableNumber = 0; double ALQValue = 0.0; bool predictionMode = false; - WellProducer::ControlModeEnum controlMode = WellProducer::CMODE_UNDEFINED; + WellProducer::ControlModeEnum whistctl_cmode = WellProducer::CMODE_UNDEFINED; bool operator==(const WellProductionProperties& other) const; bool operator!=(const WellProductionProperties& other) const; WellProductionProperties(); - WellProductionProperties (const DeckRecord& record, - bool prediction_mode, - const WellProductionProperties& prev_properties, - WellProducer::ControlModeEnum controlModeWHISTCTL, - bool switching_from_injector, - bool addGrupProductionControl); - bool hasProductionControl(WellProducer::ControlModeEnum controlModeArg) const { return (m_productionControls & controlModeArg) != 0; @@ -78,21 +71,18 @@ namespace Opm { // this is used to check whether the specified control mode is an effective history matching production mode static bool effectiveHistoryProductionControl(const WellProducer::ControlModeEnum cmode); + void handleWCONPROD( const DeckRecord& record); + void handleWCONHIST( const DeckRecord& record); + void resetDefaultBHPLimit(); private: int m_productionControls = 0; void init_rates( const DeckRecord& record ); - void init_history(const WellProductionProperties& prevProperties, - const DeckRecord& record, - const WellProducer::ControlModeEnum controlModeWHISTCL, - const bool switching_from_injector); + void init_history(const DeckRecord& record); - void init_prediction( const DeckRecord& record, bool addGroupProductionControl ); WellProductionProperties(const DeckRecord& record); - void resetDefaultBHPLimit(); - void setBHPLimit(const double limit); double getBHPLimit() const; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp index 3cbd1192e..92aa15711 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Schedule.cpp @@ -89,7 +89,6 @@ namespace Opm { udq_config(this->m_timeMap, std::make_shared(deck)), rft_config(this->m_timeMap) { - m_controlModeWHISTCTL = WellProducer::CMODE_UNDEFINED; addGroup( "FIELD", 0 ); /* @@ -230,7 +229,7 @@ namespace Opm { handleWELSPECS( section, keywordIdx, currentStep ); else if (keyword.name() == "WHISTCTL") - handleWHISTCTL(parseContext, errors, keyword); + handleWHISTCTL(keyword, currentStep, parseContext, errors); else if (keyword.name() == "WCONHIST") handleWCONHIST(keyword, currentStep, parseContext, errors); @@ -426,27 +425,36 @@ namespace Opm { return true; } - void Schedule::handleWHISTCTL(const ParseContext& parseContext, ErrorGuard& errors, const DeckKeyword& keyword) { - for( const auto& record : keyword ) { - const std::string& cmodeString = record.getItem("CMODE").getTrimmedString(0); - const WellProducer::ControlModeEnum controlMode = WellProducer::ControlModeFromString( cmodeString ); - if (controlMode != WellProducer::NONE) { - if (!WellProductionProperties::effectiveHistoryProductionControl(controlMode) ) { - std::string msg = "The WHISTCTL keyword specifies an un-supported control mode " + cmodeString - + ", which makes WHISTCTL keyword not affect the simulation at all"; - OpmLog::warning(msg); - } + void Schedule::handleWHISTCTL(const DeckKeyword& keyword, std::size_t currentStep, const ParseContext& parseContext, ErrorGuard& errors) { + const auto& record = keyword.getRecord(0); + const std::string& cmodeString = record.getItem("CMODE").getTrimmedString(0); + const WellProducer::ControlModeEnum controlMode = WellProducer::ControlModeFromString( cmodeString ); + + if (controlMode != WellProducer::NONE) { + if (!WellProductionProperties::effectiveHistoryProductionControl(controlMode) ) { + std::string msg = "The WHISTCTL keyword specifies an un-supported control mode " + cmodeString + + ", which makes WHISTCTL keyword not affect the simulation at all"; + OpmLog::warning(msg); } + } - m_controlModeWHISTCTL = controlMode; - const std::string bhp_terminate = record.getItem("BPH_TERMINATE").getTrimmedString(0); - if (bhp_terminate == "YES") { - std::string msg = "The WHISTCTL keyword does not handle 'YES'. i.e. to terminate the run"; - OpmLog::error(msg); - parseContext.handleError( ParseContext::UNSUPPORTED_TERMINATE_IF_BHP , msg, errors ); + const std::string bhp_terminate = record.getItem("BPH_TERMINATE").getTrimmedString(0); + if (bhp_terminate == "YES") { + std::string msg = "The WHISTCTL keyword does not handle 'YES'. i.e. to terminate the run"; + OpmLog::error(msg); + parseContext.handleError( ParseContext::UNSUPPORTED_TERMINATE_IF_BHP , msg, errors ); + } + + + for (auto& well_pair : this->m_wells) { + auto& well = well_pair.second; + const WellProductionProperties& properties(well.getProductionProperties(currentStep)); + if (properties.whistctl_cmode != controlMode) { + WellProductionProperties new_properties( properties ); + new_properties.whistctl_cmode = controlMode; + well.setProductionProperties(currentStep, new_properties); } - } } @@ -659,23 +667,42 @@ namespace Opm { for( const auto& well_name : well_names) { auto& well = this->m_wells.at(well_name); - bool addGrupProductionControl = well.isAvailableForGroupControl(currentStep); - bool switching_from_injector = !well.isProducer(currentStep); - const WellProductionProperties& prev_properties = well.getProductionProperties(currentStep); - WellProductionProperties properties(record, isPredictionMode, prev_properties, m_controlModeWHISTCTL, switching_from_injector, addGrupProductionControl); updateWellStatus( well , currentStep , status ); - if (well.setProductionProperties(currentStep, properties)) { - m_events.addEvent( ScheduleEvents::PRODUCTION_UPDATE , currentStep); - this->addWellEvent( well.name(), ScheduleEvents::PRODUCTION_UPDATE, currentStep); - } - if ( !well.getAllowCrossFlow() && !isPredictionMode && (properties.OilRate + properties.WaterRate + properties.GasRate) == 0 ) { + if (isPredictionMode) { + bool addGrupProductionControl = well.isAvailableForGroupControl(currentStep); + WellProductionProperties properties; - std::string msg = + if (addGrupProductionControl) + properties.addProductionControl(WellProducer::GRUP); + + properties.handleWCONPROD(record); + + if (well.setProductionProperties(currentStep, properties)) { + m_events.addEvent( ScheduleEvents::PRODUCTION_UPDATE , currentStep); + this->addWellEvent( well.name(), ScheduleEvents::PRODUCTION_UPDATE, currentStep); + } + } else { + bool switching_from_injector = !well.isProducer(currentStep); + WellProductionProperties properties(well.getProductionProperties(currentStep)); + properties.handleWCONHIST(record); + + if (switching_from_injector) + properties.resetDefaultBHPLimit(); + + if (well.setProductionProperties(currentStep, properties)) { + m_events.addEvent( ScheduleEvents::PRODUCTION_UPDATE , currentStep); + this->addWellEvent( well.name(), ScheduleEvents::PRODUCTION_UPDATE, currentStep); + } + + if ( !well.getAllowCrossFlow() && !isPredictionMode && (properties.OilRate + properties.WaterRate + properties.GasRate) == 0 ) { + + std::string msg = "Well " + well.name() + " is a history matched well with zero rate where crossflow is banned. " + "This well will be closed at " + std::to_string ( m_timeMap.getTimePassedUntil(currentStep) / (60*60*24) ) + " days"; - OpmLog::note(msg); - updateWellStatus( well, currentStep, WellCommon::StatusEnum::SHUT ); + OpmLog::note(msg); + updateWellStatus( well, currentStep, WellCommon::StatusEnum::SHUT ); + } } } } diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.cpp index 0e03b1544..7d502dbf2 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Well/WellProductionProperties.cpp @@ -36,16 +36,13 @@ namespace Opm { void WellProductionProperties::init_rates( const DeckRecord& record ) { - this->OilRate = ( record.getItem( "ORAT" ).getSIDouble( 0 ) ); - this->WaterRate = ( record.getItem( "WRAT" ).getSIDouble( 0 ) ); - this->GasRate = ( record.getItem( "GRAT" ).getSIDouble( 0 ) ); + this->OilRate = record.getItem("ORAT").getSIDouble(0); + this->WaterRate = record.getItem("WRAT").getSIDouble(0); + this->GasRate = record.getItem("GRAT").getSIDouble(0); } - void WellProductionProperties::init_history(const WellProductionProperties& prev_properties, - const DeckRecord& record, - const WellProducer::ControlModeEnum controlModeWHISTCL, - const bool switching_from_injector) + void WellProductionProperties::init_history(const DeckRecord& record) { this->predictionMode = false; // update LiquidRate @@ -63,14 +60,14 @@ namespace Opm { } namespace wp = WellProducer; - auto cmode = wp::ControlModeFromString( cmodeItem.getTrimmedString( 0 ) ); + auto cmode = wp::CMODE_UNDEFINED; - // when there is an effective control mode specified by WHISTCL, we always use this control mode - if (effectiveHistoryProductionControl(controlModeWHISTCL) ) { - cmode = controlModeWHISTCL; - } + if (effectiveHistoryProductionControl(this->whistctl_cmode) ) + cmode = this->whistctl_cmode; + else + cmode = wp::ControlModeFromString( cmodeItem.getTrimmedString( 0 ) ); - if (effectiveHistoryProductionControl(cmode) ) { + if (effectiveHistoryProductionControl(cmode)) { this->addProductionControl( cmode ); this->controlMode = cmode; } else { @@ -84,44 +81,30 @@ namespace Opm { if ( !this->hasProductionControl( wp::BHP ) ) this->addProductionControl( wp::BHP ); - if (cmode == wp::BHP) { + if (cmode == wp::BHP) this->setBHPLimit(this->BHPH); - } else { - // when the well is switching to history matching producer from prediction mode - // or switching from injector to producer - // or switching from BHP control to RATE control (under history matching mode) - // we use the defaulted BHP limit, otherwise, we use the previous BHP limit - if ( prev_properties.predictionMode || switching_from_injector - || prev_properties.controlMode == wp::BHP ) { - this->resetDefaultBHPLimit(); - } else { - this->setBHPLimit(prev_properties.getBHPLimit()); - } - } - this->VFPTableNumber = record.getItem("VFPTable").get< int >(0); + const auto vfp_table = record.getItem("VFPTable").get< int >(0); + if (vfp_table != 0) + this->VFPTableNumber = vfp_table; - if (this->VFPTableNumber == 0) - this->VFPTableNumber = prev_properties.VFPTableNumber; - - this->ALQValue = record.getItem("Lift").get< double >(0); //NOTE: Unit of ALQ is never touched - - if (this->ALQValue == 0.) - this->ALQValue = prev_properties.ALQValue; + auto alq_value = record.getItem("Lift").get< double >(0); //NOTE: Unit of ALQ is never touched + if (alq_value != 0.) + this->ALQValue = alq_value; } - void WellProductionProperties::init_prediction( const DeckRecord& record, bool addGroupProductionControl) + void WellProductionProperties::handleWCONPROD( const DeckRecord& record ) { this->predictionMode = true; - this->LiquidRate = record.getItem("LRAT" ).getSIDouble(0); - this->ResVRate = record.getItem("RESV" ).getSIDouble(0); this->BHPLimit = record.getItem("BHP" ).getSIDouble(0); this->THPLimit = record.getItem("THP" ).getSIDouble(0); this->ALQValue = record.getItem("ALQ" ).get< double >(0); //NOTE: Unit of ALQ is never touched this->VFPTableNumber = record.getItem("VFP_TABLE").get< int >(0); + this->LiquidRate = record.getItem("LRAT").getSIDouble(0); + this->ResVRate = record.getItem("RESV").getSIDouble(0); namespace wp = WellProducer; using mode = std::pair< const std::string, wp::ControlModeEnum >; @@ -130,6 +113,8 @@ namespace Opm { { "LRAT", wp::LRAT }, { "RESV", wp::RESV }, { "THP", wp::THP } }; + + this->init_rates(record); for( const auto& cmode : modes ) { if( !record.getItem( cmode.first ).defaultApplied( 0 ) ) { @@ -143,12 +128,6 @@ namespace Opm { // There is always a BHP constraint, when not specified, will use the default value this->addProductionControl( wp::BHP ); - - if (addGroupProductionControl) { - this->addProductionControl(WellProducer::GRUP); - } - - { const auto& cmodeItem = record.getItem("CMODE"); if (cmodeItem.hasValue(0)) { @@ -157,26 +136,37 @@ namespace Opm { if (this->hasProductionControl( cmode )) this->controlMode = cmode; else - throw std::invalid_argument("Setting CMODE to unspecified control"); + throw std::invalid_argument("Trying to set CMODE to: " + cmodeItem.getTrimmedString(0) + " - no value has been specified for this control"); } } } - WellProductionProperties::WellProductionProperties(const DeckRecord& record, - bool prediction_mode, - const WellProductionProperties& prev_properties, - WellProducer::ControlModeEnum controlModeWHISTCTL, - bool switching_from_injector, - bool addGrupProductionControl) + /* + This is now purely "history" constructor - i.e. the record should + originate from the WCONHIST keyword. Predictions are handled with the + default constructor and the handleWCONPROD() method. + */ + void WellProductionProperties::handleWCONHIST(const DeckRecord& record) { this->init_rates(record); - if (prediction_mode) - this->init_prediction(record, addGrupProductionControl); - else - this->init_history(prev_properties, record, controlModeWHISTCTL, switching_from_injector); + this->LiquidRate = 0; + this->ResVRate = 0; + + // when the well is switching to history matching producer from prediction mode + // or switching from injector to producer + // or switching from BHP control to RATE control (under history matching mode) + // we use the defaulted BHP limit, otherwise, we use the previous BHP limit + if (this->predictionMode) + this->resetDefaultBHPLimit(); + + if (this->controlMode == WellProducer::BHP) + this->resetDefaultBHPLimit(); + + this->init_history(record); } + bool WellProductionProperties::operator==(const WellProductionProperties& other) const { return OilRate == other.OilRate && WaterRate == other.WaterRate @@ -190,6 +180,7 @@ namespace Opm { && VFPTableNumber == other.VFPTableNumber && controlMode == other.controlMode && m_productionControls == other.m_productionControls + && whistctl_cmode == other.whistctl_cmode && this->predictionMode == other.predictionMode; } diff --git a/tests/parser/WellTests.cpp b/tests/parser/WellTests.cpp index baa8139dd..e604fe5a1 100644 --- a/tests/parser/WellTests.cpp +++ b/tests/parser/WellTests.cpp @@ -745,33 +745,15 @@ namespace { return input; } - std::string whistctl() { - const std::string input = - "WHISTCTL\n" - "ORAT /\n" - "WCONHIST\n" - "-- 1 2 3 4-6 7 8 9 10\n" - " 'P' 'OPEN' 'RESV' 3* 3 10. 1* 500/\n/\n"; - - return input; - } Opm::WellProductionProperties properties(const std::string& input) { Opm::Parser parser; auto deck = parser.parseString(input); const auto& record = deck.getKeyword("WCONHIST").getRecord(0); - Opm::WellProductionProperties prev_p; - prev_p.BHPLimit = 100.; - prev_p.VFPTableNumber = 12; - prev_p.ALQValue = 18.; - Opm::WellProducer::ControlModeEnum whistctl_cmode = Opm::WellProducer::NONE; - if (deck.hasKeyword("WHISTCTL") ) { - const auto& whistctl_record = deck.getKeyword("WHISTCTL").getRecord(0); - const std::string& cmode_string = whistctl_record.getItem("CMODE").getTrimmedString(0); - whistctl_cmode = Opm::WellProducer::ControlModeFromString(cmode_string); - } - Opm::WellProductionProperties hist(record, false, prev_p, whistctl_cmode, false, false); + Opm::WellProductionProperties hist; + hist.handleWCONHIST(record); + return hist; } @@ -823,9 +805,8 @@ namespace { auto deck = parser.parseString(input); const auto& kwd = deck.getKeyword("WCONPROD"); const auto& record = kwd.getRecord(0); - Opm::WellProducer::ControlModeEnum whistctl_cmode = Opm::WellProducer::NONE; - Opm::WellProductionProperties prev_properties; - Opm::WellProductionProperties pred( record, true, prev_properties, whistctl_cmode, false, false ); + Opm::WellProductionProperties pred; + pred.handleWCONPROD(record); return pred; } @@ -847,8 +828,6 @@ BOOST_AUTO_TEST_CASE(WCH_All_Specified_BHP_Defaulted) BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::ORAT); BOOST_CHECK(p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } @@ -865,8 +844,6 @@ BOOST_AUTO_TEST_CASE(WCH_ORAT_Defaulted_BHP_Defaulted) BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::WRAT); BOOST_CHECK(p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } @@ -883,8 +860,6 @@ BOOST_AUTO_TEST_CASE(WCH_OWRAT_Defaulted_BHP_Defaulted) BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::GRAT); BOOST_CHECK(p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } @@ -901,8 +876,6 @@ BOOST_AUTO_TEST_CASE(WCH_Rates_Defaulted_BHP_Defaulted) BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::LRAT); BOOST_CHECK(p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } @@ -920,8 +893,6 @@ BOOST_AUTO_TEST_CASE(WCH_Rates_Defaulted_BHP_Specified) BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::RESV); BOOST_CHECK_EQUAL(true, p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } @@ -944,27 +915,6 @@ BOOST_AUTO_TEST_CASE(WCH_Rates_NON_Defaulted_VFP) BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); } -BOOST_AUTO_TEST_CASE(WCH_Whistctl) -{ - const Opm::WellProductionProperties& p = - WCONHIST::properties(WCONHIST::whistctl()); - - // the original RESV contorl in WCONHIST should be overwritten by - // ORAT specified with WHISCTL now. - BOOST_CHECK( p.hasProductionControl(Opm::WellProducer::ORAT)); - BOOST_CHECK( !p.hasProductionControl(Opm::WellProducer::WRAT)); - BOOST_CHECK( !p.hasProductionControl(Opm::WellProducer::GRAT)); - BOOST_CHECK( !p.hasProductionControl(Opm::WellProducer::LRAT)); - BOOST_CHECK( !p.hasProductionControl(Opm::WellProducer::RESV)); - - BOOST_CHECK_EQUAL(p.controlMode , Opm::WellProducer::ORAT); - - BOOST_CHECK_EQUAL(true, p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 3); - BOOST_CHECK_EQUAL(p.ALQValue, 10.); - BOOST_CHECK_EQUAL(p.BHPLimit, 101325.); -} - BOOST_AUTO_TEST_CASE(WCH_BHP_Specified) { const Opm::WellProductionProperties& p = @@ -980,15 +930,12 @@ BOOST_AUTO_TEST_CASE(WCH_BHP_Specified) BOOST_CHECK_EQUAL(true, p.hasProductionControl(Opm::WellProducer::BHP)); - BOOST_CHECK_EQUAL(p.VFPTableNumber, 12); - BOOST_CHECK_EQUAL(p.ALQValue, 18.); BOOST_CHECK_EQUAL(p.BHPLimit, 5.e7); // 500 barsa } BOOST_AUTO_TEST_CASE(WCONPROD_ORAT_CMode) { - const Opm::WellProductionProperties& p = - WCONPROD::properties(WCONPROD::orat_CMODE_other_defaulted()); + const Opm::WellProductionProperties& p = WCONPROD::properties(WCONPROD::orat_CMODE_other_defaulted()); BOOST_CHECK( p.hasProductionControl(Opm::WellProducer::ORAT)); BOOST_CHECK( p.hasProductionControl(Opm::WellProducer::WRAT));