r'''Wrapper for the various P-NTU method function calls,
    which can solve a heat exchanger. The heat capacities and mass flows
    of each stream and the type of the heat exchanger are always required.
    As additional inputs, one combination of the following inputs is required:
    
    * Three of the four inlet and outlet stream temperatures.
    * Temperatures for the side 1 outlet and side 2 inlet and UA
    * Temperatures for the side 1 outlet side 2 outlet and UA
    * Temperatures for the side 1 inlet and side 2 inlet and UA
    * Temperatures for the side 1 inlet and side 2 outlet and UA

    Computes the total heat exchanged as well as both temperatures of both
    streams.
      
    Parameters
    ----------
    m1 : float
        Mass flow rate of stream 1 (shell side = 1, tube side = 2), [kg/s]
    m2 : float
        Mass flow rate of stream 2 (shell side = 1, tube side = 2), [kg/s]
    Cp1 : float
        Averaged heat capacity of stream 1 (shell side), [J/kg/K]
    Cp2 : float
        Averaged heat capacity of stream 2 (tube side), [J/kg/K]
    UA : float, optional
        Combined Area-heat transfer coefficient term, [W/K]
    T1i : float, optional
        Inlet temperature of stream 1 (shell side), [K]
    T1o : float, optional
        Outlet temperature of stream 1 (shell side), [K]
    T2i : float, optional
        Inlet temperature of stream 2 (tube side), [K]
    T2o : float, optional
        Outlet temperature of stream 2 (tube-side), [K]
    subtype : str, optional
        The subtype of exchanger; one of 'E', 'G', 'H', 'J', 'counterflow',
        'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2',
        or 'crossflow, mixed 1&2'. For plate exchangers 'Np1/Np2' where `Np1`
        is the number of side 1 passes and `Np2` is the number of side 2 passes
    Ntp : int, optional
        For real heat exchangers (types 'E', 'G', 'H', and 'J'), the number of 
        tube passes needss to be specified as well. Not all types support
        any number of tube passes.
    optimal : bool, optional
        For real heat exchangers (types 'E', 'G', 'H', and 'J'), there is often
        a more countercurrent (optimal) way to arrange the tube passes and a
        more parallel (optimal=False) way to arrange them. This controls that.

    Returns
    -------
    results : dict
        * Q : Heat exchanged in the heat exchanger, [W]
        * UA : Combined area-heat transfer coefficient term, [W/K]
        * T1i : Inlet temperature of stream 1, [K]
        * T1o : Outlet temperature of stream 1, [K]
        * T2i : Inlet temperature of stream 2, [K]
        * T2o : Outlet temperature of stream 2, [K]
        * P1 : Thermal effectiveness with respect to stream 1, [-]
        * P2 : Thermal effectiveness with respect to stream 2, [-]
        * R1 : Heat capacity ratio with respect to stream 1, [-]
        * R2 : Heat capacity ratio with respect to stream 2, [-]
        * C1 : The heat capacity rate of fluid 1, [W/K]
        * C2 : The heat capacity rate of fluid 2, [W/K]
        * NTU1 : Thermal Number of Transfer Units with respect to stream 1 [-]
        * NTU2 : Thermal Number of Transfer Units with respect to stream 2 [-]
    
    Notes
    -----
    The main equations used in this method are as follows. For the individual
    expressions used to calculate `P1`, see the `See Also` section.
    
    .. math::
        Q = P_1 C_1 \Delta T_{max} = P_2 C_2 \Delta T_{max}
        
        \Delta T_{max} = T_{h,i} - T_{c,i} = |T_{2,i} - T_{1,i}|
        
        R_1 = \frac{C_1}{C_2} = \frac{T_{2,i} - T_{2,o}}{T_{1,o} - T_{1, i}}
        
        R_2 = \frac{C_2}{C_1} = \frac{T_{1,o} - T_{1, i}}{T_{2,i} - T_{2,o}}

        R_1 = \frac{1}{R_2}
        
        NTU_1 = \frac{UA}{C_1}
        
        NTU_2 = \frac{UA}{C_2}
        
        NTU_1 = NTU_2 R_2
        
        NTU_2 = NTU_1 R_1
        
        P_1 = \frac{T_{1,o} - T_{1,i}}{T_{2,i} - T_{1,i}}
        
        P_2 = \frac{T_{2,i} - T_{2,o}}{T_{2,i} - T_{1,i}}
        
        P_1 = P_2 R_2
        
        P_2 = P_1 R_1
        
        C_1 = m_1 Cp_1
        
        C_2 = m_2 Cp_2
        
    Once `P1` has been calculated, there are six different cases for calculating
    the other stream temperatures depending on the two temperatures provided. 
    They were derived with SymPy.
        
    Two known inlet temperatures:
        
    .. math::
        T_{1,o} = - P_{1} T_{1,i} + P_{1} T_{2,i} + T_{1,i}
        
        T_{2,o} = P_{1} R_{1} T_{1,i} - P_{1} R_{1} T_{2,i} + T_{2,i}
        
    Two known outlet temperatures:
        
    .. math::
        T_{1,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o} 
        - T_{1,o}}{P_{1} R_{1} + P_{1} - 1}
        
        T_{2,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o}
        - T_{2,o}}{P_{1} R_{1} + P_{1} - 1}
        
    Inlet 1 known, outlet 2 known:
        
    .. math::
        T_{1,o} = \frac{1}{P_{1} R_{1} - 1} \left(P_{1} R_{1} T_{1,i}
        + P_{1} T_{1,i} - P_{1} T_{2,o} - T_{1,i}\right)
        
        T_{2,i} = \frac{P_{1} R_{1} T_{1,i} - T_{2,o}}{P_{1} R_{1} - 1}
        
    Outlet 1 known, inlet 2 known:
        
    .. math::
        T_{1,i} = \frac{P_{1} T_{2,i} - T_{1,o}}{P_{1} - 1}
        
        T_{2,o}  = \frac{1}{P_{1} - 1} \left(R_{1} \left(P_{1} T_{2,i}
        - T_{1,o}\right) - \left(P_{1} - 1\right) \left(R_{1} T_{1,o}
        - T_{2,i}\right)\right)
    
    Input and output of 2 known:
        
    .. math::
        T_{1,i} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} 
        - T_{2,i} + T_{2,o}\right)
        
        T_{1,o} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} 
        + \left(P_{1} - 1\right) \left(T_{2,i} - T_{2,o}\right)\right)
        
    Input and output of 1 known:
        
    .. math::
        T_{2,i} = \frac{1}{P_{1}} \left(P_{1} T_{1,i} - T_{1,i} 
        + T_{1,o}\right)
        
        T_{2,o} = \frac{1}{P_{1}} \left(P_{1} R_{1} \left(T_{1,i} 
        - T_{1,o}\right) + P_{1} T_{1,i} - T_{1,i} + T_{1,o}\right)
        
    See also
    --------
    temperature_effectiveness_basic
    temperature_effectiveness_plate
    temperature_effectiveness_TEMA_E
    temperature_effectiveness_TEMA_G
    temperature_effectiveness_TEMA_H
    temperature_effectiveness_TEMA_J
    NTU_from_P_basic
    NTU_from_P_plate
    NTU_from_P_E
    NTU_from_P_G
    NTU_from_P_H
    NTU_from_P_J

    Examples
    --------
    Solve a heat exchanger with the UA specified, and known inlet temperatures:
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, 
    ... subtype='E', Ntp=4, T2i=15, T1i=130, UA=3041.75))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.3144902812241522,
     'NTU2': 1.1040834845735028,
     'P1': 0.1730811614360235,
     'P2': 0.6076373841775751,
     'Q': 192514.71424206023,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130,
     'T1o': 110.09566643485729,
     'T2i': 15,
     'T2o': 84.87829918042112,
     'UA': 3041.75}
    
    Solve the same heat exchanger as if T1i, T2i, and T2o were known but UA was
    not:
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, subtype='E', 
    ... Ntp=4, T1i=130, T2i=15, T2o=84.87829918042112))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.31449028122515194,
     'NTU2': 1.1040834845770124,
     'P1': 0.17308116143602348,
     'P2': 0.607637384177575,
     'Q': 192514.7142420602,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130,
     'T1o': 110.09566643485729,
     'T2i': 15,
     'T2o': 84.87829918042112,
     'UA': 3041.7500000096693}

    Solve a 2 pass/2 pass plate heat exchanger with overall parallel flow and
    its individual passes operating in parallel and known outlet temperatures.
    Note the overall parallel part is trigered with `optimal=False`, and the
    individual pass parallel is triggered by appending 'p' to the subtype. The 
    subpass counterflow can be specified by appending 'c' instead to the 
    subtype, but this is never necessary as it is the default.
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, 
    ... T1o=126.7, T2o=26.7, subtype='2/2p', optimal=False))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.031017369727047148,
     'NTU2': 0.1088929219600726,
     'P1': 0.028945295974795074,
     'P2': 0.10161847646759273,
     'Q': 32200.050307849266,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130.02920288542694,
     'T1o': 126.7,
     'T2i': 15.012141449056527,
     'T2o': 26.7,
     'UA': 300}

    References
    ----------
    .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat 
       Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002.
    .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. 
       CRC Press, 2013.
    .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat
       Transfer, 3E. New York: McGraw-Hill, 1998.

def P_NTU_method(m1, m2, Cp1, Cp2, UA=None, T1i=None, T1o=None, 
                 T2i=None, T2o=None, subtype='crossflow', Ntp=1, optimal=True):
    r'''Wrapper for the various P-NTU method function calls,
    which can solve a heat exchanger. The heat capacities and mass flows
    of each stream and the type of the heat exchanger are always required.
    As additional inputs, one combination of the following inputs is required:
    
    * Three of the four inlet and outlet stream temperatures.
    * Temperatures for the side 1 outlet and side 2 inlet and UA
    * Temperatures for the side 1 outlet side 2 outlet and UA
    * Temperatures for the side 1 inlet and side 2 inlet and UA
    * Temperatures for the side 1 inlet and side 2 outlet and UA

    Computes the total heat exchanged as well as both temperatures of both
    streams.
      
    Parameters
    ----------
    m1 : float
        Mass flow rate of stream 1 (shell side = 1, tube side = 2), [kg/s]
    m2 : float
        Mass flow rate of stream 2 (shell side = 1, tube side = 2), [kg/s]
    Cp1 : float
        Averaged heat capacity of stream 1 (shell side), [J/kg/K]
    Cp2 : float
        Averaged heat capacity of stream 2 (tube side), [J/kg/K]
    UA : float, optional
        Combined Area-heat transfer coefficient term, [W/K]
    T1i : float, optional
        Inlet temperature of stream 1 (shell side), [K]
    T1o : float, optional
        Outlet temperature of stream 1 (shell side), [K]
    T2i : float, optional
        Inlet temperature of stream 2 (tube side), [K]
    T2o : float, optional
        Outlet temperature of stream 2 (tube-side), [K]
    subtype : str, optional
        The subtype of exchanger; one of 'E', 'G', 'H', 'J', 'counterflow',
        'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2',
        or 'crossflow, mixed 1&2'. For plate exchangers 'Np1/Np2' where `Np1`
        is the number of side 1 passes and `Np2` is the number of side 2 passes
    Ntp : int, optional
        For real heat exchangers (types 'E', 'G', 'H', and 'J'), the number of 
        tube passes needss to be specified as well. Not all types support
        any number of tube passes.
    optimal : bool, optional
        For real heat exchangers (types 'E', 'G', 'H', and 'J'), there is often
        a more countercurrent (optimal) way to arrange the tube passes and a
        more parallel (optimal=False) way to arrange them. This controls that.

    Returns
    -------
    results : dict
        * Q : Heat exchanged in the heat exchanger, [W]
        * UA : Combined area-heat transfer coefficient term, [W/K]
        * T1i : Inlet temperature of stream 1, [K]
        * T1o : Outlet temperature of stream 1, [K]
        * T2i : Inlet temperature of stream 2, [K]
        * T2o : Outlet temperature of stream 2, [K]
        * P1 : Thermal effectiveness with respect to stream 1, [-]
        * P2 : Thermal effectiveness with respect to stream 2, [-]
        * R1 : Heat capacity ratio with respect to stream 1, [-]
        * R2 : Heat capacity ratio with respect to stream 2, [-]
        * C1 : The heat capacity rate of fluid 1, [W/K]
        * C2 : The heat capacity rate of fluid 2, [W/K]
        * NTU1 : Thermal Number of Transfer Units with respect to stream 1 [-]
        * NTU2 : Thermal Number of Transfer Units with respect to stream 2 [-]
    
    Notes
    -----
    The main equations used in this method are as follows. For the individual
    expressions used to calculate `P1`, see the `See Also` section.
    
    .. math::
        Q = P_1 C_1 \Delta T_{max} = P_2 C_2 \Delta T_{max}
        
        \Delta T_{max} = T_{h,i} - T_{c,i} = |T_{2,i} - T_{1,i}|
        
        R_1 = \frac{C_1}{C_2} = \frac{T_{2,i} - T_{2,o}}{T_{1,o} - T_{1, i}}
        
        R_2 = \frac{C_2}{C_1} = \frac{T_{1,o} - T_{1, i}}{T_{2,i} - T_{2,o}}

        R_1 = \frac{1}{R_2}
        
        NTU_1 = \frac{UA}{C_1}
        
        NTU_2 = \frac{UA}{C_2}
        
        NTU_1 = NTU_2 R_2
        
        NTU_2 = NTU_1 R_1
        
        P_1 = \frac{T_{1,o} - T_{1,i}}{T_{2,i} - T_{1,i}}
        
        P_2 = \frac{T_{2,i} - T_{2,o}}{T_{2,i} - T_{1,i}}
        
        P_1 = P_2 R_2
        
        P_2 = P_1 R_1
        
        C_1 = m_1 Cp_1
        
        C_2 = m_2 Cp_2
        
    Once `P1` has been calculated, there are six different cases for calculating
    the other stream temperatures depending on the two temperatures provided. 
    They were derived with SymPy.
        
    Two known inlet temperatures:
        
    .. math::
        T_{1,o} = - P_{1} T_{1,i} + P_{1} T_{2,i} + T_{1,i}
        
        T_{2,o} = P_{1} R_{1} T_{1,i} - P_{1} R_{1} T_{2,i} + T_{2,i}
        
    Two known outlet temperatures:
        
    .. math::
        T_{1,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o} 
        - T_{1,o}}{P_{1} R_{1} + P_{1} - 1}
        
        T_{2,i} = \frac{P_{1} R_{1} T_{1,o} + P_{1} T_{2,o}
        - T_{2,o}}{P_{1} R_{1} + P_{1} - 1}
        
    Inlet 1 known, outlet 2 known:
        
    .. math::
        T_{1,o} = \frac{1}{P_{1} R_{1} - 1} \left(P_{1} R_{1} T_{1,i}
        + P_{1} T_{1,i} - P_{1} T_{2,o} - T_{1,i}\right)
        
        T_{2,i} = \frac{P_{1} R_{1} T_{1,i} - T_{2,o}}{P_{1} R_{1} - 1}
        
    Outlet 1 known, inlet 2 known:
        
    .. math::
        T_{1,i} = \frac{P_{1} T_{2,i} - T_{1,o}}{P_{1} - 1}
        
        T_{2,o}  = \frac{1}{P_{1} - 1} \left(R_{1} \left(P_{1} T_{2,i}
        - T_{1,o}\right) - \left(P_{1} - 1\right) \left(R_{1} T_{1,o}
        - T_{2,i}\right)\right)
    
    Input and output of 2 known:
        
    .. math::
        T_{1,i} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} 
        - T_{2,i} + T_{2,o}\right)
        
        T_{1,o} = \frac{1}{P_{1} R_{1}} \left(P_{1} R_{1} T_{2,i} 
        + \left(P_{1} - 1\right) \left(T_{2,i} - T_{2,o}\right)\right)
        
    Input and output of 1 known:
        
    .. math::
        T_{2,i} = \frac{1}{P_{1}} \left(P_{1} T_{1,i} - T_{1,i} 
        + T_{1,o}\right)
        
        T_{2,o} = \frac{1}{P_{1}} \left(P_{1} R_{1} \left(T_{1,i} 
        - T_{1,o}\right) + P_{1} T_{1,i} - T_{1,i} + T_{1,o}\right)
        
    See also
    --------
    temperature_effectiveness_basic
    temperature_effectiveness_plate
    temperature_effectiveness_TEMA_E
    temperature_effectiveness_TEMA_G
    temperature_effectiveness_TEMA_H
    temperature_effectiveness_TEMA_J
    NTU_from_P_basic
    NTU_from_P_plate
    NTU_from_P_E
    NTU_from_P_G
    NTU_from_P_H
    NTU_from_P_J

    Examples
    --------
    Solve a heat exchanger with the UA specified, and known inlet temperatures:
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, 
    ... subtype='E', Ntp=4, T2i=15, T1i=130, UA=3041.75))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.3144902812241522,
     'NTU2': 1.1040834845735028,
     'P1': 0.1730811614360235,
     'P2': 0.6076373841775751,
     'Q': 192514.71424206023,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130,
     'T1o': 110.09566643485729,
     'T2i': 15,
     'T2o': 84.87829918042112,
     'UA': 3041.75}
    
    Solve the same heat exchanger as if T1i, T2i, and T2o were known but UA was
    not:
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900, subtype='E', 
    ... Ntp=4, T1i=130, T2i=15, T2o=84.87829918042112))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.31449028122515194,
     'NTU2': 1.1040834845770124,
     'P1': 0.17308116143602348,
     'P2': 0.607637384177575,
     'Q': 192514.7142420602,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130,
     'T1o': 110.09566643485729,
     'T2i': 15,
     'T2o': 84.87829918042112,
     'UA': 3041.7500000096693}

    Solve a 2 pass/2 pass plate heat exchanger with overall parallel flow and
    its individual passes operating in parallel and known outlet temperatures.
    Note the overall parallel part is trigered with `optimal=False`, and the
    individual pass parallel is triggered by appending 'p' to the subtype. The 
    subpass counterflow can be specified by appending 'c' instead to the 
    subtype, but this is never necessary as it is the default.
        
    >>> pprint(P_NTU_method(m1=5.2, m2=1.45, Cp1=1860., Cp2=1900., UA=300, 
    ... T1o=126.7, T2o=26.7, subtype='2/2p', optimal=False))
    {'C1': 9672.0,
     'C2': 2755.0,
     'NTU1': 0.031017369727047148,
     'NTU2': 0.1088929219600726,
     'P1': 0.028945295974795074,
     'P2': 0.10161847646759273,
     'Q': 32200.050307849266,
     'R1': 3.5107078039927404,
     'R2': 0.2848428453267163,
     'T1i': 130.02920288542694,
     'T1o': 126.7,
     'T2i': 15.012141449056527,
     'T2o': 26.7,
     'UA': 300}

    References
    ----------
    .. [1] Shah, Ramesh K., and Dusan P. Sekulic. Fundamentals of Heat 
       Exchanger Design. 1st edition. Hoboken, NJ: Wiley, 2002.
    .. [2] Thulukkanam, Kuppan. Heat Exchanger Design Handbook, Second Edition. 
       CRC Press, 2013.
    .. [3] Rohsenow, Warren and James Hartnett and Young Cho. Handbook of Heat
       Transfer, 3E. New York: McGraw-Hill, 1998.
    '''
    # Shellside: 1
    # Tubeside: 2
    C1 = m1*Cp1
    C2 = m2*Cp2
    R1 = C1/C2
    R2 = C2/C1
    
    if UA is not None:
        NTU1 = UA/C1
        NTU2 = UA/C2
        
        if subtype in ['counterflow', 'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', 'crossflow, mixed 1&2']:
            P1 = temperature_effectiveness_basic(R1, NTU1, subtype=subtype)
        elif subtype == 'E':
            P1 = temperature_effectiveness_TEMA_E(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'G':
            P1 = temperature_effectiveness_TEMA_G(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'H':
            P1 = temperature_effectiveness_TEMA_H(R1=R1, NTU1=NTU1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'J':
            P1 = temperature_effectiveness_TEMA_J(R1=R1, NTU1=NTU1, Ntp=Ntp)
        elif '/' in subtype:
            passes_counterflow = True
            Np1, end = subtype.split('/')
            if end[-1] in ['c','p']:
                passes_counterflow = True if end[-1] == 'c' else False
                end = end[0:-1]
            Np1, Np2 = int(Np1), int(end)
            P1 = temperature_effectiveness_plate(R1=R1, NTU1=NTU1, Np1=Np1, Np2=Np2, counterflow=optimal, passes_counterflow=passes_counterflow)
        else:
            raise Exception("Supported types are 'E', 'G', 'H', 'J', 'counterflow',\
    'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', \
    'crossflow, mixed 1&2', or 'Np1/Np2' for plate exchangers")
        
        possible_inputs = [(T1i, T2i), (T1o, T2o), (T1i, T2o), (T1o, T2i), (T1i, T1o), (T2i, T2o)]
        if not any([i for i in possible_inputs if None not in i]):
            raise Exception('One set of (T1i, T2i), (T1o, T2o), (T1i, T2o), (T1o, T2i), (T1i, T1o), or (T2i, T2o) is required along with UA.')
        
        # Deal with different temperature inputs, generated with SymPy
        if T1i and T2i:
            T2o = P1*R1*T1i - P1*R1*T2i + T2i
            T1o = -P1*T1i + P1*T2i + T1i
        elif T1o and T2o:
            T2i = (P1*R1*T1o + P1*T2o - T2o)/(P1*R1 + P1 - 1.)
            T1i = (P1*R1*T1o + P1*T2o - T1o)/(P1*R1 + P1 - 1.)
        elif T1o and T2i:
            T2o = (R1*(P1*T2i - T1o) - (P1 - 1.)*(R1*T1o - T2i))/(P1 - 1.)
            T1i = (P1*T2i - T1o)/(P1 - 1.)
        elif T1i and T2o:
            T1o = (P1*R1*T1i + P1*T1i - P1*T2o - T1i)/(P1*R1 - 1.)
            T2i = (P1*R1*T1i - T2o)/(P1*R1 - 1.)
        elif T2i and T2o:
            T1o = (P1*R1*T2i + (P1 - 1.)*(T2i - T2o))/(P1*R1)
            T1i = (P1*R1*T2i - T2i + T2o)/(P1*R1)
        elif T1i and T1o:
            T2o = (P1*R1*(T1i - T1o) + P1*T1i - T1i + T1o)/P1
            T2i = (P1*T1i - T1i + T1o)/P1 
    else:
        # Case where we're solving for UA
        # Three temperatures are required
        # Ensures all four temperatures are set and Q is calculated
        if T1i is not None and T1o is not None:
            Q = m1*Cp1*(T1i-T1o)
            if T2i is not None and T2o is None:
                T2o = T2i + Q/(m2*Cp2)
            elif T2o is not None and T2i is None:
                T2i = T2o - Q/(m2*Cp2)
            elif T2o is not None and T2i is not None:
                Q2 = m2*Cp2*(T2o-T2i)
                if abs((Q-Q2)/Q) > 0.01:
                    raise Exception('The specified heat capacities, mass flows,'
                                    ' and temperatures are inconsistent')
            else:
                raise Exception('At least one temperature is required to be '
                                'specified on side 2.')
                
        elif T2i is not None and T2o is not None:
            Q = m2*Cp2*(T2o-T2i)
            if T1i is not None and T1o is None:
                T1o = T1i - Q/(m1*Cp1)
            elif T1o is not None and T1i is None:
                T1i = T1o + Q/(m1*Cp1)
            else:
                raise Exception('At least one temperature is required to be '
                                'specified on side 2.')
        else:
            raise Exception('Three temperatures are required to be specified '
                            'when solving for UA')
                
        P1 = Q/(C1*abs(T2i-T1i))
        if subtype in ['counterflow', 'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', 'crossflow, mixed 1&2']:
            NTU1 = NTU_from_P_basic(P1=P1, R1=R1, subtype=subtype)
        elif subtype == 'E':
            NTU1 = NTU_from_P_E(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'G':
            NTU1 = NTU_from_P_G(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'H':
            NTU1 = NTU_from_P_H(P1=P1, R1=R1, Ntp=Ntp, optimal=optimal)
        elif subtype == 'J':
            NTU1 = NTU_from_P_J(P1=P1, R1=R1, Ntp=Ntp)
        elif '/' in subtype:
            passes_counterflow = True
            Np1, end = subtype.split('/')
            if end[-1] in ['c','p']:
                passes_counterflow = True if end[-1] == 'c' else False
                end = end[0:-1]
            Np1, Np2 = int(Np1), int(end)
            NTU1 = NTU_from_P_plate(P1=P1, R1=R1, Np1=Np1, Np2=Np2, counterflow=optimal, passes_counterflow=passes_counterflow)
        else:
            raise Exception("Supported types are 'E', 'G', 'H', 'J', 'counterflow',\
    'parallel', 'crossflow', 'crossflow, mixed 1', 'crossflow, mixed 2', \
    'crossflow, mixed 1&2', or 'Np1/Np2' for plate exchangers")
        UA = NTU1*C1
        NTU2 = UA/C2
        
    Q = abs(T1i-T2i)*P1*C1
    # extra:
    P2 = P1*R1
#    effectiveness = max(C1, C2)/min(C1, C2)
    results = {'Q': Q, 'T1i': T1i, 'T1o': T1o, 'T2i': T2i, 'T2o': T2o, 
          'C1': C1, 'C2': C2, 'R1': R1, 'R2': R2, 'P1': P1, 'P2': P2, 'NTU1': NTU1, 'NTU2': NTU2, 'UA': UA}
    return results