158BlackoilWellModelNetwork<TypeTag>::
159computeWellGroupThp(
const double dt,
DeferredLogger& local_deferredLogger)
162 const int reportStepIdx = well_model_.simulator().episodeIndex();
163 const auto& network = well_model_.schedule()[reportStepIdx].network();
164 const auto& balance = well_model_.schedule()[reportStepIdx].network_balance();
165 const Scalar thp_tolerance = balance.thp_tolerance();
167 if (!network.active()) {
171 auto& well_state = well_model_.wellState();
172 auto& group_state = well_model_.groupState();
174 bool well_group_thp_updated =
false;
175 for (
const std::string& nodeName : network.node_names()) {
176 const bool has_choke = network.node(nodeName).as_choke();
178 const auto& summary_state = well_model_.simulator().vanguard().summaryState();
179 const Group& group = well_model_.schedule().getGroup(nodeName, reportStepIdx);
182 std::vector<Scalar> resv_coeff(Indices::numPhases, 1.0);
184 const auto ctrl = group.productionControls(summary_state);
185 auto cmode_tmp = ctrl.cmode;
186 Scalar target_tmp{0.0};
187 bool fld_none =
false;
188 if (cmode_tmp == Group::ProductionCMode::FLD || cmode_tmp == Group::ProductionCMode::NONE) {
192 const Scalar efficiencyFactor = 1.0;
193 const Group& parentGroup = well_model_.schedule().getGroup(group.parent(), reportStepIdx);
194 auto target = well_model_.groupStateHelper().
195 getAutoChokeGroupProductionTargetRate(group,
199 target_tmp = target.first;
200 cmode_tmp = target.second;
203 TargetCalculatorType tcalc{well_model_.groupStateHelper(), resv_coeff, group};
207 target_tmp = well_model_.groupStateHelper().getProductionGroupTarget(group);
210 const Scalar orig_target = target_tmp;
212 auto mismatch = [&] (
auto group_thp) {
213 Scalar group_rate(0.0);
215 for (
auto& well : well_model_) {
216 std::string well_name = well->name();
217 auto& ws = well_state.well(well_name);
218 if (group.hasWell(well_name)) {
219 well->setDynamicThpLimit(group_thp);
220 const Well& well_ecl = well_model_.eclWells()[well->indexOfWell()];
221 const auto inj_controls = Well::InjectionControls(0);
222 const auto prod_controls = well_ecl.productionControls(summary_state);
223 well->iterateWellEqWithSwitching(well_model_.simulator(),
227 well_model_.groupStateHelper(),
232 rate = -tcalc.calcModeRateFromRates(ws.surface_rates);
236 return (group_rate - orig_target)/orig_target;
239 const auto upbranch = network.uptree_branch(nodeName);
240 const auto it = this->node_pressures_.find((*upbranch).uptree_node());
241 const Scalar nodal_pressure = it->second;
242 Scalar well_group_thp = nodal_pressure;
244 std::optional<Scalar> autochoke_thp;
245 if (
auto iter = this->well_group_thp_calc_.find(nodeName);
246 iter != this->well_group_thp_calc_.end())
248 autochoke_thp = this->well_group_thp_calc_.at(nodeName);
253 std::array<Scalar, 2> range_initial;
254 if (!autochoke_thp.has_value()){
255 Scalar min_thp, max_thp;
257 std::string node_name = nodeName;
258 while (!network.node(node_name).terminal_pressure().has_value()) {
259 auto branch = network.uptree_branch(node_name).value();
260 node_name = branch.uptree_node();
262 min_thp = network.node(node_name).terminal_pressure().value();
263 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch, min_thp, max_thp);
266 std::array<Scalar, 2> range = {Scalar{0.9}*min_thp, Scalar{1.1}*max_thp};
267 std::optional<Scalar> appr_sol;
268 WellBhpThpCalculatorType::bruteForceBracketCommonTHP(mismatch,
274 local_deferredLogger);
277 range_initial = {min_thp, max_thp};
280 if (!autochoke_thp.has_value() || autochoke_thp.value() > nodal_pressure) {
283 std::array<Scalar, 2> range = autochoke_thp.has_value() ?
284 std::array<Scalar, 2>{Scalar{0.9} * autochoke_thp.value(),
285 Scalar{1.1} * autochoke_thp.value()} : range_initial;
287 std::optional<Scalar> approximate_solution;
288 const Scalar tolerance1 = thp_tolerance;
289 local_deferredLogger.debug(
"Using brute force search to bracket the group THP");
290 const bool finding_bracket = WellBhpThpCalculatorType::
291 bruteForceBracketCommonTHP(mismatch,
295 approximate_solution,
297 local_deferredLogger);
299 if (approximate_solution.has_value()) {
300 autochoke_thp = *approximate_solution;
301 local_deferredLogger.debug(
"Approximate group THP value found: " +
302 std::to_string(autochoke_thp.value()));
303 }
else if (finding_bracket) {
304 const Scalar tolerance2 = thp_tolerance;
305 const int max_iteration_solve = 100;
307 autochoke_thp = RegulaFalsiBisection<ThrowOnError>::
314 local_deferredLogger.debug(
" bracket = [" + std::to_string(low) +
", " +
315 std::to_string(high) +
"], " +
316 "iteration = " + std::to_string(iteration));
317 local_deferredLogger.debug(
"Group THP value = " + std::to_string(autochoke_thp.value()));
319 autochoke_thp.reset();
320 local_deferredLogger.debug(
"Group THP solve failed due to bracketing failure");
323 if (autochoke_thp.has_value()) {
324 well_group_thp_calc_[nodeName] = autochoke_thp.value();
328 well_group_thp = std::max(autochoke_thp.value(), nodal_pressure);
331 for (
auto& well : well_model_) {
332 std::string well_name = well->name();
334 if (well->isInjector() || !well->wellEcl().predictionMode())
337 if (group.hasWell(well_name)) {
338 well->setDynamicThpLimit(well_group_thp);
340 const auto& ws = well_model_.wellState().well(well->indexOfWell());
341 const bool thp_is_limit = ws.production_cmode == Well::ProducerCMode::THP;
343 well->prepareWellBeforeAssembling(well_model_.simulator(),
345 well_model_.groupStateHelper(),
346 well_model_.wellState());
351 const auto& current_well_group_thp = group_state.is_autochoke_group(nodeName)
352 ? group_state.well_group_thp(nodeName)
354 if (std::abs(current_well_group_thp - well_group_thp) > balance.pressure_tolerance()) {
355 well_group_thp_updated =
true;
356 group_state.update_well_group_thp(nodeName, well_group_thp);
360 return well_group_thp_updated;