docs: def pitch_angle_solver(angle=None, pitch=None, pitch_parallel=None,
                         pitch_normal=None):
      r'''Utility to take any two of `angle`, `pitch`, `pitch_parallel`, and
      `pitch_normal` and calculate the other two. This is useful for applications
      with tube banks, as in shell and tube heat exchangers or air coolers and
      allows for a wider range of user input. 
  
      .. math::
          \text{pitch normal} = \text{pitch} \cdot \sin(\text{angle})
          
      .. math::
          \text{pitch parallel} = \text{pitch} \cdot \cos(\text{angle})
          
      Parameters
      ----------
      angle : float, optional
          The angle of the tube layout, [degrees] 
      pitch : float, optional
          The shortest distance between tube centers; defined in relation to the 
          flow direction only, [m]
      pitch_parallel : float, optional
          The distance between tube center along a line parallel to the flow;
          has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m]
      pitch_normal : float, optional
          The distance between tube centers in a line 90° to the line of flow;
          has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m]
  
      Returns
      -------
      angle : float
          The angle of the tube layout, [degrees] 
      pitch : float
          The shortest distance between tube centers; defined in relation to the 
          flow direction only, [m]
      pitch_parallel : float
          The distance between tube center along a line parallel to the flow;
          has been called `longitudinal` pitch, `pp`, `s2`, `SL`, and `p2`, [m]
      pitch_normal : float
          The distance between tube centers in a line 90° to the line of flow;
          has been called the `transverse` pitch, `pn`, `s1`, `ST`, and `p1`, [m]
          
      Notes
      -----
      For the 90 and 0 degree case, the normal or parallel pitches can be zero;
      given the angle and the zero value, obviously is it not possible to
      calculate the pitch and a math error will be raised.
      
      No exception will be raised if three or four inputs are provided; the other
      two will simply be calculated according to the list of if statements used.
      
      An exception will be raised if only one input is provided.
          
      Examples
      --------
      >>> pitch_angle_solver(pitch=1, angle=30)
      (30, 1, 0.8660254037844387, 0.49999999999999994)
  
      References
      ----------
      .. [1] Schlunder, Ernst U, and International Center for Heat and Mass
         Transfer. Heat Exchanger Design Handbook. Washington:
         Hemisphere Pub. Corp., 1983.
      '''
      if angle is not None and pitch is not None:
          pitch_normal = pitch*sin(radians(angle))
          pitch_parallel = pitch*cos(radians(angle))
      elif angle is not None and pitch_normal is not None:
          pitch = pitch_normal/sin(radians(angle))
          pitch_parallel = pitch*cos(radians(angle))
      elif angle is not None and pitch_parallel is not None:
          pitch = pitch_parallel/cos(radians(angle))
          pitch_normal = pitch*sin(radians(angle))
      elif pitch_normal is not None and pitch is not None:
          angle = degrees(asin(pitch_normal/pitch))
          pitch_parallel = pitch*cos(radians(angle))
      elif pitch_parallel is not None and pitch is not None:
          angle = degrees(acos(pitch_parallel/pitch))
          pitch_normal = pitch*sin(radians(angle))
      elif pitch_parallel is not None and pitch_normal is not None:
          angle = degrees(asin(pitch_normal/(pitch_normal**2 + pitch_parallel**2)**0.5))
          pitch = (pitch_normal**2 + pitch_parallel**2)**0.5
      else:
          raise Exception('Two of the arguments are required')
      return angle, pitch, pitch_parallel, pitch_normal