adding computeWellPotentialWithTHP() to StandardWell

it is not clear at the moment how all the well potentials related will
work with MS wells. For the moment, keep the well poentials related only
in StandardWell.

By theory, they should be very similar, while some dependence on certain
member variables makes some small refactoring work necessary.
This commit is contained in:
Kai Bao 2017-07-24 14:48:57 +02:00
parent c3cc4021fa
commit 1d34c9dc6e
4 changed files with 257 additions and 2 deletions

View File

@ -245,11 +245,18 @@ namespace Opm
const ModelParameters& param,
WellState& well_state);
using WellInterface<TypeTag>::wellHasTHPConstraints;
using WellInterface<TypeTag>::mostStrictBhpFromBhpLimits;
// TODO: maybe we should provide a light version of computeWellFlux, which does not include the
// calculation of the derivatives
void computeWellRatesWithBhp(const Simulator& ebosSimulator,
const EvalWell& bhp,
std::vector<double>& well_flux) const;
std::vector<double> computeWellPotentialWithTHP(const Simulator& ebosSimulator,
const double initial_bhp, // bhp from BHP constraints
const std::vector<double>& initial_potential) const;
};
}

View File

@ -1968,4 +1968,129 @@ namespace Opm
}
}
template<typename TypeTag>
std::vector<double>
StandardWell<TypeTag>::
computeWellPotentialWithTHP(const Simulator& ebosSimulator,
const double initial_bhp, // bhp from BHP constraints
const std::vector<double>& initial_potential) const
{
// TODO: pay attention to the situation that finally the potential is calculated based on the bhp control
// TODO: should we consider the bhp constraints during the iterative process?
const int np = numberOfPhases();
assert( np == int(initial_potential.size()) );
std::vector<double> potentials = initial_potential;
std::vector<double> old_potentials = potentials; // keeping track of the old potentials
double bhp = initial_bhp;
double old_bhp = bhp;
bool converged = false;
const int max_iteration = 1000;
const double bhp_tolerance = 1000.; // 1000 pascal
int iteration = 0;
while ( !converged && iteration < max_iteration ) {
// for each iteration, we calculate the bhp based on the rates/potentials with thp constraints
// with considering the bhp value from the bhp limits. At the beginning of each iteration,
// we initialize the bhp to be the bhp value from the bhp limits. Then based on the bhp values calculated
// from the thp constraints, we decide the effective bhp value for well potential calculation.
bhp = initial_bhp;
// The number of the well controls/constraints
const int nwc = well_controls_get_num(wellControls());
for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) {
if (well_controls_iget_type(wellControls(), ctrl_index) == THP) {
double aqua = 0.0;
double liquid = 0.0;
double vapour = 0.0;
const Opm::PhaseUsage& pu = phase_usage_;
if (active()[ Water ]) {
aqua = potentials[pu.phase_pos[ Water ] ];
}
if (active()[ Oil ]) {
liquid = potentials[pu.phase_pos[ Oil ] ];
}
if (active()[ Gas ]) {
vapour = potentials[pu.phase_pos[ Gas ] ];
}
const int vfp = well_controls_iget_vfp(wellControls(), ctrl_index);
const double thp = well_controls_iget_target(wellControls(), ctrl_index);
const double alq = well_controls_iget_alq(wellControls(), ctrl_index);
// Calculating the BHP value based on THP
// TODO: check whether it is always correct to do calculation based on the depth of the first perforation.
const double rho = 0.; // perf_densities_[0]; // TODO: this item is the one keeping the function from WellInterface
const double well_ref_depth = perf_depth_[0];
if (wellType() == INJECTOR) {
const double vfp_ref_depth = vfp_properties_->getInj()->getTable(vfp)->getDatumDepth();
const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_);
const double bhp_calculated = vfp_properties_->getInj()->bhp(vfp, aqua, liquid, vapour, thp) - dp;
// apply the strictest of the bhp controlls i.e. smallest bhp for injectors
if (bhp_calculated < bhp) {
bhp = bhp_calculated;
}
}
else if (wellType() == PRODUCER) {
const double vfp_ref_depth = vfp_properties_->getProd()->getTable(vfp)->getDatumDepth();
const double dp = wellhelpers::computeHydrostaticCorrection(well_ref_depth, vfp_ref_depth, rho, gravity_);
const double bhp_calculated = vfp_properties_->getProd()->bhp(vfp, aqua, liquid, vapour, thp, alq) - dp;
// apply the strictest of the bhp controlls i.e. largest bhp for producers
if (bhp_calculated > bhp) {
bhp = bhp_calculated;
}
} else {
OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type of well");
}
}
}
// there should be always some available bhp/thp constraints there
if (std::isinf(bhp) || std::isnan(bhp)) {
OPM_THROW(std::runtime_error, "Unvalid bhp value obtained during the potential calculation for well " << name());
}
converged = std::abs(old_bhp - bhp) < bhp_tolerance;
computeWellRatesWithBhp(ebosSimulator, bhp, potentials);
// checking whether the potentials have valid values
for (const double value : potentials) {
if (std::isinf(value) || std::isnan(value)) {
OPM_THROW(std::runtime_error, "Unvalid potential value obtained during the potential calculation for well " << name());
}
}
if (!converged) {
old_bhp = bhp;
for (int p = 0; p < np; ++p) {
// TODO: improve the interpolation, will it always be valid with the way below?
// TODO: finding better paramters, better iteration strategy for better convergence rate.
const double potential_update_damping_factor = 0.001;
potentials[p] = potential_update_damping_factor * potentials[p] + (1.0 - potential_update_damping_factor) * old_potentials[p];
old_potentials[p] = potentials[p];
}
}
++iteration;
}
if (!converged) {
OPM_THROW(std::runtime_error, "Failed in getting converged for the potential calculation for well " << name());
}
return potentials;
}
}

View File

@ -159,7 +159,7 @@ namespace Opm
// TODO: for this kind of function, maybe can make a function with parameter perf
const std::vector<int>& saturationTableNumber() const;
const double wsolvent() const;
double wsolvent() const;
virtual bool getWellConvergence(Simulator& ebosSimulator,
const std::vector<double>& B_avg,
@ -197,6 +197,10 @@ namespace Opm
virtual void applySolutionWellState(const BVector& x, const ModelParameters& param,
WellState& well_state) const = 0;
void computeWellPotentials(const Simulator& ebosSimulator,
const WellState& well_state,
std::vector<double>& well_potentials) const;
protected:
// TODO: some variables shared by all the wells should be made static
// well name
@ -255,6 +259,10 @@ namespace Opm
const VFPProperties* vfp_properties_;
double gravity_;
bool wellHasTHPConstraints() const;
double mostStrictBhpFromBhpLimits() const;
};
}

View File

@ -376,7 +376,7 @@ namespace Opm
template<typename TypeTag>
const double
double
WellInterface<TypeTag>::
wsolvent() const
{
@ -387,4 +387,119 @@ namespace Opm
return 0.0;
}
template<typename TypeTag>
void
WellInterface<TypeTag>::
computeWellPotentials(const Simulator& ebosSimulator,
const WellState& well_state,
std::vector<double>& well_potentials) const
{
const int np = numberOfPhases();
well_potentials.resize(np, 0.0);
// get the bhp value based on the bhp constraints
const double bhp = mostStrictBhpFromBhpLimits();
// does the well have a THP related constraint?
if ( !wellHasTHPConstraints() ) {
assert(std::abs(bhp) != std::numeric_limits<double>::max());
computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials);
} else {
// the well has a THP related constraint
// checking whether a well is newly added, it only happens at the beginning of the report step
if ( !well_state.isNewWell(index_of_well_) ) {
for (int p = 0; p < np; ++p) {
// This is dangerous for new added well
// since we are not handling the initialization correctly for now
well_potentials[p] = well_state.wellRates()[index_of_well_ * np + p];
}
} else {
// We need to generate a reasonable rates to start the iteration process
computeWellRatesWithBhp(ebosSimulator, bhp, well_potentials);
for (double& value : well_potentials) {
// make the value a little safer in case the BHP limits are default ones
// TODO: a better way should be a better rescaling based on the investigation of the VFP table.
const double rate_safety_scaling_factor = 0.00001;
value *= rate_safety_scaling_factor;
}
}
// potentials = computeWellPotentialWithTHP(ebosSimulator, w, bhp, potentials);
}
}
template<typename TypeTag>
double
WellInterface<TypeTag>::
mostStrictBhpFromBhpLimits() const
{
double bhp;
// initial bhp value, making the value not usable
switch( well_type_ ) {
case INJECTOR:
bhp = std::numeric_limits<double>::max();
break;
case PRODUCER:
bhp = -std::numeric_limits<double>::max();
break;
default:
OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name());
}
// The number of the well controls/constraints
const int nwc = well_controls_get_num(well_controls_);
for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) {
// finding a BHP constraint
if (well_controls_iget_type(well_controls_, ctrl_index) == BHP) {
// get the bhp constraint value, it should always be postive assummingly
const double bhp_target = well_controls_iget_target(well_controls_, ctrl_index);
switch(well_type_) {
case INJECTOR: // using the lower bhp contraint from Injectors
if (bhp_target < bhp) {
bhp = bhp_target;
}
break;
case PRODUCER:
if (bhp_target > bhp) {
bhp = bhp_target;
}
break;
default:
OPM_THROW(std::logic_error, "Expected PRODUCER or INJECTOR type for well " << name());
} // end of switch
}
}
return bhp;
}
template<typename TypeTag>
bool
WellInterface<TypeTag>::
wellHasTHPConstraints() const
{
const int nwc = well_controls_get_num(well_controls_);
for (int ctrl_index = 0; ctrl_index < nwc; ++ctrl_index) {
if (well_controls_iget_type(well_controls_, ctrl_index) == THP) {
return true;
}
}
return false;
}
}