Merge pull request #4199 from akva2/well_test

Add class for well tests
This commit is contained in:
Bård Skaflestad 2022-10-29 13:49:22 +02:00 committed by GitHub
commit 7b499c4f0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 600 additions and 523 deletions

View File

@ -112,6 +112,7 @@ list (APPEND MAIN_SOURCE_FILES
opm/simulators/wells/WellInterfaceIndices.cpp opm/simulators/wells/WellInterfaceIndices.cpp
opm/simulators/wells/WellProdIndexCalculator.cpp opm/simulators/wells/WellProdIndexCalculator.cpp
opm/simulators/wells/WellState.cpp opm/simulators/wells/WellState.cpp
opm/simulators/wells/WellTest.cpp
opm/simulators/wells/WGState.cpp opm/simulators/wells/WGState.cpp
) )
@ -390,6 +391,7 @@ list (APPEND PUBLIC_HEADER_FILES
opm/simulators/wells/WellInterface_impl.hpp opm/simulators/wells/WellInterface_impl.hpp
opm/simulators/wells/WellProdIndexCalculator.hpp opm/simulators/wells/WellProdIndexCalculator.hpp
opm/simulators/wells/WellState.hpp opm/simulators/wells/WellState.hpp
opm/simulators/wells/WellTest.hpp
opm/simulators/wells/WGState.hpp opm/simulators/wells/WGState.hpp
) )

View File

@ -103,7 +103,6 @@ public:
using WellInterfaceFluidSystem<FluidSystem>::Gas; using WellInterfaceFluidSystem<FluidSystem>::Gas;
using WellInterfaceFluidSystem<FluidSystem>::Oil; using WellInterfaceFluidSystem<FluidSystem>::Oil;
using WellInterfaceFluidSystem<FluidSystem>::Water; using WellInterfaceFluidSystem<FluidSystem>::Water;
using RatioLimitCheckReport = typename WellInterfaceFluidSystem<FluidSystem>::RatioLimitCheckReport;
static constexpr bool has_solvent = getPropValue<TypeTag, Properties::EnableSolvent>(); static constexpr bool has_solvent = getPropValue<TypeTag, Properties::EnableSolvent>();
static constexpr bool has_zFraction = getPropValue<TypeTag, Properties::EnableExtbo>(); static constexpr bool has_zFraction = getPropValue<TypeTag, Properties::EnableExtbo>();

View File

@ -24,17 +24,16 @@
#include <opm/material/fluidsystems/BlackOilFluidSystem.hpp> #include <opm/material/fluidsystems/BlackOilFluidSystem.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/input/eclipse/Schedule/Schedule.hpp> #include <opm/input/eclipse/Schedule/Schedule.hpp>
#include <opm/simulators/utils/DeferredLogger.hpp> #include <opm/simulators/utils/DeferredLogger.hpp>
#include <opm/simulators/wells/RateConverter.hpp> #include <opm/simulators/wells/GroupState.hpp>
#include <opm/simulators/wells/ParallelWellInfo.hpp> #include <opm/simulators/wells/ParallelWellInfo.hpp>
#include <opm/simulators/wells/RateConverter.hpp>
#include <opm/simulators/wells/SingleWellState.hpp>
#include <opm/simulators/wells/TargetCalculator.hpp>
#include <opm/simulators/wells/WellGroupHelpers.hpp> #include <opm/simulators/wells/WellGroupHelpers.hpp>
#include <opm/simulators/wells/WellState.hpp> #include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/wells/SingleWellState.hpp>
#include <opm/simulators/wells/GroupState.hpp>
#include <opm/simulators/wells/TargetCalculator.hpp>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
@ -503,443 +502,6 @@ checkConstraints(WellState& well_state,
} }
} }
template<typename FluidSystem>
bool
WellInterfaceFluidSystem<FluidSystem>::
checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
const double* rates_or_potentials,
DeferredLogger& deferred_logger) const
{
const PhaseUsage& pu = phaseUsage();
if (econ_production_limits.onMinOilRate()) {
assert(FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx));
const double oil_rate = rates_or_potentials[pu.phase_pos[ Oil ] ];
const double min_oil_rate = econ_production_limits.minOilRate();
if (std::abs(oil_rate) < min_oil_rate) {
return true;
}
}
if (econ_production_limits.onMinGasRate() ) {
assert(FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx));
const double gas_rate = rates_or_potentials[pu.phase_pos[ Gas ] ];
const double min_gas_rate = econ_production_limits.minGasRate();
if (std::abs(gas_rate) < min_gas_rate) {
return true;
}
}
if (econ_production_limits.onMinLiquidRate() ) {
assert(FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx));
assert(FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx));
const double oil_rate = rates_or_potentials[pu.phase_pos[ Oil ] ];
const double water_rate = rates_or_potentials[pu.phase_pos[ Water ] ];
const double liquid_rate = oil_rate + water_rate;
const double min_liquid_rate = econ_production_limits.minLiquidRate();
if (std::abs(liquid_rate) < min_liquid_rate) {
return true;
}
}
if (econ_production_limits.onMinReservoirFluidRate()) {
deferred_logger.warning("NOT_SUPPORTING_MIN_RESERVOIR_FLUID_RATE", "Minimum reservoir fluid production rate limit is not supported yet");
}
return false;
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
assert(FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx));
assert(FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx));
// function to calculate water cut based on rates
auto waterCut = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double oil_rate = -rates[pu.phase_pos[Oil]];
const double water_rate = -rates[pu.phase_pos[Water]];
const double liquid_rate = oil_rate + water_rate;
if (liquid_rate <= 0.)
return 0.;
else if (water_rate < 0)
return 0.;
else if (oil_rate < 0)
return 1.;
else
return (water_rate / liquid_rate);
};
const double max_water_cut_limit = econ_production_limits.maxWaterCut();
assert(max_water_cut_limit > 0.);
const bool watercut_limit_violated = checkMaxRatioLimitWell(ws, max_water_cut_limit, waterCut);
if (watercut_limit_violated) {
report.ratio_limit_violated = true;
checkMaxRatioLimitCompletions(ws, max_water_cut_limit, waterCut, report);
}
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
checkMaxGORLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
assert(FluidSystem::phaseIsActive(FluidSystem::oilPhaseIdx));
assert(FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx));
// function to calculate gor based on rates
auto gor = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double oil_rate = -rates[pu.phase_pos[Oil]];
const double gas_rate = -rates[pu.phase_pos[Gas]];
if (gas_rate <= 0.)
return 0.;
else if (oil_rate <= 0.)
return 1.e100; // big value to mark it as violated
else
return (gas_rate / oil_rate);
};
const double max_gor_limit = econ_production_limits.maxGasOilRatio();
assert(max_gor_limit > 0.);
const bool gor_limit_violated = checkMaxRatioLimitWell(ws, max_gor_limit, gor);
if (gor_limit_violated) {
report.ratio_limit_violated = true;
checkMaxRatioLimitCompletions(ws, max_gor_limit, gor, report);
}
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
checkMaxWGRLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
assert(FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx));
assert(FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx));
// function to calculate wgr based on rates
auto wgr = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double water_rate = -rates[pu.phase_pos[Water]];
const double gas_rate = -rates[pu.phase_pos[Gas]];
if (water_rate <= 0.)
return 0.;
else if (gas_rate <= 0.)
return 1.e100; // big value to mark it as violated
else
return (water_rate / gas_rate);
};
const double max_wgr_limit = econ_production_limits.maxWaterGasRatio();
assert(max_wgr_limit > 0.);
const bool wgr_limit_violated = checkMaxRatioLimitWell(ws, max_wgr_limit, wgr);
if (wgr_limit_violated) {
report.ratio_limit_violated = true;
checkMaxRatioLimitCompletions(ws, max_wgr_limit, wgr, report);
}
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report,
DeferredLogger& deferred_logger) const
{
// TODO: not sure how to define the worst-offending completion when more than one
// ratio related limit is violated.
// The defintion used here is that we define the violation extent based on the
// ratio between the value and the corresponding limit.
// For each violated limit, we decide the worst-offending completion separately.
// Among the worst-offending completions, we use the one has the biggest violation
// extent.
if (econ_production_limits.onMaxWaterCut()) {
checkMaxWaterCutLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxGasOilRatio()) {
checkMaxGORLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxWaterGasRatio()) {
checkMaxWGRLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxGasLiquidRatio()) {
deferred_logger.warning("NOT_SUPPORTING_MAX_GLR", "the support for max Gas-Liquid ratio is not implemented yet!");
}
if (report.ratio_limit_violated) {
// No worst offending completion is found because all the completions are either injecting or
// have trivial rates.
if(report.worst_offending_completion == INVALIDCOMPLETION) {
std::string message = "The well ratio limit is violated but all the completion rates are trivial! " + this->name() + " is kept open";
deferred_logger.warning("WECON_INVALIDCOMPLETION", message);
report.ratio_limit_violated = false;
}
// Due to numerical instability there may exist corner cases where the well breaks
// the ratio limit but no completion does.
else if(report.violation_extent <= 1.) {
std::string message = "The well ratio limit is violated but no completion ratio limit is violated! " + this->name() + " is kept open";
deferred_logger.warning("WECON_INCONSISTANT_COMPLETION_WELL", message);
report.ratio_limit_violated = false;
}
}
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
updateWellTestStateEconomic(const SingleWellState& ws,
const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const
{
if (this->wellIsStopped())
return;
const WellEconProductionLimits& econ_production_limits = well_ecl_.getEconLimits();
// if no limit is effective here, then continue to the next well
if ( !econ_production_limits.onAnyEffectiveLimit() ) {
return;
}
if (this->isInjector()) {
deferred_logger.warning("ECON_LIMITS_INJECTOR_" + this->name(), this->name() + " is an injector, the production economic limits for this well will be ignored.\n");
return;
}
// flag to check if the mim oil/gas rate limit is violated
bool rate_limit_violated = false;
const auto& quantity_limit = econ_production_limits.quantityLimit();
if (econ_production_limits.onAnyRateLimit()) {
if (quantity_limit == WellEconProductionLimits::QuantityLimit::POTN) {
rate_limit_violated = checkRateEconLimits(econ_production_limits, ws.well_potentials.data(), deferred_logger);
// Due to instability of the bhpFromThpLimit code the potentials are sometimes wrong
// this can lead to premature shutting of wells due to rate limits of the potentials.
// Since rates are supposed to be less or equal to the potentials, we double-check
// that also the rate limit is violated before shutting the well.
if (rate_limit_violated)
rate_limit_violated = checkRateEconLimits(econ_production_limits, ws.surface_rates.data(), deferred_logger);
}
else {
rate_limit_violated = checkRateEconLimits(econ_production_limits, ws.surface_rates.data(), deferred_logger);
}
}
if (rate_limit_violated) {
if (econ_production_limits.endRun()) {
const std::string warning_message = std::string("ending run after well closed due to economic limits")
+ std::string("is not supported yet \n")
+ std::string("the program will keep running after ") + name()
+ std::string(" is closed");
deferred_logger.warning("NOT_SUPPORTING_ENDRUN", warning_message);
}
if (econ_production_limits.validFollowonWell()) {
deferred_logger.warning("NOT_SUPPORTING_FOLLOWONWELL", "opening following on well after well closed is not supported yet");
}
well_test_state.close_well(name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (this->well_ecl_.getAutomaticShutIn()) {
const std::string msg = std::string("well ") + name() + std::string(" will be shut due to rate economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = std::string("well ") + name() + std::string(" will be stopped due to rate economic limit");
deferred_logger.info(msg);
}
}
// the well is closed, not need to check other limits
return;
}
if ( !econ_production_limits.onAnyRatioLimit() ) {
// there is no need to check the ratio limits
return;
}
// checking for ratio related limits, mostly all kinds of ratio.
RatioLimitCheckReport ratio_report;
checkRatioEconLimits(econ_production_limits, ws, ratio_report, deferred_logger);
if (ratio_report.ratio_limit_violated) {
const auto workover = econ_production_limits.workover();
switch (workover) {
case WellEconProductionLimits::EconWorkover::CON:
{
const int worst_offending_completion = ratio_report.worst_offending_completion;
well_test_state.close_completion(name(), worst_offending_completion, simulation_time);
if (write_message_to_opmlog) {
if (worst_offending_completion < 0) {
const std::string msg = std::string("Connection ") + std::to_string(- worst_offending_completion)
+ std::string(" for well ") + name() + std::string(" will be closed due to economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = std::string("Completion ") + std::to_string(worst_offending_completion)
+ std::string(" for well ") + name() + std::string(" will be closed due to economic limit");
deferred_logger.info(msg);
}
}
bool allCompletionsClosed = true;
const auto& connections = well_ecl_.getConnections();
for (const auto& connection : connections) {
if (connection.state() == Connection::State::OPEN
&& !well_test_state.completion_is_closed(name(), connection.complnum())) {
allCompletionsClosed = false;
}
}
if (allCompletionsClosed) {
well_test_state.close_well(name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (this->well_ecl_.getAutomaticShutIn()) {
const std::string msg = name() + std::string(" will be shut due to last completion closed");
deferred_logger.info(msg);
} else {
const std::string msg = name() + std::string(" will be stopped due to last completion closed");
deferred_logger.info(msg);
}
}
}
break;
}
case WellEconProductionLimits::EconWorkover::WELL:
{
well_test_state.close_well(name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (well_ecl_.getAutomaticShutIn()) {
// tell the control that the well is closed
const std::string msg = name() + std::string(" will be shut due to ratio economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = name() + std::string(" will be stopped due to ratio economic limit");
deferred_logger.info(msg);
}
}
break;
}
case WellEconProductionLimits::EconWorkover::NONE:
break;
default:
{
deferred_logger.warning("NOT_SUPPORTED_WORKOVER_TYPE",
"not supporting workover type " + WellEconProductionLimits::EconWorkover2String(workover) );
}
}
}
}
template<typename FluidSystem>
void
WellInterfaceFluidSystem<FluidSystem>::
updateWellTestState(const SingleWellState& ws,
const double& simulationTime,
const bool& writeMessageToOPMLog,
WellTestState& wellTestState,
DeferredLogger& deferred_logger) const
{
// updating well test state based on physical (THP/BHP) limits.
updateWellTestStatePhysical(simulationTime, writeMessageToOPMLog, wellTestState, deferred_logger);
// updating well test state based on Economic limits for operable wells
if (this->isOperableAndSolvable())
updateWellTestStateEconomic(ws, simulationTime, writeMessageToOPMLog, wellTestState, deferred_logger);
// TODO: well can be shut/closed due to other reasons
}
template<typename FluidSystem>
template <typename RatioFunc>
void WellInterfaceFluidSystem<FluidSystem>::
checkMaxRatioLimitCompletions(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc,
RatioLimitCheckReport& report) const
{
int worst_offending_completion = INVALIDCOMPLETION;
// the maximum water cut value of the completions
// it is used to identify the most offending completion
double max_ratio_completion = 0;
const int np = number_of_phases_;
const auto& perf_data = ws.perf_data;
const auto& perf_phase_rates = perf_data.phase_rates;
// look for the worst_offending_completion
for (const auto& completion : completions_) {
std::vector<double> completion_rates(np, 0.0);
// looping through the connections associated with the completion
const std::vector<int>& conns = completion.second;
for (const int c : conns) {
for (int p = 0; p < np; ++p) {
const double connection_rate = perf_phase_rates[c * np + p];
completion_rates[p] += connection_rate;
}
} // end of for (const int c : conns)
parallel_well_info_.communication().sum(completion_rates.data(), completion_rates.size());
const double ratio_completion = ratioFunc(completion_rates, phaseUsage());
if (ratio_completion > max_ratio_completion) {
worst_offending_completion = completion.first;
max_ratio_completion = ratio_completion;
}
} // end of for (const auto& completion : completions_)
const double violation_extent = max_ratio_completion / max_ratio_limit;
if (violation_extent > report.violation_extent) {
report.worst_offending_completion = worst_offending_completion;
report.violation_extent = violation_extent;
}
}
template<typename FluidSystem>
template<typename RatioFunc>
bool WellInterfaceFluidSystem<FluidSystem>::
checkMaxRatioLimitWell(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc) const
{
const int np = number_of_phases_;
std::vector<double> well_rates(np, 0.0);
for (int p = 0; p < np; ++p) {
well_rates[p] = ws.surface_rates[p];
}
const double well_ratio = ratioFunc(well_rates, phaseUsage());
return (well_ratio > max_ratio_limit);
}
template<typename FluidSystem> template<typename FluidSystem>
int int
WellInterfaceFluidSystem<FluidSystem>:: WellInterfaceFluidSystem<FluidSystem>::

View File

@ -39,8 +39,9 @@ namespace RateConverter
class Group; class Group;
class GroupState; class GroupState;
class Schedule; class Schedule;
class WellState; struct RatioLimitCheckReport;
class SingleWellState; class SingleWellState;
class WellState;
template<class FluidSystem> template<class FluidSystem>
class WellInterfaceFluidSystem : public WellInterfaceGeneric { class WellInterfaceFluidSystem : public WellInterfaceGeneric {
@ -51,12 +52,6 @@ protected:
static constexpr int INVALIDCOMPLETION = std::numeric_limits<int>::max(); static constexpr int INVALIDCOMPLETION = std::numeric_limits<int>::max();
public: public:
void updateWellTestState(const SingleWellState& ws,
const double& simulationTime,
const bool& writeMessageToOPMLog,
WellTestState& wellTestState,
DeferredLogger& deferred_logger) const;
int flowPhaseToEbosPhaseIdx(const int phaseIdx) const; int flowPhaseToEbosPhaseIdx(const int phaseIdx) const;
static constexpr int Water = BlackoilPhases::Aqua; static constexpr int Water = BlackoilPhases::Aqua;
@ -122,39 +117,6 @@ protected:
const SummaryState& summaryState, const SummaryState& summaryState,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger) const;
bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
const double* rates_or_potentials,
Opm::DeferredLogger& deferred_logger) const;
struct RatioLimitCheckReport{
bool ratio_limit_violated = false;
int worst_offending_completion = INVALIDCOMPLETION;
double violation_extent = 0.0;
};
void checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
void checkMaxGORLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
void checkMaxWGRLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
void checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report,
DeferredLogger& deferred_logger) const;
void updateWellTestStateEconomic(const SingleWellState& ws,
const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const;
std::optional<double> std::optional<double>
getGroupInjectionTargetRate(const Group& group, getGroupInjectionTargetRate(const Group& group,
const WellState& well_state, const WellState& well_state,
@ -175,18 +137,6 @@ protected:
// For the conversion between the surface volume rate and reservoir voidage rate // For the conversion between the surface volume rate and reservoir voidage rate
const RateConverterType& rateConverter_; const RateConverterType& rateConverter_;
private:
template <typename RatioFunc>
void checkMaxRatioLimitCompletions(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc,
RatioLimitCheckReport& report) const;
template<typename RatioFunc>
bool checkMaxRatioLimitWell(const SingleWellState& well_state,
const double max_ratio_limit,
const RatioFunc& ratioFunc) const;
}; };
} }

View File

@ -27,10 +27,11 @@
#include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp> #include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp>
#include <opm/simulators/wells/PerforationData.hpp> #include <opm/simulators/wells/PerforationData.hpp>
#include <opm/simulators/wells/ParallelWellInfo.hpp> #include <opm/simulators/wells/ParallelWellInfo.hpp>
#include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/wells/WellHelpers.hpp>
#include <opm/simulators/wells/VFPHelpers.hpp> #include <opm/simulators/wells/VFPHelpers.hpp>
#include <opm/simulators/wells/VFPProperties.hpp>
#include <opm/simulators/wells/WellHelpers.hpp>
#include <opm/simulators/wells/WellState.hpp>
#include <opm/simulators/wells/WellTest.hpp>
#include <cassert> #include <cassert>
#include <cmath> #include <cmath>
#include <cstddef> #include <cstddef>
@ -215,6 +216,24 @@ double WellInterfaceGeneric::mostStrictBhpFromBhpLimits(const SummaryState& summ
return 0.0; return 0.0;
} }
void WellInterfaceGeneric::updateWellTestState(const SingleWellState& ws,
const double& simulationTime,
const bool& writeMessageToOPMLog,
WellTestState& wellTestState,
DeferredLogger& deferred_logger) const
{
// updating well test state based on Economic limits for operable wells
if (this->isOperableAndSolvable()) {
WellTest(*this).updateWellTestStateEconomic(ws, simulationTime, writeMessageToOPMLog, wellTestState, deferred_logger);
} else {
// updating well test state based on physical (THP/BHP) limits.
WellTest(*this).updateWellTestStatePhysical(simulationTime, writeMessageToOPMLog, wellTestState, deferred_logger);
}
// TODO: well can be shut/closed due to other reasons
}
double WellInterfaceGeneric::getTHPConstraint(const SummaryState& summaryState) const double WellInterfaceGeneric::getTHPConstraint(const SummaryState& summaryState) const
{ {
if (dynamic_thp_limit_) { if (dynamic_thp_limit_) {
@ -394,26 +413,6 @@ bool WellInterfaceGeneric::isVFPActive(DeferredLogger& deferred_logger) const
} }
} }
void WellInterfaceGeneric::updateWellTestStatePhysical(const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const
{
if (!isOperableAndSolvable()) {
if (well_test_state.well_is_closed(name())) {
// Already closed, do nothing.
} else {
well_test_state.close_well(name(), WellTestConfig::Reason::PHYSICAL, simulation_time);
if (write_message_to_opmlog) {
const std::string action = well_ecl_.getAutomaticShutIn() ? "shut" : "stopped";
const std::string msg = "Well " + name()
+ " will be " + action + " as it can not operate under current reservoir conditions.";
deferred_logger.info(msg);
}
}
}
}
bool WellInterfaceGeneric::isOperableAndSolvable() const bool WellInterfaceGeneric::isOperableAndSolvable() const
{ {
return operability_status_.isOperableAndSolvable(); return operability_status_.isOperableAndSolvable();

View File

@ -172,6 +172,10 @@ public:
return well_index_; return well_index_;
} }
const std::map<int,std::vector<int>>& getCompletions() const {
return completions_;
}
double getTHPConstraint(const SummaryState& summaryState) const; double getTHPConstraint(const SummaryState& summaryState) const;
double getALQ(const WellState& well_state) const; double getALQ(const WellState& well_state) const;
double wsolvent() const; double wsolvent() const;
@ -193,15 +197,15 @@ public:
DeferredLogger& deferred_logger DeferredLogger& deferred_logger
) const; ) const;
void updateWellTestState(const SingleWellState& ws,
const double& simulationTime,
const bool& writeMessageToOPMLog,
WellTestState& wellTestState,
DeferredLogger& deferred_logger) const;
protected: protected:
bool getAllowCrossFlow() const; bool getAllowCrossFlow() const;
double mostStrictBhpFromBhpLimits(const SummaryState& summaryState) const; double mostStrictBhpFromBhpLimits(const SummaryState& summaryState) const;
void updateWellTestStatePhysical(const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const;
std::optional<double> bhpMax(const std::function<double(const double)>& fflo, std::optional<double> bhpMax(const std::function<double(const double)>& fflo,
const double bhp_limit, const double bhp_limit,

View File

@ -0,0 +1,458 @@
/*
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2017 Statoil ASA.
Copyright 2018 IRIS
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <opm/simulators/wells/WellTest.hpp>
#include <opm/input/eclipse/Schedule/Well/WellTestState.hpp>
#include <opm/simulators/utils/DeferredLogger.hpp>
#include <opm/simulators/wells/ParallelWellInfo.hpp>
#include <opm/simulators/wells/SingleWellState.hpp>
#include <opm/simulators/wells/WellInterfaceGeneric.hpp>
namespace Opm
{
template<class RatioFunc>
bool WellTest::checkMaxRatioLimitWell(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc) const
{
const int np = well_.numPhases();
std::vector<double> well_rates(np, 0.0);
for (int p = 0; p < np; ++p) {
well_rates[p] = ws.surface_rates[p];
}
const double well_ratio = ratioFunc(well_rates, well_.phaseUsage());
return (well_ratio > max_ratio_limit);
}
template<class RatioFunc>
void WellTest::checkMaxRatioLimitCompletions(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc,
RatioLimitCheckReport& report) const
{
int worst_offending_completion = RatioLimitCheckReport::INVALIDCOMPLETION;
// the maximum water cut value of the completions
// it is used to identify the most offending completion
double max_ratio_completion = 0;
const int np = well_.numPhases();
const auto& perf_data = ws.perf_data;
const auto& perf_phase_rates = perf_data.phase_rates;
// look for the worst_offending_completion
for (const auto& completion : well_.getCompletions()) {
std::vector<double> completion_rates(np, 0.0);
// looping through the connections associated with the completion
const std::vector<int>& conns = completion.second;
for (const int c : conns) {
for (int p = 0; p < np; ++p) {
const double connection_rate = perf_phase_rates[c * np + p];
completion_rates[p] += connection_rate;
}
} // end of for (const int c : conns)
well_.parallelWellInfo().communication().sum(completion_rates.data(), completion_rates.size());
const double ratio_completion = ratioFunc(completion_rates, well_.phaseUsage());
if (ratio_completion > max_ratio_completion) {
worst_offending_completion = completion.first;
max_ratio_completion = ratio_completion;
}
} // end of for (const auto& completion : completions_)
const double violation_extent = max_ratio_completion / max_ratio_limit;
if (violation_extent > report.violation_extent) {
report.worst_offending_completion = worst_offending_completion;
report.violation_extent = violation_extent;
}
}
void WellTest::checkMaxGORLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
static constexpr int Oil = BlackoilPhases::Liquid;
static constexpr int Gas = BlackoilPhases::Vapour;
// function to calculate gor based on rates
auto gor = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double oil_rate = -rates[pu.phase_pos[Oil]];
const double gas_rate = -rates[pu.phase_pos[Gas]];
if (gas_rate <= 0.)
return 0.;
else if (oil_rate <= 0.)
return 1.e100; // big value to mark it as violated
else
return (gas_rate / oil_rate);
};
const double max_gor_limit = econ_production_limits.maxGasOilRatio();
assert(max_gor_limit > 0.);
const bool gor_limit_violated = this->checkMaxRatioLimitWell(ws, max_gor_limit, gor);
if (gor_limit_violated) {
report.ratio_limit_violated = true;
this->checkMaxRatioLimitCompletions(ws, max_gor_limit, gor, report);
}
}
void WellTest::checkMaxWGRLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
static constexpr int Gas = BlackoilPhases::Vapour;
static constexpr int Water = BlackoilPhases::Aqua;
// function to calculate wgr based on rates
auto wgr = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double water_rate = -rates[pu.phase_pos[Water]];
const double gas_rate = -rates[pu.phase_pos[Gas]];
if (water_rate <= 0.)
return 0.;
else if (gas_rate <= 0.)
return 1.e100; // big value to mark it as violated
else
return (water_rate / gas_rate);
};
const double max_wgr_limit = econ_production_limits.maxWaterGasRatio();
assert(max_wgr_limit > 0.);
const bool wgr_limit_violated = this->checkMaxRatioLimitWell(ws, max_wgr_limit, wgr);
if (wgr_limit_violated) {
report.ratio_limit_violated = true;
this->checkMaxRatioLimitCompletions(ws, max_wgr_limit, wgr, report);
}
}
void WellTest::checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const
{
static constexpr int Oil = BlackoilPhases::Liquid;
static constexpr int Water = BlackoilPhases::Aqua;
// function to calculate water cut based on rates
auto waterCut = [](const std::vector<double>& rates,
const PhaseUsage& pu) {
const double oil_rate = -rates[pu.phase_pos[Oil]];
const double water_rate = -rates[pu.phase_pos[Water]];
const double liquid_rate = oil_rate + water_rate;
if (liquid_rate <= 0.)
return 0.;
else if (water_rate < 0)
return 0.;
else if (oil_rate < 0)
return 1.;
else
return (water_rate / liquid_rate);
};
const double max_water_cut_limit = econ_production_limits.maxWaterCut();
assert(max_water_cut_limit > 0.);
const bool watercut_limit_violated =
this->checkMaxRatioLimitWell(ws, max_water_cut_limit, waterCut);
if (watercut_limit_violated) {
report.ratio_limit_violated = true;
this->checkMaxRatioLimitCompletions(ws, max_water_cut_limit,
waterCut, report);
}
}
bool WellTest::checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
const std::vector<double>& rates_or_potentials,
DeferredLogger& deferred_logger) const
{
static constexpr int Gas = BlackoilPhases::Vapour;
static constexpr int Oil = BlackoilPhases::Liquid;
static constexpr int Water = BlackoilPhases::Aqua;
const PhaseUsage& pu = well_.phaseUsage();
if (econ_production_limits.onMinOilRate()) {
const double oil_rate = rates_or_potentials[pu.phase_pos[Oil]];
const double min_oil_rate = econ_production_limits.minOilRate();
if (std::abs(oil_rate) < min_oil_rate) {
return true;
}
}
if (econ_production_limits.onMinGasRate() ) {
const double gas_rate = rates_or_potentials[pu.phase_pos[Gas]];
const double min_gas_rate = econ_production_limits.minGasRate();
if (std::abs(gas_rate) < min_gas_rate) {
return true;
}
}
if (econ_production_limits.onMinLiquidRate() ) {
const double oil_rate = rates_or_potentials[pu.phase_pos[Oil]];
const double water_rate = rates_or_potentials[pu.phase_pos[Water]];
const double liquid_rate = oil_rate + water_rate;
const double min_liquid_rate = econ_production_limits.minLiquidRate();
if (std::abs(liquid_rate) < min_liquid_rate) {
return true;
}
}
if (econ_production_limits.onMinReservoirFluidRate()) {
deferred_logger.warning("NOT_SUPPORTING_MIN_RESERVOIR_FLUID_RATE", "Minimum reservoir fluid production rate limit is not supported yet");
}
return false;
}
WellTest::RatioLimitCheckReport WellTest::
checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
DeferredLogger& deferred_logger) const
{
// TODO: not sure how to define the worst-offending completion when more than one
// ratio related limit is violated.
// The defintion used here is that we define the violation extent based on the
// ratio between the value and the corresponding limit.
// For each violated limit, we decide the worst-offending completion separately.
// Among the worst-offending completions, we use the one has the biggest violation
// extent.
RatioLimitCheckReport report;
if (econ_production_limits.onMaxWaterCut()) {
this->checkMaxWaterCutLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxGasOilRatio()) {
this->checkMaxGORLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxWaterGasRatio()) {
this->checkMaxWGRLimit(econ_production_limits, ws, report);
}
if (econ_production_limits.onMaxGasLiquidRatio()) {
deferred_logger.warning("NOT_SUPPORTING_MAX_GLR", "the support for max Gas-Liquid ratio is not implemented yet!");
}
if (report.ratio_limit_violated) {
// No worst offending completion is found because all the completions are either injecting or
// have trivial rates.
if(report.worst_offending_completion == RatioLimitCheckReport::INVALIDCOMPLETION) {
std::string message = "The well ratio limit is violated but all the completion rates are trivial! " + well_.name() + " is kept open";
deferred_logger.warning("WECON_INVALIDCOMPLETION", message);
report.ratio_limit_violated = false;
}
// Due to numerical instability there may exist corner cases where the well breaks
// the ratio limit but no completion does.
else if(report.violation_extent <= 1.) {
std::string message = "The well ratio limit is violated but no completion ratio limit is violated! " + well_.name() + " is kept open";
deferred_logger.warning("WECON_INCONSISTANT_COMPLETION_WELL", message);
report.ratio_limit_violated = false;
}
}
return report;
}
void WellTest::updateWellTestStateEconomic(const SingleWellState& ws,
const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const
{
if (well_.wellIsStopped())
return;
const WellEconProductionLimits& econ_production_limits = well_.wellEcl().getEconLimits();
// if no limit is effective here, then continue to the next well
if (!econ_production_limits.onAnyEffectiveLimit()) {
return;
}
if (well_.isInjector()) {
deferred_logger.warning("ECON_LIMITS_INJECTOR_" + well_.name(), well_.name() + " is an injector, the production economic limits for this well will be ignored.\n");
return;
}
// flag to check if the mim oil/gas rate limit is violated
bool rate_limit_violated = false;
const auto& quantity_limit = econ_production_limits.quantityLimit();
if (econ_production_limits.onAnyRateLimit()) {
if (quantity_limit == WellEconProductionLimits::QuantityLimit::POTN) {
rate_limit_violated = this->checkRateEconLimits(econ_production_limits,
ws.well_potentials,
deferred_logger);
// Due to instability of the bhpFromThpLimit code the potentials are sometimes wrong
// this can lead to premature shutting of wells due to rate limits of the potentials.
// Since rates are supposed to be less or equal to the potentials, we double-check
// that also the rate limit is violated before shutting the well.
if (rate_limit_violated)
rate_limit_violated = this->checkRateEconLimits(econ_production_limits,
ws.surface_rates,
deferred_logger);
}
else {
rate_limit_violated = this->checkRateEconLimits(econ_production_limits,
ws.surface_rates,
deferred_logger);
}
}
if (rate_limit_violated) {
if (econ_production_limits.endRun()) {
const std::string warning_message = std::string("ending run after well closed due to economic limits")
+ std::string("is not supported yet \n")
+ std::string("the program will keep running after ") + well_.name()
+ std::string(" is closed");
deferred_logger.warning("NOT_SUPPORTING_ENDRUN", warning_message);
}
if (econ_production_limits.validFollowonWell()) {
deferred_logger.warning("NOT_SUPPORTING_FOLLOWONWELL", "opening following on well after well closed is not supported yet");
}
well_test_state.close_well(well_.name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (well_.wellEcl().getAutomaticShutIn()) {
const std::string msg = std::string("well ") + well_.name() + std::string(" will be shut due to rate economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = std::string("well ") + well_.name() + std::string(" will be stopped due to rate economic limit");
deferred_logger.info(msg);
}
}
// the well is closed, not need to check other limits
return;
}
if ( !econ_production_limits.onAnyRatioLimit() ) {
// there is no need to check the ratio limits
return;
}
// checking for ratio related limits, mostly all kinds of ratio.
RatioLimitCheckReport ratio_report =
this->checkRatioEconLimits(econ_production_limits, ws, deferred_logger);
if (ratio_report.ratio_limit_violated) {
const auto workover = econ_production_limits.workover();
switch (workover) {
case WellEconProductionLimits::EconWorkover::CON:
{
const int worst_offending_completion = ratio_report.worst_offending_completion;
well_test_state.close_completion(well_.name(), worst_offending_completion, simulation_time);
if (write_message_to_opmlog) {
if (worst_offending_completion < 0) {
const std::string msg = std::string("Connection ") + std::to_string(- worst_offending_completion)
+ std::string(" for well ") + well_.name() + std::string(" will be closed due to economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = std::string("Completion ") + std::to_string(worst_offending_completion)
+ std::string(" for well ") + well_.name() + std::string(" will be closed due to economic limit");
deferred_logger.info(msg);
}
}
bool allCompletionsClosed = true;
const auto& connections = well_.wellEcl().getConnections();
for (const auto& connection : connections) {
if (connection.state() == Connection::State::OPEN
&& !well_test_state.completion_is_closed(well_.name(), connection.complnum())) {
allCompletionsClosed = false;
}
}
if (allCompletionsClosed) {
well_test_state.close_well(well_.name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (well_.wellEcl().getAutomaticShutIn()) {
const std::string msg = well_.name() + std::string(" will be shut due to last completion closed");
deferred_logger.info(msg);
} else {
const std::string msg = well_.name() + std::string(" will be stopped due to last completion closed");
deferred_logger.info(msg);
}
}
}
break;
}
case WellEconProductionLimits::EconWorkover::WELL:
{
well_test_state.close_well(well_.name(), WellTestConfig::Reason::ECONOMIC, simulation_time);
if (write_message_to_opmlog) {
if (well_.wellEcl().getAutomaticShutIn()) {
// tell the control that the well is closed
const std::string msg = well_.name() + std::string(" will be shut due to ratio economic limit");
deferred_logger.info(msg);
} else {
const std::string msg = well_.name() + std::string(" will be stopped due to ratio economic limit");
deferred_logger.info(msg);
}
}
break;
}
case WellEconProductionLimits::EconWorkover::NONE:
break;
default:
{
deferred_logger.warning("NOT_SUPPORTED_WORKOVER_TYPE",
"not supporting workover type " + WellEconProductionLimits::EconWorkover2String(workover));
}
}
}
}
void WellTest::updateWellTestStatePhysical(const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const
{
if (well_test_state.well_is_closed(well_.name())) {
// Already closed, do nothing.
} else {
well_test_state.close_well(well_.name(), WellTestConfig::Reason::PHYSICAL, simulation_time);
if (write_message_to_opmlog) {
const std::string action = well_.wellEcl().getAutomaticShutIn() ? "shut" : "stopped";
const std::string msg = "Well " + well_.name()
+ " will be " + action + " as it can not operate under current reservoir conditions.";
deferred_logger.info(msg);
}
}
}
} // namespace Opm

View File

@ -0,0 +1,103 @@
/*
Copyright 2017 SINTEF Digital, Mathematics and Cybernetics.
Copyright 2017 Statoil ASA.
Copyright 2017 IRIS
Copyright 2019 Norce
This file is part of the Open Porous Media project (OPM).
OPM is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OPM is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OPM. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef OPM_WELL_TEST_HEADER_INCLUDED
#define OPM_WELL_TEST_HEADER_INCLUDED
#include <limits>
#include <vector>
namespace Opm
{
class DeferredLogger;
class PhaseUsage;
class SingleWellState;
class WellEconProductionLimits;
class WellInterfaceGeneric;
class WellTestState;
//! \brief Class for performing well tests.
class WellTest {
public:
//! \brief Constructor sets reference to well.
WellTest(const WellInterfaceGeneric& well) : well_(well) {}
void updateWellTestStateEconomic(const SingleWellState& ws,
const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const;
void updateWellTestStatePhysical(const double simulation_time,
const bool write_message_to_opmlog,
WellTestState& well_test_state,
DeferredLogger& deferred_logger) const;
private:
struct RatioLimitCheckReport {
static constexpr int INVALIDCOMPLETION = std::numeric_limits<int>::max();
bool ratio_limit_violated = false;
int worst_offending_completion = INVALIDCOMPLETION;
double violation_extent = 0.0;
};
void checkMaxGORLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
void checkMaxWGRLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
void checkMaxWaterCutLimit(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
RatioLimitCheckReport& report) const;
template<class RatioFunc>
bool checkMaxRatioLimitWell(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc) const;
template<class RatioFunc>
void checkMaxRatioLimitCompletions(const SingleWellState& ws,
const double max_ratio_limit,
const RatioFunc& ratioFunc,
RatioLimitCheckReport& report) const;
bool checkRateEconLimits(const WellEconProductionLimits& econ_production_limits,
const std::vector<double>& rates_or_potentials,
DeferredLogger& deferred_logger) const;
RatioLimitCheckReport
checkRatioEconLimits(const WellEconProductionLimits& econ_production_limits,
const SingleWellState& ws,
DeferredLogger& deferred_logger) const;
const WellInterfaceGeneric& well_; //!< Reference to well interface
};
}
#endif // OPM_WELL_TEST_HEADER_INCLUDED