mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-01-04 13:36:57 -06:00
Add support for explicit guiderate for injection groups
With this commit the guiderate logic used for the production groups is also used for injectors This allows for setting guiderates explicit at different group levels Only RATE, NETV and VOID guiderate type is suppored.
This commit is contained in:
parent
8adea8c68a
commit
f4ac7f555b
@ -260,16 +260,24 @@ public:
|
||||
(*this)(d);
|
||||
};
|
||||
|
||||
auto keyHandle = [&](auto& d)
|
||||
{
|
||||
if constexpr (is_pair<Key>::value)
|
||||
pair(d);
|
||||
else
|
||||
(*this)(d);
|
||||
};
|
||||
|
||||
if (m_op == Operation::PACKSIZE) {
|
||||
m_packSize += Mpi::packSize(data.size(), m_comm);
|
||||
for (auto& it : data) {
|
||||
m_packSize += Mpi::packSize(it.first, m_comm);
|
||||
keyHandle(it.first);
|
||||
handle(it.second);
|
||||
}
|
||||
} else if (m_op == Operation::PACK) {
|
||||
Mpi::pack(data.size(), m_buffer, m_position, m_comm);
|
||||
for (auto& it : data) {
|
||||
Mpi::pack(it.first, m_buffer, m_position, m_comm);
|
||||
keyHandle(it.first);
|
||||
handle(it.second);
|
||||
}
|
||||
} else if (m_op == Operation::UNPACK) {
|
||||
@ -277,7 +285,7 @@ public:
|
||||
Mpi::unpack(size, m_buffer, m_position, m_comm);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
Key key;
|
||||
Mpi::unpack(key, m_buffer, m_position, m_comm);
|
||||
keyHandle(key);
|
||||
Data entry;
|
||||
handle(entry);
|
||||
data.insert(std::make_pair(key, entry));
|
||||
|
@ -501,6 +501,7 @@ namespace Opm {
|
||||
void assignGroupControl(const Group& group, data::GroupData& gdata) const;
|
||||
data::GuideRateValue getGuideRateValues(const Well& well) const;
|
||||
data::GuideRateValue getGuideRateValues(const Group& group) const;
|
||||
data::GuideRateValue getGuideRateInjectionGroupValues(const Group& group) const;
|
||||
void getGuideRateValues(const GuideRate::RateVector& qs,
|
||||
const bool is_inj,
|
||||
const std::string& wgname,
|
||||
|
@ -1319,6 +1319,7 @@ namespace Opm {
|
||||
well_state_nupcol_ = well_state_;
|
||||
}
|
||||
|
||||
const auto& summaryState = ebosSimulator_.vanguard().summaryState();
|
||||
// the group target reduction rates needs to be update since wells may have swicthed to/from GRUP control
|
||||
// Currently the group target reduction does not honor NUPCOL. TODO: is that true?
|
||||
std::vector<double> groupTargetReduction(numPhases(), 0.0);
|
||||
@ -1326,13 +1327,13 @@ namespace Opm {
|
||||
std::vector<double> groupTargetReductionInj(numPhases(), 0.0);
|
||||
WellGroupHelpers::updateGroupTargetReduction(fieldGroup, schedule(), reportStepIdx, /*isInjector*/ true, phase_usage_, *guideRate_, well_state_nupcol_, well_state_, groupTargetReductionInj);
|
||||
|
||||
// the guiderate update should not be part of this function
|
||||
// remove in seperate PR since it affects existing functionality
|
||||
const double simulationTime = ebosSimulator_.time();
|
||||
std::vector<double> pot(numPhases(), 0.0);
|
||||
WellGroupHelpers::updateGuideRateForGroups(fieldGroup, schedule(), phase_usage_, reportStepIdx, simulationTime, /*isInjector*/ false, well_state_, comm, guideRate_.get(), pot);
|
||||
std::vector<double> potInj(numPhases(), 0.0);
|
||||
WellGroupHelpers::updateGuideRateForGroups(fieldGroup, schedule(), phase_usage_, reportStepIdx, simulationTime, /*isInjector*/ true, well_state_, comm, guideRate_.get(), potInj);
|
||||
WellGroupHelpers::updateGuideRateForProductionGroups(fieldGroup, schedule(), phase_usage_, reportStepIdx, simulationTime, well_state_, comm, guideRate_.get(), pot);
|
||||
WellGroupHelpers::updateGuideRatesForInjectionGroups(fieldGroup, schedule(), summaryState, phase_usage_, reportStepIdx, well_state_, guideRate_.get());
|
||||
|
||||
const auto& summaryState = ebosSimulator_.vanguard().summaryState();
|
||||
WellGroupHelpers::updateREINForGroups(fieldGroup, schedule(), reportStepIdx, phase_usage_, summaryState, well_state_nupcol_, well_state_);
|
||||
WellGroupHelpers::updateVREPForGroups(fieldGroup, schedule(), reportStepIdx, well_state_nupcol_, well_state_);
|
||||
|
||||
@ -2830,7 +2831,7 @@ namespace Opm {
|
||||
|
||||
for (const auto& wname : sched.wellNames(reportStepIdx)) {
|
||||
if (! (this->well_state_.hasWellRates(wname) &&
|
||||
this->guideRate_->has(wname)))
|
||||
this->guideRate_->hasProductionGroupOrWell(wname)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@ -2867,10 +2868,15 @@ namespace Opm {
|
||||
const auto& gname = up[start + gi];
|
||||
const auto& group = sched.getGroup(gname, reportStepIdx);
|
||||
|
||||
if (this->guideRate_->has(gname)) {
|
||||
if (this->guideRate_->hasProductionGroupOrWell(gname)) {
|
||||
gr[gname].production = this->getGuideRateValues(group);
|
||||
}
|
||||
|
||||
if (this->guideRate_->hasInjectionGroup(::Opm::Phase::WATER, gname)
|
||||
|| this->guideRate_->hasInjectionGroup(::Opm::Phase::GAS, gname)) {
|
||||
gr[gname].injection = this->getGuideRateInjectionGroupValues(group);
|
||||
}
|
||||
|
||||
const auto parent = group.parent();
|
||||
if (parent == "FIELD") { continue; }
|
||||
|
||||
@ -2943,18 +2949,40 @@ namespace Opm {
|
||||
return grval;
|
||||
}
|
||||
|
||||
if (! this->guideRate_->has(wname)) {
|
||||
if (! this->guideRate_->hasProductionGroupOrWell(wname)) {
|
||||
// No guiderates exist for 'wname'.
|
||||
return grval;
|
||||
}
|
||||
|
||||
const auto qs = WellGroupHelpers::
|
||||
getRateVector(this->well_state_, this->phase_usage_, wname);
|
||||
getWellRateVector(this->well_state_, this->phase_usage_, wname);
|
||||
|
||||
this->getGuideRateValues(qs, well.isInjector(), wname, grval);
|
||||
|
||||
return grval;
|
||||
}
|
||||
template <typename TypeTag>
|
||||
data::GuideRateValue
|
||||
BlackoilWellModel<TypeTag>::
|
||||
getGuideRateInjectionGroupValues(const Group& group) const
|
||||
{
|
||||
auto grval = data::GuideRateValue{};
|
||||
|
||||
assert (this->guideRate_ != nullptr);
|
||||
|
||||
const auto& gname = group.name();
|
||||
if (this->guideRate_->hasInjectionGroup(Opm::Phase::GAS, gname)) {
|
||||
// No guiderates exist for 'gname'.
|
||||
grval.set(data::GuideRateValue::Item::Gas,
|
||||
this->guideRate_->getInjectionGroup(Opm::Phase::GAS, gname));
|
||||
}
|
||||
if (this->guideRate_->hasInjectionGroup(Opm::Phase::WATER, gname)) {
|
||||
// No guiderates exist for 'gname'.
|
||||
grval.set(data::GuideRateValue::Item::Water,
|
||||
this->guideRate_->getInjectionGroup(Opm::Phase::WATER, gname));
|
||||
}
|
||||
return grval;
|
||||
}
|
||||
|
||||
template <typename TypeTag>
|
||||
data::GuideRateValue
|
||||
@ -2966,20 +2994,21 @@ namespace Opm {
|
||||
assert (this->guideRate_ != nullptr);
|
||||
|
||||
const auto& gname = group.name();
|
||||
if (! this->well_state_.hasProductionGroupRates(gname)) {
|
||||
// No flow rates for 'gname' -- might be before group comes
|
||||
|
||||
if ( ! this->well_state_.hasProductionGroupRates(gname)) {
|
||||
// No flow rates for production group 'gname' -- might be before group comes
|
||||
// online (e.g., for the initial condition before simulation
|
||||
// starts).
|
||||
return grval;
|
||||
}
|
||||
|
||||
if (! this->guideRate_->has(gname)) {
|
||||
if (! this->guideRate_->hasProductionGroupOrWell(gname)) {
|
||||
// No guiderates exist for 'gname'.
|
||||
return grval;
|
||||
}
|
||||
|
||||
const auto qs = WellGroupHelpers::
|
||||
getProductionGroupRateVector(this->well_state_, this->phase_usage_, gname);
|
||||
getProductionGroupRateVector(this->well_state_, this->phase_usage_, gname);
|
||||
|
||||
const auto is_inj = false; // This procedure only applies to G*PGR.
|
||||
this->getGuideRateValues(qs, is_inj, gname, grval);
|
||||
@ -2997,7 +3026,7 @@ namespace Opm {
|
||||
{
|
||||
auto getGR = [this, &wgname, &qs](const GuideRateModel::Target t)
|
||||
{
|
||||
return this->guideRate_->get(wgname, t, qs);
|
||||
return this->guideRate_->getProductionGroupOrWell(wgname, t, qs);
|
||||
};
|
||||
|
||||
// Note: GuideRate does currently (2020-07-20) not support Target::RES.
|
||||
@ -3026,7 +3055,7 @@ namespace Opm {
|
||||
|
||||
auto xgrPos = groupGuideRates.find(group.name());
|
||||
if ((xgrPos == groupGuideRates.end()) ||
|
||||
!this->guideRate_->has(group.name()))
|
||||
!this->guideRate_->hasProductionGroupOrWell(group.name()))
|
||||
{
|
||||
// No guiderates defined for this group.
|
||||
return;
|
||||
|
@ -163,6 +163,126 @@ namespace WellGroupHelpers
|
||||
const double group_grat_target_from_sales_;
|
||||
};
|
||||
|
||||
|
||||
/// Based on a group control mode, extract or calculate rates, and
|
||||
/// provide other conveniences.
|
||||
class InjectionTargetCalculator
|
||||
{
|
||||
public:
|
||||
InjectionTargetCalculator(const Group::InjectionCMode cmode,
|
||||
const PhaseUsage& pu,
|
||||
const std::vector<double>& resv_coeff,
|
||||
const std::string& group_name,
|
||||
const double sales_target,
|
||||
const WellStateFullyImplicitBlackoil& well_state,
|
||||
const Phase& injection_phase)
|
||||
: cmode_(cmode)
|
||||
, pu_(pu)
|
||||
, resv_coeff_(resv_coeff)
|
||||
, group_name_(group_name)
|
||||
, sales_target_(sales_target)
|
||||
, well_state_(well_state)
|
||||
{
|
||||
// initialize to avoid warning
|
||||
pos_ = pu.phase_pos[BlackoilPhases::Aqua];
|
||||
target_ = GuideRateModel::Target::WAT;
|
||||
|
||||
switch (injection_phase) {
|
||||
case Phase::WATER: {
|
||||
pos_ = pu.phase_pos[BlackoilPhases::Aqua];
|
||||
target_ = GuideRateModel::Target::WAT;
|
||||
break;
|
||||
}
|
||||
case Phase::OIL: {
|
||||
pos_ = pu.phase_pos[BlackoilPhases::Liquid];
|
||||
target_ = GuideRateModel::Target::OIL;
|
||||
break;
|
||||
}
|
||||
case Phase::GAS: {
|
||||
pos_ = pu.phase_pos[BlackoilPhases::Vapour];
|
||||
target_ = GuideRateModel::Target::GAS;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename RateVec>
|
||||
auto calcModeRateFromRates(const RateVec& rates) const
|
||||
{
|
||||
return rates[pos_];
|
||||
}
|
||||
|
||||
double groupTarget(const Group::InjectionControls ctrl) const
|
||||
{
|
||||
switch (cmode_) {
|
||||
case Group::InjectionCMode::RATE:
|
||||
return ctrl.surface_max_rate;
|
||||
case Group::InjectionCMode::RESV:
|
||||
return ctrl.resv_max_rate;
|
||||
case Group::InjectionCMode::REIN: {
|
||||
double production_rate = well_state_.currentInjectionREINRates(ctrl.reinj_group)[pos_];
|
||||
return ctrl.target_reinj_fraction * production_rate;
|
||||
}
|
||||
case Group::InjectionCMode::VREP: {
|
||||
const std::vector<double>& group_injection_reductions
|
||||
= well_state_.currentInjectionGroupReductionRates(group_name_);
|
||||
double voidage_rate
|
||||
= well_state_.currentInjectionVREPRates(ctrl.voidage_group) * ctrl.target_void_fraction;
|
||||
double inj_reduction = 0.0;
|
||||
if (ctrl.phase != Phase::WATER)
|
||||
inj_reduction += group_injection_reductions[pu_.phase_pos[BlackoilPhases::Aqua]]
|
||||
* resv_coeff_[pu_.phase_pos[BlackoilPhases::Aqua]];
|
||||
if (ctrl.phase != Phase::OIL)
|
||||
inj_reduction += group_injection_reductions[pu_.phase_pos[BlackoilPhases::Liquid]]
|
||||
* resv_coeff_[pu_.phase_pos[BlackoilPhases::Liquid]];
|
||||
if (ctrl.phase != Phase::GAS)
|
||||
inj_reduction += group_injection_reductions[pu_.phase_pos[BlackoilPhases::Vapour]]
|
||||
* resv_coeff_[pu_.phase_pos[BlackoilPhases::Vapour]];
|
||||
voidage_rate -= inj_reduction;
|
||||
return voidage_rate / resv_coeff_[pos_];
|
||||
}
|
||||
case Group::InjectionCMode::SALE: {
|
||||
assert(pos_ == pu_.phase_pos[BlackoilPhases::Vapour]);
|
||||
// Gas injection rate = Total gas production rate + gas import rate - gas consumption rate - sales rate;
|
||||
// Gas import and consumption is already included in the REIN rates
|
||||
double inj_rate = well_state_.currentInjectionREINRates(group_name_)[pos_];
|
||||
inj_rate -= sales_target_;
|
||||
return inj_rate;
|
||||
}
|
||||
default:
|
||||
// Should never be here.
|
||||
assert(false);
|
||||
return 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
GuideRateModel::Target guideTargetMode() const
|
||||
{
|
||||
return target_;
|
||||
}
|
||||
|
||||
private:
|
||||
template <typename ElemType>
|
||||
static ElemType zero()
|
||||
{
|
||||
// This is for Evaluation types.
|
||||
ElemType x;
|
||||
x = 0.0;
|
||||
return x;
|
||||
}
|
||||
Group::InjectionCMode cmode_;
|
||||
const PhaseUsage& pu_;
|
||||
const std::vector<double>& resv_coeff_;
|
||||
const std::string& group_name_;
|
||||
double sales_target_;
|
||||
const WellStateFullyImplicitBlackoil& well_state_;
|
||||
int pos_;
|
||||
GuideRateModel::Target target_;
|
||||
|
||||
};
|
||||
} // namespace WellGroupHelpers
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -227,6 +227,60 @@ namespace WellGroupHelpers
|
||||
return gefac * rate;
|
||||
}
|
||||
|
||||
void updateGuideRatesForInjectionGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const Opm::PhaseUsage& pu,
|
||||
const int reportStepIdx,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
GuideRate* guideRate)
|
||||
{
|
||||
for (const std::string& groupName : group.groups()) {
|
||||
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
|
||||
updateGuideRatesForInjectionGroups(
|
||||
groupTmp, schedule, summaryState, pu, reportStepIdx, wellState, guideRate);
|
||||
}
|
||||
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
|
||||
for (Phase phase : all) {
|
||||
|
||||
if(!group.hasInjectionControl(phase))
|
||||
continue;
|
||||
|
||||
double guideRateValue = 0.0;
|
||||
const auto& controls = group.injectionControls(phase, summaryState);
|
||||
switch (controls.guide_rate_def){
|
||||
case Group::GuideRateInjTarget::RATE:
|
||||
break;
|
||||
case Group::GuideRateInjTarget::VOID:
|
||||
{
|
||||
guideRateValue = wellState.currentInjectionVREPRates(group.name());
|
||||
break;
|
||||
}
|
||||
case Group::GuideRateInjTarget::NETV:
|
||||
{
|
||||
guideRateValue = wellState.currentInjectionVREPRates(group.name());
|
||||
const std::vector<double>& injRES = wellState.currentInjectionGroupReservoirRates(group.name());
|
||||
if (phase != Phase::OIL && pu.phase_used[BlackoilPhases::Liquid])
|
||||
guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Liquid]];
|
||||
if (phase != Phase::GAS && pu.phase_used[BlackoilPhases::Vapour])
|
||||
guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Vapour]];
|
||||
if (phase != Phase::WATER && pu.phase_used[BlackoilPhases::Aqua])
|
||||
guideRateValue -= injRES[pu.phase_pos[BlackoilPhases::Aqua]];
|
||||
break;
|
||||
}
|
||||
case Group::GuideRateInjTarget::RESV:
|
||||
OPM_THROW(std::runtime_error, "GUIDE PHASE RESV not implemented. Group " + group.name());
|
||||
case Group::GuideRateInjTarget::POTN:
|
||||
break;
|
||||
case Group::GuideRateInjTarget::NO_GUIDE_RATE:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
guideRate->injectionGroupCompute(group.name(), phase, reportStepIdx, guideRateValue);
|
||||
}
|
||||
}
|
||||
|
||||
void updateGroupTargetReduction(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const int reportStepIdx,
|
||||
@ -282,7 +336,7 @@ namespace WellGroupHelpers
|
||||
const bool individual_control = (currentGroupControl != Group::ProductionCMode::FLD
|
||||
&& currentGroupControl != Group::ProductionCMode::NONE);
|
||||
const int num_group_controlled_wells
|
||||
= groupControlledWells(schedule, wellStateNupcol, reportStepIdx, subGroupName, "");
|
||||
= groupControlledWells(schedule, wellStateNupcol, reportStepIdx, subGroupName, "", !isInjector, Phase::OIL);
|
||||
if (individual_control || num_group_controlled_wells == 0) {
|
||||
for (int phase = 0; phase < np; phase++) {
|
||||
groupTargetReduction[phase]
|
||||
@ -290,7 +344,7 @@ namespace WellGroupHelpers
|
||||
}
|
||||
} else {
|
||||
// The subgroup may participate in group control.
|
||||
if (!guide_rate.has(subGroupName)) {
|
||||
if (!guide_rate.hasProductionGroupOrWell(subGroupName)) {
|
||||
// Accumulate from this subgroup only if no group guide rate is set for it.
|
||||
for (int phase = 0; phase < np; phase++) {
|
||||
groupTargetReduction[phase] += subGroupTargetReduction[phase];
|
||||
@ -582,6 +636,7 @@ namespace WellGroupHelpers
|
||||
wellState.setCurrentProductionGroupRates(group.name(), rates);
|
||||
}
|
||||
|
||||
|
||||
void updateREINForGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const int reportStepIdx,
|
||||
@ -738,7 +793,7 @@ namespace WellGroupHelpers
|
||||
|
||||
|
||||
GuideRate::RateVector
|
||||
getRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& name)
|
||||
getWellRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& name)
|
||||
{
|
||||
return getGuideRateVector(well_state.currentWellRates(name), pu);
|
||||
}
|
||||
@ -757,8 +812,12 @@ namespace WellGroupHelpers
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu)
|
||||
{
|
||||
if (schedule.hasWell(name, reportStepIdx) || guideRate->has(name)) {
|
||||
return guideRate->get(name, target, getRateVector(wellState, pu, name));
|
||||
if (schedule.hasWell(name, reportStepIdx)) {
|
||||
return guideRate->getProductionGroupOrWell(name, target, getWellRateVector(wellState, pu, name));
|
||||
}
|
||||
|
||||
if (guideRate->hasProductionGroupOrWell(name)) {
|
||||
return guideRate->getProductionGroupOrWell(name, target, getProductionGroupRateVector(wellState, pu, name));
|
||||
}
|
||||
|
||||
double totalGuideRate = 0.0;
|
||||
@ -786,7 +845,7 @@ namespace WellGroupHelpers
|
||||
if (!wellState.isProductionGrup(wellName))
|
||||
continue;
|
||||
|
||||
totalGuideRate += guideRate->get(wellName, target, getRateVector(wellState, pu, wellName));
|
||||
totalGuideRate += guideRate->getProductionGroupOrWell(wellName, target, getWellRateVector(wellState, pu, wellName));
|
||||
}
|
||||
return totalGuideRate;
|
||||
}
|
||||
@ -802,7 +861,11 @@ namespace WellGroupHelpers
|
||||
const PhaseUsage& pu)
|
||||
{
|
||||
if (schedule.hasWell(name, reportStepIdx)) {
|
||||
return guideRate->get(name, target, getRateVector(wellState, pu, name));
|
||||
return guideRate->getProductionGroupOrWell(name, target, getWellRateVector(wellState, pu, name));
|
||||
}
|
||||
|
||||
if (guideRate->hasInjectionGroup(injectionPhase, name)) {
|
||||
return guideRate->getInjectionGroup(injectionPhase, name);
|
||||
}
|
||||
|
||||
double totalGuideRate = 0.0;
|
||||
@ -832,7 +895,7 @@ namespace WellGroupHelpers
|
||||
if (!wellState.isInjectionGrup(wellName))
|
||||
continue;
|
||||
|
||||
totalGuideRate += guideRate->get(wellName, target, getRateVector(wellState, pu, wellName));
|
||||
totalGuideRate += guideRate->getProductionGroupOrWell(wellName, target, getWellRateVector(wellState, pu, wellName));
|
||||
}
|
||||
return totalGuideRate;
|
||||
}
|
||||
@ -843,21 +906,35 @@ namespace WellGroupHelpers
|
||||
const WellStateFullyImplicitBlackoil& well_state,
|
||||
const int report_step,
|
||||
const std::string& group_name,
|
||||
const std::string& always_included_child)
|
||||
const std::string& always_included_child,
|
||||
const bool is_production_group,
|
||||
const Phase injection_phase)
|
||||
{
|
||||
const Group& group = schedule.getGroup(group_name, report_step);
|
||||
int num_wells = 0;
|
||||
for (const std::string& child_group : group.groups()) {
|
||||
const auto ctrl = well_state.currentProductionGroupControl(child_group);
|
||||
const bool included = (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE)
|
||||
|| (child_group == always_included_child);
|
||||
|
||||
bool included = (child_group == always_included_child);
|
||||
if (is_production_group) {
|
||||
const auto ctrl = well_state.currentProductionGroupControl(child_group);
|
||||
included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE);
|
||||
} else {
|
||||
const auto ctrl = well_state.currentInjectionGroupControl(injection_phase, child_group);
|
||||
included = included || (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE);
|
||||
}
|
||||
|
||||
if (included) {
|
||||
num_wells
|
||||
+= groupControlledWells(schedule, well_state, report_step, child_group, always_included_child);
|
||||
+= groupControlledWells(schedule, well_state, report_step, child_group, always_included_child, is_production_group, injection_phase);
|
||||
}
|
||||
}
|
||||
for (const std::string& child_well : group.wells()) {
|
||||
const bool included = (well_state.isProductionGrup(child_well)) || (child_well == always_included_child);
|
||||
bool included = (child_well == always_included_child);
|
||||
if (is_production_group) {
|
||||
included = included || well_state.isProductionGrup(child_well);
|
||||
} else {
|
||||
included = included || well_state.isInjectionGrup(child_well);
|
||||
}
|
||||
if (included) {
|
||||
++num_wells;
|
||||
}
|
||||
@ -866,17 +943,23 @@ namespace WellGroupHelpers
|
||||
}
|
||||
|
||||
FractionCalculator::FractionCalculator(const Schedule& schedule,
|
||||
const SummaryState& summary_state,
|
||||
const WellStateFullyImplicitBlackoil& well_state,
|
||||
const int report_step,
|
||||
const GuideRate* guide_rate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu)
|
||||
const PhaseUsage& pu,
|
||||
const bool is_producer,
|
||||
const Phase injection_phase)
|
||||
: schedule_(schedule)
|
||||
, summary_state_(summary_state)
|
||||
, well_state_(well_state)
|
||||
, report_step_(report_step)
|
||||
, guide_rate_(guide_rate)
|
||||
, target_(target)
|
||||
, pu_(pu)
|
||||
, is_producer_(is_producer)
|
||||
, injection_phase_(injection_phase)
|
||||
{
|
||||
}
|
||||
double FractionCalculator::fraction(const std::string& name,
|
||||
@ -912,15 +995,26 @@ namespace WellGroupHelpers
|
||||
{
|
||||
double total_guide_rate = 0.0;
|
||||
for (const std::string& child_group : group.groups()) {
|
||||
const auto ctrl = well_state_.currentProductionGroupControl(child_group);
|
||||
const bool included = (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE)
|
||||
|| (child_group == always_included_child);
|
||||
bool included = (child_group == always_included_child);
|
||||
if (is_producer_) {
|
||||
const auto ctrl = well_state_.currentProductionGroupControl(child_group);
|
||||
included = included || (ctrl == Group::ProductionCMode::FLD) || (ctrl == Group::ProductionCMode::NONE);
|
||||
} else {
|
||||
const auto ctrl = well_state_.currentInjectionGroupControl(injection_phase_, child_group);
|
||||
included = included || (ctrl == Group::InjectionCMode::FLD) || (ctrl == Group::InjectionCMode::NONE);
|
||||
}
|
||||
if (included) {
|
||||
total_guide_rate += guideRate(child_group, always_included_child);
|
||||
}
|
||||
}
|
||||
for (const std::string& child_well : group.wells()) {
|
||||
const bool included = (well_state_.isProductionGrup(child_well)) || (child_well == always_included_child);
|
||||
bool included = (child_well == always_included_child);
|
||||
if (is_producer_) {
|
||||
included = included || well_state_.isProductionGrup(child_well);
|
||||
} else {
|
||||
included = included || well_state_.isInjectionGrup(child_well);
|
||||
}
|
||||
|
||||
if (included) {
|
||||
total_guide_rate += guideRate(child_well, always_included_child);
|
||||
}
|
||||
@ -930,11 +1024,13 @@ namespace WellGroupHelpers
|
||||
double FractionCalculator::guideRate(const std::string& name, const std::string& always_included_child)
|
||||
{
|
||||
if (schedule_.hasWell(name, report_step_)) {
|
||||
return guide_rate_->get(name, target_, getRateVector(well_state_, pu_, name));
|
||||
return guide_rate_->getProductionGroupOrWell(name, target_, getWellRateVector(well_state_, pu_, name));
|
||||
} else {
|
||||
if (groupControlledWells(name, always_included_child) > 0) {
|
||||
if (guide_rate_->has(name)) {
|
||||
return guide_rate_->get(name, target_, getGroupRateVector(name));
|
||||
if (is_producer_ && guide_rate_->hasProductionGroupOrWell(name)) {
|
||||
return guide_rate_->getProductionGroupOrWell(name, target_, getGroupRateVector(name));
|
||||
} else if (!is_producer_ && guide_rate_->hasInjectionGroup(injection_phase_, name)) {
|
||||
return guide_rate_->getInjectionGroup(injection_phase_, name);
|
||||
} else {
|
||||
// We are a group, with default guide rate.
|
||||
// Compute guide rate by accumulating our children's guide rates.
|
||||
@ -951,250 +1047,16 @@ namespace WellGroupHelpers
|
||||
const std::string& always_included_child)
|
||||
{
|
||||
return ::Opm::WellGroupHelpers::groupControlledWells(
|
||||
schedule_, well_state_, report_step_, group_name, always_included_child);
|
||||
schedule_, well_state_, report_step_, group_name, always_included_child, is_producer_, injection_phase_);
|
||||
}
|
||||
|
||||
GuideRate::RateVector FractionCalculator::getGroupRateVector(const std::string& group_name)
|
||||
{
|
||||
assert(is_producer_);
|
||||
return getProductionGroupRateVector(this->well_state_, this->pu_, group_name);
|
||||
}
|
||||
|
||||
|
||||
double fractionFromGuideRates(const std::string& name,
|
||||
const std::string& controlGroupName,
|
||||
const Schedule& schedule,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu,
|
||||
const bool alwaysIncludeThis)
|
||||
{
|
||||
FractionCalculator calc(schedule, wellState, reportStepIdx, guideRate, target, pu);
|
||||
return calc.fraction(name, controlGroupName, alwaysIncludeThis);
|
||||
}
|
||||
|
||||
double fractionFromInjectionPotentials(const std::string& name,
|
||||
const std::string& controlGroupName,
|
||||
const Schedule& schedule,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu,
|
||||
const Phase& injectionPhase,
|
||||
const bool alwaysIncludeThis)
|
||||
{
|
||||
double thisGuideRate
|
||||
= getGuideRateInj(name, schedule, wellState, reportStepIdx, guideRate, target, injectionPhase, pu);
|
||||
double controlGroupGuideRate = getGuideRateInj(
|
||||
controlGroupName, schedule, wellState, reportStepIdx, guideRate, target, injectionPhase, pu);
|
||||
if (alwaysIncludeThis)
|
||||
controlGroupGuideRate += thisGuideRate;
|
||||
|
||||
assert(controlGroupGuideRate >= thisGuideRate);
|
||||
const double guideRateEpsilon = 1e-12;
|
||||
return (controlGroupGuideRate > guideRateEpsilon) ? thisGuideRate / controlGroupGuideRate : 0.0;
|
||||
}
|
||||
|
||||
|
||||
std::pair<bool, double> checkGroupConstraintsInj(const std::string& name,
|
||||
const std::string& parent,
|
||||
const Group& group,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const double* rates,
|
||||
Phase injectionPhase,
|
||||
const PhaseUsage& pu,
|
||||
const double efficiencyFactor,
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const std::vector<double>& resv_coeff,
|
||||
DeferredLogger& deferred_logger)
|
||||
{
|
||||
// When called for a well ('name' is a well name), 'parent'
|
||||
// will be the name of 'group'. But if we recurse, 'name' and
|
||||
// 'parent' will stay fixed while 'group' will be higher up
|
||||
// in the group tree.
|
||||
// efficiency factor is the well efficiency factor for the first group the well is
|
||||
// part of. Later it is the accumulated factor including the group efficiency factor
|
||||
// of the child of group.
|
||||
|
||||
const Group::InjectionCMode& currentGroupControl
|
||||
= wellState.currentInjectionGroupControl(injectionPhase, group.name());
|
||||
if (currentGroupControl == Group::InjectionCMode::FLD || currentGroupControl == Group::InjectionCMode::NONE) {
|
||||
// Return if we are not available for parent group.
|
||||
if (!group.injectionGroupControlAvailable(injectionPhase)) {
|
||||
return std::make_pair(false, 1.0);
|
||||
}
|
||||
// Otherwise: check injection share of parent's control.
|
||||
const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx);
|
||||
return checkGroupConstraintsInj(name,
|
||||
parent,
|
||||
parentGroup,
|
||||
wellState,
|
||||
reportStepIdx,
|
||||
guideRate,
|
||||
rates,
|
||||
injectionPhase,
|
||||
pu,
|
||||
efficiencyFactor * group.getGroupEfficiencyFactor(),
|
||||
schedule,
|
||||
summaryState,
|
||||
resv_coeff,
|
||||
deferred_logger);
|
||||
}
|
||||
|
||||
// If we are here, we are at the topmost group to be visited in the recursion.
|
||||
// This is the group containing the control we will check against.
|
||||
|
||||
// This can be false for FLD-controlled groups, we must therefore
|
||||
// check for FLD first (done above).
|
||||
if (!group.isInjectionGroup()) {
|
||||
return std::make_pair(false, 1.0);
|
||||
}
|
||||
|
||||
int phasePos;
|
||||
GuideRateModel::Target target;
|
||||
|
||||
switch (injectionPhase) {
|
||||
case Phase::WATER: {
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Aqua];
|
||||
target = GuideRateModel::Target::WAT;
|
||||
break;
|
||||
}
|
||||
case Phase::OIL: {
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Liquid];
|
||||
target = GuideRateModel::Target::OIL;
|
||||
break;
|
||||
}
|
||||
case Phase::GAS: {
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Vapour];
|
||||
target = GuideRateModel::Target::GAS;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_DEFLOG_THROW(
|
||||
std::logic_error, "Expected WATER, OIL or GAS as injecting type for " + name, deferred_logger);
|
||||
}
|
||||
|
||||
assert(group.hasInjectionControl(injectionPhase));
|
||||
const auto& groupcontrols = group.injectionControls(injectionPhase, summaryState);
|
||||
|
||||
const std::vector<double>& groupInjectionReductions
|
||||
= wellState.currentInjectionGroupReductionRates(group.name());
|
||||
const double groupTargetReduction = groupInjectionReductions[phasePos];
|
||||
double fraction = fractionFromInjectionPotentials(
|
||||
name, group.name(), schedule, wellState, reportStepIdx, guideRate, target, pu, injectionPhase, true);
|
||||
double target_fraction = 1.0;
|
||||
bool constraint_broken = false;
|
||||
double efficiencyFactorInclGroup = efficiencyFactor * group.getGroupEfficiencyFactor();
|
||||
|
||||
switch (currentGroupControl) {
|
||||
case Group::InjectionCMode::RATE: {
|
||||
const double current_rate = rates[phasePos];
|
||||
const double target_rate = fraction
|
||||
* std::max(0.0,
|
||||
(groupcontrols.surface_max_rate - groupTargetReduction + current_rate * efficiencyFactorInclGroup))
|
||||
/ efficiencyFactorInclGroup;
|
||||
if (current_rate > target_rate) {
|
||||
constraint_broken = true;
|
||||
target_fraction = target_rate / current_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::RESV: {
|
||||
const double coeff = resv_coeff[phasePos];
|
||||
const double current_rate = rates[phasePos];
|
||||
const double target_rate = fraction
|
||||
* std::max(0.0,
|
||||
(groupcontrols.resv_max_rate / coeff - groupTargetReduction
|
||||
+ current_rate * efficiencyFactorInclGroup))
|
||||
/ efficiencyFactorInclGroup;
|
||||
if (current_rate > target_rate) {
|
||||
constraint_broken = true;
|
||||
target_fraction = target_rate / current_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::REIN: {
|
||||
double productionRate = wellState.currentInjectionREINRates(groupcontrols.reinj_group)[phasePos];
|
||||
const double current_rate = rates[phasePos];
|
||||
const double target_rate = fraction
|
||||
* std::max(0.0,
|
||||
(groupcontrols.target_reinj_fraction * productionRate - groupTargetReduction
|
||||
+ current_rate * efficiencyFactorInclGroup))
|
||||
/ efficiencyFactorInclGroup;
|
||||
if (current_rate > target_rate) {
|
||||
constraint_broken = true;
|
||||
target_fraction = target_rate / current_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::VREP: {
|
||||
const double coeff = resv_coeff[phasePos];
|
||||
double voidageRate
|
||||
= wellState.currentInjectionVREPRates(groupcontrols.voidage_group) * groupcontrols.target_void_fraction;
|
||||
|
||||
double injReduction = 0.0;
|
||||
if (groupcontrols.phase != Phase::WATER)
|
||||
injReduction += groupInjectionReductions[pu.phase_pos[BlackoilPhases::Aqua]]
|
||||
* resv_coeff[pu.phase_pos[BlackoilPhases::Aqua]];
|
||||
if (groupcontrols.phase != Phase::OIL)
|
||||
injReduction += groupInjectionReductions[pu.phase_pos[BlackoilPhases::Liquid]]
|
||||
* resv_coeff[pu.phase_pos[BlackoilPhases::Liquid]];
|
||||
if (groupcontrols.phase != Phase::GAS)
|
||||
injReduction += groupInjectionReductions[pu.phase_pos[BlackoilPhases::Vapour]]
|
||||
* resv_coeff[pu.phase_pos[BlackoilPhases::Vapour]];
|
||||
voidageRate -= injReduction;
|
||||
|
||||
const double current_rate = rates[phasePos];
|
||||
const double target_rate = fraction
|
||||
* std::max(0.0, (voidageRate / coeff - groupTargetReduction + current_rate * efficiencyFactorInclGroup))
|
||||
/ efficiencyFactorInclGroup;
|
||||
if (current_rate > target_rate) {
|
||||
constraint_broken = true;
|
||||
target_fraction = target_rate / current_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::SALE: {
|
||||
// only for gas injectors
|
||||
assert(phasePos == pu.phase_pos[BlackoilPhases::Vapour]);
|
||||
|
||||
// Gas injection rate = Total gas production rate + gas import rate - gas consumption rate - sales rate;
|
||||
// Gas import and consumption is already included in the REIN rates
|
||||
double inj_rate = wellState.currentInjectionREINRates(group.name())[phasePos];
|
||||
const auto& gconsale = schedule[reportStepIdx].gconsale().get(group.name(), summaryState);
|
||||
inj_rate -= gconsale.sales_target;
|
||||
|
||||
const double current_rate = rates[phasePos];
|
||||
const double target_rate = fraction
|
||||
* std::max(0.0, (inj_rate - groupTargetReduction + current_rate * efficiencyFactorInclGroup)) / efficiencyFactorInclGroup;
|
||||
if (current_rate > target_rate) {
|
||||
constraint_broken = true;
|
||||
target_fraction = target_rate / current_rate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::NONE: {
|
||||
assert(false); // Should already be handled at the top.
|
||||
}
|
||||
case Group::InjectionCMode::FLD: {
|
||||
assert(false); // Should already be handled at the top.
|
||||
}
|
||||
default:
|
||||
OPM_DEFLOG_THROW(
|
||||
std::runtime_error, "Invalid group control specified for group " + group.name(), deferred_logger);
|
||||
}
|
||||
|
||||
return std::make_pair(constraint_broken, target_fraction);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
std::vector<std::string>
|
||||
groupChainTopBot(const std::string& bottom, const std::string& top, const Schedule& schedule, const int report_step)
|
||||
{
|
||||
@ -1286,7 +1148,7 @@ namespace WellGroupHelpers
|
||||
gratTargetFromSales = wellState.currentGroupGratTargetFromSales(group.name());
|
||||
|
||||
TargetCalculator tcalc(currentGroupControl, pu, resv_coeff, gratTargetFromSales);
|
||||
FractionCalculator fcalc(schedule, wellState, reportStepIdx, guideRate, tcalc.guideTargetMode(), pu);
|
||||
FractionCalculator fcalc(schedule, summaryState, wellState, reportStepIdx, guideRate, tcalc.guideTargetMode(), pu, true, Phase::OIL);
|
||||
|
||||
auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); };
|
||||
|
||||
@ -1308,7 +1170,7 @@ namespace WellGroupHelpers
|
||||
// we need to find out the level where the current well is applied to the local reduction
|
||||
size_t local_reduction_level = 0;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guideRate->has(chain[ii])) {
|
||||
if ((ii == 0) || guideRate->hasProductionGroupOrWell(chain[ii])) {
|
||||
local_reduction_level = ii;
|
||||
}
|
||||
}
|
||||
@ -1316,7 +1178,7 @@ namespace WellGroupHelpers
|
||||
double efficiencyFactorInclGroup = efficiencyFactor * group.getGroupEfficiencyFactor();
|
||||
double target = orig_target;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guideRate->has(chain[ii])) {
|
||||
if ((ii == 0) || guideRate->hasProductionGroupOrWell(chain[ii])) {
|
||||
// Apply local reductions only at the control level
|
||||
// (top) and for levels where we have a specified
|
||||
// group guide rate.
|
||||
@ -1333,9 +1195,132 @@ namespace WellGroupHelpers
|
||||
// the current well to be always included, because we
|
||||
// want to know the situation that applied to the
|
||||
// calculation of reductions.
|
||||
const int num_gr_ctrl = groupControlledWells(schedule, wellState, reportStepIdx, chain[ii + 1], "");
|
||||
const int num_gr_ctrl = groupControlledWells(schedule, wellState, reportStepIdx, chain[ii + 1], "", true, Phase::OIL);
|
||||
if (num_gr_ctrl == 0) {
|
||||
if (guideRate->has(chain[ii + 1])) {
|
||||
if (guideRate->hasProductionGroupOrWell(chain[ii + 1])) {
|
||||
target += localReduction(chain[ii + 1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
target *= localFraction(chain[ii + 1]);
|
||||
}
|
||||
// Avoid negative target rates comming from too large local reductions.
|
||||
const double target_rate = std::max(1e-12, target / efficiencyFactorInclGroup);
|
||||
return std::make_pair(current_rate > target_rate, target_rate / current_rate);
|
||||
}
|
||||
|
||||
std::pair<bool, double> checkGroupConstraintsInj(const std::string& name,
|
||||
const std::string& parent,
|
||||
const Group& group,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const double* rates,
|
||||
Phase injectionPhase,
|
||||
const PhaseUsage& pu,
|
||||
const double efficiencyFactor,
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const std::vector<double>& resv_coeff,
|
||||
DeferredLogger& deferred_logger)
|
||||
{
|
||||
// When called for a well ('name' is a well name), 'parent'
|
||||
// will be the name of 'group'. But if we recurse, 'name' and
|
||||
// 'parent' will stay fixed while 'group' will be higher up
|
||||
// in the group tree.
|
||||
// efficiencyfactor is the well efficiency factor for the first group the well is
|
||||
// part of. Later it is the accumulated factor including the group efficiency factor
|
||||
// of the child of group.
|
||||
|
||||
const Group::InjectionCMode& currentGroupControl = wellState.currentInjectionGroupControl(injectionPhase, group.name());
|
||||
|
||||
if (currentGroupControl == Group::InjectionCMode::FLD || currentGroupControl == Group::InjectionCMode::NONE) {
|
||||
// Return if we are not available for parent group.
|
||||
if (!group.injectionGroupControlAvailable(injectionPhase)) {
|
||||
return std::make_pair(false, 1);
|
||||
}
|
||||
// Otherwise: check production share of parent's control.
|
||||
const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx);
|
||||
return checkGroupConstraintsInj(name,
|
||||
parent,
|
||||
parentGroup,
|
||||
wellState,
|
||||
reportStepIdx,
|
||||
guideRate,
|
||||
rates,
|
||||
injectionPhase,
|
||||
pu,
|
||||
efficiencyFactor * group.getGroupEfficiencyFactor(),
|
||||
schedule,
|
||||
summaryState,
|
||||
resv_coeff,
|
||||
deferred_logger);
|
||||
}
|
||||
|
||||
// This can be false for FLD-controlled groups, we must therefore
|
||||
// check for FLD first (done above).
|
||||
if (!group.isInjectionGroup()) {
|
||||
return std::make_pair(false, 1.0);
|
||||
}
|
||||
|
||||
// If we are here, we are at the topmost group to be visited in the recursion.
|
||||
// This is the group containing the control we will check against.
|
||||
double sales_target = 0;
|
||||
if (schedule[reportStepIdx].gconsale().has(group.name())) {
|
||||
const auto& gconsale = schedule[reportStepIdx].gconsale().get(group.name(), summaryState);
|
||||
sales_target = gconsale.sales_target;
|
||||
}
|
||||
InjectionTargetCalculator tcalc(currentGroupControl, pu, resv_coeff, group.name(), sales_target, wellState, injectionPhase);
|
||||
FractionCalculator fcalc(schedule, summaryState, wellState, reportStepIdx, guideRate, tcalc.guideTargetMode(), pu, false, injectionPhase);
|
||||
|
||||
auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); };
|
||||
|
||||
auto localReduction = [&](const std::string& group_name) {
|
||||
const std::vector<double>& groupTargetReductions
|
||||
= wellState.currentInjectionGroupReductionRates(group_name);
|
||||
return tcalc.calcModeRateFromRates(groupTargetReductions);
|
||||
};
|
||||
|
||||
const double orig_target = tcalc.groupTarget(group.injectionControls(injectionPhase, summaryState));
|
||||
// Assume we have a chain of groups as follows: BOTTOM -> MIDDLE -> TOP.
|
||||
// Then ...
|
||||
// TODO finish explanation.
|
||||
const double current_rate
|
||||
= tcalc.calcModeRateFromRates(rates); // Switch sign since 'rates' are negative for producers.
|
||||
const auto chain = groupChainTopBot(name, group.name(), schedule, reportStepIdx);
|
||||
// Because 'name' is the last of the elements, and not an ancestor, we subtract one below.
|
||||
const size_t num_ancestors = chain.size() - 1;
|
||||
// we need to find out the level where the current well is applied to the local reduction
|
||||
size_t local_reduction_level = 0;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guideRate->hasInjectionGroup(injectionPhase, chain[ii])) {
|
||||
local_reduction_level = ii;
|
||||
}
|
||||
}
|
||||
|
||||
double efficiencyFactorInclGroup = efficiencyFactor * group.getGroupEfficiencyFactor();
|
||||
double target = orig_target;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guideRate->hasInjectionGroup(injectionPhase, chain[ii])) {
|
||||
// Apply local reductions only at the control level
|
||||
// (top) and for levels where we have a specified
|
||||
// group guide rate.
|
||||
target -= localReduction(chain[ii]);
|
||||
|
||||
// Add my reduction back at the level where it is included in the local reduction
|
||||
if (local_reduction_level == ii )
|
||||
target += current_rate * efficiencyFactorInclGroup;
|
||||
}
|
||||
if (ii < num_ancestors - 1) {
|
||||
// Not final level. Add sub-level reduction back, if
|
||||
// it was nonzero due to having no group-controlled
|
||||
// wells. Note that we make this call without setting
|
||||
// the current well to be always included, because we
|
||||
// want to know the situation that applied to the
|
||||
// calculation of reductions.
|
||||
const int num_gr_ctrl = groupControlledWells(schedule, wellState, reportStepIdx, chain[ii + 1], "", false, injectionPhase);
|
||||
if (num_gr_ctrl == 0) {
|
||||
if (guideRate->hasInjectionGroup(injectionPhase, chain[ii + 1])) {
|
||||
target += localReduction(chain[ii + 1]);
|
||||
}
|
||||
}
|
||||
@ -1348,6 +1333,7 @@ namespace WellGroupHelpers
|
||||
}
|
||||
|
||||
|
||||
|
||||
} // namespace WellGroupHelpers
|
||||
|
||||
} // namespace Opm
|
||||
|
@ -95,16 +95,15 @@ namespace WellGroupHelpers
|
||||
std::vector<double>& groupTargetReduction);
|
||||
|
||||
template <class Comm>
|
||||
void updateGuideRateForGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const PhaseUsage& pu,
|
||||
const int reportStepIdx,
|
||||
const double& simTime,
|
||||
const bool isInjector,
|
||||
WellStateFullyImplicitBlackoil& wellState,
|
||||
const Comm& comm,
|
||||
GuideRate* guideRate,
|
||||
std::vector<double>& pot)
|
||||
void updateGuideRateForProductionGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const PhaseUsage& pu,
|
||||
const int reportStepIdx,
|
||||
const double& simTime,
|
||||
WellStateFullyImplicitBlackoil& wellState,
|
||||
const Comm& comm,
|
||||
GuideRate* guideRate,
|
||||
std::vector<double>& pot)
|
||||
{
|
||||
const int np = pu.num_phases;
|
||||
for (const std::string& groupName : group.groups()) {
|
||||
@ -112,44 +111,25 @@ namespace WellGroupHelpers
|
||||
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
|
||||
|
||||
// Note that group effiency factors for groupTmp are applied in updateGuideRateForGroups
|
||||
updateGuideRateForGroups(
|
||||
groupTmp, schedule, pu, reportStepIdx, simTime, isInjector, wellState, comm, guideRate, thisPot);
|
||||
updateGuideRateForProductionGroups(
|
||||
groupTmp, schedule, pu, reportStepIdx, simTime, wellState, comm, guideRate, thisPot);
|
||||
|
||||
// accumulate group contribution from sub group unconditionally
|
||||
if (isInjector) {
|
||||
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
|
||||
for (Phase phase : all) {
|
||||
int phasePos;
|
||||
if (phase == Phase::GAS && pu.phase_used[BlackoilPhases::Vapour])
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Vapour];
|
||||
else if (phase == Phase::OIL && pu.phase_used[BlackoilPhases::Liquid])
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Liquid];
|
||||
else if (phase == Phase::WATER && pu.phase_used[BlackoilPhases::Aqua])
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Aqua];
|
||||
else
|
||||
continue;
|
||||
|
||||
pot[phasePos] += thisPot[phasePos];
|
||||
}
|
||||
} else {
|
||||
const Group::ProductionCMode& currentGroupControl = wellState.currentProductionGroupControl(groupName);
|
||||
if (currentGroupControl != Group::ProductionCMode::FLD
|
||||
const Group::ProductionCMode& currentGroupControl = wellState.currentProductionGroupControl(groupName);
|
||||
if (currentGroupControl != Group::ProductionCMode::FLD
|
||||
&& currentGroupControl != Group::ProductionCMode::NONE) {
|
||||
continue;
|
||||
}
|
||||
for (int phase = 0; phase < np; phase++) {
|
||||
pot[phase] += thisPot[phase];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (int phase = 0; phase < np; phase++) {
|
||||
pot[phase] += thisPot[phase];
|
||||
}
|
||||
|
||||
}
|
||||
for (const std::string& wellName : group.wells()) {
|
||||
const auto& wellTmp = schedule.getWell(wellName, reportStepIdx);
|
||||
const auto wefac = wellTmp.getEfficiencyFactor();
|
||||
|
||||
if (wellTmp.isProducer() && isInjector)
|
||||
continue;
|
||||
|
||||
if (wellTmp.isInjector() && !isInjector)
|
||||
if (wellTmp.isInjector())
|
||||
continue;
|
||||
|
||||
if (wellTmp.getStatus() == Well::Status::SHUT)
|
||||
@ -195,12 +175,7 @@ namespace WellGroupHelpers
|
||||
oilPot = comm.sum(oilPot);
|
||||
gasPot = comm.sum(gasPot);
|
||||
waterPot = comm.sum(waterPot);
|
||||
|
||||
if (isInjector) {
|
||||
wellState.setCurrentGroupInjectionPotentials(group.name(), pot);
|
||||
} else {
|
||||
guideRate->compute(group.name(), reportStepIdx, simTime, oilPot, gasPot, waterPot);
|
||||
}
|
||||
guideRate->productionGroupCompute(group.name(), reportStepIdx, simTime, oilPot, gasPot, waterPot);
|
||||
}
|
||||
|
||||
template <class Comm>
|
||||
@ -238,10 +213,17 @@ namespace WellGroupHelpers
|
||||
oilpot = comm.sum(oilpot);
|
||||
gaspot = comm.sum(gaspot);
|
||||
waterpot = comm.sum(waterpot);
|
||||
guideRate->compute(well.name(), reportStepIdx, simTime, oilpot, gaspot, waterpot);
|
||||
guideRate->wellCompute(well.name(), reportStepIdx, simTime, oilpot, gaspot, waterpot);
|
||||
}
|
||||
}
|
||||
|
||||
void updateGuideRatesForInjectionGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const Opm::PhaseUsage& pu,
|
||||
const int reportStepIdx,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
GuideRate* guideRate);
|
||||
|
||||
void updateVREPForGroups(const Group& group,
|
||||
const Schedule& schedule,
|
||||
@ -283,7 +265,7 @@ namespace WellGroupHelpers
|
||||
const int report_time_step);
|
||||
|
||||
GuideRate::RateVector
|
||||
getRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& name);
|
||||
getWellRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& name);
|
||||
|
||||
GuideRate::RateVector
|
||||
getProductionGroupRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& group_name);
|
||||
@ -310,18 +292,23 @@ namespace WellGroupHelpers
|
||||
const WellStateFullyImplicitBlackoil& well_state,
|
||||
const int report_step,
|
||||
const std::string& group_name,
|
||||
const std::string& always_included_child);
|
||||
const std::string& always_included_child,
|
||||
const bool is_production_group,
|
||||
const Phase injection_phase);
|
||||
|
||||
|
||||
class FractionCalculator
|
||||
{
|
||||
public:
|
||||
FractionCalculator(const Schedule& schedule,
|
||||
const SummaryState& summary_state,
|
||||
const WellStateFullyImplicitBlackoil& well_state,
|
||||
const int report_step,
|
||||
const GuideRate* guide_rate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu);
|
||||
const PhaseUsage& pu,
|
||||
const bool is_producer,
|
||||
const Phase injection_phase);
|
||||
double fraction(const std::string& name, const std::string& control_group_name, const bool always_include_this);
|
||||
double localFraction(const std::string& name, const std::string& always_included_child);
|
||||
|
||||
@ -332,36 +319,17 @@ namespace WellGroupHelpers
|
||||
int groupControlledWells(const std::string& group_name, const std::string& always_included_child);
|
||||
GuideRate::RateVector getGroupRateVector(const std::string& group_name);
|
||||
const Schedule& schedule_;
|
||||
const SummaryState& summary_state_;
|
||||
const WellStateFullyImplicitBlackoil& well_state_;
|
||||
int report_step_;
|
||||
const GuideRate* guide_rate_;
|
||||
GuideRateModel::Target target_;
|
||||
PhaseUsage pu_;
|
||||
bool is_producer_;
|
||||
Phase injection_phase_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
double fractionFromGuideRates(const std::string& name,
|
||||
const std::string& controlGroupName,
|
||||
const Schedule& schedule,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu,
|
||||
const bool alwaysIncludeThis = false);
|
||||
|
||||
double fractionFromInjectionPotentials(const std::string& name,
|
||||
const std::string& controlGroupName,
|
||||
const Schedule& schedule,
|
||||
const WellStateFullyImplicitBlackoil& wellState,
|
||||
const int reportStepIdx,
|
||||
const GuideRate* guideRate,
|
||||
const GuideRateModel::Target target,
|
||||
const PhaseUsage& pu,
|
||||
const Phase& injectionPhase,
|
||||
const bool alwaysIncludeThis = false);
|
||||
|
||||
std::pair<bool, double> checkGroupConstraintsInj(const std::string& name,
|
||||
const std::string& parent,
|
||||
const Group& group,
|
||||
|
@ -2142,47 +2142,35 @@ namespace Opm
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename TypeTag>
|
||||
template <class EvalWell>
|
||||
void
|
||||
WellInterface<TypeTag>::getGroupInjectionControl(const Group& group,
|
||||
const WellState& well_state,
|
||||
const Opm::Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const InjectorType& injectorType,
|
||||
const EvalWell& bhp,
|
||||
const EvalWell& injection_rate,
|
||||
EvalWell& control_eq,
|
||||
double efficiencyFactor)
|
||||
const WellState& well_state,
|
||||
const Opm::Schedule& schedule,
|
||||
const SummaryState& summaryState,
|
||||
const InjectorType& injectorType,
|
||||
const EvalWell& bhp,
|
||||
const EvalWell& injection_rate,
|
||||
EvalWell& control_eq,
|
||||
double efficiencyFactor)
|
||||
{
|
||||
const auto& well = well_ecl_;
|
||||
const auto pu = phaseUsage();
|
||||
|
||||
// Setting some defaults to silence warnings below.
|
||||
// Will be overwritten in the switch statement.
|
||||
int phasePos = -1;
|
||||
Well::GuideRateTarget wellTarget = Well::GuideRateTarget::UNDEFINED;
|
||||
Phase injectionPhase = Phase::WATER;
|
||||
switch (injectorType) {
|
||||
case InjectorType::WATER:
|
||||
{
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Aqua];
|
||||
wellTarget = Well::GuideRateTarget::WAT;
|
||||
injectionPhase = Phase::WATER;
|
||||
break;
|
||||
}
|
||||
case InjectorType::OIL:
|
||||
{
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Liquid];
|
||||
wellTarget = Well::GuideRateTarget::OIL;
|
||||
injectionPhase = Phase::OIL;
|
||||
break;
|
||||
}
|
||||
case InjectorType::GAS:
|
||||
{
|
||||
phasePos = pu.phase_pos[BlackoilPhases::Vapour];
|
||||
wellTarget = Well::GuideRateTarget::GAS;
|
||||
injectionPhase = Phase::GAS;
|
||||
break;
|
||||
}
|
||||
@ -2192,7 +2180,6 @@ namespace Opm
|
||||
}
|
||||
|
||||
const Group::InjectionCMode& currentGroupControl = well_state.currentInjectionGroupControl(injectionPhase, group.name());
|
||||
|
||||
if (currentGroupControl == Group::InjectionCMode::FLD ||
|
||||
currentGroupControl == Group::InjectionCMode::NONE) {
|
||||
if (!group.injectionGroupControlAvailable(injectionPhase)) {
|
||||
@ -2217,103 +2204,62 @@ namespace Opm
|
||||
}
|
||||
|
||||
efficiencyFactor *= group.getGroupEfficiencyFactor();
|
||||
assert(group.hasInjectionControl(injectionPhase));
|
||||
const auto& groupcontrols = group.injectionControls(injectionPhase, summaryState);
|
||||
const auto& well = well_ecl_;
|
||||
const auto pu = phaseUsage();
|
||||
|
||||
const std::vector<double>& groupInjectionReductions = well_state.currentInjectionGroupReductionRates(group.name());
|
||||
double groupTargetReduction = groupInjectionReductions[phasePos];
|
||||
double fraction = WellGroupHelpers::fractionFromInjectionPotentials(well.name(),
|
||||
group.name(),
|
||||
schedule,
|
||||
well_state,
|
||||
current_step_,
|
||||
guide_rate_,
|
||||
GuideRateModel::convert_target(wellTarget),
|
||||
pu,
|
||||
injectionPhase,
|
||||
false);
|
||||
switch (currentGroupControl) {
|
||||
case Group::InjectionCMode::NONE:
|
||||
{
|
||||
// The NONE case is handled earlier
|
||||
assert(false);
|
||||
break;
|
||||
if (!group.isInjectionGroup()) {
|
||||
// use bhp as control eq and let the updateControl code find a valid control
|
||||
const auto& controls = well.injectionControls(summaryState);
|
||||
control_eq = bhp - controls.bhp_limit;
|
||||
return;
|
||||
}
|
||||
case Group::InjectionCMode::RATE:
|
||||
{
|
||||
double target = std::max(0.0, (groupcontrols.surface_max_rate - groupTargetReduction)) / efficiencyFactor;
|
||||
control_eq = injection_rate - fraction * target;
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::RESV:
|
||||
{
|
||||
std::vector<double> convert_coeff(number_of_phases_, 1.0);
|
||||
rateConverter_.calcCoeff(/*fipreg*/ 0, pvtRegionIdx_, convert_coeff);
|
||||
double coeff = convert_coeff[phasePos];
|
||||
double target = std::max(0.0, (groupcontrols.resv_max_rate/coeff - groupTargetReduction)) / efficiencyFactor;
|
||||
control_eq = injection_rate - fraction * target;
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::REIN:
|
||||
{
|
||||
double productionRate = well_state.currentInjectionREINRates(groupcontrols.reinj_group)[phasePos];
|
||||
double target = std::max(0.0, (groupcontrols.target_reinj_fraction*productionRate - groupTargetReduction)) / efficiencyFactor;
|
||||
control_eq = injection_rate - fraction * target;
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::VREP:
|
||||
{
|
||||
std::vector<double> convert_coeff(number_of_phases_, 1.0);
|
||||
rateConverter_.calcCoeff(/*fipreg*/ 0, pvtRegionIdx_, convert_coeff);
|
||||
double coeff = convert_coeff[phasePos];
|
||||
double voidageRate = well_state.currentInjectionVREPRates(groupcontrols.voidage_group)*groupcontrols.target_void_fraction;
|
||||
|
||||
double injReduction = 0.0;
|
||||
std::vector<double> groupInjectionReservoirRates = well_state.currentInjectionGroupReservoirRates(group.name());
|
||||
if (groupcontrols.phase != Phase::WATER)
|
||||
injReduction += groupInjectionReservoirRates[pu.phase_pos[BlackoilPhases::Aqua]];
|
||||
// If we are here, we are at the topmost group to be visited in the recursion.
|
||||
// This is the group containing the control we will check against.
|
||||
|
||||
if (groupcontrols.phase != Phase::OIL)
|
||||
injReduction += groupInjectionReservoirRates[pu.phase_pos[BlackoilPhases::Liquid]];
|
||||
// Make conversion factors for RESV <-> surface rates.
|
||||
std::vector<double> resv_coeff(phaseUsage().num_phases, 1.0);
|
||||
rateConverter_.calcCoeff(0, pvtRegionIdx_, resv_coeff); // FIPNUM region 0 here, should use FIPNUM from WELSPECS.
|
||||
|
||||
if (groupcontrols.phase != Phase::GAS)
|
||||
injReduction += groupInjectionReservoirRates[pu.phase_pos[BlackoilPhases::Vapour]];
|
||||
|
||||
voidageRate -= injReduction;
|
||||
|
||||
double target = std::max(0.0, ( voidageRate/coeff - groupTargetReduction)) / efficiencyFactor;
|
||||
control_eq = injection_rate - fraction * target;
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::FLD:
|
||||
{
|
||||
// The FLD case is handled earlier
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
case Group::InjectionCMode::SALE:
|
||||
{
|
||||
// only for gas injectors
|
||||
assert (phasePos == pu.phase_pos[BlackoilPhases::Vapour]);
|
||||
|
||||
// Gas injection rate = Total gas production rate + gas import rate - gas consumption rate - sales rate;
|
||||
// The import and consumption is already included in the REIN rates.
|
||||
double inj_rate = well_state.currentInjectionREINRates(group.name())[phasePos];
|
||||
double sales_target = 0;
|
||||
if (schedule[current_step_].gconsale().has(group.name())) {
|
||||
const auto& gconsale = schedule[current_step_].gconsale().get(group.name(), summaryState);
|
||||
inj_rate -= gconsale.sales_target;
|
||||
sales_target = gconsale.sales_target;
|
||||
}
|
||||
WellGroupHelpers::InjectionTargetCalculator tcalc(currentGroupControl, pu, resv_coeff, group.name(), sales_target, well_state, injectionPhase);
|
||||
WellGroupHelpers::FractionCalculator fcalc(schedule, summaryState, well_state, current_step_, guide_rate_, tcalc.guideTargetMode(), pu, false, injectionPhase);
|
||||
|
||||
double target = std::max(0.0, (inj_rate - groupTargetReduction)) / efficiencyFactor;
|
||||
control_eq = injection_rate - fraction * target;
|
||||
break;
|
||||
}
|
||||
// default:
|
||||
// OPM_DEFLOG_THROW(std::runtime_error, "Unvalid group control specified for group " + well.groupName(), deferred_logger );
|
||||
auto localFraction = [&](const std::string& child) {
|
||||
return fcalc.localFraction(child, "");
|
||||
};
|
||||
|
||||
auto localReduction = [&](const std::string& group_name) {
|
||||
const std::vector<double>& groupTargetReductions = well_state.currentInjectionGroupReductionRates(group_name);
|
||||
return tcalc.calcModeRateFromRates(groupTargetReductions);
|
||||
};
|
||||
|
||||
const double orig_target = tcalc.groupTarget(group.injectionControls(injectionPhase, summaryState));
|
||||
const auto chain = WellGroupHelpers::groupChainTopBot(name(), group.name(), schedule, current_step_);
|
||||
// Because 'name' is the last of the elements, and not an ancestor, we subtract one below.
|
||||
const size_t num_ancestors = chain.size() - 1;
|
||||
double target = orig_target;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guide_rate_->hasInjectionGroup(injectionPhase, chain[ii])) {
|
||||
// Apply local reductions only at the control level
|
||||
// (top) and for levels where we have a specified
|
||||
// group guide rate.
|
||||
target -= localReduction(chain[ii]);
|
||||
}
|
||||
target *= localFraction(chain[ii+1]);
|
||||
}
|
||||
// Avoid negative target rates coming from too large local reductions.
|
||||
const double target_rate = std::max(0.0, target / efficiencyFactor);
|
||||
const auto current_rate = injection_rate; // Switch sign since 'rates' are negative for producers.
|
||||
control_eq = current_rate - target_rate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
template <typename TypeTag>
|
||||
template <class EvalWell>
|
||||
void
|
||||
@ -2375,7 +2321,7 @@ namespace Opm
|
||||
gratTargetFromSales = well_state.currentGroupGratTargetFromSales(group.name());
|
||||
|
||||
WellGroupHelpers::TargetCalculator tcalc(currentGroupControl, pu, resv_coeff, gratTargetFromSales);
|
||||
WellGroupHelpers::FractionCalculator fcalc(schedule, well_state, current_step_, guide_rate_, tcalc.guideTargetMode(), pu);
|
||||
WellGroupHelpers::FractionCalculator fcalc(schedule, summaryState, well_state, current_step_, guide_rate_, tcalc.guideTargetMode(), pu, true, Phase::OIL);
|
||||
|
||||
auto localFraction = [&](const std::string& child) {
|
||||
return fcalc.localFraction(child, "");
|
||||
@ -2392,7 +2338,7 @@ namespace Opm
|
||||
const size_t num_ancestors = chain.size() - 1;
|
||||
double target = orig_target;
|
||||
for (size_t ii = 0; ii < num_ancestors; ++ii) {
|
||||
if ((ii == 0) || guide_rate_->has(chain[ii])) {
|
||||
if ((ii == 0) || guide_rate_->hasProductionGroupOrWell(chain[ii])) {
|
||||
// Apply local reductions only at the control level
|
||||
// (top) and for levels where we have a specified
|
||||
// group guide rate.
|
||||
|
@ -449,6 +449,7 @@ namespace Opm
|
||||
return this->production_group_rates.find(groupName) != this->production_group_rates.end();
|
||||
}
|
||||
|
||||
|
||||
void setCurrentProductionGroupReductionRates(const std::string& groupName, const std::vector<double>& target ) {
|
||||
production_group_reduction_rates[groupName] = target;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user