thermo/unifac.py
def UNIFAC(T, xs, chemgroups, cached=None, subgroup_data=None, 
           interaction_data=None, modified=False):
    r'''Calculates activity coefficients using the UNIFAC model (optionally 
    modified), given a mixture's temperature, liquid mole fractions, 
    and optionally the subgroup data and interaction parameter data of your 
    choice. The default is to use the original UNIFAC model, with the latest 
    parameters published by DDBST. The model supports modified forms (Dortmund, 
    NIST) when the `modified` parameter is True.
            
    Parameters
    ----------
    T : float
        Temperature of the system, [K]
    xs : list[float]
        Mole fractions of all species in the system in the liquid phase, [-]
    chemgroups : list[dict]
        List of dictionaries of subgroup IDs and their counts for all species 
        in the mixture, [-]
    subgroup_data : dict[UNIFAC_subgroup]
        UNIFAC subgroup data; available dictionaries in this module are UFSG
        (original), DOUFSG (Dortmund), or NISTUFSG ([4]_).
    interaction_data : dict[dict[tuple(a_mn, b_mn, c_mn)]]
        UNIFAC interaction parameter data; available dictionaries in this 
        module are UFIP (original), DOUFIP2006 (Dortmund parameters as 
        published by 2006), DOUFIP2016 (Dortmund parameters as published by 
        2016), and NISTUFIP ([4]_).
    modified : bool
        True if using the modified form and temperature dependence, otherwise
        False.

    Returns
    -------
    gammas : list[float]
        Activity coefficients of all species in the mixture, [-]

    Notes
    -----
    The actual implementation of UNIFAC is formulated slightly different than
    the formulas above for computational efficiency. DDBST switched to using 
    the more efficient forms in their publication, but the numerical results
    are identical.
    
    The model is as follows:
        
    .. math::
        \ln \gamma_i =  \ln \gamma_i^c + \ln \gamma_i^r
        
    **Combinatorial component**
    
    .. math::
        \ln \gamma_i^c = \ln \frac{\phi_i}{x_i} + \frac{z}{2} q_i 
        \ln\frac{\theta_i}{\phi_i} + L_i - \frac{\phi_i}{x_i}
        \sum_{j=1}^{n} x_j L_j
        
        \theta_i = \frac{x_i q_i}{\sum_{j=1}^{n} x_j q_j} 
        
         \phi_i = \frac{x_i r_i}{\sum_{j=1}^{n} x_j r_j}
         
          L_i = 5(r_i - q_i)-(r_i-1)
   
    **Residual component**
    
    .. math::
        \ln \gamma_i^r = \sum_{k}^n \nu_k^{(i)} \left[ \ln \Gamma_k
        - \ln \Gamma_k^{(i)} \right]
        
        \ln \Gamma_k = Q_k \left[1 - \ln \sum_m \Theta_m \Psi_{mk} - \sum_m 
        \frac{\Theta_m \Psi_{km}}{\sum_n \Theta_n \Psi_{nm}}\right]
        
        \Theta_m = \frac{Q_m X_m}{\sum_{n} Q_n X_n}
        
        X_m = \frac{ \sum_j \nu^j_m x_j}{\sum_j \sum_n \nu_n^j x_j}
        
    **R and Q**
    
    .. math::
        r_i = \sum_{k=1}^{n} \nu_k R_k 
        
        q_i = \sum_{k=1}^{n}\nu_k Q_k
    
    The newer forms of UNIFAC (Dortmund, NIST) calculate the combinatorial
    part slightly differently:
        
    .. math::
        \ln \gamma_i^c = 1 - {V'}_i + \ln({V'}_i) - 5q_i \left(1
        - \frac{V_i}{F_i}+ \ln\left(\frac{V_i}{F_i}\right)\right)
        
        V'_i = \frac{r_i^{3/4}}{\sum_j r_j^{3/4}x_j}
        
    
    This is more clear when looking at the full rearranged form as in [3]_.

    Examples
    --------
    >>> UNIFAC(T=333.15, xs=[0.5, 0.5], chemgroups=[{1:2, 2:4}, {1:1, 2:1, 18:1}])
    [1.4276025835624173, 1.3646545010104225]
    
    >>> UNIFAC(373.15, [0.2, 0.3, 0.2, 0.2], 
    ... [{9:6}, {78:6}, {1:1, 18:1}, {1:1, 2:1, 14:1}],
    ... subgroup_data=DOUFSG, interaction_data=DOUFIP2006, modified=True)
    [1.186431113706829, 1.440280133911197, 1.204479833499608, 1.9720706090299824]

    References
    ----------
    .. [1] Gmehling, Jurgen. Chemical Thermodynamics: For Process Simulation.
       Weinheim, Germany: Wiley-VCH, 2012.
    .. [2] Fredenslund, Aage, Russell L. Jones, and John M. Prausnitz. "Group
       Contribution Estimation of Activity Coefficients in Nonideal Liquid 
       Mixtures." AIChE Journal 21, no. 6 (November 1, 1975): 1086-99. 
       doi:10.1002/aic.690210607.
    .. [3] Jakob, Antje, Hans Grensemann, Jürgen Lohmann, and Jürgen Gmehling. 
       "Further Development of Modified UNIFAC (Dortmund):  Revision and 
       Extension 5." Industrial & Engineering Chemistry Research 45, no. 23 
       (November 1, 2006): 7924-33. doi:10.1021/ie060355c.
    .. [4] Kang, Jeong Won, Vladimir Diky, and Michael Frenkel. "New Modified 
       UNIFAC Parameters Using Critically Evaluated Phase Equilibrium Data." 
       Fluid Phase Equilibria 388 (February 25, 2015): 128-41. 
       doi:10.1016/j.fluid.2014.12.042.
    '''
    cmps = range(len(xs))
    if subgroup_data is None:
        subgroups = UFSG
    else:
        subgroups = subgroup_data
    if interaction_data is None:
        interactions = UFIP
    else:
        interactions = interaction_data

    # Obtain r and q values using the subgroup values
    if not cached:
        rs = []
        qs = []
        for groups in chemgroups:
            ri = 0.
            qi = 0.
            for group, count in groups.items():
                ri += subgroups[group].R*count
                qi += subgroups[group].Q*count
            rs.append(ri)
            qs.append(qi)
        

        group_counts = {}
        for groups in chemgroups:
            for group, count in groups.items():
                if group in group_counts:
                    group_counts[group] += count
                else:
                    group_counts[group] = count
    else:
        rs, qs, group_counts = cached

    # Sum the denominator for calculating Xs
    group_sum = sum(count*xs[i] for i in cmps for count in chemgroups[i].values())

    # Caclulate each numerator for calculating Xs
    group_count_xs = {}
    for group in group_counts:
        tot_numerator = sum(chemgroups[i][group]*xs[i] for i in cmps if group in chemgroups[i])        
        group_count_xs[group] = tot_numerator/group_sum

    rsxs = sum([rs[i]*xs[i] for i in cmps])
    Vis = [rs[i]/rsxs for i in cmps]
    qsxs = sum([qs[i]*xs[i] for i in cmps])
    Fis = [qs[i]/qsxs for i in cmps]

    if modified:
        rsxs2 = sum([rs[i]**0.75*xs[i] for i in cmps])
        Vis2 = [rs[i]**0.75/rsxs2 for i in cmps]
        loggammacs = [1. - Vis2[i] + log(Vis2[i]) - 5.*qs[i]*(1. - Vis[i]/Fis[i]
                      + log(Vis[i]/Fis[i])) for i in cmps]
    else:
        loggammacs = [1. - Vis[i] + log(Vis[i]) - 5.*qs[i]*(1. - Vis[i]/Fis[i]
                      + log(Vis[i]/Fis[i])) for i in cmps]

    Q_sum_term = sum([subgroups[group].Q*group_count_xs[group] for group in group_counts])
    area_fractions = {group: subgroups[group].Q*group_count_xs[group]/Q_sum_term
                      for group in group_counts.keys()}

    UNIFAC_psis = {k: {m:(UNIFAC_psi(T, m, k, subgroups, interactions, modified=modified))
                   for m in group_counts} for k in group_counts}

    loggamma_groups = {}
    for k in group_counts:
        sum1, sum2 = 0., 0.
        for m in group_counts:
            sum1 += area_fractions[m]*UNIFAC_psis[k][m]
            sum3 = sum(area_fractions[n]*UNIFAC_psis[m][n] for n in group_counts)
            sum2 -= area_fractions[m]*UNIFAC_psis[m][k]/sum3
        loggamma_groups[k] = subgroups[k].Q*(1. - log(sum1) + sum2)


    loggammars = []
    for groups in chemgroups:
        chem_loggamma_groups = {}
        chem_group_sum = sum(groups.values())
        chem_group_count_xs = {group: count/chem_group_sum for group, count in groups.items()}
                               
        Q_sum_term = sum([subgroups[group].Q*chem_group_count_xs[group] for group in groups])
        chem_area_fractions = {group: subgroups[group].Q*chem_group_count_xs[group]/Q_sum_term
                               for group in groups.keys()}
        for k in groups:
            sum1, sum2 = 0., 0.
            for m in groups:
                sum1 += chem_area_fractions[m]*UNIFAC_psis[k][m]
                sum3 = sum(chem_area_fractions[n]*UNIFAC_psis[m][n] for n in groups)
                sum2 -= chem_area_fractions[m]*UNIFAC_psis[m][k]/sum3

            chem_loggamma_groups[k] = subgroups[k].Q*(1. - log(sum1) + sum2)

        tot = sum([count*(loggamma_groups[group] - chem_loggamma_groups[group])
                   for group, count in groups.items()])
        loggammars.append(tot)

    return [exp(loggammacs[i]+loggammars[i]) for i in cmps]
def UNIFAC(T, xs, chemgroups, cached=None, subgroup_data=None, 
           interaction_data=None, modified=False):
    r'''Calculates activity coefficients using the UNIFAC model (optionally 
    modified), given a mixture's temperature, liquid mole fractions, 
    and optionally the subgroup data and interaction parameter data of your 
    choice. The default is to use the original UNIFAC model, with the latest 
    parameters published by DDBST. The model supports modified forms (Dortmund, 
    NIST) when the `modified` parameter is True.
            
    Parameters
    ----------
    T : float
        Temperature of the system, [K]
    xs : list[float]
        Mole fractions of all species in the system in the liquid phase, [-]
    chemgroups : list[dict]
        List of dictionaries of subgroup IDs and their counts for all species 
        in the mixture, [-]
    subgroup_data : dict[UNIFAC_subgroup]
        UNIFAC subgroup data; available dictionaries in this module are UFSG
        (original), DOUFSG (Dortmund), or NISTUFSG ([4]_).
    interaction_data : dict[dict[tuple(a_mn, b_mn, c_mn)]]
        UNIFAC interaction parameter data; available dictionaries in this 
        module are UFIP (original), DOUFIP2006 (Dortmund parameters as 
        published by 2006), DOUFIP2016 (Dortmund parameters as published by 
        2016), and NISTUFIP ([4]_).
    modified : bool
        True if using the modified form and temperature dependence, otherwise
        False.

    Returns
    -------
    gammas : list[float]
        Activity coefficients of all species in the mixture, [-]

    Notes
    -----
    The actual implementation of UNIFAC is formulated slightly different than
    the formulas above for computational efficiency. DDBST switched to using 
    the more efficient forms in their publication, but the numerical results
    are identical.
    
    The model is as follows:
        
    .. math::
        \ln \gamma_i =  \ln \gamma_i^c + \ln \gamma_i^r
        
    **Combinatorial component**
    
    .. math::
        \ln \gamma_i^c = \ln \frac{\phi_i}{x_i} + \frac{z}{2} q_i 
        \ln\frac{\theta_i}{\phi_i} + L_i - \frac{\phi_i}{x_i}
        \sum_{j=1}^{n} x_j L_j
        
        \theta_i = \frac{x_i q_i}{\sum_{j=1}^{n} x_j q_j} 
        
         \phi_i = \frac{x_i r_i}{\sum_{j=1}^{n} x_j r_j}
         
          L_i = 5(r_i - q_i)-(r_i-1)
   
    **Residual component**
    
    .. math::
        \ln \gamma_i^r = \sum_{k}^n \nu_k^{(i)} \left[ \ln \Gamma_k
        - \ln \Gamma_k^{(i)} \right]
        
        \ln \Gamma_k = Q_k \left[1 - \ln \sum_m \Theta_m \Psi_{mk} - \sum_m 
        \frac{\Theta_m \Psi_{km}}{\sum_n \Theta_n \Psi_{nm}}\right]
        
        \Theta_m = \frac{Q_m X_m}{\sum_{n} Q_n X_n}
        
        X_m = \frac{ \sum_j \nu^j_m x_j}{\sum_j \sum_n \nu_n^j x_j}
        
    **R and Q**
    
    .. math::
        r_i = \sum_{k=1}^{n} \nu_k R_k 
        
        q_i = \sum_{k=1}^{n}\nu_k Q_k
    
    The newer forms of UNIFAC (Dortmund, NIST) calculate the combinatorial
    part slightly differently:
        
    .. math::
        \ln \gamma_i^c = 1 - {V'}_i + \ln({V'}_i) - 5q_i \left(1
        - \frac{V_i}{F_i}+ \ln\left(\frac{V_i}{F_i}\right)\right)
        
        V'_i = \frac{r_i^{3/4}}{\sum_j r_j^{3/4}x_j}
        
    
    This is more clear when looking at the full rearranged form as in [3]_.

    Examples
    --------
    >>> UNIFAC(T=333.15, xs=[0.5, 0.5], chemgroups=[{1:2, 2:4}, {1:1, 2:1, 18:1}])
    [1.4276025835624173, 1.3646545010104225]
    
    >>> UNIFAC(373.15, [0.2, 0.3, 0.2, 0.2], 
    ... [{9:6}, {78:6}, {1:1, 18:1}, {1:1, 2:1, 14:1}],
    ... subgroup_data=DOUFSG, interaction_data=DOUFIP2006, modified=True)
    [1.186431113706829, 1.440280133911197, 1.204479833499608, 1.9720706090299824]

    References
    ----------
    .. [1] Gmehling, Jurgen. Chemical Thermodynamics: For Process Simulation.
       Weinheim, Germany: Wiley-VCH, 2012.
    .. [2] Fredenslund, Aage, Russell L. Jones, and John M. Prausnitz. "Group
       Contribution Estimation of Activity Coefficients in Nonideal Liquid 
       Mixtures." AIChE Journal 21, no. 6 (November 1, 1975): 1086-99. 
       doi:10.1002/aic.690210607.
    .. [3] Jakob, Antje, Hans Grensemann, Jürgen Lohmann, and Jürgen Gmehling. 
       "Further Development of Modified UNIFAC (Dortmund):  Revision and 
       Extension 5." Industrial & Engineering Chemistry Research 45, no. 23 
       (November 1, 2006): 7924-33. doi:10.1021/ie060355c.
    .. [4] Kang, Jeong Won, Vladimir Diky, and Michael Frenkel. "New Modified 
       UNIFAC Parameters Using Critically Evaluated Phase Equilibrium Data." 
       Fluid Phase Equilibria 388 (February 25, 2015): 128-41. 
       doi:10.1016/j.fluid.2014.12.042.
    '''
    cmps = range(len(xs))
    if subgroup_data is None:
        subgroups = UFSG
    else:
        subgroups = subgroup_data
    if interaction_data is None:
        interactions = UFIP
    else:
        interactions = interaction_data

    # Obtain r and q values using the subgroup values
    if not cached:
        rs = []
        qs = []
        for groups in chemgroups:
            ri = 0.
            qi = 0.
            for group, count in groups.items():
                ri += subgroups[group].R*count
                qi += subgroups[group].Q*count
            rs.append(ri)
            qs.append(qi)
        

        group_counts = {}
        for groups in chemgroups:
            for group, count in groups.items():
                if group in group_counts:
                    group_counts[group] += count
                else:
                    group_counts[group] = count
    else:
        rs, qs, group_counts = cached

    # Sum the denominator for calculating Xs
    group_sum = sum(count*xs[i] for i in cmps for count in chemgroups[i].values())

    # Caclulate each numerator for calculating Xs
    group_count_xs = {}
    for group in group_counts:
        tot_numerator = sum(chemgroups[i][group]*xs[i] for i in cmps if group in chemgroups[i])        
        group_count_xs[group] = tot_numerator/group_sum

    rsxs = sum([rs[i]*xs[i] for i in cmps])
    Vis = [rs[i]/rsxs for i in cmps]
    qsxs = sum([qs[i]*xs[i] for i in cmps])
    Fis = [qs[i]/qsxs for i in cmps]

    if modified:
        rsxs2 = sum([rs[i]**0.75*xs[i] for i in cmps])
        Vis2 = [rs[i]**0.75/rsxs2 for i in cmps]
        loggammacs = [1. - Vis2[i] + log(Vis2[i]) - 5.*qs[i]*(1. - Vis[i]/Fis[i]
                      + log(Vis[i]/Fis[i])) for i in cmps]
    else:
        loggammacs = [1. - Vis[i] + log(Vis[i]) - 5.*qs[i]*(1. - Vis[i]/Fis[i]
                      + log(Vis[i]/Fis[i])) for i in cmps]

    Q_sum_term = sum([subgroups[group].Q*group_count_xs[group] for group in group_counts])
    area_fractions = {group: subgroups[group].Q*group_count_xs[group]/Q_sum_term
                      for group in group_counts.keys()}

    UNIFAC_psis = {k: {m:(UNIFAC_psi(T, m, k, subgroups, interactions, modified=modified))
                   for m in group_counts} for k in group_counts}

    loggamma_groups = {}
    for k in group_counts:
        sum1, sum2 = 0., 0.
        for m in group_counts:
            sum1 += area_fractions[m]*UNIFAC_psis[k][m]
            sum3 = sum(area_fractions[n]*UNIFAC_psis[m][n] for n in group_counts)
            sum2 -= area_fractions[m]*UNIFAC_psis[m][k]/sum3
        loggamma_groups[k] = subgroups[k].Q*(1. - log(sum1) + sum2)


    loggammars = []
    for groups in chemgroups:
        chem_loggamma_groups = {}
        chem_group_sum = sum(groups.values())
        chem_group_count_xs = {group: count/chem_group_sum for group, count in groups.items()}
                               
        Q_sum_term = sum([subgroups[group].Q*chem_group_count_xs[group] for group in groups])
        chem_area_fractions = {group: subgroups[group].Q*chem_group_count_xs[group]/Q_sum_term
                               for group in groups.keys()}
        for k in groups:
            sum1, sum2 = 0., 0.
            for m in groups:
                sum1 += chem_area_fractions[m]*UNIFAC_psis[k][m]
                sum3 = sum(chem_area_fractions[n]*UNIFAC_psis[m][n] for n in groups)
                sum2 -= chem_area_fractions[m]*UNIFAC_psis[m][k]/sum3

            chem_loggamma_groups[k] = subgroups[k].Q*(1. - log(sum1) + sum2)

        tot = sum([count*(loggamma_groups[group] - chem_loggamma_groups[group])
                   for group, count in groups.items()])
        loggammars.append(tot)

    return [exp(loggammacs[i]+loggammars[i]) for i in cmps]
def UNIFAC ( T , xs , chemgroups , cached = None , subgroup_data = None , interaction_data = None , modified = False ) : cmps = range ( len ( xs ) ) if subgroup_data is None : subgroups = UFSG else : subgroups = subgroup_data if interaction_data is None : interactions = UFIP else : interactions = interaction_data # Obtain r and q values using the subgroup values if not cached : rs = [ ] qs = [ ] for groups in chemgroups : ri = 0. qi = 0. for group , count in groups . items ( ) : ri += subgroups [ group ] . R * count qi += subgroups [ group ] . Q * count rs . append ( ri ) qs . append ( qi ) group_counts = { } for groups in chemgroups : for group , count in groups . items ( ) : if group in group_counts : group_counts [ group ] += count else : group_counts [ group ] = count else : rs , qs , group_counts = cached # Sum the denominator for calculating Xs group_sum = sum ( count * xs [ i ] for i in cmps for count in chemgroups [ i ] . values ( ) ) # Caclulate each numerator for calculating Xs group_count_xs = { } for group in group_counts : tot_numerator = sum ( chemgroups [ i ] [ group ] * xs [ i ] for i in cmps if group in chemgroups [ i ] ) group_count_xs [ group ] = tot_numerator / group_sum rsxs = sum ( [ rs [ i ] * xs [ i ] for i in cmps ] ) Vis = [ rs [ i ] / rsxs for i in cmps ] qsxs = sum ( [ qs [ i ] * xs [ i ] for i in cmps ] ) Fis = [ qs [ i ] / qsxs for i in cmps ] if modified : rsxs2 = sum ( [ rs [ i ] ** 0.75 * xs [ i ] for i in cmps ] ) Vis2 = [ rs [ i ] ** 0.75 / rsxs2 for i in cmps ] loggammacs = [ 1. - Vis2 [ i ] + log ( Vis2 [ i ] ) - 5. * qs [ i ] * ( 1. - Vis [ i ] / Fis [ i ] + log ( Vis [ i ] / Fis [ i ] ) ) for i in cmps ] else : loggammacs = [ 1. - Vis [ i ] + log ( Vis [ i ] ) - 5. * qs [ i ] * ( 1. - Vis [ i ] / Fis [ i ] + log ( Vis [ i ] / Fis [ i ] ) ) for i in cmps ] Q_sum_term = sum ( [ subgroups [ group ] . Q * group_count_xs [ group ] for group in group_counts ] ) area_fractions = { group : subgroups [ group ] . Q * group_count_xs [ group ] / Q_sum_term for group in group_counts . keys ( ) } UNIFAC_psis = { k : { m : ( UNIFAC_psi ( T , m , k , subgroups , interactions , modified = modified ) ) for m in group_counts } for k in group_counts } loggamma_groups = { } for k in group_counts : sum1 , sum2 = 0. , 0. for m in group_counts : sum1 += area_fractions [ m ] * UNIFAC_psis [ k ] [ m ] sum3 = sum ( area_fractions [ n ] * UNIFAC_psis [ m ] [ n ] for n in group_counts ) sum2 -= area_fractions [ m ] * UNIFAC_psis [ m ] [ k ] / sum3 loggamma_groups [ k ] = subgroups [ k ] . Q * ( 1. - log ( sum1 ) + sum2 ) loggammars = [ ] for groups in chemgroups : chem_loggamma_groups = { } chem_group_sum = sum ( groups . values ( ) ) chem_group_count_xs = { group : count / chem_group_sum for group , count in groups . items ( ) } Q_sum_term = sum ( [ subgroups [ group ] . Q * chem_group_count_xs [ group ] for group in groups ] ) chem_area_fractions = { group : subgroups [ group ] . Q * chem_group_count_xs [ group ] / Q_sum_term for group in groups . keys ( ) } for k in groups : sum1 , sum2 = 0. , 0. for m in groups : sum1 += chem_area_fractions [ m ] * UNIFAC_psis [ k ] [ m ] sum3 = sum ( chem_area_fractions [ n ] * UNIFAC_psis [ m ] [ n ] for n in groups ) sum2 -= chem_area_fractions [ m ] * UNIFAC_psis [ m ] [ k ] / sum3 chem_loggamma_groups [ k ] = subgroups [ k ] . Q * ( 1. - log ( sum1 ) + sum2 ) tot = sum ( [ count * ( loggamma_groups [ group ] - chem_loggamma_groups [ group ] ) for group , count in groups . items ( ) ] ) loggammars . append ( tot ) return [ exp ( loggammacs [ i ] + loggammars [ i ] ) for i in cmps ]
r'''Calculates activity coefficients using the UNIFAC model (optionally 
    modified), given a mixture's temperature, liquid mole fractions, 
    and optionally the subgroup data and interaction parameter data of your 
    choice. The default is to use the original UNIFAC model, with the latest 
    parameters published by DDBST. The model supports modified forms (Dortmund, 
    NIST) when the `modified` parameter is True.
            
    Parameters
    ----------
    T : float
        Temperature of the system, [K]
    xs : list[float]
        Mole fractions of all species in the system in the liquid phase, [-]
    chemgroups : list[dict]
        List of dictionaries of subgroup IDs and their counts for all species 
        in the mixture, [-]
    subgroup_data : dict[UNIFAC_subgroup]
        UNIFAC subgroup data; available dictionaries in this module are UFSG
        (original), DOUFSG (Dortmund), or NISTUFSG ([4]_).
    interaction_data : dict[dict[tuple(a_mn, b_mn, c_mn)]]
        UNIFAC interaction parameter data; available dictionaries in this 
        module are UFIP (original), DOUFIP2006 (Dortmund parameters as 
        published by 2006), DOUFIP2016 (Dortmund parameters as published by 
        2016), and NISTUFIP ([4]_).
    modified : bool
        True if using the modified form and temperature dependence, otherwise
        False.

    Returns
    -------
    gammas : list[float]
        Activity coefficients of all species in the mixture, [-]

    Notes
    -----
    The actual implementation of UNIFAC is formulated slightly different than
    the formulas above for computational efficiency. DDBST switched to using 
    the more efficient forms in their publication, but the numerical results
    are identical.
    
    The model is as follows:
        
    .. math::
        \ln \gamma_i =  \ln \gamma_i^c + \ln \gamma_i^r
        
    **Combinatorial component**
    
    .. math::
        \ln \gamma_i^c = \ln \frac{\phi_i}{x_i} + \frac{z}{2} q_i 
        \ln\frac{\theta_i}{\phi_i} + L_i - \frac{\phi_i}{x_i}
        \sum_{j=1}^{n} x_j L_j
        
        \theta_i = \frac{x_i q_i}{\sum_{j=1}^{n} x_j q_j} 
        
         \phi_i = \frac{x_i r_i}{\sum_{j=1}^{n} x_j r_j}
         
          L_i = 5(r_i - q_i)-(r_i-1)
   
    **Residual component**
    
    .. math::
        \ln \gamma_i^r = \sum_{k}^n \nu_k^{(i)} \left[ \ln \Gamma_k
        - \ln \Gamma_k^{(i)} \right]
        
        \ln \Gamma_k = Q_k \left[1 - \ln \sum_m \Theta_m \Psi_{mk} - \sum_m 
        \frac{\Theta_m \Psi_{km}}{\sum_n \Theta_n \Psi_{nm}}\right]
        
        \Theta_m = \frac{Q_m X_m}{\sum_{n} Q_n X_n}
        
        X_m = \frac{ \sum_j \nu^j_m x_j}{\sum_j \sum_n \nu_n^j x_j}
        
    **R and Q**
    
    .. math::
        r_i = \sum_{k=1}^{n} \nu_k R_k 
        
        q_i = \sum_{k=1}^{n}\nu_k Q_k
    
    The newer forms of UNIFAC (Dortmund, NIST) calculate the combinatorial
    part slightly differently:
        
    .. math::
        \ln \gamma_i^c = 1 - {V'}_i + \ln({V'}_i) - 5q_i \left(1
        - \frac{V_i}{F_i}+ \ln\left(\frac{V_i}{F_i}\right)\right)
        
        V'_i = \frac{r_i^{3/4}}{\sum_j r_j^{3/4}x_j}
        
    
    This is more clear when looking at the full rearranged form as in [3]_.

    Examples
    --------
    >>> UNIFAC(T=333.15, xs=[0.5, 0.5], chemgroups=[{1:2, 2:4}, {1:1, 2:1, 18:1}])
    [1.4276025835624173, 1.3646545010104225]
    
    >>> UNIFAC(373.15, [0.2, 0.3, 0.2, 0.2], 
    ... [{9:6}, {78:6}, {1:1, 18:1}, {1:1, 2:1, 14:1}],
    ... subgroup_data=DOUFSG, interaction_data=DOUFIP2006, modified=True)
    [1.186431113706829, 1.440280133911197, 1.204479833499608, 1.9720706090299824]

    References
    ----------
    .. [1] Gmehling, Jurgen. Chemical Thermodynamics: For Process Simulation.
       Weinheim, Germany: Wiley-VCH, 2012.
    .. [2] Fredenslund, Aage, Russell L. Jones, and John M. Prausnitz. "Group
       Contribution Estimation of Activity Coefficients in Nonideal Liquid 
       Mixtures." AIChE Journal 21, no. 6 (November 1, 1975): 1086-99. 
       doi:10.1002/aic.690210607.
    .. [3] Jakob, Antje, Hans Grensemann, Jürgen Lohmann, and Jürgen Gmehling. 
       "Further Development of Modified UNIFAC (Dortmund):  Revision and 
       Extension 5." Industrial & Engineering Chemistry Research 45, no. 23 
       (November 1, 2006): 7924-33. doi:10.1021/ie060355c.
    .. [4] Kang, Jeong Won, Vladimir Diky, and Michael Frenkel. "New Modified 
       UNIFAC Parameters Using Critically Evaluated Phase Equilibrium Data." 
       Fluid Phase Equilibria 388 (February 25, 2015): 128-41. 
       doi:10.1016/j.fluid.2014.12.042.
r Calculates activity coefficients using the UNIFAC model ( optionally modified ) given a mixture s temperature liquid mole fractions and optionally the subgroup data and interaction parameter data of your choice . The default is to use the original UNIFAC model with the latest parameters published by DDBST . The model supports modified forms ( Dortmund NIST ) when the modified parameter is True . Parameters ---------- T : float Temperature of the system [ K ] xs : list [ float ] Mole fractions of all species in the system in the liquid phase [ - ] chemgroups : list [ dict ] List of dictionaries of subgroup IDs and their counts for all species in the mixture [ - ] subgroup_data : dict [ UNIFAC_subgroup ] UNIFAC subgroup data ; available dictionaries in this module are UFSG ( original ) DOUFSG ( Dortmund ) or NISTUFSG ( [ 4 ] _ ) . interaction_data : dict [ dict [ tuple ( a_mn b_mn c_mn ) ]] UNIFAC interaction parameter data ; available dictionaries in this module are UFIP ( original ) DOUFIP2006 ( Dortmund parameters as published by 2006 ) DOUFIP2016 ( Dortmund parameters as published by 2016 ) and NISTUFIP ( [ 4 ] _ ) . modified : bool True if using the modified form and temperature dependence otherwise False .