mirror of
https://github.com/OPM/opm-simulators.git
synced 2026-11-04 13:17:18 -06:00
Store the group potentials in the GroupState object so we avoid recomputing them later when we need to send the slave group potentials to the master.
741 lines
27 KiB
C++
741 lines
27 KiB
C++
/*
|
|
Copyright 2025 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 <config.h>
|
|
#include <opm/input/eclipse/EclipseState/Phase.hpp>
|
|
#include <opm/input/eclipse/Schedule/Schedule.hpp>
|
|
#include <opm/output/data/GuideRateValue.hpp>
|
|
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
|
|
#include <opm/simulators/utils/ParallelCommunication.hpp>
|
|
#include <opm/simulators/wells/GuideRateHandler.hpp>
|
|
#include <opm/common/TimingMacros.hpp>
|
|
|
|
#include <utility>
|
|
|
|
#include <fmt/format.h>
|
|
|
|
namespace Opm {
|
|
|
|
// ---------------------------------------------
|
|
// Constructor for the GuideRateHandler class
|
|
// ---------------------------------------------
|
|
template <class Scalar>
|
|
GuideRateHandler<Scalar>::GuideRateHandler(
|
|
BlackoilWellModelGeneric<Scalar>& well_model,
|
|
const Schedule& schedule,
|
|
const SummaryState& summary_state,
|
|
const Parallel::Communication& comm
|
|
) :
|
|
well_model_{well_model}
|
|
, schedule_{schedule}
|
|
, summary_state_{summary_state}
|
|
, comm_{comm}
|
|
, phase_usage_{well_model_.phaseUsage()}
|
|
, guide_rate_{well_model_.guideRate()}
|
|
{
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------
|
|
// Public methods for GuideRateHandler sorted alphabetically
|
|
// -----------------------------------------------------------
|
|
|
|
template <class Scalar>
|
|
DeferredLogger&
|
|
GuideRateHandler<Scalar>::
|
|
deferredLogger()
|
|
{
|
|
assert(this->deferred_logger_ != nullptr);
|
|
return *this->deferred_logger_;
|
|
}
|
|
|
|
// This is an alternative to plotting the summary keywords:
|
|
// - GOPGR (group oil production guide rate),
|
|
// - WOPGR (well oil production guide rate),
|
|
// - GWPGR (group water production guide rate),
|
|
// - WWPGR (well water production guide rate),
|
|
// - GGPGR (group gas production guide rate),
|
|
// - WGPGR (well gas production guide rate),
|
|
// - GGIGR (group gas injection guide rate),
|
|
// - WGIGR (well gas injection guide rate),
|
|
// - GWIGR (group water injection guide rate),
|
|
// - WWIGR (well water injection guide rate),
|
|
// - GVPGR (group reservoir volume production guide rate),
|
|
// - WVPGR (well reservoir volume production guide rate),
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::
|
|
debugDumpGuideRates(const int report_step_idx, const double sim_time)
|
|
{
|
|
if (this->comm_.rank() == 0) {
|
|
GuideRateDumper dumper{*this, report_step_idx, sim_time};
|
|
dumper.dumpGuideRates();
|
|
}
|
|
}
|
|
|
|
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::
|
|
receiveMasterGroupPotentialsFromSlaves()
|
|
{
|
|
assert(this->isReservoirCouplingMaster());
|
|
auto& rescoup_master = this->reservoirCouplingMaster();
|
|
const auto& comm = rescoup_master.getComm();
|
|
if (comm.rank() == 0) {
|
|
rescoup_master.receivePotentialsFromSlaves();
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::
|
|
sendSlaveGroupPotentialsToMaster(const GroupState<Scalar>& group_state)
|
|
{
|
|
assert(this->isReservoirCouplingSlave());
|
|
auto& rescoup_slave = this->reservoirCouplingSlave();
|
|
const auto& slave_master_group_map = rescoup_slave.getSlaveToMasterGroupNameMap();
|
|
std::vector<Potentials> potentials;
|
|
if (this->comm_.rank() == 0) {
|
|
// NOTE: Traversing a std::map is guaranteed to iterate the keys in lexical order for
|
|
// std::string keys, so the master can from this order determine which potentials
|
|
// correspond to which slave group.
|
|
for (const auto& item : slave_master_group_map) {
|
|
const auto& slave_group_name = item.first;
|
|
Potentials pot;
|
|
// For injection groups, we do not have potentials. In that case,
|
|
// we will send dummy values (0.0) for the potentials.
|
|
if (this->guide_rate_.hasPotentials(slave_group_name)) {
|
|
const auto& gr_pot = group_state.get_production_group_potential(slave_group_name);
|
|
pot[Potentials::Phase::Oil] = gr_pot.oil_rate;
|
|
pot[Potentials::Phase::Gas] = gr_pot.gas_rate;
|
|
pot[Potentials::Phase::Water] = gr_pot.water_rate;
|
|
}
|
|
potentials.push_back(pot);
|
|
}
|
|
rescoup_slave.sendPotentialsToMaster(potentials);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::setLogger(DeferredLogger *deferred_logger)
|
|
{
|
|
deferred_logger_ = deferred_logger;
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
if (reservoir_coupling_master_) {
|
|
reservoir_coupling_master_->setDeferredLogger(deferred_logger);
|
|
}
|
|
if (reservoir_coupling_slave_) {
|
|
reservoir_coupling_slave_->setDeferredLogger(deferred_logger);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::
|
|
updateGuideRates(
|
|
const int report_step_idx,
|
|
const double sim_time,
|
|
WellState<Scalar> &well_state,
|
|
GroupState<Scalar> &group_state
|
|
)
|
|
{
|
|
OPM_TIMEFUNCTION();
|
|
auto num_phases = this->phase_usage_.num_phases;
|
|
UpdateGuideRates updater {
|
|
*this, report_step_idx, sim_time, well_state, group_state, num_phases
|
|
};
|
|
updater.update();
|
|
|
|
}
|
|
|
|
// ------------------------------------------
|
|
// Inner class GuideRateDumper constructor
|
|
// ------------------------------------------
|
|
|
|
// NOTE: See debugDumpGuideRates() above for more information on the
|
|
// purpose of this class. It is used to dump the guide rates
|
|
// to the terminal in a human-readable format.
|
|
template <class Scalar>
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
GuideRateDumper(
|
|
GuideRateHandler<Scalar> &parent, const int report_step_idx, const double sim_time
|
|
) : parent_{parent}
|
|
, report_step_idx_{report_step_idx}
|
|
, sim_time_{sim_time}
|
|
, well_model_{parent.wellModel()}
|
|
, schedule_{parent.schedule()}
|
|
, comm_{parent.getComm()}
|
|
{
|
|
}
|
|
|
|
// ---------------------------------------------------------------------
|
|
// Public methods for inner class GuideRateDumper sorted alphabetically
|
|
// ---------------------------------------------------------------------
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
dumpGuideRates()
|
|
{
|
|
if (this->comm_.rank() == 0) {
|
|
auto calcGR = BlackoilWellModelGuideRates{this->well_model_};
|
|
this->well_guide_rates_ = calcGR.calculateWellGuideRates(this->report_step_idx_);
|
|
this->group_guide_rates_ = calcGR.calculateAllGroupGuideRates(this->report_step_idx_);
|
|
const auto& group = this->schedule_.getGroup("FIELD", this->report_step_idx_);
|
|
printHeader_();
|
|
int level = 0; // 0 is the root level
|
|
this->dumpGuideRatesRecursive_(group, level);
|
|
printFooter_();
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------
|
|
// Private methods for GuideRateHandler sorted alphabetically
|
|
// -----------------------------------------------------------
|
|
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
dumpGuideRatesRecursive_(const Group& group, int level)
|
|
{
|
|
if (group.name() != "FIELD") {
|
|
this->printGroupGuideRates_(group, level);
|
|
}
|
|
for (const std::string& group_name : group.groups()) {
|
|
const Group& group_tmp = this->schedule_.getGroup(group_name, this->report_step_idx_);
|
|
this->dumpGuideRatesRecursive_(group_tmp, level+1);
|
|
}
|
|
for (const std::string& well_name : group.wells()) {
|
|
const auto& well_tmp = this->schedule_.getWell(well_name, this->report_step_idx_);
|
|
printWellGuideRates_(well_tmp, level+1);
|
|
}
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
getGroupGuideRatesInjection_(
|
|
const Group& group,
|
|
const data::GroupGuideRates& group_guide_rate,
|
|
std::vector<std::string>& msg_items
|
|
) const
|
|
{
|
|
// NOTE: See comment in getGroupGuideRatesProduction_() for a discussion of the
|
|
// different types of group guiderate enums.
|
|
const auto& wm_guide_rate = this->well_model_.guideRate();
|
|
const auto& name = group.name();
|
|
using Value = data::GuideRateValue::Item;
|
|
constexpr std::pair<Phase, Value> phase_value_pairs[] = {
|
|
{Phase::OIL, Value::Oil},
|
|
{Phase::GAS, Value::Gas},
|
|
{Phase::WATER, Value::Water}
|
|
};
|
|
const auto& guide_rate_value = group_guide_rate.production;
|
|
for (const auto& [phase, value] : phase_value_pairs) {
|
|
if (wm_guide_rate.has(name, phase)) {
|
|
if (guide_rate_value.has(value)) {
|
|
msg_items.push_back(
|
|
fmt::format(
|
|
"{}(Group Inj, {}): {}",
|
|
guide_rate_value.itemName(value),
|
|
guide_rate_value.get(value)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
getGroupGuideRatesProduction_(
|
|
const Group& group,
|
|
const data::GroupGuideRates& group_guide_rate,
|
|
std::vector<std::string>& msg_items
|
|
) const
|
|
{
|
|
// NOTE: There are many different types of group guide rate phase target enums that are
|
|
// used in different contexts:
|
|
// - Group.hpp defines Group::GuideRateProdTarget and Group::GuideRateInjTarget
|
|
// - Production: OIL, WAT, GAS, LIQ, RES, COMB, WGA, CVAL, INJV, POTN, FORM
|
|
// - These are defined by item 9 of GCONPROD and used if item 10 of GCONPROD is not
|
|
// set to FORM.
|
|
// - Injection: RATE, VOID, NETV, RESV
|
|
// - GuideRateModel.hpp defines GuideRateModel::Target
|
|
// - OIL, LIQ, GAS, WAT, RES, COMB, NONE
|
|
// - These are defined by item 2 of GUIDERAT.
|
|
// - These are used if item 10 of GCONPROD is set to FORM, then the guide rate set in
|
|
// item 9 of GCONPROD is ignored.
|
|
// - Phase.hpp defines the Phase enum which is used by BlackoilWellModelGuideRates.cpp and
|
|
// GuideRate.cpp
|
|
// - OIL, GAS, WATER, SOLVENT, POLYMER, ENERGY, POLYMW, FOAM, BRINE
|
|
// - Only OIL, GAS, WATER are used in the context of guide rates and only for
|
|
// injection groups. And only GAS and WATER are checked for in
|
|
// getGuideRateInjectionGroupValues() in BlackoilWellModelGuideRates.cpp.
|
|
// - GuideRateValue.hpp defines GuideRateValue::Item which is used by
|
|
// BlackoilWellModelGuideRates.cpp to assign guide rates. The values are:
|
|
// - Oil, Gas, Water, ResV
|
|
// - However, ResV is not implemented, see getGuideRateValues() in
|
|
// BlackoilWellModelGuideRates.cpp. This, despite the summary keywords GVPGR and WVPGR
|
|
// are using it, see Summary.cpp. TODO: Check if this is a bug.
|
|
const auto& wm_guide_rate = this->well_model_.guideRate();
|
|
const auto& name = group.name();
|
|
if (wm_guide_rate.has(name)) { // Check if group has production guiderates
|
|
constexpr data::GuideRateValue::Item value_types[] = {
|
|
data::GuideRateValue::Item::Oil,
|
|
data::GuideRateValue::Item::Gas,
|
|
data::GuideRateValue::Item::Water,
|
|
data::GuideRateValue::Item::ResV
|
|
};
|
|
const auto& guide_rate_value = group_guide_rate.production;
|
|
for (const auto& value_type : value_types) {
|
|
if (guide_rate_value.has(value_type)) {
|
|
msg_items.push_back(
|
|
fmt::format(
|
|
"{}(Group Prod, {}): {}",
|
|
name,
|
|
guide_rate_value.itemName(value_type),
|
|
guide_rate_value.get(value_type)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
printGroupGuideRates_(const Group& group, int level)
|
|
{
|
|
const auto& name = group.name();
|
|
assert( level >= 1 );
|
|
std::string indent(level - 1, ' ');
|
|
auto gr_itr = this->group_guide_rates_.find(name);
|
|
if (gr_itr == this->group_guide_rates_.end()) {
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}: <NO GUIDERATE>", indent, name)
|
|
);
|
|
return;
|
|
}
|
|
const auto& group_guide_rate = gr_itr->second;
|
|
std::vector<std::string> msg_items;
|
|
getGroupGuideRatesProduction_(group, group_guide_rate, msg_items);
|
|
getGroupGuideRatesInjection_(group, group_guide_rate, msg_items);
|
|
if (msg_items.empty()) {
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}(Group): <NO GUIDERATES FOUND>", indent, name)
|
|
);
|
|
return;
|
|
}
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}", indent, fmt::join(msg_items, ", "))
|
|
);
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
printHeader_()
|
|
{
|
|
this->deferredLogger().debug(
|
|
fmt::format(
|
|
"\n---- GUIDE RATE REPORT: step {}, simtime {} ----",
|
|
this->report_step_idx_,
|
|
this->sim_time_
|
|
)
|
|
);
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
printFooter_()
|
|
{
|
|
this->deferredLogger().debug(
|
|
fmt::format(
|
|
"---- END GUIDE RATE REPORT: step {}, simtime {} ----\n",
|
|
this->report_step_idx_,
|
|
this->sim_time_
|
|
)
|
|
);
|
|
}
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::GuideRateDumper::
|
|
printWellGuideRates_(const Well& well, int level)
|
|
{
|
|
const auto& name = well.name();
|
|
assert( level >= 1 );
|
|
std::string indent(level - 1, ' ');
|
|
auto gr_itr = this->well_guide_rates_.find(name);
|
|
if (gr_itr == this->well_guide_rates_.end()) {
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}: <NO GUIDERATE>", indent, name)
|
|
);
|
|
return;
|
|
}
|
|
const auto& guide_rate_value = gr_itr->second;
|
|
std::vector<std::string> msg_items;
|
|
constexpr data::GuideRateValue::Item value_types[] = {
|
|
data::GuideRateValue::Item::Oil,
|
|
data::GuideRateValue::Item::Gas,
|
|
data::GuideRateValue::Item::Water
|
|
};
|
|
const std::string well_type = well.isInjector() ? "Inj" : "Prod";
|
|
for (const auto& value_type : value_types) {
|
|
if (guide_rate_value.has(value_type)) {
|
|
msg_items.push_back(
|
|
fmt::format(
|
|
"{}(Well {}, {}): {}",
|
|
name,
|
|
well_type,
|
|
guide_rate_value.itemName(value_type),
|
|
guide_rate_value.get(value_type)
|
|
)
|
|
);
|
|
}
|
|
}
|
|
if (msg_items.empty()) {
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}(Well {}): <NO GUIDERATES FOUND>", indent, name, well_type)
|
|
);
|
|
return;
|
|
}
|
|
this->deferredLogger().debug(
|
|
fmt::format("{}{}", indent, fmt::join(msg_items, ", "))
|
|
);
|
|
}
|
|
|
|
// ------------------------------------------
|
|
// Inner class UpdateGuideRates constructor
|
|
// ------------------------------------------
|
|
|
|
template <class Scalar>
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
UpdateGuideRates(
|
|
GuideRateHandler<Scalar>& parent,
|
|
const int report_step_idx,
|
|
const double sim_time,
|
|
const WellState<Scalar>& well_state,
|
|
GroupState<Scalar>& group_state,
|
|
const int num_phases
|
|
) : parent_{parent}
|
|
, report_step_idx_{report_step_idx}
|
|
, sim_time_{sim_time}
|
|
, well_state_{well_state}
|
|
, group_state_{group_state}
|
|
, num_phases_{num_phases}
|
|
, unit_system_{parent_.schedule_.getUnits()}
|
|
{
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Inner class UpdateGuideRates public methods sorted alphabetically
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
template <class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
update()
|
|
{
|
|
this->guideRate().updateGuideRateExpiration(this->sim_time_, this->report_step_idx_);
|
|
const Group& group = this->schedule().getGroup("FIELD", this->report_step_idx_);
|
|
std::vector<Scalar> pot(this->num_phases_, 0.0);
|
|
this->updateGuideRatesForProductionGroups_(group, pot);
|
|
this->updateGuideRatesForInjectionGroups_(group);
|
|
this->updateGuideRatesForWells_();
|
|
}
|
|
|
|
// --------------------------------------------------------------------
|
|
// Inner class UpdateGuideRates private methods sorted alphabetically
|
|
// --------------------------------------------------------------------
|
|
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
template<class Scalar>
|
|
bool
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
isMasterGroup_(const Group& group)
|
|
{
|
|
if (this->isReservoirCouplingMaster()) {
|
|
return this->reservoirCouplingMaster().isMasterGroup(group.name());
|
|
}
|
|
else {
|
|
return false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
updateGuideRatesForInjectionGroups_(const Group& group)
|
|
{
|
|
OPM_TIMEFUNCTION();
|
|
// NOTE: Even if this is a pure production group, we still need to compute the
|
|
// group potentials since they may be used by an injection group at a higher
|
|
// level in the group hierarchy.
|
|
for (const std::string& group_name : group.groups()) {
|
|
const Group& group_tmp = this->schedule().getGroup(group_name, this->report_step_idx_);
|
|
// compute guide rates for sub groups first recursively
|
|
this->updateGuideRatesForInjectionGroups_(group_tmp);
|
|
}
|
|
const Phase all[] = {Phase::WATER, Phase::OIL, Phase::GAS};
|
|
const auto &pu = this->phaseUsage();
|
|
for (Phase phase : all) {
|
|
if(!group.hasInjectionControl(phase))
|
|
continue;
|
|
|
|
std::optional<Scalar> guide_rate_value;
|
|
const auto& controls = group.injectionControls(phase, this->summaryState());
|
|
switch (controls.guide_rate_def){
|
|
case Group::GuideRateInjTarget::RATE:
|
|
break;
|
|
case Group::GuideRateInjTarget::VOID:
|
|
{
|
|
guide_rate_value = std::max(
|
|
Scalar(0.0), this->group_state_.injection_vrep_rate(group.name())
|
|
);
|
|
break;
|
|
}
|
|
case Group::GuideRateInjTarget::NETV:
|
|
{
|
|
guide_rate_value = this->group_state_.injection_vrep_rate(group.name());
|
|
const std::vector<Scalar>& injRES
|
|
= this->group_state_.injection_reservoir_rates(group.name());
|
|
if (phase != Phase::OIL && pu.phase_used[BlackoilPhases::Liquid])
|
|
guide_rate_value = *guide_rate_value - injRES[pu.phase_pos[BlackoilPhases::Liquid]];
|
|
if (phase != Phase::GAS && pu.phase_used[BlackoilPhases::Vapour])
|
|
guide_rate_value = *guide_rate_value - injRES[pu.phase_pos[BlackoilPhases::Vapour]];
|
|
if (phase != Phase::WATER && pu.phase_used[BlackoilPhases::Aqua])
|
|
guide_rate_value = *guide_rate_value - injRES[pu.phase_pos[BlackoilPhases::Aqua]];
|
|
|
|
guide_rate_value = std::max(Scalar(0.0), *guide_rate_value);
|
|
break;
|
|
}
|
|
case Group::GuideRateInjTarget::RESV:
|
|
OPM_DEFLOG_THROW(
|
|
std::runtime_error,
|
|
"GUIDE PHASE RESV not implemented. Group " + group.name(),
|
|
this->deferredLogger()
|
|
);
|
|
case Group::GuideRateInjTarget::NO_GUIDE_RATE:
|
|
break;
|
|
default:
|
|
OPM_DEFLOG_THROW(
|
|
std::logic_error,
|
|
"Invalid GuideRateInjTarget in updateGuideRatesForInjectionGroups",
|
|
this->deferredLogger()
|
|
);
|
|
}
|
|
|
|
if (guide_rate_value) {
|
|
guide_rate_value = this->unit_system_.from_si(
|
|
UnitSystem::measure::rate, *guide_rate_value
|
|
);
|
|
}
|
|
this->guideRate().compute(
|
|
group.name(), phase, this->report_step_idx_, guide_rate_value
|
|
);
|
|
}
|
|
}
|
|
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
updateGuideRatesForProductionGroups_(const Group& group, std::vector<Scalar>& pot)
|
|
{
|
|
OPM_TIMEFUNCTION();
|
|
// NOTE: Even if this is a pure injection group, we still need to compute the
|
|
// group potentials since they may be used by a production group at a higher
|
|
// level in the group hierarchy. See MOD4_UDQ_ACTIONX.DATA for an example.
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
if (this->isMasterGroup_(group)) {
|
|
// This is a master group, so we do not need to compute potentials
|
|
// for sub groups. We just set the guide rates for the master group
|
|
// as submitted from its corresponding slave group.
|
|
this->updateProductionGroupPotentialFromSlaveGroup_(group, pot);
|
|
}
|
|
else {
|
|
this->updateProductionGroupPotentialFromSubGroups(group, pot);
|
|
}
|
|
#else
|
|
this->updateProductionGroupPotentialFromSubGroups(group, pot);
|
|
#endif
|
|
std::array<Scalar,3> potentials{};
|
|
const auto &pu = this->phaseUsage();
|
|
auto& [oil_pot, gas_pot, water_pot] = potentials;
|
|
if (pu.phase_used[BlackoilPhases::Liquid])
|
|
oil_pot = pot[pu.phase_pos[BlackoilPhases::Liquid]];
|
|
|
|
if (pu.phase_used[BlackoilPhases::Vapour])
|
|
gas_pot = pot[pu.phase_pos[BlackoilPhases::Vapour]];
|
|
|
|
if (pu.phase_used[BlackoilPhases::Aqua])
|
|
water_pot = pot[pu.phase_pos[BlackoilPhases::Aqua]];
|
|
|
|
// Synchronize potentials across all ranks
|
|
this->comm().sum(potentials.data(), potentials.size());
|
|
oil_pot = this->unit_system_.from_si(UnitSystem::measure::liquid_surface_rate, oil_pot);
|
|
water_pot = this->unit_system_.from_si(UnitSystem::measure::liquid_surface_rate, water_pot);
|
|
gas_pot = this->unit_system_.from_si(UnitSystem::measure::gas_surface_rate, gas_pot);
|
|
this->guideRate().compute(
|
|
group.name(), this->report_step_idx_, this->sim_time_, oil_pot, gas_pot, water_pot
|
|
);
|
|
// NOTE: For reservoir coupling: We will later need to send the slave group potentials
|
|
// to the master. The above call to GuideRate::compute() will as a side-effect store
|
|
// them in the GuideRate object. But those potentials are meant to be internal to the
|
|
// GuideRate object, so we choose to store the potentials in the GroupState object
|
|
// below for this reason.
|
|
this->group_state_.update_group_production_potential(
|
|
group.name(), oil_pot, gas_pot, water_pot
|
|
);
|
|
}
|
|
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
updateGuideRatesForWells_()
|
|
{
|
|
OPM_TIMEFUNCTION();
|
|
const auto &pu = this->phaseUsage();
|
|
for (const auto& well : this->schedule().getWells(this->report_step_idx_)) {
|
|
std::array<Scalar,3> potentials{};
|
|
auto& [oilpot, gaspot, waterpot] = potentials;
|
|
|
|
const auto& well_index = this->well_state_.index(well.name());
|
|
if (well_index.has_value() && this->well_state_.wellIsOwned(well_index.value(), well.name()))
|
|
{
|
|
// the well is found and owned
|
|
const auto& ws = this->well_state_.well(well_index.value());
|
|
const auto& wpot = ws.well_potentials;
|
|
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]];
|
|
}
|
|
this->comm().sum(potentials.data(), potentials.size());
|
|
oilpot = this->unit_system_.from_si(UnitSystem::measure::liquid_surface_rate, oilpot);
|
|
waterpot = this->unit_system_.from_si(UnitSystem::measure::liquid_surface_rate, waterpot);
|
|
gaspot = this->unit_system_.from_si(UnitSystem::measure::gas_surface_rate, gaspot);
|
|
this->guideRate().compute(
|
|
well.name(), this->report_step_idx_, this->sim_time_, oilpot, gaspot, waterpot
|
|
);
|
|
}
|
|
}
|
|
|
|
#ifdef RESERVOIR_COUPLING_ENABLED
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
updateProductionGroupPotentialFromSlaveGroup_(const Group& group, std::vector<Scalar>& pot)
|
|
{
|
|
assert(this->isReservoirCouplingMaster());
|
|
auto& rescoup_master = this->reservoirCouplingMaster();
|
|
const auto& slave_pot = rescoup_master.getSlaveGroupPotentials(group.name());
|
|
auto& pu = this->phaseUsage();
|
|
// TODO: Here we should check that the master uses the same phases as the
|
|
// slave.
|
|
pot[pu.phase_pos[BlackoilPhases::Liquid]] = slave_pot[Potentials::Phase::Oil];
|
|
pot[pu.phase_pos[BlackoilPhases::Vapour]] = slave_pot[Potentials::Phase::Gas];
|
|
pot[pu.phase_pos[BlackoilPhases::Aqua]] = slave_pot[Potentials::Phase::Water];
|
|
}
|
|
#endif
|
|
|
|
template<class Scalar>
|
|
void
|
|
GuideRateHandler<Scalar>::UpdateGuideRates::
|
|
updateProductionGroupPotentialFromSubGroups(const Group& group, std::vector<Scalar>& pot)
|
|
{
|
|
for (const std::string& group_name : group.groups()) {
|
|
std::vector<Scalar> this_pot(this->num_phases_, 0.0);
|
|
const Group& group_tmp = this->schedule().getGroup(group_name, this->report_step_idx_);
|
|
|
|
// compute group potentials and guide rates for sub groups recursively
|
|
this->updateGuideRatesForProductionGroups_(group_tmp, this_pot);
|
|
|
|
// If group_tmp is *not* available for group control at a higher level,
|
|
// it should not contribute to the group potentials and guide rates of the parent group
|
|
const auto current_group_control = this->group_state_.production_control(group_name);
|
|
if (current_group_control != Group::ProductionCMode::FLD
|
|
&& current_group_control != Group::ProductionCMode::NONE) {
|
|
continue;
|
|
}
|
|
|
|
// Apply potential for group_tmp to the parent's pot
|
|
auto gefac = group_tmp.getGroupEfficiencyFactor();
|
|
|
|
for (int phase = 0; phase < this->num_phases_; phase++) {
|
|
pot[phase] += gefac*this_pot[phase];
|
|
}
|
|
|
|
}
|
|
|
|
// If this is group on the lowest level in the group tree, add contribution from its wells
|
|
for (const std::string& well_name : group.wells()) {
|
|
const auto& well_tmp = this->schedule().getWell(well_name, this->report_step_idx_);
|
|
const auto wefac = well_tmp.getEfficiencyFactor();
|
|
|
|
// Only include producers in group potentials for production groups
|
|
if (well_tmp.isInjector())
|
|
continue;
|
|
|
|
if (well_tmp.getStatus() == Well::Status::SHUT)
|
|
continue;
|
|
const auto& well_index = this->well_state_.index(well_name);
|
|
if (!well_index.has_value()) // the well is not found
|
|
continue;
|
|
|
|
if (!this->well_state_.wellIsOwned(well_index.value(), well_name) ) // Only sum once
|
|
{
|
|
continue;
|
|
}
|
|
|
|
const auto& ws = this->well_state_.well(well_index.value());
|
|
for (int phase = 0; phase < this->num_phases_; phase++) {
|
|
pot[phase] += wefac * ws.well_potentials[phase];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
template class GuideRateHandler<double>;
|
|
|
|
#if FLOW_INSTANTIATE_FLOAT
|
|
template class GuideRateHandler<float>;
|
|
#endif
|
|
|
|
} // namespace Opm
|
|
|