mirror of
https://github.com/OPM/opm-simulators.git
synced 2025-01-20 01:32:57 -06:00
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:
parent
c3cc4021fa
commit
1d34c9dc6e
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user