Merge pull request #5395 from steink/consistent_vfp_interpolation

Small fixes related to thp-control/vfp-extrapolation
This commit is contained in:
Kai Bao 2024-06-04 15:15:41 +02:00 committed by GitHub
commit 904cb7e2c4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 103 additions and 64 deletions

View File

@ -363,7 +363,8 @@ template<class Scalar>
Scalar VFPHelpers<Scalar>::
findTHP(const std::vector<Scalar>& bhp_array,
const std::vector<double>& thp_array,
Scalar bhp)
Scalar bhp,
const bool find_largest)
{
int nthp = thp_array.size();
@ -429,49 +430,79 @@ findTHP(const std::vector<Scalar>& bhp_array,
}
//bhp_array not sorted, raw search.
else {
//Find i so that bhp_array[i-1] <= bhp <= bhp_array[i];
//Since the BHP values might not be sorted, first search within
//our interpolation values, and then try to extrapolate.
int i=0;
bool found = false;
for (; i<nthp-1; ++i) {
const Scalar& y0 = bhp_array[i ];
const Scalar& y1 = bhp_array[i+1];
//Here we're into damage prevention territory, and there may be any number of
//solutions (including zero). The well is currently not controlled by THP, and
//since we're doing severe extrapolaton we would also like, if possible, to prevent
//it from switcing to THP. Accordingly, if there are multiple solutions, we return
//the value for the intersection corresponding to the largest (smallest) THP-value
//for producers (injectors).
if (y0 < bhp && bhp <= y1) {
//first check which extrapolations are valid
const bool first_slope_positive = bhp_array[1] >= bhp_array[0];
const bool valid_low = (bhp < bhp_array[0] && first_slope_positive) || (bhp >= bhp_array[0] && !first_slope_positive);
const bool last_slope_positive = bhp_array[nthp-1] >= bhp_array[nthp-2];
const bool valid_high = (bhp > bhp_array[nthp-1] && last_slope_positive) || (bhp <= bhp_array[nthp-1] && !last_slope_positive);
bool found = false;
int array_ix = 0;
if (find_largest){//find intersection corresponding to the largest thp
// high extrap -> table interp -> low extrap
if (valid_high) {
found = true;
break;
array_ix = nthp-2;
} else {
//search backward within table
for (int i = nthp-2; i>=0; --i) {
const Scalar& y0 = bhp_array[i ];
const Scalar& y1 = bhp_array[i+1];
if (std::min(y0, y1) < bhp && bhp <= std::max(y0, y1)) {
found = true;
array_ix = i;
break;
}
}
if (!found && valid_low) {
found = true;
array_ix = 0;
}
}
} else {//find intersection corresponding to the smallest thp
//low extrap -> table interp -> high extrap
if (valid_low) {
found = true;
array_ix = 0;
} else {
//search forward within table
for (int i = 0; i<nthp-1; ++i) {
const Scalar& y0 = bhp_array[i ];
const Scalar& y1 = bhp_array[i+1];
if (std::min(y0, y1) < bhp && bhp <= std::max(y0, y1)) {
found = true;
array_ix = i;
break;
}
}
if (!found && valid_high) {
found = true;
array_ix = nthp-2;
}
}
}
if (found) {
const Scalar& x0 = thp_array[i ];
const Scalar& x1 = thp_array[i+1];
const Scalar& y0 = bhp_array[i ];
const Scalar& y1 = bhp_array[i+1];
const Scalar& x0 = thp_array[array_ix ];
const Scalar& x1 = thp_array[array_ix+1];
const Scalar& y0 = bhp_array[array_ix ];
const Scalar& y1 = bhp_array[array_ix+1];
thp = findX(x0, x1, y0, y1, bhp);
}
else if (bhp <= bhp_array[0]) {
//TODO: LOG extrapolation
const Scalar& x0 = thp_array[0];
const Scalar& x1 = thp_array[1];
const Scalar& y0 = bhp_array[0];
const Scalar& y1 = bhp_array[1];
thp = findX(x0, x1, y0, y1, bhp);
}
//Target bhp greater than all values in array, extrapolate
else if (bhp > bhp_array[nthp-1]) {
//TODO: LOG extrapolation
const Scalar& x0 = thp_array[nthp-2];
const Scalar& x1 = thp_array[nthp-1];
const Scalar& y0 = bhp_array[nthp-2];
const Scalar& y1 = bhp_array[nthp-1];
thp = findX(x0, x1, y0, y1, bhp);
}
else {
OPM_THROW(std::logic_error, "Programmer error: Unable to find THP in THP array");
} else {
// no intersection, just return largest/smallest value in table
if (find_largest) {
thp = thp_array[nthp-1];
} else {
thp = thp_array[0];
}
}
}
return thp;
}

View File

@ -189,7 +189,8 @@ public:
*/
static Scalar findTHP(const std::vector<Scalar>& bhp_array,
const std::vector<double>& thp_array,
Scalar bhp);
Scalar bhp,
const bool find_largest = true);
/**
* Get (flo, bhp) at minimum bhp for given thp,wfr,gfr,alq

View File

@ -76,7 +76,7 @@ thp(const int table_id,
bhp_array[i] = VFPHelpers<Scalar>::interpolate(table, flo_i, thp_i).value;
}
return VFPHelpers<Scalar>::findTHP(bhp_array, thp_array, bhp_arg);
return VFPHelpers<Scalar>::findTHP(bhp_array, thp_array, bhp_arg, /*find_largest*/ false);
}
template<class Scalar>

View File

@ -38,25 +38,19 @@ thp(const int table_id,
const Scalar liquid,
const Scalar vapour,
const Scalar bhp_arg,
const Scalar alq) const
const Scalar alq,
const Scalar explicit_wfr,
const Scalar explicit_gfr,
const bool use_expvfp) const
{
const VFPProdTable& table = detail::getTable(m_tables, table_id);
// Find interpolation variables.
Scalar flo = 0.0;
Scalar wfr = 0.0;
Scalar gfr = 0.0;
if (aqua == 0.0 && liquid == 0.0 && vapour == 0.0) {
// All zero, likely at initial state.
// Set FLO variable to minimum to avoid extrapolation.
// The water and gas fractions are kept at 0.0.
flo = table.getFloAxis().front();
} else {
// The usual case.
// Recall that production rate is negative in Opm, so switch the sign.
flo = -detail::getFlo(table, aqua, liquid, vapour);
wfr = detail::getWFR(table, aqua, liquid, vapour);
gfr = detail::getGFR(table, aqua, liquid, vapour);
Scalar flo = detail::getFlo(table, aqua, liquid, vapour);
Scalar wfr = detail::getWFR(table, aqua, liquid, vapour);
Scalar gfr = detail::getGFR(table, aqua, liquid, vapour);
if (use_expvfp || -flo < table.getFloAxis().front()) {
wfr = explicit_wfr;
gfr = explicit_gfr;
}
const std::vector<double> thp_array = table.getTHPAxis();
@ -67,7 +61,7 @@ thp(const int table_id,
* by interpolating for every value of thp. This might be somewhat
* expensive, but let us assome that nthp is small.
*/
auto flo_i = VFPHelpers<Scalar>::findInterpData( flo, table.getFloAxis());
auto flo_i = VFPHelpers<Scalar>::findInterpData(-flo, table.getFloAxis());
auto wfr_i = VFPHelpers<Scalar>::findInterpData( wfr, table.getWFRAxis());
auto gfr_i = VFPHelpers<Scalar>::findInterpData( gfr, table.getGFRAxis());
auto alq_i = VFPHelpers<Scalar>::findInterpData( alq, table.getALQAxis());
@ -77,7 +71,7 @@ thp(const int table_id,
bhp_array[i] = VFPHelpers<Scalar>::interpolate(table, flo_i, thp_i, wfr_i, gfr_i, alq_i).value;
}
return VFPHelpers<Scalar>::findTHP(bhp_array, thp_array, bhp_arg);
return VFPHelpers<Scalar>::findTHP(bhp_array, thp_array, bhp_arg, /*find_largest*/ true);
}
template<class Scalar>

View File

@ -109,7 +109,10 @@ public:
const Scalar liquid,
const Scalar vapour,
const Scalar bhp,
const Scalar alq) const;
const Scalar alq,
const Scalar explicit_wrf,
const Scalar explicit_gfr,
const bool use_expvfp) const;
/**
* Returns the table associated with the ID, or throws an exception if

View File

@ -143,11 +143,14 @@ calculateThpFromBhp(const std::vector<Scalar>& rates,
else if (well_.isProducer()) {
const Scalar vfp_ref_depth = well_.vfpProperties()->getProd()->getTable(table_id).getDatumDepth();
const Scalar dp = wellhelpers::computeHydrostaticCorrection(well_.refDepth(), vfp_ref_depth, rho, well_.gravity());
const bool use_vfpexp = well_.useVfpExplicit();
const Scalar wfr = well_.vfpProperties()->getExplicitWFR(table_id, well_.indexOfWell());
const Scalar gfr = well_.vfpProperties()->getExplicitGFR(table_id, well_.indexOfWell());
auto thp_func =
[this, table_id, aqua, liquid, vapour, dp, &alq]
[this, table_id, aqua, liquid, vapour, dp, &alq, wfr, gfr, use_vfpexp]
(const Scalar bhp_value, const Scalar pressure_loss) {
return this->well_.vfpProperties()->getProd()->thp(
table_id, aqua, liquid, vapour, bhp_value + dp - pressure_loss, alq.value());
table_id, aqua, liquid, vapour, bhp_value + dp - pressure_loss, alq.value(), wfr, gfr, use_vfpexp);
};
thp = findThpFromBhpIteratively(thp_func, bhp, thp_limit, dp, deferred_logger);
}

View File

@ -280,8 +280,16 @@ namespace Opm
{
const auto& summary_state = simulator.vanguard().summaryState();
const auto& schedule = simulator.vanguard().schedule();
auto& ws = well_state.well(this->index_of_well_);
std::string from;
if (this->isInjector()) {
from = WellInjectorCMode2String(ws.injection_cmode);
} else {
from = WellProducerCMode2String(ws.production_cmode);
}
const bool oscillating = std::count(this->well_control_log_.begin(), this->well_control_log_.end(), from) >= param_.max_number_of_well_switches_;
if (this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) {
if (oscillating || this->wellUnderZeroRateTarget(simulator, well_state, deferred_logger) || !(this->well_ecl_.getStatus() == WellStatus::OPEN)) {
return false;
}
@ -293,7 +301,6 @@ namespace Opm
} else {
bool changed = false;
if (!fixed_control) {
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);

View File

@ -528,7 +528,7 @@ BOOST_AUTO_TEST_CASE(THPToBHPAndBackPlane)
double alq = 32.9;
double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq, 0, 0 , false);
double thp_val = properties->thp(1, aqua, liquid, vapour, bhp_val, alq);
double thp_val = properties->thp(1, aqua, liquid, vapour, bhp_val, alq, 0, 0, false);
BOOST_CHECK_CLOSE(thp_val, thp, max_d_tol);
}
@ -547,7 +547,7 @@ BOOST_AUTO_TEST_CASE(THPToBHPAndBackNonTrivial)
double alq = 32.9;
double bhp_val = properties->bhp(1, aqua, liquid, vapour, thp, alq, 0, 0, false);
double thp_val = properties->thp(1, aqua, liquid, vapour, bhp_val, alq);
double thp_val = properties->thp(1, aqua, liquid, vapour, bhp_val, alq, 0, 0, false);
BOOST_CHECK_CLOSE(thp_val, thp, max_d_tol);
}
@ -633,7 +633,7 @@ VFPPROD \n\
double bhp_interp = properties.bhp(42, aqua, liquid, vapour, thp, alq, 0, 0, false);
double bhp_ref = thp;
double thp_interp = properties.thp(42, aqua, liquid, vapour, bhp_ref, alq);
double thp_interp = properties.thp(42, aqua, liquid, vapour, bhp_ref, alq, 0, 0, false);
double thp_ref = thp;
double bhp_diff = std::abs(bhp_interp - bhp_ref);