Skip to content

polykin.thermo.flash¤

flash2_TV ¤

flash2_TV(
    F: float,
    z: FloatVector,
    T: float,
    beta: float,
    Kcalc: Callable[
        [float, float, FloatVector, FloatVector],
        FloatVector,
    ],
    *,
    P0: float = 100000.0,
    maxiter: int = 50,
    atol_inner: float = 1e-09,
    rtol_outer: float = 1e-06
) -> FlashResult

Solve a 2-phase flash problem at given temperature and vapor fraction.

References

  • J.F. Boston, H.I. Britt, "A radically different formulation and solution of the single-stage flash problem", Computers & Chemical Engineering, Volume 2, Issues 2-3, 1978, p. 109-122,
PARAMETER DESCRIPTION
F

Feed mole flowrate (mol/s).

TYPE: float

z

Feed mole fractions (mol/mol).

TYPE: FloatVector

T

Temperature (K).

TYPE: float

beta

Vapor phase fraction (mol/mol).

TYPE: float

Kcalc

Function to calculate K-values, with signature Kcalc(T, P, x, y).

TYPE: Callable[[float, float, FloatVector, FloatVector], FloatVector]

P0

Initial guess for pressure (Pa).

TYPE: float DEFAULT: 100000.0

maxiter

Maximum number of iterations.

TYPE: int DEFAULT: 50

atol_inner

Absolute tolerance for the inner R-loop.

TYPE: float DEFAULT: 1e-09

rtol_outer

Relative tolerance for the outer volatility parameters loop.

TYPE: float DEFAULT: 1e-06

RETURNS DESCRIPTION
FlashResult

Flash result.

Source code in src/polykin/thermo/flash/vle.py
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
def flash2_TV(
    F: float,
    z: FloatVector,
    T: float,
    beta: float,
    Kcalc: Callable[[float, float, FloatVector, FloatVector], FloatVector],
    *,
    P0: float = 1e5,
    maxiter: int = 50,
    atol_inner: float = 1e-9,
    rtol_outer: float = 1e-6,
) -> FlashResult:
    r"""Solve a 2-phase flash problem at given temperature and vapor fraction.

    **References**

    *  J.F. Boston, H.I. Britt, "A radically different formulation and solution
       of the single-stage flash problem", Computers & Chemical Engineering,
       Volume 2, Issues 2-3, 1978, p. 109-122,

    Parameters
    ----------
    F : float
        Feed mole flowrate (mol/s).
    z : FloatVector
        Feed mole fractions (mol/mol).
    T : float
        Temperature (K).
    beta : float
        Vapor phase fraction (mol/mol).
    Kcalc : Callable[[float, float, FloatVector, FloatVector], FloatVector]
        Function to calculate K-values, with signature `Kcalc(T, P, x, y)`.
    P0 : float
        Initial guess for pressure (Pa).
    maxiter : int
        Maximum number of iterations.   
    atol_inner : float
        Absolute tolerance for the inner R-loop.
    rtol_outer : float
        Relative tolerance for the outer volatility parameters loop.

    Returns
    -------
    FlashResult
        Flash result.
    """

    method = "2-Phase TV Flash"
    message = ""
    success = False

    # Initial guesses
    z = z/z.sum()
    P = P0
    K = Kcalc(T, P, z, z)
    x = z/(1 + beta*(K - 1))
    y = K*x
    x /= x.sum()
    y /= y.sum()

    # Initialize volatility parameters
    Pref = 1e5
    u, Kb, A, B = _parameters_TV(T, P, x, y, beta, Kcalc, Pref, all=True)
    Kb0 = Kb

    # Outer loop
    for _ in range(maxiter):

        v_old = np.concatenate((u, [A]))

        # Inner R-loop
        if abs(beta - 0) <= eps:
            R = 0.0
        elif abs(beta - 1) <= eps:
            R = 1.0
        else:
            sol = root_brent(lambda R: _Rloop(R, u, Kb0, beta, z),
                             0.0, 1.0,
                             maxiter=maxiter,
                             tolx=atol_inner,
                             tolf=atol_inner)
            R = sol.x
            if not sol.success:
                message = f"Inner R-loop did not converge after {maxiter} iterations. Solution: {sol}."
                break

        # Compute x, y
        p = z/(1 - R + Kb0*R*exp(u))
        eup = exp(u)*p
        sum_p = p.sum()
        sum_eup = eup.sum()
        x = p/sum_p
        y = eup/sum_eup

        # Compute P
        Kb = sum_p/sum_eup
        sol = root_newton(lambda P: log(Kb*P) - A - B*(P/Pref),
                          x0=exp(A)/Kb, tolx=0.0, tolf=1e-10)
        if sol.success:
            P = sol.x
        else:
            message = f"Pressure did not converge after {maxiter} iterations. Solution: {sol}."
            break

        # Update u, A, K
        u, A, K = _parameters_TV(T, P, x, y, beta, Kcalc, Pref, all=False, B=B)
        v_new = np.concatenate((u, [A]))

        # Check convergence
        v0 = min(vi for vi in v_new if vi > 0.0)
        if np.allclose(v_new, v_old, atol=v0*rtol_outer):
            success = True
            message = "Outer loop converged within specified tolerance."
            break

        else:
            message = f"Outer loop did not converge after {maxiter} iterations."

    # Overall balances
    V = F*beta
    L = F - V

    return FlashResult(method, success, message, T, P, F, L, V, beta, z, x, y, K)