changed assessing safe THP range

This commit is contained in:
Paul 2024-03-24 20:47:46 +01:00
parent 393c70a83e
commit 6e76602e8f
6 changed files with 106 additions and 95 deletions

View File

@ -466,7 +466,7 @@ template<class Scalar> class WellContributions;
const double dt, const double dt,
DeferredLogger& local_deferredLogger); DeferredLogger& local_deferredLogger);
void computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger); void computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger);
/// Update rank's notion of intersecting wells and their /// Update rank's notion of intersecting wells and their
/// associate solution variables. /// associate solution variables.

View File

@ -49,12 +49,9 @@
#include <opm/simulators/utils/MPIPacker.hpp> #include <opm/simulators/utils/MPIPacker.hpp>
#include <opm/simulators/utils/phaseUsageFromDeck.hpp> #include <opm/simulators/utils/phaseUsageFromDeck.hpp>
<<<<<<< HEAD
#if COMPILE_BDA_BRIDGE #if COMPILE_BDA_BRIDGE
#include <opm/simulators/linalg/bda/WellContributions.hpp> #include <opm/simulators/linalg/bda/WellContributions.hpp>
#endif #endif
=======
>>>>>>> f92db77d3 (moved common thp calculation to updateWellControls)
#if HAVE_MPI #if HAVE_MPI
#include <opm/simulators/utils/MPISerializer.hpp> #include <opm/simulators/utils/MPISerializer.hpp>
@ -1258,8 +1255,10 @@ namespace Opm {
BlackoilWellModel<TypeTag>:: BlackoilWellModel<TypeTag>::
computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger) computeWellGroupThp(const double dt, DeferredLogger& local_deferredLogger)
{ {
const int reportStepIdx = this->ebosSimulator_.episodeIndex(); const int reportStepIdx = this->simulator_.episodeIndex();
const auto& network = schedule()[reportStepIdx].network(); const auto& network = this->schedule()[reportStepIdx].network();
const auto& balance = this->schedule()[reportStepIdx].network_balance();
const Scalar thp_tolerance = balance.thp_tolerance();
if (!network.active()) { if (!network.active()) {
return; return;
@ -1271,75 +1270,87 @@ namespace Opm {
for (const std::string& nodeName : network.node_names()) { for (const std::string& nodeName : network.node_names()) {
const bool has_choke = network.node(nodeName).as_choke(); const bool has_choke = network.node(nodeName).as_choke();
if (has_choke) { if (has_choke) {
const auto& summary_state = this->ebosSimulator_.vanguard().summaryState(); const auto& summary_state = this->simulator_.vanguard().summaryState();
const Group& group = schedule().getGroup(nodeName, reportStepIdx); const Group& group = this->schedule().getGroup(nodeName, reportStepIdx);
const auto ctrl = group.productionControls(summary_state); const auto ctrl = group.productionControls(summary_state);
const auto cmode = ctrl.cmode; const auto cmode = ctrl.cmode;
const auto pu = this->phase_usage_; const auto pu = this->phase_usage_;
//TODO: Auto choke combined with RESV control is not supported //TODO: Auto choke combined with RESV control is not supported
std::vector<double> resv_coeff(pu.num_phases, 1.0); std::vector<Scalar> resv_coeff(pu.num_phases, 1.0);
double gratTargetFromSales = 0.0; Scalar gratTargetFromSales = 0.0;
if (group_state.has_grat_sales_target(group.name())) if (group_state.has_grat_sales_target(group.name()))
gratTargetFromSales = group_state.grat_sales_target(group.name()); gratTargetFromSales = group_state.grat_sales_target(group.name());
WellGroupHelpers::TargetCalculator tcalc(cmode, pu, resv_coeff, WGHelpers::TargetCalculator tcalc(cmode, pu, resv_coeff,
gratTargetFromSales, nodeName, group_state, gratTargetFromSales, nodeName, group_state,
group.has_gpmaint_control(cmode)); group.has_gpmaint_control(cmode));
const double orig_target = tcalc.groupTarget(ctrl, local_deferredLogger); const Scalar orig_target = tcalc.groupTarget(ctrl, local_deferredLogger);
auto mismatch = [&] (auto well_group_thp) { auto mismatch = [&] (auto well_group_thp) {
double well_group_rate(0.0); Scalar well_group_rate(0.0);
double rate(0.0); Scalar rate(0.0);
for (auto& well : this->well_container_) { for (auto& well : this->well_container_) {
std::string well_name = well->name(); std::string well_name = well->name();
auto& ws = well_state.well(well_name); auto& ws = well_state.well(well_name);
if (group.hasWell(well_name)) { if (group.hasWell(well_name)) {
well->setDynamicThpLimit(well_group_thp); well->setDynamicThpLimit(well_group_thp);
const Well& well_ecl = wells_ecl_[well->indexOfWell()]; const Well& well_ecl = this->wells_ecl_[well->indexOfWell()];
const auto inj_controls = Well::InjectionControls(0); const auto inj_controls = Well::InjectionControls(0);
const auto prod_controls = well_ecl.productionControls(summary_state); 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); well->iterateWellEqWithSwitching(this->simulator_, dt, inj_controls, prod_controls, well_state, group_state, local_deferredLogger, false, false);
rate = -tcalc.calcModeRateFromRates(ws.surface_rates); rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
well_group_rate += rate; well_group_rate += rate;
} }
} }
return well_group_rate - orig_target; return (well_group_rate - orig_target)/orig_target;
}; };
double thp(0.0); Scalar min_thp, max_thp;
double min_thp(1.0E8); std::array<Scalar, 2> range_initial;
double max_thp(0.0); //Find an initial bracket
for (auto& well : this->well_container_) { if (!this->well_group_thp_calc_.has_value()){
std::string well_name = well->name(); // Retrieve the terminal pressure of the associated root of the manifold group
if (group.hasWell(well_name)) { std::string node_name = nodeName;
auto& ws = well_state.well(well_name); while (!network.node(node_name).terminal_pressure().has_value()) {
thp = ws.thp; auto branch = network.uptree_branch(node_name).value();
min_thp = std::min(min_thp, thp); node_name = branch.uptree_node();
max_thp = std::max(max_thp, thp);
} }
min_thp = network.node(node_name).terminal_pressure().value();
WellBhpThpCalculator<Scalar>::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
// Narrow down the bracket
Scalar low1, high1;
std::array<Scalar, 2> range = {0.9*min_thp, 1.1*max_thp};
std::optional<Scalar> appr_sol;
WellBhpThpCalculator<Scalar>::bruteForceBracketCommonTHP(mismatch, range, low1, high1, appr_sol, 0.0, local_deferredLogger);
min_thp = low1;
max_thp = high1;
range_initial = {min_thp, max_thp};
} }
const auto upbranch = network.uptree_branch(nodeName); const auto upbranch = network.uptree_branch(nodeName);
const auto it = node_pressures_.find((*upbranch).uptree_node()); const auto it = this->node_pressures_.find((*upbranch).uptree_node());
const double nodal_pressure = it->second; const Scalar nodal_pressure = it->second;
double well_group_thp = nodal_pressure; Scalar well_group_thp = nodal_pressure;
if (!this->well_group_thp_calc_.has_value() || this->well_group_thp_calc_ > nodal_pressure) { if (!this->well_group_thp_calc_.has_value() || this->well_group_thp_calc_ > nodal_pressure) {
std::array<double, 2> range; // The bracket is based on the initial bracket or on a range based on a previous calculated common group thp
std::array<Scalar, 2> range;
this->well_group_thp_calc_.has_value() ? 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}; range = {0.9 * this->well_group_thp_calc_.value(), 1.1 * this->well_group_thp_calc_.value()} :
range = range_initial;
double low, high; Scalar low, high;
std::optional<double> approximate_solution; std::optional<Scalar> approximate_solution;
const double tolerance1 = 1.0E-3; const Scalar tolerance1 = thp_tolerance;
local_deferredLogger.debug("Using brute force search to bracket the common THP"); 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); const bool finding_bracket = WellBhpThpCalculator<Scalar>::bruteForceBracketCommonTHP(mismatch, range, low, high, approximate_solution, tolerance1, local_deferredLogger);
if (approximate_solution.has_value()) { if (approximate_solution.has_value()) {
this->well_group_thp_calc_ = *approximate_solution; this->well_group_thp_calc_ = *approximate_solution;
local_deferredLogger.debug("Approximate common THP value found: " + std::to_string(this->well_group_thp_calc_.value())); local_deferredLogger.debug("Approximate common THP value found: " + std::to_string(this->well_group_thp_calc_.value()));
} else if (finding_bracket) { } else if (finding_bracket) {
const double tolerance2 = 1.0E-3; const Scalar tolerance2 = thp_tolerance;
const int max_iteration_solve = 100; const int max_iteration_solve = 100;
int iteration = 0; int iteration = 0;
this->well_group_thp_calc_= RegulaFalsiBisection<ThrowOnError>:: this->well_group_thp_calc_= RegulaFalsiBisection<ThrowOnError>::
@ -1348,11 +1359,12 @@ namespace Opm {
"iteration = " + std::to_string(iteration)); "iteration = " + std::to_string(iteration));
local_deferredLogger.debug("Common THP value = " + std::to_string(this->well_group_thp_calc_.value())); local_deferredLogger.debug("Common THP value = " + std::to_string(this->well_group_thp_calc_.value()));
} else { } else {
local_deferredLogger.warning("Common THP solve failed due to bracketing failure"); this->well_group_thp_calc_ = {};
local_deferredLogger.debug("Common THP solve failed due to bracketing failure");
} }
} }
this->well_group_thp_calc_.has_value() ?
well_group_thp = std::max(this->well_group_thp_calc_.value(), nodal_pressure); well_group_thp = std::max(this->well_group_thp_calc_.value(), nodal_pressure) : well_group_thp = nodal_pressure;
for (auto& well : this->well_container_) { for (auto& well : this->well_container_) {
std::string well_name = well->name(); std::string well_name = well->name();
@ -1365,7 +1377,7 @@ namespace Opm {
} }
} }
// Use the common THP in computeNetworkPressures // Use the common group THP in computeNetworkPressures
group_state.update_well_group_thp(nodeName, well_group_thp); group_state.update_well_group_thp(nodeName, well_group_thp);
} }
} }
@ -2031,7 +2043,7 @@ namespace Opm {
// network related // network related
bool more_network_update = false; bool more_network_update = false;
if (this->shouldBalanceNetwork(episodeIdx, iterationIdx) || mandatory_network_balance) { if (this->shouldBalanceNetwork(episodeIdx, iterationIdx) || mandatory_network_balance) {
const double dt = this->ebosSimulator_.timeStepSize(); const double dt = this->simulator_.timeStepSize();
// Calculate common THP for subsea manifold well group (item 3 of NODEPROP set to YES) // Calculate common THP for subsea manifold well group (item 3 of NODEPROP set to YES)
computeWellGroupThp(dt, deferred_logger); computeWellGroupThp(dt, deferred_logger);
const auto local_network_imbalance = this->updateNetworkPressures(episodeIdx); const auto local_network_imbalance = this->updateNetworkPressures(episodeIdx);

View File

@ -101,8 +101,9 @@ GroupState<Scalar>::production_rates(const std::string& gname) const
} }
//------------------------------------------------------------------------- //-------------------------------------------------------------------------
template<class Scalar> template<class Scalar>
bool GroupState<Scalar>:: void GroupState<Scalar>::
GroupState::update_well_group_thp(const std::string& gname, const double& thp) GroupState::update_well_group_thp(const std::string& gname, const double& thp)
{ {
this->group_thp[gname] = thp; this->group_thp[gname] = thp;
@ -119,6 +120,8 @@ GroupState::well_group_thp(const std::string& gname) const
return group_iter->second; return group_iter->second;
} }
//-------------------------------------------------------------------------
template<class Scalar> template<class Scalar>
bool GroupState<Scalar>:: bool GroupState<Scalar>::
has_production_reduction_rates(const std::string& gname) const has_production_reduction_rates(const std::string& gname) const

View File

@ -875,15 +875,9 @@ bruteForceBracket(const std::function<Scalar(const Scalar)>& eq,
low = range[0]; low = range[0];
high = range[1]; high = range[1];
const int sample_number = 200; const int sample_number = 200;
<<<<<<< HEAD
const Scalar interval = (high - low) / sample_number; const Scalar interval = (high - low) / sample_number;
Scalar eq_low = eq(low); Scalar eq_low = eq(low);
Scalar eq_high = 0.0; Scalar eq_high = 0.0;
=======
const double interval = (high - low) / sample_number;
double eq_low = eq(low);
double eq_high = 0.0;
>>>>>>> 3ce8d7eec (moved common thp calculation to updateWellControls)
for (int i = 0; i < sample_number + 1; ++i) { for (int i = 0; i < sample_number + 1; ++i) {
high = range[0] + interval * i; high = range[0] + interval * i;
eq_high = eq(high); eq_high = eq(high);
@ -1011,22 +1005,23 @@ getFloIPR(const WellState<Scalar>& well_state,
detail::getFlo(table, aqua_b, liquid_b, vapour_b)); detail::getFlo(table, aqua_b, liquid_b, vapour_b));
} }
template<class Scalar>
bool bool
WellBhpThpCalculator:: WellBhpThpCalculator<Scalar>::
bruteForceBracketCommonTHP(const std::function<double(const double)>& eq, bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
const std::array<double, 2>& range, const std::array<Scalar, 2>& range,
double& low, double& high, Scalar& low, Scalar& high,
std::optional<double>& approximate_solution, std::optional<Scalar>& approximate_solution,
const double& limit, const Scalar& limit,
DeferredLogger& deferred_logger) DeferredLogger& deferred_logger)
{ {
bool bracket_found = false; bool bracket_found = false;
low = range[0]; low = range[0];
high = range[1]; high = range[1];
const int sample_number = 300; const int sample_number = 300;
const double interval = (high - low) / sample_number; const Scalar interval = (high - low) / sample_number;
double eq_low = eq(low); Scalar eq_low = eq(low);
double eq_high = 0.0; Scalar eq_high = 0.0;
for (int i = 0; i < sample_number + 1; ++i) { for (int i = 0; i < sample_number + 1; ++i) {
high = range[0] + interval * i; high = range[0] + interval * i;
eq_high = eq(high); eq_high = eq(high);
@ -1050,6 +1045,30 @@ bruteForceBracketCommonTHP(const std::function<double(const double)>& eq,
return bracket_found; return bracket_found;
} }
template<class Scalar>
bool
WellBhpThpCalculator<Scalar>::
bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
Scalar& min_thp, Scalar& max_thp)
{
bool bracket_found = false;
const int sample_number = 1000;
const Scalar interval = 1E5;
Scalar eq_low = eq(min_thp);
Scalar eq_high = 0.0;
for (int i = 0; i < sample_number + 1; ++i) {
max_thp = min_thp + interval * i;
eq_high = eq(max_thp);
if (eq_high * eq_low <= 0.) {
bracket_found = true;
min_thp = max_thp - interval;
break;
}
eq_low = eq_high;
}
return bracket_found;
}
template class WellBhpThpCalculator<double>; template class WellBhpThpCalculator<double>;
#define INSTANCE(...) \ #define INSTANCE(...) \

View File

@ -120,19 +120,23 @@ public:
const SummaryState& summary_state) const; const SummaryState& summary_state) const;
//! \brief Find limits using brute-force solver. //! \brief Find limits using brute-force solver.
static bool bruteForceBracket(const std::function<double(const double)>& eq, static bool bruteForceBracket(const std::function<Scalar(const Scalar)>& eq,
const std::array<double, 2>& range, const std::array<Scalar, 2>& range,
double& low, double& high, Scalar& low, Scalar& high,
DeferredLogger& deferred_logger); DeferredLogger& deferred_logger);
//! \brief Find limits using brute-force solver. //! \brief Find limits using brute-force solver.
static bool bruteForceBracketCommonTHP(const std::function<double(const double)>& eq, static bool bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
const std::array<double, 2>& range, const std::array<Scalar, 2>& range,
double& low, double& high, Scalar& low, Scalar& high,
std::optional<double>& approximate_solution, std::optional<Scalar>& approximate_solution,
const double& limit, const Scalar& limit,
DeferredLogger& deferred_logger); DeferredLogger& deferred_logger);
//! \brief Find limits using brute-force solver.
static bool bruteForceBracketCommonTHP(const std::function<Scalar(const Scalar)>& eq,
Scalar& min_thp, Scalar& max_thp);
private: private:
//! \brief Compute BHP from THP limit for an injector - implementation. //! \brief Compute BHP from THP limit for an injector - implementation.
template<class ErrorPolicy> template<class ErrorPolicy>
@ -169,12 +173,6 @@ private:
std::optional<Scalar>& approximate_solution, std::optional<Scalar>& approximate_solution,
DeferredLogger& deferred_logger) const; DeferredLogger& deferred_logger) const;
//! \brief Find limits using brute-force solver.
static bool bruteForceBracket(const std::function<Scalar(const Scalar)>& eq,
const std::array<Scalar, 2>& range,
Scalar& low, Scalar& high,
DeferredLogger& deferred_logger);
Scalar findThpFromBhpIteratively(const std::function<Scalar(const Scalar, const Scalar)>& thp_func, Scalar findThpFromBhpIteratively(const std::function<Scalar(const Scalar, const Scalar)>& thp_func,
const Scalar bhp, const Scalar bhp,
const Scalar thp_limit, const Scalar thp_limit,

View File

@ -374,26 +374,15 @@ public:
void updateConnectionTransmissibilityFactor(const Simulator& simulator, void updateConnectionTransmissibilityFactor(const Simulator& simulator,
SingleWellState<Scalar>& ws) const; SingleWellState<Scalar>& ws) const;
SingleWellState<double>& ws) const;
virtual bool iterateWellEqWithSwitching(const Simulator& simulator, virtual bool iterateWellEqWithSwitching(const Simulator& simulator,
const double dt, const double dt,
const WellInjectionControls& inj_controls, const WellInjectionControls& inj_controls,
const WellProductionControls& prod_controls, const WellProductionControls& prod_controls,
WellState& well_state, WellState<Scalar>& well_state,
const GroupState& group_state, const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger, DeferredLogger& deferred_logger,
const bool fixed_control = false, const bool fixed_control = false,
const bool fixed_status = false) = 0; const bool fixed_status = false) = 0;
bool solveWellWithTHPConstraint(const Simulator& simulator,
const double dt,
const Well::InjectionControls& inj_controls,
const Well::ProductionControls& prod_controls,
WellState& well_state,
const GroupState& group_state,
DeferredLogger& deferred_logger);
protected: protected:
// simulation parameters // simulation parameters
const ModelParameters& param_; const ModelParameters& param_;
@ -448,16 +437,6 @@ protected:
const GroupState<Scalar>& group_state, const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger) = 0; DeferredLogger& deferred_logger) = 0;
virtual bool iterateWellEqWithSwitching(const Simulator& simulator,
const double dt,
const WellInjectionControls& inj_controls,
const WellProductionControls& prod_controls,
WellState<Scalar>& well_state,
const GroupState<Scalar>& group_state,
DeferredLogger& deferred_logger,
const bool fixed_control = false,
const bool fixed_status = false) = 0;
virtual void updateIPRImplicit(const Simulator& simulator, virtual void updateIPRImplicit(const Simulator& simulator,
WellState<Scalar>& well_state, WellState<Scalar>& well_state,
DeferredLogger& deferred_logger) = 0; DeferredLogger& deferred_logger) = 0;