opm-simulators/opm/simulators/wells/WellGroupHelpers.cpp
Bård Skaflestad e1ce1c9124 Centralize Rate Vector Derivation for Guide Rates
This commit creates a single implementation function for deriving
'RateVector' objects that go into the guiderate calculation.  In
particular, we now use the same implementation function for both the
well and the group levels.  While here, also expose the group level
derivation as a free function and reimplement the FractionCalculator
version in terms of this free function.  Finally, remove the
previous attempt at such a free function taking only the group name.
This function no longer exists in isolation and is only accessible
through the FractionCalculator.

This is in preparation of reporting guiderate values to the output
layer (summary and restart files).
2020-09-02 17:22:04 +02:00

1193 lines
54 KiB
C++

/*
Copyright 2019 Norce.
Copyright 2020 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/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/TargetCalculator.hpp>
#include <algorithm>
#include <vector>
namespace {
Opm::GuideRate::RateVector
getGuideRateVector(const std::vector<double>& rates, const Opm::PhaseUsage& pu)
{
using Opm::BlackoilPhases;
double oilRate = 0.0;
if (pu.phase_used[BlackoilPhases::Liquid])
oilRate = rates[pu.phase_pos[BlackoilPhases::Liquid]];
double gasRate = 0.0;
if (pu.phase_used[BlackoilPhases::Vapour])
gasRate = rates[pu.phase_pos[BlackoilPhases::Vapour]];
double waterRate = 0.0;
if (pu.phase_used[BlackoilPhases::Aqua])
waterRate = rates[pu.phase_pos[BlackoilPhases::Aqua]];
return {oilRate, gasRate, waterRate};
}
} // namespace Anonymous
namespace Opm
{
namespace WellGroupHelpers
{
void setCmodeGroup(const Group& group,
const Schedule& schedule,
const SummaryState& summaryState,
const int reportStepIdx,
WellStateFullyImplicitBlackoil& wellState)
{
for (const std::string& groupName : group.groups()) {
setCmodeGroup(
schedule.getGroup(groupName, reportStepIdx), schedule, summaryState, reportStepIdx, wellState);
}
// use NONE as default control
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
for (Phase phase : all) {
if (!wellState.hasInjectionGroupControl(phase, group.name())) {
wellState.setCurrentInjectionGroupControl(phase, group.name(), Group::InjectionCMode::NONE);
}
}
if (!wellState.hasProductionGroupControl(group.name())) {
wellState.setCurrentProductionGroupControl(group.name(), Group::ProductionCMode::NONE);
}
if (group.isInjectionGroup()
&& schedule.hasWellGroupEvent(group.name(), ScheduleEvents::GROUP_INJECTION_UPDATE, reportStepIdx)) {
for (Phase phase : all) {
if (!group.hasInjectionControl(phase))
continue;
const auto& controls = group.injectionControls(phase, summaryState);
wellState.setCurrentInjectionGroupControl(phase, group.name(), controls.cmode);
}
}
if (group.isProductionGroup()
&& schedule.hasWellGroupEvent(group.name(), ScheduleEvents::GROUP_PRODUCTION_UPDATE, reportStepIdx)) {
const auto controls = group.productionControls(summaryState);
wellState.setCurrentProductionGroupControl(group.name(), controls.cmode);
}
if (schedule.gConSale(reportStepIdx).has(group.name())) {
wellState.setCurrentInjectionGroupControl(Phase::GAS, group.name(), Group::InjectionCMode::SALE);
}
}
void accumulateGroupEfficiencyFactor(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
double& factor)
{
factor *= group.getGroupEfficiencyFactor();
if (group.parent() != "FIELD")
accumulateGroupEfficiencyFactor(
schedule.getGroup(group.parent(), reportStepIdx), schedule, reportStepIdx, factor);
}
double sumWellPhaseRates(const std::vector<double>& rates,
const Group& group,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const int phasePos,
const bool injector)
{
double rate = 0.0;
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
rate += groupTmp.getGroupEfficiencyFactor()
* sumWellPhaseRates(rates, groupTmp, schedule, wellState, reportStepIdx, phasePos, injector);
}
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
const auto& it = wellState.wellMap().find(wellName);
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto& wellEcl = schedule.getWell(wellName, reportStepIdx);
// only count producers or injectors
if ((wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector))
continue;
if (wellEcl.getStatus() == Well::Status::SHUT)
continue;
double factor = wellEcl.getEfficiencyFactor();
const auto wellrate_index = well_index * wellState.numPhases();
if (injector)
rate += factor * rates[wellrate_index + phasePos];
else
rate -= factor * rates[wellrate_index + phasePos];
}
return rate;
}
double sumWellRates(const Group& group,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const int phasePos,
const bool injector)
{
return sumWellPhaseRates(wellState.wellRates(), group, schedule, wellState, reportStepIdx, phasePos, injector);
}
double sumWellResRates(const Group& group,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const int phasePos,
const bool injector)
{
return sumWellPhaseRates(
wellState.wellReservoirRates(), group, schedule, wellState, reportStepIdx, phasePos, injector);
}
double sumSolventRates(const Group& group,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const bool injector)
{
double rate = 0.0;
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
rate += groupTmp.getGroupEfficiencyFactor()
* sumSolventRates(groupTmp, schedule, wellState, reportStepIdx, injector);
}
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
const auto& it = wellState.wellMap().find(wellName);
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto& wellEcl = schedule.getWell(wellName, reportStepIdx);
// only count producers or injectors
if ((wellEcl.isProducer() && injector) || (wellEcl.isInjector() && !injector))
continue;
if (wellEcl.getStatus() == Well::Status::SHUT)
continue;
double factor = wellEcl.getEfficiencyFactor();
if (injector)
rate += factor * wellState.solventWellRate(well_index);
else
rate -= factor * wellState.solventWellRate(well_index);
}
return rate;
}
void updateGroupTargetReduction(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const bool isInjector,
const PhaseUsage& pu,
const GuideRate& guide_rate,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState,
std::vector<double>& groupTargetReduction)
{
const int np = wellState.numPhases();
for (const std::string& subGroupName : group.groups()) {
std::vector<double> subGroupTargetReduction(np, 0.0);
const Group& subGroup = schedule.getGroup(subGroupName, reportStepIdx);
updateGroupTargetReduction(subGroup,
schedule,
reportStepIdx,
isInjector,
pu,
guide_rate,
wellStateNupcol,
wellState,
subGroupTargetReduction);
// accumulate group contribution from sub group
if (isInjector) {
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
for (Phase phase : all) {
const Group::InjectionCMode& currentGroupControl
= wellState.currentInjectionGroupControl(phase, subGroupName);
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;
if (currentGroupControl != Group::InjectionCMode::FLD
&& currentGroupControl != Group::InjectionCMode::NONE) {
// Subgroup is under individual control.
groupTargetReduction[phasePos]
+= sumWellRates(subGroup, schedule, wellStateNupcol, reportStepIdx, phasePos, isInjector);
} else {
groupTargetReduction[phasePos] += subGroupTargetReduction[phasePos];
}
}
} else {
const Group::ProductionCMode& currentGroupControl
= wellState.currentProductionGroupControl(subGroupName);
const bool individual_control = (currentGroupControl != Group::ProductionCMode::FLD
&& currentGroupControl != Group::ProductionCMode::NONE);
const int num_group_controlled_wells
= groupControlledWells(schedule, wellStateNupcol, reportStepIdx, subGroupName, "");
if (individual_control || num_group_controlled_wells == 0) {
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase]
+= sumWellRates(subGroup, schedule, wellStateNupcol, reportStepIdx, phase, isInjector);
}
} else {
// The subgroup may participate in group control.
if (!guide_rate.has(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];
}
}
}
}
}
for (const std::string& wellName : group.wells()) {
const auto& wellTmp = schedule.getWell(wellName, reportStepIdx);
if (wellTmp.isProducer() && isInjector)
continue;
if (wellTmp.isInjector() && !isInjector)
continue;
if (wellTmp.getStatus() == Well::Status::SHUT)
continue;
const auto& end = wellState.wellMap().end();
const auto& it = wellState.wellMap().find(wellName);
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto wellrate_index = well_index * wellState.numPhases();
const double efficiency = wellTmp.getEfficiencyFactor();
// add contributino from wells not under group control
if (isInjector) {
if (wellState.currentInjectionControls()[well_index] != Well::InjectorCMode::GRUP)
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase] += wellStateNupcol.wellRates()[wellrate_index + phase] * efficiency;
}
} else {
if (wellState.currentProductionControls()[well_index] != Well::ProducerCMode::GRUP)
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase] -= wellStateNupcol.wellRates()[wellrate_index + phase] * efficiency;
}
}
}
const double groupEfficiency = group.getGroupEfficiencyFactor();
for (double& elem : groupTargetReduction) {
elem *= groupEfficiency;
}
if (isInjector)
wellState.setCurrentInjectionGroupReductionRates(group.name(), groupTargetReduction);
else
wellState.setCurrentProductionGroupReductionRates(group.name(), 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)
{
const int np = pu.num_phases;
for (const std::string& groupName : group.groups()) {
std::vector<double> thisPot(np, 0.0);
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateGuideRateForGroups(groupTmp, schedule, pu, reportStepIdx, simTime, isInjector, 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) {
const Group::InjectionCMode& currentGroupControl = wellState.currentInjectionGroupControl(phase,
groupName); 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 &&
currentGroupControl != Group::ProductionCMode::NONE) { 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);
if (wellTmp.isProducer() && isInjector)
continue;
if (wellTmp.isInjector() && !isInjector)
continue;
if (wellTmp.getStatus() == Well::Status::SHUT)
continue;
const auto& end = wellState.wellMap().end();
const auto& it = wellState.wellMap().find( wellName );
if (it == end) // the well is not found
continue;
int well_index = it->second[0];
const auto wellrate_index = well_index * wellState.numPhases();
// add contribution from wells unconditionally
for (int phase = 0; phase < np; phase++) {
pot[phase] += wellState.wellPotentials()[wellrate_index + phase];
}
}
double oilPot = 0.0;
if (pu.phase_used[BlackoilPhases::Liquid])
oilPot = pot [ pu.phase_pos[BlackoilPhases::Liquid]];
double gasPot = 0.0;
if (pu.phase_used[BlackoilPhases::Vapour])
gasPot = pot [ pu.phase_pos[BlackoilPhases::Vapour]];
double waterPot = 0.0;
if (pu.phase_used[BlackoilPhases::Aqua])
waterPot = pot [pu.phase_pos[BlackoilPhases::Aqua]];
const double gefac = group.getGroupEfficiencyFactor();
oilPot = comm.sum(oilPot) * gefac;
gasPot = comm.sum(gasPot) * gefac;
waterPot = comm.sum(waterPot) * gefac;
if (isInjector) {
wellState.setCurrentGroupInjectionPotentials(group.name(), pot);
} else {
guideRate->compute(group.name(), reportStepIdx, simTime, oilPot, gasPot, waterPot);
}
}
*/
/*
template <class Comm>
void updateGuideRatesForWells(const Schedule& schedule, const PhaseUsage& pu, const int reportStepIdx, const
double& simTime, const WellStateFullyImplicitBlackoil& wellState, const Comm& comm, GuideRate* guideRate) {
const auto& end = wellState.wellMap().end();
for (const auto& well : schedule.getWells(reportStepIdx)) {
double oilpot = 0.0;
double gaspot = 0.0;
double waterpot = 0.0;
const auto& it = wellState.wellMap().find( well.name());
if (it != end) { // the well is found
int well_index = it->second[0];
const auto wpot = wellState.wellPotentials().data() + well_index*wellState.numPhases();
if (pu.phase_used[BlackoilPhases::Liquid] > 0)
oilpot = wpot[pu.phase_pos[BlackoilPhases::Liquid]];
if (pu.phase_used[BlackoilPhases::Vapour] > 0)
gaspot = wpot[pu.phase_pos[BlackoilPhases::Vapour]];
if (pu.phase_used[BlackoilPhases::Aqua] > 0)
waterpot = wpot[pu.phase_pos[BlackoilPhases::Aqua]];
}
const double wefac = well.getEfficiencyFactor();
oilpot = comm.sum(oilpot) * wefac;
gaspot = comm.sum(gaspot) * wefac;
waterpot = comm.sum(waterpot) * wefac;
guideRate->compute(well.name(), reportStepIdx, simTime, oilpot, gaspot, waterpot);
}
}
*/
void updateVREPForGroups(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState)
{
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateVREPForGroups(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState);
}
const int np = wellState.numPhases();
double resv = 0.0;
for (int phase = 0; phase < np; ++phase) {
resv += sumWellPhaseRates(wellStateNupcol.wellReservoirRates(),
group,
schedule,
wellState,
reportStepIdx,
phase,
/*isInjector*/ false);
}
wellState.setCurrentInjectionVREPRates(group.name(), resv);
}
void updateReservoirRatesInjectionGroups(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState)
{
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateReservoirRatesInjectionGroups(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState);
}
const int np = wellState.numPhases();
std::vector<double> resv(np, 0.0);
for (int phase = 0; phase < np; ++phase) {
resv[phase] = sumWellPhaseRates(wellStateNupcol.wellReservoirRates(),
group,
schedule,
wellState,
reportStepIdx,
phase,
/*isInjector*/ true);
}
wellState.setCurrentInjectionGroupReservoirRates(group.name(), resv);
}
void updateWellRates(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState)
{
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateWellRates(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState);
}
const int np = wellState.numPhases();
const auto& end = wellState.wellMap().end();
for (const std::string& wellName : group.wells()) {
std::vector<double> rates(np, 0.0);
const auto& it = wellState.wellMap().find(wellName);
if (it != end) { // the well is found on this node
int well_index = it->second[0];
const auto& wellTmp = schedule.getWell(wellName, reportStepIdx);
int sign = 1;
// production wellRates are negative. The users of currentWellRates uses the convention in
// opm-common that production and injection rates are positive.
if (!wellTmp.isInjector())
sign = -1;
for (int phase = 0; phase < np; ++phase) {
rates[phase] = sign * wellStateNupcol.wellRates()[well_index * np + phase];
}
}
wellState.setCurrentWellRates(wellName, rates);
}
}
void updateGroupProductionRates(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState)
{
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateGroupProductionRates(groupTmp, schedule, reportStepIdx, wellStateNupcol, wellState);
}
const int np = wellState.numPhases();
std::vector<double> rates(np, 0.0);
for (int phase = 0; phase < np; ++phase) {
rates[phase] = sumWellPhaseRates(
wellStateNupcol.wellRates(), group, schedule, wellState, reportStepIdx, phase, /*isInjector*/ false);
}
wellState.setCurrentProductionGroupRates(group.name(), rates);
}
void updateREINForGroups(const Group& group,
const Schedule& schedule,
const int reportStepIdx,
const PhaseUsage& pu,
const SummaryState& st,
const WellStateFullyImplicitBlackoil& wellStateNupcol,
WellStateFullyImplicitBlackoil& wellState)
{
const int np = wellState.numPhases();
for (const std::string& groupName : group.groups()) {
const Group& groupTmp = schedule.getGroup(groupName, reportStepIdx);
updateREINForGroups(groupTmp, schedule, reportStepIdx, pu, st, wellStateNupcol, wellState);
}
std::vector<double> rein(np, 0.0);
for (int phase = 0; phase < np; ++phase) {
rein[phase] = sumWellPhaseRates(
wellStateNupcol.wellRates(), group, schedule, wellState, reportStepIdx, phase, /*isInjector*/ false);
}
// add import rate and substract consumption rate for group for gas
if (schedule.gConSump(reportStepIdx).has(group.name())) {
const auto& gconsump = schedule.gConSump(reportStepIdx).get(group.name(), st);
if (pu.phase_used[BlackoilPhases::Vapour]) {
rein[pu.phase_pos[BlackoilPhases::Vapour]] += gconsump.import_rate;
rein[pu.phase_pos[BlackoilPhases::Vapour]] -= gconsump.consumption_rate;
}
}
wellState.setCurrentInjectionREINRates(group.name(), rein);
}
GuideRate::RateVector
getRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& name)
{
return getGuideRateVector(well_state.currentWellRates(name), pu);
}
GuideRate::RateVector
getProductionGroupRateVector(const WellStateFullyImplicitBlackoil& well_state, const PhaseUsage& pu, const std::string& group_name)
{
return getGuideRateVector(well_state.currentProductionGroupRates(group_name), pu);
}
double getGuideRate(const std::string& name,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const GuideRate* guideRate,
const GuideRateModel::Target target,
const PhaseUsage& pu)
{
if (schedule.hasWell(name, reportStepIdx) || guideRate->has(name)) {
return guideRate->get(name, target, getRateVector(wellState, pu, name));
}
double totalGuideRate = 0.0;
const Group& group = schedule.getGroup(name, reportStepIdx);
for (const std::string& groupName : group.groups()) {
const Group::ProductionCMode& currentGroupControl = wellState.currentProductionGroupControl(groupName);
if (currentGroupControl == Group::ProductionCMode::FLD
|| currentGroupControl == Group::ProductionCMode::NONE) {
// accumulate from sub wells/groups
totalGuideRate += getGuideRate(groupName, schedule, wellState, reportStepIdx, guideRate, target, pu);
}
}
for (const std::string& wellName : group.wells()) {
const auto& wellTmp = schedule.getWell(wellName, reportStepIdx);
if (wellTmp.isInjector())
continue;
if (wellTmp.getStatus() == Well::Status::SHUT)
continue;
// Only count wells under group control or the ru
if (!wellState.isProductionGrup(wellName))
continue;
totalGuideRate += guideRate->get(wellName, target, getRateVector(wellState, pu, wellName));
}
return totalGuideRate;
}
double getGuideRateInj(const std::string& name,
const Schedule& schedule,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const GuideRate* guideRate,
const GuideRateModel::Target target,
const Phase& injectionPhase,
const PhaseUsage& pu)
{
if (schedule.hasWell(name, reportStepIdx)) {
return guideRate->get(name, target, getRateVector(wellState, pu, name));
}
double totalGuideRate = 0.0;
const Group& group = schedule.getGroup(name, reportStepIdx);
for (const std::string& groupName : group.groups()) {
const Group::InjectionCMode& currentGroupControl
= wellState.currentInjectionGroupControl(injectionPhase, groupName);
if (currentGroupControl == Group::InjectionCMode::FLD
|| currentGroupControl == Group::InjectionCMode::NONE) {
// accumulate from sub wells/groups
totalGuideRate += getGuideRateInj(
groupName, schedule, wellState, reportStepIdx, guideRate, target, injectionPhase, pu);
}
}
for (const std::string& wellName : group.wells()) {
const auto& wellTmp = schedule.getWell(wellName, reportStepIdx);
if (!wellTmp.isInjector())
continue;
if (wellTmp.getStatus() == Well::Status::SHUT)
continue;
// Only count wells under group control or the ru
if (!wellState.isInjectionGrup(wellName))
continue;
totalGuideRate += guideRate->get(wellName, target, getRateVector(wellState, pu, wellName));
}
return totalGuideRate;
}
int groupControlledWells(const Schedule& schedule,
const WellStateFullyImplicitBlackoil& well_state,
const int report_step,
const std::string& group_name,
const std::string& always_included_child)
{
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);
if (included) {
num_wells
+= groupControlledWells(schedule, well_state, report_step, 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);
if (included) {
++num_wells;
}
}
return num_wells;
}
FractionCalculator::FractionCalculator(const Schedule& schedule,
const WellStateFullyImplicitBlackoil& well_state,
const int report_step,
const GuideRate* guide_rate,
const GuideRateModel::Target target,
const PhaseUsage& pu)
: schedule_(schedule)
, well_state_(well_state)
, report_step_(report_step)
, guide_rate_(guide_rate)
, target_(target)
, pu_(pu)
{
}
double FractionCalculator::fraction(const std::string& name,
const std::string& control_group_name,
const bool always_include_this)
{
double fraction = 1.0;
std::string current = name;
while (current != control_group_name) {
fraction *= localFraction(current, always_include_this ? name : "");
current = parent(current);
}
return fraction;
}
double FractionCalculator::localFraction(const std::string& name, const std::string& always_included_child)
{
const double my_guide_rate = guideRate(name, always_included_child);
const Group& parent_group = schedule_.getGroup(parent(name), report_step_);
const double total_guide_rate = guideRateSum(parent_group, always_included_child);
assert(total_guide_rate >= my_guide_rate);
const double guide_rate_epsilon = 1e-12;
return (total_guide_rate > guide_rate_epsilon) ? my_guide_rate / total_guide_rate : 0.0;
}
std::string FractionCalculator::parent(const std::string& name)
{
if (schedule_.hasWell(name)) {
return schedule_.getWell(name, report_step_).groupName();
} else {
return schedule_.getGroup(name, report_step_).parent();
}
}
double FractionCalculator::guideRateSum(const Group& group, const std::string& always_included_child)
{
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);
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);
if (included) {
total_guide_rate += guideRate(child_well, always_included_child);
}
}
return total_guide_rate;
}
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));
} else {
if (groupControlledWells(name, always_included_child) > 0) {
if (guide_rate_->has(name)) {
return guide_rate_->get(name, target_, getGroupRateVector(name));
} else {
// We are a group, with default guide rate.
// Compute guide rate by accumulating our children's guide rates.
const Group& group = schedule_.getGroup(name, report_step_);
return guideRateSum(group, always_included_child);
}
} else {
// No group-controlled subordinate wells.
return 0.0;
}
}
}
int FractionCalculator::groupControlledWells(const std::string& group_name,
const std::string& always_included_child)
{
return ::Opm::WellGroupHelpers::groupControlledWells(
schedule_, well_state_, report_step_, group_name, always_included_child);
}
GuideRate::RateVector FractionCalculator::getGroupRateVector(const std::string& group_name)
{
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.
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;
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 * efficiencyFactor))
/ efficiencyFactor;
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 * efficiencyFactor))
/ efficiencyFactor;
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 * efficiencyFactor))
/ efficiencyFactor;
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 * efficiencyFactor))
/ efficiencyFactor;
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.gConSale(reportStepIdx).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 * efficiencyFactor)) / efficiencyFactor;
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)
{
// Get initial parent, 'bottom' can be a well or a group.
std::string parent;
if (schedule.hasWell(bottom, report_step)) {
parent = schedule.getWell(bottom, report_step).groupName();
} else {
parent = schedule.getGroup(bottom, report_step).parent();
}
// Build the chain from bottom to top.
std::vector<std::string> chain;
chain.push_back(bottom);
chain.push_back(parent);
while (parent != top) {
parent = schedule.getGroup(parent, report_step).parent();
chain.push_back(parent);
}
assert(chain.back() == top);
// Reverse order and return.
std::reverse(chain.begin(), chain.end());
return chain;
}
std::pair<bool, double> checkGroupConstraintsProd(const std::string& name,
const std::string& parent,
const Group& group,
const WellStateFullyImplicitBlackoil& wellState,
const int reportStepIdx,
const GuideRate* guideRate,
const double* rates,
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.
const Group::ProductionCMode& currentGroupControl = wellState.currentProductionGroupControl(group.name());
if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) {
// Return if we are not available for parent group.
if (!group.productionGroupControlAvailable()) {
return std::make_pair(false, 1);
}
// Otherwise: check production share of parent's control.
const auto& parentGroup = schedule.getGroup(group.parent(), reportStepIdx);
return checkGroupConstraintsProd(name,
parent,
parentGroup,
wellState,
reportStepIdx,
guideRate,
rates,
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.isProductionGroup()) {
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.
// gconsale may adjust the grat target.
// the adjusted rates is send to the targetCalculator
double gratTargetFromSales = 0.0;
if (wellState.hasGroupGratTargetFromSales(group.name()))
gratTargetFromSales = wellState.currentGroupGratTargetFromSales(group.name());
TargetCalculator tcalc(currentGroupControl, pu, resv_coeff, gratTargetFromSales);
FractionCalculator fcalc(schedule, wellState, reportStepIdx, guideRate, tcalc.guideTargetMode(), pu);
auto localFraction = [&](const std::string& child) { return fcalc.localFraction(child, name); };
auto localReduction = [&](const std::string& group_name) {
const std::vector<double>& groupTargetReductions
= wellState.currentProductionGroupReductionRates(group_name);
return tcalc.calcModeRateFromRates(groupTargetReductions);
};
const double orig_target = tcalc.groupTarget(group.productionControls(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->has(chain[ii])) {
local_reduction_level = ii;
}
}
double target = orig_target;
for (size_t ii = 0; ii < num_ancestors; ++ii) {
if ((ii == 0) || guideRate->has(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 * efficiencyFactor;
}
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], "");
if (num_gr_ctrl == 0) {
if (guideRate->has(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 / efficiencyFactor);
return std::make_pair(current_rate > target_rate, target_rate / current_rate);
}
} // namespace WellGroupHelpers
} // namespace Opm