add group target calculation

added temporary output

mainly rebasing

rebasing

some further attempts

fixed target calculations

remove some case specific choices

clean up

some clean up

generalised code for calculating target rate in groupControlledWells

small rebase fix

<double> replaced by <Scalar>

<double> replaced by <Scalar> (2)
This commit is contained in:
Paul 2024-05-13 11:22:30 +02:00 committed by plgbrts
parent 6b2c372f11
commit 9d735b8d6e
14 changed files with 299 additions and 94 deletions

View File

@ -479,7 +479,8 @@ template<class Scalar> class WellContributions;
bool updateWellControlsAndNetwork(const bool mandatory_network_balance,
const double dt,
DeferredLogger& local_deferredLogger);
double computeWellGroupTarget(DeferredLogger& local_deferredLogger);
void computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger);
/// Update rank's notion of intersecting wells and their

View File

@ -1313,6 +1313,7 @@ updateAndCommunicateGroupData(const int reportStepIdx,
phase_usage_,
guideRate_,
well_state,
summaryState_,
this->groupState(),
groupTargetReduction);
std::vector<Scalar> groupTargetReductionInj(numPhases(), 0.0);
@ -1323,6 +1324,7 @@ updateAndCommunicateGroupData(const int reportStepIdx,
phase_usage_,
guideRate_,
well_state,
summaryState_,
this->groupState(),
groupTargetReductionInj);

View File

@ -42,12 +42,11 @@
#include <opm/simulators/wells/ParallelWBPCalculation.hpp>
#include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/wells/WellBhpThpCalculator.hpp>
#include <opm/simulators/wells/WellGroupControls.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/TargetCalculator.hpp>
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/TargetCalculator.hpp>
#include <opm/simulators/utils/MPIPacker.hpp>
#include <opm/simulators/utils/phaseUsageFromDeck.hpp>
@ -55,7 +54,6 @@
#include <opm/simulators/linalg/gpubridge/WellContributions.hpp>
#endif
#if HAVE_MPI
#include <opm/simulators/utils/MPISerializer.hpp>
#endif
@ -1329,7 +1327,6 @@ namespace Opm {
const auto& balance = this->schedule()[reportStepIdx].network_balance();
const Scalar thp_tolerance = balance.thp_tolerance();
if (!network.active()) {
return;
}
@ -1342,20 +1339,51 @@ namespace Opm {
if (has_choke) {
const auto& summary_state = this->simulator_.vanguard().summaryState();
const Group& group = this->schedule().getGroup(nodeName, reportStepIdx);
const auto ctrl = group.productionControls(summary_state);
const auto cmode = ctrl.cmode;
const auto pu = this->phase_usage_;
const auto pu = this->phase_usage_;
//TODO: Auto choke combined with RESV control is not supported
std::vector<Scalar> resv_coeff(pu.num_phases, 1.0);
Scalar gratTargetFromSales = 0.0;
if (group_state.has_grat_sales_target(group.name()))
gratTargetFromSales = group_state.grat_sales_target(group.name());
const auto ctrl = group.productionControls(summary_state);
auto cmode_tmp = ctrl.cmode;
Scalar target_tmp{0.0};
bool fld_none = false;
if (cmode_tmp == Group::ProductionCMode::FLD || cmode_tmp == Group::ProductionCMode::NONE) {
fld_none = true;
// Target is set for an ancestor group. Target for autochoke group to be
// derived from via group guide rates
const Scalar efficiencyFactor = 1.0;
const Group& parentGroup = this->schedule().getGroup(group.parent(), reportStepIdx);
auto target = WellGroupControls<Scalar>::getAutoChokeGroupProductionTargetRate(
group.name(),
parentGroup,
well_state,
group_state,
this->schedule(),
summary_state,
resv_coeff,
efficiencyFactor,
reportStepIdx,
pu,
&this->guideRate_,
local_deferredLogger);
target_tmp = target.first;
cmode_tmp = target.second;
}
const auto cmode = cmode_tmp;
WGHelpers::TargetCalculator tcalc(cmode, pu, resv_coeff,
gratTargetFromSales, nodeName, group_state,
group.has_gpmaint_control(cmode));
const Scalar orig_target = tcalc.groupTarget(ctrl, local_deferredLogger);
if (!fld_none)
{
target_tmp = tcalc.groupTarget(ctrl, local_deferredLogger);
}
const Scalar orig_target = target_tmp;
std::cout<< "Group: " << group.name() << " orig_target: " << orig_target*86400 << std::endl;
auto mismatch = [&] (auto group_thp) {
Scalar group_rate(0.0);
@ -1376,31 +1404,6 @@ namespace Opm {
return (group_rate - orig_target)/orig_target;
};
double min_thp, max_thp;
std::array<double, 2> range_initial;
//Find an initial bracket
if (!this->well_group_thp_calc_.has_value()){
// Retrieve the terminal pressure of the associated root of the manifold group
std::string node_name = nodeName;
while (!network.node(node_name).terminal_pressure().has_value()) {
auto branch = network.uptree_branch(node_name).value();
node_name = branch.uptree_node();
}
min_thp = network.node(node_name).terminal_pressure().value();
std::optional<double> approximate_solution0;
WellBhpThpCalculator::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp, local_deferredLogger);
// Narrow down the bracket
double low1, high1;
std::array<double, 2> range = {0.9*min_thp, 1.1*max_thp};
std::optional<double> appr_sol;
WellBhpThpCalculator::bruteForceBracketCommonTHP(mismatch, range, low1, high1, appr_sol, 0.0, local_deferredLogger);
min_thp = low1;
max_thp = high1;
range_initial = {min_thp, max_thp};
}
const auto upbranch = network.uptree_branch(nodeName);
const auto it = this->node_pressures_.find((*upbranch).uptree_node());
const Scalar nodal_pressure = it->second;
@ -1471,7 +1474,6 @@ namespace Opm {
for (auto& well : this->well_container_) {
std::string well_name = well->name();
if (group.hasWell(well_name)) {
well->setDynamicThpLimit(well_group_thp);
}
}

View File

@ -37,6 +37,7 @@ FractionCalculator<Scalar>::
FractionCalculator(const Schedule& schedule,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const SummaryState& summary_state,
const int report_step,
const GuideRate* guide_rate,
const GuideRateModel::Target target,
@ -46,6 +47,7 @@ FractionCalculator(const Schedule& schedule,
: schedule_(schedule)
, well_state_(well_state)
, group_state_(group_state)
, summary_state_(summary_state)
, report_step_(report_step)
, guide_rate_(guide_rate)
, target_(target)
@ -123,6 +125,7 @@ guideRateSum(const Group& group,
total_guide_rate += guideRate(child_group, always_included_child);
}
}
for (const std::string& child_well : group.wells()) {
bool included = (child_well == always_included_child);
if (is_producer_) {
@ -147,7 +150,7 @@ guideRate(const std::string& name,
return WellGroupHelpers<Scalar>::getGuideRate(name, schedule_, well_state_, group_state_,
report_step_, guide_rate_, target_, pu_);
} else {
if (groupControlledWells(name, always_included_child) > 0) {
if ((groupControlledWells(name, always_included_child) > 0)) {
if (is_producer_ && guide_rate_->has(name)) {
return guide_rate_->get(name, target_, getGroupRateVector(name));
} else if (!is_producer_ && guide_rate_->has(name, injection_phase_)) {
@ -174,6 +177,7 @@ groupControlledWells(const std::string& group_name,
return WellGroupHelpers<Scalar>::groupControlledWells(schedule_,
well_state_,
this->group_state_,
this->summary_state_,
report_step_,
group_name,
always_included_child,

View File

@ -41,6 +41,7 @@ public:
FractionCalculator(const Schedule& schedule,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const SummaryState& summary_state,
const int report_step,
const GuideRate* guide_rate,
const GuideRateModel::Target target,
@ -65,6 +66,7 @@ private:
const Schedule& schedule_;
const WellState<Scalar>& well_state_;
const GroupState<Scalar>& group_state_;
const SummaryState& summary_state_;
int report_step_;
const GuideRate* guide_rate_;
GuideRateModel::Target target_;

View File

@ -113,26 +113,6 @@ GroupState::update_well_group_thp(const std::string& gname, const double& thp)
this->group_thp[gname] = thp;
}
template<class Scalar>
double GroupState<Scalar>::
GroupState::well_group_thp(const std::string& gname) const
{
auto group_iter = this->group_thp.find(gname);
if (group_iter == this->group_thp.end())
throw std::logic_error("No such group");
return group_iter->second;
}
//-------------------------------------------------------------------------
template<class Scalar>
void GroupState<Scalar>::
GroupState::update_well_group_thp(const std::string& gname, const double& thp)
{
this->group_thp[gname] = thp;
}
template<class Scalar>
Scalar GroupState<Scalar>::
GroupState::well_group_thp(const std::string& gname) const
@ -144,6 +124,13 @@ GroupState::well_group_thp(const std::string& gname) const
return group_iter->second;
}
template<class Scalar>
bool GroupState<Scalar>::
GroupState::is_autochoke_group(const std::string& gname) const
{
return (this->group_thp.count(gname) > 0);
}
//-------------------------------------------------------------------------
template<class Scalar>

View File

@ -55,8 +55,8 @@ public:
const std::vector<Scalar>& production_rates(const std::string& gname) const;
void update_well_group_thp(const std::string& gname, const double& thp);
Scalar well_group_thp(const std::string& gname) const;
bool is_autochoke_group(const std::string& gname) const;
bool has_production_reduction_rates(const std::string& gname) const;
void update_production_reduction_rates(const std::string& gname,
@ -214,11 +214,7 @@ private:
std::map<std::string, Scalar> inj_vrep_rate;
std::map<std::string, Scalar> m_grat_sales_target;
std::map<std::string, Scalar> m_gpmaint_target;
<<<<<<< HEAD
std::map<std::string, Scalar> group_thp;
=======
std::map<std::string, double> group_thp;
>>>>>>> 8e410ac73 (Automatic choke)
std::map<std::pair<Phase, std::string>, Group::InjectionCMode> injection_controls;
WellContainer<GPMaint::State> gpmaint_state;

View File

@ -1051,13 +1051,14 @@ bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
Scalar& min_thp, Scalar& max_thp)
{
bool bracket_found = false;
constexpr int sample_number = 1000;
constexpr int sample_number = 1000;
constexpr Scalar interval = 1E5;
Scalar eq_low = eq(min_thp);
Scalar eq_high = 0.0;
for (int i = 0; i < sample_number + 1; ++i) {
max_thp = min_thp + interval * i;
eq_high = eq(max_thp);
// std::cout << "max_thp: " << max_thp/1E5 << " eq_high: " << eq_high << " eq_low: " << eq_low << std::endl;
if (eq_high * eq_low <= 0.) {
bracket_found = true;
min_thp = max_thp - interval;

View File

@ -130,24 +130,6 @@ public:
static bool bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
Scalar& min_thp, Scalar& max_thp);
//! \brief Find limits using brute-force solver.
static bool bruteForceBracket(const std::function<double(const double)>& eq,
const std::array<double, 2>& range,
double& low, double& high,
DeferredLogger& deferred_logger);
//! \brief Find limits using brute-force solver.
static bool bruteForceBracketCommonTHP(const std::function<double(const double)>& eq,
const std::array<double, 2>& range,
double& low, double& high,
std::optional<double>& approximate_solution,
const double& limit,
DeferredLogger& deferred_logger);
//! \brief Find limits using brute-force solver.
static bool bruteForceBracketCommonTHP(const std::function<double(const double)>& eq,
double& min_thp, double& max_thp);
private:
//! \brief Compute BHP from THP limit for an injector - implementation.
template<class ErrorPolicy>

View File

@ -151,6 +151,7 @@ getGroupInjectionControl(const Group& group,
WGHelpers::FractionCalculator fcalc(schedule,
well_state,
group_state,
summaryState,
well_.currentStep(),
well_.guideRate(),
tcalc.guideTargetMode(),
@ -284,6 +285,7 @@ getGroupInjectionTargetRate(const Group& group,
WGHelpers::FractionCalculator fcalc(schedule,
well_state,
group_state,
summaryState,
well_.currentStep(),
well_.guideRate(),
tcalc.guideTargetMode(),
@ -401,6 +403,7 @@ getGroupProductionControl(const Group& group,
WGHelpers::FractionCalculator fcalc(schedule,
well_state,
group_state,
summaryState,
well_.currentStep(),
well_.guideRate(),
tcalc.guideTargetMode(),
@ -499,6 +502,7 @@ getGroupProductionTargetRate(const Group& group,
WGHelpers::FractionCalculator fcalc(schedule,
well_state,
group_state,
summaryState,
well_.currentStep(),
well_.guideRate(),
tcalc.guideTargetMode(),
@ -525,14 +529,17 @@ getGroupProductionTargetRate(const Group& group,
// Because 'name' is the last of the elements, and not an ancestor, we subtract one below.
const std::size_t num_ancestors = chain.size() - 1;
Scalar target = orig_target;
std::cout << "target: " << target*86400 << " time: " << well_.currentStep() << " orig" << std::endl;
for (std::size_t ii = 0; ii < num_ancestors; ++ii) {
if ((ii == 0) || well_.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]);
std::cout << "ii: " << ii << " group: " << chain[ii] << " LocRed:" << localReduction(chain[ii])*86400 << " target: " << target*86400 << std::endl;
}
target *= localFraction(chain[ii+1]);
std::cout << "ii: " << ii << " group: " << chain[ii+1] << " LocFrac: " << localFraction(chain[ii+1]) << " target: " << target*86400 << std::endl;
}
// Avoid negative target rates coming from too large local reductions.
const Scalar target_rate = std::max(Scalar(0.0), target / efficiencyFactor);
@ -549,6 +556,116 @@ getGroupProductionTargetRate(const Group& group,
return scale;
}
template<class Scalar>
std::pair<Scalar, Group::ProductionCMode> WellGroupControls<Scalar>::
getAutoChokeGroupProductionTargetRate(const std::string& name,
const Group& group,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const Schedule& schedule,
const SummaryState& summaryState,
const std::vector<Scalar>& resv_coeff,
Scalar efficiencyFactor,
const int reportStepIdx,
const PhaseUsage& pu,
const GuideRate* guideRate,
DeferredLogger& deferred_logger)
{
const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name());
if (currentGroupControl == Group::ProductionCMode::FLD ||
currentGroupControl == Group::ProductionCMode::NONE) {
if (!group.productionGroupControlAvailable()) {
return std::make_pair(1.0, currentGroupControl);
} else {
// Produce share of parents control
const auto& parent = schedule.getGroup(group.parent(), reportStepIdx);
efficiencyFactor *= group.getGroupEfficiencyFactor();
return getAutoChokeGroupProductionTargetRate(name, parent, well_state, group_state,
schedule, summaryState,
resv_coeff, efficiencyFactor, reportStepIdx, pu,
guideRate, deferred_logger);
}
}
if (!group.isProductionGroup()) {
return std::make_pair(1.0, currentGroupControl);
}
// 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.
// Make conversion factors for RESV <-> surface rates.
// std::vector<double> resv_coeff(well_.phaseUsage().num_phases, 1.0);
// rateConverter(0, well_.pvtRegionIdx(), group.name(), resv_coeff); // FIPNUM region 0 here, should use FIPNUM from WELSPECS.
// gconsale may adjust the grat target.
// the adjusted rates is send to the targetCalculator
Scalar gratTargetFromSales = 0.0;
if (group_state.has_grat_sales_target(group.name()))
gratTargetFromSales = group_state.grat_sales_target(group.name());
WGHelpers::TargetCalculator tcalc(currentGroupControl,
pu,
resv_coeff,
gratTargetFromSales,
group.name(),
group_state,
group.has_gpmaint_control(currentGroupControl));
WGHelpers::FractionCalculator fcalc(schedule,
well_state,
group_state,
summaryState,
reportStepIdx,
guideRate,
tcalc.guideTargetMode(),
pu,
true,
Phase::OIL);
auto localFraction = [&](const std::string& child) {
return fcalc.localFraction(child, child); //Note child needs to be passed to always include since the global isGrup map is not updated yet.
};
auto localReduction = [&](const std::string& group_name) {
const std::vector<Scalar>& groupTargetReductions = group_state.production_reduction_rates(group_name);
return tcalc.calcModeRateFromRates(groupTargetReductions);
};
std::optional<Group::ProductionControls> ctrl;
if (!group.has_gpmaint_control(currentGroupControl))
ctrl = group.productionControls(summaryState);
// Scalar fr_true = fcalc.fraction("B1", "M5S", true);
// fr_true = fcalc.fraction("B1", "M5S", true);
// Scalar fr_false = fcalc.fraction("B1", "M5S", false);
// fr_false = fcalc.fraction("B1", "M5S", false);
// std::cout << "fr_true: " << fr_true << " fr_false: " << fr_false << std::endl;
const double orig_target = tcalc.groupTarget(ctrl, deferred_logger);
const auto chain = WellGroupHelpers<Scalar>::groupChainTopBot(name, group.name(),
schedule, reportStepIdx);
// Because 'name' is the last of the elements, and not an ancestor, we subtract one below.
const std::size_t num_ancestors = chain.size() - 1;
double target = orig_target;
std::cout << "target: " << target*86400 << " time: " << reportStepIdx << " modified" << std::endl;
for (std::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]);
std::cout << "ii: " << ii << " group: " << chain[ii] << " LocRed:" << localReduction(chain[ii])*86400 << " target: " << target*86400 << std::endl;
}
target *= localFraction(chain[ii+1]);
std::cout << "ii: " << ii << " group: " << chain[ii+1] << " LocFrac: " << localFraction(chain[ii+1]) << " target: " << target*86400 << std::endl;
}
// Avoid negative target rates coming from too large local reductions.
const double target_rate = std::max(0.0, target / efficiencyFactor);
return std::make_pair(target_rate, currentGroupControl);
}
#define INSTANTIATE(T,...) \
template void WellGroupControls<T>:: \
getGroupInjectionControl(const Group&, \

View File

@ -24,6 +24,7 @@
#ifndef OPM_WELL_GROUP_CONTROLS_HEADER_INCLUDED
#define OPM_WELL_GROUP_CONTROLS_HEADER_INCLUDED
#include <opm/input/eclipse/Schedule/Group/GuideRate.hpp>
#include <string>
#include <functional>
#include <optional>
@ -37,6 +38,7 @@ class Group;
template<class Scalar> class GroupState;
enum class InjectorType;
using RegionId = int;
struct PhaseUsage;
class Schedule;
class SummaryState;
template<class Scalar> class WellInterfaceGeneric;
@ -99,6 +101,19 @@ public:
Scalar efficiencyFactor,
DeferredLogger& deferred_logger) const;
static std::pair<Scalar, Group::ProductionCMode> getAutoChokeGroupProductionTargetRate(const std::string& name,
const Group& parent,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const Schedule& schedule,
const SummaryState& summaryState,
const std::vector<Scalar>& resv_coeff,
Scalar efficiencyFactor,
const int reportStepIdx,
const PhaseUsage& pu,
const GuideRate* guideRate,
DeferredLogger& deferred_logger);
private:
const WellInterfaceGeneric<Scalar>& well_; //!< Reference to well interface
};

View File

@ -26,6 +26,7 @@
#include <opm/input/eclipse/Schedule/Group/GConSale.hpp>
#include <opm/input/eclipse/Schedule/Group/GPMaint.hpp>
#include <opm/input/eclipse/Schedule/Group/Group.hpp>
#include <opm/input/eclipse/Schedule/Group/GuideRateConfig.hpp>
#include <opm/input/eclipse/Schedule/Network/ExtNetwork.hpp>
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
@ -35,6 +36,7 @@
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
#include <opm/simulators/utils/ParallelCommunication.hpp>
#include <opm/simulators/wells/BlackoilWellModelConstraints.hpp>
#include <opm/simulators/wells/FractionCalculator.hpp>
#include <opm/simulators/wells/GroupState.hpp>
#include <opm/simulators/wells/RegionAverageCalculator.hpp>
@ -345,6 +347,7 @@ updateGroupTargetReduction(const Group& group,
const PhaseUsage& pu,
const GuideRate& guide_rate,
const WellState<Scalar>& wellState,
const SummaryState& summaryState,
GroupState<Scalar>& group_state,
std::vector<Scalar>& groupTargetReduction)
{
@ -359,6 +362,7 @@ updateGroupTargetReduction(const Group& group,
pu,
guide_rate,
wellState,
summaryState,
group_state,
subGroupTargetReduction);
@ -400,7 +404,7 @@ updateGroupTargetReduction(const Group& group,
const bool individual_control = (currentGroupControl != Group::InjectionCMode::FLD
&& currentGroupControl != Group::InjectionCMode::NONE);
const int num_group_controlled_wells
= groupControlledWells(schedule, wellState, group_state, reportStepIdx, subGroupName, "", !isInjector, phase);
= groupControlledWells(schedule, wellState, group_state, summaryState, reportStepIdx, subGroupName, "", !isInjector, phase);
if (individual_control || num_group_controlled_wells == 0) {
groupTargetReduction[phase_pos]
+= subGroupEfficiency * sumWellSurfaceRates(subGroup, schedule, wellState, reportStepIdx, phase_pos, isInjector);
@ -416,7 +420,8 @@ updateGroupTargetReduction(const Group& group,
const bool individual_control = (currentGroupControl != Group::ProductionCMode::FLD
&& currentGroupControl != Group::ProductionCMode::NONE);
const int num_group_controlled_wells
= groupControlledWells(schedule, wellState, group_state, reportStepIdx, subGroupName, "", !isInjector, /*injectionPhaseNotUsed*/Phase::OIL);
= groupControlledWells(schedule, wellState, group_state, summaryState, reportStepIdx, subGroupName, "", !isInjector, /*injectionPhaseNotUsed*/Phase::OIL);
std::cout<< "Group: " << subGroupName << " NumGroupControlledWells: " << num_group_controlled_wells << std::endl;
if (individual_control || num_group_controlled_wells == 0) {
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase]
@ -431,6 +436,7 @@ updateGroupTargetReduction(const Group& group,
}
}
}
std::cout << "A: Group: " << group.name() << ", currentGroupControl: " << Group::ProductionCMode2String(currentGroupControl) << ", GroupTargetReduction[1]: " << groupTargetReduction[1]*86400 << std::endl;
}
}
@ -464,10 +470,16 @@ updateGroupTargetReduction(const Group& group,
groupTargetReduction[phase] += ws.surface_rates[phase] * efficiency;
}
} else {
if (ws.production_cmode != Well::ProducerCMode::GRUP)
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase] -= ws.surface_rates[phase] * efficiency;
if ((ws.production_cmode != Well::ProducerCMode::GRUP)){
if (!group.as_choke()) {
for (int phase = 0; phase < np; phase++) {
groupTargetReduction[phase] -= ws.surface_rates[phase] * efficiency;
}
}
std::cout<< "B: " << " Group: " << group.name() << ", Well: "
<< wellName << ", cmode: " << ws.production_cmode << ", GroupTargetReduction[1]: "
<< groupTargetReduction[1]*86400 << std::endl;
}
}
}
if (isInjector)
@ -1086,6 +1098,7 @@ int WellGroupHelpers<Scalar>::
groupControlledWells(const Schedule& schedule,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const SummaryState& summary_state,
const int report_step,
const std::string& group_name,
const std::string& always_included_child,
@ -1107,16 +1120,65 @@ groupControlledWells(const Schedule& schedule,
if (included) {
num_wells
+= groupControlledWells(schedule, well_state, group_state, report_step, child_group, always_included_child, is_production_group, injection_phase);
+= groupControlledWells(schedule, well_state, group_state, summary_state, report_step, child_group, always_included_child, is_production_group, injection_phase);
}
}
for (const std::string& child_well : group.wells()) {
bool included = (child_well == always_included_child);
if (is_production_group) {
included = included || well_state.isProductionGrup(child_well);
included = included || well_state.isProductionGrup(child_well) || group.as_choke();
} else {
included = included || well_state.isInjectionGrup(child_well);
}
const auto ctrl1 = group_state.production_control(group.name());
if (group.as_choke() && ((ctrl1 == Group::ProductionCMode::FLD) || (ctrl1 == Group::ProductionCMode::NONE))){
// The auto choke group has not own group control but inherits control from an ancestor group.
// Number of wells should be calculated as zero when wells of auto choke group do not deliver target.
// This behaviour is then similar to no-autochoke group with wells not on GRUP control.
// The rates of these wells are summed up. The parent group target is reduced with this rate.
// This reduced target becomes the target of the other child group of this parent.
const PhaseUsage& pu = well_state.phaseUsage();
std::vector<Scalar> rates(pu.num_phases, 0.0);
for (int phase_pos = 0; phase_pos < pu.num_phases; ++phase_pos) {
rates[phase_pos] = WellGroupHelpers<Scalar>::sumWellSurfaceRates(group,
schedule,
well_state,
report_step,
phase_pos,
false);
}
// Get the ancestor of the auto choke group that has group control (cmode != FLD, NONE)
const auto& control_group_name = control_group(group, group_state, report_step, schedule);
const auto& control_group = schedule.getGroup(control_group_name, report_step);
const auto& ctrl = control_group.productionControls(summary_state);
const auto& control_group_guide_rate = ctrl.guide_rate;
const auto& control_group_cmode = ctrl.cmode;
const auto& group_guide_rate = group.productionControls(summary_state).guide_rate;
Scalar gratTargetFromSales = 0.0;
if (group_state.has_grat_sales_target(control_group_name))
gratTargetFromSales = group_state.grat_sales_target(control_group_name);
std::vector<Scalar> resv_coeff(pu.num_phases, 1.0);
WGHelpers::TargetCalculator tcalc(control_group_cmode,
pu,
resv_coeff,
gratTargetFromSales,
group.name(),
group_state,
group.has_gpmaint_control(control_group_cmode));
auto deferred_logger = Opm::DeferredLogger();
const auto& control_group_target = tcalc.groupTarget(ctrl, deferred_logger);
// Target rate for the auto choke group
const Scalar target_rate = control_group_target * group_guide_rate / control_group_guide_rate;
const Scalar current_rate = tcalc.calcModeRateFromRates(rates);
if (current_rate < 0.99 * target_rate)
included = false;
}
if (included) {
++num_wells;
}
@ -1155,6 +1217,27 @@ groupChainTopBot(const std::string& bottom,
return chain;
}
template<class Scalar>
std::string
WellGroupHelpers<Scalar>::
control_group(const Group& group,
const GroupState<Scalar>& group_state,
const int reportStepIdx,
const Schedule& schedule)
{
const Group::ProductionCMode& currentGroupControl = group_state.production_control(group.name());
if (currentGroupControl == Group::ProductionCMode::FLD || currentGroupControl == Group::ProductionCMode::NONE) {
const auto& parent = schedule.getGroup(group.parent(), reportStepIdx);
return control_group(parent,
group_state,
reportStepIdx,
schedule);
}
return group.name();
}
template<class Scalar>
std::pair<bool, Scalar>
WellGroupHelpers<Scalar>::
@ -1234,6 +1317,7 @@ checkGroupConstraintsProd(const std::string& name,
WGHelpers::FractionCalculator fcalc(schedule,
wellState,
group_state,
summaryState,
reportStepIdx,
guideRate,
tcalc.guideTargetMode(),
@ -1276,6 +1360,7 @@ checkGroupConstraintsProd(const std::string& name,
const int num_gr_ctrl = groupControlledWells(schedule,
wellState,
group_state,
summaryState,
reportStepIdx,
chain[ii],
"",
@ -1296,6 +1381,7 @@ checkGroupConstraintsProd(const std::string& name,
// we add a factor here to avoid switching due to numerical instability
const Scalar factor = 1.01;
if (currentRateFraction > (guiderateFraction * factor)) {
std::cout << guided_group << " localCurrentRate(guided_group): " << localCurrentRate(guided_group) << chain[ii-1] << " localCurrentRate(chain[ii-1]) " << localCurrentRate(chain[ii-1]) << std::endl;
return std::make_pair(true, guiderateFraction / currentRateFraction);
}
}
@ -1409,6 +1495,7 @@ checkGroupConstraintsInj(const std::string& name,
WGHelpers::FractionCalculator fcalc(schedule,
wellState,
group_state,
summaryState,
reportStepIdx,
guideRate,
tcalc.guideTargetMode(),
@ -1451,12 +1538,13 @@ checkGroupConstraintsInj(const std::string& name,
for (std::size_t ii = 1; ii < num_ancestors; ++ii) {
const int num_gr_ctrl = groupControlledWells(schedule,
wellState,
group_state,
reportStepIdx,
chain[ii],
"",
/*is_producer*/ false,
injectionPhase);
group_state,
summaryState,
reportStepIdx,
chain[ii],
"",
/*is_producer*/ false,
injectionPhase);
if (guideRate->has(chain[ii], injectionPhase) && num_gr_ctrl > 0) {
local_reduction_level = ii;
}

View File

@ -105,6 +105,7 @@ public:
const PhaseUsage& pu,
const GuideRate& guide_rate,
const WellState<Scalar>& wellState,
const SummaryState& summaryState,
GroupState<Scalar>& group_state,
std::vector<Scalar>& groupTargetReduction);
@ -247,6 +248,7 @@ public:
static int groupControlledWells(const Schedule& schedule,
const WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
const SummaryState& summary_state,
const int report_step,
const std::string& group_name,
const std::string& always_included_child,
@ -276,6 +278,12 @@ public:
const Schedule& schedule,
const int report_step);
static std::string
control_group(const Group& group,
const GroupState<Scalar>& group_state,
const int reportStepIdx,
const Schedule& schedule);
static std::pair<bool, Scalar>
checkGroupConstraintsProd(const std::string& name,
const std::string& parent,

View File

@ -519,7 +519,7 @@ WellInterfaceGeneric<Scalar>::getDynamicThpLimit() const
}
template<class Scalar>
void WellInterfaceGeneric::
void WellInterfaceGeneric<Scalar>::
setDynamicThpLimit(const std::optional<Scalar> thp_limit)
{
dynamic_thp_limit_ = thp_limit;