20 #include <fmt/format.h> 21 #include <fmt/ranges.h> 23 #include <opm/common/ErrorMacros.hpp> 24 #include <opm/common/OpmLog/OpmLog.hpp> 26 #include <opm/material/fluidstates/CompositionalFluidState.hpp> 28 #include <opm/input/eclipse/EclipseState/Tables/StandardCond.hpp> 34 template <
typename TypeTag>
36 CompWell(
const Well& well,
38 const std::vector<CompConnectionData>& well_connection_data)
39 : CompWellInterface<TypeTag>(well, index_of_well, well_connection_data)
43 template <
typename TypeTag>
49 well_equations_.init(this->number_of_connection_, this->well_cells_);
52 template <
typename TypeTag>
55 calculateExplicitQuantities(
const Simulator& simulator,
56 const SingleWellState& well_state)
58 updatePrimaryVariables(simulator, well_state);
62 auto fluid_state_scalar = this->primary_variables_.template toFluidState<Scalar>();
64 flashFluidState_(fluid_state_scalar);
70 template <
typename TypeTag>
73 updatePrimaryVariables(
const Simulator& ,
74 const SingleWellState& well_state)
76 this->primary_variables_.update(well_state);
79 template <
typename TypeTag>
82 updateSecondaryQuantities(
const Simulator& simulator)
85 updateSurfaceQuantities(simulator);
89 template <
typename TypeTag>
95 auto fluid_state = this->primary_variables_.template toFluidState<EvalWell>();
97 flashFluidState_(fluid_state);
101 EvalWell total_mass = 0.;
102 for (
unsigned compidx = 0; compidx < FluidSystem::numComponents; ++compidx) {
103 total_mass += this->new_component_masses_[compidx];
106 const auto& so = fluid_state.saturation(FluidSystem::oilPhaseIdx);
107 const auto& sg = fluid_state.saturation(FluidSystem::gasPhaseIdx);
108 const auto& density_oil = fluid_state.density(FluidSystem::oilPhaseIdx);
109 const auto& density_gas = fluid_state.density(FluidSystem::gasPhaseIdx);
111 fluid_density_ = density_oil * so + density_gas * sg;
117 for (
unsigned compidx = 0; compidx < FluidSystem::numComponents; ++compidx) {
118 mass_fractions_[compidx] = this->new_component_masses_[compidx] / total_mass;
122 template <
typename TypeTag>
125 updateSurfaceQuantities(
const Simulator& simulator)
127 const auto& surface_cond = simulator.vanguard().eclState().getTableManager().stCond();
128 if (this->well_ecl_.isInjector()) {
129 const auto& inj_composition = this->well_ecl_.getInjectionProperties().gasInjComposition();
130 FluidState<Scalar> fluid_state;
131 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; ++comp_idx) {
132 fluid_state.setMoleFraction(comp_idx, std::max(inj_composition[comp_idx], 1.e-10));
134 updateSurfaceCondition_(surface_cond, fluid_state);
137 auto fluid_state = this->primary_variables_.template toFluidState<EvalWell>();
138 updateSurfaceCondition_(surface_cond, fluid_state);
142 template <
typename TypeTag>
145 calculateSingleConnectionRate(
const Simulator& simulator,
146 std::vector<EvalWell>& con_rates)
const 148 constexpr
int con_idx = 0;
149 constexpr
int np = 2;
150 const EvalWell& bhp = this->primary_variables_.getBhp();
151 const unsigned cell_idx = this->well_cells_[0];
152 const auto& int_quantities = simulator.problem().model().cachedIntensiveQuantities(cell_idx, 0);
153 assert(int_quantities);
154 std::vector<EvalWell> mob(np, 0.);
155 getMobility(simulator, con_idx, mob);
157 const Scalar tw = this->well_index_[0];
159 const auto& fluid_state = int_quantities->fluidState();
161 const EvalWell cell_pressure = PrimaryVariables::extendEval(fluid_state.pressure(FluidSystem::oilPhaseIdx));
162 const EvalWell drawdown = cell_pressure - bhp;
165 std::vector<EvalWell> cq_v(np);
166 for (
unsigned phase_idx = 0; phase_idx < np; ++phase_idx) {
167 cq_v[phase_idx] = - mob[phase_idx] * tw * drawdown;
168 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; comp_idx++) {
169 const EvalWell density = PrimaryVariables::extendEval(fluid_state.density(phase_idx));
170 const EvalWell mass_fraction = PrimaryVariables::extendEval(fluid_state.massFraction(phase_idx, comp_idx));
171 con_rates[comp_idx] += cq_v[phase_idx] * density * mass_fraction;
175 EvalWell total_mobility = 0.;
176 for (
unsigned phase_idx = 0; phase_idx < np; ++phase_idx) {
177 total_mobility += mob[phase_idx];
179 EvalWell cq_v = - total_mobility * tw * drawdown;
180 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; comp_idx++) {
181 con_rates[comp_idx] = cq_v * fluid_density_ * mass_fractions_[comp_idx];
186 template <
typename TypeTag>
187 void CompWell<TypeTag>::
188 getMobility(
const Simulator& simulator,
189 const int connectin_idx,
190 std::vector<EvalWell>& mob)
const 192 const unsigned cell_idx = this->well_cells_[connectin_idx];
193 const auto& int_quants = simulator.problem().model().cachedIntensiveQuantities(cell_idx, 0);
195 const auto& material_law_manager = simulator.problem().materialLawManager();
199 const int satid = this->saturation_table_number_[connectin_idx] - 1;
200 const int satid_elem = material_law_manager->satnumRegionIdx(cell_idx);
202 if (satid == satid_elem) {
203 for (
unsigned phase_idx = 0; phase_idx < FluidSystem::numPhases; ++phase_idx) {
204 mob[phase_idx] = PrimaryVariables::extendEval(int_quants->mobility(phase_idx));
208 OPM_THROW(std::logic_error,
209 "CompWell::getMobility: a connection saturation table differing from the " 210 "cell saturation region is not supported yet");
215 template <
typename TypeTag>
218 assembleWellEq(
const Simulator& simulator,
219 const SingleWellState& well_state,
222 this->well_equations_.clear();
224 this->updateSecondaryQuantities(simulator);
226 assembleSourceTerm(dt);
228 std::vector<EvalWell> connection_rates(FluidSystem::numComponents, 0.);
229 calculateSingleConnectionRate(simulator, connection_rates);
231 auto& con_rates = this->connectionRates_[0];
232 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; ++comp_idx) {
233 con_rates[comp_idx] = PrimaryVariables::restrictEval(connection_rates[comp_idx]);
238 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; ++comp_idx) {
240 this->well_equations_.residual()[0][comp_idx] += connection_rates[comp_idx].value();
241 for (
unsigned pvIdx = 0; pvIdx < PrimaryVariables::numWellEq; ++pvIdx) {
243 this->well_equations_.C()[0][0][pvIdx][comp_idx] -= connection_rates[comp_idx].derivative(pvIdx + PrimaryVariables::numResEq);
244 this->well_equations_.D()[0][0][comp_idx][pvIdx] += connection_rates[comp_idx].derivative(pvIdx + PrimaryVariables::numResEq);
247 for (
unsigned pvIdx = 0; pvIdx < PrimaryVariables::numResEq; ++pvIdx) {
248 this->well_equations_.B()[0][0][comp_idx][pvIdx] += connection_rates[comp_idx].derivative(pvIdx);
252 const auto& summary_state = simulator.vanguard().summaryState();
253 assembleControlEq(well_state, summary_state);
255 this->well_equations_.invert();
262 template <
typename TypeTag>
265 assembleControlEq(
const SingleWellState& well_state,
266 const SummaryState& summary_state)
269 if (this->well_ecl_.isProducer()) {
270 const auto prod_controls = this->well_ecl_.productionControls(summary_state);
271 assembleControlEqProd(well_state, prod_controls, control_eq);
273 const auto inj_controls = this->well_ecl_.injectionControls(summary_state);
274 assembleControlEqInj(well_state, inj_controls, control_eq);
277 this->well_equations_.residual()[0][PrimaryVariables::Bhp] = control_eq.value();
278 for (
unsigned pvIdx = 0; pvIdx < PrimaryVariables::numWellEq; ++pvIdx) {
279 this->well_equations_.D()[0][0][PrimaryVariables::Bhp][pvIdx] = control_eq.derivative(pvIdx + PrimaryVariables::numResEq);
283 template <
typename TypeTag>
286 assembleControlEqProd(
const SingleWellState& well_state,
287 const Well::ProductionControls& prod_controls,
288 EvalWell& control_eq)
const 291 const auto current = well_state.production_cmode;
293 const auto& surface_cond = this->surface_conditions_;
296 case WellProducerCMode::BHP : {
297 const Scalar bhp_limit = prod_controls.bhp_limit;
298 control_eq = this->primary_variables_.getBhp() - bhp_limit;
301 case WellProducerCMode::ORAT : {
302 const Scalar rate_target = prod_controls.oil_rate;
303 const EvalWell& total_rate = this->primary_variables_.getTotalRate();
304 const EvalWell oil_rate = total_rate * surface_cond.volume_fractions_[FluidSystem::oilPhaseIdx];
305 control_eq = oil_rate + rate_target;
308 case WellProducerCMode::GRAT : {
309 const Scalar rate_target = prod_controls.gas_rate;
310 const EvalWell& total_rate = this->primary_variables_.getTotalRate();
311 const EvalWell gas_rate = total_rate * surface_cond.volume_fractions_[FluidSystem::gasPhaseIdx];
312 control_eq = gas_rate + rate_target;
316 OPM_THROW(std::logic_error,
"only handles BHP, ORAT and GRAT control for producers for now");
320 template <
typename TypeTag>
323 assembleControlEqInj(
const SingleWellState& well_state,
324 const Well::InjectionControls& inj_controls,
325 EvalWell& control_eq)
const 328 const auto current = well_state.injection_cmode;
331 case WellInjectorCMode::BHP : {
332 const Scalar bhp_limit = inj_controls.bhp_limit;
333 control_eq = this->primary_variables_.getBhp() - bhp_limit;
336 case WellInjectorCMode::RATE : {
337 const Scalar rate_target = inj_controls.surface_rate;
338 const EvalWell& injection_rate = this->primary_variables_.getTotalRate();
339 control_eq = injection_rate - rate_target;
343 OPM_THROW(std::logic_error,
"only handles BHP and RATE control for injectors for now");
348 template <
typename TypeTag>
351 assembleSourceTerm(
const Scalar dt)
354 const EvalWell total_surface_rate = this->primary_variables_.getTotalRate();
355 const EvalWell density = this->surface_conditions_.density();
356 const EvalWell total_mass_rate = total_surface_rate * density;
357 std::array<EvalWell, FluidSystem::numComponents> component_mass_rates;
358 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; ++comp_idx) {
359 component_mass_rates[comp_idx] = total_mass_rate * this->surface_conditions_.massFraction(comp_idx);
362 for (
unsigned comp_idx = 0; comp_idx < FluidSystem::numComponents; ++comp_idx) {
363 const EvalWell residual = (this->new_component_masses_[comp_idx] - this->component_masses_[comp_idx]) / dt - component_mass_rates[comp_idx];
365 for (
int pvIdx = 0; pvIdx < PrimaryVariables::numWellEq; ++pvIdx) {
366 this->well_equations_.D()[0][0][comp_idx][pvIdx] += residual.derivative(pvIdx + PrimaryVariables::numResEq);
368 this->well_equations_.residual()[0][comp_idx] += residual.value();
372 template <
typename TypeTag>
375 iterateWellEq(
const Simulator& simulator,
377 SingleWellState& well_state)
379 constexpr
int max_iter = 200;
382 bool converged =
false;
385 updateWellControl(simulator.vanguard().summaryState(), well_state);
387 assembleWellEq(simulator, well_state, dt);
390 converged = this->getConvergence();
398 solveEqAndUpdateWellState(well_state);
399 }
while (it < max_iter);
403 template <
typename TypeTag>
406 solveEqAndUpdateWellState(SingleWellState& well_state)
408 BVectorWell dx_well(1);
410 this->well_equations_.solve(dx_well);
412 this->updateWellState(dx_well, well_state);
415 template<
typename TypeTag>
418 apply(BVector& r)
const 420 this->well_equations_.apply(r);
423 template <
typename TypeTag>
426 recoverWellSolutionAndUpdateWellState(
const BVector& x,
427 SingleWellState& well_state)
431 this->well_equations_.recoverSolutionWell(x, xw);
433 updateWellState(xw, well_state);
436 template <
typename TypeTag>
439 updatePrimaryVariablesNewton(
const BVectorWell& dwells)
441 this->primary_variables_.updateNewton(dwells);
444 template <
typename TypeTag>
447 updateWellState(
const CompWell::BVectorWell& xw,
448 SingleWellState& well_state)
450 updatePrimaryVariablesNewton(xw);
451 updateWellStateFromPrimaryVariables(well_state);
454 template <
typename TypeTag>
457 updateWellStateFromPrimaryVariables(SingleWellState& well_state)
const 459 well_state.bhp = this->primary_variables_.getBhp().value();
461 auto& total_molar_fractions = well_state.total_molar_fractions;
462 const auto fluid_state = this->primary_variables_.template toFluidState<Scalar>();
463 for (
int comp_idx = 0; comp_idx < FluidSystem::numComponents - 1; ++comp_idx) {
464 total_molar_fractions[comp_idx] = fluid_state.moleFraction(comp_idx);
466 const Scalar total_rate = this->primary_variables_.getTotalRate().value();
467 auto& surface_phase_rates = well_state.surface_phase_rates;
468 if (well_state.producer) {
469 const auto& surface_cond = this->surface_conditions_;
470 for (
int p = 0; p < FluidSystem::numPhases; ++p) {
471 surface_phase_rates[p] = total_rate * getValue(surface_cond.volume_fractions_[p]);
475 surface_phase_rates[FluidSystem::gasPhaseIdx] = total_rate;
479 template <
typename TypeTag>
482 getConvergence()
const 484 bool converged =
true;
485 for (
const auto& val : this->well_equations_.residual()[0]) {
486 converged = converged && (std::abs(val) < 1.e-8);
491 template <
typename TypeTag>
494 addWellContributions(SparseMatrixAdapter&)
const 499 template <
typename TypeTag>
502 updateWellControl(
const SummaryState& summary_state,
503 SingleWellState& well_state)
const 506 if (this->well_ecl_.isInjector()) {
507 from = WellInjectorCMode2String(well_state.injection_cmode);
509 from = WellProducerCMode2String(well_state.production_cmode);
511 bool changed =
false;
512 if (this->well_ecl_.isProducer()) {
513 const auto production_controls = this->well_ecl_.productionControls(summary_state);
514 const auto current_control = well_state.production_cmode;
516 if (production_controls.hasControl(Well::ProducerCMode::BHP) && current_control != WellProducerCMode::BHP) {
517 const Scalar bhp_limit = production_controls.bhp_limit;
518 const Scalar current_bhp = well_state.bhp;
519 if (current_bhp < bhp_limit) {
520 well_state.bhp = bhp_limit;
521 well_state.production_cmode = WellProducerCMode::BHP;
526 if (!changed && production_controls.hasControl(Well::ProducerCMode::ORAT) && current_control != WellProducerCMode::ORAT) {
527 const Scalar current_rate = -well_state.surface_phase_rates[FluidSystem::oilPhaseIdx];
528 if (current_rate > production_controls.oil_rate) {
529 well_state.production_cmode = WellProducerCMode::ORAT;
534 if (!changed && production_controls.hasControl(Well::ProducerCMode::WRAT) && current_control != WellProducerCMode::WRAT) {
535 const Scalar current_rate = -well_state.surface_phase_rates[FluidSystem::waterPhaseIdx];
536 if (current_rate > production_controls.water_rate) {
537 well_state.production_cmode = WellProducerCMode::WRAT;
542 if (!changed && production_controls.hasControl(Well::ProducerCMode::GRAT) && current_control != WellProducerCMode::GRAT) {
543 const Scalar current_rate = -well_state.surface_phase_rates[FluidSystem::gasPhaseIdx];
544 if (current_rate > production_controls.gas_rate) {
545 well_state.production_cmode = WellProducerCMode::GRAT;
550 const auto injection_controls = this->well_ecl_.injectionControls(summary_state);
551 const auto current_control = well_state.injection_cmode;
552 if (injection_controls.hasControl(Well::InjectorCMode::BHP) && current_control != WellInjectorCMode::BHP) {
553 const Scalar bhp_limit = injection_controls.bhp_limit;
554 const Scalar current_bhp = well_state.bhp;
555 OpmLog::debug(fmt::format(
"Well {} BHP control check: current_bhp={:.6e}, bhp_limit={:.6e}, exceeds_limit={}",
556 this->well_ecl_.name(), current_bhp, bhp_limit, (current_bhp > bhp_limit)));
557 if (current_bhp > bhp_limit) {
558 well_state.bhp = bhp_limit;
559 well_state.injection_cmode = WellInjectorCMode::BHP;
563 if (!changed && injection_controls.hasControl(Well::InjectorCMode::RATE) && current_control != WellInjectorCMode::RATE) {
565 const Scalar rate_limit = injection_controls.surface_rate;
567 const Scalar current_rate = std::accumulate(well_state.surface_phase_rates.begin(),
568 well_state.surface_phase_rates.end(), 0.0);
569 OpmLog::debug(fmt::format(
"Well {} RATE control check: current_rate={:.6e}, rate_limit={:.6e}, bhp={:.6e}, phase_rates=[{}]",
570 this->well_ecl_.name(), current_rate, rate_limit, well_state.bhp,
571 fmt::join(well_state.surface_phase_rates,
", ")));
572 if (current_rate > rate_limit) {
573 OpmLog::debug(fmt::format(
"Well {} RATE control TRIGGERED: current_rate={:.6e} > rate_limit={:.6e}",
574 this->well_ecl_.name(), current_rate, rate_limit));
575 well_state.injection_cmode = WellInjectorCMode::RATE;
583 if (this->well_ecl_.isInjector()) {
584 to = WellInjectorCMode2String(well_state.injection_cmode);
586 to = WellProducerCMode2String(well_state.production_cmode);
588 OpmLog::info(fmt::format(
"Well {} changed control from {} to {} \n", this->well_ecl_.name(), from, to));
592 template <
typename TypeTag>
593 template <
typename T>
596 updateSurfaceCondition_(
const StandardCond& surface_cond, FluidState<T>& fluid_state)
598 static_assert(std::is_same_v<T, Scalar> || std::is_same_v<T, EvalWell>,
"Unsupported type in CompWell::updateSurfaceCondition_");
600 fluid_state.setTemperature(surface_cond.temperature);
601 fluid_state.setPressure(FluidSystem::oilPhaseIdx, surface_cond.pressure);
602 fluid_state.setPressure(FluidSystem::gasPhaseIdx, surface_cond.pressure);
604 for (
int i = 0; i < FluidSystem::numComponents; ++i) {
605 fluid_state.setKvalue(i, fluid_state.wilsonK_(i));
608 flashFluidState_(fluid_state);
610 for (
unsigned compidx = 0; compidx < FluidSystem::numComponents; ++compidx) {
611 this->surface_conditions_.mass_fractions_[FluidSystem::oilPhaseIdx][compidx] =
612 fluid_state.massFraction(FluidSystem::oilPhaseIdx, compidx);
613 this->surface_conditions_.mass_fractions_[FluidSystem::gasPhaseIdx][compidx] =
614 fluid_state.massFraction(FluidSystem::gasPhaseIdx, compidx);
616 const auto& density_oil = fluid_state.density(FluidSystem::oilPhaseIdx);
617 const auto& density_gas = fluid_state.density(FluidSystem::gasPhaseIdx);
618 this->surface_conditions_.surface_densities_[FluidSystem::oilPhaseIdx] = density_oil;
619 this->surface_conditions_.surface_densities_[FluidSystem::gasPhaseIdx] = density_gas;
620 this->surface_conditions_.volume_fractions_[FluidSystem::oilPhaseIdx] = fluid_state.saturation(FluidSystem::oilPhaseIdx);
621 this->surface_conditions_.volume_fractions_[FluidSystem::gasPhaseIdx] = fluid_state.saturation(FluidSystem::gasPhaseIdx);
624 template <
typename TypeTag>
625 template <
typename T>
628 flashFluidState_(FluidState<T>& fluid_state)
630 static_assert(std::is_same_v<T, Scalar> || std::is_same_v<T, EvalWell>,
"Unsupported type in CompWell::flashFluidState_");
std::array< T, FluidSystem::numComponents > wellboreComponentMasses(const CompositionalFluidState< T, FluidSystem > &fluid_state, const Scalar wellbore_volume)
Mass [kg] of each component held in the wellbore control volume, given an already-flashed two-phase f...
Definition: CompWellFlash.hpp:113
Structs needed for tpfalinearizer and its gpuparams struct extracted to be defined in one place that ...
Definition: blackoilbioeffectsmodules.hh:45
void flashWellboreFluidState(CompositionalFluidState< T, FluidSystem > &fluid_state, const typename FluidSystem::Scalar flash_tolerance=1.e-6)
Flash the wellbore fluid at its current (pressure, temperature, overall composition) and fill in the ...
Definition: CompWellFlash.hpp:54