r'''This function calculates the heat transfer coefficient for internal
    convection inside a circular pipe. 
    
    Requires at a minimum a flow's Reynolds and Prandtl numbers `Re` and `Pr`. 
    Relative roughness `eD` can be specified to include the enhancement of heat 
    transfer from the added turbulence. 
    
    For laminar flow, thermally and hydraulically developing flow is supported
    with the pipe diameter `Di` and distance `x` is provided.
    
    If no correlation's name is provided as `Method`, the most accurate 
    applicable correlation is selected.
    
    * If laminar, `x` and `Di` provided:  'Baehr-Stephan laminar thermal/velocity entry'
    * Otherwise if laminar, no entry information provided: 'Laminar - constant T' (Nu = 3.66)
    * If turbulent and `Pr` < 0.03: 'Martinelli'
    * If turbulent, `x` and `Di` provided: 'Hausen'
    * Otherwise if turbulent: 'Churchill-Zajic'

    Parameters
    ----------
    Re : float
        Reynolds number, [-]
    Pr : float
        Prandtl number, [-]
    eD : float, optional
        Relative roughness, [-]
    Di : float, optional
        Inside diameter of pipe, [m]
    x : float, optional
        Length inside of pipe for calculation, [m]
    fd : float, optoinal
        Darcy friction factor [-]

    Returns
    -------
    Nu : float
        Nusselt number, [-]
    methods : list, only returned if AvailableMethods == True
        List of methods which can be used to calculate `Nu` with the given inputs

    Other Parameters
    ----------------
    Method : string, optional
        A string of the function name to use, as in the dictionary
        vertical_cylinder_correlations
    AvailableMethods : bool, optional
        If True, function will consider which methods which can be used to
        calculate Nu with the given inputs
        
    Examples
    --------
    Turbulent example
    
    >>> Nu_conv_internal(Re=1E5, Pr=.7)
    183.71057902604906

    Entry length - laminar example
    
    >>> Nu_conv_internal(Re=1E2, Pr=.7, x=.01, Di=.1)
    14.91799128769779

def Nu_conv_internal(Re, Pr, eD=0, Di=None, x=None, fd=None, Method=None, 
                     AvailableMethods=False):
    r'''This function calculates the heat transfer coefficient for internal
    convection inside a circular pipe. 
    
    Requires at a minimum a flow's Reynolds and Prandtl numbers `Re` and `Pr`. 
    Relative roughness `eD` can be specified to include the enhancement of heat 
    transfer from the added turbulence. 
    
    For laminar flow, thermally and hydraulically developing flow is supported
    with the pipe diameter `Di` and distance `x` is provided.
    
    If no correlation's name is provided as `Method`, the most accurate 
    applicable correlation is selected.
    
    * If laminar, `x` and `Di` provided:  'Baehr-Stephan laminar thermal/velocity entry'
    * Otherwise if laminar, no entry information provided: 'Laminar - constant T' (Nu = 3.66)
    * If turbulent and `Pr` < 0.03: 'Martinelli'
    * If turbulent, `x` and `Di` provided: 'Hausen'
    * Otherwise if turbulent: 'Churchill-Zajic'

    Parameters
    ----------
    Re : float
        Reynolds number, [-]
    Pr : float
        Prandtl number, [-]
    eD : float, optional
        Relative roughness, [-]
    Di : float, optional
        Inside diameter of pipe, [m]
    x : float, optional
        Length inside of pipe for calculation, [m]
    fd : float, optoinal
        Darcy friction factor [-]

    Returns
    -------
    Nu : float
        Nusselt number, [-]
    methods : list, only returned if AvailableMethods == True
        List of methods which can be used to calculate `Nu` with the given inputs

    Other Parameters
    ----------------
    Method : string, optional
        A string of the function name to use, as in the dictionary
        vertical_cylinder_correlations
    AvailableMethods : bool, optional
        If True, function will consider which methods which can be used to
        calculate Nu with the given inputs
        
    Examples
    --------
    Turbulent example
    
    >>> Nu_conv_internal(Re=1E5, Pr=.7)
    183.71057902604906

    Entry length - laminar example
    
    >>> Nu_conv_internal(Re=1E2, Pr=.7, x=.01, Di=.1)
    14.91799128769779
    '''
    def list_methods():
        methods = []
        if Re < LAMINAR_TRANSITION_PIPE:
            # Laminar!
            if all((Re, Pr, x, Di)):
                methods.append('Baehr-Stephan laminar thermal/velocity entry')
                methods.append('Hausen laminar thermal entry')
                methods.append('Seider-Tate laminar thermal entry')

            methods.append('Laminar - constant T')
            methods.append('Laminar - constant Q')
        else:
            if all((Re, Pr)) and Pr < 0.03:
                # Liquid metals
                methods.append('Martinelli')
            if all((Re, Pr, Di, x)):
                methods.append('Hausen')
            if Re and Pr and (eD is not None or fd is not None):
                # handle correlations with roughness
                methods.append('Churchill-Zajic')
                methods.append('Petukhov-Kirillov-Popov')
                methods.append('Gnielinski')
                methods.append('Bhatti-Shah')
                methods.append('Dipprey-Sabersky')
                methods.append('Sandall')
                methods.append('Webb')
                methods.append('Friend-Metzner')
                methods.append('Prandtl')
                methods.append('von-Karman')
                methods.append('Gowen-Smith')
                methods.append('Kawase-Ulbrecht')
                methods.append('Kawase-De')
                methods.append('Nunner')
            if Re and Pr:
                methods.append('Dittus-Boelter')
                methods.append('Sieder-Tate')
                methods.append('Drexel-McAdams')
                methods.append('Colburn')
                methods.append('ESDU')
                methods.append('Gnielinski smooth low Pr') # 1
                methods.append('Gnielinski smooth high Pr') # 2
        return methods

    if AvailableMethods:
        return list_methods()
    if not Method:
        Method = list_methods()[0]
        
    if eD is not None and fd is None:
        fd = friction_factor(Re=Re, eD=eD)

    if Method == 'Laminar - constant T':
        Nu = laminar_T_const()
    elif Method == 'Laminar - constant Q':
        Nu = laminar_Q_const()
    elif Method == 'Baehr-Stephan laminar thermal/velocity entry':
        Nu = laminar_entry_thermal_Hausen(Re=Re, Pr=Pr, L=x, Di=Di)
    elif Method == 'Hausen laminar thermal entry':
        Nu = laminar_entry_Seider_Tate(Re=Re, Pr=Pr, L=x, Di=Di)
    elif Method == 'Seider-Tate laminar thermal entry':
        Nu = laminar_entry_Baehr_Stephan(Re=Re, Pr=Pr, L=x, Di=Di)
    elif Method == 'Churchill-Zajic':
        Nu = turbulent_Churchill_Zajic(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Petukhov-Kirillov-Popov':
        Nu = turbulent_Petukhov_Kirillov_Popov(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Gnielinski':
        Nu = turbulent_Gnielinski(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Sandall':
        Nu = turbulent_Sandall(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Webb':
        Nu = turbulent_Webb(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Friend-Metzner':
        Nu = turbulent_Friend_Metzner(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Prandtl':
        Nu = turbulent_Prandtl(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'von-Karman':
        Nu = turbulent_von_Karman(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Martinelli':
        Nu = turbulent_Martinelli(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Gowen-Smith':
        Nu = turbulent_Gowen_Smith(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Kawase-Ulbrecht':
        Nu = turbulent_Kawase_Ulbrecht(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Kawase-De':
        Nu = turbulent_Kawase_De(Re=Re, Pr=Pr, fd=fd)
    elif Method == 'Dittus-Boelter':
        Nu = turbulent_Dittus_Boelter(Re=Re, Pr=Pr)
    elif Method == 'Sieder-Tate':
        Nu = turbulent_Sieder_Tate(Re=Re, Pr=Pr)
    elif Method == 'Drexel-McAdams':
        Nu = turbulent_Drexel_McAdams(Re=Re, Pr=Pr)
    elif Method == 'Colburn':
        Nu = turbulent_Colburn(Re=Re, Pr=Pr)
    elif Method == 'ESDU':
        Nu = turbulent_ESDU(Re=Re, Pr=Pr)
    elif Method == 'Gnielinski smooth low Pr':
        Nu = turbulent_Gnielinski_smooth_1(Re=Re, Pr=Pr)
    elif Method == 'Gnielinski smooth high Pr':
        Nu = turbulent_Gnielinski_smooth_2(Re=Re, Pr=Pr)
    elif Method == 'Hausen':
        Nu = turbulent_entry_Hausen(Re=Re, Pr=Pr, Di=Di, x=x)
    elif Method == 'Bhatti-Shah':
        Nu = turbulent_Bhatti_Shah(Re=Re, Pr=Pr, fd=fd, eD=eD)
    elif Method == 'Dipprey-Sabersky':
        Nu = turbulent_Dipprey_Sabersky(Re=Re, Pr=Pr, fd=fd, eD=eD)
    elif Method == 'Nunner':
        fd_smooth = friction_factor(Re, eD=0)
        Nu = turbulent_Nunner(Re=Re, Pr=Pr, fd=fd, fd_smooth=fd_smooth)
    else:
        raise Exception("Correlation name not recognized; see the "
                        "documentation for the available options.")
    return Nu