From b36f415a961e80afb84ce6fa3993fc2e8ba8c0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C3=A5rd=20Skaflestad?= Date: Thu, 5 May 2022 23:04:12 +0200 Subject: [PATCH] Identify Additional IWEL and SWEL Items In particular, add entries from WECON, WGRUPCON and, WVFPEXP and fix an incorrect item attribution for the WTEST 'reason' parameter. Thanks to [at]tskille for invaluable assistance in identify these items. --- opm/output/eclipse/VectorItems/well.hpp | 173 ++++- src/opm/output/eclipse/AggregateWellData.cpp | 730 ++++++++++++++----- 2 files changed, 699 insertions(+), 204 deletions(-) diff --git a/opm/output/eclipse/VectorItems/well.hpp b/opm/output/eclipse/VectorItems/well.hpp index 7b0887068..a8f8e2fdc 100644 --- a/opm/output/eclipse/VectorItems/well.hpp +++ b/opm/output/eclipse/VectorItems/well.hpp @@ -39,15 +39,64 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems Status = 10, // Well status VFPTab = 11, // ID (one-based) of well's current VFP table. + EconWorkoverProcedure = 14, // Economic limit workover procedure (WECON(7)). + // 0 => No action taken ("NONE"), + // 1 => Close worst-offending connection ("CON"), + // 2 => Close worst-offending connection and + // all other connections below this ("+CON"), + // 3 => Shut/stop well ("WELL"), + // 6 => Plug well ("PLUG"). + PreferredPhase = 15, // Well's preferred phase (from WELSPECS) item18 = 17, // Unknown - XFlow = 22, - item25 = 24, // Unknown - item32 = 31, // Unknown - WTestCloseReason = 39, - WTestConfigReason = 42, - WTestRemaining = 45, + + XFlow = 22, // Whether or not well supports cross flow. + // 0 => Cross flow NOT supported + // 1 => Cross flow IS supported. + + WGrupConControllable = 24, // Well controllable by group (WGRUPCON(2)) + // -1 => YES, 0 => NO + + EconLimitEndRun = 29, // Whether or not to end simulation run at next report time + // if well is shut or stopped for any reason (WECON(8)). + // 0 => No, 1 => Yes. + + item32 = 31, // Unkown + + WGrupConGRPhase = 32, // Phase to which well's guiderate applies (WGRUPCON(4)) + // 0 => None/defaulted, + // 1 => Oil, + // 2 => Water, + // 3 => Gas, + // 4 => Liquid, + // 5 => Surface flow rate of injecting phase (injectors only), + // 6 => Reservoir fluid volume rate. + + WTestCloseReason = 39, // Dynamic reason for closing a well + // 0 => Flowing or manually SHUT/STOPped + // 3 => Well closed for Physical reasons (P) + // 5 => Well closed for Economic reasons (E) + // 6 => Well closed for Group/Field reasons (G) + // 9 => Well closed for THP design limit (D) + + WTestConfigReason = 40, // Which checks to perform when deciding to + // close a well in WTEST (WTEST(3)). + // + // Product of prime factors representing individual reasons. + // 2 => Physical (P), + // 3 => Economic (E), + // 5 => Group or Field (G), + // 7 => THP design limit (D), + // 11 => Test connections individually (C). + // + // Example: PEG = 2 * 3 * 5 = 30 + // PEGDC = 2 * 3 * 5 * 7 * 11 = 2310 + + WTestRemaining = 45, // Remaining number of times well can be tested (WTEST(4)). + // 0 => Unlimited number of tests + // >0 => Well can be tested at most this many more times + item48 = 47, // Unknown HistReqWCtrl = 49, // Well's requested control mode from @@ -55,6 +104,16 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems LiftOpt = 53, // Well's lift gas injection to be calculated by optimisation or not + THPLookupVFPTable = 54, // How to look up VFP table values for THP controlled wells + // (WVFPEXP(2)). 0 => Implicit, 1 => Explicit. + + EconLimitQuantity = 55, // Quantity to which well's economic limits apply (WECON(10)) + // 0 => Well's flow rate ("RATE") + // 1 => Well's potential flow rates ("POTN") + + EconWorkoverProcedure_2 = 66, // Secondary economic limit workover procedure (WECON(12)). + // Usually just a copy of EconWorkoverProcedure. + MsWID = 70, // Multisegment well ID // Value 0 for regular wells // Value 1..#MS wells for MS wells @@ -76,9 +135,23 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems // = 2 for MSW wells and DF (WELSEGS item 7) + CloseWellIfTHPStabilised = 86, // Whether or not to close well + // if operating in "stabilised" + // part of VFP curve (WVFPEXP(3)) + // 0 => No, 1 => Yes + + PreventTHPIfUnstable = 93, // Whether or not to prevent well + // changing from rate control to THP + // control if constrained to operate + // on unstable side of VFP curve. + // WVFPEXP(4). + // 0 => No, + // 2 => YES1, + // 3 => YES2. + CompOrd = 98, // Well's completion ordering scheme. - LiftOptAllocExtra = 144 + LiftOptAllocExtra = 144, }; namespace Value { @@ -147,6 +220,61 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems Auto = 3, }; + namespace WGrupCon { + enum Controllable : int { + Yes = -1, + No = 0, + }; + + enum GRPhase : int { + Defaulted = 0, + Oil = 1, + Water = 2, + Gas = 3, + Liquid = 4, + SurfaceInjectionRate = 5, + ReservoirVolumeRate = 6, + }; + } // namespace WGrupCon + + namespace WVfpExp { + enum Lookup : int { + Implicit = 0, + Explicit = 1, + }; + + enum class CloseStabilised : int { + No = 0, + Yes = 1, + }; + + enum class PreventTHP : int { + No = 0, + Yes1 = 2, + Yes2 = 3, + }; + } // namespace WVfpExp + + namespace EconLimit { + enum WOProcedure : int { + None = 0, // NONE + Con = 1, // CON + ConAndBelow = 2, // +CON + StopOrShut = 3, // WELL + Plug = 6, // PLUG + }; + + enum EndRun : int { + No = 0, // Run continues if well shut/stopped + Yes = 1, // Run terminates if well shut/stopped + }; + + enum Quantity : int { + Rate = 0, // Apply limits to actual flow rates ("RATE") + Potential = 1, // Apply limits to potential flow rates ("POTN") + }; + } // namespace EconLimit + } // Value } // IWell @@ -165,13 +293,25 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems DatumDepth = 9, // Well's reference depth for BHP Alq_value = 10, // Well's artificial lift quantity - DrainageRadius = 17, // Well's drainage radius - item 7 from WELSPECS - EfficiencyFactor1 = 24, // Item2 from WEFAC; this value is repeated at two locations. - EfficiencyFactor2 = 31, // Item2 from WEFAC - WTestInterval = 32, + EconLimitMinOil = 12, // Well's minimum oil production rate economic limit (WECON(2)) + EconLimitMinGas = 13, // Well's minimum gas production rate economic limit (WECON(3)) + EconLimitMaxWct = 14, // Well's maximum water cut economic limit (WECON(4)) + EconLimitMaxGor = 15, // Well's maximum gas/oil ratio economic limit (WECON(5)) + + DrainageRadius = 16, // Well's drainage radius (WELSPECS(7)) + + WGrupConGuideRate = 17, // Well's guide rate (WGRUPCON(3)) + + EconLimitMaxWgr = 18, // Well's maximum water/gas ratio economic limit (WECON(6)) + + EfficiencyFactor1 = 24, // Well's efficiency factor (WEFAC(2)) + + EfficiencyFactor2 = 31, // Well's efficiency factor (WEFAC(2), copy of EfficiencyFactor1) + WTestInterval = 32, // Well's WTEST interval (WTEST(2)) HistLiqRateTarget = 33, // Well's historical/observed liquid // rate target/limit - WTestStartupTime = 39, + + WTestStartupTime = 39, // Well's WTEST startup time (WTEST(5)) HistGasRateTarget = 54, // Well's historical/observed gas rate // target/limit @@ -182,7 +322,14 @@ namespace Opm { namespace RestartIO { namespace Helpers { namespace VectorItems LOmaxRate = 56, // Well's maximum lift gas rate LOweightFac = 57, // Well's wighting factor for preferential allocation of lift gas LOminRate = 67, // Well's mimimum lift gas rate - LOincFac =115, + + EconLimitMaxWct_2 = 71, // Well's secondary maximum water cut economic limit (WECON(11)). + + EconLimitMinLiq = 82, // Well's minimum liquid production rate economic limit (WECON(14)). + + WGrupConGRScaling = 84, // Guide rate scaling factor (WGRUPCON(5)) + + LOincFac = 115, TracerOffset = 122, // Tracer data start at this index }; diff --git a/src/opm/output/eclipse/AggregateWellData.cpp b/src/opm/output/eclipse/AggregateWellData.cpp index d1a8de6ee..ae49ddf95 100644 --- a/src/opm/output/eclipse/AggregateWellData.cpp +++ b/src/opm/output/eclipse/AggregateWellData.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -275,6 +276,187 @@ namespace { iWell[VI::IWell::index::ActWCtrl] = curr; } + template + void assignGasliftOpt(const std::string& well, + const Opm::GasLiftOpt& glo, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + + if (! glo.has_well(well)) { + return; + } + + // Integer flag indicating lift gas optimisation to be calculated + const auto& w_glo = glo.well(well); + + iWell[Ix::LiftOpt] = w_glo.use_glo(); + iWell[Ix::LiftOptAllocExtra] = w_glo.alloc_extra_gas(); + } + + template + void assignMSWInfo(const Opm::Well& well, + const std::size_t msWellID, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + + // Multi-segmented well information + iWell[Ix::MsWID] = 0; // MS Well ID (0 or 1..#MS wells) + iWell[Ix::NWseg] = 0; // Number of well segments + iWell[Ix::MSW_PlossMod] = 0; // Segment pressure loss model + iWell[Ix::MSW_MulPhaseMod] = 0; // Segment multi phase flow model + + if (well.isMultiSegment()) { + iWell[Ix::MsWID] = static_cast(msWellID); + iWell[Ix::NWseg] = well.getSegments().size(); + iWell[Ix::MSW_PlossMod] = PLossMod(well); + iWell[Ix::MSW_MulPhaseMod] = 1; // temporary solution - valid for HO - multiphase model - only implemented now + //iWell[Ix::MSW_MulPhaseMod] = MPhaseMod(well); + } + } + + template + void assignWellTest(const std::string& well, + const Opm::WellTestConfig& wtest_config, + const Opm::WellTestState& wtest_state, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + + const auto wtest_rst = wtest_state.restart_well(wtest_config, well); + + if (! wtest_rst.has_value()) { + return; + } + + iWell[Ix::WTestConfigReason] = wtest_rst->config_reasons; + iWell[Ix::WTestCloseReason] = wtest_rst->close_reason; + iWell[Ix::WTestRemaining] = wtest_rst->num_test; + } + + int wgrupConGuideratePhase(const Opm::Well::GuideRateTarget grTarget) + { + using GRTarget = Opm::Well::GuideRateTarget; + using GRPhase = VI::IWell::Value::WGrupCon::GRPhase; + + switch (grTarget) { + case GRTarget::OIL: return GRPhase::Oil; + case GRTarget::WAT: return GRPhase::Water; + case GRTarget::GAS: return GRPhase::Gas; + case GRTarget::LIQ: return GRPhase::Liquid; + case GRTarget::RAT: return GRPhase::SurfaceInjectionRate; + case GRTarget::RES: return GRPhase::ReservoirVolumeRate; + case GRTarget::UNDEFINED: return GRPhase::Defaulted; + + default: + throw std::invalid_argument { + fmt::format("Unsupported guiderate phase '{}' for restart", + Opm::Well::GuideRateTarget2String(grTarget)) + }; + } + } + + template + void assignWGrupCon(const Opm::Well& well, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + namespace Value = VI::IWell::Value; + + iWell[Ix::WGrupConControllable] = well.isAvailableForGroupControl() + ? Value::WGrupCon::Controllable::Yes // Common case + : Value::WGrupCon::Controllable::No; + + iWell[Ix::WGrupConGRPhase] = + wgrupConGuideratePhase(well.getRawGuideRatePhase()); + } + + template + void assignTHPLookupOptions(const Opm::Well& well, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + namespace Value = VI::IWell::Value; + + const auto& options = well.getWVFPEXP(); + + iWell[Ix::THPLookupVFPTable] = options.explicit_lookup() + ? Value::WVfpExp::Lookup::Explicit + : Value::WVfpExp::Lookup::Implicit; + + iWell[Ix::CloseWellIfTHPStabilised] = options.shut() + ? static_cast(Value::WVfpExp::CloseStabilised::Yes) + : static_cast(Value::WVfpExp::CloseStabilised::No); + + auto& prevent = iWell[Ix::PreventTHPIfUnstable]; + if (options.report_first()) { + prevent = static_cast(Value::WVfpExp::PreventTHP::Yes1); + } + else if (options.report_every()) { + prevent = static_cast(Value::WVfpExp::PreventTHP::Yes2); + } + else { + prevent = static_cast(Value::WVfpExp::PreventTHP::No); + } + } + + int workoverProcedure(const Opm::WellEconProductionLimits::EconWorkover procedure) + { + namespace Value = VI::IWell::Value; + using WO = Opm::WellEconProductionLimits::EconWorkover; + + switch (procedure) { + case WO::NONE: return Value::EconLimit::WOProcedure::None; + case WO::CON: return Value::EconLimit::WOProcedure::Con; + case WO::CONP: return Value::EconLimit::WOProcedure::ConAndBelow; + case WO::WELL: return Value::EconLimit::WOProcedure::StopOrShut; + case WO::PLUG: return Value::EconLimit::WOProcedure::Plug; + + default: + throw std::invalid_argument { + fmt::format("Unsupported workover procedure '{}' for restart", + Opm::WellEconProductionLimits::EconWorkover2String(procedure)) + }; + } + } + + int econLimitQuantity(const Opm::WellEconProductionLimits::QuantityLimit quantity) + { + namespace Value = VI::IWell::Value; + using Quant = Opm::WellEconProductionLimits::QuantityLimit; + + switch (quantity) { + case Quant::RATE: return Value::EconLimit::Quantity::Rate; + case Quant::POTN: return Value::EconLimit::Quantity::Potential; + } + + throw std::invalid_argument { + fmt::format("Unsupported economic limit quantity '{}' for restart", + Opm::WellEconProductionLimits::QuantityLimit2String(quantity)) + }; + } + + template + void assignEconomicLimits(const Opm::Well& well, + IWellArray& iWell) + { + using Ix = VI::IWell::index; + namespace Value = VI::IWell::Value; + + const auto& limits = well.getEconLimits(); + + iWell[Ix::EconWorkoverProcedure] = workoverProcedure(limits.workover()); + iWell[Ix::EconWorkoverProcedure_2] = + workoverProcedure(limits.workoverSecondary()); + + iWell[Ix::EconLimitEndRun] = limits.endRun() + ? Value::EconLimit::EndRun::Yes + : Value::EconLimit::EndRun::No; // Common case + + iWell[Ix::EconLimitQuantity] = econLimitQuantity(limits.quantityLimit()); + } + template void staticContrib(const Opm::Well& well, const Opm::GasLiftOpt& glo, @@ -324,47 +506,27 @@ namespace { // The following items aren't fully characterised yet, but // needed for restart of M2. Will need further refinement. iWell[Ix::item18] = -100; - iWell[Ix::item25] = - 1; iWell[Ix::item32] = 7; iWell[Ix::item48] = - 1; - // integer flag indicating lift gas optimisation to be calculated - if (glo.has_well(well.name())) { - const auto& w_glo = glo.well(well.name()); - iWell[Ix::LiftOpt] = (w_glo.use_glo()) ? 1 : 0; - iWell[Ix::LiftOptAllocExtra] = w_glo.alloc_extra_gas() ? 1 : 0; - } - // Deliberate misrepresentation. Function 'eclipseControlMode' // returns the target control mode requested in the simulation // deck. This item is supposed to be the well's actual, active // target control mode in the simulator. // - // Observe that the setupCurrentContro() function is called again - // for open wells in the dynamicContrib() function. + // Observe that setCurrentControl() is called again, for open + // wells, in the dynamicContrib() function. setCurrentControl(Opm::Well::eclipseControlMode(well, st), iWell); setHistoryControlMode(well, Opm::Well::eclipseControlMode(well, st), iWell); - // Multi-segmented well information - iWell[Ix::MsWID] = 0; // MS Well ID (0 or 1..#MS wells) - iWell[Ix::NWseg] = 0; // Number of well segments - iWell[Ix::MSW_PlossMod] = 0; // Segment pressure loss model - iWell[Ix::MSW_MulPhaseMod] = 0; // Segment multi phase flow model - if (well.isMultiSegment()) { - iWell[Ix::MsWID] = static_cast(msWellID); - iWell[Ix::NWseg] = well.getSegments().size(); - iWell[Ix::MSW_PlossMod] = PLossMod(well); - iWell[Ix::MSW_MulPhaseMod] = 1; // temporary solution - valid for HO - multiphase model - only implemented now - //iWell[Ix::MSW_MulPhaseMod] = MPhaseMod(well); - } - iWell[Ix::CompOrd] = compOrder(well); - const auto& wtest_rst = wtest_state.restart_well(wtest_config, well.name()); - if (wtest_rst.has_value()) { - iWell[Ix::WTestConfigReason] = wtest_rst->config_reasons; - iWell[Ix::WTestCloseReason] = wtest_rst->close_reason; - iWell[Ix::WTestRemaining] = wtest_rst->num_test; - } + + assignGasliftOpt(well.name(), glo, iWell); + assignMSWInfo(well, msWellID, iWell); + assignWGrupCon(well, iWell); + assignTHPLookupOptions(well, iWell); + assignEconomicLimits(well, iWell); + assignWellTest(well.name(), wtest_config, wtest_state, iWell); } template @@ -520,6 +682,7 @@ namespace { const double rate) { float rLimit = 1.0e+20f; + if (rate > 0.0) { rLimit = static_cast(units.from_si(u, rate)); } @@ -530,13 +693,345 @@ namespace { return rLimit; } + template + void assignOWGRateTargetsProd(const Opm::Well::ProductionControls& pc, + const bool predMode, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + if (predMode) { + if (pc.oil_rate != 0.0) { + sWell[Ix::OilRateTarget] = swprop(M::liquid_surface_rate, pc.oil_rate); + } + + if (pc.water_rate != 0.0) { + sWell[Ix::WatRateTarget] = swprop(M::liquid_surface_rate, pc.water_rate); + } + + if (pc.gas_rate != 0.0) { + sWell[Ix::GasRateTarget] = swprop(M::gas_surface_rate, pc.gas_rate); + + sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; + } + } + else { + sWell[Ix::OilRateTarget] = + swprop(M::liquid_surface_rate, pc.oil_rate); + + sWell[Ix::WatRateTarget] = + swprop(M::liquid_surface_rate, pc.water_rate); + + sWell[Ix::GasRateTarget] = + swprop(M::gas_surface_rate, pc.gas_rate); + + sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; + } + } + + template + void assignLiqRateTargetsProd(const Opm::Well::ProductionControls& pc, + const bool predMode, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = ::Opm::RestartIO::Helpers::VectorItems::SWell::index; + using M = ::Opm::UnitSystem::measure; + + if (pc.liquid_rate != 0.0) { // check if this works - may need to be rewritten + sWell[Ix::LiqRateTarget] = swprop(M::liquid_surface_rate, pc.liquid_rate); + + sWell[Ix::HistLiqRateTarget] = sWell[Ix::LiqRateTarget]; + } + else if (!predMode) { + sWell[Ix::LiqRateTarget] = + swprop(M::liquid_surface_rate, pc.oil_rate + pc.water_rate); + } + } + + template + void assignResVRateTargetsProd(const std::string& well, + const Opm::SummaryState& smry, + const Opm::Well::ProductionControls& pc, + const bool predMode, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + if (pc.resv_rate != 0.0) { + sWell[Ix::ResVRateTarget] = swprop(M::rate, pc.resv_rate); + } + else if (!predMode) { + // Write out summary voidage production rate if target/limit + // is not set + const auto vr = smry.get_well_var("WVPR", well, 0.0); + if (vr != 0.0) { + sWell[Ix::ResVRateTarget] = static_cast(vr); + } + } + } + + template + void assignALQProd(const Opm::VFPProdTable::ALQ_TYPE alqType, + const double alq_value, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + if (alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_GRAT) { + sWell[Ix::Alq_value] = swprop(M::gas_surface_rate, alq_value); + } + else if ((alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_IGLR) || + (alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_TGLR)) + { + sWell[Ix::Alq_value] = swprop(M::gas_oil_ratio, alq_value); + } + else { + // Note: Not all ALQ types have associated units of + // measurement. + sWell[Ix::Alq_value] = alq_value; + } + } + + template + void assignPredictionTargetsProd(const Opm::UnitSystem& units, + const Opm::Well::ProductionControls& pc, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + sWell[Ix::OilRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.oil_rate); + sWell[Ix::WatRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.water_rate); + sWell[Ix::GasRateTarget] = getRateLimit(units, M::gas_surface_rate, pc.gas_rate); + sWell[Ix::LiqRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.liquid_rate); + sWell[Ix::ResVRateTarget] = getRateLimit(units, M::rate, pc.resv_rate); + } + + template + void assignProductionTargets(const Opm::Well& well, + const Opm::SummaryState& smry, + const Opm::Schedule& sched, + const std::size_t sim_step, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + const auto pc = well.productionControls(smry); + const auto predMode = well.predictionMode(); + + assignOWGRateTargetsProd(pc, predMode, std::forward(swprop), sWell); + assignLiqRateTargetsProd(pc, predMode, std::forward(swprop), sWell); + assignResVRateTargetsProd(well.name(), smry, pc, predMode, + std::forward(swprop), sWell); + + sWell[Ix::THPTarget] = (pc.thp_limit != 0.0) + ? swprop(M::pressure, pc.thp_limit) + : 0.0; + + sWell[Ix::BHPTarget] = (pc.bhp_limit != 0.0) + ? swprop(M::pressure, pc.bhp_limit) + : swprop(M::pressure, 1.0*::Opm::unit::atm); + + sWell[Ix::HistBHPTarget] = sWell[Ix::BHPTarget]; + + if (pc.alq_value != 0.0) { + const auto alqType = sched[sim_step].vfpprod(pc.vfp_table_number).getALQType(); + assignALQProd(alqType, pc.alq_value, std::forward(swprop), sWell); + } + + if (predMode) { + assignPredictionTargetsProd(sched.getUnits(), pc, sWell); + } + } + + template + void assignInjectionTargets(const Opm::Well& well, + const Opm::SummaryState& smry, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + using IP = ::Opm::Well::InjectorCMode; + using IT = ::Opm::InjectorType; + + const auto& ic = well.injectionControls(smry); + + if (ic.hasControl(IP::RATE)) { + switch (ic.injector_type) { + case IT::OIL: + sWell[Ix::OilRateTarget] = swprop(M::liquid_surface_rate, ic.surface_rate); + break; + + case IT::WATER: + sWell[Ix::WatRateTarget] = swprop(M::liquid_surface_rate, ic.surface_rate); + sWell[Ix::HistLiqRateTarget] = sWell[Ix::WatRateTarget]; + break; + + case IT::GAS: + sWell[Ix::GasRateTarget] = swprop(M::gas_surface_rate, ic.surface_rate); + sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; + break; + + default: + break; + } + } + + if (ic.hasControl(IP::RESV)) { + sWell[Ix::ResVRateTarget] = swprop(M::rate, ic.reservoir_rate); + } + + if (ic.hasControl(IP::THP)) { + sWell[Ix::THPTarget] = swprop(M::pressure, ic.thp_limit); + } + + sWell[Ix::BHPTarget] = ic.hasControl(IP::BHP) + ? swprop(M::pressure, ic.bhp_limit) + : swprop(M::pressure, 1.0E05*::Opm::unit::psia); + + sWell[Ix::HistBHPTarget] = sWell[Ix::BHPTarget]; + } + + template + void assignGasLiftOptimisation(const Opm::GasLiftOpt::Well& w_glo, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + sWell[Ix::LOmaxRate] = swprop(M::gas_surface_rate, w_glo.max_rate().value_or(0.0)); + sWell[Ix::LOminRate] = swprop(M::gas_surface_rate, w_glo.min_rate()); + + sWell[Ix::LOweightFac] = static_cast(w_glo.weight_factor()); + sWell[Ix::LOincFac] = static_cast(w_glo.inc_weight_factor()); + } + + template + void assignReferenceDepth(const Opm::Well& well, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + if (const auto depth = datumDepth(well); depth.has_value()) { + sWell[Ix::DatumDepth] = swprop(M::length, depth.value()); + } + else { + // BHP reference depth missing for this well. Typically + // caused by defaulted WELSPECS(5) and no active reservoir + // connections from which to infer the depth. Output + // sentinel value. + // + // Note: All unit systems get the *same* sentinel value so + // we intentionally omit unit conversion here. + sWell[Ix::DatumDepth] = -1.0e+20f; + } + } + + template + void assignWGrupCon(const Opm::Well& well, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + + if (const auto gr = well.getGuideRate(); gr > 0.0) { + sWell[Ix::WGrupConGuideRate] = static_cast(gr); + } + + sWell[Ix::WGrupConGRScaling] = + static_cast(well.getGuideRateScalingFactor()); + } + + template + void assignEconomicLimits(const Opm::Well& well, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + const auto& limits = well.getEconLimits(); + + if (limits.onMinOilRate()) { + sWell[Ix::EconLimitMinOil] = swprop(M::liquid_surface_rate, limits.minOilRate()); + } + + if (limits.onMinGasRate()) { + sWell[Ix::EconLimitMinGas] = swprop(M::gas_surface_rate, limits.minGasRate()); + } + + if (limits.onMaxWaterCut()) { + sWell[Ix::EconLimitMaxWct] = swprop(M::identity, limits.maxWaterCut()); + } + + if (limits.onMaxGasOilRatio()) { + sWell[Ix::EconLimitMaxGor] = swprop(M::gas_oil_ratio, limits.maxGasOilRatio()); + } + + if (limits.onMaxWaterGasRatio()) { + sWell[Ix::EconLimitMaxWgr] = swprop(M::oil_gas_ratio, limits.maxWaterGasRatio()); + } + + if (limits.onSecondaryMaxWaterCut()) { + sWell[Ix::EconLimitMaxWct_2] = swprop(M::identity, limits.maxSecondaryMaxWaterCut()); + } + + if (limits.onMinLiquidRate()) { + sWell[Ix::EconLimitMinLiq] = swprop(M::liquid_surface_rate, limits.minLiquidRate()); + } + } + + template + void assignWellTest(const std::string& well, + const Opm::Schedule& sched, + const Opm::WellTestState& wtest_state, + const std::size_t sim_step, + SWProp&& swprop, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + using M = ::Opm::UnitSystem::measure; + + const auto& wtest_config = sched[sim_step].wtest_config(); + const auto& wtest_param = wtest_state.restart_well(wtest_config, well); + + if (! wtest_param.has_value()) { + return; + } + + sWell[Ix::WTestInterval] = swprop(M::time, wtest_param->test_interval); + sWell[Ix::WTestStartupTime] = swprop(M::time, wtest_param->startup_time); + } + + template + void assignEfficiencyFactors(const Opm::Well& well, + SWellArray& sWell) + { + using Ix = VI::SWell::index; + + sWell[Ix::EfficiencyFactor1] = well.getEfficiencyFactor(); + sWell[Ix::EfficiencyFactor2] = sWell[Ix::EfficiencyFactor1]; + } + template void assignTracerData(const Opm::TracerConfig& tracers, const Opm::SummaryState& smry, - const std::string& wname, - SWellArray &sWell) + const std::string& wname, + SWellArray& sWell) { - using Ix = ::Opm::RestartIO::Helpers::VectorItems::SWell::index; + using Ix = VI::SWell::index; std::fill(sWell.begin() + Ix::TracerOffset, sWell.end(), 0); std::size_t output_index = Ix::TracerOffset; @@ -556,183 +1051,36 @@ namespace { const ::Opm::SummaryState& smry, SWellArray& sWell) { - using Ix = ::Opm::RestartIO::Helpers::VectorItems::SWell::index; + using Ix = VI::SWell::index; using M = ::Opm::UnitSystem::measure; - const auto& units = sched.getUnits(); + assignDefaultSWell(sWell); + + const auto& units = sched.getUnits(); auto swprop = [&units](const M u, const double x) -> float { return static_cast(units.from_si(u, x)); }; - - assignDefaultSWell(sWell); if (well.isProducer()) { - const auto& pc = well.productionControls(smry); - const auto& predMode = well.predictionMode(); - - if (predMode) { - if ((pc.oil_rate != 0.0)) { - sWell[Ix::OilRateTarget] = - swprop(M::liquid_surface_rate, pc.oil_rate); - } - - if ((pc.water_rate != 0.0)) { - sWell[Ix::WatRateTarget] = - swprop(M::liquid_surface_rate, pc.water_rate); - } - - if ((pc.gas_rate != 0.0)) { - sWell[Ix::GasRateTarget] = - swprop(M::gas_surface_rate, pc.gas_rate); - sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; - } - } else { - sWell[Ix::OilRateTarget] = - swprop(M::liquid_surface_rate, pc.oil_rate); - sWell[Ix::WatRateTarget] = - swprop(M::liquid_surface_rate, pc.water_rate); - sWell[Ix::GasRateTarget] = - swprop(M::gas_surface_rate, pc.gas_rate); - sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; - } - - if (pc.liquid_rate != 0.0) { // check if this works - may need to be rewritten - sWell[Ix::LiqRateTarget] = - swprop(M::liquid_surface_rate, pc.liquid_rate); - sWell[Ix::HistLiqRateTarget] = sWell[Ix::LiqRateTarget]; - } - else if (!predMode) { - sWell[Ix::LiqRateTarget] = - swprop(M::liquid_surface_rate, pc.oil_rate + pc.water_rate); - } - - if (pc.resv_rate != 0.0) { - sWell[Ix::ResVRateTarget] = - swprop(M::rate, pc.resv_rate); - } - else if ((smry.has("WVPR:" + well.name())) && (!predMode)) { - // Write out summary voidage production rate if - // target/limit is not set - auto vr = static_cast(smry.get("WVPR:" + well.name())); - if (vr != 0.0) { - sWell[Ix::ResVRateTarget] = vr; - } - } - - sWell[Ix::THPTarget] = pc.thp_limit != 0.0 - ? swprop(M::pressure, pc.thp_limit) - : 0.0; - - sWell[Ix::BHPTarget] = pc.bhp_limit != 0.0 - ? swprop(M::pressure, pc.bhp_limit) - : swprop(M::pressure, 1.0*::Opm::unit::atm); - sWell[Ix::HistBHPTarget] = sWell[Ix::BHPTarget]; - - if (pc.alq_value != 0.0) { - const auto alqType = sched[sim_step].vfpprod(pc.vfp_table_number).getALQType(); - if (alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_GRAT) { - sWell[Ix::Alq_value] = static_cast(units.from_si(M::gas_surface_rate, pc.alq_value)); - } - else if ((alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_IGLR) || - (alqType == Opm::VFPProdTable::ALQ_TYPE::ALQ_TGLR)) - { - sWell[Ix::Alq_value] = static_cast(units.from_si(M::gas_oil_ratio, pc.alq_value)); - } - else { - // not all alq_value options have units - sWell[Ix::Alq_value] = pc.alq_value; - } - } - - if (predMode) { - //if (well.getStatus() == Opm::Well::Status::OPEN) { - sWell[Ix::OilRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.oil_rate); - sWell[Ix::WatRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.water_rate); - sWell[Ix::GasRateTarget] = getRateLimit(units, M::gas_surface_rate, pc.gas_rate); - sWell[Ix::LiqRateTarget] = getRateLimit(units, M::liquid_surface_rate, pc.liquid_rate); - sWell[Ix::ResVRateTarget] = getRateLimit(units, M::rate, pc.resv_rate); - //} - } + assignProductionTargets(well, smry, sched, sim_step, swprop, sWell); } else if (well.isInjector()) { - const auto& ic = well.injectionControls(smry); - - using IP = ::Opm::Well::InjectorCMode; - using IT = ::Opm::InjectorType; - - if (ic.hasControl(IP::RATE)) { - if (ic.injector_type == IT::OIL) { - sWell[Ix::OilRateTarget] = - swprop(M::liquid_surface_rate, ic.surface_rate); - } - if (ic.injector_type == IT::WATER) { - sWell[Ix::WatRateTarget] = - swprop(M::liquid_surface_rate, ic.surface_rate); - sWell[Ix::HistLiqRateTarget] = sWell[Ix::WatRateTarget]; - } - if (ic.injector_type == IT::GAS) { - sWell[Ix::GasRateTarget] = - swprop(M::gas_surface_rate, ic.surface_rate); - sWell[Ix::HistGasRateTarget] = sWell[Ix::GasRateTarget]; - } - } - - if (ic.hasControl(IP::RESV)) { - sWell[Ix::ResVRateTarget] = - swprop(M::rate, ic.reservoir_rate); - } - - if (ic.hasControl(IP::THP)) { - sWell[Ix::THPTarget] = swprop(M::pressure, ic.thp_limit); - } - - sWell[Ix::BHPTarget] = ic.hasControl(IP::BHP) - ? swprop(M::pressure, ic.bhp_limit) - : swprop(M::pressure, 1.0E05*::Opm::unit::psia); - sWell[Ix::HistBHPTarget] = sWell[Ix::BHPTarget]; + assignInjectionTargets(well, smry, swprop, sWell); } - // assign gas lift data if (glo.has_well(well.name())) { - const auto& w_glo = glo.well(well.name()); - sWell[Ix::LOmaxRate] = swprop(M::gas_surface_rate, w_glo.max_rate().value_or(0.)); - sWell[Ix::LOminRate] = swprop(M::gas_surface_rate, w_glo.min_rate()); - sWell[Ix::LOweightFac] = static_cast(w_glo.weight_factor()); - sWell[Ix::LOincFac] = static_cast(w_glo.inc_weight_factor()); + assignGasLiftOptimisation(glo.well(well.name()), swprop, sWell); } - if (const auto depth = datumDepth(well); depth.has_value()) { - sWell[Ix::DatumDepth] = swprop(M::length, depth.value()); - } - else { - // BHP reference depth missing for this well. Typically - // caused by defaulted WELSPECS(5) and no active reservoir - // connections from which to infer the depth. Output - // sentinel value. - // - // Note: All unit systems get the *same* sentinel value so - // we intentionally omit unit conversion here. - sWell[Ix::DatumDepth] = -1.0e+20f; - } + assignReferenceDepth(well, swprop, sWell); sWell[Ix::DrainageRadius] = swprop(M::length, well.getDrainageRadius()); - // Restart files from Eclipse indicate that the efficiency - // factor is found in two items in the restart file; since only - // one of the items is needed in the OPM restart, and we are not - // really certain that the two values are equal by construction - // we have only assigned one of the items explicitly here. - sWell[Ix::EfficiencyFactor1] = well.getEfficiencyFactor(); - sWell[Ix::EfficiencyFactor2] = sWell[Ix::EfficiencyFactor1]; - - const auto& wtest_config = sched[sim_step].wtest_config(); - const auto& wtest_rst = wtest_state.restart_well(wtest_config, well.name()); - if (wtest_rst.has_value()) { - sWell[Ix::WTestInterval] = units.from_si(Opm::UnitSystem::measure::time, wtest_rst->test_interval); - sWell[Ix::WTestStartupTime] = units.from_si(Opm::UnitSystem::measure::time, wtest_rst->startup_time); - } - + assignWGrupCon(well, sWell); + assignEfficiencyFactors(well, sWell); + assignEconomicLimits(well, swprop, sWell); + assignWellTest(well.name(), sched, wtest_state, sim_step, swprop, sWell); assignTracerData(tracers, smry, well.name(), sWell); } } // SWell