Merge pull request #1152 from GitPaean/fixing_updating_well_petentials_2
[WIP] changes needed to compute well potentials each time step.
This commit is contained in:
commit
2e9b04fc37
|
@ -34,7 +34,7 @@ namespace Opm
|
|||
BHP_limit_(-1e100),
|
||||
reinjection_fraction_target_(1),
|
||||
voidage_replacment_fraction_(1),
|
||||
guide_rate_(1.0),
|
||||
guide_rate_(-1.0),
|
||||
guide_rate_type_(NONE_GRT)
|
||||
{
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace Opm
|
|||
liquid_max_rate_(-1e100),
|
||||
reservoir_flow_max_rate_(-1e100),
|
||||
BHP_limit_(-1e100),
|
||||
guide_rate_(1.0),
|
||||
guide_rate_(-1.0),
|
||||
guide_rate_type_(NONE_GRT)
|
||||
{
|
||||
}
|
||||
|
|
|
@ -194,6 +194,8 @@ namespace Opm
|
|||
roots_[i]->applyProdGroupControls();
|
||||
roots_[i]->applyInjGroupControls();
|
||||
}
|
||||
|
||||
group_control_applied_ = true;
|
||||
}
|
||||
|
||||
/// Applies explicit reinjection controls. This must be called at each timestep to be correct.
|
||||
|
@ -282,9 +284,8 @@ namespace Opm
|
|||
|
||||
void WellCollection::updateWellTargets(const std::vector<double>& well_rates)
|
||||
{
|
||||
if ( !needUpdateWellTargets() && groupTargetConverged(well_rates)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: if it gets converged, should we still update targets?
|
||||
|
||||
// set the target_updated to be false
|
||||
for (WellNode* well_node : leaf_nodes_) {
|
||||
|
@ -296,8 +297,7 @@ namespace Opm
|
|||
// While there will be somre more complication invloved for sure.
|
||||
for (size_t i = 0; i < leaf_nodes_.size(); ++i) {
|
||||
// find a node needs to update targets, then update targets for all the wellls inside the group.
|
||||
// if (leaf_nodes_[i]->shouldUpdateWellTargets() && !leaf_nodes_[i]->individualControl()) {
|
||||
if (!leaf_nodes_[i]->individualControl() && !leaf_nodes_[i]->targetUpdated()) {
|
||||
if (!leaf_nodes_[i]->targetUpdated()) {
|
||||
WellsGroupInterface* parent_node = leaf_nodes_[i]->getParent();
|
||||
// update the target within this group.
|
||||
if (leaf_nodes_[i]->isProducer()) {
|
||||
|
@ -324,6 +324,13 @@ namespace Opm
|
|||
}
|
||||
|
||||
|
||||
|
||||
bool WellCollection::groupControlApplied() const
|
||||
{
|
||||
return group_control_applied_;
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::groupTargetConverged(const std::vector<double>& well_rates) const
|
||||
{
|
||||
// TODO: eventually, there should be only one root node
|
||||
|
@ -336,4 +343,154 @@ namespace Opm
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void WellCollection::
|
||||
setGuideRatesWithPotentials(const Wells* wells,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& well_potentials) const
|
||||
{
|
||||
// TODO: assuming the order of well_potentials is the same with the order in wells struct
|
||||
// TODO: it will overwrite the well potentials from other means. It should be changed after
|
||||
// fixing the other part of the code. It makes the current flow only support guide rates based on
|
||||
// well potentials.
|
||||
const int np = wells->number_of_phases;
|
||||
const int nw = wells->number_of_wells;
|
||||
|
||||
for (int w = 0; w < nw; ++w) {
|
||||
const std::string well_name = wells->name[w];
|
||||
|
||||
WellNode& well_node = findWellNode(well_name);
|
||||
|
||||
const WellType well_type = wells->type[w];
|
||||
// TODO: eventually the following standard will be wrong, it will belong to FIELD group
|
||||
if (well_node.getParent() != nullptr) { // If it does not belong a group, will it belong to FIELD?
|
||||
const WellsGroupInterface* group = well_node.getParent();
|
||||
if (well_type == PRODUCER) {
|
||||
// The guide rates is calculated based on the group control
|
||||
// Currently only supporting WRAT, ORAT and GRAT.
|
||||
ProductionSpecification::ControlMode control_mode = group->prodSpec().control_mode_;
|
||||
if (control_mode == ProductionSpecification::FLD) {
|
||||
if (group->getParent() != nullptr) {
|
||||
// TODO: only handle one level FLD control
|
||||
const WellsGroupInterface* higher_group = group->getParent();
|
||||
control_mode = higher_group->prodSpec().control_mode_;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Group " << group->name() << " is under FLD control while no higher level of group is specified.");
|
||||
}
|
||||
}
|
||||
|
||||
switch (control_mode) {
|
||||
case ProductionSpecification::WRAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water rate controlled well.");
|
||||
}
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + water_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::WATER;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::ORAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil rate controlled well.");
|
||||
}
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + oil_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::GRAT: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas rate controlled well.");
|
||||
}
|
||||
const int gas_index = phase_usage.phase_pos[BlackoilPhases::Vapour];
|
||||
well_node.prodSpec().guide_rate_ = well_potentials[np * w + gas_index];
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::GAS;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::FLD: {
|
||||
OPM_THROW(std::logic_error, "Not support more than one continous level of FLD control");
|
||||
}
|
||||
case ProductionSpecification::LRAT: {
|
||||
double guide_rate = 0;
|
||||
if (phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
const double potential_oil = well_potentials[np * w + oil_index];
|
||||
guide_rate += potential_oil;
|
||||
}
|
||||
if (phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
const double potential_water = well_potentials[np * w + water_index];
|
||||
guide_rate += potential_water;
|
||||
}
|
||||
// not sure if no water and no oil, what will happen here, zero guide_rate?
|
||||
well_node.prodSpec().guide_rate_ = guide_rate;
|
||||
well_node.prodSpec().guide_rate_type_ = ProductionSpecification::LIQ;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::NONE: {
|
||||
// Group control is not in use for this group.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Not supported control_mode for guide rate computed" <<
|
||||
" from well potentials: " << ProductionSpecification::toString(group->prodSpec().control_mode_) );
|
||||
}
|
||||
|
||||
} else if (well_type == INJECTOR) {
|
||||
// The guide rates is calculated based on the group injector type
|
||||
switch (group->injSpec().injector_type_) {
|
||||
case InjectionSpecification::WATER: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water injecting well.");
|
||||
}
|
||||
const int water_index = phase_usage.phase_pos[BlackoilPhases::Aqua];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + water_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e water
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::OIL: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil injecting well.");
|
||||
}
|
||||
const int oil_index = phase_usage.phase_pos[BlackoilPhases::Liquid];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + oil_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e. oil
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::GAS: {
|
||||
if (!phase_usage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas injecting well.");
|
||||
}
|
||||
const int gas_index = phase_usage.phase_pos[BlackoilPhases::Vapour];
|
||||
well_node.injSpec().guide_rate_ = well_potentials[np * w + gas_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e gas
|
||||
well_node.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Not supported injector type for guide rate computed" <<
|
||||
" from well potentials: " << InjectionSpecification::toString(group->injSpec().injector_type_) );
|
||||
}
|
||||
} else { // neither injector nor producer
|
||||
OPM_THROW(std::logic_error, "Expected well type to be either INJECTOR or PRODUCER for well " << well_node.name() );
|
||||
|
||||
}
|
||||
} // end of if (well_node.getParent() != nullptr)
|
||||
} // end of for (int w = 0; w < nw; ++w)
|
||||
}
|
||||
|
||||
|
||||
bool WellCollection::requireWellPotentials() const
|
||||
{
|
||||
for (const auto& well_node : leaf_nodes_) {
|
||||
if (well_node->isGuideRateWellPotential()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -139,6 +139,9 @@ namespace Opm
|
|||
/// Whether we have active group control
|
||||
bool groupControlActive() const;
|
||||
|
||||
/// Whether we have applied the group control
|
||||
bool groupControlApplied() const;
|
||||
|
||||
/// Whether the group target is converged
|
||||
// It is considered converged if eitehr the group targets are matched or the group targets are not matched while the wells are
|
||||
// running under their own limits so that they can not produce more
|
||||
|
@ -146,6 +149,15 @@ namespace Opm
|
|||
// The strategy may need to be adjusted when more complicated multi-layered group control situation applied, not sure about thatyet.
|
||||
bool groupTargetConverged(const std::vector<double>& well_rates) const;
|
||||
|
||||
|
||||
/// Setting the guide rates with well potentials
|
||||
void setGuideRatesWithPotentials(const Wells* wells,
|
||||
const PhaseUsage& phase_usage,
|
||||
const std::vector<double>& well_potentials) const;
|
||||
|
||||
|
||||
bool requireWellPotentials() const;
|
||||
|
||||
private:
|
||||
// To account for the possibility of a forest
|
||||
std::vector<std::shared_ptr<WellsGroupInterface> > roots_;
|
||||
|
@ -157,7 +169,8 @@ namespace Opm
|
|||
|
||||
bool group_control_active_ = false;
|
||||
|
||||
|
||||
// This is used to mark whether apply or update the group control
|
||||
bool group_control_applied_ = false;
|
||||
};
|
||||
|
||||
} // namespace Opm
|
||||
|
|
|
@ -694,14 +694,17 @@ namespace Opm
|
|||
const double total_reinjected = getTotalVoidageRate(well_voidage_rates);
|
||||
// TODO: we might need the reservoir condition well potentials here
|
||||
const double my_guide_rate = injectionGuideRate(false);
|
||||
const InjectionSpecification::InjectorType injector_type = injSpec().injector_type_;
|
||||
for (size_t i = 0; i < children_.size(); ++i ) {
|
||||
const double child_guide_rate = children_[i]->injectionGuideRate(false);
|
||||
const double child_target = child_guide_rate / my_guide_rate * total_reinjected / efficiencyFactor()
|
||||
* injSpec().voidage_replacment_fraction_;
|
||||
children_[i]->applyVREPGroupControl(child_target, well_voidage_rates, conversion_coeffs, false);
|
||||
children_[i]->applyVREPGroupControl(child_target, injector_type, well_voidage_rates, conversion_coeffs, false);
|
||||
}
|
||||
}
|
||||
break;
|
||||
// TODO: It should not be put under default case. It should always perform, since there can be multi VREP controls
|
||||
// for different group levels. The same applies to other different types apply**Controls
|
||||
default:
|
||||
{
|
||||
for (size_t i = 0; i < children_.size(); ++i ) {
|
||||
|
@ -714,6 +717,7 @@ namespace Opm
|
|||
|
||||
// TODO: actually, it is not tested since it never get into this function.
|
||||
void WellsGroup::applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group)
|
||||
|
@ -734,7 +738,7 @@ namespace Opm
|
|||
}
|
||||
for (size_t i = 0; i < children_.size(); ++i) {
|
||||
const double child_target = target / efficiencyFactor() * children_[i]->injectionGuideRate(only_group) / my_guide_rate;
|
||||
children_[i]->applyVREPGroupControl(child_target, well_voidage_rates, conversion_coeffs, false);
|
||||
children_[i]->applyVREPGroupControl(child_target, injector_type, well_voidage_rates, conversion_coeffs, false);
|
||||
}
|
||||
// I do not know why here.
|
||||
injSpec().control_mode_ = InjectionSpecification::FLD;
|
||||
|
@ -784,7 +788,7 @@ namespace Opm
|
|||
}
|
||||
|
||||
// the rates left for the wells under group control to split
|
||||
const double rate_for_group_control = target_rate - rate_individual_control;
|
||||
const double rate_for_group_control = std::max(target_rate - rate_individual_control, 0.0);
|
||||
|
||||
const double my_guide_rate = productionGuideRate(true);
|
||||
|
||||
|
@ -859,13 +863,22 @@ namespace Opm
|
|||
const double bigger_of_two = std::max(production_rate, production_target);
|
||||
|
||||
if (std::abs(production_target - production_rate) > relative_tolerance * bigger_of_two) {
|
||||
// underproducing the target while potentially can produce more
|
||||
// then we should not consider the effort to match the group target is done yet
|
||||
if (canProduceMore()) {
|
||||
if (production_rate < production_target) {
|
||||
// underproducing the target while potentially can produce more
|
||||
// then we should not consider the effort to match the group target is done yet
|
||||
if (canProduceMore()) {
|
||||
return false;
|
||||
} else {
|
||||
// can not produce more to meet the target
|
||||
OpmLog::info("group " + name() + " can not meet its target!");
|
||||
return true;
|
||||
}
|
||||
} else { // overproducing the target, the only possibility is that all the wells/groups are under individual control
|
||||
// while somehow our algorithms did not make the wells/groups return group controls
|
||||
// either we should fix the algorithm of determining the target for well to return group controls
|
||||
// or we should do something here
|
||||
OpmLog::info("group " + name() + " is overproducing its target!");
|
||||
return false;
|
||||
} else {
|
||||
// can not produce more to meet the target
|
||||
OpmLog::info("group " + name() + " can not meet its target!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -908,7 +921,8 @@ namespace Opm
|
|||
self_index_(-1),
|
||||
group_control_index_(-1),
|
||||
shut_well_(true), // This is default for now
|
||||
target_updated_(false) // This is default for now, not sure whether to use the default value
|
||||
target_updated_(false), // This is default for now, not sure whether to use the default value
|
||||
is_guiderate_wellpotential_(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -1071,7 +1085,7 @@ namespace Opm
|
|||
const bool only_group)
|
||||
{
|
||||
if ( !isInjector() ) {
|
||||
// assert(target == 0.0);
|
||||
assert(target == 0.0 || std::isnan(target));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1127,6 +1141,7 @@ namespace Opm
|
|||
// Put the well under group control immediately when GRUP control mdoe is specified
|
||||
if (injSpec().control_mode_ == InjectionSpecification::GRUP) {
|
||||
set_current_control(self_index_, group_control_index_, wells_);
|
||||
individual_control_ = false;
|
||||
}
|
||||
} else {
|
||||
// We will now modify the last control, that
|
||||
|
@ -1190,6 +1205,7 @@ namespace Opm
|
|||
}
|
||||
|
||||
void WellNode::applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& /*well_voidage_rates*/,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group)
|
||||
|
@ -1209,11 +1225,37 @@ namespace Opm
|
|||
// WellControls* ctrl = wells_->ctrls[self_index_];
|
||||
// for this case, distr contains the FVF information
|
||||
// which results in the previous implementation of RESV keywords.
|
||||
std::vector<double> distr(np);
|
||||
std::copy(conversion_coeffs.begin() + np * self_index_,
|
||||
conversion_coeffs.begin() + np * (self_index_ + 1),
|
||||
distr.begin());
|
||||
|
||||
const int* phase_pos = phaseUsage().phase_pos;
|
||||
const int* phase_used = phaseUsage().phase_used;
|
||||
std::vector<double> distr(np, 0.0);
|
||||
switch(injector_type) {
|
||||
case InjectionSpecification::WATER: {
|
||||
if (!phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not active and Water VREP injection control specified.");
|
||||
}
|
||||
const int phase_position = phase_pos[BlackoilPhases::Aqua];
|
||||
distr[phase_position] = conversion_coeffs[np * self_index_ + phase_position];
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::OIL: {
|
||||
if (!phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not active and Oil VREP injection control specified.");
|
||||
}
|
||||
const int phase_position = phase_pos[BlackoilPhases::Liquid];
|
||||
distr[phase_position] = conversion_coeffs[np * self_index_ + phase_position];
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::GAS: {
|
||||
if (!phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not active and Gas VREP injection control specified.");
|
||||
}
|
||||
const int phase_position = phase_pos[BlackoilPhases::Vapour];
|
||||
distr[phase_position] = conversion_coeffs[np * self_index_ + phase_position];
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::runtime_error, "Group VREP injection type not handled: " << InjectionSpecification::toString(injector_type));
|
||||
}
|
||||
|
||||
const double invalid_alq = -std::numeric_limits<double>::max();
|
||||
const int invalid_vfp = -std::numeric_limits<int>::max();
|
||||
|
@ -1223,9 +1265,11 @@ namespace Opm
|
|||
// TODO: basically, one group control index is not enough eventually. There can be more than one sources for the
|
||||
// group control
|
||||
group_control_index_ = well_controls_get_num(wells_->ctrls[self_index_]) - 1;
|
||||
// it should only apply for nodes with GRUP injeciton control
|
||||
individual_control_ = false;
|
||||
set_current_control(self_index_, group_control_index_, wells_);
|
||||
// Put the well under group control immediately when GRUP control mdoe is specified
|
||||
if (injSpec().control_mode_ == InjectionSpecification::GRUP) {
|
||||
set_current_control(self_index_, group_control_index_, wells_);
|
||||
individual_control_ = false;
|
||||
}
|
||||
} else {
|
||||
well_controls_iset_type(wells_->ctrls[self_index_] , group_control_index_ , RESERVOIR_RATE);
|
||||
well_controls_iset_target(wells_->ctrls[self_index_] , group_control_index_ , ntarget);
|
||||
|
@ -1249,7 +1293,7 @@ namespace Opm
|
|||
const bool only_group)
|
||||
{
|
||||
if ( !isProducer() ) {
|
||||
assert(target == 0.0);
|
||||
assert(target == 0.0 || std::isnan(target));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1312,6 +1356,7 @@ namespace Opm
|
|||
// Put the well under group control immediately when GRUP control mdoe is specified
|
||||
if (prodSpec().control_mode_ == ProductionSpecification::GRUP) {
|
||||
set_current_control(self_index_, group_control_index_, wells_);
|
||||
individual_control_ = false;
|
||||
}
|
||||
} else {
|
||||
// We will now modify the last control, that
|
||||
|
@ -1607,6 +1652,18 @@ namespace Opm
|
|||
}
|
||||
|
||||
|
||||
bool WellNode::isGuideRateWellPotential() const
|
||||
{
|
||||
return is_guiderate_wellpotential_;
|
||||
}
|
||||
|
||||
|
||||
void WellNode::setIsGuideRateWellPotential(const bool flag)
|
||||
{
|
||||
is_guiderate_wellpotential_ = flag;
|
||||
}
|
||||
|
||||
|
||||
bool WellNode::canProduceMore() const
|
||||
{
|
||||
return (isProducer() && !individualControl());
|
||||
|
|
|
@ -211,6 +211,7 @@ namespace Opm
|
|||
const std::vector<double>& conversion_coeffs) = 0;
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group) = 0;
|
||||
|
@ -366,6 +367,7 @@ namespace Opm
|
|||
const std::vector<double>& conversion_coeffs);
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group);
|
||||
|
@ -484,6 +486,7 @@ namespace Opm
|
|||
const std::vector<double>& conversion_coeffs);
|
||||
|
||||
virtual void applyVREPGroupControl(const double target,
|
||||
const InjectionSpecification::InjectorType injector_type,
|
||||
const std::vector<double>& well_voidage_rates,
|
||||
const std::vector<double>& conversion_coeffs,
|
||||
const bool only_group);
|
||||
|
@ -511,6 +514,10 @@ namespace Opm
|
|||
|
||||
bool targetUpdated() const;
|
||||
|
||||
bool isGuideRateWellPotential() const;
|
||||
|
||||
void setIsGuideRateWellPotential(const bool flag);
|
||||
|
||||
virtual void setTargetUpdated(const bool flag);
|
||||
|
||||
virtual bool canProduceMore() const;
|
||||
|
@ -524,6 +531,10 @@ namespace Opm
|
|||
bool shut_well_;
|
||||
// TODO: used when updating well targets
|
||||
bool target_updated_;
|
||||
// whether the guide rate is specified with well potential
|
||||
// TODO: we have never handle the guide rates for groups, maybe this
|
||||
// is something will go to WellsGroupInterface later
|
||||
bool is_guiderate_wellpotential_;
|
||||
};
|
||||
|
||||
/// Creates the WellsGroupInterface for the given well
|
||||
|
|
|
@ -331,7 +331,6 @@ namespace Opm
|
|||
const UnstructuredGrid& grid)
|
||||
: w_(0), is_parallel_run_(false)
|
||||
{
|
||||
std::vector<double> dummy_well_potentials;
|
||||
// TODO: not sure about the usage of this WellsManager constructor
|
||||
// TODO: not sure whether this is the correct thing to do here.
|
||||
DynamicListEconLimited dummy_list_econ_limited;
|
||||
|
@ -339,7 +338,7 @@ namespace Opm
|
|||
UgGridHelpers::globalCell(grid), UgGridHelpers::cartDims(grid),
|
||||
UgGridHelpers::dimensions(grid),
|
||||
UgGridHelpers::cell2Faces(grid), UgGridHelpers::beginFaceCentroids(grid),
|
||||
dummy_list_econ_limited, dummy_well_potentials,
|
||||
dummy_list_econ_limited,
|
||||
std::unordered_set<std::string>());
|
||||
|
||||
}
|
||||
|
@ -734,10 +733,9 @@ namespace Opm
|
|||
|
||||
}
|
||||
|
||||
void WellsManager::setupGuideRates(std::vector< const Well* >& wells, const size_t timeStep, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index,
|
||||
const PhaseUsage& phaseUsage, const std::vector<double>& well_potentials)
|
||||
// only handle the guide rates from the keyword WGRUPCON
|
||||
void WellsManager::setupGuideRates(std::vector< const Well* >& wells, const size_t timeStep, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index)
|
||||
{
|
||||
const int np = phaseUsage.num_phases;
|
||||
for (auto wellIter = wells.begin(); wellIter != wells.end(); ++wellIter ) {
|
||||
const auto* well = *wellIter;
|
||||
|
||||
|
@ -749,7 +747,8 @@ namespace Opm
|
|||
const int wix = well_names_to_index[well->name()];
|
||||
WellNode& wellnode = *well_collection_.getLeafNodes()[wix];
|
||||
|
||||
if (well->getGuideRatePhase(timeStep) != GuideRate::UNDEFINED) {
|
||||
// TODO: looks like only handling OIL phase guide rate for producers
|
||||
if (well->getGuideRatePhase(timeStep) != GuideRate::UNDEFINED && well->getGuideRate(timeStep) >= 0.) {
|
||||
if (well_data[wix].type == PRODUCER) {
|
||||
wellnode.prodSpec().guide_rate_ = well->getGuideRate(timeStep);
|
||||
if (well->getGuideRatePhase(timeStep) == GuideRate::OIL) {
|
||||
|
@ -769,125 +768,10 @@ namespace Opm
|
|||
} else {
|
||||
OPM_THROW(std::runtime_error, "Unknown well type " << well_data[wix].type << " for well " << well->name());
|
||||
}
|
||||
} else if (well_potentials.size() > 0) { // default: calculate guide rates from well potentials
|
||||
|
||||
// Note: Modification of the guide rate using GUIDERAT is not supported
|
||||
|
||||
// only set the guide rates if there is a parent group with valied control
|
||||
if (wellnode.getParent() != nullptr) {
|
||||
const WellsGroupInterface& group = *wellnode.getParent();
|
||||
if ( well->isProducer(timeStep) ) {
|
||||
// The guide rates is calculated based on the group control
|
||||
// Currently only supporting WRAT, ORAT and GRAT.
|
||||
ProductionSpecification::ControlMode control_mode = group.prodSpec().control_mode_;
|
||||
if (control_mode == ProductionSpecification::FLD) {
|
||||
if (group.getParent() != nullptr) {
|
||||
const WellsGroupInterface& higher_group = *(group.getParent());
|
||||
control_mode = higher_group.prodSpec().control_mode_;
|
||||
} else {
|
||||
OPM_THROW(std::runtime_error, "Group " << group.name() << " is under FLD control while no higher level of group is specified.");
|
||||
}
|
||||
}
|
||||
|
||||
switch (control_mode) {
|
||||
case ProductionSpecification::WRAT: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water rate controlled well.");
|
||||
}
|
||||
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
||||
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + water_index];
|
||||
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::WATER;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::ORAT: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil rate controlled well.");
|
||||
}
|
||||
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
||||
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + oil_index];
|
||||
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::OIL;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::GRAT: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas rate controlled well.");
|
||||
}
|
||||
const int gas_index = phaseUsage.phase_pos[BlackoilPhases::Vapour];
|
||||
wellnode.prodSpec().guide_rate_ = well_potentials[np*wix + gas_index];
|
||||
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::GAS;
|
||||
break;
|
||||
}
|
||||
case ProductionSpecification::FLD: {
|
||||
OPM_THROW(std::logic_error, "Not support more than one continous level of FLD control");
|
||||
}
|
||||
case ProductionSpecification::LRAT: {
|
||||
double guide_rate = 0;
|
||||
if (phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
||||
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
||||
const double potential_oil = well_potentials[np*wix + oil_index];
|
||||
guide_rate += potential_oil;
|
||||
}
|
||||
if (phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
||||
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
||||
const double potential_water = well_potentials[np*wix + water_index];
|
||||
guide_rate += potential_water;
|
||||
}
|
||||
// not sure if no water and no oil, what will happen here, zero guide_rate?
|
||||
wellnode.prodSpec().guide_rate_ = guide_rate;
|
||||
wellnode.prodSpec().guide_rate_type_ = ProductionSpecification::LIQ;
|
||||
}
|
||||
case ProductionSpecification::NONE: {
|
||||
// Group control is not in use for this group.
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Not supported control_mode for guide rate computed" <<
|
||||
" from well potentials: " << group.prodSpec().control_mode_);
|
||||
}
|
||||
} else {
|
||||
// The guide rates is calculated based on the group injector type
|
||||
switch (group.injSpec().injector_type_) {
|
||||
case InjectionSpecification::WATER: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Aqua]) {
|
||||
OPM_THROW(std::runtime_error, "Water phase not used, yet found water injecting well.");
|
||||
}
|
||||
const int water_index = phaseUsage.phase_pos[BlackoilPhases::Aqua];
|
||||
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + water_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e water
|
||||
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::OIL: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Liquid]) {
|
||||
OPM_THROW(std::runtime_error, "Oil phase not used, yet found oil injecting well.");
|
||||
}
|
||||
const int oil_index = phaseUsage.phase_pos[BlackoilPhases::Liquid];
|
||||
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + oil_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e. oil
|
||||
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
break;
|
||||
}
|
||||
case InjectionSpecification::GAS: {
|
||||
if (!phaseUsage.phase_used[BlackoilPhases::Vapour]) {
|
||||
OPM_THROW(std::runtime_error, "Gas phase not used, yet found gas injecting well.");
|
||||
}
|
||||
const int gas_index = phaseUsage.phase_pos[BlackoilPhases::Vapour];
|
||||
wellnode.injSpec().guide_rate_ = well_potentials[np*wix + gas_index];
|
||||
// Guide rates applies to the phase that the well is injecting i.e gas
|
||||
wellnode.injSpec().guide_rate_type_ = InjectionSpecification::RAT;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
OPM_THROW(std::logic_error, "Not supported injector type for guide rate computed" <<
|
||||
" from well potentials: " << group.injSpec().injector_type_);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // if neither WGRUPCON nor well_potentials is given, distribute the flow equaly
|
||||
} else {
|
||||
wellnode.setIsGuideRateWellPotential(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} // namespace Opm
|
||||
|
|
|
@ -94,7 +94,6 @@ namespace Opm
|
|||
FC begin_face_centroids,
|
||||
const DynamicListEconLimited& list_econ_limited,
|
||||
bool is_parallel_run=false,
|
||||
const std::vector<double>& well_potentials=std::vector<double>(),
|
||||
const std::unordered_set<std::string>& deactivated_wells = std::unordered_set<std::string> ());
|
||||
|
||||
WellsManager(const Opm::EclipseState& eclipseState,
|
||||
|
@ -163,7 +162,6 @@ namespace Opm
|
|||
const C2F& cell_to_faces,
|
||||
FC begin_face_centroids,
|
||||
const DynamicListEconLimited& list_econ_limited,
|
||||
const std::vector<double>& well_potentials,
|
||||
const std::unordered_set<std::string>& deactivated_wells);
|
||||
// Disable copying and assignment.
|
||||
WellsManager(const WellsManager& other);
|
||||
|
@ -192,8 +190,8 @@ namespace Opm
|
|||
const std::unordered_set<std::string>& deactivated_wells,
|
||||
const DynamicListEconLimited& list_econ_limited);
|
||||
|
||||
void setupGuideRates(std::vector<const Well*>& wells, const size_t timeStep, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index,
|
||||
const PhaseUsage& phaseUsage, const std::vector<double>& well_potentials);
|
||||
void setupGuideRates(std::vector<const Well*>& wells, const size_t timeStep, std::vector<WellData>& well_data, std::map<std::string, int>& well_names_to_index);
|
||||
|
||||
// Data
|
||||
Wells* w_;
|
||||
WellCollection well_collection_;
|
||||
|
|
|
@ -318,13 +318,12 @@ WellsManager(const Opm::EclipseState& eclipseState,
|
|||
FC begin_face_centroids,
|
||||
const DynamicListEconLimited& list_econ_limited,
|
||||
bool is_parallel_run,
|
||||
const std::vector<double>& well_potentials,
|
||||
const std::unordered_set<std::string>& deactivated_wells)
|
||||
: w_(0), is_parallel_run_(is_parallel_run)
|
||||
{
|
||||
init(eclipseState, timeStep, number_of_cells, global_cell,
|
||||
cart_dims, dimensions,
|
||||
cell_to_faces, begin_face_centroids, list_econ_limited, well_potentials, deactivated_wells);
|
||||
cell_to_faces, begin_face_centroids, list_econ_limited, deactivated_wells);
|
||||
}
|
||||
|
||||
/// Construct wells from deck.
|
||||
|
@ -339,7 +338,6 @@ WellsManager::init(const Opm::EclipseState& eclipseState,
|
|||
const C2F& cell_to_faces,
|
||||
FC begin_face_centroids,
|
||||
const DynamicListEconLimited& list_econ_limited,
|
||||
const std::vector<double>& well_potentials,
|
||||
const std::unordered_set<std::string>& deactivated_wells)
|
||||
{
|
||||
if (dimensions != 3) {
|
||||
|
@ -445,8 +443,8 @@ WellsManager::init(const Opm::EclipseState& eclipseState,
|
|||
well_collection_.setWellsPointer(w_);
|
||||
|
||||
if (well_collection_.groupControlActive()) {
|
||||
setupGuideRates(wells, timeStep, well_data, well_names_to_index, pu, well_potentials);
|
||||
well_collection_.applyGroupControls();
|
||||
// here does not consider the well potentials related guide rate setting
|
||||
setupGuideRates(wells, timeStep, well_data, well_names_to_index);
|
||||
}
|
||||
|
||||
// Debug output.
|
||||
|
|
Loading…
Reference in New Issue
Block a user