|
|
|
|
@@ -1181,7 +1181,6 @@ namespace Opm {
|
|
|
|
|
if (this->terminal_output_ && (network_update_iteration == iteration_to_relax) ) {
|
|
|
|
|
local_deferredLogger.info(" we begin using relaxed tolerance for network update now after " + std::to_string(iteration_to_relax) + " iterations ");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const bool relax_network_balance = network_update_iteration >= iteration_to_relax;
|
|
|
|
|
std::tie(do_network_update, well_group_control_changed) =
|
|
|
|
|
updateWellControlsAndNetworkIteration(mandatory_network_balance, relax_network_balance, dt,local_deferredLogger);
|
|
|
|
|
@@ -1274,18 +1273,19 @@ namespace Opm {
|
|
|
|
|
if (has_choke) {
|
|
|
|
|
const auto& summary_state = this->ebosSimulator_.vanguard().summaryState();
|
|
|
|
|
const Group& group = schedule().getGroup(nodeName, reportStepIdx);
|
|
|
|
|
std::optional<Group::ProductionControls> ctrl = group.productionControls(summary_state);
|
|
|
|
|
const auto cmode = ctrl->cmode;
|
|
|
|
|
const auto ctrl = group.productionControls(summary_state);
|
|
|
|
|
const auto cmode = ctrl.cmode;
|
|
|
|
|
const auto pu = this->phase_usage_;
|
|
|
|
|
//PJPE: conversion factor res<-> surface rates TODO: to be calculated
|
|
|
|
|
//TODO: Auto choke combined with RESV control is not supported
|
|
|
|
|
std::vector<double> resv_coeff(pu.num_phases, 1.0);
|
|
|
|
|
// rateConverter(0, well_.pvtRegionIdx(), group.name(), resv_coeff); // FIPNUM region 0 here, should use FIPNUM from WELSPECS.
|
|
|
|
|
double gratTargetFromSales = 0.0; //PJPE: check this
|
|
|
|
|
double gratTargetFromSales = 0.0;
|
|
|
|
|
if (group_state.has_grat_sales_target(group.name()))
|
|
|
|
|
gratTargetFromSales = group_state.grat_sales_target(group.name());
|
|
|
|
|
|
|
|
|
|
WellGroupHelpers::TargetCalculator tcalc(cmode, pu, resv_coeff,
|
|
|
|
|
gratTargetFromSales, nodeName, group_state,
|
|
|
|
|
group.has_gpmaint_control(cmode));
|
|
|
|
|
double orig_target = tcalc.groupTarget(ctrl, local_deferredLogger);
|
|
|
|
|
//PJPE: TODO: include efficiency factor
|
|
|
|
|
const double orig_target = tcalc.groupTarget(ctrl, local_deferredLogger);
|
|
|
|
|
|
|
|
|
|
auto mismatch = [&] (auto well_group_thp) {
|
|
|
|
|
double well_group_rate(0.0);
|
|
|
|
|
@@ -1294,32 +1294,18 @@ namespace Opm {
|
|
|
|
|
std::string well_name = well->name();
|
|
|
|
|
auto& ws = well_state.well(well_name);
|
|
|
|
|
if (group.hasWell(well_name)) {
|
|
|
|
|
rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
|
|
|
|
|
std::cout << "(1) well: " << well_name << ", rate: " << rate << ", thp: " << ws.thp << ", Cmode: " << ws.production_cmode << std::endl;
|
|
|
|
|
well->setDynamicThpLimit(well_group_thp);
|
|
|
|
|
|
|
|
|
|
const Well& well_ecl = wells_ecl_[well->indexOfWell()];
|
|
|
|
|
const auto inj_controls = Well::InjectionControls(0);
|
|
|
|
|
const auto prod_controls = well_ecl.productionControls(summary_state);
|
|
|
|
|
well->iterateWellEqWithSwitching(this->ebosSimulator_, dt, inj_controls, prod_controls, well_state, group_state, local_deferredLogger, false, false);
|
|
|
|
|
// May be used instead if the wells of the sub-sea manifold has only THP constraints
|
|
|
|
|
// well->updateWellStateWithTHPTargetProd(this->ebosSimulator_, well_state, local_deferredLogger);
|
|
|
|
|
|
|
|
|
|
rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
|
|
|
|
|
if (ws.production_cmode == Opm::WellProducerCMode::THP)
|
|
|
|
|
ws.thp = well_group_thp;
|
|
|
|
|
|
|
|
|
|
std::cout << "(2) well: " << well_name << ", rate: " << rate << ", thp: " << ws.thp << ", Cmode: " << ws.production_cmode << std::endl;
|
|
|
|
|
well_group_rate += rate;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
double diff = well_group_rate - orig_target; // (well_group_rate - orig_target)/orig_target;
|
|
|
|
|
std::cout << "well_group_thp: " << well_group_thp << " mismatch: " << diff << "\n" << std::endl;
|
|
|
|
|
return diff;
|
|
|
|
|
return well_group_rate - orig_target;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
double well_group_rate(0.0);
|
|
|
|
|
double rate(0.0);
|
|
|
|
|
double thp(0.0);
|
|
|
|
|
double min_thp(1.0E8);
|
|
|
|
|
double max_thp(0.0);
|
|
|
|
|
@@ -1327,8 +1313,6 @@ namespace Opm {
|
|
|
|
|
std::string well_name = well->name();
|
|
|
|
|
if (group.hasWell(well_name)) {
|
|
|
|
|
auto& ws = well_state.well(well_name);
|
|
|
|
|
rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
|
|
|
|
|
well_group_rate += rate;
|
|
|
|
|
thp = ws.thp;
|
|
|
|
|
min_thp = std::min(min_thp, thp);
|
|
|
|
|
max_thp = std::max(max_thp, thp);
|
|
|
|
|
@@ -1340,83 +1324,46 @@ namespace Opm {
|
|
|
|
|
const double nodal_pressure = it->second;
|
|
|
|
|
double well_group_thp = nodal_pressure;
|
|
|
|
|
|
|
|
|
|
if ((reportStepIdx <= 1) || (well_group_rate > 0.9*orig_target)) {
|
|
|
|
|
std::array<double, 2> range; // = {1.0E5, 150.0E5}; //PJPE what lower/upper bound to be taken here?
|
|
|
|
|
if (well_group_thp_prev_ == 0) {
|
|
|
|
|
range = {0.9*min_thp, 1.1*max_thp};
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
range = {0.9* this->well_group_thp_prev_, 1.1*this->well_group_thp_prev_};
|
|
|
|
|
}
|
|
|
|
|
if (!this->well_group_thp_calc_.has_value() || this->well_group_thp_calc_ > nodal_pressure) {
|
|
|
|
|
std::array<double, 2> range;
|
|
|
|
|
this->well_group_thp_calc_.has_value() ?
|
|
|
|
|
range = {0.9 * this->well_group_thp_calc_.value(), 1.1 * this->well_group_thp_calc_.value()} : range = {0.9 * min_thp, 1.1 * max_thp};
|
|
|
|
|
|
|
|
|
|
double low, high;
|
|
|
|
|
// For testing
|
|
|
|
|
// low = 3.42767e+06;// 3.32833e+06;
|
|
|
|
|
// // high = 3.42767e+06;// 3.32833e+06;
|
|
|
|
|
// // std::cout << "low: " << low << " high: " << high << std::endl;
|
|
|
|
|
// double low_value_test = mismatch(low);
|
|
|
|
|
// low_value_test = mismatch(low);
|
|
|
|
|
// low_value_test = mismatch(low);
|
|
|
|
|
// std::cout << "low_value: " << low_value_test << " high_value: " << high_value_test << std::endl;
|
|
|
|
|
|
|
|
|
|
std::optional<double> approximate_solution;
|
|
|
|
|
const double limit = 1.0E-3;// 1.0E-4;
|
|
|
|
|
local_deferredLogger.debug(" Using brute force search to bracket the common THP ");
|
|
|
|
|
std::cout << " Using brute force search to bracket the common THP " << std::endl;
|
|
|
|
|
const bool finding_bracket = WellBhpThpCalculator::bruteForceBracketCommonTHP(mismatch, range, low, high, approximate_solution, limit, local_deferredLogger);
|
|
|
|
|
|
|
|
|
|
double low_value = mismatch(low);
|
|
|
|
|
double high_value = mismatch(high);
|
|
|
|
|
const double tolerance1 = 1.0E-3;
|
|
|
|
|
local_deferredLogger.debug("Using brute force search to bracket the common THP");
|
|
|
|
|
const bool finding_bracket = WellBhpThpCalculator::bruteForceBracketCommonTHP(mismatch, range, low, high, approximate_solution, tolerance1, local_deferredLogger);
|
|
|
|
|
|
|
|
|
|
if (approximate_solution.has_value()) {
|
|
|
|
|
well_group_thp = *approximate_solution;
|
|
|
|
|
double check = mismatch(well_group_thp);
|
|
|
|
|
local_deferredLogger.debug("Approximate value found: mismatch = " + std::to_string(check) + ", well_group_thp = " + std::to_string(well_group_thp));
|
|
|
|
|
this->well_group_thp_calc_ = *approximate_solution;
|
|
|
|
|
local_deferredLogger.debug("Approximate common THP value found: " + std::to_string(this->well_group_thp_calc_.value()));
|
|
|
|
|
} else if (finding_bracket) {
|
|
|
|
|
// PJPE: TODO what settings to take here?
|
|
|
|
|
std::cout << "Found bracket: [" << low << ", " << high << "]; mismatch: [" << low_value << ", "<< high_value << "]" << std::endl;
|
|
|
|
|
const double tolerance = 1.0E-3;
|
|
|
|
|
const double tolerance2 = 1.0E-3;
|
|
|
|
|
const int max_iteration_solve = 100;
|
|
|
|
|
int iteration = 0;
|
|
|
|
|
well_group_thp = RegulaFalsiBisection<ThrowOnError>::
|
|
|
|
|
solve(mismatch, low, high, max_iteration_solve, tolerance, iteration);
|
|
|
|
|
local_deferredLogger.debug( " bracket = [" + std::to_string(low) + ", " + std::to_string(high) + "], " +
|
|
|
|
|
"mismatch = [" + std::to_string(low_value) + ", " + std::to_string(high_value) + "], " +
|
|
|
|
|
"iteration = " + std::to_string(iteration));
|
|
|
|
|
double check = mismatch(well_group_thp);
|
|
|
|
|
local_deferredLogger.debug(" mismatch = " + std::to_string(check) + ", well_group_thp = " + std::to_string(well_group_thp));
|
|
|
|
|
this->well_group_thp_calc_= RegulaFalsiBisection<ThrowOnError>::
|
|
|
|
|
solve(mismatch, low, high, max_iteration_solve, tolerance2, iteration);
|
|
|
|
|
local_deferredLogger.debug(" bracket = [" + std::to_string(low) + ", " + std::to_string(high) + "], " +
|
|
|
|
|
"iteration = " + std::to_string(iteration));
|
|
|
|
|
local_deferredLogger.debug("Common THP value = " + std::to_string(this->well_group_thp_calc_.value()));
|
|
|
|
|
} else {
|
|
|
|
|
local_deferredLogger.warning("Common THP solve failed due to bracketing failure");
|
|
|
|
|
well_group_thp = nodal_pressure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::cout << "well_group_thp = " << well_group_thp << ", nodal_pressure = " << nodal_pressure << std::endl;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
well_group_thp = nodal_pressure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
well_group_thp = std::max(well_group_thp, nodal_pressure);
|
|
|
|
|
well_group_thp = std::max(this->well_group_thp_calc_.value(), nodal_pressure);
|
|
|
|
|
|
|
|
|
|
for (auto& well : this->well_container_) {
|
|
|
|
|
std::string well_name = well->name();
|
|
|
|
|
if (group.hasWell(well_name)) {
|
|
|
|
|
auto& ws = well_state.well(well_name);
|
|
|
|
|
std::cout << "Cmode of Well: " << well_name << " is " << ws.production_cmode <<std::endl;
|
|
|
|
|
if (ws.production_cmode == Opm::WellProducerCMode::THP){
|
|
|
|
|
if (ws.production_cmode == Opm::WellProducerCMode::THP) {
|
|
|
|
|
well->setDynamicThpLimit(well_group_thp);
|
|
|
|
|
ws.thp = well_group_thp;
|
|
|
|
|
}
|
|
|
|
|
// const Well& well_ecl = wells_ecl_[well->indexOfWell()];
|
|
|
|
|
// const auto inj_controls = Well::InjectionControls(0);
|
|
|
|
|
// const auto prod_controls = well_ecl.productionControls(summary_state);
|
|
|
|
|
// // well->updateWellStateWithTHPTargetProd(this->ebosSimulator_, well_state, local_deferredLogger);
|
|
|
|
|
// well->iterateWellEqWithSwitching(this->ebosSimulator_, dt, inj_controls, prod_controls, well_state_copy, group_state, local_deferredLogger);
|
|
|
|
|
if (ws.production_cmode == Opm::WellProducerCMode::THP)
|
|
|
|
|
ws.thp = well_group_thp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
well_group_thp_prev_ = well_group_thp;
|
|
|
|
|
|
|
|
|
|
// Use the common THP in computeNetworkPressures
|
|
|
|
|
group_state.update_well_group_thp(nodeName, well_group_thp);
|
|
|
|
|
@@ -2555,10 +2502,10 @@ namespace Opm {
|
|
|
|
|
const std::string msg = "Compute initial well solution for " + well->name() + " initially failed. Continue with the previous rates";
|
|
|
|
|
deferred_logger.warning("WELL_INITIAL_SOLVE_FAILED", msg);
|
|
|
|
|
}
|
|
|
|
|
// If we're using local well solves that include control switches, they also update
|
|
|
|
|
// operability, so reset before main iterations begin
|
|
|
|
|
well->resetWellOperability();
|
|
|
|
|
}
|
|
|
|
|
// If we're using local well solves that include control switches, they also update
|
|
|
|
|
// operability, so reset before main iterations begin
|
|
|
|
|
well->resetWellOperability();
|
|
|
|
|
}
|
|
|
|
|
updatePrimaryVariables(deferred_logger);
|
|
|
|
|
|
|
|
|
|
|