29 #ifndef OPM_CHI_FLASH_HPP 30 #define OPM_CHI_FLASH_HPP 40 #include <opm/material/eos/CubicEOS.hpp> 42 #include <opm/input/eclipse/EclipseState/Compositional/CompositionalConfig.hpp> 44 #include <opm/common/ErrorMacros.hpp> 45 #include <opm/common/OpmLog/OpmLog.hpp> 47 #include <dune/common/fvector.hh> 48 #include <dune/common/fmatrix.hh> 49 #include <dune/common/classname.hh> 54 #include <type_traits> 56 #include <fmt/format.h> 57 #include <fmt/ranges.h> 66 template <
class Scalar,
class Flu
idSystem,
bool isThermal = false>
69 static constexpr
int numPhases = FluidSystem::numPhases;
70 static constexpr
int numComponents = FluidSystem::numComponents;
71 enum { oilPhaseIdx = FluidSystem::oilPhaseIdx};
72 enum { gasPhaseIdx = FluidSystem::gasPhaseIdx};
73 enum { waterPhaseIdx = FluidSystem::waterPhaseIdx};
74 static constexpr
int numMiscibleComponents = FluidSystem::numMiscibleComponents;
75 static constexpr
int numMisciblePhases = FluidSystem::numMisciblePhases;
76 static constexpr
int numEq = numMisciblePhases + numMisciblePhases * numMiscibleComponents;
78 using EOSType = CompositionalConfig::EOSType;
85 template <
class Flu
idState>
86 static bool solve(FluidState& fluid_state,
87 const std::string& twoPhaseMethod,
88 Scalar flash_tolerance,
89 const EOSType& eos_type,
93 ScalarFluidState fluid_state_scalar;
95 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
96 fluid_state_scalar.setKvalue(compIdx, Opm::getValue(fluid_state.K(compIdx) ) );
97 fluid_state_scalar.setMoleFraction(compIdx, Opm::getValue(fluid_state.moleFraction(compIdx) ) );
100 fluid_state_scalar.setLvalue(Opm::getValue(fluid_state.L()));
102 fluid_state_scalar.setPressure(FluidSystem::oilPhaseIdx,
103 Opm::getValue(fluid_state.pressure(FluidSystem::oilPhaseIdx)));
104 fluid_state_scalar.setPressure(FluidSystem::gasPhaseIdx,
105 Opm::getValue(fluid_state.pressure(FluidSystem::gasPhaseIdx)));
107 fluid_state_scalar.setTemperature(Opm::getValue(fluid_state.temperature(0)));
109 const auto is_single_phase = flash_solve_scalar_(fluid_state_scalar, twoPhaseMethod, flash_tolerance, eos_type, verbosity);
113 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
114 const auto x_i = fluid_state_scalar.moleFraction(oilPhaseIdx, compIdx);
115 fluid_state.setMoleFraction(oilPhaseIdx, compIdx, x_i);
116 const auto y_i = fluid_state_scalar.moleFraction(gasPhaseIdx, compIdx);
117 fluid_state.setMoleFraction(gasPhaseIdx, compIdx, y_i);
121 updateDerivatives_(fluid_state_scalar, fluid_state, eos_type, is_single_phase);
123 return is_single_phase;
133 template <
class Flu
idState,
class ComponentVector>
134 static void solve(FluidState& fluid_state,
135 const ComponentVector& globalMolarities,
136 Scalar tolerance = 0.0)
140 using MaterialLawParams =
typename MaterialLaw::Params;
142 MaterialLawParams matParams;
143 solve<MaterialLaw>(fluid_state, matParams, globalMolarities, tolerance);
146 template <
class Vector>
147 static typename Vector::field_type solveRachfordRice_g_(
const Vector& K,
const Vector& z,
int verbosity)
151 using field_type =
typename Vector::field_type;
152 constexpr field_type tol = 1e-12;
153 constexpr
int itmax = 10000;
154 field_type Kmin = K[0];
155 field_type Kmax = K[0];
156 for (
int compIdx = 1; compIdx < numComponents; ++compIdx){
157 if (K[compIdx] < Kmin)
159 else if (K[compIdx] >= Kmax)
163 auto Vmin = 1 / (1 - Kmax);
164 auto Vmax = 1 / (1 - Kmin);
166 auto V = (Vmin + Vmax)/2;
168 if (verbosity == 3 || verbosity == 4) {
169 OpmLog::debug(fmt::format(
"Initial guess {}c : V = {} and [Vmin, Vmax] = [{}, {}]",
170 numComponents, V, Vmin, Vmax));
171 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}",
"Iteration",
"abs(step)",
"V"));
174 for (
int iteration = 1; iteration < itmax; ++iteration) {
176 field_type denum = 0.0;
178 for (
int compIdx = 0; compIdx < numComponents; ++compIdx){
179 auto dK = K[compIdx] - 1.0;
180 auto a = z[compIdx] * dK;
181 auto b = (1 + V * dK);
183 denum += z[compIdx] * (dK*dK) / (b*b);
185 auto delta = r / denum;
189 if (V < Vmin || V > Vmax)
192 if (verbosity == 3 || verbosity == 4) {
193 OpmLog::debug(fmt::format(
"V = {} is not within the range [Vmin, Vmax], solve using Bisection method!", V));
201 decltype(Vmax) Lmin = 1.0;
202 decltype(Vmin) Lmax = 0.0;
203 auto L = bisection_g_(K, Lmin, Lmax, z, verbosity);
206 if (verbosity >= 1) {
207 OpmLog::debug(fmt::format(
"Rachford-Rice (Bisection) converged to final solution L = {}", L));
213 if (verbosity == 3 || verbosity == 4) {
214 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}", iteration, Opm::abs(delta), V));
217 if ( Opm::abs(r) < tol ) {
222 if (verbosity >= 1) {
223 OpmLog::debug(fmt::format(
"Rachford-Rice converged to final solution L = {}", L));
230 OPM_THROW(std::runtime_error,
" Rachford-Rice did not converge within maximum number of iterations");
234 template <
typename Flu
idState>
235 static bool flash_solve_scalar_(FluidState& fluid_state,
236 const std::string& twoPhaseMethod,
237 const Scalar flash_tolerance,
238 const EOSType& eos_type,
239 const int verbosity = 0)
242 bool is_stable =
false;
243 auto L_scalar = fluid_state.L();
245 ScalarVector K_scalar, z_scalar;
246 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
247 K_scalar[compIdx] = fluid_state.K(compIdx);
248 z_scalar[compIdx] = fluid_state.moleFraction(compIdx);
251 if ( L_scalar <= 0 || L_scalar == 1 ) {
252 if (verbosity >= 1) {
253 OpmLog::debug(
"Perform stability test (L <= 0 or L == 1)!");
255 phaseStabilityTest_(is_stable, K_scalar, fluid_state, z_scalar, eos_type, verbosity);
257 if (verbosity >= 1) {
258 OpmLog::debug(fmt::format(
"Inputs after stability test are K = [{}], L = [{}], z = [{}], P = {}, and T = {}",
259 fmt::join(K_scalar,
" "), L_scalar, fmt::join(z_scalar,
" "),
260 fluid_state.pressure(0), fluid_state.temperature(0)));
264 const bool is_single_phase = is_stable;
267 if ( !is_single_phase ) {
269 L_scalar = solveRachfordRice_g_(K_scalar, z_scalar, verbosity);
270 flash_2ph(z_scalar, twoPhaseMethod, K_scalar, L_scalar, fluid_state, flash_tolerance, eos_type, verbosity);
273 L_scalar = li_single_phase_label_(fluid_state, z_scalar, verbosity);
275 fluid_state.setLvalue(L_scalar);
276 return is_single_phase;
279 template <
class Vector>
280 static typename Vector::field_type bisection_g_(
const Vector& K,
typename Vector::field_type Lmin,
281 typename Vector::field_type Lmax,
const Vector& z,
int verbosity)
284 typename Vector::field_type gLmin = rachfordRice_g_(K, Lmin, z);
287 if (verbosity >= 3) {
288 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}",
"Iteration",
"g(Lmid)",
"L"));
291 constexpr
int max_it = 10000;
293 auto closeLmaxLmin = [](
double max_v,
double min_v) {
294 return Opm::abs(max_v - min_v) / 2. < 1e-10;
299 if (closeLmaxLmin(Lmax, Lmin) ){
300 OPM_THROW(std::runtime_error, fmt::format(
"Strange bisection with Lmax {} and Lmin {}?", Lmax, Lmin));
302 for (
int iteration = 0; iteration < max_it; ++iteration){
304 auto L = (Lmin + Lmax) / 2;
305 auto gMid = rachfordRice_g_(K, L, z);
306 if (verbosity == 3 || verbosity == 4) {
307 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}", iteration, gMid, L));
311 if (Opm::abs(gMid) < 1e-16 || closeLmaxLmin(Lmax, Lmin)){
315 else if (Dune::sign(gMid) != Dune::sign(gLmin)) {
325 OPM_THROW(std::runtime_error,
326 fmt::format(
" Rachford-Rice bisection failed with {} iterations!", max_it));
329 template <
class Vector,
class FlashFlu
idState>
330 static typename Vector::field_type li_single_phase_label_(
const FlashFluidState& fluid_state,
const Vector& z,
int verbosity)
333 typename Vector::field_type sumVz = 0.0;
334 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
336 const auto& V_crit = FluidSystem::criticalVolume(compIdx);
339 sumVz += (V_crit * z[compIdx]);
343 typename Vector::field_type Tc_est = 0.0;
344 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
346 const auto& V_crit = FluidSystem::criticalVolume(compIdx);
347 const auto& T_crit = FluidSystem::criticalTemperature(compIdx);
350 Tc_est += (V_crit * T_crit * z[compIdx] / sumVz);
354 const auto& T = fluid_state.temperature(0);
357 typename Vector::field_type L;
363 if (verbosity >= 1) {
364 OpmLog::debug(fmt::format(
"Cell is single-phase, liquid (L = 1.0) due to Li's phase labeling method giving T < Tc_est ({} < {})!",
373 if (verbosity >= 1) {
374 OpmLog::debug(fmt::format(
"Cell is single-phase, vapor (L = 0.0) due to Li's phase labeling method giving T >= Tc_est ({} >= {})!",
382 template <
class FlashFlu
idState,
class ComponentVector>
383 static void phaseStabilityTest_(
bool& isStable, ComponentVector& K, FlashFluidState& fluid_state,
const ComponentVector& z,
const EOSType& eos_type,
int verbosity)
386 bool isTrivialL, isTrivialV;
387 ComponentVector x, y;
388 typename FlashFluidState::ValueType S_l, S_v;
389 ComponentVector K0 = K;
390 ComponentVector K1 = K;
393 if (verbosity == 3 || verbosity == 4) {
394 OpmLog::debug(
"Stability test for vapor phase:");
396 checkStability_(fluid_state, isTrivialV, K0, y, S_v, z,
true, eos_type, verbosity);
397 bool V_unstable = (S_v < (1.0 + 1e-5)) || isTrivialV;
400 if (verbosity == 3 || verbosity == 4) {
401 OpmLog::debug(
"Stability test for liquid phase:");
403 checkStability_(fluid_state, isTrivialL, K1, x, S_l, z,
false, eos_type, verbosity);
404 bool L_stable = (S_l < (1.0 + 1e-5)) || isTrivialL;
407 isStable = L_stable && V_unstable;
411 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
412 fluid_state.setMoleFraction(gasPhaseIdx, compIdx, z[compIdx]);
413 fluid_state.setMoleFraction(oilPhaseIdx, compIdx, z[compIdx]);
418 for (
int compIdx = 0; compIdx<numComponents; ++compIdx) {
419 K[compIdx] = y[compIdx] / x[compIdx];
426 template <
class FlashFlu
idState>
427 static typename FlashFluidState::ValueType wilsonK_(
const FlashFluidState& fluid_state,
int compIdx)
429 const auto& acf = FluidSystem::acentricFactor(compIdx);
430 const auto& T_crit = FluidSystem::criticalTemperature(compIdx);
431 const auto& T = fluid_state.temperature(0);
432 const auto& p_crit = FluidSystem::criticalPressure(compIdx);
433 const auto& p = fluid_state.pressure(0);
435 const auto& tmp = Opm::exp(5.3727 * (1+acf) * (1-T_crit/T)) * (p_crit/p);
439 template <
class Vector>
440 static typename Vector::field_type rachfordRice_g_(
const Vector& K,
typename Vector::field_type L,
const Vector& z)
442 typename Vector::field_type g=0;
443 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
444 g += (z[compIdx]*(K[compIdx]-1))/(K[compIdx]-L*(K[compIdx]-1));
449 template <
class Vector>
450 static typename Vector::field_type rachfordRice_dg_dL_(
const Vector& K,
const typename Vector::field_type L,
const Vector& z)
452 typename Vector::field_type dg=0;
453 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
454 dg += (z[compIdx]*(K[compIdx]-1)*(K[compIdx]-1))/((K[compIdx]-L*(K[compIdx]-1))*(K[compIdx]-L*(K[compIdx]-1)));
459 template <
class FlashFlu
idState,
class ComponentVector>
460 static void checkStability_(
const FlashFluidState& fluid_state,
bool& isTrivial, ComponentVector& K, ComponentVector& xy_loc,
461 typename FlashFluidState::ValueType& S_loc,
const ComponentVector& z,
bool isGas,
const EOSType& eos_type,
464 using FlashEval =
typename FlashFluidState::ValueType;
468 FlashFluidState fluid_state_fake = fluid_state;
469 FlashFluidState fluid_state_global = fluid_state;
472 if (verbosity >= 3) {
473 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}",
"Iteration",
"K-Norm",
"R-Norm"));
478 for (
int i = 0; i < 20000; ++i) {
481 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
482 xy_loc[compIdx] = K[compIdx] * z[compIdx];
483 S_loc += xy_loc[compIdx];
485 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
486 xy_loc[compIdx] /= S_loc;
487 fluid_state_fake.setMoleFraction(gasPhaseIdx, compIdx, xy_loc[compIdx]);
491 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
492 xy_loc[compIdx] = z[compIdx]/K[compIdx];
493 S_loc += xy_loc[compIdx];
495 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
496 xy_loc[compIdx] /= S_loc;
497 fluid_state_fake.setMoleFraction(oilPhaseIdx, compIdx, xy_loc[compIdx]);
501 int phaseIdx = (isGas ?
static_cast<int>(gasPhaseIdx) : static_cast<int>(oilPhaseIdx));
502 int phaseIdx2 = (isGas ?
static_cast<int>(oilPhaseIdx) : static_cast<int>(gasPhaseIdx));
504 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
505 fluid_state_global.setMoleFraction(phaseIdx2, compIdx, z[compIdx]);
508 typename FluidSystem::template ParameterCache<FlashEval> paramCache_fake(eos_type);
509 paramCache_fake.updatePhase(fluid_state_fake, phaseIdx);
511 typename FluidSystem::template ParameterCache<FlashEval> paramCache_global(eos_type);
512 paramCache_global.updatePhase(fluid_state_global, phaseIdx2);
515 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
516 auto phiFake = CubicEOS::computeFugacityCoefficient(fluid_state_fake, paramCache_fake, phaseIdx, compIdx);
517 auto phiGlobal = CubicEOS::computeFugacityCoefficient(fluid_state_global, paramCache_global, phaseIdx2, compIdx);
519 fluid_state_fake.setFugacityCoefficient(phaseIdx, compIdx, phiFake);
520 fluid_state_global.setFugacityCoefficient(phaseIdx2, compIdx, phiGlobal);
524 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
526 auto fug_fake = fluid_state_fake.fugacity(phaseIdx, compIdx);
527 auto fug_global = fluid_state_global.fugacity(phaseIdx2, compIdx);
528 auto fug_ratio = fug_global / fug_fake;
529 R[compIdx] = fug_ratio / S_loc;
532 auto fug_fake = fluid_state_fake.fugacity(phaseIdx, compIdx);
533 auto fug_global = fluid_state_global.fugacity(phaseIdx2, compIdx);
534 auto fug_ratio = fug_fake / fug_global;
535 R[compIdx] = fug_ratio * S_loc;
539 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
540 K[compIdx] *= R[compIdx];
544 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
545 auto a = Opm::getValue(R[compIdx]) - 1.0;
546 auto b = Opm::log(Opm::getValue(K[compIdx]));
552 if (verbosity >= 3) {
553 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}", i, K_norm, R_norm));
557 isTrivial = (K_norm < 1e-5);
558 if (isTrivial || R_norm < 1e-10)
563 OPM_THROW(std::runtime_error,
" Stability test did not converge");
567 template <
class FlashFlu
idState,
class ComponentVector>
568 static void computeLiquidVapor_(FlashFluidState& fluid_state,
typename FlashFluidState::ValueType& L, ComponentVector& K,
const ComponentVector& z)
573 typename FlashFluidState::ValueType sumx=0;
574 typename FlashFluidState::ValueType sumy=0;
575 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
576 x[compIdx] = z[compIdx]/(L + (1-L)*K[compIdx]);
578 y[compIdx] = (K[compIdx]*z[compIdx])/(L + (1-L)*K[compIdx]);
584 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
585 fluid_state.setMoleFraction(oilPhaseIdx, compIdx, x[compIdx]);
586 fluid_state.setMoleFraction(gasPhaseIdx, compIdx, y[compIdx]);
590 template <
class Flu
idState,
class ComponentVector>
591 static void flash_2ph(
const ComponentVector& z_scalar,
592 const std::string& flash_2p_method,
593 ComponentVector& K_scalar,
594 typename FluidState::ValueType& L_scalar,
595 FluidState& fluid_state_scalar,
596 const Scalar flash_tolerance,
597 const EOSType& eos_type,
599 if (verbosity >= 1) {
600 OpmLog::debug(fmt::format(
"Cell is two-phase! Solve Rachford-Rice with initial K = [{}]",
601 fmt::join(K_scalar,
" ")));
606 bool converged =
false;
607 if (flash_2p_method ==
"newton") {
608 if (verbosity >= 1) {
609 OpmLog::debug(
"Calculate composition using Newton.");
611 converged = newtonComposition_(K_scalar, L_scalar, fluid_state_scalar, z_scalar, flash_tolerance, eos_type, verbosity);
612 }
else if (flash_2p_method ==
"ssi") {
614 if (verbosity >= 1) {
615 OpmLog::debug(
"Calculate composition using Successive Substitution.");
617 converged = successiveSubstitutionComposition_(K_scalar, L_scalar, fluid_state_scalar, z_scalar,
false, flash_tolerance, eos_type, verbosity);
618 }
else if (flash_2p_method ==
"ssi+newton") {
619 converged = successiveSubstitutionComposition_(K_scalar, L_scalar, fluid_state_scalar, z_scalar,
true, flash_tolerance, eos_type, verbosity);
621 converged = newtonComposition_(K_scalar, L_scalar, fluid_state_scalar, z_scalar, flash_tolerance, eos_type, verbosity);
624 OPM_THROW(std::logic_error,
625 "unknown two phase flash method " + flash_2p_method +
" is specified");
629 OPM_THROW(std::runtime_error,
630 "flash calculation did not get converged with " + flash_2p_method);
634 template <
class FlashFlu
idState,
class ComponentVector>
635 static bool newtonComposition_(ComponentVector& K,
636 typename FlashFluidState::ValueType& L,
637 FlashFluidState& fluid_state,
638 const ComponentVector& z,
639 const Scalar tolerance,
640 const EOSType& eos_type,
645 constexpr std::size_t num_equations = numMisciblePhases * numMiscibleComponents + 1;
646 constexpr std::size_t num_primary_variables = numMisciblePhases * numMiscibleComponents + 1;
648 using NewtonMatrix = Dune::FieldMatrix<Scalar, num_equations, num_primary_variables>;
650 NewtonVector soln(0.);
651 NewtonVector res(0.);
652 NewtonMatrix jac (0.);
655 computeLiquidVapor_(fluid_state, L, K, z);
658 if (verbosity >= 1) {
659 OpmLog::debug(fmt::format(
" the current L is {}", Opm::getValue(L)));
660 std::vector<Scalar> x_vals(numComponents), y_vals(numComponents);
661 for (
int compIdx = 0; compIdx < numComponents; ++compIdx) {
662 x_vals[compIdx] = Opm::getValue(fluid_state.moleFraction(oilPhaseIdx, compIdx));
663 y_vals[compIdx] = Opm::getValue(fluid_state.moleFraction(gasPhaseIdx, compIdx));
665 OpmLog::debug(fmt::format(
"Initial guess: x = [{}], y = [{}], and L = {}",
666 fmt::join(x_vals,
" "), fmt::join(y_vals,
" "), Opm::getValue(L)));
670 if (verbosity == 2 || verbosity == 4) {
671 OpmLog::debug(fmt::format(
"{:>10}{:>16}{:>16}",
"Iteration",
"Norm2(step)",
"Norm2(Residual)"));
675 using Eval = DenseAd::Evaluation<Scalar, num_primary_variables>;
677 std::vector<Eval> x(numComponents), y(numComponents);
681 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
682 x[compIdx] = Eval(fluid_state.moleFraction(oilPhaseIdx, compIdx), compIdx);
683 const unsigned idx = compIdx + numComponents;
684 y[compIdx] = Eval(fluid_state.moleFraction(gasPhaseIdx, compIdx), idx);
686 l = Eval(L, num_primary_variables - 1);
689 CompositionalFluidState<Eval, FluidSystem> flash_fluid_state;
690 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
691 flash_fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, compIdx, x[compIdx]);
692 flash_fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, compIdx, y[compIdx]);
694 flash_fluid_state.setKvalue(compIdx, y[compIdx] / x[compIdx]);
696 flash_fluid_state.setLvalue(l);
698 flash_fluid_state.setPressure(FluidSystem::oilPhaseIdx,
699 fluid_state.pressure(FluidSystem::oilPhaseIdx));
700 flash_fluid_state.setPressure(FluidSystem::gasPhaseIdx,
701 fluid_state.pressure(FluidSystem::gasPhaseIdx));
704 flash_fluid_state.setSaturation(FluidSystem::gasPhaseIdx,
705 fluid_state.saturation(FluidSystem::gasPhaseIdx));
706 flash_fluid_state.setSaturation(FluidSystem::oilPhaseIdx,
707 fluid_state.saturation(FluidSystem::oilPhaseIdx));
708 flash_fluid_state.setTemperature(fluid_state.temperature(0));
710 using ParamCache =
typename FluidSystem::template ParameterCache<typename CompositionalFluidState<Eval, FluidSystem>::ValueType>;
711 ParamCache paramCache(eos_type);
713 for (
unsigned phaseIdx = 0; phaseIdx < numMisciblePhases; ++phaseIdx) {
714 paramCache.updatePhase(flash_fluid_state, phaseIdx);
715 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
717 Eval phi = FluidSystem::fugacityCoefficient(flash_fluid_state, paramCache, phaseIdx, compIdx);
718 flash_fluid_state.setFugacityCoefficient(phaseIdx, compIdx, phi);
721 bool converged =
false;
723 constexpr
unsigned max_iter = 1000;
724 while (iter < max_iter) {
725 assembleNewton_<CompositionalFluidState<Eval, FluidSystem>, ComponentVector, num_primary_variables, num_equations>
726 (flash_fluid_state, z, jac, res);
727 if (verbosity >= 1) {
728 OpmLog::debug(fmt::format(
" newton residual is {}", res.two_norm()));
730 converged = res.two_norm() < tolerance;
735 jac.solve(soln, res);
736 constexpr Scalar damping_factor = 1.0;
738 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
739 x[compIdx] -= soln[compIdx] * damping_factor;
740 y[compIdx] -= soln[compIdx + numComponents] * damping_factor;
742 l -= soln[num_equations - 1] * damping_factor;
744 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
745 flash_fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, compIdx, x[compIdx]);
746 flash_fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, compIdx, y[compIdx]);
748 flash_fluid_state.setKvalue(compIdx, y[compIdx] / x[compIdx]);
750 flash_fluid_state.setLvalue(l);
752 for (
unsigned phaseIdx = 0; phaseIdx < numMisciblePhases; ++phaseIdx) {
753 paramCache.updatePhase(flash_fluid_state, phaseIdx);
754 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
755 Eval phi = FluidSystem::fugacityCoefficient(flash_fluid_state, paramCache, phaseIdx, compIdx);
756 flash_fluid_state.setFugacityCoefficient(phaseIdx, compIdx, phi);
761 if (verbosity >= 1) {
762 fmt::memory_buffer buf;
763 for (
unsigned i = 0; i < num_equations; ++i) {
764 for (
unsigned j = 0; j < num_primary_variables; ++j) {
765 fmt::format_to(std::back_inserter(buf),
" {}", jac[i][j]);
767 fmt::format_to(std::back_inserter(buf),
"\n");
769 OpmLog::debug(fmt::to_string(buf));
772 OPM_THROW(std::runtime_error,
773 fmt::format(
" Newton composition update did not converge within maxIterations {}", max_iter));
777 for (
unsigned idx = 0; idx < numComponents; ++idx) {
778 const auto x_i = Opm::getValue(flash_fluid_state.moleFraction(oilPhaseIdx, idx));
779 fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, idx, x_i);
780 const auto y_i = Opm::getValue(flash_fluid_state.moleFraction(gasPhaseIdx, idx));
781 fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, idx, y_i);
782 const auto K_i = Opm::getValue(flash_fluid_state.K(idx));
783 fluid_state.setKvalue(idx, K_i);
787 L = Opm::getValue(l);
788 fluid_state.setLvalue(L);
793 template<
typename FlashFlu
idState,
typename ComponentVector, std::
size_t num_primary, std::
size_t num_equation >
794 static void assembleNewton_(
const FlashFluidState& fluid_state,
795 const ComponentVector& global_composition,
796 Dune::FieldMatrix<double, num_equation, num_primary>& jac,
799 using Eval = DenseAd::Evaluation<double, num_primary>;
800 std::vector<Eval> x(numComponents), y(numComponents);
801 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
802 x[compIdx] = fluid_state.moleFraction(oilPhaseIdx, compIdx);
803 y[compIdx] = fluid_state.moleFraction(gasPhaseIdx, compIdx);
805 const Eval& l = fluid_state.L();
809 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
812 auto local_res = -global_composition[compIdx] + l * x[compIdx] + (1 - l) * y[compIdx];
813 res[compIdx] = Opm::getValue(local_res);
814 for (
unsigned i = 0; i < num_primary; ++i) {
815 jac[compIdx][i] = local_res.derivative(i);
821 auto local_res = (fluid_state.fugacity(oilPhaseIdx, compIdx) -
822 fluid_state.fugacity(gasPhaseIdx, compIdx));
823 res[compIdx + numComponents] = Opm::getValue(local_res);
824 for (
unsigned i = 0; i < num_primary; ++i) {
825 jac[compIdx + numComponents][i] = local_res.derivative(i);
832 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
836 auto local_res = sumx - sumy;
837 res[num_equation - 1] = Opm::getValue(local_res);
838 for (
unsigned i = 0; i < num_primary; ++i) {
839 jac[num_equation - 1][i] = local_res.derivative(i);
843 template <
typename FlashFlu
idStateScalar,
typename Flu
idState>
844 static void updateDerivatives_(
const FlashFluidStateScalar& fluid_state_scalar,
845 FluidState& fluid_state,
846 const EOSType& eos_type,
847 bool is_single_phase)
850 updateDerivativesTwoPhase_(fluid_state_scalar, fluid_state, eos_type);
852 updateDerivativesSinglePhase_(fluid_state_scalar, fluid_state);
856 template <
typename FlashFlu
idStateScalar,
typename Flu
idState>
857 static void updateDerivativesTwoPhase_(
const FlashFluidStateScalar& fluid_state_scalar,
858 FluidState& fluid_state,
859 const EOSType& eos_type)
861 using InputEval =
typename FluidState::ValueType;
864 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
865 z[compIdx] = fluid_state.moleFraction(compIdx);
869 constexpr std::size_t num_equations = numMisciblePhases * numMiscibleComponents + 1;
870 constexpr std::size_t secondary_num_pv = isThermal ? numComponents + 2 : numComponents + 1;
876 SecondaryFlashFluidState secondary_fluid_state;
877 SecondaryComponentVector secondary_z;
880 const SecondaryEval sec_p = SecondaryEval(fluid_state_scalar.pressure(FluidSystem::oilPhaseIdx), 0);
881 secondary_fluid_state.setPressure(FluidSystem::oilPhaseIdx, sec_p);
882 secondary_fluid_state.setPressure(FluidSystem::gasPhaseIdx, sec_p);
884 if constexpr (isThermal) {
886 const SecondaryEval sec_T = SecondaryEval(fluid_state_scalar.temperature(0), 1);
887 secondary_fluid_state.setTemperature(sec_T);
890 secondary_fluid_state.setTemperature(Opm::getValue(fluid_state_scalar.temperature(0)));
895 constexpr
unsigned z_offset = isThermal ? 2 : 1;
896 for (
unsigned idx = 0; idx < numComponents; ++idx) {
897 secondary_z[idx] = SecondaryEval(Opm::getValue(z[idx]), idx + z_offset);
900 for (
unsigned idx = 0; idx < numComponents; ++idx) {
902 const auto x_i = fluid_state_scalar.moleFraction(oilPhaseIdx, idx);
903 secondary_fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, idx, x_i);
904 const auto y_i = fluid_state_scalar.moleFraction(gasPhaseIdx, idx);
905 secondary_fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, idx, y_i);
907 const auto K_i = fluid_state_scalar.K(idx);
908 secondary_fluid_state.setKvalue(idx, K_i);
910 const auto L = fluid_state_scalar.L();
911 secondary_fluid_state.setLvalue(L);
915 using SecondaryParamCache =
typename FluidSystem::template ParameterCache<typename SecondaryFlashFluidState::ValueType>;
916 SecondaryParamCache secondary_param_cache(eos_type);
917 for (
unsigned phase_idx = 0; phase_idx < numMisciblePhases; ++phase_idx) {
918 secondary_param_cache.updatePhase(secondary_fluid_state, phase_idx);
919 for (
unsigned comp_idx = 0; comp_idx < numComponents; ++comp_idx) {
920 SecondaryEval phi = FluidSystem::fugacityCoefficient(secondary_fluid_state, secondary_param_cache, phase_idx, comp_idx);
921 secondary_fluid_state.setFugacityCoefficient(phase_idx, comp_idx, phi);
926 using SecondaryNewtonMatrix = Dune::FieldMatrix<Scalar, num_equations, secondary_num_pv>;
927 SecondaryNewtonMatrix sec_jac;
928 SecondaryNewtonVector sec_res;
931 assembleNewton_<SecondaryFlashFluidState, SecondaryComponentVector, secondary_num_pv, num_equations>
932 (secondary_fluid_state, secondary_z, sec_jac, sec_res);
936 constexpr std::size_t primary_num_pv = numMisciblePhases * numMiscibleComponents + 1;
941 PrimaryFlashFluidState primary_fluid_state;
943 PrimaryComponentVector primary_z;
944 for (
unsigned comp_idx = 0; comp_idx < numComponents; ++comp_idx) {
945 primary_z[comp_idx] = Opm::getValue(z[comp_idx]);
947 for (
unsigned comp_idx = 0; comp_idx < numComponents; ++comp_idx) {
948 const auto x_ii = PrimaryEval(fluid_state_scalar.moleFraction(oilPhaseIdx, comp_idx), comp_idx);
949 primary_fluid_state.setMoleFraction(oilPhaseIdx, comp_idx, x_ii);
950 const unsigned idx = comp_idx + numComponents;
951 const auto y_ii = PrimaryEval(fluid_state_scalar.moleFraction(gasPhaseIdx, comp_idx), idx);
952 primary_fluid_state.setMoleFraction(gasPhaseIdx, comp_idx, y_ii);
953 primary_fluid_state.setKvalue(comp_idx, y_ii / x_ii);
956 l = PrimaryEval(fluid_state_scalar.L(), primary_num_pv - 1);
957 primary_fluid_state.setLvalue(l);
958 primary_fluid_state.setPressure(oilPhaseIdx, fluid_state_scalar.pressure(oilPhaseIdx));
959 primary_fluid_state.setPressure(gasPhaseIdx, fluid_state_scalar.pressure(gasPhaseIdx));
960 primary_fluid_state.setTemperature(fluid_state_scalar.temperature(0));
963 using PrimaryParamCache =
typename FluidSystem::template ParameterCache<typename PrimaryFlashFluidState::ValueType>;
964 PrimaryParamCache primary_param_cache(eos_type);
965 for (
unsigned phase_idx = 0; phase_idx < numMisciblePhases; ++phase_idx) {
966 primary_param_cache.updatePhase(primary_fluid_state, phase_idx);
967 for (
unsigned comp_idx = 0; comp_idx < numComponents; ++comp_idx) {
968 PrimaryEval phi = FluidSystem::fugacityCoefficient(primary_fluid_state, primary_param_cache, phase_idx, comp_idx);
969 primary_fluid_state.setFugacityCoefficient(phase_idx, comp_idx, phi);
974 using PrimaryNewtonMatrix = Dune::FieldMatrix<Scalar, num_equations, primary_num_pv>;
975 PrimaryNewtonVector pri_res;
976 PrimaryNewtonMatrix pri_jac;
979 assembleNewton_<PrimaryFlashFluidState, PrimaryComponentVector, primary_num_pv, num_equations>
980 (primary_fluid_state, primary_z, pri_jac, pri_res);
986 sec_jac.template leftmultiply<PrimaryNewtonMatrix>(pri_jac);
988 ComponentVector x(numComponents), y(numComponents);
989 InputEval L_eval = L;
996 const auto p_l = fluid_state.pressure(FluidSystem::oilPhaseIdx);
997 const auto p_v = fluid_state.pressure(FluidSystem::gasPhaseIdx);
1000 [[maybe_unused]]
const auto& T_input = fluid_state.temperature(0);
1002 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
1003 x[compIdx] = fluid_state_scalar.moleFraction(FluidSystem::oilPhaseIdx,compIdx);
1004 y[compIdx] = fluid_state_scalar.moleFraction(FluidSystem::gasPhaseIdx,compIdx);
1011 constexpr std::size_t num_deri = InputEval::numVars;
1012 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
1013 std::vector<double> deri(num_deri, 0.);
1015 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1016 deri[idx] = -sec_jac[compIdx][0] * p_l.derivative(idx);
1019 if constexpr (isThermal) {
1020 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1021 deri[idx] += -sec_jac[compIdx][1] * T_input.derivative(idx);
1025 for (
unsigned cIdx = 0; cIdx < numComponents; ++cIdx) {
1026 const double pz = -sec_jac[compIdx][cIdx + z_offset];
1027 const auto& zi = z[cIdx];
1028 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1029 deri[idx] += pz * zi.derivative(idx);
1032 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1033 x[compIdx].setDerivative(idx, deri[idx]);
1036 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1037 deri[idx] = -sec_jac[compIdx + numComponents][0] * p_v.derivative(idx);
1040 if constexpr (isThermal) {
1041 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1042 deri[idx] += -sec_jac[compIdx + numComponents][1] * T_input.derivative(idx);
1045 for (
unsigned cIdx = 0; cIdx < numComponents; ++cIdx) {
1046 const double pz = -sec_jac[compIdx + numComponents][cIdx + z_offset];
1047 const auto& zi = z[cIdx];
1048 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1049 deri[idx] += pz * zi.derivative(idx);
1052 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1053 y[compIdx].setDerivative(idx, deri[idx]);
1057 std::vector<double> deriL(num_deri, 0.);
1058 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1059 deriL[idx] = -sec_jac[2 * numComponents][0] * p_v.derivative(idx);
1062 if constexpr (isThermal) {
1063 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1064 deriL[idx] += -sec_jac[2 * numComponents][1] * T_input.derivative(idx);
1067 for (
unsigned cIdx = 0; cIdx < numComponents; ++cIdx) {
1068 const double pz = -sec_jac[2 * numComponents][cIdx + z_offset];
1069 const auto& zi = z[cIdx];
1070 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1071 deriL[idx] += pz * zi.derivative(idx);
1075 for (
unsigned idx = 0; idx < num_deri; ++idx) {
1076 L_eval.setDerivative(idx, deriL[idx]);
1081 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
1082 fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, compIdx, x[compIdx]);
1083 fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, compIdx, y[compIdx]);
1085 fluid_state.setLvalue(L_eval);
1088 template <
typename FlashFlu
idStateScalar,
typename Flu
idState>
1089 static void updateDerivativesSinglePhase_(
const FlashFluidStateScalar& fluid_state_scalar,
1090 FluidState& fluid_state)
1092 using InputEval =
typename FluidState::ValueType;
1094 InputEval L_eval = fluid_state_scalar.L();;
1098 for (
unsigned compIdx = 0; compIdx < numComponents; ++compIdx) {
1099 fluid_state.setMoleFraction(FluidSystem::oilPhaseIdx, compIdx, fluid_state.moleFraction(compIdx) );
1100 fluid_state.setMoleFraction(FluidSystem::gasPhaseIdx, compIdx, fluid_state.moleFraction(compIdx) );
1102 fluid_state.setLvalue(L_eval);
1106 template <
class FlashFlu
idState,
class ComponentVector>
1107 static bool successiveSubstitutionComposition_(ComponentVector& K,
1108 typename ComponentVector::field_type& L,
1109 FlashFluidState& fluid_state,
1110 const ComponentVector& z,
1111 const bool newton_afterwards,
1112 const Scalar flash_tolerance,
1113 const EOSType& eos_type,
1114 const int verbosity)
1117 const int maxIterations = newton_afterwards ? 5 : 100;
1120 if (verbosity >= 1) {
1121 OpmLog::debug(fmt::format(
"Initial guess: K = [{}] and L = {}", fmt::join(K,
" "), L));
1124 if (verbosity == 2 || verbosity == 4) {
1126 int fugWidth = (numComponents * 12)/2;
1127 int convWidth = fugWidth + 7;
1128 OpmLog::debug(fmt::format(
"{:>10}{:>{}}{:>{}}",
"Iteration",
"fL/fV", fugWidth,
"norm2(fL/fv-1)", convWidth));
1133 for (
int i=0; i < maxIterations; ++i){
1135 computeLiquidVapor_(fluid_state, L, K, z);
1138 using ParamCache =
typename FluidSystem::template ParameterCache<typename FlashFluidState::ValueType>;
1139 ParamCache paramCache(eos_type);
1140 for (
int phaseIdx=0; phaseIdx<numMisciblePhases; ++phaseIdx){
1141 paramCache.updatePhase(fluid_state, phaseIdx);
1142 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
1143 auto phi = FluidSystem::fugacityCoefficient(fluid_state, paramCache, phaseIdx, compIdx);
1144 fluid_state.setFugacityCoefficient(phaseIdx, compIdx, phi);
1149 ComponentVector newFugRatio;
1150 ComponentVector convFugRatio;
1151 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
1152 newFugRatio[compIdx] = fluid_state.fugacity(oilPhaseIdx, compIdx)/fluid_state.fugacity(gasPhaseIdx, compIdx);
1153 convFugRatio[compIdx] = newFugRatio[compIdx] - 1.0;
1157 if (verbosity >= 2) {
1158 constexpr
int prec = 5;
1159 constexpr
int fugWidth = prec + 3;
1160 constexpr
int convWidth = prec + 9;
1161 OpmLog::debug(fmt::format(
"{:>5}{:>{}.{}f}{:>{}.{}e}",
1163 fmt::join(newFugRatio,
" "), fugWidth, prec,
1164 convFugRatio.two_norm(), convWidth, prec));
1168 if (convFugRatio.two_norm() < flash_tolerance) {
1170 if (verbosity >= 1) {
1171 std::vector<typename ComponentVector::field_type> x_vals(numComponents), y_vals(numComponents);
1172 for (
int compIdx = 0; compIdx < numComponents; ++compIdx) {
1173 x_vals[compIdx] = fluid_state.moleFraction(oilPhaseIdx, compIdx);
1174 y_vals[compIdx] = fluid_state.moleFraction(gasPhaseIdx, compIdx);
1176 OpmLog::debug(fmt::format(
"Solution converged to the following result :\n" 1181 fmt::join(x_vals,
" "),
1182 fmt::join(y_vals,
" "),
1191 for (
int compIdx=0; compIdx<numComponents; ++compIdx){
1192 K[compIdx] *= newFugRatio[compIdx];
1196 L = solveRachfordRice_g_(K, z, 0);
1201 const std::string msg = fmt::format(
"Successive substitution composition update did not converge within maxIterations {}.", maxIterations);
1202 if (!newton_afterwards) {
1203 OPM_THROW(std::runtime_error, msg);
1204 }
else if (verbosity > 0) {
Implements a dummy linear saturation-capillary pressure relation which just disables capillary pressu...
Represents all relevant thermodynamic quantities of a multi-phase, multi-component fluid system assum...
This class implements a small container which holds the transmissibility mulitpliers for all the face...
Definition: Exceptions.hpp:30
This file contains helper classes for the material laws.
Definition: CubicEOS.hpp:33
A generic traits class which does not provide any indices.
Definition: MaterialTraits.hpp:44
Implements a dummy linear saturation-capillary pressure relation which just disables capillary pressu...
Definition: NullMaterial.hpp:45
A number of commonly used algebraic functions for the localized OPM automatic differentiation (AD) fr...
static void solve(FluidState &fluid_state, const ComponentVector &globalMolarities, Scalar tolerance=0.0)
Calculates the chemical equilibrium from the component fugacities in a phase.
Definition: PTFlash.hpp:134
static bool solve(FluidState &fluid_state, const std::string &twoPhaseMethod, Scalar flash_tolerance, const EOSType &eos_type, int verbosity=0)
Calculates the fluid state from the global mole fractions of the components and the phase pressures...
Definition: PTFlash.hpp:86
Definition: SymmTensor.hpp:24
Some templates to wrap the valgrind client request macros.
Represents a function evaluation and its derivatives w.r.t.
Definition: Evaluation.hpp:63
Determines the phase compositions, pressures and saturations given the total mass of all components f...
Definition: PTFlash.hpp:67
Representation of an evaluation of a function and its derivatives w.r.t.
Represents all relevant thermodynamic quantities of a multi-phase, multi-component fluid system assum...
Definition: CompositionalFluidState.hpp:53