r'''Returns the effectiveness of a heat exchanger at a specified heat 
    capacity rate, number of transfer units, and configuration. The following
    configurations are supported:
        
        * Counterflow (ex. double-pipe)
        * Parallel (ex. double pipe inefficient configuration)
        * Shell and tube exchangers with even numbers of tube passes,
          one or more shells in series
        * Crossflow, single pass, fluids unmixed
        * Crossflow, single pass, Cmax mixed, Cmin unmixed
        * Crossflow, single pass, Cmin mixed, Cmax unmixed
        * Boiler or condenser
    
    These situations are normally not those which occur in real heat exchangers,
    but are useful for academic purposes. More complicated expressions are 
    available for other methods. These equations are confirmed in [1]_,
    [2]_, and [3]_.
    
    For parallel flow heat exchangers:

    .. math::
        \epsilon = \frac{1 - \exp[-NTU(1+C_r)]}{1+C_r}

    For counterflow heat exchangers:

    .. math::
        \epsilon = \frac{1 - \exp[-NTU(1-C_r)]}{1-C_r\exp[-NTU(1-C_r)]},\; C_r < 1

        \epsilon = \frac{NTU}{1+NTU},\; C_r = 1

    For TEMA E shell-and-tube heat exchangers with one shell pass, 2n tube 
    passes:

    .. math::
        \epsilon_1 = 2\left\{1 + C_r + \sqrt{1+C_r^2}\times\frac{1+\exp
        [-(NTU)_1\sqrt{1+C_r^2}]}{1-\exp[-(NTU)_1\sqrt{1+C_r^2}]}\right\}^{-1}

    For TEMA E shell-and-tube heat exchangers with more than one shell pass, 2n  
    tube passes (this model assumes each exchanger has an equal share of the 
    overall NTU or said more plainly, the same UA):

    .. math::
        \epsilon = \left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^2
        -1\right]\left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^n
        - C_r\right]^{-1}

    For cross-flow (single-pass) heat exchangers with both fluids unmixed, there
    is an approximate and an exact formula. The approximate one is:

    .. math::
        \epsilon = 1 - \exp\left[\left(\frac{1}{C_r}\right)
        (NTU)^{0.22}\left\{\exp\left[C_r(NTU)^{0.78}\right]-1\right\}\right]
        
    The exact solution for crossflow (fluids unmixed) uses SciPy's quad
    to perform an integral (there is no analytical integral solution available).
    :math:`I_0(v)` is the modified Bessel function of the first kind. This formula
    was developed in [4]_.
    
    .. math::
        \epsilon = \frac{1}{C_r} - \frac{\exp(-C_r \cdot NTU)}{2(C_r NTU)^2}
        \int_0^{2 NTU\sqrt{C_r}} \left(1 + NTU - \frac{v^2}{4C_r NTU}\right)
        \exp\left(-\frac{v^2}{4C_r NTU}\right)v I_0(v) dv

    For cross-flow (single-pass) heat exchangers with Cmax mixed, Cmin unmixed:

    .. math::
        \epsilon = \left(\frac{1}{C_r}\right)(1 - \exp\left\{-C_r[1-\exp(-NTU)]\right\})

    For cross-flow (single-pass) heat exchangers with Cmin mixed, Cmax unmixed:

    .. math::
        \epsilon = 1 - \exp(-C_r^{-1}\{1 - \exp[-C_r(NTU)]\})

    For cases where `Cr` = 0, as in an exchanger with latent heat exchange,
    flow arrangement does not matter: 

    .. math::
        \epsilon = 1 - \exp(-NTU)

    Parameters
    ----------
    NTU : float
        Thermal Number of Transfer Units [-]
    Cr : float
        The heat capacity rate ratio, of the smaller fluid to the larger
        fluid, [-]
    subtype : str, optional
        The subtype of exchanger; one of 'counterflow', 'parallel', 'crossflow'
        'crossflow approximate', 'crossflow, mixed Cmin', 
        'crossflow, mixed Cmax', 'boiler', 'condenser', 'S&T', or 'nS&T' where 
        n is the number of shell and tube exchangers in a row.

    Returns
    -------
    effectiveness : float
        The thermal effectiveness of the heat exchanger, [-]

    Notes
    -----
    Once the effectiveness of the exchanger has been calculated, the total
    heat transferred can be calculated according to the following formulas,
    depending on which stream temperatures are known:
        
    If the inlet temperatures for both sides are known:
        
    .. math::
        Q=\epsilon C_{min}(T_{h,i}-T_{c,i})
        
    If the outlet temperatures for both sides are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_{hot}C_{cold}(T_{c,o}-T_{h,o})}
        {\epsilon  C_{min}(C_{hot} +C_{cold}) - (C_{hot}C_{cold}) }
    
    If the hot inlet and cold outlet are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_c(T_{co}-T_{hi})}{\epsilon C_{min}-C_c}
        
    If the hot outlet stream and cold inlet stream are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_h(T_{ci}-T_{ho})}{\epsilon C_{min}-C_h}
    
    
    If the inlet and outlet conditions for a single side are known, the
    effectiveness wasn't needed for it to be calculated. For completeness,
    the formulas are as follows:
        
    .. math::
        Q = C_c(T_{c,o} - T_{c,i}) = C_h(T_{h,i} - T_{h,o})
        
    There is also a term called :math:`Q_{max}`, which is the heat which would
    have been transferred if the effectiveness was 1. It is calculated as
    follows:
        
    .. math::
        Q_{max} = \frac{Q}{\text{effectiveness}}
        
    Examples
    --------
    Worst case, parallel flow:
    
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='parallel')
    0.5881156068417585
    
    Crossflow, somewhat higher effectiveness:
        
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='crossflow')
    0.8444821799748551

    Counterflow, better than either crossflow or parallel flow:

    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='counterflow')
    0.9206703686051108
    
    One shell and tube heat exchanger gives worse performance than counterflow,
    but they are designed to be economical and compact which a counterflow
    exchanger would not be. As the number of shells approaches infinity,
    the counterflow result is obtained exactly.
    
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='S&T')
    0.6834977044311439
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='50S&T')
    0.9205058702789254

    
    Overall case of rating an existing heat exchanger where a known flowrate
    of steam and oil are contacted in crossflow, with the steam side mixed
    (example 10-9 in [3]_):
        
    >>> U = 275 # W/m^2/K
    >>> A = 10.82 # m^2
    >>> Cp_oil = 1900 # J/kg/K
    >>> Cp_steam = 1860 # J/kg/K
    >>> m_steam = 5.2 # kg/s
    >>> m_oil = 0.725 # kg/s
    >>> Thi = 130 # °C
    >>> Tci = 15 # °C
    >>> Cmin = calc_Cmin(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> Cmax = calc_Cmax(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> Cr = calc_Cr(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> NTU = NTU_from_UA(UA=U*A, Cmin=Cmin)
    >>> eff = effectiveness_from_NTU(NTU=NTU, Cr=Cr, subtype='crossflow, mixed Cmax')
    >>> Q = eff*Cmin*(Thi - Tci)
    >>> Tco = Tci + Q/(m_oil*Cp_oil)
    >>> Tho = Thi - Q/(m_steam*Cp_steam)
    >>> Cmin, Cmax, Cr
    (1377.5, 9672.0, 0.14242142266335814)
    >>> NTU, eff, Q
    (2.160072595281307, 0.8312180361425988, 131675.32715043944)
    >>> Tco, Tho
    (110.59007415639887, 116.38592564614977)
    
    Alternatively, if only the outlet temperatures had been known:
        
    >>> Tco = 110.59007415639887
    >>> Tho = 116.38592564614977
    >>> Cc, Ch = Cmin, Cmax # In this case but not always
    >>> Q = eff*Cmin*Cc*Ch*(Tco - Tho)/(eff*Cmin*(Cc+Ch) - Ch*Cc)
    >>> Thi = Tho + Q/Ch
    >>> Tci = Tco - Q/Cc
    >>> Q, Tci, Thi
    (131675.32715043964, 14.999999999999858, 130.00000000000003)

    References
    ----------
    .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and
       David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ:
       Wiley, 2011.
    .. [2] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat 
       Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002.
    .. [3] Holman, Jack. Heat Transfer. 10th edition. Boston: McGraw-Hill 
       Education, 2009.
    .. [4] Triboix, Alain. "Exact and Approximate Formulas for Cross Flow Heat 
       Exchangers with Unmixed Fluids." International Communications in Heat 
       and Mass Transfer 36, no. 2 (February 1, 2009): 121-24. 
       doi:10.1016/j.icheatmasstransfer.2008.10.012.

def effectiveness_from_NTU(NTU, Cr, subtype='counterflow'):
    r'''Returns the effectiveness of a heat exchanger at a specified heat 
    capacity rate, number of transfer units, and configuration. The following
    configurations are supported:
        
        * Counterflow (ex. double-pipe)
        * Parallel (ex. double pipe inefficient configuration)
        * Shell and tube exchangers with even numbers of tube passes,
          one or more shells in series
        * Crossflow, single pass, fluids unmixed
        * Crossflow, single pass, Cmax mixed, Cmin unmixed
        * Crossflow, single pass, Cmin mixed, Cmax unmixed
        * Boiler or condenser
    
    These situations are normally not those which occur in real heat exchangers,
    but are useful for academic purposes. More complicated expressions are 
    available for other methods. These equations are confirmed in [1]_,
    [2]_, and [3]_.
    
    For parallel flow heat exchangers:

    .. math::
        \epsilon = \frac{1 - \exp[-NTU(1+C_r)]}{1+C_r}

    For counterflow heat exchangers:

    .. math::
        \epsilon = \frac{1 - \exp[-NTU(1-C_r)]}{1-C_r\exp[-NTU(1-C_r)]},\; C_r < 1

        \epsilon = \frac{NTU}{1+NTU},\; C_r = 1

    For TEMA E shell-and-tube heat exchangers with one shell pass, 2n tube 
    passes:

    .. math::
        \epsilon_1 = 2\left\{1 + C_r + \sqrt{1+C_r^2}\times\frac{1+\exp
        [-(NTU)_1\sqrt{1+C_r^2}]}{1-\exp[-(NTU)_1\sqrt{1+C_r^2}]}\right\}^{-1}

    For TEMA E shell-and-tube heat exchangers with more than one shell pass, 2n  
    tube passes (this model assumes each exchanger has an equal share of the 
    overall NTU or said more plainly, the same UA):

    .. math::
        \epsilon = \left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^2
        -1\right]\left[\left(\frac{1-\epsilon_1 C_r}{1-\epsilon_1}\right)^n
        - C_r\right]^{-1}

    For cross-flow (single-pass) heat exchangers with both fluids unmixed, there
    is an approximate and an exact formula. The approximate one is:

    .. math::
        \epsilon = 1 - \exp\left[\left(\frac{1}{C_r}\right)
        (NTU)^{0.22}\left\{\exp\left[C_r(NTU)^{0.78}\right]-1\right\}\right]
        
    The exact solution for crossflow (fluids unmixed) uses SciPy's quad
    to perform an integral (there is no analytical integral solution available).
    :math:`I_0(v)` is the modified Bessel function of the first kind. This formula
    was developed in [4]_.
    
    .. math::
        \epsilon = \frac{1}{C_r} - \frac{\exp(-C_r \cdot NTU)}{2(C_r NTU)^2}
        \int_0^{2 NTU\sqrt{C_r}} \left(1 + NTU - \frac{v^2}{4C_r NTU}\right)
        \exp\left(-\frac{v^2}{4C_r NTU}\right)v I_0(v) dv

    For cross-flow (single-pass) heat exchangers with Cmax mixed, Cmin unmixed:

    .. math::
        \epsilon = \left(\frac{1}{C_r}\right)(1 - \exp\left\{-C_r[1-\exp(-NTU)]\right\})

    For cross-flow (single-pass) heat exchangers with Cmin mixed, Cmax unmixed:

    .. math::
        \epsilon = 1 - \exp(-C_r^{-1}\{1 - \exp[-C_r(NTU)]\})

    For cases where `Cr` = 0, as in an exchanger with latent heat exchange,
    flow arrangement does not matter: 

    .. math::
        \epsilon = 1 - \exp(-NTU)

    Parameters
    ----------
    NTU : float
        Thermal Number of Transfer Units [-]
    Cr : float
        The heat capacity rate ratio, of the smaller fluid to the larger
        fluid, [-]
    subtype : str, optional
        The subtype of exchanger; one of 'counterflow', 'parallel', 'crossflow'
        'crossflow approximate', 'crossflow, mixed Cmin', 
        'crossflow, mixed Cmax', 'boiler', 'condenser', 'S&T', or 'nS&T' where 
        n is the number of shell and tube exchangers in a row.

    Returns
    -------
    effectiveness : float
        The thermal effectiveness of the heat exchanger, [-]

    Notes
    -----
    Once the effectiveness of the exchanger has been calculated, the total
    heat transferred can be calculated according to the following formulas,
    depending on which stream temperatures are known:
        
    If the inlet temperatures for both sides are known:
        
    .. math::
        Q=\epsilon C_{min}(T_{h,i}-T_{c,i})
        
    If the outlet temperatures for both sides are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_{hot}C_{cold}(T_{c,o}-T_{h,o})}
        {\epsilon  C_{min}(C_{hot} +C_{cold}) - (C_{hot}C_{cold}) }
    
    If the hot inlet and cold outlet are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_c(T_{co}-T_{hi})}{\epsilon C_{min}-C_c}
        
    If the hot outlet stream and cold inlet stream are known:
        
    .. math::
        Q = \frac{\epsilon C_{min}C_h(T_{ci}-T_{ho})}{\epsilon C_{min}-C_h}
    
    
    If the inlet and outlet conditions for a single side are known, the
    effectiveness wasn't needed for it to be calculated. For completeness,
    the formulas are as follows:
        
    .. math::
        Q = C_c(T_{c,o} - T_{c,i}) = C_h(T_{h,i} - T_{h,o})
        
    There is also a term called :math:`Q_{max}`, which is the heat which would
    have been transferred if the effectiveness was 1. It is calculated as
    follows:
        
    .. math::
        Q_{max} = \frac{Q}{\text{effectiveness}}
        
    Examples
    --------
    Worst case, parallel flow:
    
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='parallel')
    0.5881156068417585
    
    Crossflow, somewhat higher effectiveness:
        
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='crossflow')
    0.8444821799748551

    Counterflow, better than either crossflow or parallel flow:

    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='counterflow')
    0.9206703686051108
    
    One shell and tube heat exchanger gives worse performance than counterflow,
    but they are designed to be economical and compact which a counterflow
    exchanger would not be. As the number of shells approaches infinity,
    the counterflow result is obtained exactly.
    
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='S&T')
    0.6834977044311439
    >>> effectiveness_from_NTU(NTU=5, Cr=0.7, subtype='50S&T')
    0.9205058702789254

    
    Overall case of rating an existing heat exchanger where a known flowrate
    of steam and oil are contacted in crossflow, with the steam side mixed
    (example 10-9 in [3]_):
        
    >>> U = 275 # W/m^2/K
    >>> A = 10.82 # m^2
    >>> Cp_oil = 1900 # J/kg/K
    >>> Cp_steam = 1860 # J/kg/K
    >>> m_steam = 5.2 # kg/s
    >>> m_oil = 0.725 # kg/s
    >>> Thi = 130 # °C
    >>> Tci = 15 # °C
    >>> Cmin = calc_Cmin(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> Cmax = calc_Cmax(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> Cr = calc_Cr(mh=m_steam, mc=m_oil, Cph=Cp_steam, Cpc=Cp_oil)
    >>> NTU = NTU_from_UA(UA=U*A, Cmin=Cmin)
    >>> eff = effectiveness_from_NTU(NTU=NTU, Cr=Cr, subtype='crossflow, mixed Cmax')
    >>> Q = eff*Cmin*(Thi - Tci)
    >>> Tco = Tci + Q/(m_oil*Cp_oil)
    >>> Tho = Thi - Q/(m_steam*Cp_steam)
    >>> Cmin, Cmax, Cr
    (1377.5, 9672.0, 0.14242142266335814)
    >>> NTU, eff, Q
    (2.160072595281307, 0.8312180361425988, 131675.32715043944)
    >>> Tco, Tho
    (110.59007415639887, 116.38592564614977)
    
    Alternatively, if only the outlet temperatures had been known:
        
    >>> Tco = 110.59007415639887
    >>> Tho = 116.38592564614977
    >>> Cc, Ch = Cmin, Cmax # In this case but not always
    >>> Q = eff*Cmin*Cc*Ch*(Tco - Tho)/(eff*Cmin*(Cc+Ch) - Ch*Cc)
    >>> Thi = Tho + Q/Ch
    >>> Tci = Tco - Q/Cc
    >>> Q, Tci, Thi
    (131675.32715043964, 14.999999999999858, 130.00000000000003)

    References
    ----------
    .. [1] Bergman, Theodore L., Adrienne S. Lavine, Frank P. Incropera, and
       David P. DeWitt. Introduction to Heat Transfer. 6E. Hoboken, NJ:
       Wiley, 2011.
    .. [2] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat 
       Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002.
    .. [3] Holman, Jack. Heat Transfer. 10th edition. Boston: McGraw-Hill 
       Education, 2009.
    .. [4] Triboix, Alain. "Exact and Approximate Formulas for Cross Flow Heat 
       Exchangers with Unmixed Fluids." International Communications in Heat 
       and Mass Transfer 36, no. 2 (February 1, 2009): 121-24. 
       doi:10.1016/j.icheatmasstransfer.2008.10.012.
    '''
    if Cr > 1:
        raise Exception('Heat capacity rate must be less than 1 by definition.')
        
    if subtype == 'counterflow':
        if Cr < 1:
            return (1. - exp(-NTU*(1. - Cr)))/(1. - Cr*exp(-NTU*(1. - Cr)))
        elif Cr == 1:
            return NTU/(1. + NTU)
    elif subtype == 'parallel':
            return (1. - exp(-NTU*(1. + Cr)))/(1. + Cr)
    elif 'S&T' in subtype:
        str_shells = subtype.split('S&T')[0]
        shells = int(str_shells) if str_shells else 1
        NTU = NTU/shells
        
        top = 1. + exp(-NTU*(1. + Cr**2)**.5)
        bottom = 1. - exp(-NTU*(1. + Cr**2)**.5)
        effectiveness = 2./(1. + Cr + (1. + Cr**2)**.5*top/bottom)
        if shells > 1:
            term = ((1. - effectiveness*Cr)/(1. - effectiveness))**shells
            effectiveness = (term - 1.)/(term - Cr)
        return effectiveness
    elif subtype == 'crossflow':
        def to_int(v, NTU, Cr):
            return (1. + NTU - v*v/(4.*Cr*NTU))*exp(-v*v/(4.*Cr*NTU))*v*iv(0, v)
        int_term = quad(to_int, 0, 2.*NTU*Cr**0.5, args=(NTU, Cr))[0]
        return 1./Cr - exp(-Cr*NTU)/(2.*(Cr*NTU)**2)*int_term
    elif subtype == 'crossflow approximate':
        return 1. - exp(1./Cr*NTU**0.22*(exp(-Cr*NTU**0.78) - 1.))
    elif subtype == 'crossflow, mixed Cmin':
        return 1. -exp(-Cr**-1*(1. - exp(-Cr*NTU)))
    elif subtype ==  'crossflow, mixed Cmax':
        return (1./Cr)*(1. - exp(-Cr*(1. - exp(-NTU))))
    elif subtype in ['boiler', 'condenser']:
        return  1. - exp(-NTU)
    else:
        raise Exception('Input heat exchanger type not recognized')