WellInterface: use Scalar type

This commit is contained in:
Arne Morten Kvarving 2024-02-20 09:05:09 +01:00
parent 5dfb926643
commit 3d8e5e5750
2 changed files with 213 additions and 200 deletions

View File

@ -98,7 +98,7 @@ public:
using MatrixBlockType = Dune::FieldMatrix<Scalar, Indices::numEq, Indices::numEq>;
using Eval = typename Base::Eval;
using BVector = Dune::BlockVector<VectorBlockType>;
using PressureMatrix = Dune::BCRSMatrix<Opm::MatrixBlock<double, 1, 1>>;
using PressureMatrix = Dune::BCRSMatrix<Opm::MatrixBlock<Scalar, 1, 1>>;
using RateConverterType =
typename WellInterfaceFluidSystem<FluidSystem>::RateConverterType;
@ -148,17 +148,17 @@ public:
virtual ~WellInterface() = default;
virtual void init(const PhaseUsage* phase_usage_arg,
const std::vector<double>& depth_arg,
const double gravity_arg,
const std::vector<Scalar>& depth_arg,
const Scalar gravity_arg,
const int num_cells,
const std::vector< Scalar >& B_avg,
const std::vector<Scalar>& B_avg,
const bool changed_to_open_this_step);
virtual void initPrimaryVariablesEvaluation() = 0;
virtual ConvergenceReport getWellConvergence(const SummaryState& summary_state,
const WellState<Scalar>& well_state,
const std::vector<double>& B_avg,
const std::vector<Scalar>& B_avg,
DeferredLogger& deferred_logger,
const bool relax_tolerance) const = 0;
@ -186,19 +186,16 @@ public:
DeferredLogger& deferred_logger);
virtual void computeWellRatesWithBhp(
const Simulator& simulator,
const double& bhp,
std::vector<double>& well_flux,
DeferredLogger& deferred_logger
) const = 0;
virtual void computeWellRatesWithBhp(const Simulator& ebosSimulator,
const Scalar& bhp,
std::vector<Scalar>& well_flux,
DeferredLogger& deferred_logger) const = 0;
virtual std::optional<double> computeBhpAtThpLimitProdWithAlq(
const Simulator& simulator,
const SummaryState& summary_state,
const double alq_value,
DeferredLogger& deferred_logger
) const = 0;
virtual std::optional<Scalar>
computeBhpAtThpLimitProdWithAlq(const Simulator& ebos_simulator,
const SummaryState& summary_state,
const Scalar alq_value,
DeferredLogger& deferred_logger) const = 0;
/// using the solution x to recover the solution xw for wells and applying
/// xw to update Well State
@ -216,7 +213,7 @@ public:
// TODO: before we decide to put more information under mutable, this function is not const
virtual void computeWellPotentials(const Simulator& simulator,
const WellState<Scalar>& well_state,
std::vector<double>& well_potentials,
std::vector<Scalar>& well_potentials,
DeferredLogger& deferred_logger) = 0;
virtual void updateWellStateWithTarget(const Simulator& simulator,
@ -226,7 +223,7 @@ public:
virtual void computeWellRatesWithBhpIterations(const Simulator& simulator,
const Scalar& bhp,
std::vector<double>& well_flux,
std::vector<Scalar>& well_flux,
DeferredLogger& deferred_logger) const = 0;
bool updateWellStateWithTHPTargetProd(const Simulator& simulator,
@ -245,7 +242,7 @@ public:
const GroupState<Scalar>& group_state,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
const double WQTotal,
const Scalar WQTotal,
DeferredLogger& deferred_logger,
const bool fixed_control = false,
const bool fixed_status = false);
@ -263,7 +260,7 @@ public:
WellState<Scalar>& well_state,
DeferredLogger& deferred_logger) const = 0;
virtual double connectionDensity(const int globalConnIdx,
virtual Scalar connectionDensity(const int globalConnIdx,
const int openConnIdx) const = 0;
/// \brief Wether the Jacobian will also have well contributions in it.
@ -298,12 +295,11 @@ public:
const WellState<Scalar>& well_state,
DeferredLogger& deferred_logger);
bool gliftBeginTimeStepWellTestIterateWellEquations(
const Simulator& simulator,
const double dt,
WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger);
bool gliftBeginTimeStepWellTestIterateWellEquations(const Simulator& ebos_simulator,
const double dt,
WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger);
void gliftBeginTimeStepWellTestUpdateALQ(const Simulator& simulator,
WellState<Scalar>& well_state,
@ -325,8 +321,9 @@ public:
/// Compute well rates based on current reservoir conditions and well variables.
/// Used in updateWellStateRates().
virtual std::vector<double> computeCurrentWellRates(const Simulator& simulator,
DeferredLogger& deferred_logger) const = 0;
virtual std::vector<Scalar>
computeCurrentWellRates(const Simulator& simulator,
DeferredLogger& deferred_logger) const = 0;
/// Modify the well_state's rates if there is only one nonzero rate.
/// If so, that rate is kept as is, but the others are set proportionally
@ -345,55 +342,50 @@ public:
return connectionRates_;
}
virtual std::vector<double> getPrimaryVars() const
virtual std::vector<Scalar> getPrimaryVars() const
{
return {};
}
virtual int setPrimaryVars(std::vector<double>::const_iterator)
virtual int setPrimaryVars(typename std::vector<Scalar>::const_iterator)
{
return 0;
}
std::vector<double> wellIndex(const int perf,
std::vector<Scalar> wellIndex(const int perf,
const IntensiveQuantities& intQuants,
const double trans_mult,
const SingleWellState<double>& ws) const;
const Scalar trans_mult,
const SingleWellState<Scalar>& ws) const;
void updateConnectionDFactor(const Simulator& simulator,
SingleWellState<double>& ws) const;
SingleWellState<Scalar>& ws) const;
void updateConnectionTransmissibilityFactor(const Simulator& simulator,
SingleWellState<double>& ws) const;
SingleWellState<Scalar>& ws) const;
protected:
// simulation parameters
const ModelParameters& param_;
std::vector<RateVector> connectionRates_;
std::vector< Scalar > B_avg_;
std::vector<Scalar> B_avg_;
bool changed_to_stopped_this_step_ = false;
bool thp_update_iterations = false;
double wpolymer() const;
Scalar wpolymer() const;
Scalar wfoam() const;
Scalar wsalt() const;
Scalar wmicrobes() const;
Scalar woxygen() const;
Scalar wurea() const;
double wfoam() const;
double wsalt() const;
double wmicrobes() const;
double woxygen() const;
double wurea() const;
virtual double getRefDensity() const = 0;
virtual Scalar getRefDensity() const = 0;
// Component fractions for each phase for the well
const std::vector<double>& compFrac() const;
const std::vector<Scalar>& compFrac() const;
std::vector<double> initialWellRateFractions(const Simulator& simulator,
const WellState<Scalar>& well_state) const;
std::vector<Scalar>
initialWellRateFractions(const Simulator& ebosSimulator,
const WellState<Scalar>& well_state) const;
// check whether the well is operable under BHP limit with current reservoir condition
virtual void checkOperabilityUnderBHPLimit(const WellState<Scalar>& well_state,
@ -453,15 +445,16 @@ protected:
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger);
std::optional<double> estimateOperableBhp(const Simulator& simulator,
const double dt,
WellState<Scalar>& well_state,
const SummaryState& summary_state,
DeferredLogger& deferred_logger);
std::optional<Scalar>
estimateOperableBhp(const Simulator& ebos_simulator,
const double dt,
WellState<Scalar>& well_state,
const SummaryState& summary_state,
DeferredLogger& deferred_logger);
bool solveWellWithBhp(const Simulator& simulator,
const double dt,
const double bhp,
const Scalar bhp,
WellState<Scalar>& well_state,
DeferredLogger& deferred_logger);
@ -486,21 +479,20 @@ protected:
[[maybe_unused]] DeferredLogger& deferred_logger) const;
void computeConnLevelProdInd(const FluidState& fs,
const std::function<double(const double)>& connPICalc,
const std::function<Scalar(const Scalar)>& connPICalc,
const std::vector<Scalar>& mobility,
double* connPI) const;
Scalar* connPI) const;
void computeConnLevelInjInd(const FluidState& fs,
const Phase preferred_phase,
const std::function<double(const double)>& connIICalc,
const std::function<Scalar(const Scalar)>& connIICalc,
const std::vector<Scalar>& mobility,
double* connII,
Scalar* connII,
DeferredLogger& deferred_logger) const;
double computeConnectionDFactor(const int perf,
Scalar computeConnectionDFactor(const int perf,
const IntensiveQuantities& intQuants,
const SingleWellState<double>& ws) const;
const SingleWellState<Scalar>& ws) const;
};
} // namespace Opm

View File

@ -89,10 +89,10 @@ namespace Opm
void
WellInterface<TypeTag>::
init(const PhaseUsage* phase_usage_arg,
const std::vector<double>& /* depth_arg */,
const double gravity_arg,
const std::vector<Scalar>& /* depth_arg */,
const Scalar gravity_arg,
const int /* num_cells */,
const std::vector< Scalar >& B_avg,
const std::vector<Scalar>& B_avg,
const bool changed_to_open_this_step)
{
this->phase_usage_ = phase_usage_arg;
@ -105,7 +105,7 @@ namespace Opm
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
wpolymer() const
{
@ -121,7 +121,7 @@ namespace Opm
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
wfoam() const
{
@ -135,7 +135,7 @@ namespace Opm
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
wsalt() const
{
@ -147,7 +147,7 @@ namespace Opm
}
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
wmicrobes() const
{
@ -159,7 +159,7 @@ namespace Opm
}
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
woxygen() const
{
@ -177,7 +177,7 @@ namespace Opm
// vtkblackoilmicpmodule.hh and ecloutputblackoilmodel.hh respectively).
template<typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
wurea() const
{
@ -273,7 +273,7 @@ namespace Opm
const GroupState<Scalar>& group_state,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
const double wqTotal,
const Scalar wqTotal,
DeferredLogger& deferred_logger,
const bool fixed_control,
const bool fixed_status)
@ -285,7 +285,7 @@ namespace Opm
return false;
}
const double sgn = this->isInjector() ? 1.0 : -1.0;
const Scalar sgn = this->isInjector() ? 1.0 : -1.0;
if (!this->wellIsStopped()){
if (wqTotal*sgn <= 0.0 && !fixed_status){
this->stopWell();
@ -295,7 +295,7 @@ namespace Opm
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);
prod_controls.hasControl(Well::ProducerCMode::GRUP);
changed = this->checkIndividualConstraints(ws, summary_state, deferred_logger, inj_controls, prod_controls);
if (hasGroupControl) {
@ -318,22 +318,32 @@ namespace Opm
}
} else if (!fixed_status){
// well is stopped, check if current bhp allows reopening
const double bhp = well_state.well(this->index_of_well_).bhp;
double prod_limit = prod_controls.bhp_limit;
double inj_limit = inj_controls.bhp_limit;
const Scalar bhp = well_state.well(this->index_of_well_).bhp;
Scalar prod_limit = prod_controls.bhp_limit;
Scalar inj_limit = inj_controls.bhp_limit;
const bool has_thp = this->wellHasTHPConstraints(summary_state);
if (has_thp){
std::vector<double> rates(this->num_components_);
std::vector<Scalar> rates(this->num_components_);
if (this->isInjector()){
const double bhp_thp = WellBhpThpCalculator(*this).calculateBhpFromThp(well_state, rates, this->well_ecl_, summary_state, this->getRefDensity(), deferred_logger);
inj_limit = std::min(bhp_thp, inj_controls.bhp_limit);
const Scalar bhp_thp = WellBhpThpCalculator(*this).
calculateBhpFromThp(well_state, rates,
this->well_ecl_,
summary_state,
this->getRefDensity(),
deferred_logger);
inj_limit = std::min(bhp_thp, static_cast<Scalar>(inj_controls.bhp_limit));
} else {
// if the well can operate, it must at least be able to produce at the lowest bhp of the bhp-curve (explicit fractions)
const double bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity());
prod_limit = std::max(bhp_min, prod_controls.bhp_limit);
// if the well can operate, it must at least be able to produce
// at the lowest bhp of the bhp-curve (explicit fractions)
const Scalar bhp_min = WellBhpThpCalculator(*this).
calculateMinimumBhpFromThp(well_state,
this->well_ecl_,
summary_state,
this->getRefDensity());
prod_limit = std::max(bhp_min, static_cast<Scalar>(prod_controls.bhp_limit));
}
}
const double bhp_diff = (this->isInjector())? inj_limit - bhp: bhp - prod_limit;
const Scalar bhp_diff = (this->isInjector())? inj_limit - bhp: bhp - prod_limit;
if (bhp_diff > 0){
this->openWell();
well_state.well(this->index_of_well_).bhp = (this->isInjector())? inj_limit : prod_limit;
@ -401,19 +411,25 @@ namespace Opm
deferred_logger.debug(msg);
return;
}
std::vector<double> potentials;
std::vector<Scalar> potentials;
try {
computeWellPotentials(simulator, well_state_copy, potentials, deferred_logger);
} catch (const std::exception& e) {
const std::string msg = std::string("well ") + this->name() + std::string(": computeWellPotentials() failed during testing for re-opening: ") + e.what();
const std::string msg = fmt::format("well {}: computeWellPotentials() "
"failed during testing for re-opening: ",
this->name(), e.what());
deferred_logger.info(msg);
return;
}
const int np = well_state_copy.numPhases();
for (int p = 0; p < np; ++p) {
ws.well_potentials[p] = std::max(0.0, potentials[p]);
ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
}
this->updateWellTestState(well_state_copy.well(this->indexOfWell()), simulation_time, /*writeMessageToOPMLog=*/ false, welltest_state_temp, deferred_logger);
this->updateWellTestState(well_state_copy.well(this->indexOfWell()),
simulation_time,
/*writeMessageToOPMLog=*/ false,
welltest_state_temp,
deferred_logger);
this->closeCompletions(welltest_state_temp);
// Stop testing if the well is closed or shut due to all completions shut
@ -510,7 +526,8 @@ namespace Opm
} else {
// solve well with the estimated target bhp (or limit)
ws.thp = this->getTHPConstraint(summary_state);
const double bhp = std::max(bhp_target.value(), prod_controls.bhp_limit);
const Scalar bhp = std::max(bhp_target.value(),
static_cast<Scalar>(prod_controls.bhp_limit));
solveWellWithBhp(simulator, dt, bhp, well_state, deferred_logger);
}
}
@ -531,8 +548,8 @@ namespace Opm
this->operability_status_.use_vfpexplicit = true;
auto bhp_stable = WellBhpThpCalculator(*this).estimateStableBhp(well_state, this->well_ecl_, rates, this->getRefDensity(), summary_state);
// if we find an intersection with a sufficiently lower bhp, re-solve equations
const double reltol = 1e-3;
const double cur_bhp = ws.bhp;
const Scalar reltol = 1e-3;
const Scalar cur_bhp = ws.bhp;
if (bhp_stable.has_value() && cur_bhp - bhp_stable.value() > cur_bhp*reltol){
const auto msg = fmt::format("Well {} converged to an unstable solution, re-solving", this->name());
deferred_logger.debug(msg);
@ -557,10 +574,16 @@ namespace Opm
this->stopWell();
} else {
// solve well with the estimated target bhp (or limit)
const double bhp = std::max(bhp_target.value(), prod_controls.bhp_limit);
const Scalar bhp = std::max(bhp_target.value(),
static_cast<Scalar>(prod_controls.bhp_limit));
solveWellWithBhp(simulator, dt, bhp, well_state, deferred_logger);
ws.thp = this->getTHPConstraint(summary_state);
converged = this->iterateWellEqWithSwitching(simulator, dt, inj_controls, prod_controls, well_state, group_state, deferred_logger);
converged = this->iterateWellEqWithSwitching(simulator, dt,
inj_controls,
prod_controls,
well_state,
group_state,
deferred_logger);
}
}
// update operability
@ -571,7 +594,7 @@ namespace Opm
}
template<typename TypeTag>
std::optional<double>
std::optional<typename WellInterface<TypeTag>::Scalar>
WellInterface<TypeTag>::
estimateOperableBhp(const Simulator& simulator,
const double dt,
@ -581,7 +604,7 @@ namespace Opm
{
// Given an unconverged well or closed well, estimate an operable bhp (if any)
// Get minimal bhp from vfp-curve
double bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity());
Scalar bhp_min = WellBhpThpCalculator(*this).calculateMinimumBhpFromThp(well_state, this->well_ecl_, summary_state, this->getRefDensity());
// Solve
const bool converged = solveWellWithBhp(simulator, dt, bhp_min, well_state, deferred_logger);
if (!converged || this->wellIsStopped()) {
@ -598,7 +621,7 @@ namespace Opm
WellInterface<TypeTag>::
solveWellWithBhp(const Simulator& simulator,
const double dt,
const double bhp,
const Scalar bhp,
WellState<Scalar>& well_state,
DeferredLogger& deferred_logger)
{
@ -750,9 +773,7 @@ namespace Opm
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger)
{
prepareWellBeforeAssembling(simulator, dt, well_state, group_state, deferred_logger);
assembleWellEqWithoutIteration(simulator, dt, well_state, group_state, deferred_logger);
}
@ -805,18 +826,20 @@ namespace Opm
}
if (this->operability_status_.has_negative_potentials) {
auto well_state_copy = well_state;
std::vector<double> potentials;
std::vector<Scalar> potentials;
try {
computeWellPotentials(simulator, well_state_copy, potentials, deferred_logger);
} catch (const std::exception& e) {
const std::string msg = std::string("well ") + this->name() + std::string(": computeWellPotentials() failed during attempt to recompute potentials for well : ") + e.what();
const std::string msg = fmt::format("well {}: computeWellPotentials() failed "
"during attempt to recompute potentials for well: ",
this->name(), e.what());
deferred_logger.info(msg);
this->operability_status_.has_negative_potentials = true;
}
auto& ws = well_state.well(this->indexOfWell());
const int np = well_state.numPhases();
for (int p = 0; p < np; ++p) {
ws.well_potentials[p] = std::max(0.0, potentials[p]);
ws.well_potentials[p] = std::max(Scalar{0.0}, potentials[p]);
}
}
this->changed_to_open_this_step_ = false;
@ -852,7 +875,8 @@ namespace Opm
template<typename TypeTag>
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::volumetricSurfaceRateForConnection(int cellIdx, int phaseIdx) const {
WellInterface<TypeTag>::volumetricSurfaceRateForConnection(int cellIdx, int phaseIdx) const
{
for (int perfIdx = 0; perfIdx < this->number_of_perforations_; ++perfIdx) {
if (this->cells()[perfIdx] == cellIdx) {
const unsigned activeCompIdx = Indices::canonicalToActiveComponentIndex(FluidSystem::solventComponentIndex(phaseIdx));
@ -896,12 +920,11 @@ namespace Opm
template<typename TypeTag>
bool
WellInterface<TypeTag>::
gliftBeginTimeStepWellTestIterateWellEquations(
const Simulator& simulator,
const double dt,
WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger)
gliftBeginTimeStepWellTestIterateWellEquations(const Simulator& simulator,
const double dt,
WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger)
{
const auto& well_name = this->name();
assert(this->wellHasTHPConstraints(simulator.vanguard().summaryState()));
@ -954,7 +977,7 @@ namespace Opm
}
const auto& gl_well = glo.well(well_name);
auto& max_alq_optional = gl_well.max_rate();
double max_alq;
Scalar max_alq;
if (max_alq_optional) {
max_alq = *max_alq_optional;
}
@ -1080,7 +1103,7 @@ namespace Opm
const auto current = ws.injection_cmode;
switch(current) {
switch (current) {
case Well::InjectorCMode::RATE:
{
ws.surface_rates[phasePos] = (1.0 - this->rsRvInj()) * controls.surface_rate;
@ -1098,9 +1121,9 @@ namespace Opm
case Well::InjectorCMode::RESV:
{
std::vector<double> convert_coeff(this->number_of_phases_, 1.0);
std::vector<Scalar> convert_coeff(this->number_of_phases_, 1.0);
this->rateConverter_.calcCoeff(/*fipreg*/ 0, this->pvtRegionIdx_, convert_coeff);
const double coeff = convert_coeff[phasePos];
const Scalar coeff = convert_coeff[phasePos];
ws.surface_rates[phasePos] = controls.reservoir_rate/coeff;
break;
}
@ -1108,7 +1131,7 @@ namespace Opm
case Well::InjectorCMode::THP:
{
auto rates = ws.surface_rates;
double bhp = WellBhpThpCalculator(*this).calculateBhpFromThp(well_state,
Scalar bhp = WellBhpThpCalculator(*this).calculateBhpFromThp(well_state,
rates,
well,
summaryState,
@ -1120,7 +1143,7 @@ namespace Opm
// if the total rates are negative or zero
// we try to provide a better intial well rate
// using the well potentials
double total_rate = std::accumulate(rates.begin(), rates.end(), 0.0);
Scalar total_rate = std::accumulate(rates.begin(), rates.end(), 0.0);
if (total_rate <= 0.0)
ws.surface_rates = ws.well_potentials;
@ -1129,7 +1152,7 @@ namespace Opm
case Well::InjectorCMode::BHP:
{
ws.bhp = controls.bhp_limit;
double total_rate = 0.0;
Scalar total_rate = 0.0;
for (int p = 0; p<np; ++p) {
total_rate += ws.surface_rates[p];
}
@ -1145,8 +1168,8 @@ namespace Opm
{
assert(well.isAvailableForGroupControl());
const auto& group = schedule.getGroup(well.groupName(), this->currentStep());
const double efficiencyFactor = well.getEfficiencyFactor();
std::optional<double> target =
const Scalar efficiencyFactor = well.getEfficiencyFactor();
std::optional<Scalar> target =
this->getGroupInjectionTargetRate(group,
well_state,
group_state,
@ -1171,7 +1194,7 @@ namespace Opm
// the zero rate target, then we can use to retain the composition information
// within the wellbore from the previous result, and hopefully it is a good
// initial guess for the zero rate target.
ws.surface_rates[phasePos] = std::max(1.e-7, ws.surface_rates[phasePos]);
ws.surface_rates[phasePos] = std::max(Scalar{1.e-7}, ws.surface_rates[phasePos]);
if (ws.bhp == 0.) {
ws.bhp = controls.bhp_limit;
@ -1185,7 +1208,7 @@ namespace Opm
switch (current) {
case Well::ProducerCMode::ORAT:
{
double current_rate = -ws.surface_rates[ pu.phase_pos[Oil] ];
Scalar current_rate = -ws.surface_rates[ pu.phase_pos[Oil] ];
// for trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if (current_rate > 0.0) {
@ -1193,7 +1216,7 @@ namespace Opm
ws.surface_rates[p] *= controls.oil_rate/current_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
double control_fraction = fractions[pu.phase_pos[Oil]];
if (control_fraction != 0.0) {
for (int p = 0; p<np; ++p) {
@ -1205,7 +1228,7 @@ namespace Opm
}
case Well::ProducerCMode::WRAT:
{
double current_rate = -ws.surface_rates[ pu.phase_pos[Water] ];
Scalar current_rate = -ws.surface_rates[ pu.phase_pos[Water] ];
// for trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if (current_rate > 0.0) {
@ -1213,11 +1236,11 @@ namespace Opm
ws.surface_rates[p] *= controls.water_rate/current_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
double control_fraction = fractions[pu.phase_pos[Water]];
const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
const Scalar control_fraction = fractions[pu.phase_pos[Water]];
if (control_fraction != 0.0) {
for (int p = 0; p<np; ++p) {
ws.surface_rates[p] = - fractions[p] * controls.water_rate/control_fraction;
ws.surface_rates[p] = - fractions[p] * controls.water_rate / control_fraction;
}
}
}
@ -1225,7 +1248,7 @@ namespace Opm
}
case Well::ProducerCMode::GRAT:
{
double current_rate = -ws.surface_rates[pu.phase_pos[Gas] ];
Scalar current_rate = -ws.surface_rates[pu.phase_pos[Gas] ];
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if (current_rate > 0.0) {
@ -1233,11 +1256,11 @@ namespace Opm
ws.surface_rates[p] *= controls.gas_rate/current_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
double control_fraction = fractions[pu.phase_pos[Gas]];
const std::vector<Scalar > fractions = initialWellRateFractions(simulator, well_state);
const Scalar control_fraction = fractions[pu.phase_pos[Gas]];
if (control_fraction != 0.0) {
for (int p = 0; p<np; ++p) {
ws.surface_rates[p] = - fractions[p] * controls.gas_rate/control_fraction;
ws.surface_rates[p] = - fractions[p] * controls.gas_rate / control_fraction;
}
}
}
@ -1247,8 +1270,8 @@ namespace Opm
}
case Well::ProducerCMode::LRAT:
{
double current_rate = -ws.surface_rates[ pu.phase_pos[Water] ]
- ws.surface_rates[ pu.phase_pos[Oil] ];
Scalar current_rate = - ws.surface_rates[ pu.phase_pos[Water] ]
- ws.surface_rates[ pu.phase_pos[Oil] ];
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if (current_rate > 0.0) {
@ -1256,8 +1279,8 @@ namespace Opm
ws.surface_rates[p] *= controls.liquid_rate/current_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
double control_fraction = fractions[pu.phase_pos[Water]] + fractions[pu.phase_pos[Oil]];
const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
const Scalar control_fraction = fractions[pu.phase_pos[Water]] + fractions[pu.phase_pos[Oil]];
if (control_fraction != 0.0) {
for (int p = 0; p<np; ++p) {
ws.surface_rates[p] = - fractions[p] * controls.liquid_rate / control_fraction;
@ -1274,9 +1297,9 @@ namespace Opm
}
case Well::ProducerCMode::RESV:
{
std::vector<double> convert_coeff(this->number_of_phases_, 1.0);
std::vector<Scalar> convert_coeff(this->number_of_phases_, 1.0);
this->rateConverter_.calcCoeff(/*fipreg*/ 0, this->pvtRegionIdx_, ws.surface_rates, convert_coeff);
double total_res_rate = 0.0;
Scalar total_res_rate = 0.0;
for (int p = 0; p<np; ++p) {
total_res_rate -= ws.surface_rates[p] * convert_coeff[p];
}
@ -1288,13 +1311,13 @@ namespace Opm
ws.surface_rates[p] *= controls.resv_rate/total_res_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
for (int p = 0; p<np; ++p) {
ws.surface_rates[p] = - fractions[p] * controls.resv_rate / convert_coeff[p];
}
}
} else {
std::vector<double> hrates(this->number_of_phases_,0.);
std::vector<Scalar> hrates(this->number_of_phases_,0.);
if (FluidSystem::phaseIsActive(FluidSystem::waterPhaseIdx)) {
hrates[pu.phase_pos[Water]] = controls.water_rate;
}
@ -1304,9 +1327,9 @@ namespace Opm
if (FluidSystem::phaseIsActive(FluidSystem::gasPhaseIdx)) {
hrates[pu.phase_pos[Gas]] = controls.gas_rate;
}
std::vector<double> hrates_resv(this->number_of_phases_,0.);
std::vector<Scalar> hrates_resv(this->number_of_phases_,0.);
this->rateConverter_.calcReservoirVoidageRates(/*fipreg*/ 0, this->pvtRegionIdx_, hrates, hrates_resv);
double target = std::accumulate(hrates_resv.begin(), hrates_resv.end(), 0.0);
Scalar target = std::accumulate(hrates_resv.begin(), hrates_resv.end(), 0.0);
// or trivial rates or opposite direction we don't just scale the rates
// but use either the potentials or the mobility ratio to initial the well rates
if (total_res_rate > 0.0) {
@ -1314,19 +1337,18 @@ namespace Opm
ws.surface_rates[p] *= target/total_res_rate;
}
} else {
const std::vector<double> fractions = initialWellRateFractions(simulator, well_state);
const std::vector<Scalar> fractions = initialWellRateFractions(simulator, well_state);
for (int p = 0; p<np; ++p) {
ws.surface_rates[p] = - fractions[p] * target / convert_coeff[p];
}
}
}
break;
}
case Well::ProducerCMode::BHP:
{
ws.bhp = controls.bhp_limit;
double total_rate = 0.0;
Scalar total_rate = 0.0;
for (int p = 0; p<np; ++p) {
total_rate -= ws.surface_rates[p];
}
@ -1350,14 +1372,14 @@ namespace Opm
// more sophisticated design might be needed in the future
auto rates = ws.surface_rates;
this->adaptRatesForVFP(rates);
const double bhp = WellBhpThpCalculator(*this).calculateBhpFromThp(
const Scalar bhp = WellBhpThpCalculator(*this).calculateBhpFromThp(
well_state, rates, well, summaryState, this->getRefDensity(), deferred_logger);
ws.bhp = bhp;
ws.thp = this->getTHPConstraint(summaryState);
// if the total rates are negative or zero
// we try to provide a better initial well rate
// using the well potentials
const double total_rate = -std::accumulate(rates.begin(), rates.end(), 0.0);
const Scalar total_rate = -std::accumulate(rates.begin(), rates.end(), 0.0);
if (total_rate <= 0.0) {
for (int p = 0; p < this->number_of_phases_; ++p) {
ws.surface_rates[p] = -ws.well_potentials[p];
@ -1370,14 +1392,14 @@ namespace Opm
{
assert(well.isAvailableForGroupControl());
const auto& group = schedule.getGroup(well.groupName(), this->currentStep());
const double efficiencyFactor = well.getEfficiencyFactor();
double scale = this->getGroupProductionTargetRate(group,
well_state,
group_state,
schedule,
summaryState,
efficiencyFactor,
deferred_logger);
const Scalar efficiencyFactor = well.getEfficiencyFactor();
Scalar scale = this->getGroupProductionTargetRate(group,
well_state,
group_state,
schedule,
summaryState,
efficiencyFactor,
deferred_logger);
// we don't want to scale with zero and get zero rates.
if (scale > 0) {
@ -1394,9 +1416,8 @@ namespace Opm
case Well::ProducerCMode::NONE:
{
OPM_DEFLOG_THROW(std::runtime_error, "Well control must be specified for well " + this->name() , deferred_logger);
}
break;
}
} // end of switch
if (ws.bhp == 0.) {
@ -1406,16 +1427,16 @@ namespace Opm
}
template<typename TypeTag>
std::vector<double>
std::vector<typename WellInterface<TypeTag>::Scalar>
WellInterface<TypeTag>::
initialWellRateFractions(const Simulator& simulator,
const WellState<Scalar>& well_state) const
{
const int np = this->number_of_phases_;
std::vector<double> scaling_factor(np);
std::vector<Scalar> scaling_factor(np);
const auto& ws = well_state.well(this->index_of_well_);
double total_potentials = 0.0;
Scalar total_potentials = 0.0;
for (int p = 0; p<np; ++p) {
total_potentials += ws.well_potentials[p];
}
@ -1427,7 +1448,7 @@ namespace Opm
}
// if we don't have any potentials we weight it using the mobilites
// We only need approximation so we don't bother with the vapporized oil and dissolved gas
double total_tw = 0;
Scalar total_tw = 0;
const int nperf = this->number_of_perforations_;
for (int perf = 0; perf < nperf; ++perf) {
total_tw += this->well_index_[perf];
@ -1436,8 +1457,8 @@ namespace Opm
const int cell_idx = this->well_cells_[perf];
const auto& intQuants = simulator.model().intensiveQuantities(cell_idx, /*timeIdx=*/0);
const auto& fs = intQuants.fluidState();
const double well_tw_fraction = this->well_index_[perf] / total_tw;
double total_mobility = 0.0;
const Scalar well_tw_fraction = this->well_index_[perf] / total_tw;
Scalar total_mobility = 0.0;
for (int p = 0; p < np; ++p) {
int modelPhaseIdx = this->flowPhaseToModelPhaseIdx(p);
total_mobility += fs.invB(modelPhaseIdx).value() * intQuants.mobility(modelPhaseIdx).value();
@ -1463,7 +1484,7 @@ namespace Opm
// if more than one nonzero rate.
auto& ws = well_state.well(this->index_of_well_);
int nonzero_rate_index = -1;
const double floating_point_error_epsilon = 1e-14;
const Scalar floating_point_error_epsilon = 1e-14;
for (int p = 0; p < this->number_of_phases_; ++p) {
if (std::abs(ws.surface_rates[p]) > floating_point_error_epsilon) {
if (nonzero_rate_index == -1) {
@ -1476,7 +1497,7 @@ namespace Opm
}
// Calculate the rates that follow from the current primary variables.
std::vector<double> well_q_s = computeCurrentWellRates(simulator, deferred_logger);
std::vector<Scalar> well_q_s = computeCurrentWellRates(simulator, deferred_logger);
if (nonzero_rate_index == -1) {
// No nonzero rates.
@ -1488,13 +1509,13 @@ namespace Opm
}
// Set the currently-zero phase flows to be nonzero in proportion to well_q_s.
const double initial_nonzero_rate = ws.surface_rates[nonzero_rate_index];
const Scalar initial_nonzero_rate = ws.surface_rates[nonzero_rate_index];
const int comp_idx_nz = this->flowPhaseToModelCompIdx(nonzero_rate_index);
if (std::abs(well_q_s[comp_idx_nz]) > floating_point_error_epsilon) {
for (int p = 0; p < this->number_of_phases_; ++p) {
if (p != nonzero_rate_index) {
const int comp_idx = this->flowPhaseToModelCompIdx(p);
double& rate = ws.surface_rates[p];
Scalar& rate = ws.surface_rates[p];
rate = (initial_nonzero_rate / well_q_s[comp_idx_nz]) * (well_q_s[comp_idx]);
}
}
@ -1502,12 +1523,12 @@ namespace Opm
}
template <typename TypeTag>
std::vector<double>
std::vector<typename WellInterface<TypeTag>::Scalar>
WellInterface<TypeTag>::
wellIndex(const int perf,
const IntensiveQuantities& intQuants,
const double trans_mult,
const SingleWellState<double>& ws) const
const Scalar trans_mult,
const SingleWellState<Scalar>& ws) const
{
// Add a Forchheimer term to the gas phase CTF if the run uses
// either of the WDFAC or the WDFACCOR keywords.
@ -1525,7 +1546,7 @@ namespace Opm
return wi;
}
const double d = this->computeConnectionDFactor(perf, intQuants, ws);
const Scalar d = this->computeConnectionDFactor(perf, intQuants, ws);
if (d < 1.0e-15) {
return wi;
}
@ -1533,43 +1554,43 @@ namespace Opm
// Solve quadratic equations for connection rates satisfying the ipr and the flow-dependent skin.
// If more than one solution, pick the one corresponding to lowest absolute rate (smallest skin).
const auto& connection = this->well_ecl_.getConnections()[ws.perf_data.ecl_index[perf]];
const double Kh = connection.Kh();
const double scaling = 3.141592653589 * Kh * connection.wpimult();
const Scalar Kh = connection.Kh();
const Scalar scaling = 3.141592653589 * Kh * connection.wpimult();
const unsigned gas_comp_idx = Indices::canonicalToActiveComponentIndex(FluidSystem::gasCompIdx);
const double connection_pressure = ws.perf_data.pressure[perf];
const double cell_pressure = getValue(intQuants.fluidState().pressure(FluidSystem::gasPhaseIdx));
const double drawdown = cell_pressure - connection_pressure;
const double invB = getValue(intQuants.fluidState().invB(FluidSystem::gasPhaseIdx));
const double mob_g = getValue(intQuants.mobility(FluidSystem::gasPhaseIdx)) * invB;
const double a = d;
const double b = 2*scaling/wi[gas_comp_idx];
const double c = -2*scaling*mob_g*drawdown;
const Scalar connection_pressure = ws.perf_data.pressure[perf];
const Scalar cell_pressure = getValue(intQuants.fluidState().pressure(FluidSystem::gasPhaseIdx));
const Scalar drawdown = cell_pressure - connection_pressure;
const Scalar invB = getValue(intQuants.fluidState().invB(FluidSystem::gasPhaseIdx));
const Scalar mob_g = getValue(intQuants.mobility(FluidSystem::gasPhaseIdx)) * invB;
const Scalar a = d;
const Scalar b = 2*scaling/wi[gas_comp_idx];
const Scalar c = -2*scaling*mob_g*drawdown;
double consistent_Q = -1.0e20;
Scalar consistent_Q = -1.0e20;
// Find and check negative solutions (a --> -a)
const double r2n = b*b + 4*a*c;
const Scalar r2n = b*b + 4*a*c;
if (r2n >= 0) {
const double rn = std::sqrt(r2n);
const double xn1 = (b-rn)*0.5/a;
const Scalar rn = std::sqrt(r2n);
const Scalar xn1 = (b-rn)*0.5/a;
if (xn1 <= 0) {
consistent_Q = xn1;
}
const double xn2 = (b+rn)*0.5/a;
const Scalar xn2 = (b+rn)*0.5/a;
if (xn2 <= 0 && xn2 > consistent_Q) {
consistent_Q = xn2;
}
}
// Find and check positive solutions
consistent_Q *= -1;
const double r2p = b*b - 4*a*c;
const Scalar r2p = b*b - 4*a*c;
if (r2p >= 0) {
const double rp = std::sqrt(r2p);
const double xp1 = (rp-b)*0.5/a;
const Scalar rp = std::sqrt(r2p);
const Scalar xp1 = (rp-b)*0.5/a;
if (xp1 > 0 && xp1 < consistent_Q) {
consistent_Q = xp1;
}
const double xp2 = -(rp+b)*0.5/a;
const Scalar xp2 = -(rp+b)*0.5/a;
if (xp2 > 0 && xp2 < consistent_Q) {
consistent_Q = xp2;
}
@ -1583,7 +1604,7 @@ namespace Opm
void
WellInterface<TypeTag>::
updateConnectionDFactor(const Simulator& simulator,
SingleWellState<double>& ws) const
SingleWellState<Scalar>& ws) const
{
if (! this->well_ecl_.getWDFAC().useDFactor()) {
return;
@ -1600,11 +1621,11 @@ namespace Opm
}
template <typename TypeTag>
double
typename WellInterface<TypeTag>::Scalar
WellInterface<TypeTag>::
computeConnectionDFactor(const int perf,
const IntensiveQuantities& intQuants,
const SingleWellState<double>& ws) const
const SingleWellState<Scalar>& ws) const
{
auto rhoGS = [regIdx = this->pvtRegionIdx()]() {
return FluidSystem::referenceDensity(FluidSystem::gasPhaseIdx, regIdx);
@ -1622,7 +1643,7 @@ namespace Opm
// Note that rv here is from grid block with typically
// p_block > connection_pressure
// so we may very well have rv > rv_sat
const double rv_sat = gasPvt.saturatedOilVaporizationFactor
const Scalar rv_sat = gasPvt.saturatedOilVaporizationFactor
(regIdx, temperature, connection_pressure);
if (! (rv < rv_sat)) {
@ -1645,7 +1666,7 @@ namespace Opm
void
WellInterface<TypeTag>::
updateConnectionTransmissibilityFactor(const Simulator& simulator,
SingleWellState<double>& ws) const
SingleWellState<Scalar>& ws) const
{
auto connCF = [&connIx = std::as_const(ws.perf_data.ecl_index),
&conns = this->well_ecl_.getConnections()]
@ -1772,7 +1793,7 @@ namespace Opm
auto bhp_at_thp_limit = computeBhpAtThpLimitProdWithAlq(
simulator, summary_state, this->getALQ(well_state), deferred_logger);
if (bhp_at_thp_limit) {
std::vector<double> rates(this->number_of_phases_, 0.0);
std::vector<Scalar> rates(this->number_of_phases_, 0.0);
if (thp_update_iterations) {
computeWellRatesWithBhpIterations(simulator, *bhp_at_thp_limit,
rates, deferred_logger);
@ -1794,9 +1815,9 @@ namespace Opm
void
WellInterface<TypeTag>::
computeConnLevelProdInd(const FluidState& fs,
const std::function<double(const double)>& connPICalc,
const std::function<Scalar(const Scalar)>& connPICalc,
const std::vector<Scalar>& mobility,
double* connPI) const
Scalar* connPI) const
{
const auto& pu = this->phaseUsage();
const int np = this->number_of_phases_;
@ -1830,9 +1851,9 @@ namespace Opm
WellInterface<TypeTag>::
computeConnLevelInjInd(const FluidState& fs,
const Phase preferred_phase,
const std::function<double(const double)>& connIICalc,
const std::function<Scalar(const Scalar)>& connIICalc,
const std::vector<Scalar>& mobility,
double* connII,
Scalar* connII,
DeferredLogger& deferred_logger) const
{
// Assumes single phase injection