diff --git a/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp b/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp index 3524c3627..c49031c8e 100644 --- a/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp +++ b/opm/parser/eclipse/EclipseState/Schedule/Well/Well.hpp @@ -433,28 +433,6 @@ public: double getBHPLimit() const; }; - struct WellProductivityIndex { - double pi_value; - Phase preferred_phase; - - bool operator==(const WellProductivityIndex& rhs) const - { - return (this->pi_value == rhs.pi_value) - && (this->preferred_phase == rhs.preferred_phase); - } - - bool operator!=(const WellProductivityIndex& rhs) const - { - return ! (*this == rhs); - } - - template - void serializeOp(Serializer& serializer) - { - serializer(this->pi_value); - serializer(this->preferred_phase); - } - }; Well() = default; Well(const std::string& wname, @@ -520,7 +498,6 @@ public: const WellPolymerProperties& getPolymerProperties() const; const WellBrineProperties& getBrineProperties() const; const WellTracerProperties& getTracerProperties() const; - const WellProductivityIndex& getWellProductivityIndex() const; /* The rate of a given phase under the following assumptions: * * Returns zero if production is requested for an injector (and vice * versa) @@ -580,7 +557,7 @@ public: bool updateEconLimits(std::shared_ptr econ_limits); bool updateProduction(std::shared_ptr production); bool updateInjection(std::shared_ptr injection); - bool updateWellProductivityIndex(const WellProductivityIndex& prodIndex); + bool updateWellProductivityIndex(const double prodIndex); bool updateWSEGSICD(const std::vector >& sicd_pairs); bool updateWSEGVALV(const std::vector >& valve_pairs); @@ -639,7 +616,7 @@ public: serializer(has_produced); serializer(has_injected); serializer(prediction_mode); - serializer.optional(productivity_index); + serializer(productivity_index); serializer(econ_limits); serializer(foam_properties); serializer(polymer_properties); @@ -677,7 +654,7 @@ private: bool has_produced = false; bool has_injected = false; bool prediction_mode = true; - std::optional productivity_index{ std::nullopt }; + std::optional productivity_index{ std::nullopt }; std::shared_ptr econ_limits; std::shared_ptr foam_properties; diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp index b023dd355..1c61eeacc 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/KeywordHandlers.cpp @@ -1108,10 +1108,6 @@ namespace { using WELL_NAME = ParserKeywords::WELPI::WELL_NAME; using PI = ParserKeywords::WELPI::STEADY_STATE_PRODUCTIVITY_OR_INJECTIVITY_INDEX_VALUE; - const auto& usys = handlerContext.section.unitSystem(); - const auto gasPI = UnitSystem::measure::gas_productivity_index; - const auto liqPI = UnitSystem::measure::liquid_productivity_index; - for (const auto& record : handlerContext.keyword) { const auto well_names = this->wellNames(record.getItem().getTrimmedString(0), handlerContext.currentStep); @@ -1123,29 +1119,22 @@ namespace { const auto rawProdIndex = record.getItem().get(0); for (const auto& well_name : well_names) { - // All wells in a single record *hopefully* have the same preferred phase... - const auto& well = this->getWell(well_name, handlerContext.currentStep); - const auto preferred = well.getPreferredPhase(); - const auto unitPI = (preferred == Phase::GAS) ? gasPI : liqPI; - - const auto wellPI = Well::WellProductivityIndex { - usys.to_si(unitPI, rawProdIndex), - preferred - }; + auto well2 = std::make_shared(this->getWell(well_name, handlerContext.currentStep)); // Note: Need to ensure we have an independent copy of // well's connections because // Well::updateWellProductivityIndex() implicitly mutates // internal state in the WellConnections class. - auto well2 = std::make_shared(well); auto connections = std::make_shared(well2->getConnections()); well2->forceUpdateConnections(std::move(connections)); - if (well2->updateWellProductivityIndex(wellPI)) + if (well2->updateWellProductivityIndex(rawProdIndex)) this->updateWell(std::move(well2), handlerContext.currentStep); this->addWellGroupEvent(well_name, ScheduleEvents::WELL_PRODUCTIVITY_INDEX, handlerContext.currentStep); } } + + this->m_events.addEvent(ScheduleEvents::WELL_PRODUCTIVITY_INDEX, handlerContext.currentStep); } void Schedule::handleWELSEGS(const HandlerContext& handlerContext, const ParseContext&, ErrorGuard&) { diff --git a/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp b/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp index f330c2d07..df7f862b9 100644 --- a/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp +++ b/src/opm/parser/eclipse/EclipseState/Schedule/Well/Well.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -376,7 +377,7 @@ Well Well::serializeObject() result.efficiency_factor = 8.0; result.solvent_fraction = 9.0; result.prediction_mode = false; - result.productivity_index = WellProductivityIndex { 10.0, Phase::GAS }; + result.productivity_index = 10.0; result.econ_limits = std::make_shared(Opm::WellEconProductionLimits::serializeObject()); result.foam_properties = std::make_shared(WellFoamProperties::serializeObject()); result.polymer_properties = std::make_shared(WellPolymerProperties::serializeObject()); @@ -492,7 +493,7 @@ bool Well::updateInjection(std::shared_ptr injection_ar return update; } -bool Well::updateWellProductivityIndex(const WellProductivityIndex& prodIndex) { +bool Well::updateWellProductivityIndex(const double prodIndex) { const auto update = this->productivity_index != prodIndex; if (update) this->productivity_index = prodIndex; @@ -837,6 +838,32 @@ void Well::setInsertIndex(std::size_t index) { this->insert_index = index; } +namespace { + double convertWellPIToSI(const double rawWellPI, + const Phase preferred_phase, + const UnitSystem& unit_system) + { + using M = UnitSystem::measure; + + // XXX: Should really have LIQUID here too, but the 'Phase' type does + // not provide that enumerator. + switch (preferred_phase) { + case Phase::GAS: + return unit_system.to_si(M::gas_productivity_index, rawWellPI); + + case Phase::OIL: + case Phase::WATER: + return unit_system.to_si(M::liquid_productivity_index, rawWellPI); + + default: + throw std::invalid_argument { + "Preferred phase " + std::to_string(static_cast(preferred_phase)) + + " is not supported. Must be one of 'OIL', 'GAS', or 'WATER'" + }; + } + } +} + double Well::getWellPIScalingFactor(const double currentEffectivePI) const { if (this->connections->empty()) // No connections for this well. Unexpected. @@ -846,11 +873,10 @@ double Well::getWellPIScalingFactor(const double currentEffectivePI) const { // WELPI not activated. Nothing to do. return 1.0; - if (this->productivity_index->pi_value == currentEffectivePI) - // No change in scaling. - return 1.0; + const double requestedWellPI_SI = + convertWellPIToSI(*this->productivity_index, this->getPreferredPhase(), this->unit_system); - return this->productivity_index->pi_value / currentEffectivePI; + return requestedWellPI_SI / currentEffectivePI; } void Well::applyWellProdIndexScaling(const double scalingFactor, std::vector& scalingApplicable) { @@ -898,16 +924,6 @@ const WellTracerProperties& Well::getTracerProperties() const { return *this->tracer_properties; } -const Well::WellProductivityIndex& Well::getWellProductivityIndex() const -{ - if (this->productivity_index) - return *this->productivity_index; - else - throw std::logic_error { - "WELPI not activated in well " + this->name() - }; -} - const WellEconProductionLimits& Well::getEconLimits() const { return *this->econ_limits; } diff --git a/tests/parser/ScheduleTests.cpp b/tests/parser/ScheduleTests.cpp index 20f41f9a7..4896a9ac7 100644 --- a/tests/parser/ScheduleTests.cpp +++ b/tests/parser/ScheduleTests.cpp @@ -67,8 +67,7 @@ namespace { double cp_rm3_per_db() { - return prefix::centi*unit::Poise * unit::cubic(unit::meter) - / (unit::day * unit::barsa); + return UnitSystem::newMETRIC().to_si(UnitSystem::measure::transmissibility, 1.0); } } @@ -3936,9 +3935,15 @@ END BOOST_REQUIRE_EQUAL(sched.getTimeMap().last(), std::size_t{5}); BOOST_REQUIRE_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 1), - "Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 1"); + R"(Schedule must have WELL_PRODUCTIVITY_INDEX Event for well "P" at report step 1)"); BOOST_REQUIRE_MESSAGE(sched.hasWellGroupEvent("P", ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 3), + R"(Schedule must have WELL_PRODUCTIVITY_INDEX Event for well "P" at report step 3)"); + + BOOST_REQUIRE_MESSAGE(sched.getEvents().hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 1), + "Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 1"); + + BOOST_REQUIRE_MESSAGE(sched.getEvents().hasEvent(ScheduleEvents::Events::WELL_PRODUCTIVITY_INDEX, 3), "Schedule must have WELL_PRODUCTIVITY_INDEX Event at report step 3"); auto getScalingFactor = [&sched](const std::size_t report_step, const double wellPI) -> double diff --git a/tests/parser/WellTests.cpp b/tests/parser/WellTests.cpp index a56620a0e..a47a9ac18 100644 --- a/tests/parser/WellTests.cpp +++ b/tests/parser/WellTests.cpp @@ -53,10 +53,14 @@ using namespace Opm; namespace { + double liquid_PI_unit() + { + return UnitSystem::newMETRIC().to_si(UnitSystem::measure::liquid_productivity_index, 1.0); + } + double cp_rm3_per_db() { - return prefix::centi*unit::Poise * unit::cubic(unit::meter) - / (unit::day * unit::barsa); + return UnitSystem::newMETRIC().to_si(UnitSystem::measure::transmissibility, 1.0); } } @@ -1199,8 +1203,6 @@ COMPDAT END )"); - using WellPIType = Well::WellProductivityIndex; - const auto es = EclipseState{ deck }; const auto sched = Schedule{ deck, es }; @@ -1232,14 +1234,14 @@ END // / // // (ignoring units of measure) - BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }), + BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(2.0), "First call to updateWellProductivityIndex() must be a state change"); - BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPIType{ 2.0, Phase::GAS }), + BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(2.0), "Second call to updateWellProductivityIndex() must NOT be a state change"); // Want PI=2, but actual/effective PI=1 => scale CF by 2.0/1.0. { - const auto scalingFactor = wellP.getWellPIScalingFactor(1.0); + const auto scalingFactor = wellP.getWellPIScalingFactor(1.0*liquid_PI_unit()); BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10); std::vector scalingApplicable; @@ -1255,7 +1257,7 @@ END // Repeated application of WELPI multiplies scaling factors. { - const auto scalingFactor = wellP.getWellPIScalingFactor(1.0); + const auto scalingFactor = wellP.getWellPIScalingFactor(1.0*liquid_PI_unit()); BOOST_CHECK_CLOSE(scalingFactor, 2.0, 1.0e-10); std::vector scalingApplicable; @@ -1270,14 +1272,14 @@ END } // New WELPI record does not reset the scaling factors - wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::GAS }); + wellP.updateWellProductivityIndex(3.0); for (const auto& conn : wellP.getConnections()) { BOOST_CHECK_CLOSE(conn.CF(), 4.0*expectCF, 1.0e-10); } // Effective PI=desired PI => no scaling change { - const auto scalingFactor = wellP.getWellPIScalingFactor(3.0); + const auto scalingFactor = wellP.getWellPIScalingFactor(3.0*liquid_PI_unit()); BOOST_CHECK_CLOSE(scalingFactor, 1.0, 1.0e-10); std::vector scalingApplicable; @@ -1290,11 +1292,6 @@ END BOOST_CHECK_MESSAGE(applicable, "All connections must be eligible for WELPI scaling"); } } - - BOOST_CHECK_MESSAGE(wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::OIL }), - "Fourth call to updateWellProductivityIndex() must be a state change"); - BOOST_CHECK_MESSAGE(!wellP.updateWellProductivityIndex(WellPIType{ 3.0, Phase::OIL }), - "Fifth call to updateWellProductivityIndex() must NOT be a state change"); } BOOST_AUTO_TEST_CASE(Has_Same_Connections_Pointers) {