opm-simulators/opm/simulators/wells/BlackoilWellModelConstraints.cpp
2022-11-03 09:40:58 +01:00

519 lines
24 KiB
C++

/*
Copyright 2016 SINTEF ICT, Applied Mathematics.
Copyright 2016 - 2017 Statoil ASA.
Copyright 2017 Dr. Blatt - HPC-Simulation-Software & Services
Copyright 2016 - 2018 IRIS AS
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 <config.h>
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/simulators/wells/BlackoilWellModelGeneric.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/WellInterfaceGeneric.hpp>
#include <fmt/format.h>
#include <stdexcept>
namespace Opm {
bool BlackoilWellModelConstraints::
hasTHPConstraints() const
{
int local_result = false;
for (const auto& well : wellModel_.genericWells()) {
if (well->wellHasTHPConstraints(wellModel_.summaryState())) {
local_result = true;
}
}
return wellModel_.comm().max(local_result);
}
std::pair<Group::InjectionCMode, double>
BlackoilWellModelConstraints::
checkGroupInjectionConstraints(const Group& group,
const int reportStepIdx,
const Phase& phase) const
{
const auto& well_state = wellModel_.wellState();
const auto& pu = wellModel_.phaseUsage();
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
OPM_THROW(std::runtime_error, "Unknown phase" );
auto currentControl = wellModel_.groupState().injection_control(group.name(), phase);
if (group.has_control(phase, Group::InjectionCMode::RATE))
{
if (currentControl != Group::InjectionCMode::RATE)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(), well_state, reportStepIdx, phasePos, /*isInjector*/true);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
const auto& controls = group.injectionControls(phase, wellModel_.summaryState());
double target = controls.surface_max_rate;
if (group.has_gpmaint_control(phase, Group::InjectionCMode::RATE))
target = wellModel_.groupState().gpmaint_target(group.name());
if (target < current_rate) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = target / current_rate;
return std::make_pair(Group::InjectionCMode::RATE, scale);
}
}
}
if (group.has_control(phase, Group::InjectionCMode::RESV))
{
if (currentControl != Group::InjectionCMode::RESV)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(), well_state, reportStepIdx, phasePos, /*isInjector*/true);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
const auto& controls = group.injectionControls(phase, wellModel_.summaryState());
double target = controls.resv_max_rate;
if (group.has_gpmaint_control(phase, Group::InjectionCMode::RESV))
target = wellModel_.groupState().gpmaint_target(group.name());
if (target < current_rate) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = target / current_rate;
return std::make_pair(Group::InjectionCMode::RESV, scale);
}
}
}
if (group.has_control(phase, Group::InjectionCMode::REIN))
{
if (currentControl != Group::InjectionCMode::REIN)
{
double production_Rate = 0.0;
const auto& controls = group.injectionControls(phase, wellModel_.summaryState());
const Group& groupRein = wellModel_.schedule().getGroup(controls.reinj_group, reportStepIdx);
production_Rate += WellGroupHelpers::sumWellSurfaceRates(groupRein, wellModel_.schedule(),
well_state, reportStepIdx,
phasePos, /*isInjector*/false);
// sum over all nodes
production_Rate = wellModel_.comm().sum(production_Rate);
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
phasePos, /*isInjector*/true);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
if (controls.target_reinj_fraction*production_Rate < current_rate) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = controls.target_reinj_fraction*production_Rate / current_rate;
return std::make_pair(Group::InjectionCMode::REIN, scale);
}
}
}
if (group.has_control(phase, Group::InjectionCMode::VREP))
{
if (currentControl != Group::InjectionCMode::VREP)
{
double voidage_rate = 0.0;
const auto& controls = group.injectionControls(phase, wellModel_.summaryState());
const Group& groupVoidage = wellModel_.schedule().getGroup(controls.voidage_group, reportStepIdx);
voidage_rate += WellGroupHelpers::sumWellResRates(groupVoidage, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], false);
voidage_rate += WellGroupHelpers::sumWellResRates(groupVoidage, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Liquid], false);
voidage_rate += WellGroupHelpers::sumWellResRates(groupVoidage, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Vapour], false);
// sum over all nodes
voidage_rate = wellModel_.comm().sum(voidage_rate);
double total_rate = 0.0;
total_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], true);
total_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Liquid], true);
total_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Vapour], true);
// sum over all nodes
total_rate = wellModel_.comm().sum(total_rate);
if (controls.target_void_fraction*voidage_rate < total_rate) {
double scale = 1.0;
if (total_rate > 1e-12)
scale = controls.target_void_fraction*voidage_rate / total_rate;
return std::make_pair(Group::InjectionCMode::VREP, scale);
}
}
}
return std::make_pair(Group::InjectionCMode::NONE, 1.0);
}
std::pair<Group::ProductionCMode, double>
BlackoilWellModelConstraints::
checkGroupProductionConstraints(const Group& group,
const int reportStepIdx,
DeferredLogger& deferred_logger) const
{
const auto& well_state = wellModel_.wellState();
const auto& pu = wellModel_.phaseUsage();
const auto controls = group.productionControls(wellModel_.summaryState());
const Group::ProductionCMode& currentControl = wellModel_.groupState().production_control(group.name());
if (group.has_control(Group::ProductionCMode::ORAT))
{
if (currentControl != Group::ProductionCMode::ORAT)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Liquid], false);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
if (controls.oil_target < current_rate ) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = controls.oil_target / current_rate;
return std::make_pair(Group::ProductionCMode::ORAT, scale);
}
}
}
if (group.has_control(Group::ProductionCMode::WRAT))
{
if (currentControl != Group::ProductionCMode::WRAT)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], false);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
if (controls.water_target < current_rate ) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = controls.water_target / current_rate;
return std::make_pair(Group::ProductionCMode::WRAT, scale);
}
}
}
if (group.has_control(Group::ProductionCMode::GRAT))
{
if (currentControl != Group::ProductionCMode::GRAT)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Vapour], false);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
if (controls.gas_target < current_rate ) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = controls.gas_target / current_rate;
return std::make_pair(Group::ProductionCMode::GRAT, scale);
}
}
}
if (group.has_control(Group::ProductionCMode::LRAT))
{
if (currentControl != Group::ProductionCMode::LRAT)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Liquid], false);
current_rate += WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], false);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
bool skip = false;
if (controls.liquid_target == controls.oil_target) {
double current_water_rate = WellGroupHelpers::sumWellSurfaceRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], false);
current_water_rate = wellModel_.comm().sum(current_water_rate);
if (std::abs(current_water_rate) < 1e-12) {
skip = true;
deferred_logger.debug("LRAT_ORAT_GROUP", "GROUP " + group.name() + " The LRAT target is equal the ORAT target and the water rate is zero, skip checking LRAT");
}
}
if (!skip && controls.liquid_target < current_rate ) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = controls.liquid_target / current_rate;
return std::make_pair(Group::ProductionCMode::LRAT, scale);
}
}
}
if (group.has_control(Group::ProductionCMode::CRAT))
{
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "CRAT control for production groups not implemented" , deferred_logger);
}
if (group.has_control(Group::ProductionCMode::RESV))
{
if (currentControl != Group::ProductionCMode::RESV)
{
double current_rate = 0.0;
current_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Aqua], true);
current_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Liquid], true);
current_rate += WellGroupHelpers::sumWellResRates(group, wellModel_.schedule(),
well_state, reportStepIdx,
pu.phase_pos[BlackoilPhases::Vapour], true);
// sum over all nodes
current_rate = wellModel_.comm().sum(current_rate);
double target = controls.resv_target;
if (group.has_gpmaint_control(Group::ProductionCMode::RESV))
target = wellModel_.groupState().gpmaint_target(group.name());
if ( target < current_rate ) {
double scale = 1.0;
if (current_rate > 1e-12)
scale = target / current_rate;
return std::make_pair(Group::ProductionCMode::RESV, scale);
}
}
}
if (group.has_control(Group::ProductionCMode::PRBL))
{
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "PRBL control for production groups not implemented", deferred_logger);
}
return std::make_pair(Group::ProductionCMode::NONE, 1.0);
}
bool BlackoilWellModelConstraints::
checkGroupConstraints(const Group& group,
const int reportStepIdx,
DeferredLogger& deferred_logger) const
{
if (group.isInjectionGroup()) {
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
for (Phase phase : all) {
if (!group.hasInjectionControl(phase)) {
continue;
}
const auto& check = this->checkGroupInjectionConstraints(group,
reportStepIdx, phase);
if (check.first != Group::InjectionCMode::NONE) {
return true;
}
}
}
if (group.isProductionGroup()) {
const auto& check = this->checkGroupProductionConstraints(group,
reportStepIdx,
deferred_logger);
if (check.first != Group::ProductionCMode::NONE)
{
return true;
}
}
// call recursively down the group hierarchy
bool violated = false;
for (const std::string& groupName : group.groups()) {
const auto& grp = wellModel_.schedule().getGroup(groupName, reportStepIdx);
violated = violated || this->checkGroupConstraints(grp, reportStepIdx, deferred_logger);
}
return violated;
}
void BlackoilWellModelConstraints::
actionOnBrokenConstraints(const Group& group,
const Group::InjectionCMode& newControl,
const Phase& controlPhase,
GroupState& group_state,
DeferredLogger& deferred_logger) const
{
auto oldControl = wellModel_.groupState().injection_control(group.name(), controlPhase);
if (oldControl != newControl) {
const std::string from = Group::InjectionCMode2String(oldControl);
group_state.injection_control(group.name(), controlPhase, newControl);
if (wellModel_.comm().rank() == 0) {
auto msg = fmt::format("Switching injection control mode for group {} from {} to {}",
group.name(),
Group::InjectionCMode2String(oldControl),
Group::InjectionCMode2String(newControl));
deferred_logger.debug(msg);
}
}
}
void BlackoilWellModelConstraints::
actionOnBrokenConstraints(const Group& group,
const Group::ExceedAction& exceed_action,
const Group::ProductionCMode& newControl,
GroupState& group_state,
DeferredLogger& deferred_logger) const
{
const Group::ProductionCMode oldControl = wellModel_.groupState().production_control(group.name());
std::string ss;
switch(exceed_action) {
case Group::ExceedAction::NONE: {
if (oldControl != newControl && oldControl != Group::ProductionCMode::NONE) {
ss = fmt::format("Group production exceed action is NONE for group {}. Nothing happens.",
group.name());
}
break;
}
case Group::ExceedAction::CON: {
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GroupProductionExceedLimit CON not implemented", deferred_logger);
break;
}
case Group::ExceedAction::CON_PLUS: {
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GroupProductionExceedLimit CON_PLUS not implemented", deferred_logger);
break;
}
case Group::ExceedAction::WELL: {
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GroupProductionExceedLimit WELL not implemented", deferred_logger);
break;
}
case Group::ExceedAction::PLUG: {
OPM_DEFLOG_THROW(std::runtime_error, "Group " + group.name() + "GroupProductionExceedLimit PLUG not implemented", deferred_logger);
break;
}
case Group::ExceedAction::RATE: {
if (oldControl != newControl) {
group_state.production_control(group.name(), newControl);
ss = fmt::format("Switching production control mode for group {} from {} to {}",
group.name(),
Group::ProductionCMode2String(oldControl),
Group::ProductionCMode2String(newControl));
}
break;
}
default:
throw("Invalid procedure for maximum rate limit selected for group" + group.name());
}
if (!ss.empty() && wellModel_.comm().rank() == 0)
deferred_logger.debug(ss);
}
bool BlackoilWellModelConstraints::
updateGroupIndividualControl(const Group& group,
const int reportStepIdx,
std::map<std::pair<std::string,Opm::Phase>,std::string>& switched_inj,
std::map<std::string, std::string>& switched_prod,
GroupState& group_state,
WellState& well_state,
DeferredLogger& deferred_logger) const
{
bool changed = false;
if (group.isInjectionGroup())
{
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
for (Phase phase : all) {
if (!group.hasInjectionControl(phase)) {
continue;
}
const auto& changed_this = this->checkGroupInjectionConstraints(group,
reportStepIdx,
phase);
if (changed_this.first != Group::InjectionCMode::NONE)
{
switched_inj.insert_or_assign({group.name(), phase},
Group::InjectionCMode2String(changed_this.first));
this->actionOnBrokenConstraints(group, changed_this.first, phase,
group_state, deferred_logger);
WellGroupHelpers::updateWellRatesFromGroupTargetScale(changed_this.second,
group,
wellModel_.schedule(),
reportStepIdx,
/* isInjector */ false,
wellModel_.groupState(),
well_state);
changed = true;
}
}
}
if (group.isProductionGroup()) {
const auto& changed_this = this->checkGroupProductionConstraints(group,
reportStepIdx,
deferred_logger);
const auto controls = group.productionControls(wellModel_.summaryState());
if (changed_this.first != Group::ProductionCMode::NONE)
{
switched_prod.insert_or_assign(group.name(),
Group::ProductionCMode2String(changed_this.first));
this->actionOnBrokenConstraints(group,
controls.exceed_action,
changed_this.first,
group_state, deferred_logger);
WellGroupHelpers::updateWellRatesFromGroupTargetScale(changed_this.second,
group,
wellModel_.schedule(),
reportStepIdx,
/* isInjector */ false,
wellModel_.groupState(),
well_state);
changed = true;
}
}
return changed;
}
}