23 #ifndef OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED 24 #define OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED 27 #ifndef OPM_BLACKOILWELLMODEL_NETWORK_HEADER_INCLUDED 29 #include <opm/simulators/wells/BlackoilWellModelNetwork.hpp> 32 #include <opm/common/TimingMacros.hpp> 33 #include <opm/common/utility/numeric/RootFinders.hpp> 35 #include <opm/input/eclipse/Units/Units.hpp> 37 #include <opm/simulators/utils/DeferredLoggingErrorHelpers.hpp> 38 #include <opm/simulators/wells/BlackoilWellModel.hpp> 39 #include <opm/simulators/wells/TargetCalculator.hpp> 40 #include <opm/simulators/wells/WellBhpThpCalculator.hpp> 42 #include <fmt/format.h> 46 template<
typename TypeTag>
47 BlackoilWellModelNetwork<TypeTag>::
48 BlackoilWellModelNetwork(BlackoilWellModel<TypeTag>& well_model)
49 : BaseType(well_model)
50 , well_model_(well_model)
53 template<
typename TypeTag>
55 BlackoilWellModelNetwork<TypeTag>::
56 doPreStepRebalance(DeferredLogger& deferred_logger)
59 const double dt = well_model_.simulator().timeStepSize();
62 auto& well_state = well_model_.wellState();
64 const bool changed_well_group =
65 well_model_.updateWellControlsAndNetwork(
true,
68 well_model_.assembleWellEqWithoutIteration(dt);
69 const bool converged =
70 well_model_.getWellConvergence(well_model_.B_avg(),
true).converged() &&
73 OPM_BEGIN_PARALLEL_TRY_CATCH();
74 for (
auto& well : this->well_model_) {
75 well->solveEqAndUpdateWellState(well_model_.simulator(),
76 well_model_.groupStateHelper(),
79 OPM_END_PARALLEL_TRY_CATCH(
"BlackoilWellModelNetwork::doPreStepRebalance() failed: ",
80 well_model_.simulator().vanguard().grid().comm());
83 deferred_logger.warning(
"Initial (pre-step) network balance did not converge.");
87 template<
typename TypeTag>
88 std::tuple<bool, typename BlackoilWellModelNetwork<TypeTag>::Scalar>
89 BlackoilWellModelNetwork<TypeTag>::
90 update(
const bool mandatory_network_balance,
91 DeferredLogger& deferred_logger,
92 const bool relax_network_tolerance)
95 const int episodeIdx = well_model_.simulator().episodeIndex();
96 const auto& network = well_model_.schedule()[episodeIdx].network();
97 if (!well_model_.wellsActive() && !network.active()) {
101 const auto& comm = well_model_.simulator().vanguard().grid().comm();
104 Scalar network_imbalance = 0.0;
105 bool more_network_update =
false;
106 if (this->shouldBalance(episodeIdx) || mandatory_network_balance) {
107 OPM_TIMEBLOCK(BalanceNetwork);
108 const double dt = well_model_.simulator().timeStepSize();
110 const bool well_group_thp_updated = computeWellGroupThp(dt, deferred_logger);
111 const int max_number_of_sub_iterations =
112 well_model_.param().network_max_sub_iterations_;
113 const Scalar network_pressure_update_damping_factor =
114 well_model_.param().network_pressure_update_damping_factor_;
115 const Scalar network_max_pressure_update =
116 well_model_.param().network_max_pressure_update_in_bars_ * unit::barsa;
117 bool more_network_sub_update =
false;
118 for (
int i = 0; i < max_number_of_sub_iterations; i++) {
119 const auto local_network_imbalance =
120 this->updatePressures(episodeIdx,
121 network_pressure_update_damping_factor,
122 network_max_pressure_update);
123 network_imbalance = comm.max(local_network_imbalance);
124 const auto& balance = well_model_.schedule()[episodeIdx].network_balance();
125 constexpr Scalar relaxation_factor = 10.0;
126 const Scalar tolerance =
127 relax_network_tolerance ? relaxation_factor * balance.pressure_tolerance()
128 : balance.pressure_tolerance();
129 more_network_sub_update = this->active() && network_imbalance > tolerance;
130 #ifdef RESERVOIR_COUPLING_ENABLED 131 if (well_model_.isReservoirCouplingMaster()) {
135 well_model_.rescoupHelper().maybeExchangeNetworkSubIterationWithSlaves();
138 if (!more_network_sub_update) {
142 for (
const auto& well : well_model_) {
143 if (well->isInjector() || !well->wellEcl().predictionMode()) {
147 const auto it = this->node_pressures_.find(well->wellEcl().groupName());
148 if (it != this->node_pressures_.end()) {
149 well->prepareWellBeforeAssembling(well_model_.simulator(),
151 well_model_.groupStateHelper(),
152 well_model_.wellState());
155 well_model_.updateAndCommunicateGroupData(episodeIdx,
true);
157 more_network_update = more_network_sub_update || well_group_thp_updated;
159 return { more_network_update, network_imbalance };
162 template <
typename TypeTag>
168 const int reportStepIdx = well_model_.simulator().episodeIndex();
169 const auto& network = well_model_.schedule()[reportStepIdx].network();
170 const auto& balance = well_model_.schedule()[reportStepIdx].network_balance();
171 const Scalar thp_tolerance = balance.thp_tolerance();
173 if (!network.active()) {
177 auto& well_state = well_model_.wellState();
178 auto& group_state = well_model_.groupState();
180 bool well_group_thp_updated =
false;
181 for (
const std::string& nodeName : network.node_names()) {
182 const bool has_choke = network.node(nodeName).as_choke();
184 const auto& summary_state = well_model_.simulator().vanguard().summaryState();
185 const Group& group = well_model_.schedule().getGroup(nodeName, reportStepIdx);
188 std::vector<Scalar> resv_coeff(Indices::numPhases, 1.0);
190 const auto ctrl = group.productionControls(summary_state);
191 auto cmode_tmp = ctrl.cmode;
192 Scalar target_tmp{0.0};
193 bool fld_none =
false;
194 if (cmode_tmp == Group::ProductionCMode::FLD || cmode_tmp == Group::ProductionCMode::NONE) {
198 const Scalar efficiencyFactor = 1.0;
199 const Group& parentGroup = well_model_.schedule().getGroup(group.parent(), reportStepIdx);
200 auto target = well_model_.groupStateHelper().
201 getAutoChokeGroupProductionTargetRate(group,
205 target_tmp = target.first;
206 cmode_tmp = target.second;
209 TargetCalculatorType tcalc{well_model_.groupStateHelper(), resv_coeff, group};
213 target_tmp = well_model_.groupStateHelper().getProductionGroupTarget(group);
216 const Scalar orig_target = target_tmp;
218 auto mismatch = [&] (
auto group_thp) {
219 Scalar group_rate(0.0);
221 for (
auto& well : well_model_) {
222 std::string well_name = well->name();
223 auto& ws = well_state.well(well_name);
224 if (group.hasWell(well_name)) {
225 well->setDynamicThpLimit(group_thp);
226 const Well& well_ecl = well_model_.eclWells()[well->indexOfWell()];
227 const auto inj_controls = Well::InjectionControls(0);
228 const auto prod_controls = well_ecl.productionControls(summary_state);
229 well->iterateWellEqWithSwitching(well_model_.simulator(),
233 well_model_.groupStateHelper(),
238 rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
242 return (group_rate - orig_target)/orig_target;
245 const auto upbranch = network.uptree_branch(nodeName);
246 const auto it = this->node_pressures_.find((*upbranch).uptree_node());
247 const Scalar nodal_pressure = it->second;
248 Scalar well_group_thp = nodal_pressure;
250 std::optional<Scalar> autochoke_thp;
251 if (
auto iter = this->well_group_thp_calc_.find(nodeName);
252 iter != this->well_group_thp_calc_.end())
254 autochoke_thp = this->well_group_thp_calc_.at(nodeName);
259 std::array<Scalar, 2> range_initial;
260 if (!autochoke_thp.has_value()){
261 Scalar min_thp, max_thp;
263 std::string node_name = nodeName;
264 while (!network.node(node_name).terminal_pressure().has_value()) {
265 auto branch = network.uptree_branch(node_name).value();
266 node_name = branch.uptree_node();
268 min_thp = network.node(node_name).terminal_pressure().value();
269 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
272 std::array<Scalar, 2> range = {Scalar{0.9}*min_thp, Scalar{1.1}*max_thp};
273 std::optional<Scalar> appr_sol;
274 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch,
280 local_deferredLogger);
283 range_initial = {min_thp, max_thp};
286 if (!autochoke_thp.has_value() || autochoke_thp.value() > nodal_pressure) {
289 std::array<Scalar, 2> range = autochoke_thp.has_value() ?
290 std::array<Scalar, 2>{Scalar{0.9} * autochoke_thp.value(),
291 Scalar{1.1} * autochoke_thp.value()} : range_initial;
293 std::optional<Scalar> approximate_solution;
294 const Scalar tolerance1 = thp_tolerance;
295 local_deferredLogger.debug(
"Using brute force search to bracket the group THP");
296 const bool finding_bracket = WellBhpThpCalculatorType::
297 bruteForceBracketCommonTHP(mismatch,
301 approximate_solution,
303 local_deferredLogger);
305 if (approximate_solution.has_value()) {
306 autochoke_thp = *approximate_solution;
307 local_deferredLogger.debug(
"Approximate group THP value found: " +
308 std::to_string(autochoke_thp.value()));
309 }
else if (finding_bracket) {
310 const Scalar tolerance2 = thp_tolerance;
311 const int max_iteration_solve = 100;
313 autochoke_thp = RegulaFalsiBisection<ThrowOnError>::
320 local_deferredLogger.debug(
" bracket = [" + std::to_string(low) +
", " +
321 std::to_string(high) +
"], " +
322 "iteration = " + std::to_string(iteration));
323 local_deferredLogger.debug(
"Group THP value = " + std::to_string(autochoke_thp.value()));
325 autochoke_thp.reset();
326 local_deferredLogger.debug(
"Group THP solve failed due to bracketing failure");
329 if (autochoke_thp.has_value()) {
330 well_group_thp_calc_[nodeName] = autochoke_thp.value();
334 well_group_thp = std::max(autochoke_thp.value(), nodal_pressure);
337 for (
auto& well : well_model_) {
338 std::string well_name = well->name();
340 if (well->isInjector() || !well->wellEcl().predictionMode())
343 if (group.hasWell(well_name)) {
344 well->setDynamicThpLimit(well_group_thp);
346 const auto& ws = well_model_.wellState().well(well->indexOfWell());
347 const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP;
349 well->prepareWellBeforeAssembling(well_model_.simulator(),
351 well_model_.groupStateHelper(),
352 well_model_.wellState());
357 const auto& current_well_group_thp = group_state.is_autochoke_group(nodeName)
358 ? group_state.well_group_thp(nodeName)
360 if (std::abs(current_well_group_thp - well_group_thp) > balance.pressure_tolerance()) {
361 well_group_thp_updated =
true;
362 group_state.update_well_group_thp(nodeName, well_group_thp);
366 return well_group_thp_updated;
371 #endif // OPM_BLACKOILWELLMODEL_NETWORK_IMPL_HEADER_INCLUDED bool computeWellGroupThp(const double dt, DeferredLogger &local_deferredLogger)
This function is to be used for well groups in an extended network that act as a subsea manifold The ...
Definition: BlackoilWellModelNetwork_impl.hpp:165
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
Definition: DeferredLogger.hpp:56
Based on a group control mode, extract or calculate rates, and provide other conveniences.
Definition: TargetCalculator.hpp:38
Class for computing BHP limits.
Definition: WellBhpThpCalculator.hpp:41