r'''Determine the bundle diameter required to fit a specified number of
    tubes in a heat exchanger. Uses the highly accurate method of [1]_,
    which takes into account pitch, number of tube passes, angle, 
    and tube diameter. The method is analytically correct when used in the
    other direction (calculating number of tubes from bundle diameter); in
    reverse, it is solved by bisection.

    Parameters
    ----------
    Ntubes : int
        Total number of tubes that fit in the heat exchanger, [-]
    Do : float
        Tube outer diameter, [m]
    pitch : float
        Pitch; distance between two orthogonal tube centers, [m]
    Ntp : int
        Number of tube passes, [-]
    angle : float, optional
        The angle the tubes are positioned; 30, 45, 60 or 90, [degrees]

    Returns
    -------
    DBundle : float
        Outer diameter of tube bundle, [m]

    Notes
    -----
    This function will fail when there are more than 100,000 tubes. There are 
    a range of correct diameters for which there can be the given number of 
    tubes; a number within that range is returned as found by bisection.

    Examples
    --------
    >>> DBundle_for_Ntubes_Phadkeb(Ntubes=782, Do=.028, pitch=.036, Ntp=2, angle=45.)
    1.1879392959379533

    References
    ----------
    .. [1] Phadke, P. S., Determining tube counts for shell and tube
       exchangers, Chem. Eng., September, 91, 65-68 (1984).

def DBundle_for_Ntubes_Phadkeb(Ntubes, Do, pitch, Ntp, angle=30):
    r'''Determine the bundle diameter required to fit a specified number of
    tubes in a heat exchanger. Uses the highly accurate method of [1]_,
    which takes into account pitch, number of tube passes, angle, 
    and tube diameter. The method is analytically correct when used in the
    other direction (calculating number of tubes from bundle diameter); in
    reverse, it is solved by bisection.

    Parameters
    ----------
    Ntubes : int
        Total number of tubes that fit in the heat exchanger, [-]
    Do : float
        Tube outer diameter, [m]
    pitch : float
        Pitch; distance between two orthogonal tube centers, [m]
    Ntp : int
        Number of tube passes, [-]
    angle : float, optional
        The angle the tubes are positioned; 30, 45, 60 or 90, [degrees]

    Returns
    -------
    DBundle : float
        Outer diameter of tube bundle, [m]

    Notes
    -----
    This function will fail when there are more than 100,000 tubes. There are 
    a range of correct diameters for which there can be the given number of 
    tubes; a number within that range is returned as found by bisection.

    Examples
    --------
    >>> DBundle_for_Ntubes_Phadkeb(Ntubes=782, Do=.028, pitch=.036, Ntp=2, angle=45.)
    1.1879392959379533

    References
    ----------
    .. [1] Phadke, P. S., Determining tube counts for shell and tube
       exchangers, Chem. Eng., September, 91, 65-68 (1984).
    '''
    if angle == 30 or angle == 60:
        Ns = triangular_Ns[-1]
    elif angle == 45 or angle == 90:
        Ns = square_Ns[-1]
    s = Ns + 1
    r = s**0.5
    DBundle_max = (Do + 2.*pitch*r)*(1. - 1E-8) # Cannot be exact or floor(s) will give an int too high
    def to_solve(DBundle):
        ans = Ntubes_Phadkeb(DBundle=DBundle, Do=Do, pitch=pitch, Ntp=Ntp, angle=angle) - Ntubes
        return ans
    return sp_bisect(to_solve, 0, DBundle_max)