r'''Returns the number of transfer units of a plate heat exchanger
    with a specified side 1 heat capacity ratio `R1`, side 1 number
    of transfer units `NTU1`, number of passes on sides 1 and 2 (respectively
    `Np1` and `Np2`). 
            
    For all cases, the function also takes as arguments whether the exchanger 
    is setup in an overall counter or parallel orientation `counterflow`, and 
    whether or not individual stream passes are themselves counterflow or
    parallel. 
    
    The 20 supported cases are as follows. (the first number of sides listed
    refers to side 1, and the second number refers to side 2):
        
    * 1 pass/1 pass parallelflow
    * 1 pass/1 pass counterflow
    * 1 pass/2 pass
    * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in parallel)
    * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in counterflow)
    * 1 pass/4 pass 
    * 2 pass/2 pass, overall parallelflow, individual passes in parallel 
    * 2 pass/2 pass, overall parallelflow, individual passes counterflow
    * 2 pass/2 pass, overall counterflow, individual passes parallelflow 
    * 2 pass/2 pass, overall counterflow, individual passes counterflow 
    * 2 pass/3 pass or 3 pass/2 pass, overall parallelflow 
    * 2 pass/3 pass or 3 pass/2 pass, overall counterflow
    * 2 pass/4 pass or 4 pass/2 pass, overall parallel flow
    * 2 pass/4 pass or 4 pass/2 pass, overall counterflow flow
    
    For all except the simplest cases numerical solutions are used.
    
    1 pass/1 pass counterflow (also 2/2 fully counterflow):
    
    .. math::
        NTU_1 = - \frac{1}{R_{1} - 1} \log{\left (\frac{P_{1} R_{1} - 1}{P_{1} 
        - 1} \right )}
    
    1 pass/1 pass parallel flow (also 2/2 fully parallelflow):
    
    .. math::
        NTU_1 = \frac{1}{R_{1} + 1} \log{\left (- \frac{1}{P_{1} \left(R_{1} 
        + 1\right) - 1} \right )}
                
    Parameters
    ----------
    P1 : float
        Thermal effectiveness of the heat exchanger in the P-NTU method,
        calculated with respect to stream 1 [-]
    R1 : float
        Heat capacity ratio of the heat exchanger in the P-NTU method,
        calculated with respect to stream 1 [-]
    Np1 : int
        Number of passes on side 1 [-]
    Np2 : int
        Number of passes on side 2 [-]
    counterflow : bool
        Whether or not the overall flow through the heat exchanger is in
        counterflow or parallel flow, [-]
    passes_counterflow : bool
        In addition to the overall flow direction, in some cases individual 
        passes may be in counter or parallel flow; this controlls that [-]
    reverse : bool
        Used **internally only** to allow cases like the 1-4 formula to work  
        for the 4-1 flow case, without having to duplicate the code [-]

    Returns
    -------
    NTU1 : float
        Thermal number of transfer units of the heat exchanger in the P-NTU 
        method, calculated with respect to stream 1 [-]

    Notes
    -----
    The defaults of counterflow=True and passes_counterflow=True will always
    result in the most efficient heat exchanger option, normally what is
    desired.
    
    If a number of passes which is not supported is provided, an exception is
    raised.
    
    For more details, see :obj:`temperature_effectiveness_plate`.

    Examples
    --------
    Three passes on side 1; one pass on side 2; two end passes in counterflow
    orientation.
    
    >>> NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=1)
    0.9998336056060733

def NTU_from_P_plate(P1, R1, Np1, Np2, counterflow=True, 
                     passes_counterflow=True, reverse=False):
    r'''Returns the number of transfer units of a plate heat exchanger
    with a specified side 1 heat capacity ratio `R1`, side 1 number
    of transfer units `NTU1`, number of passes on sides 1 and 2 (respectively
    `Np1` and `Np2`). 
            
    For all cases, the function also takes as arguments whether the exchanger 
    is setup in an overall counter or parallel orientation `counterflow`, and 
    whether or not individual stream passes are themselves counterflow or
    parallel. 
    
    The 20 supported cases are as follows. (the first number of sides listed
    refers to side 1, and the second number refers to side 2):
        
    * 1 pass/1 pass parallelflow
    * 1 pass/1 pass counterflow
    * 1 pass/2 pass
    * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in parallel)
    * 1 pass/3 pass or 3 pass/1 pass (with the two end passes in counterflow)
    * 1 pass/4 pass 
    * 2 pass/2 pass, overall parallelflow, individual passes in parallel 
    * 2 pass/2 pass, overall parallelflow, individual passes counterflow
    * 2 pass/2 pass, overall counterflow, individual passes parallelflow 
    * 2 pass/2 pass, overall counterflow, individual passes counterflow 
    * 2 pass/3 pass or 3 pass/2 pass, overall parallelflow 
    * 2 pass/3 pass or 3 pass/2 pass, overall counterflow
    * 2 pass/4 pass or 4 pass/2 pass, overall parallel flow
    * 2 pass/4 pass or 4 pass/2 pass, overall counterflow flow
    
    For all except the simplest cases numerical solutions are used.
    
    1 pass/1 pass counterflow (also 2/2 fully counterflow):
    
    .. math::
        NTU_1 = - \frac{1}{R_{1} - 1} \log{\left (\frac{P_{1} R_{1} - 1}{P_{1} 
        - 1} \right )}
    
    1 pass/1 pass parallel flow (also 2/2 fully parallelflow):
    
    .. math::
        NTU_1 = \frac{1}{R_{1} + 1} \log{\left (- \frac{1}{P_{1} \left(R_{1} 
        + 1\right) - 1} \right )}
                
    Parameters
    ----------
    P1 : float
        Thermal effectiveness of the heat exchanger in the P-NTU method,
        calculated with respect to stream 1 [-]
    R1 : float
        Heat capacity ratio of the heat exchanger in the P-NTU method,
        calculated with respect to stream 1 [-]
    Np1 : int
        Number of passes on side 1 [-]
    Np2 : int
        Number of passes on side 2 [-]
    counterflow : bool
        Whether or not the overall flow through the heat exchanger is in
        counterflow or parallel flow, [-]
    passes_counterflow : bool
        In addition to the overall flow direction, in some cases individual 
        passes may be in counter or parallel flow; this controlls that [-]
    reverse : bool
        Used **internally only** to allow cases like the 1-4 formula to work  
        for the 4-1 flow case, without having to duplicate the code [-]

    Returns
    -------
    NTU1 : float
        Thermal number of transfer units of the heat exchanger in the P-NTU 
        method, calculated with respect to stream 1 [-]

    Notes
    -----
    The defaults of counterflow=True and passes_counterflow=True will always
    result in the most efficient heat exchanger option, normally what is
    desired.
    
    If a number of passes which is not supported is provided, an exception is
    raised.
    
    For more details, see :obj:`temperature_effectiveness_plate`.

    Examples
    --------
    Three passes on side 1; one pass on side 2; two end passes in counterflow
    orientation.
    
    >>> NTU_from_P_plate(P1=0.5743, R1=1/3., Np1=3, Np2=1)
    0.9998336056060733
    '''
    NTU_min = 1E-11
    function = temperature_effectiveness_plate
    if Np1 == 1 and Np2 == 1 and counterflow:
        try:
            return -log((P1*R1 - 1.)/(P1 - 1.))/(R1 - 1.)
        except ValueError:
            raise ValueError('The maximum P1 obtainable at the specified R1 is %f at the limit of NTU1=inf.' %(1./R1))
        
    elif Np1 == 1 and Np2 == 1 and not counterflow:
        try:
            return log(-1./(P1*(R1 + 1.) - 1.))/(R1 + 1.)
        except ValueError:
            raise ValueError('The maximum P1 obtainable at the specified R1 is %f at the limit of NTU1=inf.' %Pp(1E10, R1))
    elif Np1 == 1 and Np2 == 2:
        NTU_max = 100.
    elif Np1 == 1 and Np2 == 3 and counterflow:
        NTU_max = 100.
    elif Np1 == 1 and Np2 == 3 and not counterflow:
        NTU_max = 100.
    elif Np1 == 1 and Np2 == 4:
        NTU_max = 100.
    elif Np1 == 2 and Np2 == 2:
        if counterflow and passes_counterflow:
            return NTU_from_P_plate(P1, R1, Np1=1, Np2=1, counterflow=True, 
                                    passes_counterflow=True)
        elif counterflow and not passes_counterflow:
            NTU_max = 100
        elif not counterflow and passes_counterflow:
            NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_2_parallel_counterflow, R1)
        elif not counterflow and not passes_counterflow:
            return NTU_from_P_plate(P1, R1, Np1=1, Np2=1, counterflow=False, 
                                    passes_counterflow=False)
    elif Np1 == 2 and Np2 == 3:
        if counterflow:
            NTU_max = 100
        elif not counterflow:
            NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_3_parallel, R1)
    elif Np1 == 2 and Np2 == 4:
        if counterflow:
            NTU_max = 100
        elif not counterflow:
            NTU_max = _NTU_max_for_P_solver(NTU_from_plate_2_4_parallel, R1)
    elif not reverse:
        # Proved to work by example
        P2 = P1*R1
        R2 = 1./R1
        NTU2 = NTU_from_P_plate(R1=R2, P1=P2, Np1=Np2, Np2=Np1,
                                counterflow=counterflow, 
                                passes_counterflow=passes_counterflow, 
                                reverse=True)
        NTU1 = NTU2/R1
        return NTU1
    else:
        raise Exception('Supported number of passes does not have a formula available')
    return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, function, Np1=Np1, 
                              Np2=Np2, counterflow=counterflow, 
                              passes_counterflow=passes_counterflow)