Merge pull request #4895 from GitPaean/steink-local_control_switch_option_0920_3

Option for doing well control/status switching during local well solve
This commit is contained in:
Bård Skaflestad
2023-09-29 11:57:35 +02:00
committed by GitHub
16 changed files with 501 additions and 30 deletions
@@ -174,6 +174,10 @@ template<class TypeTag, class MyTypeTag>
struct UseAverageDensityMsWells { struct UseAverageDensityMsWells {
using type = UndefinedProperty; using type = UndefinedProperty;
}; };
template<class TypeTag, class MyTypeTag>
struct LocalWellSolveControlSwitching {
using type = UndefinedProperty;
};
// Network solver parameters // Network solver parameters
template<class TypeTag, class MyTypeTag> template<class TypeTag, class MyTypeTag>
struct NetworkMaxStrictIterations { struct NetworkMaxStrictIterations {
@@ -366,6 +370,11 @@ template<class TypeTag>
struct UseAverageDensityMsWells<TypeTag, TTag::FlowModelParameters> { struct UseAverageDensityMsWells<TypeTag, TTag::FlowModelParameters> {
static constexpr bool value = false; static constexpr bool value = false;
}; };
template<class TypeTag>
struct LocalWellSolveControlSwitching<TypeTag, TTag::FlowModelParameters> {
static constexpr bool value = false;
};
// Network solver parameters // Network solver parameters
template<class TypeTag> template<class TypeTag>
struct NetworkMaxStrictIterations<TypeTag, TTag::FlowModelParameters> { struct NetworkMaxStrictIterations<TypeTag, TTag::FlowModelParameters> {
@@ -530,6 +539,9 @@ namespace Opm
/// Whether to approximate segment densities by averaging over segment and its outlet /// Whether to approximate segment densities by averaging over segment and its outlet
bool use_average_density_ms_wells_; bool use_average_density_ms_wells_;
/// Whether to allow control switching during local well solutions
bool local_well_solver_control_switching_;
/// Maximum number of iterations in the network solver before relaxing tolerance /// Maximum number of iterations in the network solver before relaxing tolerance
int network_max_strict_iterations_; int network_max_strict_iterations_;
@@ -586,6 +598,7 @@ namespace Opm
check_well_operability_iter_ = EWOMS_GET_PARAM(TypeTag, bool, EnableWellOperabilityCheckIter); check_well_operability_iter_ = EWOMS_GET_PARAM(TypeTag, bool, EnableWellOperabilityCheckIter);
max_number_of_well_switches_ = EWOMS_GET_PARAM(TypeTag, int, MaximumNumberOfWellSwitches); max_number_of_well_switches_ = EWOMS_GET_PARAM(TypeTag, int, MaximumNumberOfWellSwitches);
use_average_density_ms_wells_ = EWOMS_GET_PARAM(TypeTag, bool, UseAverageDensityMsWells); use_average_density_ms_wells_ = EWOMS_GET_PARAM(TypeTag, bool, UseAverageDensityMsWells);
local_well_solver_control_switching_ = EWOMS_GET_PARAM(TypeTag, bool, LocalWellSolveControlSwitching);
nonlinear_solver_ = EWOMS_GET_PARAM(TypeTag, std::string, NonlinearSolver); nonlinear_solver_ = EWOMS_GET_PARAM(TypeTag, std::string, NonlinearSolver);
std::string approach = EWOMS_GET_PARAM(TypeTag, std::string, LocalSolveApproach); std::string approach = EWOMS_GET_PARAM(TypeTag, std::string, LocalSolveApproach);
if (approach == "jacobi") { if (approach == "jacobi") {
@@ -651,6 +664,7 @@ namespace Opm
EWOMS_REGISTER_PARAM(TypeTag, bool, EnableWellOperabilityCheckIter, "Enable the well operability checking during iterations"); EWOMS_REGISTER_PARAM(TypeTag, bool, EnableWellOperabilityCheckIter, "Enable the well operability checking during iterations");
EWOMS_REGISTER_PARAM(TypeTag, int, MaximumNumberOfWellSwitches, "Maximum number of times a well can switch to the same control"); EWOMS_REGISTER_PARAM(TypeTag, int, MaximumNumberOfWellSwitches, "Maximum number of times a well can switch to the same control");
EWOMS_REGISTER_PARAM(TypeTag, bool, UseAverageDensityMsWells, "Approximate segment densitities by averaging over segment and its outlet"); EWOMS_REGISTER_PARAM(TypeTag, bool, UseAverageDensityMsWells, "Approximate segment densitities by averaging over segment and its outlet");
EWOMS_REGISTER_PARAM(TypeTag, bool, LocalWellSolveControlSwitching, "Allow control switching during local well solutions");
EWOMS_REGISTER_PARAM(TypeTag, int, NetworkMaxStrictIterations, "Maximum iterations in network solver before relaxing tolerance"); EWOMS_REGISTER_PARAM(TypeTag, int, NetworkMaxStrictIterations, "Maximum iterations in network solver before relaxing tolerance");
EWOMS_REGISTER_PARAM(TypeTag, int, NetworkMaxIterations, "Maximum number of iterations in the network solver before giving up"); EWOMS_REGISTER_PARAM(TypeTag, int, NetworkMaxIterations, "Maximum number of iterations in the network solver before giving up");
EWOMS_REGISTER_PARAM(TypeTag, std::string, NonlinearSolver, "Choose nonlinear solver. Valid choices are newton or nldd."); EWOMS_REGISTER_PARAM(TypeTag, std::string, NonlinearSolver, "Choose nonlinear solver. Valid choices are newton or nldd.");
@@ -2258,6 +2258,9 @@ namespace Opm {
deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg); deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
} }
} }
// If we're using local well solves that include control switches, they also update
// operability, so reset before main iterations begin
well->resetWellOperability();
} }
updatePrimaryVariables(deferred_logger); updatePrimaryVariables(deferred_logger);
@@ -256,6 +256,14 @@ namespace Opm
const GroupState& group_state, const GroupState& group_state,
DeferredLogger& deferred_logger) override; DeferredLogger& deferred_logger) override;
virtual bool iterateWellEqWithSwitching(const Simulator& ebosSimulator,
const double dt,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger) override;
virtual void assembleWellEqWithoutIteration(const Simulator& ebosSimulator, virtual void assembleWellEqWithoutIteration(const Simulator& ebosSimulator,
const double dt, const double dt,
const Well::InjectionControls& inj_controls, const Well::InjectionControls& inj_controls,
@@ -140,11 +140,9 @@ segmentNumberToIndex(const int segment_number) const
template<typename Scalar> template<typename Scalar>
void void
MultisegmentWellGeneric<Scalar>:: MultisegmentWellGeneric<Scalar>::
detectOscillations(const std::vector<double>& measure_history, detectOscillations(const std::vector<double>& measure_history, bool& oscillate, bool& stagnate) const
const int it,
bool& oscillate,
bool& stagnate) const
{ {
const auto it = measure_history.size() - 1;
if ( it < 2 ) { if ( it < 2 ) {
oscillate = false; oscillate = false;
stagnate = false; stagnate = false;
@@ -65,7 +65,6 @@ protected:
/// Detect oscillation or stagnation based on the residual measure history /// Detect oscillation or stagnation based on the residual measure history
void detectOscillations(const std::vector<double>& measure_history, void detectOscillations(const std::vector<double>& measure_history,
const int it,
bool& oscillate, bool& oscillate,
bool& stagnate) const; bool& stagnate) const;
+173 -1
View File
@@ -1339,7 +1339,7 @@ namespace Opm
bool is_oscillate = false; bool is_oscillate = false;
bool is_stagnate = false; bool is_stagnate = false;
this->detectOscillations(measure_history, it, is_oscillate, is_stagnate); this->detectOscillations(measure_history, is_oscillate, is_stagnate);
// TODO: maybe we should have more sophisticated strategy to recover the relaxation factor, // TODO: maybe we should have more sophisticated strategy to recover the relaxation factor,
// for example, to recover it to be bigger // for example, to recover it to be bigger
@@ -1412,7 +1412,179 @@ namespace Opm
} }
template<typename TypeTag>
bool
MultisegmentWell<TypeTag>::
iterateWellEqWithSwitching(const Simulator& ebosSimulator,
const double dt,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger)
{
//if (!this->isOperableAndSolvable() && !this->wellIsStopped()) return true;
const int max_iter_number = this->param_.max_inner_iter_ms_wells_;
{
// getWellFiniteResiduals returns false for nan/inf residuals
const auto& [isFinite, residuals] = this->getFiniteWellResiduals(Base::B_avg_, deferred_logger);
if(!isFinite)
return false;
}
std::vector<std::vector<Scalar> > residual_history;
std::vector<double> measure_history;
int it = 0;
// relaxation factor
double relaxation_factor = 1.;
const double min_relaxation_factor = 0.6;
bool converged = false;
int stagnate_count = 0;
bool relax_convergence = false;
this->regularize_ = false;
// Max status switch frequency should be 2 to avoid getting stuck in cycle
const int min_its_after_switch = 2;
int its_since_last_switch = min_its_after_switch;
int switch_count= 0;
const auto well_status = this->wellStatus_;
const auto& summary_state = ebosSimulator.vanguard().summaryState();
const bool allow_switching = !this->wellUnderZeroRateTarget(summary_state, well_state) && (this->well_ecl_.getStatus() == WellStatus::OPEN);
bool changed = false;
bool final_check = false;
for (; it < max_iter_number; ++it, ++debug_cost_counter_) {
its_since_last_switch++;
if (its_since_last_switch >= min_its_after_switch){
const double wqTotal = this->primary_variables_.getWQTotal().value();
changed = this->updateWellControlAndStatusLocalIteration (ebosSimulator, well_state, group_state, inj_controls, prod_controls, wqTotal, deferred_logger);
if (changed){
its_since_last_switch = 0;
switch_count++;
}
if (!changed && final_check) {
break;
} else {
final_check = false;
}
}
assembleWellEqWithoutIteration(ebosSimulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
const BVectorWell dx_well = this->linSys_.solve();
if (it > this->param_.strict_inner_iter_wells_) {
relax_convergence = true;
this->regularize_ = true;
}
const auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence);
converged = report.converged();
if (converged) {
// if equations are sufficiently linear they might converge in less than min_its_after_switch
// in this case, make sure all constraints are satisfied before returning
if (switch_count > 0 && its_since_last_switch < min_its_after_switch) {
final_check = true;
its_since_last_switch = min_its_after_switch;
} else {
break;
}
}
// getFinteWellResiduals returns false for nan/inf residuals
{
const auto& [isFinite, residuals] = this->getFiniteWellResiduals(Base::B_avg_, deferred_logger);
if (!isFinite)
return false;
residual_history.push_back(residuals);
}
if (!converged) {
measure_history.push_back(this->getResidualMeasureValue(well_state,
residual_history[it],
this->param_.tolerance_wells_,
this->param_.tolerance_pressure_ms_wells_,
deferred_logger));
bool is_oscillate = false;
bool is_stagnate = false;
this->detectOscillations(measure_history, is_oscillate, is_stagnate);
// TODO: maybe we should have more sophisticated strategy to recover the relaxation factor,
// for example, to recover it to be bigger
if (is_oscillate || is_stagnate) {
// HACK!
std::string message;
if (relaxation_factor == min_relaxation_factor) {
++stagnate_count;
if (false) { // this disables the usage of the relaxed tolerance
fmt::format_to(std::back_inserter(message), " Well {} observes severe stagnation and/or oscillation."
" We relax the tolerance and check for convergence. \n", this->name());
const auto reportStag = getWellConvergence(summary_state, well_state, Base::B_avg_,
deferred_logger, true);
if (reportStag.converged()) {
converged = true;
fmt::format_to(std::back_inserter(message), " Well {} manages to get converged with relaxed tolerances in {} inner iterations", this->name(), it);
deferred_logger.debug(message);
return converged;
}
}
}
// a factor value to reduce the relaxation_factor
constexpr double reduction_mutliplier = 0.9;
relaxation_factor = std::max(relaxation_factor * reduction_mutliplier, min_relaxation_factor);
// debug output
if (is_stagnate) {
fmt::format_to(std::back_inserter(message), " well {} observes stagnation in inner iteration {}\n", this->name(), it);
}
if (is_oscillate) {
fmt::format_to(std::back_inserter(message), " well {} observes oscillation in inner iteration {}\n", this->name(), it);
}
fmt::format_to(std::back_inserter(message), " relaxation_factor is {} now\n", relaxation_factor);
this->regularize_ = true;
deferred_logger.debug(message);
}
}
updateWellState(summary_state, dx_well, well_state, deferred_logger, relaxation_factor);
initPrimaryVariablesEvaluation();
}
if (converged) {
if (allow_switching){
// update operability if status change
const bool is_stopped = this->wellIsStopped();
if (this->wellHasTHPConstraints(summary_state)){
this->operability_status_.can_obtain_bhp_with_thp_limit = !is_stopped;
this->operability_status_.obey_thp_limit_under_bhp_limit = !is_stopped;
} else {
this->operability_status_.operable_under_only_bhp_limit = !is_stopped;
}
// We reset the well status to it's original state. Status is updated
// on the outside based on operability status
this->wellStatus_ = well_status;
}
std::string message = fmt::format(" Well {} converged in {} inner iterations ("
"{} control/status switches).", this->name(), it, switch_count);
if (relax_convergence) {
message.append(fmt::format(" (A relaxed tolerance was used after {} iterations)",
this->param_.strict_inner_iter_wells_));
}
deferred_logger.debug(message);
} else {
const std::string message = fmt::format(" Well {} did not converged in {} inner iterations ("
"{} control/status switches).", this->name(), it, switch_count);
deferred_logger.debug(message);
}
return converged;
}
template<typename TypeTag> template<typename TypeTag>
+9
View File
@@ -205,6 +205,15 @@ namespace Opm
const GroupState& group_state, const GroupState& group_state,
DeferredLogger& deferred_logger) override; DeferredLogger& deferred_logger) override;
// iterate well equations including control switching
bool iterateWellEqWithSwitching(const Simulator& ebosSimulator,
const double dt,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger) override;
/// \brief Wether the Jacobian will also have well contributions in it. /// \brief Wether the Jacobian will also have well contributions in it.
virtual bool jacobianContainsWellContributions() const override virtual bool jacobianContainsWellContributions() const override
{ {
@@ -2155,6 +2155,103 @@ namespace Opm
} }
template<typename TypeTag>
bool
StandardWell<TypeTag>::
iterateWellEqWithSwitching(const Simulator& ebosSimulator,
const double dt,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger)
{
const int max_iter = this->param_.max_inner_iter_wells_;
int it = 0;
bool converged;
bool relax_convergence = false;
this->regularize_ = false;
const auto& summary_state = ebosSimulator.vanguard().summaryState();
// Max status switch frequency should be 2 to avoid getting stuck in cycle
constexpr int min_its_after_switch = 2;
int its_since_last_switch = min_its_after_switch;
int switch_count= 0;
const auto well_status = this->wellStatus_;
const bool allow_switching = !this->wellUnderZeroRateTarget(summary_state, well_state) && (this->well_ecl_.getStatus() == WellStatus::OPEN);
bool changed = false;
bool final_check = false;
do {
its_since_last_switch++;
if (allow_switching && its_since_last_switch >= min_its_after_switch){
const double wqTotal = this->primary_variables_.eval(WQTotal).value();
changed = this->updateWellControlAndStatusLocalIteration(ebosSimulator, well_state, group_state, inj_controls, prod_controls, wqTotal, deferred_logger);
if (changed){
its_since_last_switch = 0;
switch_count++;
}
if (!changed && final_check) {
break;
} else {
final_check = false;
}
}
assembleWellEqWithoutIteration(ebosSimulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
if (it > this->param_.strict_inner_iter_wells_) {
relax_convergence = true;
this->regularize_ = true;
}
auto report = getWellConvergence(summary_state, well_state, Base::B_avg_, deferred_logger, relax_convergence);
converged = report.converged();
if (converged) {
// if equations are sufficiently linear they might converge in less than min_its_after_switch
// in this case, make sure all constraints are satisfied before returning
if (switch_count > 0 && its_since_last_switch < min_its_after_switch) {
final_check = true;
its_since_last_switch = min_its_after_switch;
} else {
break;
}
}
++it;
solveEqAndUpdateWellState(summary_state, well_state, deferred_logger);
initPrimaryVariablesEvaluation();
} while (it < max_iter);
if (converged) {
if (allow_switching){
// update operability if status change
const bool is_stopped = this->wellIsStopped();
if (this->wellHasTHPConstraints(summary_state)){
this->operability_status_.can_obtain_bhp_with_thp_limit = !is_stopped;
this->operability_status_.obey_thp_limit_under_bhp_limit = !is_stopped;
} else {
this->operability_status_.operable_under_only_bhp_limit = !is_stopped;
}
// We reset the well status to its original state. Status is updated
// on the outside based on operability status
// \Note for future reference: For the well to update its status to stop/shut,
// the flag changed_to_stopped_this_step_ in prepareWellBeforeAssembling needs to be set to true.
// For this to happen, isOperableAndSolvable() must change from true to false,
// and (until the most recent commit) the well needs to be open for this to trigger.
// Hence, the resetting of status.
this->wellStatus_ = well_status;
}
} else {
const std::string message = fmt::format(" Well {} did not converged in {} inner iterations ("
"{} control/status switches).", this->name(), it, switch_count);
deferred_logger.debug(message);
// add operability here as well ?
}
return converged;
}
template<typename TypeTag> template<typename TypeTag>
std::vector<double> std::vector<double>
StandardWell<TypeTag>:: StandardWell<TypeTag>::
+13 -7
View File
@@ -39,13 +39,16 @@ checkIndividualConstraints(SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
const RateConvFunc& calcReservoirVoidageRates, const RateConvFunc& calcReservoirVoidageRates,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls,
const std::optional<Well::ProductionControls>& prod_controls) const
{ {
if (well_.isProducer()) { if (well_.isProducer()) {
auto new_cmode = this->activeProductionConstraint(ws, summaryState, auto new_cmode = this->activeProductionConstraint(ws, summaryState,
calcReservoirVoidageRates, calcReservoirVoidageRates,
thp_limit_violated_but_not_switched, thp_limit_violated_but_not_switched,
deferred_logger); deferred_logger,
prod_controls);
if (new_cmode != ws.production_cmode) { if (new_cmode != ws.production_cmode) {
ws.production_cmode = new_cmode; ws.production_cmode = new_cmode;
return true; return true;
@@ -55,7 +58,8 @@ checkIndividualConstraints(SingleWellState& ws,
if (well_.isInjector()) { if (well_.isInjector()) {
auto new_cmode = this->activeInjectionConstraint(ws, summaryState, auto new_cmode = this->activeInjectionConstraint(ws, summaryState,
thp_limit_violated_but_not_switched, thp_limit_violated_but_not_switched,
deferred_logger); deferred_logger,
inj_controls);
if (new_cmode != ws.injection_cmode) { if (new_cmode != ws.injection_cmode) {
ws.injection_cmode = new_cmode; ws.injection_cmode = new_cmode;
return true; return true;
@@ -69,11 +73,12 @@ Well::InjectorCMode WellConstraints::
activeInjectionConstraint(const SingleWellState& ws, activeInjectionConstraint(const SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls) const
{ {
const PhaseUsage& pu = well_.phaseUsage(); const PhaseUsage& pu = well_.phaseUsage();
const auto controls = well_.wellEcl().injectionControls(summaryState); const auto controls = inj_controls.has_value() ? inj_controls.value() : well_.wellEcl().injectionControls(summaryState);
const auto currentControl = ws.injection_cmode; const auto currentControl = ws.injection_cmode;
if (controls.hasControl(Well::InjectorCMode::BHP) && currentControl != Well::InjectorCMode::BHP) if (controls.hasControl(Well::InjectorCMode::BHP) && currentControl != Well::InjectorCMode::BHP)
@@ -166,10 +171,11 @@ activeProductionConstraint(const SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
const RateConvFunc& calcReservoirVoidageRates, const RateConvFunc& calcReservoirVoidageRates,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const DeferredLogger& deferred_logger,
const std::optional<Well::ProductionControls>& prod_controls) const
{ {
const PhaseUsage& pu = well_.phaseUsage(); const PhaseUsage& pu = well_.phaseUsage();
const auto controls = well_.wellEcl().productionControls(summaryState); const auto controls = prod_controls.has_value() ? prod_controls.value() : well_.wellEcl().productionControls(summaryState);
const auto currentControl = ws.production_cmode; const auto currentControl = ws.production_cmode;
if (controls.hasControl(Well::ProducerCMode::BHP) && currentControl != Well::ProducerCMode::BHP) { if (controls.hasControl(Well::ProducerCMode::BHP) && currentControl != Well::ProducerCMode::BHP) {
+10 -3
View File
@@ -24,9 +24,12 @@
#ifndef OPM_WELL_CONSTRAINTS_HEADER_INCLUDED #ifndef OPM_WELL_CONSTRAINTS_HEADER_INCLUDED
#define OPM_WELL_CONSTRAINTS_HEADER_INCLUDED #define OPM_WELL_CONSTRAINTS_HEADER_INCLUDED
#include <opm/input/eclipse/Schedule/Well/Well.hpp>
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <vector> #include <vector>
#include <optional>
namespace Opm namespace Opm
{ {
@@ -55,21 +58,25 @@ public:
const SummaryState& summaryState, const SummaryState& summaryState,
const RateConvFunc& calcReservoirVoidageRates, const RateConvFunc& calcReservoirVoidageRates,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls = std::nullopt,
const std::optional<Well::ProductionControls>& prod_controls = std::nullopt) const;
private: private:
WellInjectorCMode WellInjectorCMode
activeInjectionConstraint(const SingleWellState& ws, activeInjectionConstraint(const SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls = std::nullopt) const;
WellProducerCMode WellProducerCMode
activeProductionConstraint(const SingleWellState& ws, activeProductionConstraint(const SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
const RateConvFunc& calcReservoirVoidageRates, const RateConvFunc& calcReservoirVoidageRates,
bool& thp_limit_violated_but_not_switched, bool& thp_limit_violated_but_not_switched,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger,
const std::optional<Well::ProductionControls>& prod_controls = std::nullopt) const;
const WellInterfaceGeneric& well_; //!< Reference to well interface const WellInterfaceGeneric& well_; //!< Reference to well interface
}; };
+19
View File
@@ -241,6 +241,14 @@ public:
const GroupState& group_state, const GroupState& group_state,
DeferredLogger& deferred_logger) /* const */; DeferredLogger& deferred_logger) /* const */;
bool updateWellControlAndStatusLocalIteration(const Simulator& ebos_simulator,
WellState& well_state,
const GroupState& group_state,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
const double WQTotal,
DeferredLogger& deferred_logger);
virtual void updatePrimaryVariables(const SummaryState& summary_state, virtual void updatePrimaryVariables(const SummaryState& summary_state,
const WellState& well_state, const WellState& well_state,
DeferredLogger& deferred_logger) = 0; DeferredLogger& deferred_logger) = 0;
@@ -302,6 +310,9 @@ public:
const WellState& well_state, const WellState& well_state,
DeferredLogger& deferred_logger); DeferredLogger& deferred_logger);
bool updateWellOperabilityFromWellEq(const Simulator& ebos_simulator,
const WellState& well_state,
DeferredLogger& deferred_logger);
// update perforation water throughput based on solved water rate // update perforation water throughput based on solved water rate
virtual void updateWaterThroughput(const double dt, WellState& well_state) const = 0; virtual void updateWaterThroughput(const double dt, WellState& well_state) const = 0;
@@ -390,6 +401,14 @@ protected:
const GroupState& group_state, const GroupState& group_state,
DeferredLogger& deferred_logger) = 0; DeferredLogger& deferred_logger) = 0;
virtual bool iterateWellEqWithSwitching(const Simulator& ebosSimulator,
const double dt,
const WellInjectionControls& inj_controls,
const WellProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger) = 0;
bool iterateWellEquations(const Simulator& ebosSimulator, bool iterateWellEquations(const Simulator& ebosSimulator,
const double dt, const double dt,
WellState& well_state, WellState& well_state,
@@ -105,7 +105,9 @@ bool
WellInterfaceFluidSystem<FluidSystem>:: WellInterfaceFluidSystem<FluidSystem>::
checkIndividualConstraints(SingleWellState& ws, checkIndividualConstraints(SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
DeferredLogger& deferred_logger) const DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls,
const std::optional<Well::ProductionControls>& prod_controls) const
{ {
auto rRates = [this](const int fipreg, auto rRates = [this](const int fipreg,
const int pvtRegion, const int pvtRegion,
@@ -119,7 +121,7 @@ checkIndividualConstraints(SingleWellState& ws,
return WellConstraints(*this). return WellConstraints(*this).
checkIndividualConstraints(ws, summaryState, rRates, checkIndividualConstraints(ws, summaryState, rRates,
this->operability_status_.thp_limit_violated_but_not_switched, this->operability_status_.thp_limit_violated_but_not_switched,
deferred_logger); deferred_logger, inj_controls, prod_controls);
} }
template <typename FluidSystem> template <typename FluidSystem>
@@ -79,7 +79,9 @@ protected:
bool checkIndividualConstraints(SingleWellState& ws, bool checkIndividualConstraints(SingleWellState& ws,
const SummaryState& summaryState, const SummaryState& summaryState,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger,
const std::optional<Well::InjectionControls>& inj_controls = std::nullopt,
const std::optional<Well::ProductionControls>& prod_controls = std::nullopt) const;
bool checkGroupConstraints(WellState& well_state, bool checkGroupConstraints(WellState& well_state,
const GroupState& group_state, const GroupState& group_state,
+21 -3
View File
@@ -344,12 +344,25 @@ void WellInterfaceGeneric::setVFPProperties(const VFPProperties* vfp_properties_
vfp_properties_ = vfp_properties_arg; vfp_properties_ = vfp_properties_arg;
} }
void WellInterfaceGeneric::setPrevSurfaceRates(WellState& well_state, void WellInterfaceGeneric::setPrevSurfaceRates(WellState& well_state,
const WellState& prev_well_state) const const WellState& prev_well_state) const
{ {
auto& ws = well_state.well(this->index_of_well_); auto& ws = well_state.well(this->index_of_well_);
if (!this->changedToOpenThisStep()){ auto& ws_prev = prev_well_state.well(this->index_of_well_);
ws.prev_surface_rates = prev_well_state.well(this->index_of_well_).surface_rates; // The logic here is a bit fragile:
// We need non-zero prev_surface_rates for the purpose of providing explicit fractions
// (if needed) for vfp interpolation.
// We assume that current surface rates either are initialized from previous step
// or (if newly opened) from updateWellStateRates. This is fine unless well was
// stopped in previous step in which case it's rates will be zero. In this case,
// we select the previous rates of the previous well state (and hope for the best).
const bool zero_rates = std::all_of(ws.surface_rates.begin(), ws.surface_rates.end(),
[](double rate) {
return rate == 0.; // TODO: should we use a threshhold for comparison?
} );
if (zero_rates) {
ws.prev_surface_rates = ws_prev.prev_surface_rates;
} else { } else {
ws.prev_surface_rates = ws.surface_rates; ws.prev_surface_rates = ws.surface_rates;
} }
@@ -552,6 +565,11 @@ bool WellInterfaceGeneric::stopppedOrZeroRateTarget(const SummaryState& summary_
} }
void WellInterfaceGeneric::resetWellOperability()
{
this->operability_status_.resetOperability();
}
double WellInterfaceGeneric::wmicrobes_() const double WellInterfaceGeneric::wmicrobes_() const
{ {
auto injectorType = this->well_ecl_.injectorType(); auto injectorType = this->well_ecl_.injectorType();
@@ -218,6 +218,8 @@ public:
inj_fc_multiplier_ = inj_fc_multiplier; inj_fc_multiplier_ = inj_fc_multiplier;
} }
void resetWellOperability();
protected: protected:
bool getAllowCrossFlow() const; bool getAllowCrossFlow() const;
+123 -8
View File
@@ -254,7 +254,82 @@ namespace Opm
return changed; return changed;
} }
template<typename TypeTag>
bool
WellInterface<TypeTag>::
updateWellControlAndStatusLocalIteration(const Simulator& ebos_simulator,
WellState& well_state,
const GroupState& group_state,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
const double wqTotal,
DeferredLogger& deferred_logger)
{
const auto& summary_state = ebos_simulator.vanguard().summaryState();
const auto& schedule = ebos_simulator.vanguard().schedule();
if (this->wellUnderZeroRateTarget(summary_state, well_state) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) {
return false;
}
const double sgn = this->isInjector() ? 1.0 : -1.0;
if (!this->wellIsStopped()){
if (wqTotal*sgn <= 0.0){
this->stopWell();
return true;
} else {
bool changed = false;
auto& ws = well_state.well(this->index_of_well_);
const bool hasGroupControl = this->isInjector() ? inj_controls.hasControl(Well::InjectorCMode::GRUP) :
prod_controls.hasControl(Well::ProducerCMode::GRUP);
changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls);
// TODO: with current way, the checkGroupConstraints might overwrite the result from checkIndividualConstraints, which remains to be investigated
if (hasGroupControl) {
changed = this->checkGroupConstraints(well_state, group_state, schedule, summary_state,deferred_logger);
}
if (changed) {
const bool thp_controlled = this->isInjector() ? ws.injection_cmode == Well::InjectorCMode::THP :
ws.production_cmode == Well::ProducerCMode::THP;
if (!thp_controlled){
// don't call for thp since this might trigger additional local solve
updateWellStateWithTarget(ebos_simulator, group_state, well_state, deferred_logger);
} else {
ws.thp = this->getTHPConstraint(summary_state);
}
updatePrimaryVariables(summary_state, well_state, deferred_logger);
}
return changed;
}
} else {
// well is stopped, check if current bhp allows reopening
const double bhp = well_state.well(this->index_of_well_).bhp;
double prod_limit = prod_controls.bhp_limit;
double inj_limit = inj_controls.bhp_limit;
const bool has_thp = this->wellHasTHPConstraints(summary_state);
if (has_thp){
// calculate bhp from thp-limit (using explicit fractions zince zero rate)
// TODO: this will often be too strict condition for re-opening, a better
// option is probably minimum bhp on current vfp-curve, but some more functionality
// is needed for this option to be robustly implemented.
std::vector<double> rates(this->num_components_);
const double bhp_thp = WellBhpThpCalculator(*this).calculateBhpFromThp(well_state, rates, this->well_ecl_, summary_state, this->getRefDensity(), deferred_logger);
if (this->isInjector()){
inj_limit = std::min(bhp_thp, inj_controls.bhp_limit);
} else {
prod_limit = std::max(bhp_thp, prod_controls.bhp_limit);
}
}
const double bhp_diff = (this->isInjector())? inj_limit - bhp: bhp - prod_limit;
if (bhp_diff > 0){
this->openWell();
return true;
} else {
return false;
}
}
}
template<typename TypeTag> template<typename TypeTag>
void void
@@ -363,7 +438,13 @@ namespace Opm
const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0); const auto prod_controls = this->well_ecl_.isProducer() ? this->well_ecl_.productionControls(summary_state) : Well::ProductionControls(0);
bool converged = false; bool converged = false;
try { try {
converged = this->iterateWellEqWithControl(ebosSimulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger); // TODO: the following two functions will be refactored to be one to reduce the code duplication
if (!this->param_.local_well_solver_control_switching_){
converged = this->iterateWellEqWithControl(ebosSimulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
} else {
converged = this->iterateWellEqWithSwitching(ebosSimulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
}
} catch (NumericalProblem& e ) { } catch (NumericalProblem& e ) {
const std::string msg = "Inner well iterations failed for well " + this->name() + " Treat the well as unconverged. "; const std::string msg = "Inner well iterations failed for well " + this->name() + " Treat the well as unconverged. ";
deferred_logger.warning("INNER_ITERATION_FAILED", msg); deferred_logger.warning("INNER_ITERATION_FAILED", msg);
@@ -545,13 +626,21 @@ namespace Opm
const bool well_operable = this->operability_status_.isOperableAndSolvable(); const bool well_operable = this->operability_status_.isOperableAndSolvable();
if (!well_operable && old_well_operable) { if (!well_operable && old_well_operable) {
if (this->well_ecl_.getAutomaticShutIn()) { if (this->param_.local_well_solver_control_switching_) {
deferred_logger.info(" well " + this->name() + " gets SHUT during iteration "); deferred_logger.info(" well " + this->name() + " gets STOPPED during iteration ");
this->stopWell();
changed_to_stopped_this_step_ = true;
} else { } else {
if (!this->wellIsStopped()) { // \Note: keep the old manner for now for testing checking.
deferred_logger.info(" well " + this->name() + " gets STOPPED during iteration "); // Will be investgiated and fixed in a later PR
this->stopWell(); if (this->well_ecl_.getAutomaticShutIn()) {
changed_to_stopped_this_step_ = true; deferred_logger.info(" well " + this->name() + " gets SHUT during iteration ");
} else {
if (!this->wellIsStopped()) {
deferred_logger.info(" well " + this->name() + " gets STOPPED during iteration ");
this->stopWell();
changed_to_stopped_this_step_ = true;
}
} }
} }
} else if (well_operable && !old_well_operable) { } else if (well_operable && !old_well_operable) {
@@ -706,7 +795,16 @@ namespace Opm
updateWellOperability(const Simulator& ebos_simulator, updateWellOperability(const Simulator& ebos_simulator,
const WellState& well_state, const WellState& well_state,
DeferredLogger& deferred_logger) DeferredLogger& deferred_logger)
{ {
if (this->param_.local_well_solver_control_switching_) {
const bool success = updateWellOperabilityFromWellEq(ebos_simulator, well_state, deferred_logger);
if (success) {
return;
} else {
deferred_logger.debug("Operability check using well equations did not converge for well "
+ this->name() + ", reverting to classical approach." );
}
}
this->operability_status_.resetOperability(); this->operability_status_.resetOperability();
bool thp_controlled = this->isInjector() ? well_state.well(this->index_of_well_).injection_cmode == Well::InjectorCMode::THP: bool thp_controlled = this->isInjector() ? well_state.well(this->index_of_well_).injection_cmode == Well::InjectorCMode::THP:
@@ -727,6 +825,23 @@ namespace Opm
} }
} }
template<typename TypeTag>
bool
WellInterface<TypeTag>::
updateWellOperabilityFromWellEq(const Simulator& ebos_simulator,
const WellState& well_state,
DeferredLogger& deferred_logger)
{
// only makes sense if we're using this parameter is true
assert(this->param_.local_well_solver_control_switching_);
this->operability_status_.resetOperability();
WellState well_state_copy = well_state;
const auto& group_state = ebos_simulator.problem().wellModel().groupState();
const double dt = ebos_simulator.timeStepSize();
// equations should be converged at this stage, so only one it is needed
bool converged = iterateWellEquations(ebos_simulator, dt, well_state_copy, group_state, deferred_logger);
return converged;
}
template<typename TypeTag> template<typename TypeTag>
void void