docs: def NTU_from_P_basic(P1, R1, subtype='crossflow'):
      r'''Returns the number of transfer units of a basic heat exchanger type
      with a specified (for side 1) thermal effectiveness `P1`, and heat capacity 
      ratio `R1`. The supported cases are as follows:
          
      * Counterflow (ex. double-pipe) [analytical]
      * Parallel (ex. double pipe inefficient configuration) [analytical]
      * Crossflow, single pass, fluids unmixed [numerical]
      * Crossflow, single pass, fluid 1 mixed, fluid 2 unmixed [analytical]
      * Crossflow, single pass, fluid 2 mixed, fluid 1 unmixed [analytical]
      * Crossflow, single pass, both fluids mixed [numerical]
      
      The analytical solutions, for those cases they are available, are as 
      follows:
          
      Counterflow:
          
      .. math::
          NTU_1 = - \frac{1}{R_{1} - 1} \log{\left (\frac{P_{1} R_{1} - 1}{P_{1} 
          - 1} \right )}
      
      Parallel:
      
      .. math::
          NTU_1 = \frac{1}{R_{1} + 1} \log{\left (- \frac{1}{P_{1} \left(R_{1} 
          + 1\right) - 1} \right )}
      
      Crossflow, single pass, fluid 1 mixed, fluid 2 unmixed:
          
      .. math::
          NTU_1 = - \frac{1}{R_{1}} \log{\left (R_{1} \log{\left (- \left(P_{1}
          - 1\right) e^{\frac{1}{R_{1}}} \right )} \right )}
      
      Crossflow, single pass, fluid 2 mixed, fluid 1 unmixed
      
      .. math::
          NTU_1 = - \log{\left (\frac{1}{R_{1}} \log{\left (- \left(P_{1} R_{1}
          - 1\right) e^{R_{1}} \right )} \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 [-]
      subtype : float
          The type of heat exchanger; one of 'counterflow', 'parallel', 
          'crossflow', 'crossflow approximate', 'crossflow, mixed 1', 
          'crossflow, mixed 2', 'crossflow, mixed 1&2'.
          
      Returns
      -------
      NTU1 : float
          Thermal number of transfer units of the heat exchanger in the P-NTU 
          method, calculated with respect to stream 1 [-]
  
      Notes
      -----
      Although this function allows the thermal effectiveness desired to be
      specified, it does not mean such a high value can be obtained. An exception
      is raised when this occurs, although not always a helpful one.
      
      >>> NTU_from_P_basic(P1=.99, R1=.1, subtype='parallel')
      Traceback (most recent call last):
      ValueError: math domain error
              
      For the 'crossflow approximate' solution the function is monotonic, and a
      bounded solver is used within the range of NTU1 from 1E-11 to 1E5. 
      
      For the full correct 'crossflow' solution, the initial guess for newton's
      method is obtained by the 'crossflow approximate' solution; the function
      may not converge because of inaccuracy performing the numerical integral 
      involved.
  
      For the 'crossflow, mixed 1&2' solution, a bounded solver is first use, but
      the upper bound on P1 and the upper NTU1 limit is calculated from a pade
      approximation performed with mpmath. 
  
      Examples
      --------
      >>> NTU_from_P_basic(P1=.975, R1=.1, subtype='counterflow')
      3.984769850376482
      '''
      NTU_min = 1E-11
      function = temperature_effectiveness_basic
      if subtype == 'counterflow':
          return -log((P1*R1 - 1.)/(P1 - 1.))/(R1 - 1.)
      elif subtype == 'parallel':
          return log(-1./(P1*(R1 + 1.) - 1.))/(R1 + 1.)
      elif subtype == 'crossflow, mixed 1':
          return -log(R1*log(-(P1 - 1.)*exp(1./R1)))/R1
      elif subtype == 'crossflow, mixed 2':
          return -log(log(-(P1*R1 - 1.)*exp(R1))/R1)
      elif subtype == 'crossflow, mixed 1&2':
          NTU_max = _NTU_max_for_P_solver(NTU_from_P_basic_crossflow_mixed_12, R1)        
      elif subtype == 'crossflow approximate':
          # These are tricky but also easy because P1 can always be 1
          NTU_max = 1E5
      elif subtype == 'crossflow':
          guess = NTU_from_P_basic(P1, R1, subtype='crossflow approximate')
          to_solve = lambda NTU1 : _NTU_from_P_objective(NTU1, R1, P1, function, subtype='crossflow')
          return newton(to_solve, guess)
      else:
          raise Exception('Subtype not recognized.')
      return _NTU_from_P_solver(P1, R1, NTU_min, NTU_max, function, subtype=subtype)