From 2e43b440e0f6848b357571e960cc5a4744dfde9a Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Nov 2018 12:55:30 +0100 Subject: [PATCH 01/15] adding OperabilityStatus to WellInterface to check the operability status of a well. --- opm/autodiff/WellInterface.hpp | 63 ++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index c084e9ab0..a47aab56a 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -350,6 +350,10 @@ namespace Opm // whether a well is specified with a non-zero and valid VFP table number bool isVFPActive() const; + struct OperabilityStatus; + + OperabilityStatus operability_status_; + void wellTestingEconomic(Simulator& simulator, const std::vector& B_avg, const double simulation_time, const int report_step, const bool terminal_output, const WellState& well_state, WellTestState& welltest_state); @@ -366,6 +370,65 @@ namespace Opm }; + + + + // definition of the struct OperabilityStatus + template + struct + WellInterface:: + OperabilityStatus { + bool isOperable() const { + if (!operable_under_only_bhp_limit) { + return false; + } else { + return !negative_well_rates && (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()); + } + } + + bool isOperableUnderBHPLimit() const { + return operable_under_only_bhp_limit && !violate_thp_limit_under_bhp_limit; + } + + bool isOperableUnderTHPLimit() const { + return obtain_solution_with_thp_limit && !violate_bhp_limit_with_thp_limit; + } + + void reset() { + operable_under_only_bhp_limit = true; + violate_thp_limit_under_bhp_limit = false; + obtain_solution_with_thp_limit = true; + violate_bhp_limit_with_thp_limit = false; + // TODO: the following one might need to be different + negative_well_rates = false; + } + + // TODO: re-design the boolean variables so that they have meaning in the same directions. + // For example, true are all for positive situation, and false are all for negative circumstances. + + // whether the well can be operated under bhp limit + // without considering other limits. + // if it is false, then the well is not operable for sure. + bool operable_under_only_bhp_limit = true; + // if the well can be operated under bhp limit, will it violate + // the thp limit when operated under bhp limit + bool violate_thp_limit_under_bhp_limit = false; + // whether the well operate under the thp limit only + bool obtain_solution_with_thp_limit = true; + // whether the well violate bhp limit when operated under thp limit + bool violate_bhp_limit_with_thp_limit = false; + + // we get negatvie well rates + // currently, we are trying to address the one result from updateWellStateWithTHPTargetIPR + bool negative_well_rates = false; + + // could not get converged, maybe at the end of the time step, after chopping for some steps. + // TODO: the best way is that this well can not get converged during local iterations. + bool could_not_get_converged = false; + }; + +} + } #include "WellInterface_impl.hpp" From b27dc3ec2e4b2b6ec30af19ac1e73cfe5117380c Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Nov 2018 14:11:35 +0100 Subject: [PATCH 02/15] stopping use function crossflowAllowed() it turns on the crossflow when all the drawdown in the wrong direction, then the well get rates in the wrong direction. flow is not ready to handle this type of situation, then the only result will be chopped time step. --- opm/autodiff/StandardWell.hpp | 2 -- opm/autodiff/StandardWell_impl.hpp | 45 ++---------------------------- opm/autodiff/WellInterface.hpp | 2 -- 3 files changed, 3 insertions(+), 46 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index a2d2b27ff..3333e1e73 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -264,8 +264,6 @@ namespace Opm EvalWell extendEval(const Eval& in) const; - bool crossFlowAllowed(const Simulator& ebosSimulator) const; - // xw = inv(D)*(rw - C*x) void recoverSolutionWell(const BVector& x, BVectorWell& xw) const; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 5da6bf514..f3e109dd6 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -453,7 +453,7 @@ namespace Opm // TODO: it probably can be static member for StandardWell const double volume = 0.002831684659200; // 0.1 cu ft; - const bool allow_cf = crossFlowAllowed(ebosSimulator); + const bool allow_cf = getAllowCrossFlow(); const EvalWell& bhp = getBhp(); @@ -747,45 +747,6 @@ namespace Opm - template - bool - StandardWell:: - crossFlowAllowed(const Simulator& ebosSimulator) const - { - if (getAllowCrossFlow()) { - return true; - } - - // TODO: investigate the justification of the following situation - - // check for special case where all perforations have cross flow - // then the wells must allow for cross flow - for (int perf = 0; perf < number_of_perforations_; ++perf) { - const int cell_idx = well_cells_[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); - const auto& fs = intQuants.fluidState(); - const EvalWell pressure = extendEval(fs.pressure(FluidSystem::oilPhaseIdx)); - const EvalWell& bhp = getBhp(); - - // Pressure drawdown (also used to determine direction of flow) - const EvalWell well_pressure = bhp + perf_pressure_diffs_[perf]; - const EvalWell drawdown = pressure - well_pressure; - - if (drawdown.value() < 0 && well_type_ == INJECTOR) { - return false; - } - - if (drawdown.value() > 0 && well_type_ == PRODUCER) { - return false; - } - } - return true; - } - - - - - template void StandardWell:: @@ -1942,7 +1903,7 @@ namespace Opm const int np = number_of_phases_; well_flux.resize(np, 0.0); - const bool allow_cf = crossFlowAllowed(ebosSimulator); + const bool allow_cf = getAllowCrossFlow(); for (int perf = 0; perf < number_of_perforations_; ++perf) { const int cell_idx = well_cells_[perf]; @@ -2337,7 +2298,7 @@ namespace Opm return; } // compute the well water velocity with out shear effects. - const bool allow_cf = crossFlowAllowed(ebos_simulator); + const bool allow_cf = getAllowCrossFlow(); const EvalWell& bhp = getBhp(); std::vector cq_s(num_components_,0.0); double perf_dis_gas_rate = 0.; diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index a47aab56a..9bc2aa37e 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -429,8 +429,6 @@ namespace Opm } -} - #include "WellInterface_impl.hpp" #endif // OPM_WELLINTERFACE_HEADER_INCLUDED From dbe047a8a5bcab844813d26b4c9ccc9a8842670d Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Nov 2018 14:30:32 +0100 Subject: [PATCH 03/15] adding function operabilityCheckingUnderTHP to VFPProdProperties To test the operability under THP limit. --- opm/autodiff/VFPProdProperties.cpp | 31 ++++++++++++++++++++++++++++++ opm/autodiff/VFPProdProperties.hpp | 10 ++++++++++ 2 files changed, 41 insertions(+) diff --git a/opm/autodiff/VFPProdProperties.cpp b/opm/autodiff/VFPProdProperties.cpp index f86c0be40..cc2ded314 100644 --- a/opm/autodiff/VFPProdProperties.cpp +++ b/opm/autodiff/VFPProdProperties.cpp @@ -253,4 +253,35 @@ calculateBhpWithTHPTarget(const std::vector& ipr_a, + +void VFPProdProperties:: +operabilityCheckingUnderTHP(const std::vector& ipr_a, + const std::vector& ipr_b, + const double bhp_limit, + const double thp_table_id, + const double thp_limit, + const double alq, + const double dp, + bool& obtain_solution_with_thp_limit, + bool& violate_bhp_limit_with_thp_limit) const +{ + const double obtain_bhp = calculateBhpWithTHPTarget(ipr_a, ipr_b, bhp_limit, thp_table_id, thp_limit, alq, dp); + + if (obtain_bhp > 0.) { + obtain_solution_with_thp_limit = true; + + violate_bhp_limit_with_thp_limit = (obtain_bhp < bhp_limit); + + if (obtain_bhp < thp_limit) { + std::cout << " obtain_bhp " << obtain_bhp / 1.e5 << " is SMALLER than thp limit " << thp_limit / 1.e5 << std::endl; + } + + } else { + obtain_solution_with_thp_limit = false; + std::cout << " COULD NOT find an Intersection point, the well might need to be closed " << std::endl; + violate_bhp_limit_with_thp_limit = false; + } +} + + } diff --git a/opm/autodiff/VFPProdProperties.hpp b/opm/autodiff/VFPProdProperties.hpp index cbf94fda4..e0b55dd69 100644 --- a/opm/autodiff/VFPProdProperties.hpp +++ b/opm/autodiff/VFPProdProperties.hpp @@ -184,6 +184,16 @@ public: const double alq, const double dp) const; + void operabilityCheckingUnderTHP(const std::vector& ipr_a, + const std::vector& ipr_b, + const double bhp_limit, + const double thp_table_id, + const double thp_limit, + const double alq, + const double dp, + bool& obtain_solution_with_thp_limit, + bool& voilate_bhp_limit_with_thp_limit) const; + protected: // calculate a group bhp values with a group of flo rate values std::vector bhpwithflo(const std::vector& flos, From 5204bbe4ca717f4a4530a40f3a612fbe7552c59e Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 15 Nov 2018 14:31:41 +0100 Subject: [PATCH 04/15] adding function checkWellOperatability() to StandardWell it includes BHPLimit check, THPlimit check, and also to see whether if the well can produce/inject in the correct direction. --- opm/autodiff/MultisegmentWell.hpp | 4 + opm/autodiff/MultisegmentWell_impl.hpp | 14 +++ opm/autodiff/StandardWell.hpp | 14 +++ opm/autodiff/StandardWell_impl.hpp | 168 ++++++++++++++++++++++++- opm/autodiff/WellInterface.hpp | 1 + 5 files changed, 200 insertions(+), 1 deletion(-) diff --git a/opm/autodiff/MultisegmentWell.hpp b/opm/autodiff/MultisegmentWell.hpp index 40496a55e..eb56ba6bf 100644 --- a/opm/autodiff/MultisegmentWell.hpp +++ b/opm/autodiff/MultisegmentWell.hpp @@ -330,6 +330,10 @@ namespace Opm // handling the overshooting and undershooting of the fractions void processFractions(const int seg) const; + // checking the operability of the well based on current reservoir condition + // it is not implemented for multisegment well yet + virtual void checkWellOperatability(const Simulator& ebos_simulator); + void updateWellStateFromPrimaryVariables(WellState& well_state) const; bool frictionalPressureLossConsidered() const; diff --git a/opm/autodiff/MultisegmentWell_impl.hpp b/opm/autodiff/MultisegmentWell_impl.hpp index 98271352c..8f4bc2977 100644 --- a/opm/autodiff/MultisegmentWell_impl.hpp +++ b/opm/autodiff/MultisegmentWell_impl.hpp @@ -1642,6 +1642,20 @@ namespace Opm + template + void + MultisegmentWell:: + checkWellOperatability(const Simulator& ebos_simulator) + { + const std::string msg = "Support of well operatability checking for mutlisegment wells is not yet," + " checkWellOperatability() for " + name() + " will do nothing"; + OpmLog::warning("NO_OPERATABILITY_CHECKING_MS_WELLS", msg); + } + + + + + template void MultisegmentWell:: diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 3333e1e73..28d4f2a1f 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -348,6 +348,16 @@ namespace Opm // updating the inflow based on the current reservoir condition void updateIPR(const Simulator& ebos_simulator) const; + // check whether the well is operable under the current reservoir condition + // mostly related to BHP limit and THP limit + virtual void checkWellOperatability(const Simulator& ebos_simulator) override; + + // check whether the well is operable under BHP limit with current reservoir condition + void checkOperabilityUnderBHPLimit(const Simulator& ebos_simulator); + + // check whether the well is operable under THP limit with current reservoir condition + void checkOperabilityUnderTHPLimit(const Simulator& ebos_simulator); + // update WellState based on IPR and associated VFP table void updateWellStateWithTHPTargetIPR(const Simulator& ebos_simulator, WellState& well_state) const; @@ -355,6 +365,10 @@ namespace Opm void updateWellStateWithTHPTargetIPRProducer(const Simulator& ebos_simulator, WellState& well_state) const; + // for a well, when all drawdown are in the wrong direction, then this well will not + // be able to produce/inject . + bool allDrawDownWrongDirection(const Simulator& ebosSimulator) const; + // calculate the BHP from THP target based on IPR // TODO: we need to check the operablility here first, if not operable, then maybe there is // no point to do this diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index f3e109dd6..0c7d9d4bb 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1228,7 +1228,8 @@ namespace Opm // it should not be negative anyway. If it is negative, we might need to re-formulate // to taking into consideration the crossflow here. if (pressure_diff <= 0.) { - OpmLog::warning("NON_POSITIVE_DRAWDOWN_IPR", "non-positive drawdown found when updateIPR for well " + name()); + OpmLog::warning("NON_POSITIVE_DRAWDOWN_IPR", + "non-positive drawdown found when updateIPR for well " + name()); } // the well index associated with the connection @@ -1277,6 +1278,171 @@ namespace Opm + template + void + StandardWell:: + checkWellOperatability(const Simulator& ebos_simulator) + { + // TODO: this function is probably can split another function out so that + // wellTestingPhysical can share some code with this function + // on solution is that this function will be called updateWellOperatability + // and the actual checking part become another function checkWellOperatability + // Let us finish the wellTestingPhysical first. + + // focusing on PRODUCER for now + if (well_type_ == INJECTOR) { + return; + } + + if (!this->underPredictionMode() ) { + return; + } + + const bool old_well_operable = this->operability_status_.isOperable(); + + this->operability_status_.reset(); + + updateIPR(ebos_simulator); + + // checking the BHP limit related + checkOperabilityUnderBHPLimit(ebos_simulator); + + // TODO: if the BHP limit does not work anyway, we do not need to do the following + // We do it now for studying purpose. + // checking whether the well can operate under the THP constraints. + if (this->wellHasTHPConstraints()) { + checkOperabilityUnderTHPLimit(ebos_simulator); + } + + // checking whether the well can not produce or something else + this->operability_status_.negative_well_rates = allDrawDownWrongDirection(ebos_simulator); + + const bool well_operable = this->operability_status_.isOperable(); + } + + + + + + template + void + StandardWell:: + checkOperabilityUnderBHPLimit(const Simulator& ebos_simulator) + { + const double bhp_limit = mostStrictBhpFromBhpLimits(); + // TODO: a better way to detect whether the BHP is defaulted or not + if ( bhp_limit > 1.5e5 || !this->wellHasTHPConstraints() ) { + // if ( !(bhp_limit < 1.5e5 && this->wellHasTHPConstraints()) ) { + // if there is a non-defaulted BHP limit or the well does not have a THP limit + + for (int p = 0; p < number_of_phases_; ++p) { + const double temp = ipr_a_[p] - ipr_b_[p] * bhp_limit; + if (temp < 0.) { + this->operability_status_.operable_under_only_bhp_limit = false; + break; + } + } + + // checking whether running under BHP limit will violate THP limit + if (this->operability_status_.operable_under_only_bhp_limit && this->wellHasTHPConstraints()) { + // option 1: calculate well rates based on the BHP limit. + // option 2: stick with the above IPR curve + // TODO: most likely, we can use IPR here + const double bhp_limit = mostStrictBhpFromBhpLimits(); + std::vector well_rates_bhp_limit; + computeWellRatesWithBhp(ebos_simulator, bhp_limit, well_rates_bhp_limit); + + const double thp = calculateThpFromBhp(well_rates_bhp_limit, bhp_limit); + const double thp_limit = this->getTHPConstraint(); + + if (thp < thp_limit || thp > bhp_limit) { + this->operability_status_.violate_thp_limit_under_bhp_limit = true; + } + } + } else { + // defaulted BHP and there is THP constraints + // default BHP limit is about 1.03 bar. + // when applied the hydrostatic pressure correction, + // most likely we get a negative bhp value to search in the VFP table, + // which is not desirable + this->operability_status_.operable_under_only_bhp_limit = true; + this->operability_status_.violate_thp_limit_under_bhp_limit = true; + } + } + + + + + + template + void + StandardWell:: + checkOperabilityUnderTHPLimit(const Simulator& ebos_simulator) + { + // We will use IPR to make the rates for now + const double bhp_limit = mostStrictBhpFromBhpLimits(); + const double thp_limit = this->getTHPConstraint(); + const double thp_control_index = this->getTHPControlIndex(); + const int thp_table_id = well_controls_iget_vfp(well_controls_, thp_control_index); + const double alq = well_controls_iget_alq(well_controls_, thp_control_index); + + double vfp_ref_depth = 0.; + + // not considering injectors for now + vfp_ref_depth = vfp_properties_->getProd()->getTable(thp_table_id)->getDatumDepth(); + + // the density of the top perforation + const double rho = perf_densities_[0]; + + const double dp = (vfp_ref_depth - ref_depth_) * rho * gravity_; + + vfp_properties_->getProd()->operabilityCheckingUnderTHP(ipr_a_, ipr_b_, bhp_limit, + thp_table_id, thp_limit, alq, dp, + this->operability_status_.obtain_solution_with_thp_limit, + this->operability_status_.violate_bhp_limit_with_thp_limit ); + + } + + + + + + template + bool + StandardWell:: + allDrawDownWrongDirection(const Simulator& ebosSimulator) const + { + bool all_drawdown_wrong_direction = true; + + for (int perf = 0; perf < number_of_perforations_; ++perf) { + const int cell_idx = well_cells_[perf]; + const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); + const auto& fs = intQuants.fluidState(); + + const double pressure = (fs.pressure(FluidSystem::oilPhaseIdx)).value(); + const double bhp = getBhp().value(); + + // Pressure drawdown (also used to determine direction of flow) + const double well_pressure = bhp + perf_pressure_diffs_[perf]; + const double drawdown = pressure - well_pressure; + + // for now, if there is one perforation can produce/inject in the correct + // direction, we consider this well can still produce/inject. + // TODO: it can be more complicated than this + if ( (drawdown < 0. && well_type_ == INJECTOR) || + (drawdown > 0. && well_type_ == PRODUCER) ) { + all_drawdown_wrong_direction = false; + break; + } + } + + return all_drawdown_wrong_direction; + } + + + + + template void StandardWell:: diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 9bc2aa37e..be8c28fd3 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -223,6 +223,7 @@ namespace Opm void updatePerforatedCell(std::vector& is_cell_perforated); + virtual void checkWellOperatability(const Simulator& ebos_simulator) = 0; protected: From ae3b514e0b06832b10e6ad9a4e44bb39ac937761 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Fri, 16 Nov 2018 09:00:11 +0100 Subject: [PATCH 05/15] not switching to THP/BHP limit when not operable under them --- opm/autodiff/WellInterface_impl.hpp | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index cdb564ef3..1685440df 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -457,8 +457,24 @@ namespace Opm if (wellhelpers::constraintBroken( well_state.bhp(), well_state.thp(), well_state.wellRates(), w, np, well_type_, wc, ctrl_index)) { - // ctrl_index will be the index of the broken constraint after the loop. - break; + + // if the well can not work under THP / BHP control, we should not switch to THP / BHP control + // TODO: does this mean the well is not operable anymore, while it is within the non-linear iteration? + if ( !( well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit() ) && + !( well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit() ) ) { + + // ctrl_index will be the index of the broken constraint after the loop. + break; + } else { + // before we figure out to handle it, we give some debug information here + if ( well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit() ) { + OpmLog::debug("well " + name() + " breaks the BHP limit, while it is not operable under BHP limit"); + } + + if ( well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit() ) { + OpmLog::debug("well " + name() + " breaks the THP limit, while it is not operable under THP limit"); + } + } } } From ea42d1de9d8f184578ada58e2b87ca754b4a14c4 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:14:51 +0100 Subject: [PATCH 06/15] adding function isOperable() to WellInterface to indicate if the well is operable. --- opm/autodiff/BlackoilWellModel_impl.hpp | 23 ++++++++++++++----- opm/autodiff/StandardWell_impl.hpp | 30 +++++++++++++++++++++---- opm/autodiff/WellInterface.hpp | 7 ++++-- opm/autodiff/WellInterface_impl.hpp | 11 +++++++++ 4 files changed, 60 insertions(+), 11 deletions(-) diff --git a/opm/autodiff/BlackoilWellModel_impl.hpp b/opm/autodiff/BlackoilWellModel_impl.hpp index 9c877c791..1ea2acb7d 100644 --- a/opm/autodiff/BlackoilWellModel_impl.hpp +++ b/opm/autodiff/BlackoilWellModel_impl.hpp @@ -803,7 +803,9 @@ namespace Opm { // Get global (from all processes) convergence report. ConvergenceReport local_report; for (const auto& well : well_container_) { - local_report += well->getWellConvergence(B_avg); + if (well->isOperable() ) { + local_report += well->getWellConvergence(B_avg); + } } ConvergenceReport report = gatherConvergenceReport(local_report); @@ -828,9 +830,10 @@ namespace Opm { BlackoilWellModel:: calculateExplicitQuantities() const { - for (auto& well : well_container_) { - well->calculateExplicitQuantities(ebosSimulator_, well_state_); - } + // TODO: checking isOperable() ? + for (auto& well : well_container_) { + well->calculateExplicitQuantities(ebosSimulator_, well_state_); + } } @@ -934,6 +937,10 @@ namespace Opm { // process group control related prepareGroupControl(); + for (const auto& well : well_container_) { + well->checkWellOperatability(ebosSimulator_); + } + // since the controls are all updated, we should update well_state accordingly for (const auto& well : well_container_) { const int w = well->indexOfWell(); @@ -941,16 +948,22 @@ namespace Opm { const int control = well_controls_get_current(wc); well_state_.currentControls()[w] = control; + if (!well->isOperable() ) continue; + if (well_state_.effectiveEventsOccurred(w) ) { well->updateWellStateWithTarget(ebosSimulator_, well_state_); } // there is no new well control change input within a report step, // so next time step, the well does not consider to have effective events anymore + // TODO: if we can know whether this is the first time step within the report step, + // we do not need to change this + // TODO: we should do this at the end of the time step in case we will need it within + // this time step somewhere if (well_state_.effectiveEventsOccurred(w) ) { well_state_.setEffectiveEventsOccurred(w, false); } - } // end of for (int w = 0; w < nw; ++w) + } // end of for (const auto& well : well_container_) updatePrimaryVariables(); } diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 0c7d9d4bb..1031cc7e2 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -440,9 +440,9 @@ namespace Opm WellState& well_state) { - const Opm::SummaryConfig& summaryConfig = ebosSimulator.vanguard().summaryConfig(); + checkWellOperatability(ebosSimulator); - const int np = number_of_phases_; + if (!this->isOperable()) return; // clear all entries duneB_ = 0.0; @@ -461,6 +461,7 @@ namespace Opm well_state.wellVaporizedOilRates()[index_of_well_] = 0.; well_state.wellDissolvedGasRates()[index_of_well_] = 0.; + const int np = number_of_phases_; for (int p = 0; p < np; ++p) { well_state.productivityIndex()[np*index_of_well_ + p] = 0.; } @@ -588,7 +589,8 @@ namespace Opm well_state.perfPress()[first_perf_ + perf] = well_state.bhp()[index_of_well_] + perf_pressure_diffs_[perf]; // Compute Productivity index if asked for - const auto& pu = phaseUsage(); + const auto& pu = phaseUsage(); + const Opm::SummaryConfig& summaryConfig = ebosSimulator.vanguard().summaryConfig(); for (int p = 0; p < np; ++p) { if ( (pu.phase_pos[Water] == p && (summaryConfig.hasSummaryKey("WPIW:" + name()) || summaryConfig.hasSummaryKey("WPIL:" + name()))) || (pu.phase_pos[Oil] == p && (summaryConfig.hasSummaryKey("WPIO:" + name()) || summaryConfig.hasSummaryKey("WPIL:" + name()))) @@ -821,6 +823,8 @@ namespace Opm updateWellState(const BVectorWell& dwells, WellState& well_state) const { + if (!this->isOperable()) return; + updatePrimaryVariablesNewton(dwells, well_state); updateWellStateFromPrimaryVariables(well_state); @@ -1287,7 +1291,7 @@ namespace Opm // wellTestingPhysical can share some code with this function // on solution is that this function will be called updateWellOperatability // and the actual checking part become another function checkWellOperatability - // Let us finish the wellTestingPhysical first. + // Let us wait until finishing the wellTestingPhysical first. // focusing on PRODUCER for now if (well_type_ == INJECTOR) { @@ -1318,6 +1322,12 @@ namespace Opm this->operability_status_.negative_well_rates = allDrawDownWrongDirection(ebos_simulator); const bool well_operable = this->operability_status_.isOperable(); + + if (!well_operable && old_well_operable) { + OpmLog::info(" well " + name() + " gets SHUT during iteration "); + } else if (well_operable && !old_well_operable) { + OpmLog::info(" well " + name() + " gets REVIVED during iteration "); + } } @@ -1943,6 +1953,8 @@ namespace Opm StandardWell:: solveEqAndUpdateWellState(WellState& well_state) { + if (!this->isOperable()) return; + // We assemble the well equations, then we check the convergence, // which is why we do not put the assembleWellEq here. BVectorWell dx_well(1); @@ -1988,6 +2000,8 @@ namespace Opm StandardWell:: apply(const BVector& x, BVector& Ax) const { + if (!this->isOperable()) return; + if ( param_.matrix_add_well_contributions_ ) { // Contributions are already in the matrix itself @@ -2016,6 +2030,8 @@ namespace Opm StandardWell:: apply(BVector& r) const { + if (!this->isOperable()) return; + assert( invDrw_.size() == invDuneD_.N() ); // invDrw_ = invDuneD_ * resWell_ @@ -2033,6 +2049,8 @@ namespace Opm StandardWell:: recoverSolutionWell(const BVector& x, BVectorWell& xw) const { + if (!this->isOperable()) return; + BVectorWell resWell = resWell_; // resWell = resWell - B * x duneB_.mmv(x, resWell); @@ -2050,6 +2068,8 @@ namespace Opm recoverWellSolutionAndUpdateWellState(const BVector& x, WellState& well_state) const { + if (!this->isOperable()) return; + BVectorWell xw(1); recoverSolutionWell(x, xw); updateWellState(xw, well_state); @@ -2254,6 +2274,8 @@ namespace Opm StandardWell:: updatePrimaryVariables(const WellState& well_state) const { + if (!this->isOperable()) return; + const int well_index = index_of_well_; const int np = number_of_phases_; diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index be8c28fd3..cdf89f94b 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -225,6 +225,9 @@ namespace Opm virtual void checkWellOperatability(const Simulator& ebos_simulator) = 0; + // whether the well is operable + bool isOperable() const; + protected: // to indicate a invalid completion @@ -400,7 +403,7 @@ namespace Opm violate_thp_limit_under_bhp_limit = false; obtain_solution_with_thp_limit = true; violate_bhp_limit_with_thp_limit = false; - // TODO: the following one might need to be different + // TODO: the following one might need to be treated differently negative_well_rates = false; } @@ -425,7 +428,7 @@ namespace Opm // could not get converged, maybe at the end of the time step, after chopping for some steps. // TODO: the best way is that this well can not get converged during local iterations. - bool could_not_get_converged = false; + // bool could_not_get_converged = false; }; } diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 1685440df..e6790f6cc 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -1223,4 +1223,15 @@ namespace Opm } + + + + template + bool + WellInterface:: + isOperable() const { + return operability_status_.isOperable(); + } + + } From 8e17d4aeba196faf6d26415d505bc0baf601a622 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:30:27 +0100 Subject: [PATCH 07/15] shut/close well based on physical limits --- opm/autodiff/BlackoilWellModel_impl.hpp | 24 ++++++++++++------ opm/autodiff/WellInterface.hpp | 5 ++++ opm/autodiff/WellInterface_impl.hpp | 33 +++++++++++++++++++++++++ 3 files changed, 54 insertions(+), 8 deletions(-) diff --git a/opm/autodiff/BlackoilWellModel_impl.hpp b/opm/autodiff/BlackoilWellModel_impl.hpp index 1ea2acb7d..d48993f70 100644 --- a/opm/autodiff/BlackoilWellModel_impl.hpp +++ b/opm/autodiff/BlackoilWellModel_impl.hpp @@ -434,16 +434,24 @@ namespace Opm { if( well_ecl->getAutomaticShutIn() ) { // shut wells are not added to the well container well_state_.bhp()[w] = 0; + + // TODO: should we do this for all kinds of closing reasons? + // something like wellTestState_.hasWell(well_name)? + if ( wellTestState_.hasWell(well_name, WellTestConfig::Reason::ECONOMIC) || + wellTestState_.hasWell(well_name, WellTestConfig::Reason::PHYSICAL) ) { + if( well_ecl->getAutomaticShutIn() ) { + // shut wells are not added to the well container + well_state_.thp()[w] = 0.; + well_state_.bhp()[w] = 0.; const int np = numPhases(); for (int p = 0; p < np; ++p) { - well_state_.wellRates()[np * w + p] = 0; - } - continue; - } - else { - // close wells are added to the container but marked as closed - struct WellControls* well_controls = wells()->ctrls[w]; - well_controls_stop_well(well_controls); + well_state_.wellRates()[np * w + p] = 0.; + } + continue; + } else { + // close wells are added to the container but marked as closed + struct WellControls* well_controls = wells()->ctrls[w]; + well_controls_stop_well(well_controls); } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index cdf89f94b..33f07cc8a 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -367,6 +367,11 @@ namespace Opm const bool write_message_to_opmlog, WellTestState& well_test_state) const; + void updateWellTestStatePhysical(const WellState& well_state, + const double simulation_time, + const bool write_message_to_opmlog, + WellTestState& well_test_state) const; + void solveWellForTesting(Simulator& ebosSimulator, WellState& well_state, const std::vector& B_avg, bool terminal_output); void scaleProductivityIndex(const int perfIdx, double& productivity_index) const; diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index e6790f6cc..54744fc08 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -735,6 +735,15 @@ namespace Opm return; } + // if we understand correctly, only under prediction mode, we need to do well testing + // TODO: which remains to be corrected + if (!underPredictionMode() ) { + return; + } + + // updating well test state based on physical (THP/BHP) limits. + updateWellTestStatePhysical(well_state, simulationTime, writeMessageToOPMLog, wellTestState); + // updating well test state based on Economic limits. updateWellTestStateEconomic(well_state, simulationTime, writeMessageToOPMLog, wellTestState); @@ -745,6 +754,30 @@ namespace Opm + template + void + WellInterface:: + updateWellTestStatePhysical(const WellState& well_state, + const double simulation_time, + const bool write_message_to_opmlog, + WellTestState& well_test_state) const + { + if (!isOperable()) { + well_test_state.addClosedWell(name(), WellTestConfig::Reason::PHYSICAL, simulation_time); + if (write_message_to_opmlog) { + // TODO: considering auto shut in? + const std::string msg = "well " + name() + + std::string(" will be shut as it can not operate under current reservoir condition"); + OpmLog::info(msg); + } + } + + } + + + + + template void WellInterface:: From 30742112c6c75e88f2679adc22eb2551897660d8 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:31:37 +0100 Subject: [PATCH 08/15] switching to BHP limit when THP limit is not operable a well is specified with THP target, while it can not work under its THP target. For this case, we will switch to its BHP limit. --- opm/autodiff/StandardWell_impl.hpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 1031cc7e2..faa260ae0 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1109,13 +1109,21 @@ namespace Opm // TODO: we should address this in a function updateWellStateWithBHPTarget. // TODO: however, the reason that this one minght not be that critical with // TODO: the effects remaining to be investigated. - break; + case THP: { - // TODO: adding the checking for the operability - // TODO: should we do updateIPR before this or within the related functions - updateIPR(ebos_simulator); - updateWellStateWithTHPTargetIPR(ebos_simulator, well_state); + assert(this->isOperable() ); + + // when a well can not work under THP target, it change to work under BHP target + if (this->operability_status_.isOperableUnderTHPLimit() ) { + updateWellStateWithTHPTargetIPR(ebos_simulator, well_state); + } else { // go to BHP limit + assert(this->operability_status_.isOperableUnderBHPLimit() ); + + OpmLog::info("well " + name() + " can not work with THP target, switching to BHP control"); + + well_state.bhp()[well_index] = mostStrictBhpFromBhpLimits(); + } break; } From c994a9958323612664bbeb3d6d2adb64025f66c5 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:36:31 +0100 Subject: [PATCH 09/15] WCON keyword can open a well closed due to physical reason more tests will be required to figure out more accurate specific way to open a well closed due to physcal reason with WCON input. --- opm/autodiff/BlackoilWellModel_impl.hpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/opm/autodiff/BlackoilWellModel_impl.hpp b/opm/autodiff/BlackoilWellModel_impl.hpp index d48993f70..b3cc189be 100644 --- a/opm/autodiff/BlackoilWellModel_impl.hpp +++ b/opm/autodiff/BlackoilWellModel_impl.hpp @@ -396,6 +396,10 @@ namespace Opm { initial_step_ = false; } + + + + template std::vector::WellInterfacePtr > BlackoilWellModel:: @@ -429,11 +433,15 @@ namespace Opm { const Well* well_ecl = wells_ecl_[index_well]; - // well is closed due to economical reasons - if (wellTestState_.hasWell(well_name, WellTestConfig::Reason::ECONOMIC)) { - if( well_ecl->getAutomaticShutIn() ) { - // shut wells are not added to the well container - well_state_.bhp()[w] = 0; + // TODO: a new WCON keyword can re-open the well closed by physical reason + // A new WCON keywords can re-open a well that was closed/shut due to Physical limit + if ( wellTestState_.hasWell(well_name, WellTestConfig::Reason::PHYSICAL ) ) { + // TODO: more checking here, to makre sure this standard more specific and complete + // maybe there is some WCON keywords will not open the well + if (well_state_.effectiveEventsOccurred(w) ) { + wellTestState_.openWell(well_name); + } + } // TODO: should we do this for all kinds of closing reasons? // something like wellTestState_.hasWell(well_name)? From bba373c0b50e66027742106c350a9a28371575d9 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:38:20 +0100 Subject: [PATCH 10/15] THP control tolerance changed to 1.e4 to fix some convergence problem. --- opm/autodiff/StandardWell_impl.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index faa260ae0..954a0cb62 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1871,7 +1871,7 @@ namespace Opm switch(well_controls_get_current_type(well_controls_)) { case THP: type = CR::WellFailure::Type::ControlTHP; - control_tolerance = 1.e3; // 0.01 bar + control_tolerance = 1.e4; // 0.1 bar break; case BHP: // pressure type of control type = CR::WellFailure::Type::ControlBHP; From 90f805bf3e5cf4edce51c924238ce56be32b4ef8 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sat, 17 Nov 2018 23:39:09 +0100 Subject: [PATCH 11/15] we need to open the cross-flow to solve the sigularity if a well is banned from cross-flow. When it is under RATE control, its BHP might be initialized in way causing all the drawdown in the wrong direction. It will cause singular well equations. here, we open the croff-flow to fix the singularity and rely on Newton iteraton to get desired result. possible alternative approach is to adust the BHP to avoid the situation that all the drawdown are in the wrong direction. --- opm/autodiff/BlackoilWellModel_impl.hpp | 8 +++--- opm/autodiff/MultisegmentWell.hpp | 2 +- opm/autodiff/MultisegmentWell_impl.hpp | 6 ++--- opm/autodiff/StandardWell.hpp | 11 +++++++-- opm/autodiff/StandardWell_impl.hpp | 33 +++++++++++++++++++------ opm/autodiff/WellInterface.hpp | 2 +- 6 files changed, 43 insertions(+), 19 deletions(-) diff --git a/opm/autodiff/BlackoilWellModel_impl.hpp b/opm/autodiff/BlackoilWellModel_impl.hpp index b3cc189be..f49934fe7 100644 --- a/opm/autodiff/BlackoilWellModel_impl.hpp +++ b/opm/autodiff/BlackoilWellModel_impl.hpp @@ -433,10 +433,9 @@ namespace Opm { const Well* well_ecl = wells_ecl_[index_well]; - // TODO: a new WCON keyword can re-open the well closed by physical reason // A new WCON keywords can re-open a well that was closed/shut due to Physical limit if ( wellTestState_.hasWell(well_name, WellTestConfig::Reason::PHYSICAL ) ) { - // TODO: more checking here, to makre sure this standard more specific and complete + // TODO: more checking here, to make sure this standard more specific and complete // maybe there is some WCON keywords will not open the well if (well_state_.effectiveEventsOccurred(w) ) { wellTestState_.openWell(well_name); @@ -449,6 +448,7 @@ namespace Opm { wellTestState_.hasWell(well_name, WellTestConfig::Reason::PHYSICAL) ) { if( well_ecl->getAutomaticShutIn() ) { // shut wells are not added to the well container + // TODO: make a function from well_state side to handle the following well_state_.thp()[w] = 0.; well_state_.bhp()[w] = 0.; const int np = numPhases(); @@ -954,7 +954,7 @@ namespace Opm { prepareGroupControl(); for (const auto& well : well_container_) { - well->checkWellOperatability(ebosSimulator_); + well->checkWellOperability(ebosSimulator_); } // since the controls are all updated, we should update well_state accordingly @@ -973,7 +973,7 @@ namespace Opm { // there is no new well control change input within a report step, // so next time step, the well does not consider to have effective events anymore // TODO: if we can know whether this is the first time step within the report step, - // we do not need to change this + // we do not need to set it to false // TODO: we should do this at the end of the time step in case we will need it within // this time step somewhere if (well_state_.effectiveEventsOccurred(w) ) { diff --git a/opm/autodiff/MultisegmentWell.hpp b/opm/autodiff/MultisegmentWell.hpp index eb56ba6bf..c3613ff98 100644 --- a/opm/autodiff/MultisegmentWell.hpp +++ b/opm/autodiff/MultisegmentWell.hpp @@ -332,7 +332,7 @@ namespace Opm // checking the operability of the well based on current reservoir condition // it is not implemented for multisegment well yet - virtual void checkWellOperatability(const Simulator& ebos_simulator); + virtual void checkWellOperability(const Simulator& ebos_simulator); void updateWellStateFromPrimaryVariables(WellState& well_state) const; diff --git a/opm/autodiff/MultisegmentWell_impl.hpp b/opm/autodiff/MultisegmentWell_impl.hpp index 8f4bc2977..e101e21af 100644 --- a/opm/autodiff/MultisegmentWell_impl.hpp +++ b/opm/autodiff/MultisegmentWell_impl.hpp @@ -1645,10 +1645,10 @@ namespace Opm template void MultisegmentWell:: - checkWellOperatability(const Simulator& ebos_simulator) + checkWellOperability(const Simulator& ebos_simulator) { - const std::string msg = "Support of well operatability checking for mutlisegment wells is not yet," - " checkWellOperatability() for " + name() + " will do nothing"; + const std::string msg = "Support of well operatability checking for mutlisegment wells is not implemented " + "yet, checkWellOperability() for " + name() + " will do nothing"; OpmLog::warning("NO_OPERATABILITY_CHECKING_MS_WELLS", msg); } diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 28d4f2a1f..49a49407e 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -350,7 +350,7 @@ namespace Opm // check whether the well is operable under the current reservoir condition // mostly related to BHP limit and THP limit - virtual void checkWellOperatability(const Simulator& ebos_simulator) override; + virtual void checkWellOperability(const Simulator& ebos_simulator) override; // check whether the well is operable under BHP limit with current reservoir condition void checkOperabilityUnderBHPLimit(const Simulator& ebos_simulator); @@ -367,7 +367,14 @@ namespace Opm // for a well, when all drawdown are in the wrong direction, then this well will not // be able to produce/inject . - bool allDrawDownWrongDirection(const Simulator& ebosSimulator) const; + bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const; + + // turn on crossflow to avoid singular well equations + // when the well is banned from cross-flow and the BHP is not properly initialized, + // we turn on crossflow to avoid singular well equations. It can result in worng-signed + // well rates, it can cause problem for THP calculation + // TODO: looking for better alternative to avoid wrong-signed well rates + bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const; // calculate the BHP from THP target based on IPR // TODO: we need to check the operablility here first, if not operable, then maybe there is diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 954a0cb62..eb9f17a34 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -440,7 +440,7 @@ namespace Opm WellState& well_state) { - checkWellOperatability(ebosSimulator); + checkWellOperability(ebosSimulator); if (!this->isOperable()) return; @@ -453,7 +453,11 @@ namespace Opm // TODO: it probably can be static member for StandardWell const double volume = 0.002831684659200; // 0.1 cu ft; - const bool allow_cf = getAllowCrossFlow(); + // to avoid singularity of the well equation when all the drawdown is in the wrong direction + // TODO: make it a function, and avoid repeated calculation + // TODO: it can be avoided through adjusting the bhp of the well + const bool allow_cf = getAllowCrossFlow() || openCrossFlowAvoidSingularity(ebosSimulator); + const EvalWell& bhp = getBhp(); @@ -1293,12 +1297,12 @@ namespace Opm template void StandardWell:: - checkWellOperatability(const Simulator& ebos_simulator) + checkWellOperability(const Simulator& ebos_simulator) { // TODO: this function is probably can split another function out so that // wellTestingPhysical can share some code with this function - // on solution is that this function will be called updateWellOperatability - // and the actual checking part become another function checkWellOperatability + // on solution is that this function will be called updateWellOperability + // and the actual checking part become another function checkWellOperability // Let us wait until finishing the wellTestingPhysical first. // focusing on PRODUCER for now @@ -1428,13 +1432,13 @@ namespace Opm template bool StandardWell:: - allDrawDownWrongDirection(const Simulator& ebosSimulator) const + allDrawDownWrongDirection(const Simulator& ebos_simulator) const { bool all_drawdown_wrong_direction = true; for (int perf = 0; perf < number_of_perforations_; ++perf) { const int cell_idx = well_cells_[perf]; - const auto& intQuants = *(ebosSimulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); + const auto& intQuants = *(ebos_simulator.model().cachedIntensiveQuantities(cell_idx, /*timeIdx=*/0)); const auto& fs = intQuants.fluidState(); const double pressure = (fs.pressure(FluidSystem::oilPhaseIdx)).value(); @@ -1461,6 +1465,18 @@ namespace Opm + template + bool + StandardWell:: + openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const + { + return !getAllowCrossFlow() && allDrawDownWrongDirection(ebos_simulator); + } + + + + + template void StandardWell:: @@ -2494,7 +2510,8 @@ namespace Opm return; } // compute the well water velocity with out shear effects. - const bool allow_cf = getAllowCrossFlow(); + // TODO: do we need to turn on crossflow here? + const bool allow_cf = getAllowCrossFlow() || openCrossFlowAvoidSingularity(ebos_simulator); const EvalWell& bhp = getBhp(); std::vector cq_s(num_components_,0.0); double perf_dis_gas_rate = 0.; diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 33f07cc8a..f2521a21f 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -223,7 +223,7 @@ namespace Opm void updatePerforatedCell(std::vector& is_cell_perforated); - virtual void checkWellOperatability(const Simulator& ebos_simulator) = 0; + virtual void checkWellOperability(const Simulator& ebos_simulator) = 0; // whether the well is operable bool isOperable() const; From bf9b5367d98f17cb8deff4cdfa12886723d0a7fb Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sun, 18 Nov 2018 13:52:03 +0100 Subject: [PATCH 12/15] some cleaning up, no functional changes --- opm/autodiff/StandardWell.hpp | 4 ++-- opm/autodiff/StandardWell_impl.hpp | 30 ++++++++++++------------------ opm/autodiff/VFPProdProperties.cpp | 7 +++++-- 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 49a49407e..f9f634c7b 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -353,10 +353,10 @@ namespace Opm virtual void checkWellOperability(const Simulator& ebos_simulator) override; // check whether the well is operable under BHP limit with current reservoir condition - void checkOperabilityUnderBHPLimit(const Simulator& ebos_simulator); + void checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator); // check whether the well is operable under THP limit with current reservoir condition - void checkOperabilityUnderTHPLimit(const Simulator& ebos_simulator); + void checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator); // update WellState based on IPR and associated VFP table void updateWellStateWithTHPTargetIPR(const Simulator& ebos_simulator, diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index eb9f17a34..771f1c37f 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -453,12 +453,8 @@ namespace Opm // TODO: it probably can be static member for StandardWell const double volume = 0.002831684659200; // 0.1 cu ft; - // to avoid singularity of the well equation when all the drawdown is in the wrong direction - // TODO: make it a function, and avoid repeated calculation - // TODO: it can be avoided through adjusting the bhp of the well const bool allow_cf = getAllowCrossFlow() || openCrossFlowAvoidSingularity(ebosSimulator); - const EvalWell& bhp = getBhp(); // the solution gas rate and solution oil rate needs to be reset to be zero for well_state. @@ -1321,13 +1317,13 @@ namespace Opm updateIPR(ebos_simulator); // checking the BHP limit related - checkOperabilityUnderBHPLimit(ebos_simulator); + checkOperabilityUnderBHPLimitProducer(ebos_simulator); // TODO: if the BHP limit does not work anyway, we do not need to do the following // We do it now for studying purpose. // checking whether the well can operate under the THP constraints. if (this->wellHasTHPConstraints()) { - checkOperabilityUnderTHPLimit(ebos_simulator); + checkOperabilityUnderTHPLimitProducer(ebos_simulator); } // checking whether the well can not produce or something else @@ -1349,13 +1345,13 @@ namespace Opm template void StandardWell:: - checkOperabilityUnderBHPLimit(const Simulator& ebos_simulator) + checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator) { const double bhp_limit = mostStrictBhpFromBhpLimits(); // TODO: a better way to detect whether the BHP is defaulted or not if ( bhp_limit > 1.5e5 || !this->wellHasTHPConstraints() ) { - // if ( !(bhp_limit < 1.5e5 && this->wellHasTHPConstraints()) ) { - // if there is a non-defaulted BHP limit or the well does not have a THP limit + // if the BHP limit is not defaulted or the well does not have a THP limit + // we need to check the BHP limit for (int p = 0; p < number_of_phases_; ++p) { const double temp = ipr_a_[p] - ipr_b_[p] * bhp_limit; @@ -1387,6 +1383,8 @@ namespace Opm // when applied the hydrostatic pressure correction, // most likely we get a negative bhp value to search in the VFP table, // which is not desirable + // we assume we can operate under thi BHP limit and will violate the THP limit + // when operating under this BHP limit this->operability_status_.operable_under_only_bhp_limit = true; this->operability_status_.violate_thp_limit_under_bhp_limit = true; } @@ -1399,19 +1397,15 @@ namespace Opm template void StandardWell:: - checkOperabilityUnderTHPLimit(const Simulator& ebos_simulator) + checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator) { - // We will use IPR to make the rates for now const double bhp_limit = mostStrictBhpFromBhpLimits(); const double thp_limit = this->getTHPConstraint(); const double thp_control_index = this->getTHPControlIndex(); - const int thp_table_id = well_controls_iget_vfp(well_controls_, thp_control_index); + const int table_id = well_controls_iget_vfp(well_controls_, thp_control_index); const double alq = well_controls_iget_alq(well_controls_, thp_control_index); - double vfp_ref_depth = 0.; - - // not considering injectors for now - vfp_ref_depth = vfp_properties_->getProd()->getTable(thp_table_id)->getDatumDepth(); + const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); // the density of the top perforation const double rho = perf_densities_[0]; @@ -1419,7 +1413,7 @@ namespace Opm const double dp = (vfp_ref_depth - ref_depth_) * rho * gravity_; vfp_properties_->getProd()->operabilityCheckingUnderTHP(ipr_a_, ipr_b_, bhp_limit, - thp_table_id, thp_limit, alq, dp, + table_id, thp_limit, alq, dp, this->operability_status_.obtain_solution_with_thp_limit, this->operability_status_.violate_bhp_limit_with_thp_limit ); @@ -1450,7 +1444,7 @@ namespace Opm // for now, if there is one perforation can produce/inject in the correct // direction, we consider this well can still produce/inject. - // TODO: it can be more complicated than this + // TODO: it can be more complicated than this to cause worng-signed rates if ( (drawdown < 0. && well_type_ == INJECTOR) || (drawdown > 0. && well_type_ == PRODUCER) ) { all_drawdown_wrong_direction = false; diff --git a/opm/autodiff/VFPProdProperties.cpp b/opm/autodiff/VFPProdProperties.cpp index cc2ded314..9b64f71d0 100644 --- a/opm/autodiff/VFPProdProperties.cpp +++ b/opm/autodiff/VFPProdProperties.cpp @@ -273,12 +273,15 @@ operabilityCheckingUnderTHP(const std::vector& ipr_a, violate_bhp_limit_with_thp_limit = (obtain_bhp < bhp_limit); if (obtain_bhp < thp_limit) { - std::cout << " obtain_bhp " << obtain_bhp / 1.e5 << " is SMALLER than thp limit " << thp_limit / 1.e5 << std::endl; + const std::string msg = " obtained bhp " + std::to_string(obtain_bhp / 1.e5) + + " is SMALLER than thp limit " + std::to_string(thp_limit / 1.e5) + " as a producer "; + OpmLog::debug(msg); } } else { obtain_solution_with_thp_limit = false; - std::cout << " COULD NOT find an Intersection point, the well might need to be closed " << std::endl; + OpmLog::debug(" COULD NOT find bhp value under thp_limit " + std::to_string(thp_limit / 1.e5) + + ", the well might need to be closed "); violate_bhp_limit_with_thp_limit = false; } } From 8330c3a6a78d96968c204a64070ace426a6cc417 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Sun, 18 Nov 2018 15:08:17 +0100 Subject: [PATCH 13/15] adjusting OperabilityStatus of WellInterface hopefully, it improves the readability --- opm/autodiff/StandardWell_impl.hpp | 15 +++++++------- opm/autodiff/VFPProdProperties.cpp | 6 +++--- opm/autodiff/WellInterface.hpp | 33 +++++++++++++++--------------- 3 files changed, 28 insertions(+), 26 deletions(-) diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 771f1c37f..13ab5dbf1 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1326,8 +1326,9 @@ namespace Opm checkOperabilityUnderTHPLimitProducer(ebos_simulator); } - // checking whether the well can not produce or something else - this->operability_status_.negative_well_rates = allDrawDownWrongDirection(ebos_simulator); + // checking whether the well can not produce/inject + this->operability_status_.existing_drawdown_correct_direction = + ! allDrawDownWrongDirection(ebos_simulator); const bool well_operable = this->operability_status_.isOperable(); @@ -1365,7 +1366,7 @@ namespace Opm if (this->operability_status_.operable_under_only_bhp_limit && this->wellHasTHPConstraints()) { // option 1: calculate well rates based on the BHP limit. // option 2: stick with the above IPR curve - // TODO: most likely, we can use IPR here + // we use IPR here const double bhp_limit = mostStrictBhpFromBhpLimits(); std::vector well_rates_bhp_limit; computeWellRatesWithBhp(ebos_simulator, bhp_limit, well_rates_bhp_limit); @@ -1373,8 +1374,8 @@ namespace Opm const double thp = calculateThpFromBhp(well_rates_bhp_limit, bhp_limit); const double thp_limit = this->getTHPConstraint(); - if (thp < thp_limit || thp > bhp_limit) { - this->operability_status_.violate_thp_limit_under_bhp_limit = true; + if (thp < thp_limit) { + this->operability_status_.obey_thp_limit_under_bhp_limit = false; } } } else { @@ -1386,7 +1387,7 @@ namespace Opm // we assume we can operate under thi BHP limit and will violate the THP limit // when operating under this BHP limit this->operability_status_.operable_under_only_bhp_limit = true; - this->operability_status_.violate_thp_limit_under_bhp_limit = true; + this->operability_status_.obey_thp_limit_under_bhp_limit = false; } } @@ -1415,7 +1416,7 @@ namespace Opm vfp_properties_->getProd()->operabilityCheckingUnderTHP(ipr_a_, ipr_b_, bhp_limit, table_id, thp_limit, alq, dp, this->operability_status_.obtain_solution_with_thp_limit, - this->operability_status_.violate_bhp_limit_with_thp_limit ); + this->operability_status_.obey_bhp_limit_with_thp_limit ); } diff --git a/opm/autodiff/VFPProdProperties.cpp b/opm/autodiff/VFPProdProperties.cpp index 9b64f71d0..21b66690e 100644 --- a/opm/autodiff/VFPProdProperties.cpp +++ b/opm/autodiff/VFPProdProperties.cpp @@ -263,14 +263,14 @@ operabilityCheckingUnderTHP(const std::vector& ipr_a, const double alq, const double dp, bool& obtain_solution_with_thp_limit, - bool& violate_bhp_limit_with_thp_limit) const + bool& obey_bhp_limit_with_thp_limit) const { const double obtain_bhp = calculateBhpWithTHPTarget(ipr_a, ipr_b, bhp_limit, thp_table_id, thp_limit, alq, dp); if (obtain_bhp > 0.) { obtain_solution_with_thp_limit = true; - violate_bhp_limit_with_thp_limit = (obtain_bhp < bhp_limit); + obey_bhp_limit_with_thp_limit = (obtain_bhp >= bhp_limit); if (obtain_bhp < thp_limit) { const std::string msg = " obtained bhp " + std::to_string(obtain_bhp / 1.e5) + @@ -282,7 +282,7 @@ operabilityCheckingUnderTHP(const std::vector& ipr_a, obtain_solution_with_thp_limit = false; OpmLog::debug(" COULD NOT find bhp value under thp_limit " + std::to_string(thp_limit / 1.e5) + ", the well might need to be closed "); - violate_bhp_limit_with_thp_limit = false; + obey_bhp_limit_with_thp_limit = true; } } diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index f2521a21f..faf15fc3a 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -391,45 +391,46 @@ namespace Opm if (!operable_under_only_bhp_limit) { return false; } else { - return !negative_well_rates && (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()); + return existing_drawdown_correct_direction && + (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()); } } bool isOperableUnderBHPLimit() const { - return operable_under_only_bhp_limit && !violate_thp_limit_under_bhp_limit; + return operable_under_only_bhp_limit && obey_thp_limit_under_bhp_limit; } bool isOperableUnderTHPLimit() const { - return obtain_solution_with_thp_limit && !violate_bhp_limit_with_thp_limit; + return obtain_solution_with_thp_limit && obey_bhp_limit_with_thp_limit; } void reset() { operable_under_only_bhp_limit = true; - violate_thp_limit_under_bhp_limit = false; + obey_thp_limit_under_bhp_limit = true; obtain_solution_with_thp_limit = true; - violate_bhp_limit_with_thp_limit = false; + obey_bhp_limit_with_thp_limit = true; // TODO: the following one might need to be treated differently - negative_well_rates = false; + existing_drawdown_correct_direction = true; } - // TODO: re-design the boolean variables so that they have meaning in the same directions. - // For example, true are all for positive situation, and false are all for negative circumstances. - // whether the well can be operated under bhp limit // without considering other limits. // if it is false, then the well is not operable for sure. bool operable_under_only_bhp_limit = true; - // if the well can be operated under bhp limit, will it violate + // if the well can be operated under bhp limit, will it obey(not violate) // the thp limit when operated under bhp limit - bool violate_thp_limit_under_bhp_limit = false; + bool obey_thp_limit_under_bhp_limit = true; // whether the well operate under the thp limit only bool obtain_solution_with_thp_limit = true; - // whether the well violate bhp limit when operated under thp limit - bool violate_bhp_limit_with_thp_limit = false; + // whether the well obey bhp limit when operated under thp limit + bool obey_bhp_limit_with_thp_limit = true; - // we get negatvie well rates - // currently, we are trying to address the one result from updateWellStateWithTHPTargetIPR - bool negative_well_rates = false; + // there is some drawdown with correct sign/direction + // if all the drawdown are with wrong sign/direction, it means producer can not produce + // and injector can not inject. + // TODO: even not all the drawdown are with wrong sign, it is still possible that + // producer can not produce and injector can not inject if the crossflow is allowed + bool existing_drawdown_correct_direction = true; // could not get converged, maybe at the end of the time step, after chopping for some steps. // TODO: the best way is that this well can not get converged during local iterations. From f9988057af6725ce9786f6f02e51d7ff4a65deb0 Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Tue, 20 Nov 2018 13:49:11 +0100 Subject: [PATCH 14/15] fixing comments from review of PR#1648 there should be no functional change --- opm/autodiff/StandardWell.hpp | 2 +- opm/autodiff/StandardWell_impl.hpp | 72 ++++++++++++++--------------- opm/autodiff/VFPProdProperties.cpp | 41 ++-------------- opm/autodiff/VFPProdProperties.hpp | 10 ---- opm/autodiff/WellInterface.hpp | 15 +++--- opm/autodiff/WellInterface_impl.hpp | 11 +++-- 6 files changed, 51 insertions(+), 100 deletions(-) diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index f9f634c7b..4d9e36cb8 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -371,7 +371,7 @@ namespace Opm // turn on crossflow to avoid singular well equations // when the well is banned from cross-flow and the BHP is not properly initialized, - // we turn on crossflow to avoid singular well equations. It can result in worng-signed + // we turn on crossflow to avoid singular well equations. It can result in wrong-signed // well rates, it can cause problem for THP calculation // TODO: looking for better alternative to avoid wrong-signed well rates bool openCrossFlowAvoidSingularity(const Simulator& ebos_simulator) const; diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index 13ab5dbf1..b7983f411 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -1114,7 +1114,7 @@ namespace Opm case THP: { assert(this->isOperable() ); - // when a well can not work under THP target, it change to work under BHP target + // when a well can not work under THP target, it switches to BHP control if (this->operability_status_.isOperableUnderTHPLimit() ) { updateWellStateWithTHPTargetIPR(ebos_simulator, well_state); } else { // go to BHP limit @@ -1295,12 +1295,6 @@ namespace Opm StandardWell:: checkWellOperability(const Simulator& ebos_simulator) { - // TODO: this function is probably can split another function out so that - // wellTestingPhysical can share some code with this function - // on solution is that this function will be called updateWellOperability - // and the actual checking part become another function checkWellOperability - // Let us wait until finishing the wellTestingPhysical first. - // focusing on PRODUCER for now if (well_type_ == INJECTOR) { return; @@ -1319,8 +1313,6 @@ namespace Opm // checking the BHP limit related checkOperabilityUnderBHPLimitProducer(ebos_simulator); - // TODO: if the BHP limit does not work anyway, we do not need to do the following - // We do it now for studying purpose. // checking whether the well can operate under the THP constraints. if (this->wellHasTHPConstraints()) { checkOperabilityUnderTHPLimitProducer(ebos_simulator); @@ -1349,8 +1341,10 @@ namespace Opm checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator) { const double bhp_limit = mostStrictBhpFromBhpLimits(); + // Crude but works: default is one atmosphere. // TODO: a better way to detect whether the BHP is defaulted or not - if ( bhp_limit > 1.5e5 || !this->wellHasTHPConstraints() ) { + const bool bhp_limit_not_defaulted = bhp_limit > 1.5 * unit::barsa; + if ( bhp_limit_not_defaulted || !this->wellHasTHPConstraints() ) { // if the BHP limit is not defaulted or the well does not have a THP limit // we need to check the BHP limit @@ -1367,7 +1361,6 @@ namespace Opm // option 1: calculate well rates based on the BHP limit. // option 2: stick with the above IPR curve // we use IPR here - const double bhp_limit = mostStrictBhpFromBhpLimits(); std::vector well_rates_bhp_limit; computeWellRatesWithBhp(ebos_simulator, bhp_limit, well_rates_bhp_limit); @@ -1379,13 +1372,13 @@ namespace Opm } } } else { - // defaulted BHP and there is THP constraints - // default BHP limit is about 1.03 bar. - // when applied the hydrostatic pressure correction, - // most likely we get a negative bhp value to search in the VFP table, - // which is not desirable - // we assume we can operate under thi BHP limit and will violate the THP limit - // when operating under this BHP limit + // defaulted BHP and there is a THP constraint + // default BHP limit is about 1 atm. + // when applied the hydrostatic pressure correction dp, + // most likely we get a negative value (bhp + dp)to search in the VFP table, + // which is not desirable. + // we assume we can operate under defaulted BHP limit and will violate the THP limit + // when operating under defaulted BHP limit. this->operability_status_.operable_under_only_bhp_limit = true; this->operability_status_.obey_thp_limit_under_bhp_limit = false; } @@ -1400,24 +1393,30 @@ namespace Opm StandardWell:: checkOperabilityUnderTHPLimitProducer(const Simulator& ebos_simulator) { - const double bhp_limit = mostStrictBhpFromBhpLimits(); - const double thp_limit = this->getTHPConstraint(); - const double thp_control_index = this->getTHPControlIndex(); - const int table_id = well_controls_iget_vfp(well_controls_, thp_control_index); - const double alq = well_controls_iget_alq(well_controls_, thp_control_index); + const double obtain_bhp = calculateBHPWithTHPTargetIPR(); - const double vfp_ref_depth = vfp_properties_->getProd()->getTable(table_id)->getDatumDepth(); + if (obtain_bhp > 0.) { + this->operability_status_.can_obtain_bhp_with_thp_limit = true; - // the density of the top perforation - const double rho = perf_densities_[0]; - - const double dp = (vfp_ref_depth - ref_depth_) * rho * gravity_; - - vfp_properties_->getProd()->operabilityCheckingUnderTHP(ipr_a_, ipr_b_, bhp_limit, - table_id, thp_limit, alq, dp, - this->operability_status_.obtain_solution_with_thp_limit, - this->operability_status_.obey_bhp_limit_with_thp_limit ); + const double bhp_limit = mostStrictBhpFromBhpLimits(); + this->operability_status_.obey_bhp_limit_with_thp_limit = (obtain_bhp >= bhp_limit); + const double thp_limit = this->getTHPConstraint(); + if (obtain_bhp < thp_limit) { + const std::string msg = " obtained bhp " + std::to_string(unit::convert::to(obtain_bhp, unit::barsa)) + + " bars is SMALLER than thp limit " + + std::to_string(unit::convert::to(thp_limit, unit::barsa)) + + " bars as a producer for well " + name(); + OpmLog::debug(msg); + } + } else { + this->operability_status_.can_obtain_bhp_with_thp_limit = false; + const double thp_limit = this->getTHPConstraint(); + OpmLog::debug(" COULD NOT find bhp value under thp_limit " + + std::to_string(unit::convert::to(thp_limit, unit::barsa)) + + " bars for well " + name() + ", the well might need to be closed "); + this->operability_status_.obey_bhp_limit_with_thp_limit = false; + } } @@ -1445,7 +1444,7 @@ namespace Opm // for now, if there is one perforation can produce/inject in the correct // direction, we consider this well can still produce/inject. - // TODO: it can be more complicated than this to cause worng-signed rates + // TODO: it can be more complicated than this to cause wrong-signed rates if ( (drawdown < 0. && well_type_ == INJECTOR) || (drawdown > 0. && well_type_ == PRODUCER) ) { all_drawdown_wrong_direction = false; @@ -1505,6 +1504,8 @@ namespace Opm const double bhp = calculateBHPWithTHPTargetIPR(); + assert(bhp > 0.0); + well_state.bhp()[index_of_well_] = bhp; // TODO: explicit quantities are always tricky for this type of situation @@ -1557,9 +1558,6 @@ namespace Opm const double obtain_bhp = vfp_properties_->getProd()->calculateBhpWithTHPTarget(ipr_a_, ipr_b_, bhp_limit, thp_table_id, thp_target, alq, dp); - // we should have made sure that this well should be operable under THP limit now - assert(obtain_bhp > 0.); - return obtain_bhp; } diff --git a/opm/autodiff/VFPProdProperties.cpp b/opm/autodiff/VFPProdProperties.cpp index 21b66690e..89c85adfb 100644 --- a/opm/autodiff/VFPProdProperties.cpp +++ b/opm/autodiff/VFPProdProperties.cpp @@ -229,10 +229,10 @@ calculateBhpWithTHPTarget(const std::vector& ipr_a, detail::RateBhpPair{flo_bhp_limit, bhp_limit} }; double obtain_bhp = 0.; - const bool obtain_solution_with_thp_limit = detail::findIntersectionForBhp(ratebhp_samples, ratebhp_twopoints_ipr, obtain_bhp); + const bool can_obtain_bhp_with_thp_limit = detail::findIntersectionForBhp(ratebhp_samples, ratebhp_twopoints_ipr, obtain_bhp); - // \Note: assuming not that negative BHP does not make sense - if (obtain_solution_with_thp_limit && obtain_bhp > 0.) { + // \Note: assuming that negative BHP does not make sense + if (can_obtain_bhp_with_thp_limit && obtain_bhp > 0.) { // getting too high bhp that might cause negative rates (rates in the undesired direction) if (obtain_bhp >= bhp_safe_limit) { const std::string msg (" We are getting a too high BHP value from the THP constraint, which may " @@ -252,39 +252,4 @@ calculateBhpWithTHPTarget(const std::vector& ipr_a, } - - -void VFPProdProperties:: -operabilityCheckingUnderTHP(const std::vector& ipr_a, - const std::vector& ipr_b, - const double bhp_limit, - const double thp_table_id, - const double thp_limit, - const double alq, - const double dp, - bool& obtain_solution_with_thp_limit, - bool& obey_bhp_limit_with_thp_limit) const -{ - const double obtain_bhp = calculateBhpWithTHPTarget(ipr_a, ipr_b, bhp_limit, thp_table_id, thp_limit, alq, dp); - - if (obtain_bhp > 0.) { - obtain_solution_with_thp_limit = true; - - obey_bhp_limit_with_thp_limit = (obtain_bhp >= bhp_limit); - - if (obtain_bhp < thp_limit) { - const std::string msg = " obtained bhp " + std::to_string(obtain_bhp / 1.e5) + - " is SMALLER than thp limit " + std::to_string(thp_limit / 1.e5) + " as a producer "; - OpmLog::debug(msg); - } - - } else { - obtain_solution_with_thp_limit = false; - OpmLog::debug(" COULD NOT find bhp value under thp_limit " + std::to_string(thp_limit / 1.e5) + - ", the well might need to be closed "); - obey_bhp_limit_with_thp_limit = true; - } -} - - } diff --git a/opm/autodiff/VFPProdProperties.hpp b/opm/autodiff/VFPProdProperties.hpp index e0b55dd69..cbf94fda4 100644 --- a/opm/autodiff/VFPProdProperties.hpp +++ b/opm/autodiff/VFPProdProperties.hpp @@ -184,16 +184,6 @@ public: const double alq, const double dp) const; - void operabilityCheckingUnderTHP(const std::vector& ipr_a, - const std::vector& ipr_b, - const double bhp_limit, - const double thp_table_id, - const double thp_limit, - const double alq, - const double dp, - bool& obtain_solution_with_thp_limit, - bool& voilate_bhp_limit_with_thp_limit) const; - protected: // calculate a group bhp values with a group of flo rate values std::vector bhpwithflo(const std::vector& flos, diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index faf15fc3a..546a556e3 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -401,13 +401,13 @@ namespace Opm } bool isOperableUnderTHPLimit() const { - return obtain_solution_with_thp_limit && obey_bhp_limit_with_thp_limit; + return can_obtain_bhp_with_thp_limit && obey_bhp_limit_with_thp_limit; } void reset() { operable_under_only_bhp_limit = true; obey_thp_limit_under_bhp_limit = true; - obtain_solution_with_thp_limit = true; + can_obtain_bhp_with_thp_limit = true; obey_bhp_limit_with_thp_limit = true; // TODO: the following one might need to be treated differently existing_drawdown_correct_direction = true; @@ -421,20 +421,17 @@ namespace Opm // the thp limit when operated under bhp limit bool obey_thp_limit_under_bhp_limit = true; // whether the well operate under the thp limit only - bool obtain_solution_with_thp_limit = true; + bool can_obtain_bhp_with_thp_limit = true; // whether the well obey bhp limit when operated under thp limit bool obey_bhp_limit_with_thp_limit = true; // there is some drawdown with correct sign/direction // if all the drawdown are with wrong sign/direction, it means producer can not produce // and injector can not inject. - // TODO: even not all the drawdown are with wrong sign, it is still possible that - // producer can not produce and injector can not inject if the crossflow is allowed + // TODO: even there exist some drawdown with correct direction, it is still possible that + // producer can not produce and injector can not inject if the crossflow is allowed, since + // the well rate is the sum of the rates from all the perofrations bool existing_drawdown_correct_direction = true; - - // could not get converged, maybe at the end of the time step, after chopping for some steps. - // TODO: the best way is that this well can not get converged during local iterations. - // bool could_not_get_converged = false; }; } diff --git a/opm/autodiff/WellInterface_impl.hpp b/opm/autodiff/WellInterface_impl.hpp index 54744fc08..80e81cb1d 100644 --- a/opm/autodiff/WellInterface_impl.hpp +++ b/opm/autodiff/WellInterface_impl.hpp @@ -459,9 +459,10 @@ namespace Opm w, np, well_type_, wc, ctrl_index)) { // if the well can not work under THP / BHP control, we should not switch to THP / BHP control - // TODO: does this mean the well is not operable anymore, while it is within the non-linear iteration? - if ( !( well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit() ) && - !( well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit() ) ) { + const bool cannot_switch_to_bhp = well_controls_iget_type(wc, ctrl_index) == BHP && !operability_status_.isOperableUnderBHPLimit(); + const bool cannot_switch_to_thp = well_controls_iget_type(wc, ctrl_index) == THP && !operability_status_.isOperableUnderTHPLimit(); + const bool cannot_switch = cannot_switch_to_bhp || cannot_switch_to_thp; + if ( !cannot_switch ) { // ctrl_index will be the index of the broken constraint after the loop. break; @@ -735,8 +736,8 @@ namespace Opm return; } - // if we understand correctly, only under prediction mode, we need to do well testing - // TODO: which remains to be corrected + // Based on current understanding, only under prediction mode, we need to shut well due to various + // reasons or limits. With more knowlage or testing cases later, this might need to be corrected. if (!underPredictionMode() ) { return; } From 7d721408f29b111de253fc8ef70362d8213fb6de Mon Sep 17 00:00:00 2001 From: Kai Bao Date: Thu, 22 Nov 2018 07:02:29 +0100 Subject: [PATCH 15/15] adding canProduceInjectWithCurrentBhp to StandardWell and use it the operability_status determination. --- opm/autodiff/BlackoilWellModel_impl.hpp | 2 +- opm/autodiff/MultisegmentWell.hpp | 3 +- opm/autodiff/MultisegmentWell_impl.hpp | 3 +- opm/autodiff/StandardWell.hpp | 7 +++- opm/autodiff/StandardWell_impl.hpp | 46 +++++++++++++++++++++---- opm/autodiff/WellInterface.hpp | 25 +++++++------- 6 files changed, 64 insertions(+), 22 deletions(-) diff --git a/opm/autodiff/BlackoilWellModel_impl.hpp b/opm/autodiff/BlackoilWellModel_impl.hpp index f49934fe7..7d3d704c2 100644 --- a/opm/autodiff/BlackoilWellModel_impl.hpp +++ b/opm/autodiff/BlackoilWellModel_impl.hpp @@ -954,7 +954,7 @@ namespace Opm { prepareGroupControl(); for (const auto& well : well_container_) { - well->checkWellOperability(ebosSimulator_); + well->checkWellOperability(ebosSimulator_, well_state_); } // since the controls are all updated, we should update well_state accordingly diff --git a/opm/autodiff/MultisegmentWell.hpp b/opm/autodiff/MultisegmentWell.hpp index c3613ff98..318d06609 100644 --- a/opm/autodiff/MultisegmentWell.hpp +++ b/opm/autodiff/MultisegmentWell.hpp @@ -332,7 +332,8 @@ namespace Opm // checking the operability of the well based on current reservoir condition // it is not implemented for multisegment well yet - virtual void checkWellOperability(const Simulator& ebos_simulator); + virtual void checkWellOperability(const Simulator& ebos_simulator, + const WellState& well_state) override; void updateWellStateFromPrimaryVariables(WellState& well_state) const; diff --git a/opm/autodiff/MultisegmentWell_impl.hpp b/opm/autodiff/MultisegmentWell_impl.hpp index e101e21af..fd3a1362e 100644 --- a/opm/autodiff/MultisegmentWell_impl.hpp +++ b/opm/autodiff/MultisegmentWell_impl.hpp @@ -1645,7 +1645,8 @@ namespace Opm template void MultisegmentWell:: - checkWellOperability(const Simulator& ebos_simulator) + checkWellOperability(const Simulator& /* ebos_simulator */, + const WellState& /* well_state */) { const std::string msg = "Support of well operatability checking for mutlisegment wells is not implemented " "yet, checkWellOperability() for " + name() + " will do nothing"; diff --git a/opm/autodiff/StandardWell.hpp b/opm/autodiff/StandardWell.hpp index 4d9e36cb8..b5ff47380 100644 --- a/opm/autodiff/StandardWell.hpp +++ b/opm/autodiff/StandardWell.hpp @@ -350,7 +350,8 @@ namespace Opm // check whether the well is operable under the current reservoir condition // mostly related to BHP limit and THP limit - virtual void checkWellOperability(const Simulator& ebos_simulator) override; + virtual void checkWellOperability(const Simulator& ebos_simulator, + const WellState& well_state) override; // check whether the well is operable under BHP limit with current reservoir condition void checkOperabilityUnderBHPLimitProducer(const Simulator& ebos_simulator); @@ -369,6 +370,10 @@ namespace Opm // be able to produce/inject . bool allDrawDownWrongDirection(const Simulator& ebos_simulator) const; + // whether the well can produce / inject based on the current well state (bhp) + bool canProduceInjectWithCurrentBhp(const Simulator& ebos_simulator, + const WellState& well_state); + // turn on crossflow to avoid singular well equations // when the well is banned from cross-flow and the BHP is not properly initialized, // we turn on crossflow to avoid singular well equations. It can result in wrong-signed diff --git a/opm/autodiff/StandardWell_impl.hpp b/opm/autodiff/StandardWell_impl.hpp index b7983f411..5d944b626 100644 --- a/opm/autodiff/StandardWell_impl.hpp +++ b/opm/autodiff/StandardWell_impl.hpp @@ -440,7 +440,7 @@ namespace Opm WellState& well_state) { - checkWellOperability(ebosSimulator); + checkWellOperability(ebosSimulator, well_state); if (!this->isOperable()) return; @@ -1293,7 +1293,8 @@ namespace Opm template void StandardWell:: - checkWellOperability(const Simulator& ebos_simulator) + checkWellOperability(const Simulator& ebos_simulator, + const WellState& well_state) { // focusing on PRODUCER for now if (well_type_ == INJECTOR) { @@ -1315,13 +1316,12 @@ namespace Opm // checking whether the well can operate under the THP constraints. if (this->wellHasTHPConstraints()) { + this->operability_status_.has_thp_constaint = true; checkOperabilityUnderTHPLimitProducer(ebos_simulator); + this->operability_status_.can_produce_inject_with_current_bhp = + canProduceInjectWithCurrentBhp(ebos_simulator, well_state); } - // checking whether the well can not produce/inject - this->operability_status_.existing_drawdown_correct_direction = - ! allDrawDownWrongDirection(ebos_simulator); - const bool well_operable = this->operability_status_.isOperable(); if (!well_operable && old_well_operable) { @@ -1458,6 +1458,40 @@ namespace Opm + template + bool + StandardWell:: + canProduceInjectWithCurrentBhp(const Simulator& ebos_simulator, + const WellState& well_state) + { + const double bhp = well_state.bhp()[index_of_well_]; + std::vector well_rates; + computeWellRatesWithBhp(ebos_simulator, bhp, well_rates); + + const double sign = (well_type_ == PRODUCER) ? -1. : 1.; + const double threshold = sign * std::numeric_limits::min(); + + bool can_produce_inject = false; + for (const auto value : well_rates) { + if (well_type_ == PRODUCER && value < threshold) { + can_produce_inject = true; + break; + } else if (well_type_ == INJECTOR && value > threshold) { + can_produce_inject = true; + break; + } + } + + if (!can_produce_inject) { + OpmLog::debug(" well " + name() + " CANNOT produce or inejct "); + } + + return can_produce_inject; + } + + + + template bool diff --git a/opm/autodiff/WellInterface.hpp b/opm/autodiff/WellInterface.hpp index 546a556e3..272809588 100644 --- a/opm/autodiff/WellInterface.hpp +++ b/opm/autodiff/WellInterface.hpp @@ -223,7 +223,7 @@ namespace Opm void updatePerforatedCell(std::vector& is_cell_perforated); - virtual void checkWellOperability(const Simulator& ebos_simulator) = 0; + virtual void checkWellOperability(const Simulator& ebos_simulator, const WellState& well_state) = 0; // whether the well is operable bool isOperable() const; @@ -391,8 +391,8 @@ namespace Opm if (!operable_under_only_bhp_limit) { return false; } else { - return existing_drawdown_correct_direction && - (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()); + return ( (isOperableUnderBHPLimit() || isOperableUnderTHPLimit()) && + !(has_thp_constaint && !can_produce_inject_with_current_bhp) ); } } @@ -409,8 +409,8 @@ namespace Opm obey_thp_limit_under_bhp_limit = true; can_obtain_bhp_with_thp_limit = true; obey_bhp_limit_with_thp_limit = true; - // TODO: the following one might need to be treated differently - existing_drawdown_correct_direction = true; + can_produce_inject_with_current_bhp = true; + has_thp_constaint = false; } // whether the well can be operated under bhp limit @@ -425,13 +425,14 @@ namespace Opm // whether the well obey bhp limit when operated under thp limit bool obey_bhp_limit_with_thp_limit = true; - // there is some drawdown with correct sign/direction - // if all the drawdown are with wrong sign/direction, it means producer can not produce - // and injector can not inject. - // TODO: even there exist some drawdown with correct direction, it is still possible that - // producer can not produce and injector can not inject if the crossflow is allowed, since - // the well rate is the sum of the rates from all the perofrations - bool existing_drawdown_correct_direction = true; + // TODO: the following criterion is based on the current state of + // the well, we consider it is a numerical criterion. + // at the moment, we only apply it with well has THP constraint. + // whether the well can produce / inject with the current bhp of the well + // it might be updated with other criterion with investigation with more cases. + bool can_produce_inject_with_current_bhp = true; + // whether the well has a THP constraint + bool has_thp_constaint = false; }; }