mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-02-25 18:55:30 -06:00
Merge pull request #5395 from steink/consistent_vfp_interpolation
Small fixes related to thp-control/vfp-extrapolation
This commit is contained in:
commit
904cb7e2c4
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user