thermo/activity.py
def flash_inner_loop(zs, Ks, AvailableMethods=False, Method=None):
    r'''This function handles the solution of the inner loop of a flash
    calculation, solving for liquid and gas mole fractions and vapor fraction
    based on specified overall mole fractions and K values. As K values are
    weak functions of composition, this should be called repeatedly by an outer
    loop. Will automatically select an algorithm to use if no Method is
    provided. Should always provide a solution.

    The automatic algorithm selection will try an analytical solution, and use
    the Rachford-Rice method if there are 4 or more components in the mixture.

    Parameters
    ----------
    zs : list[float]
        Overall mole fractions of all species, [-]
    Ks : list[float]
        Equilibrium K-values, [-]

    Returns
    -------
    V_over_F : float
        Vapor fraction solution [-]
    xs : list[float]
        Mole fractions of each species in the liquid phase, [-]
    ys : list[float]
        Mole fractions of each species in the vapor phase, [-]
    methods : list, only returned if AvailableMethods == True
        List of methods which can be used to obtain a solution with the given
        inputs

    Other Parameters
    ----------------
    Method : string, optional
        The method name to use. Accepted methods are 'Analytical',
        'Rachford-Rice', and 'Li-Johns-Ahmadi'. All valid values are also held
        in the list `flash_inner_loop_methods`.
    AvailableMethods : bool, optional
        If True, function will determine which methods can be used to obtain
        a solution for the desired chemical, and will return methods instead of
        `V_over_F`, `xs`, and `ys`.

    Notes
    -----
    A total of three methods are available for this function. They are:

        * 'Analytical', an exact solution derived with SymPy, applicable only
          only to mixtures of two or three components
        * 'Rachford-Rice', which numerically solves an objective function
          described in :obj:`Rachford_Rice_solution`.
        * 'Li-Johns-Ahmadi', which numerically solves an objective function
          described in :obj:`Li_Johns_Ahmadi_solution`.

    Examples
    --------
    >>> flash_inner_loop(zs=[0.5, 0.3, 0.2], Ks=[1.685, 0.742, 0.532])
    (0.6907302627738537, [0.3394086969663437, 0.36505605903717053, 0.29553524399648573], [0.5719036543882892, 0.2708715958055805, 0.1572247498061304])
    '''
    l = len(zs)
    def list_methods():
        methods = []
        if l in [2,3]:
            methods.append('Analytical')
        if l >= 2:
            methods.append('Rachford-Rice')
        if l >= 3:
            methods.append('Li-Johns-Ahmadi')
        return methods
    if AvailableMethods:
        return list_methods()
    if not Method:
        Method = 'Analytical' if l < 4 else 'Rachford-Rice'
    if Method == 'Analytical':
        if l == 2:
            z1, z2 = zs
            K1, K2 = Ks
            V_over_F = (-K1*z1 - K2*z2 + z1 + z2)/(K1*K2*z1 + K1*K2*z2 - K1*z1 - K1*z2 - K2*z1 - K2*z2 + z1 + z2)
        elif l == 3:
            z1, z2, z3 = zs
            K1, K2, K3 = Ks
            V_over_F = (-K1*K2*z1/2 - K1*K2*z2/2 - K1*K3*z1/2 - K1*K3*z3/2 + K1*z1 + K1*z2/2 + K1*z3/2 - K2*K3*z2/2 - K2*K3*z3/2 + K2*z1/2 + K2*z2 + K2*z3/2 + K3*z1/2 + K3*z2/2 + K3*z3 - z1 - z2 - z3 - (K1**2*K2**2*z1**2 + 2*K1**2*K2**2*z1*z2 + K1**2*K2**2*z2**2 - 2*K1**2*K2*K3*z1**2 - 2*K1**2*K2*K3*z1*z2 - 2*K1**2*K2*K3*z1*z3 + 2*K1**2*K2*K3*z2*z3 - 2*K1**2*K2*z1*z2 + 2*K1**2*K2*z1*z3 - 2*K1**2*K2*z2**2 - 2*K1**2*K2*z2*z3 + K1**2*K3**2*z1**2 + 2*K1**2*K3**2*z1*z3 + K1**2*K3**2*z3**2 + 2*K1**2*K3*z1*z2 - 2*K1**2*K3*z1*z3 - 2*K1**2*K3*z2*z3 - 2*K1**2*K3*z3**2 + K1**2*z2**2 + 2*K1**2*z2*z3 + K1**2*z3**2 - 2*K1*K2**2*K3*z1*z2 + 2*K1*K2**2*K3*z1*z3 - 2*K1*K2**2*K3*z2**2 - 2*K1*K2**2*K3*z2*z3 - 2*K1*K2**2*z1**2 - 2*K1*K2**2*z1*z2 - 2*K1*K2**2*z1*z3 + 2*K1*K2**2*z2*z3 + 2*K1*K2*K3**2*z1*z2 - 2*K1*K2*K3**2*z1*z3 - 2*K1*K2*K3**2*z2*z3 - 2*K1*K2*K3**2*z3**2 + 4*K1*K2*K3*z1**2 + 4*K1*K2*K3*z1*z2 + 4*K1*K2*K3*z1*z3 + 4*K1*K2*K3*z2**2 + 4*K1*K2*K3*z2*z3 + 4*K1*K2*K3*z3**2 + 2*K1*K2*z1*z2 - 2*K1*K2*z1*z3 - 2*K1*K2*z2*z3 - 2*K1*K2*z3**2 - 2*K1*K3**2*z1**2 - 2*K1*K3**2*z1*z2 - 2*K1*K3**2*z1*z3 + 2*K1*K3**2*z2*z3 - 2*K1*K3*z1*z2 + 2*K1*K3*z1*z3 - 2*K1*K3*z2**2 - 2*K1*K3*z2*z3 + K2**2*K3**2*z2**2 + 2*K2**2*K3**2*z2*z3 + K2**2*K3**2*z3**2 + 2*K2**2*K3*z1*z2 - 2*K2**2*K3*z1*z3 - 2*K2**2*K3*z2*z3 - 2*K2**2*K3*z3**2 + K2**2*z1**2 + 2*K2**2*z1*z3 + K2**2*z3**2 - 2*K2*K3**2*z1*z2 + 2*K2*K3**2*z1*z3 - 2*K2*K3**2*z2**2 - 2*K2*K3**2*z2*z3 - 2*K2*K3*z1**2 - 2*K2*K3*z1*z2 - 2*K2*K3*z1*z3 + 2*K2*K3*z2*z3 + K3**2*z1**2 + 2*K3**2*z1*z2 + K3**2*z2**2)**0.5/2)/(K1*K2*K3*z1 + K1*K2*K3*z2 + K1*K2*K3*z3 - K1*K2*z1 - K1*K2*z2 - K1*K2*z3 - K1*K3*z1 - K1*K3*z2 - K1*K3*z3 + K1*z1 + K1*z2 + K1*z3 - K2*K3*z1 - K2*K3*z2 - K2*K3*z3 + K2*z1 + K2*z2 + K2*z3 + K3*z1 + K3*z2 + K3*z3 - z1 - z2 - z3)
        else:
            raise Exception('Only solutions of one or two variables are available analytically')
        xs = [zi/(1.+V_over_F*(Ki-1.)) for zi, Ki in zip(zs, Ks)]
        ys = [Ki*xi for xi, Ki in zip(xs, Ks)]
        return V_over_F, xs, ys
    elif Method == 'Rachford-Rice':
        return Rachford_Rice_solution(zs=zs, Ks=Ks)
    elif Method == 'Li-Johns-Ahmadi':
        return Li_Johns_Ahmadi_solution(zs=zs, Ks=Ks)
    else:
        raise Exception('Incorrect Method input')
def flash_inner_loop(zs, Ks, AvailableMethods=False, Method=None):
    r'''This function handles the solution of the inner loop of a flash
    calculation, solving for liquid and gas mole fractions and vapor fraction
    based on specified overall mole fractions and K values. As K values are
    weak functions of composition, this should be called repeatedly by an outer
    loop. Will automatically select an algorithm to use if no Method is
    provided. Should always provide a solution.

    The automatic algorithm selection will try an analytical solution, and use
    the Rachford-Rice method if there are 4 or more components in the mixture.

    Parameters
    ----------
    zs : list[float]
        Overall mole fractions of all species, [-]
    Ks : list[float]
        Equilibrium K-values, [-]

    Returns
    -------
    V_over_F : float
        Vapor fraction solution [-]
    xs : list[float]
        Mole fractions of each species in the liquid phase, [-]
    ys : list[float]
        Mole fractions of each species in the vapor phase, [-]
    methods : list, only returned if AvailableMethods == True
        List of methods which can be used to obtain a solution with the given
        inputs

    Other Parameters
    ----------------
    Method : string, optional
        The method name to use. Accepted methods are 'Analytical',
        'Rachford-Rice', and 'Li-Johns-Ahmadi'. All valid values are also held
        in the list `flash_inner_loop_methods`.
    AvailableMethods : bool, optional
        If True, function will determine which methods can be used to obtain
        a solution for the desired chemical, and will return methods instead of
        `V_over_F`, `xs`, and `ys`.

    Notes
    -----
    A total of three methods are available for this function. They are:

        * 'Analytical', an exact solution derived with SymPy, applicable only
          only to mixtures of two or three components
        * 'Rachford-Rice', which numerically solves an objective function
          described in :obj:`Rachford_Rice_solution`.
        * 'Li-Johns-Ahmadi', which numerically solves an objective function
          described in :obj:`Li_Johns_Ahmadi_solution`.

    Examples
    --------
    >>> flash_inner_loop(zs=[0.5, 0.3, 0.2], Ks=[1.685, 0.742, 0.532])
    (0.6907302627738537, [0.3394086969663437, 0.36505605903717053, 0.29553524399648573], [0.5719036543882892, 0.2708715958055805, 0.1572247498061304])
    '''
    l = len(zs)
    def list_methods():
        methods = []
        if l in [2,3]:
            methods.append('Analytical')
        if l >= 2:
            methods.append('Rachford-Rice')
        if l >= 3:
            methods.append('Li-Johns-Ahmadi')
        return methods
    if AvailableMethods:
        return list_methods()
    if not Method:
        Method = 'Analytical' if l < 4 else 'Rachford-Rice'
    if Method == 'Analytical':
        if l == 2:
            z1, z2 = zs
            K1, K2 = Ks
            V_over_F = (-K1*z1 - K2*z2 + z1 + z2)/(K1*K2*z1 + K1*K2*z2 - K1*z1 - K1*z2 - K2*z1 - K2*z2 + z1 + z2)
        elif l == 3:
            z1, z2, z3 = zs
            K1, K2, K3 = Ks
            V_over_F = (-K1*K2*z1/2 - K1*K2*z2/2 - K1*K3*z1/2 - K1*K3*z3/2 + K1*z1 + K1*z2/2 + K1*z3/2 - K2*K3*z2/2 - K2*K3*z3/2 + K2*z1/2 + K2*z2 + K2*z3/2 + K3*z1/2 + K3*z2/2 + K3*z3 - z1 - z2 - z3 - (K1**2*K2**2*z1**2 + 2*K1**2*K2**2*z1*z2 + K1**2*K2**2*z2**2 - 2*K1**2*K2*K3*z1**2 - 2*K1**2*K2*K3*z1*z2 - 2*K1**2*K2*K3*z1*z3 + 2*K1**2*K2*K3*z2*z3 - 2*K1**2*K2*z1*z2 + 2*K1**2*K2*z1*z3 - 2*K1**2*K2*z2**2 - 2*K1**2*K2*z2*z3 + K1**2*K3**2*z1**2 + 2*K1**2*K3**2*z1*z3 + K1**2*K3**2*z3**2 + 2*K1**2*K3*z1*z2 - 2*K1**2*K3*z1*z3 - 2*K1**2*K3*z2*z3 - 2*K1**2*K3*z3**2 + K1**2*z2**2 + 2*K1**2*z2*z3 + K1**2*z3**2 - 2*K1*K2**2*K3*z1*z2 + 2*K1*K2**2*K3*z1*z3 - 2*K1*K2**2*K3*z2**2 - 2*K1*K2**2*K3*z2*z3 - 2*K1*K2**2*z1**2 - 2*K1*K2**2*z1*z2 - 2*K1*K2**2*z1*z3 + 2*K1*K2**2*z2*z3 + 2*K1*K2*K3**2*z1*z2 - 2*K1*K2*K3**2*z1*z3 - 2*K1*K2*K3**2*z2*z3 - 2*K1*K2*K3**2*z3**2 + 4*K1*K2*K3*z1**2 + 4*K1*K2*K3*z1*z2 + 4*K1*K2*K3*z1*z3 + 4*K1*K2*K3*z2**2 + 4*K1*K2*K3*z2*z3 + 4*K1*K2*K3*z3**2 + 2*K1*K2*z1*z2 - 2*K1*K2*z1*z3 - 2*K1*K2*z2*z3 - 2*K1*K2*z3**2 - 2*K1*K3**2*z1**2 - 2*K1*K3**2*z1*z2 - 2*K1*K3**2*z1*z3 + 2*K1*K3**2*z2*z3 - 2*K1*K3*z1*z2 + 2*K1*K3*z1*z3 - 2*K1*K3*z2**2 - 2*K1*K3*z2*z3 + K2**2*K3**2*z2**2 + 2*K2**2*K3**2*z2*z3 + K2**2*K3**2*z3**2 + 2*K2**2*K3*z1*z2 - 2*K2**2*K3*z1*z3 - 2*K2**2*K3*z2*z3 - 2*K2**2*K3*z3**2 + K2**2*z1**2 + 2*K2**2*z1*z3 + K2**2*z3**2 - 2*K2*K3**2*z1*z2 + 2*K2*K3**2*z1*z3 - 2*K2*K3**2*z2**2 - 2*K2*K3**2*z2*z3 - 2*K2*K3*z1**2 - 2*K2*K3*z1*z2 - 2*K2*K3*z1*z3 + 2*K2*K3*z2*z3 + K3**2*z1**2 + 2*K3**2*z1*z2 + K3**2*z2**2)**0.5/2)/(K1*K2*K3*z1 + K1*K2*K3*z2 + K1*K2*K3*z3 - K1*K2*z1 - K1*K2*z2 - K1*K2*z3 - K1*K3*z1 - K1*K3*z2 - K1*K3*z3 + K1*z1 + K1*z2 + K1*z3 - K2*K3*z1 - K2*K3*z2 - K2*K3*z3 + K2*z1 + K2*z2 + K2*z3 + K3*z1 + K3*z2 + K3*z3 - z1 - z2 - z3)
        else:
            raise Exception('Only solutions of one or two variables are available analytically')
        xs = [zi/(1.+V_over_F*(Ki-1.)) for zi, Ki in zip(zs, Ks)]
        ys = [Ki*xi for xi, Ki in zip(xs, Ks)]
        return V_over_F, xs, ys
    elif Method == 'Rachford-Rice':
        return Rachford_Rice_solution(zs=zs, Ks=Ks)
    elif Method == 'Li-Johns-Ahmadi':
        return Li_Johns_Ahmadi_solution(zs=zs, Ks=Ks)
    else:
        raise Exception('Incorrect Method input')
def flash_inner_loop ( zs , Ks , AvailableMethods = False , Method = None ) : l = len ( zs ) def list_methods ( ) : methods = [ ] if l in [ 2 , 3 ] : methods . append ( 'Analytical' ) if l >= 2 : methods . append ( 'Rachford-Rice' ) if l >= 3 : methods . append ( 'Li-Johns-Ahmadi' ) return methods if AvailableMethods : return list_methods ( ) if not Method : Method = 'Analytical' if l < 4 else 'Rachford-Rice' if Method == 'Analytical' : if l == 2 : z1 , z2 = zs K1 , K2 = Ks V_over_F = ( - K1 * z1 - K2 * z2 + z1 + z2 ) / ( K1 * K2 * z1 + K1 * K2 * z2 - K1 * z1 - K1 * z2 - K2 * z1 - K2 * z2 + z1 + z2 ) elif l == 3 : z1 , z2 , z3 = zs K1 , K2 , K3 = Ks V_over_F = ( - K1 * K2 * z1 / 2 - K1 * K2 * z2 / 2 - K1 * K3 * z1 / 2 - K1 * K3 * z3 / 2 + K1 * z1 + K1 * z2 / 2 + K1 * z3 / 2 - K2 * K3 * z2 / 2 - K2 * K3 * z3 / 2 + K2 * z1 / 2 + K2 * z2 + K2 * z3 / 2 + K3 * z1 / 2 + K3 * z2 / 2 + K3 * z3 - z1 - z2 - z3 - ( K1 ** 2 * K2 ** 2 * z1 ** 2 + 2 * K1 ** 2 * K2 ** 2 * z1 * z2 + K1 ** 2 * K2 ** 2 * z2 ** 2 - 2 * K1 ** 2 * K2 * K3 * z1 ** 2 - 2 * K1 ** 2 * K2 * K3 * z1 * z2 - 2 * K1 ** 2 * K2 * K3 * z1 * z3 + 2 * K1 ** 2 * K2 * K3 * z2 * z3 - 2 * K1 ** 2 * K2 * z1 * z2 + 2 * K1 ** 2 * K2 * z1 * z3 - 2 * K1 ** 2 * K2 * z2 ** 2 - 2 * K1 ** 2 * K2 * z2 * z3 + K1 ** 2 * K3 ** 2 * z1 ** 2 + 2 * K1 ** 2 * K3 ** 2 * z1 * z3 + K1 ** 2 * K3 ** 2 * z3 ** 2 + 2 * K1 ** 2 * K3 * z1 * z2 - 2 * K1 ** 2 * K3 * z1 * z3 - 2 * K1 ** 2 * K3 * z2 * z3 - 2 * K1 ** 2 * K3 * z3 ** 2 + K1 ** 2 * z2 ** 2 + 2 * K1 ** 2 * z2 * z3 + K1 ** 2 * z3 ** 2 - 2 * K1 * K2 ** 2 * K3 * z1 * z2 + 2 * K1 * K2 ** 2 * K3 * z1 * z3 - 2 * K1 * K2 ** 2 * K3 * z2 ** 2 - 2 * K1 * K2 ** 2 * K3 * z2 * z3 - 2 * K1 * K2 ** 2 * z1 ** 2 - 2 * K1 * K2 ** 2 * z1 * z2 - 2 * K1 * K2 ** 2 * z1 * z3 + 2 * K1 * K2 ** 2 * z2 * z3 + 2 * K1 * K2 * K3 ** 2 * z1 * z2 - 2 * K1 * K2 * K3 ** 2 * z1 * z3 - 2 * K1 * K2 * K3 ** 2 * z2 * z3 - 2 * K1 * K2 * K3 ** 2 * z3 ** 2 + 4 * K1 * K2 * K3 * z1 ** 2 + 4 * K1 * K2 * K3 * z1 * z2 + 4 * K1 * K2 * K3 * z1 * z3 + 4 * K1 * K2 * K3 * z2 ** 2 + 4 * K1 * K2 * K3 * z2 * z3 + 4 * K1 * K2 * K3 * z3 ** 2 + 2 * K1 * K2 * z1 * z2 - 2 * K1 * K2 * z1 * z3 - 2 * K1 * K2 * z2 * z3 - 2 * K1 * K2 * z3 ** 2 - 2 * K1 * K3 ** 2 * z1 ** 2 - 2 * K1 * K3 ** 2 * z1 * z2 - 2 * K1 * K3 ** 2 * z1 * z3 + 2 * K1 * K3 ** 2 * z2 * z3 - 2 * K1 * K3 * z1 * z2 + 2 * K1 * K3 * z1 * z3 - 2 * K1 * K3 * z2 ** 2 - 2 * K1 * K3 * z2 * z3 + K2 ** 2 * K3 ** 2 * z2 ** 2 + 2 * K2 ** 2 * K3 ** 2 * z2 * z3 + K2 ** 2 * K3 ** 2 * z3 ** 2 + 2 * K2 ** 2 * K3 * z1 * z2 - 2 * K2 ** 2 * K3 * z1 * z3 - 2 * K2 ** 2 * K3 * z2 * z3 - 2 * K2 ** 2 * K3 * z3 ** 2 + K2 ** 2 * z1 ** 2 + 2 * K2 ** 2 * z1 * z3 + K2 ** 2 * z3 ** 2 - 2 * K2 * K3 ** 2 * z1 * z2 + 2 * K2 * K3 ** 2 * z1 * z3 - 2 * K2 * K3 ** 2 * z2 ** 2 - 2 * K2 * K3 ** 2 * z2 * z3 - 2 * K2 * K3 * z1 ** 2 - 2 * K2 * K3 * z1 * z2 - 2 * K2 * K3 * z1 * z3 + 2 * K2 * K3 * z2 * z3 + K3 ** 2 * z1 ** 2 + 2 * K3 ** 2 * z1 * z2 + K3 ** 2 * z2 ** 2 ) ** 0.5 / 2 ) / ( K1 * K2 * K3 * z1 + K1 * K2 * K3 * z2 + K1 * K2 * K3 * z3 - K1 * K2 * z1 - K1 * K2 * z2 - K1 * K2 * z3 - K1 * K3 * z1 - K1 * K3 * z2 - K1 * K3 * z3 + K1 * z1 + K1 * z2 + K1 * z3 - K2 * K3 * z1 - K2 * K3 * z2 - K2 * K3 * z3 + K2 * z1 + K2 * z2 + K2 * z3 + K3 * z1 + K3 * z2 + K3 * z3 - z1 - z2 - z3 ) else : raise Exception ( 'Only solutions of one or two variables are available analytically' ) xs = [ zi / ( 1. + V_over_F * ( Ki - 1. ) ) for zi , Ki in zip ( zs , Ks ) ] ys = [ Ki * xi for xi , Ki in zip ( xs , Ks ) ] return V_over_F , xs , ys elif Method == 'Rachford-Rice' : return Rachford_Rice_solution ( zs = zs , Ks = Ks ) elif Method == 'Li-Johns-Ahmadi' : return Li_Johns_Ahmadi_solution ( zs = zs , Ks = Ks ) else : raise Exception ( 'Incorrect Method input' )
r'''This function handles the solution of the inner loop of a flash
    calculation, solving for liquid and gas mole fractions and vapor fraction
    based on specified overall mole fractions and K values. As K values are
    weak functions of composition, this should be called repeatedly by an outer
    loop. Will automatically select an algorithm to use if no Method is
    provided. Should always provide a solution.

    The automatic algorithm selection will try an analytical solution, and use
    the Rachford-Rice method if there are 4 or more components in the mixture.

    Parameters
    ----------
    zs : list[float]
        Overall mole fractions of all species, [-]
    Ks : list[float]
        Equilibrium K-values, [-]

    Returns
    -------
    V_over_F : float
        Vapor fraction solution [-]
    xs : list[float]
        Mole fractions of each species in the liquid phase, [-]
    ys : list[float]
        Mole fractions of each species in the vapor phase, [-]
    methods : list, only returned if AvailableMethods == True
        List of methods which can be used to obtain a solution with the given
        inputs

    Other Parameters
    ----------------
    Method : string, optional
        The method name to use. Accepted methods are 'Analytical',
        'Rachford-Rice', and 'Li-Johns-Ahmadi'. All valid values are also held
        in the list `flash_inner_loop_methods`.
    AvailableMethods : bool, optional
        If True, function will determine which methods can be used to obtain
        a solution for the desired chemical, and will return methods instead of
        `V_over_F`, `xs`, and `ys`.

    Notes
    -----
    A total of three methods are available for this function. They are:

        * 'Analytical', an exact solution derived with SymPy, applicable only
          only to mixtures of two or three components
        * 'Rachford-Rice', which numerically solves an objective function
          described in :obj:`Rachford_Rice_solution`.
        * 'Li-Johns-Ahmadi', which numerically solves an objective function
          described in :obj:`Li_Johns_Ahmadi_solution`.

    Examples
    --------
    >>> flash_inner_loop(zs=[0.5, 0.3, 0.2], Ks=[1.685, 0.742, 0.532])
    (0.6907302627738537, [0.3394086969663437, 0.36505605903717053, 0.29553524399648573], [0.5719036543882892, 0.2708715958055805, 0.1572247498061304])
r This function handles the solution of the inner loop of a flash calculation solving for liquid and gas mole fractions and vapor fraction based on specified overall mole fractions and K values . As K values are weak functions of composition this should be called repeatedly by an outer loop . Will automatically select an algorithm to use if no Method is provided . Should always provide a solution .