Files
opm-common/src/opm/input/eclipse/Schedule/Well/Well.cpp
2023-06-27 15:50:54 +02:00

1721 lines
54 KiB
C++

/*
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 <http://www.gnu.org/licenses/>.
*/
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <opm/io/eclipse/rst/well.hpp>
#include <opm/output/eclipse/VectorItems/well.hpp>
#include <opm/input/eclipse/EclipseState/Grid/EclipseGrid.hpp>
#include <opm/input/eclipse/EclipseState/Phase.hpp>
#include <opm/input/eclipse/EclipseState/TracerConfig.hpp>
#include <opm/input/eclipse/Schedule/MSW/WellSegments.hpp>
#include <opm/input/eclipse/Schedule/ScheduleGrid.hpp>
#include <opm/input/eclipse/Schedule/UDQ/UDQActive.hpp>
#include <opm/input/eclipse/Schedule/Well/WellBrineProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellConnections.hpp>
#include <opm/input/eclipse/Schedule/Well/WellEconProductionLimits.hpp>
#include <opm/input/eclipse/Schedule/Well/WellFoamProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellMICPProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellPolymerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTracerProperties.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPDP.hpp>
#include <opm/input/eclipse/Schedule/Well/WVFPEXP.hpp>
#include <opm/input/eclipse/Units/Units.hpp>
#include <opm/common/utility/OpmInputError.hpp>
#include <opm/common/utility/shmatch.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/S.hpp>
#include <opm/input/eclipse/Parser/ParserKeywords/W.hpp>
#include <opm/input/eclipse/Deck/DeckRecord.hpp>
#include <opm/input/eclipse/Deck/DeckKeyword.hpp>
#include "../MSW/Compsegs.hpp"
#include <cassert>
#include <cmath>
#include <cstddef>
#include <memory>
#include <optional>
#include <ostream>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include <fmt/format.h>
namespace {
bool defaulted(const Opm::DeckRecord& rec, const std::string& s)
{
const auto& item = rec.getItem(s);
return item.defaultApplied(0) || (item.get<int>(0) == 0);
}
int limit(const Opm::DeckRecord& rec, const std::string& s, const int shift)
{
const auto& item = rec.getItem(s);
return shift + item.get<int>(0);
}
bool match_le(const int value,
const Opm::DeckRecord& rec,
const std::string& s,
const int shift = 0)
{
return defaulted(rec, s) || (value <= limit(rec, s, shift));
}
bool match_ge(const int value,
const Opm::DeckRecord& rec,
const std::string& s,
const int shift = 0)
{
return defaulted(rec, s) || (value >= limit(rec, s, shift));
}
bool match_eq(const int value,
const Opm::DeckRecord& rec,
const std::string& s,
const int shift = 0)
{
return defaulted(rec, s)|| (limit(rec, s, shift) == value);
}
Opm::Connection::Order order_from_int(const int int_value)
{
switch (int_value) {
case 0: return Opm::Connection::Order::TRACK;
case 1: return Opm::Connection::Order::DEPTH;
case 2: return Opm::Connection::Order::INPUT;
default:
throw std::invalid_argument {
fmt::format("Invalid integer value: {} encountered "
"when determining connection ordering", int_value)
};
}
}
Opm::Well::Status status_from_int(const int int_value)
{
using Value = Opm::RestartIO::Helpers::VectorItems::IWell::Value::Status;
switch (int_value) {
case Value::Shut: return Opm::Well::Status::SHUT;
case Value::Stop: return Opm::Well::Status::STOP;
case Value::Open: return Opm::Well::Status::OPEN;
case Value::Auto: return Opm::Well::Status::AUTO;
default:
throw std::logic_error {
fmt::format("integer value: {} could not be "
"converted to a valid well status.", int_value)
};
}
}
Opm::Well::ProducerCMode producer_cmode_from_int(const int pmode)
{
using CModeVal = ::Opm::RestartIO::Helpers::VectorItems::
IWell::Value::WellCtrlMode;
switch (pmode) {
case CModeVal::Group: return Opm::Well::ProducerCMode::GRUP;
case CModeVal::OilRate: return Opm::Well::ProducerCMode::ORAT;
case CModeVal::WatRate: return Opm::Well::ProducerCMode::WRAT;
case CModeVal::GasRate: return Opm::Well::ProducerCMode::GRAT;
case CModeVal::LiqRate: return Opm::Well::ProducerCMode::LRAT;
case CModeVal::ResVRate: return Opm::Well::ProducerCMode::RESV;
case CModeVal::THP: return Opm::Well::ProducerCMode::THP;
case CModeVal::BHP: return Opm::Well::ProducerCMode::BHP;
}
throw std::invalid_argument {
fmt::format("Cannot convert integer value {} to producer control mode", pmode)
};
}
Opm::Well::InjectorCMode injector_cmode_from_int(const int imode)
{
using CModeVal = ::Opm::RestartIO::Helpers::VectorItems::
IWell::Value::WellCtrlMode;
switch (imode) {
case CModeVal::Group: return Opm::Well::InjectorCMode::GRUP;
case CModeVal::OilRate: [[fallthrough]];
case CModeVal::WatRate: [[fallthrough]];
case CModeVal::GasRate: [[fallthrough]];
case CModeVal::LiqRate: return Opm::Well::InjectorCMode::RATE;
case CModeVal::ResVRate: return Opm::Well::InjectorCMode::RESV;
case CModeVal::THP: return Opm::Well::InjectorCMode::THP;
case CModeVal::BHP: return Opm::Well::InjectorCMode::BHP;
}
throw std::invalid_argument {
fmt::format("Cannot convert integer value {} to injector control mode", imode)
};
}
bool haveEconomicLimits(const Opm::RestartIO::RstWell& rst_well)
{
namespace Limits = Opm::RestartIO::Helpers::VectorItems::
IWell::Value::EconLimit;
auto is_finite = [](const float x)
{
const auto infty = 1.0e+20f;
return std::abs(x) < infty;
};
auto is_nonzero = [](const float x)
{
return std::abs(x) > 0.0f;
};
return (rst_well.econ_workover_procedure != Limits::WOProcedure::None)
|| (rst_well.econ_workover_procedure_2 != Limits::WOProcedure::None)
|| (rst_well.econ_limit_end_run == Limits::EndRun::Yes)
|| (rst_well.econ_limit_quantity != Limits::Rate)
|| is_nonzero(rst_well.econ_limit_min_oil)
|| is_nonzero(rst_well.econ_limit_min_gas)
|| is_nonzero(rst_well.econ_limit_min_liq)
|| is_finite (rst_well.econ_limit_max_wct)
|| is_finite (rst_well.econ_limit_max_gor)
|| is_finite (rst_well.econ_limit_max_wgr)
|| is_finite (rst_well.econ_limit_max_wct_2);
}
std::shared_ptr<Opm::WellEconProductionLimits>
economicLimits(const Opm::RestartIO::RstWell& rst_well)
{
return haveEconomicLimits(rst_well)
? std::make_shared<Opm::WellEconProductionLimits>(rst_well)
: std::make_shared<Opm::WellEconProductionLimits>();
}
Opm::Well::GuideRateTarget guideRatePhase(const int gr_phase)
{
namespace WGrupCon = Opm::RestartIO::Helpers::VectorItems::
IWell::Value::WGrupCon;
using Target = Opm::Well::GuideRateTarget;
switch (gr_phase) {
case WGrupCon::GRPhase::Defaulted: return Target::UNDEFINED;
case WGrupCon::GRPhase::Oil: return Target::OIL;
case WGrupCon::GRPhase::Water: return Target::WAT;
case WGrupCon::GRPhase::Gas: return Target::GAS;
case WGrupCon::GRPhase::Liquid: return Target::LIQ;
case WGrupCon::GRPhase::SurfaceInjectionRate: return Target::RAT;
case WGrupCon::GRPhase::ReservoirVolumeRate: return Target::RES;
}
throw std::invalid_argument {
fmt::format("Cannot convert integer value {} "
"to guiderate phase target", gr_phase)
};
}
bool isGroupControllable(const int gr_controllable_flag)
{
return gr_controllable_flag != Opm::RestartIO::Helpers::VectorItems::
IWell::Value::WGrupCon::Controllable::No;
}
double guideRateValue(const float gr_value)
{
return (std::abs(gr_value) < 1.0e+20f)
? gr_value
: Opm::ParserKeywords::WGRUPCON::GUIDE_RATE::defaultValue;
}
Opm::Well::WellGuideRate guideRate(const Opm::RestartIO::RstWell& rst_well)
{
return {
isGroupControllable(rst_well.group_controllable_flag),
guideRateValue(rst_well.grupcon_gr_value),
guideRatePhase(rst_well.grupcon_gr_phase),
rst_well.grupcon_gr_scaling
};
}
std::shared_ptr<Opm::WVFPEXP>
explicitTHPOptions(const Opm::RestartIO::RstWell& rst_well)
{
auto options = std::make_shared<Opm::WVFPEXP>();
options->update(rst_well);
return options;
}
constexpr Opm::Well::ProducerCMode def_whistctl_cmode = Opm::Well::ProducerCMode::CMODE_UNDEFINED;
const static bool def_automatic_shutin = true;
constexpr double def_solvent_fraction = 0;
}
namespace Opm {
Well::Well(const RestartIO::RstWell& rst_well,
int report_step,
const TracerConfig& tracer_config,
const UnitSystem& unit_system_arg,
double udq_undefined_arg) :
wname(rst_well.name),
group_name(rst_well.group),
init_step(report_step),
headI(rst_well.ij[0]),
headJ(rst_well.ij[1]),
ref_depth((std::abs(rst_well.datum_depth) < 1.0e+20) ? std::optional<double>{ rst_well.datum_depth } : std::nullopt),
drainage_radius(rst_well.drainage_radius),
allow_cross_flow(rst_well.allow_xflow == 1),
automatic_shutin(def_automatic_shutin),
pvt_table(rst_well.pvt_table),
unit_system(unit_system_arg),
udq_undefined(udq_undefined_arg),
wtype(rst_well.wtype),
guide_rate(guideRate(rst_well)),
efficiency_factor(rst_well.efficiency_factor),
solvent_fraction(def_solvent_fraction),
prediction_mode(rst_well.hist_requested_control == 0),
econ_limits(economicLimits(rst_well)),
foam_properties(std::make_shared<WellFoamProperties>()),
polymer_properties(std::make_shared<WellPolymerProperties>()),
micp_properties(std::make_shared<WellMICPProperties>()),
brine_properties(std::make_shared<WellBrineProperties>()),
tracer_properties(std::make_shared<WellTracerProperties>()),
connections(std::make_shared<WellConnections>(order_from_int(rst_well.completion_ordering), headI, headJ)),
production(std::make_shared<WellProductionProperties>(unit_system_arg, wname)),
injection(std::make_shared<WellInjectionProperties>(unit_system_arg, wname)),
wvfpdp(std::make_shared<WVFPDP>()),
wvfpexp(explicitTHPOptions(rst_well)),
status(status_from_int(rst_well.well_status)),
well_temperature(Metric::TemperatureOffset + ParserKeywords::STCOND::TEMPERATURE::defaultValue),
well_inj_mult(std::nullopt)
{
if (this->wtype.producer()) {
auto p = std::make_shared<WellProductionProperties>(this->unit_system, wname);
// Reverse of function ctrlMode() in AggregateWellData.cpp
p->whistctl_cmode = def_whistctl_cmode;
p->BHPTarget.update(rst_well.bhp_target_float);
p->OilRate.update(rst_well.orat_target);
p->WaterRate.update(rst_well.wrat_target);
p->GasRate.update(rst_well.grat_target);
p->LiquidRate.update(rst_well.lrat_target) ;
p->ResVRate.update(rst_well.resv_target);
p->VFPTableNumber = rst_well.vfp_table;
p->ALQValue.update(rst_well.alq_value); // Uncertain of whether the dimension comes through correct here.
p->predictionMode = this->prediction_mode;
if (rst_well.orat_target != 0)
p->addProductionControl( Well::ProducerCMode::ORAT );
if (rst_well.wrat_target != 0)
p->addProductionControl( Well::ProducerCMode::WRAT );
if (rst_well.grat_target != 0)
p->addProductionControl( Well::ProducerCMode::GRAT );
if (rst_well.lrat_target != 0)
p->addProductionControl( Well::ProducerCMode::LRAT );
if (rst_well.resv_target != 0)
p->addProductionControl( Well::ProducerCMode::RESV );
if (rst_well.thp_target != 0) {
p->THPTarget.update(rst_well.thp_target);
p->addProductionControl( Well::ProducerCMode::THP );
}
if (! p->predictionMode)
p->clearControls();
p->controlMode = producer_cmode_from_int(rst_well.active_control);
p->addProductionControl(p->controlMode);
p->addProductionControl(Well::ProducerCMode::BHP);
if (! p->predictionMode) {
p->BHPTarget.update(0.0);
p->setBHPLimit(rst_well.bhp_target_double);
p->controlMode = p->whistctl_cmode =
producer_cmode_from_int(rst_well.hist_requested_control);
}
else if (this->isAvailableForGroupControl())
p->addProductionControl(Well::ProducerCMode::GRUP);
this->updateProduction(std::move(p));
}
else {
auto i = std::make_shared<WellInjectionProperties>(this->unit_system, wname);
i->VFPTableNumber = rst_well.vfp_table;
i->predictionMode = this->prediction_mode;
if ((std::abs(rst_well.wrat_target) > 0.0f) ||
(std::abs(rst_well.grat_target) > 0.0f))
i->addInjectionControl(Well::InjectorCMode::RATE);
if (std::abs(rst_well.resv_target) > 0.0f) {
i->reservoirInjectionRate.update(rst_well.resv_target);
i->addInjectionControl(Well::InjectorCMode::RESV);
}
i->injectorType = rst_well.wtype.injector_type();
switch (i->injectorType) {
case InjectorType::WATER:
i->surfaceInjectionRate.update(rst_well.wrat_target);
break;
case InjectorType::GAS:
i->surfaceInjectionRate.update(rst_well.grat_target);
break;
default:
throw std::invalid_argument("What ...");
}
if (rst_well.thp_target != 0.0f) {
i->THPTarget.update(rst_well.thp_target);
i->addInjectionControl(Well::InjectorCMode::THP);
}
const auto active_control = i->predictionMode
? injector_cmode_from_int(rst_well.active_control)
: injector_cmode_from_int(rst_well.hist_requested_control);
if (! i->predictionMode) {
i->clearControls();
if ((active_control != Well::InjectorCMode::RATE) &&
(active_control != Well::InjectorCMode::BHP))
{
throw std::invalid_argument {
fmt::format("Unsupported control mode '{}' for "
"history controlled injection well '{}'",
WellInjectorCMode2String(active_control), this->name())
};
}
}
i->controlMode = active_control;
i->addInjectionControl(active_control);
i->addInjectionControl(Well::InjectorCMode::BHP);
i->BHPTarget.update(rst_well.bhp_target_float);
if (! i->predictionMode) {
if (i->controlMode == Well::InjectorCMode::BHP)
i->bhp_hist_limit = rst_well.hist_bhp_target;
else
i->resetDefaultHistoricalBHPLimit();
}
else if (this->isAvailableForGroupControl())
i->addInjectionControl(Well::InjectorCMode::GRUP);
this->updateInjection(std::move(i));
if (!rst_well.tracer_concentration_injection.empty()) {
auto tracer = std::make_shared<WellTracerProperties>(this->getTracerProperties());
for (std::size_t tracer_index = 0; tracer_index < tracer_config.size(); tracer_index++) {
const auto& name = tracer_config[tracer_index].name;
const auto concentration = rst_well.tracer_concentration_injection[tracer_index];
tracer->setConcentration(name, concentration);
}
this->updateTracer(tracer);
}
}
}
Well::Well(const std::string& wname_arg,
const std::string& gname,
std::size_t init_step_arg,
std::size_t insert_index_arg,
int headI_arg,
int headJ_arg,
const std::optional<double>& ref_depth_arg,
const WellType& wtype_arg,
ProducerCMode whistctl_cmode,
Connection::Order ordering_arg,
const UnitSystem& unit_system_arg,
double udq_undefined_arg,
double dr,
bool allow_xflow,
bool auto_shutin,
int pvt_table_,
GasInflowEquation inflow_eq):
wname(wname_arg),
group_name(gname),
init_step(init_step_arg),
insert_index(insert_index_arg),
headI(headI_arg),
headJ(headJ_arg),
ref_depth(ref_depth_arg),
drainage_radius(dr),
allow_cross_flow(allow_xflow),
automatic_shutin(auto_shutin),
pvt_table(pvt_table_),
gas_inflow(inflow_eq),
unit_system(unit_system_arg),
udq_undefined(udq_undefined_arg),
wtype(wtype_arg),
guide_rate({true, -1, Well::GuideRateTarget::UNDEFINED,ParserKeywords::WGRUPCON::SCALING_FACTOR::defaultValue}),
efficiency_factor(1.0),
solvent_fraction(0.0),
econ_limits(std::make_shared<WellEconProductionLimits>()),
foam_properties(std::make_shared<WellFoamProperties>()),
polymer_properties(std::make_shared<WellPolymerProperties>()),
micp_properties(std::make_shared<WellMICPProperties>()),
brine_properties(std::make_shared<WellBrineProperties>()),
tracer_properties(std::make_shared<WellTracerProperties>()),
connections(std::make_shared<WellConnections>(ordering_arg, headI, headJ)),
production(std::make_shared<WellProductionProperties>(unit_system, wname)),
injection(std::make_shared<WellInjectionProperties>(unit_system, wname)),
wvfpdp(std::make_shared<WVFPDP>()),
wvfpexp(std::make_shared<WVFPEXP>()),
status(Status::SHUT),
well_temperature(Metric::TemperatureOffset + ParserKeywords::STCOND::TEMPERATURE::defaultValue),
well_inj_mult(std::nullopt)
{
auto p = std::make_shared<WellProductionProperties>(this->unit_system, this->wname);
p->whistctl_cmode = whistctl_cmode;
this->updateProduction(p);
}
Well Well::serializationTestObject()
{
Well result;
result.wname = "test1";
result.group_name = "test2";
result.init_step = 1;
result.insert_index = 2;
result.headI = 3;
result.headJ = 4;
result.ref_depth = 5;
result.unit_system = UnitSystem::serializationTestObject();
result.udq_undefined = 6.0;
result.status = Status::AUTO;
result.drainage_radius = 7.0;
result.allow_cross_flow = true;
result.automatic_shutin = false;
result.pvt_table = 77;
result.gas_inflow = GasInflowEquation::GPP;
result.wtype = WellType(Phase::WATER);
result.guide_rate = WellGuideRate::serializationTestObject();
result.efficiency_factor = 8.0;
result.solvent_fraction = 9.0;
result.prediction_mode = false;
result.econ_limits = std::make_shared<Opm::WellEconProductionLimits>(Opm::WellEconProductionLimits::serializationTestObject());
result.foam_properties = std::make_shared<WellFoamProperties>(WellFoamProperties::serializationTestObject());
result.polymer_properties = std::make_shared<WellPolymerProperties>(WellPolymerProperties::serializationTestObject());
result.micp_properties = std::make_shared<WellMICPProperties>(WellMICPProperties::serializationTestObject());
result.brine_properties = std::make_shared<WellBrineProperties>(WellBrineProperties::serializationTestObject());
result.tracer_properties = std::make_shared<WellTracerProperties>(WellTracerProperties::serializationTestObject());
result.connections = std::make_shared<WellConnections>(WellConnections::serializationTestObject());
result.production = std::make_shared<Well::WellProductionProperties>(Well::WellProductionProperties::serializationTestObject());
result.injection = std::make_shared<Well::WellInjectionProperties>(Well::WellInjectionProperties::serializationTestObject());
result.segments = std::make_shared<WellSegments>(WellSegments::serializationTestObject());
result.wvfpexp = std::make_shared<WVFPEXP>(WVFPEXP::serializationTestObject());
result.m_pavg = PAvg();
result.well_temperature = 10.0;
result.well_inj_mult = InjMult::serializationTestObject();
return result;
}
bool Well::updateWPAVE(const PAvg& pavg) {
if (this->m_pavg == pavg)
return false;
this->m_pavg = pavg;
return true;
}
bool Well::updateEfficiencyFactor(double efficiency_factor_arg) {
if (this->efficiency_factor != efficiency_factor_arg) {
this->efficiency_factor = efficiency_factor_arg;
return true;
}
return false;
}
bool Well::updateWellGuideRate(double guide_rate_arg) {
if (this->guide_rate.guide_rate != guide_rate_arg) {
this->guide_rate.guide_rate = guide_rate_arg;
return true;
}
return false;
}
bool Well::updateFoamProperties(std::shared_ptr<WellFoamProperties> foam_properties_arg) {
if (this->wtype.producer()) {
throw std::runtime_error("Not allowed to set foam injection properties for well " + name()
+ " since it is a production well");
}
if (*this->foam_properties != *foam_properties_arg) {
this->foam_properties = foam_properties_arg;
return true;
}
return false;
}
bool Well::updatePolymerProperties(std::shared_ptr<WellPolymerProperties> polymer_properties_arg) {
if (this->wtype.producer()) {
throw std::runtime_error("Not allowed to set polymer injection properties for well " + name() +
" since it is a production well");
}
if (*this->polymer_properties != *polymer_properties_arg) {
this->polymer_properties = polymer_properties_arg;
return true;
}
return false;
}
bool Well::updateMICPProperties(std::shared_ptr<WellMICPProperties> micp_properties_arg) {
if (this->wtype.producer()) {
throw std::runtime_error("Not allowed to set micp injection properties for well " + name() +
" since it is a production well");
}
if (*this->micp_properties != *micp_properties_arg) {
this->micp_properties = micp_properties_arg;
return true;
}
return false;
}
bool Well::updateBrineProperties(std::shared_ptr<WellBrineProperties> brine_properties_arg) {
if (this->wtype.producer()) {
throw std::runtime_error("Not allowed to set brine injection properties for well " + name() +
" since it is a production well");
}
if (*this->brine_properties != *brine_properties_arg) {
this->brine_properties = brine_properties_arg;
return true;
}
return false;
}
bool Well::updateEconLimits(std::shared_ptr<WellEconProductionLimits> econ_limits_arg) {
if (*this->econ_limits != *econ_limits_arg) {
this->econ_limits = econ_limits_arg;
return true;
}
return false;
}
bool Well::updateWVFPDP(std::shared_ptr<WVFPDP> wvfpdp_arg) {
if (*this->wvfpdp != *wvfpdp_arg) {
this->wvfpdp = std::move(wvfpdp_arg);
return true;
}
return false;
}
bool Well::updateWVFPEXP(std::shared_ptr<WVFPEXP> wvfpexp_arg) {
if (*this->wvfpexp != *wvfpexp_arg) {
this->wvfpexp = std::move(wvfpexp_arg);
return true;
}
return false;
}
void Well::switchToProducer() {
auto p = std::make_shared<WellInjectionProperties>(this->getInjectionProperties());
p->BHPTarget.update(0);
p->dropInjectionControl( Opm::Well::InjectorCMode::BHP );
this->updateInjection( p );
this->wtype.update(true);
}
void Well::switchToInjector() {
auto p = std::make_shared<WellProductionProperties>(getProductionProperties());
p->setBHPLimit(0);
p->dropProductionControl( ProducerCMode::BHP );
this->updateProduction( p );
}
bool Well::updateInjection(std::shared_ptr<WellInjectionProperties> injection_arg) {
auto update = this->wtype.update(injection_arg->injectorType);
if (this->wtype.producer()) {
this->switchToInjector();
update = true;
}
if (*this->injection != *injection_arg) {
this->injection = injection_arg;
update = true;
}
return update;
}
bool Well::updateWellProductivityIndex() {
return this->connections->prepareWellPIScaling();
}
bool Well::updateHasProduced() {
if (this->wtype.producer() && this->getStatus() == Status::OPEN) {
if (this->has_produced)
return false;
this->has_produced = true;
return true;
}
return false;
}
bool Well::updateHasInjected() {
if (this->wtype.injector() && this->getStatus() == Status::OPEN) {
if (this->has_injected)
return false;
this->has_injected= true;
return true;
}
return false;
}
bool Well::updateProduction(std::shared_ptr<WellProductionProperties> production_arg) {
if (!this->wtype.producer())
this->switchToProducer( );
if (*this->production != *production_arg) {
this->production = production_arg;
return true;
}
return false;
}
bool Well::updateTracer(std::shared_ptr<WellTracerProperties> tracer_properties_arg) {
if (*this->tracer_properties != *tracer_properties_arg) {
this->tracer_properties = tracer_properties_arg;
return true;
}
return false;
}
bool Well::updateWellGuideRate(bool available, double guide_rate_arg, GuideRateTarget guide_phase, double scale_factor) {
bool update = false;
if (this->guide_rate.available != available) {
this->guide_rate.available = available;
update = true;
}
if(this->guide_rate.guide_rate != guide_rate_arg) {
this->guide_rate.guide_rate = guide_rate_arg;
update = true;
}
if(this->guide_rate.guide_phase != guide_phase) {
this->guide_rate.guide_phase = guide_phase;
update = true;
}
if(this->guide_rate.scale_factor != scale_factor) {
this->guide_rate.scale_factor = scale_factor;
update = true;
}
return update;
}
bool Well::updateGroup(const std::string& group_arg) {
if (this->group_name != group_arg) {
this->group_name = group_arg;
return true;
}
return false;
}
bool Well::updateHead(int I, int J) {
bool update = false;
if (this->headI != I) {
this->headI = I;
update = true;
}
if (this->headJ != J) {
this->headJ = J;
update = true;
}
return update;
}
bool Well::updateStatus(Status well_state) {
this->status = well_state;
return true;
}
bool Well::updateRefDepth(const std::optional<double>& ref_depth_arg) {
if (this->ref_depth != ref_depth_arg) {
this->ref_depth = ref_depth_arg;
return true;
}
return false;
}
bool Well::updateDrainageRadius(double drainage_radius_arg) {
if (this->drainage_radius != drainage_radius_arg) {
this->drainage_radius = drainage_radius_arg;
return true;
}
return false;
}
bool Well::updateCrossFlow(bool allow_cross_flow_arg) {
if (this->allow_cross_flow != allow_cross_flow_arg) {
this->allow_cross_flow = allow_cross_flow_arg;
return true;
}
return false;
}
bool Well::updateAutoShutin(bool auto_shutin) {
if (this->automatic_shutin != auto_shutin) {
this->automatic_shutin = auto_shutin;
return true;
}
return false;
}
bool Well::updateConnections(std::shared_ptr<WellConnections> connections_arg, bool force) {
connections_arg->order( );
if (force || *this->connections != *connections_arg) {
this->connections = connections_arg;
return true;
}
return false;
}
bool Well::updateConnections(std::shared_ptr<WellConnections> connections_arg, const ScheduleGrid& grid) {
bool update = this->updateConnections(connections_arg, false);
if (this->pvt_table == 0 && !this->connections->empty()) {
const auto& lowest = this->connections->lowest();
const auto& props = grid.get_cell(lowest.getI(), lowest.getJ(), lowest.getK()).props;
this->pvt_table = props->pvtnum;
update = true;
}
return update;
}
bool Well::updateSolventFraction(double solvent_fraction_arg) {
if (this->solvent_fraction != solvent_fraction_arg) {
this->solvent_fraction = solvent_fraction_arg;
return true;
}
return false;
}
bool Well::handleCOMPSEGS(const DeckKeyword& keyword,
const ScheduleGrid& grid,
const ParseContext& parseContext,
ErrorGuard& errors) {
auto [new_connections, new_segments] = Compsegs::processCOMPSEGS(
keyword,
*this->connections,
*this->segments,
grid,
parseContext,
errors
);
this->updateConnections( std::make_shared<WellConnections>(std::move(new_connections)), false );
this->updateSegments( std::make_shared<WellSegments>( std::move(new_segments)) );
return true;
}
const std::string& Well::groupName() const {
return this->group_name;
}
bool Well::isMultiSegment() const {
if (this->segments)
return true;
return false;
}
bool Well::isProducer() const {
return this->wtype.producer();
}
bool Well::isInjector() const {
return this->wtype.injector();
}
const WellType& Well::wellType() const {
return this->wtype;
}
Well::InjectorCMode Well::injection_cmode() const {
if (this->isInjector())
return this->injection->controlMode;
throw std::logic_error(fmt::format("Queried for INJECTION cmode for producer: {}", this->name()));
}
Well::ProducerCMode Well::production_cmode() const {
if (this->isProducer())
return this->production->controlMode;
throw std::logic_error(fmt::format("Queried for PRODUCTION cmode for injector : {}", this->name()));
}
InjectorType Well::injectorType() const {
if (this->wtype.producer())
throw std::runtime_error("Can not access injectorType attribute of a producer");
return this->injection->injectorType;
}
bool Well::isAvailableForGroupControl() const {
return this->guide_rate.available;
}
double Well::getGuideRate() const {
return this->guide_rate.guide_rate;
}
Well::GuideRateTarget Well::getGuideRatePhase() const
{
const auto target = this->getRawGuideRatePhase();
if (this->isInjector() && (target == GuideRateTarget::RAT)) {
return this->preferredPhaseAsGuideRatePhase();
}
return target;
}
Well::GuideRateTarget Well::getRawGuideRatePhase() const
{
return this->guide_rate.guide_phase;
}
Well::GuideRateTarget Well::preferredPhaseAsGuideRatePhase() const
{
switch (this->getPreferredPhase()) {
case Phase::OIL: return GuideRateTarget::OIL;
case Phase::GAS: return GuideRateTarget::GAS;
case Phase::WATER: return GuideRateTarget::WAT;
default:
throw std::logic_error {
fmt::format("Unable to convert well preferred "
"phase {} to GuideRate target phase",
static_cast<int>(this->getPreferredPhase()))
};
}
}
double Well::getGuideRateScalingFactor() const {
return this->guide_rate.scale_factor;
}
double Well::getEfficiencyFactor() const {
return this->efficiency_factor;
}
double Well::getSolventFraction() const {
return this->solvent_fraction;
}
std::size_t Well::seqIndex() const {
return this->insert_index;
}
int Well::getHeadI() const {
return this->headI;
}
int Well::getHeadJ() const {
return this->headJ;
}
bool Well::getAutomaticShutIn() const {
return this->automatic_shutin;
}
bool Well::getAllowCrossFlow() const {
return this->allow_cross_flow;
}
bool Well::hasRefDepth() const
{
return this->ref_depth.has_value();
}
double Well::getRefDepth() const {
if (!this->hasRefDepth())
throw std::logic_error(fmt::format("Well: {} - tried to access not initialized well reference depth", this->name()));
return *this->ref_depth;
}
double Well::getWPaveRefDepth() const {
return this->wpave_ref_depth.value_or( this->getRefDepth() );
}
void Well::updateRefDepth() {
if( !this->ref_depth ) {
// ref depth was defaulted and we get the depth of the first completion
if( this->connections->empty() )
throw std::invalid_argument( "No completions defined for well: "
+ name()
+ ". Can not infer reference depth" );
this->ref_depth = this->connections->get(0).depth();
}
}
void Well::updateWPaveRefDepth(double depth) {
this->wpave_ref_depth = depth;
}
double Well::getDrainageRadius() const {
return this->drainage_radius;
}
//const std::vector<std::string>& Well::wListNames() const {
// return this->w_list_names;
//}
const std::string& Well::name() const {
return this->wname;
}
bool Well::hasSameConnectionsPointers(const Well& other) const
{
// Note: This is *supposed* to be a pointer comparison. We need to know
// if the two connection structures represent the exact same object, not
// just if they have the same value.
return this->connections == other.connections;
}
void Well::setInsertIndex(std::size_t index) {
this->insert_index = index;
}
double Well::convertDeckPI(double deckPI) const {
using M = UnitSystem::measure;
// XXX: Should really have LIQUID here too, but the 'Phase' type does
// not provide that enumerator.
switch (this->getPreferredPhase()) {
case Phase::GAS:
return this->unit_system.to_si(M::gas_productivity_index, deckPI);
case Phase::OIL:
case Phase::WATER:
return this->unit_system.to_si(M::liquid_productivity_index, deckPI);
default:
throw std::invalid_argument {
"Preferred phase " + std::to_string(static_cast<int>(this->getPreferredPhase())) +
" is not supported. Must be one of 'OIL', 'GAS', or 'WATER'"
};
}
}
void Well::applyWellProdIndexScaling(const double scalingFactor, std::vector<bool>& scalingApplicable) {
this->connections->applyWellPIScaling(scalingFactor, scalingApplicable);
}
bool Well::hasConnections() const {
return !this->connections->empty();
}
const WellConnections& Well::getConnections() const {
return *this->connections;
}
const std::vector<const Connection *> Well::getConnections(int completion) const {
std::vector<const Connection *> connvector;
for (const auto& conn : this->getConnections()) {
if (conn.complnum() == completion)
connvector.push_back( &conn );
}
return connvector;
}
const WellFoamProperties& Well::getFoamProperties() const {
return *this->foam_properties;
}
const WellPolymerProperties& Well::getPolymerProperties() const {
return *this->polymer_properties;
}
const WellMICPProperties& Well::getMICPProperties() const {
return *this->micp_properties;
}
const WellBrineProperties& Well::getBrineProperties() const {
return *this->brine_properties;
}
const WellTracerProperties& Well::getTracerProperties() const {
return *this->tracer_properties;
}
const WVFPDP& Well::getWVFPDP() const {
return *this->wvfpdp;
}
const WVFPEXP& Well::getWVFPEXP() const {
return *this->wvfpexp;
}
const WellEconProductionLimits& Well::getEconLimits() const {
return *this->econ_limits;
}
const Well::WellProductionProperties& Well::getProductionProperties() const {
return *this->production;
}
const WellSegments& Well::getSegments() const {
if (this->segments)
return *this->segments;
else
throw std::logic_error("Asked for segment information in not MSW well: " + this->name());
}
int Well::maxSegmentID() const
{
return (this->segments == nullptr)
? 0
: this->segments->maxSegmentID();
}
int Well::maxBranchID() const
{
return (this->segments == nullptr)
? 0
: this->segments->maxBranchID();
}
const Well::WellInjectionProperties& Well::getInjectionProperties() const {
return *this->injection;
}
Well::Status Well::getStatus() const {
return this->status;
}
const PAvg& Well::pavg() const {
return this->m_pavg;
}
std::map<int, std::vector<Connection>> Well::getCompletions() const {
std::map<int, std::vector<Connection>> completions;
for (const auto& conn : *this->connections) {
auto pair = completions.find( conn.complnum() );
if (pair == completions.end())
completions[conn.complnum()] = {};
pair = completions.find(conn.complnum());
pair->second.push_back(conn);
}
return completions;
}
bool Well::hasCompletion(int completion) const {
for (const auto& conn : *this->connections) {
if (conn.complnum() == completion)
return true;
}
return false;
}
Phase Well::getPreferredPhase() const {
return this->wtype.preferred_phase();
}
int Well::pvt_table_number() const {
return this->pvt_table;
}
int Well::fip_region_number() const {
return ParserKeywords::WELSPECS::FIP_REGION::defaultValue;
}
/*
When all connections of a well are closed with the WELOPEN keywords, the well
itself should also be SHUT. In the main parsing code this is handled by the
function checkIfAllConnectionsIsShut() which is called at the end of every
report step in Schedule::iterateScheduleSection(). This is done in this way
because there is some twisted logic aggregating connection changes over a
complete report step.
However - when the WELOPEN is called runtime (typically as an ACTIONX action)
the full Schedule::iterateScheduleSection() is not run and the check if all
connections is closed is not done. Therefor we have a runtime flag here
which makes sure to close the well in this case.
*/
bool Well::handleWELOPENConnections(const DeckRecord& record, Connection::State state_arg) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_eq(c.getK(), record, "K", -1)) return false;
if (!match_ge(c.complnum(), record, "C1")) return false;
if (!match_le(c.complnum(), record, "C2")) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
for (auto c : *this->connections) {
if (match(c))
c.setState( state_arg );
new_connections->add(c);
}
return this->updateConnections(std::move(new_connections), false);
}
bool Well::handleCOMPLUMP(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_eq(c.getI(), record, "I" , -1)) return false;
if (!match_eq(c.getJ(), record, "J" , -1)) return false;
if (!match_ge(c.getK(), record, "K1", -1)) return false;
if (!match_le(c.getK(), record, "K2", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
const int complnum = record.getItem("N").get<int>(0);
if (complnum <= 0)
throw std::invalid_argument("Completion number must be >= 1. COMPLNUM=" + std::to_string(complnum) + "is invalid");
for (auto c : *this->connections) {
if (match(c))
c.setComplnum( complnum );
new_connections->add(c);
}
return this->updateConnections(std::move(new_connections), false);
}
bool Well::handleWPIMULT(const DeckRecord& record) {
auto match = [=]( const Connection &c) -> bool {
if (!match_ge(c.complnum(), record, "FIRST")) return false;
if (!match_le(c.complnum(), record, "LAST")) return false;
if (!match_eq(c.getI() , record, "I", -1)) return false;
if (!match_eq(c.getJ() , record, "J", -1)) return false;
if (!match_eq(c.getK() , record, "K", -1)) return false;
return true;
};
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
double wellPi = record.getItem("WELLPI").get< double >(0);
for (auto c : *this->connections) {
if (match(c))
c.scaleWellPi( wellPi );
new_connections->add(c);
}
return this->updateConnections(std::move(new_connections), false);
}
bool Well::handleWINJDAM(const Opm::DeckRecord& record)
{
return false;
}
bool Well::handleWINJMULT(const Opm::DeckRecord& record, const KeywordLocation& location) {
// for this keyword, the default for I, J, K will be negative
// it is not totally clear how specifying 0 or a negative values will work
// current match_eq function only treats 0 and default values for all connections,
// we might need to revisit this part later when complication regarding this occurs.
// it is possible that changing (item.get<int>(0) == 0); to (item.get<int>(0) <= 0) is solution to go
// while it remains to be discussed.
auto match = [=] ( const Connection& c) -> bool {
if (!match_eq(c.getI() , record, "I", -1)) return false;
if (!match_eq(c.getJ() , record, "J", -1)) return false;
if (!match_eq(c.getK() , record, "K", -1)) return false;
return true;
};
using Kw = ParserKeywords::WINJMULT;
const InjMultMode mode = InjMult::injMultModeFromString(record.getItem<Kw::MODE>().getTrimmedString(0), location);
const bool mode_change = (this->inj_mult_mode != mode);
if (mode_change) {
this->inj_mult_mode = mode;
}
const double fracture_pressure = record.getItem<Kw::FRACTURING_PRESSURE>().getSIDouble(0);
const double multiple_gradient = record.getItem<Kw::MULTIPLIER_GRADIENT>().getSIDouble(0);
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
const InjMult inj_mult {fracture_pressure, multiple_gradient};
bool connections_update = false;
bool well_inj_update = false;
if (mode == InjMultMode::WREV) {
// all the connections will share the same INJMULT setup when under WREV
// it is stored in the Well object
this->well_inj_mult = inj_mult;
well_inj_update = true;
} else if (mode == InjMultMode::CREV || mode == InjMultMode::CIRR){
for (auto c : *this->connections) {
if (match(c)) {
c.setInjMult(inj_mult);
}
new_connections->add(c);
}
connections_update = this->updateConnections(std::move(new_connections), false);
}
return mode_change || connections_update || well_inj_update;
}
bool Opm::Well::applyGlobalWPIMULT(const double scaling_factor)
{
auto new_connections = std::make_shared<WellConnections>(this->connections->ordering(), this->headI, this->headJ);
for (auto c : *this->connections) {
c.scaleWellPi(scaling_factor);
new_connections->add(c);
}
return this->updateConnections(std::move(new_connections), false);
}
void Well::updateSegments(std::shared_ptr<WellSegments> segments_arg) {
this->segments = std::move(segments_arg);
this->updateRefDepth( this->segments->depthTopSegment() );
}
bool Well::handleWELSEGS(const DeckKeyword& keyword) {
if (this->segments) {
auto new_segments = std::make_shared<WellSegments>( *this->segments );
new_segments->loadWELSEGS(keyword);
this->updateSegments(std::move(new_segments));
} else
this->updateSegments( std::make_shared<WellSegments>(keyword) );
return true;
}
bool Well::updatePVTTable(int pvt_table_) {
if (this->pvt_table != pvt_table_) {
this->pvt_table = pvt_table_;
return true;
} else
return false;
}
bool Well::updateWSEGSICD(const std::vector<std::pair<int, SICD> >& sicd_pairs) {
auto new_segments = std::make_shared<WellSegments>(*this->segments);
if (new_segments->updateWSEGSICD(sicd_pairs)) {
this->segments = new_segments;
return true;
} else
return false;
}
bool Well::updateWSEGAICD(const std::vector<std::pair<int, AutoICD> >& aicd_pairs, const KeywordLocation& location) {
auto new_segments = std::make_shared<WellSegments>(*this->segments);
if (new_segments->updateWSEGAICD(aicd_pairs, location)) {
this->segments = new_segments;
return true;
} else
return false;
}
bool Well::updateWSEGVALV(const std::vector<std::pair<int, Valve> >& valve_pairs) {
auto new_segments = std::make_shared<WellSegments>(*this->segments);
if (new_segments->updateWSEGVALV(valve_pairs)) {
this->segments = new_segments;
return true;
} else
return false;
}
void Well::filterConnections(const ActiveGridCells& grid) {
this->connections->filter(grid);
}
std::size_t Well::firstTimeStep() const {
return this->init_step;
}
bool Well::hasBeenDefined(std::size_t timeStep) const {
if (timeStep < this->init_step)
return false;
else
return true;
}
Well::GasInflowEquation Well::gas_inflow_equation() const {
return this->gas_inflow;
}
bool Well::predictionMode() const {
return this->prediction_mode;
}
bool Well::hasProduced( ) const {
return this->has_produced;
}
bool Well::hasInjected( ) const {
return this->has_injected;
}
bool Well::updatePrediction(bool prediction_mode_arg) {
if (this->prediction_mode != prediction_mode_arg) {
this->prediction_mode = prediction_mode_arg;
return true;
}
return false;
}
double Well::production_rate(const SummaryState& st, Phase prod_phase) const {
if( !this->isProducer() ) return 0.0;
const auto controls = this->productionControls(st);
switch( prod_phase ) {
case Phase::WATER: return controls.water_rate;
case Phase::OIL: return controls.oil_rate;
case Phase::GAS: return controls.gas_rate;
case Phase::SOLVENT:
throw std::invalid_argument( "Production of 'SOLVENT' requested." );
case Phase::POLYMER:
throw std::invalid_argument( "Production of 'POLYMER' requested." );
case Phase::ENERGY:
throw std::invalid_argument( "Production of 'ENERGY' requested." );
case Phase::POLYMW:
throw std::invalid_argument( "Production of 'POLYMW' requested.");
case Phase::FOAM:
throw std::invalid_argument( "Production of 'FOAM' requested.");
case Phase::BRINE:
throw std::invalid_argument( "Production of 'BRINE' requested.");
case Phase::ZFRACTION:
throw std::invalid_argument( "Production of 'ZFRACTION' requested.");
}
throw std::logic_error( "Unreachable state. Invalid Phase value. "
"This is likely a programming error." );
}
double Well::injection_rate(const SummaryState& st, Phase phase_arg) const {
if( !this->isInjector() ) return 0.0;
const auto controls = this->injectionControls(st);
const auto type = controls.injector_type;
if( phase_arg == Phase::WATER && type != InjectorType::WATER ) return 0.0;
if( phase_arg == Phase::OIL && type != InjectorType::OIL ) return 0.0;
if( phase_arg == Phase::GAS && type != InjectorType::GAS ) return 0.0;
return controls.surface_rate;
}
bool Well::wellNameInWellNamePattern(const std::string& wellName, const std::string& wellNamePattern) {
bool wellNameInPattern = false;
if (shmatch( wellNamePattern, wellName)) {
wellNameInPattern = true;
}
return wellNameInPattern;
}
Well::ProductionControls Well::productionControls(const SummaryState& st) const {
if (this->isProducer()) {
auto controls = this->production->controls(st, this->udq_undefined);
controls.prediction_mode = this->predictionMode();
return controls;
} else
throw std::logic_error("Trying to get production data from an injector");
}
Well::InjectionControls Well::injectionControls(const SummaryState& st) const {
if (!this->isProducer()) {
auto controls = this->injection->controls(this->unit_system, st, this->udq_undefined);
controls.prediction_mode = this->predictionMode();
return controls;
} else
throw std::logic_error("Trying to get injection data from a producer");
}
/*
These accessor functions are at the "wrong" level of abstraction; the same
properties are part of the InjectionControls and ProductionControls structs.
They are made available here to avoid passing a SummaryState instance in
situations where it is not really needed.
*/
int Well::vfp_table_number() const {
if (this->wtype.producer())
return this->production->VFPTableNumber;
else
return this->injection->VFPTableNumber;
}
/*
This short circuits the UDA and assumes the UDA contains a double.
*/
double Well::alq_value() const {
if (this->wtype.producer())
return this->production->ALQValue.getSI();
throw std::runtime_error("Can not ask for ALQ value in an injector");
}
double Well::temperature() const {
if (!this->wtype.producer())
return this->well_temperature;
throw std::runtime_error("Can only ask for temperature in an injector");
}
void Well::setWellTemperature(const double temp) {
this->well_temperature = temp;
}
bool Well::cmp_structure(const Well& other) const {
if ((this->segments && !other.segments) ||
(!this->segments && other.segments))
{
return false;
}
if (this->segments && (this->getSegments() != other.getSegments())) {
return false;
}
return (this->name() == other.name())
&& (this->groupName() == other.groupName())
&& (this->firstTimeStep() == other.firstTimeStep())
&& (this->seqIndex() == other.seqIndex())
&& (this->getHeadI() == other.getHeadI())
&& (this->getHeadJ() == other.getHeadJ())
&& (this->hasRefDepth() == other.hasRefDepth())
&& (!this->hasRefDepth() || (this->getRefDepth() == other.getRefDepth()))
&& (this->getPreferredPhase() == other.getPreferredPhase())
&& (this->unit_system == other.unit_system)
&& (this->udq_undefined == other.udq_undefined)
&& (this->getConnections() == other.getConnections())
&& (this->getDrainageRadius() == other.getDrainageRadius())
&& (this->getAllowCrossFlow() == other.getAllowCrossFlow())
&& (this->getAutomaticShutIn() == other.getAutomaticShutIn())
&& (this->getEfficiencyFactor() == other.getEfficiencyFactor())
;
}
bool Well::operator==(const Well& data) const {
return this->cmp_structure(data)
&& (this->getSolventFraction() == data.getSolventFraction())
&& (this->getEconLimits() == data.getEconLimits())
&& (this->isProducer() == data.isProducer())
&& (this->getFoamProperties() == data.getFoamProperties())
&& (this->getStatus() == data.getStatus())
&& (this->guide_rate == data.guide_rate)
&& (this->solvent_fraction == data.solvent_fraction)
&& (this->hasProduced() == data.hasProduced())
&& (this->hasInjected() == data.hasInjected())
&& (this->predictionMode() == data.predictionMode())
&& (this->getTracerProperties() == data.getTracerProperties())
&& (this->getWVFPEXP() == data.getWVFPEXP())
&& (this->getProductionProperties() == data.getProductionProperties())
&& (this->m_pavg == data.m_pavg)
&& (this->getInjectionProperties() == data.getInjectionProperties())
&& (this->well_temperature == data.well_temperature)
&& (this->inj_mult_mode == data.inj_mult_mode)
&& (this->well_inj_mult == data.well_inj_mult)
;
}
} // namespace Opm
int Opm::Well::eclipseControlMode(const Well::InjectorCMode imode,
const InjectorType itype)
{
using IMode = ::Opm::Well::InjectorCMode;
using Val = ::Opm::RestartIO::Helpers::VectorItems::IWell::Value::WellCtrlMode;
using IType = ::Opm::InjectorType;
switch (imode) {
case IMode::RATE: {
switch (itype) {
case IType::OIL: return Val::OilRate;
case IType::WATER: return Val::WatRate;
case IType::GAS: return Val::GasRate;
case IType::MULTI: return Val::WMCtlUnk;
}}
break;
case IMode::RESV: return Val::ResVRate;
case IMode::THP: return Val::THP;
case IMode::BHP: return Val::BHP;
case IMode::GRUP: return Val::Group;
default:
return Val::WMCtlUnk;
}
return Val::WMCtlUnk;
}
int Opm::Well::eclipseControlMode(const Opm::Well::ProducerCMode pmode)
{
using PMode = ::Opm::Well::ProducerCMode;
using Val = ::Opm::RestartIO::Helpers::VectorItems::IWell::Value::WellCtrlMode;
switch (pmode) {
case PMode::ORAT: return Val::OilRate;
case PMode::WRAT: return Val::WatRate;
case PMode::GRAT: return Val::GasRate;
case PMode::LRAT: return Val::LiqRate;
case PMode::RESV: return Val::ResVRate;
case PMode::THP: return Val::THP;
case PMode::BHP: return Val::BHP;
case PMode::CRAT: return Val::CombRate;
case PMode::GRUP: return Val::Group;
default:
return Val::WMCtlUnk;
}
return Val::WMCtlUnk;
}
/*
The purpose of this function is to convert OPM well status to an integer value
suitable for output in the eclipse restart file. In OPM we have different
variables for the wells status and the active control, when this is written to
a restart file they are combined to one integer. In OPM a well can have an
active control while still being shut, when this is converted to an integer
value suitable for the eclipse formatted restart file the value 0 will be used
to signal a SHUT well and the active control will be lost.
In the case of a well which is in state 'STOP' or 'AUTO' an integer
corresponding to the currently active control is writte to the restart file.
*/
int Opm::Well::eclipseControlMode(const Well& well,
const SummaryState& st)
{
if (well.isProducer()) {
const auto& ctrl = well.productionControls(st);
return eclipseControlMode(ctrl.cmode);
}
else { // Injector
const auto& ctrl = well.injectionControls(st);
return eclipseControlMode(ctrl.cmode, well.injectorType());
}
}
Opm::Well::InjMultMode Opm::Well::getInjMultMode() const {
return this->inj_mult_mode;
}
const Opm::InjMult& Opm::Well::getWellInjMult() const {
assert(this->aciveWellInjMult());
return this->well_inj_mult.value();
}
bool Opm::Well::aciveWellInjMult() const {
return this->well_inj_mult.has_value();
}