docs: def isothermal_gas(rho, fd, P1=None, P2=None, L=None, D=None, m=None):
      r'''Calculation function for dealing with flow of a compressible gas in a
      pipeline for the complete isothermal flow equation. Can calculate any of
      the following, given all other inputs:
  
      * Mass flow rate
      * Upstream pressure (numerical)
      * Downstream pressure (analytical or numerical if an overflow occurs)
      * Diameter of pipe (numerical)
      * Length of pipe
  
      A variety of forms of this equation have been presented, differing in their
      use of the ideal gas law and choice of gas constant. The form here uses
      density explicitly, allowing for non-ideal values to be used.
  
      .. math::
          \dot m^2 = \frac{\left(\frac{\pi D^2}{4}\right)^2 \rho_{avg}
          \left(P_1^2-P_2^2\right)}{P_1\left(f_d\frac{L}{D} + 2\ln\frac{P_1}{P_2}
          \right)}
  
      Parameters
      ----------
      rho : float
          Average density of gas in pipe, [kg/m^3]
      fd : float
          Darcy friction factor for flow in pipe [-]
      P1 : float, optional
          Inlet pressure to pipe, [Pa]
      P2 : float, optional
          Outlet pressure from pipe, [Pa]
      L : float, optional
          Length of pipe, [m]
      D : float, optional
          Diameter of pipe, [m]
      m : float, optional
          Mass flow rate of gas through pipe, [kg/s]
  
      Returns
      -------
      m, P1, P2, D, or L : float
          The missing input which was solved for [base SI]
  
      Notes
      -----
      The solution for P2 has the following closed form, derived using Maple:
  
      .. math::
          P_2={P_1 \left( {{ e}^{0.5\cdot{\frac {1}{{m}^{2}} \left( -C{m}^{2}
          +\text{ lambertW} \left(-{\frac {BP_1}{{m}^{2}}{{ e}^{-{\frac {-C{m}^{
          2}+BP_1}{{m}^{2}}}}}}\right){}{m}^{2}+BP_1 \right) }}} \right) ^{-1}}
  
      .. math::
         B = \frac{\pi^2 D^4}{4^2} \rho_{avg}
  
      .. math::
         C = f_d \frac{L}{D}
  
      A wide range of conditions are impossible due to choked flow. See
      `P_isothermal_critical_flow` for details. An exception is raised when
      they occur.
  
      The 2 multiplied by the logarithm is often shown  as a power of the
      pressure ratio; this is only the case when the pressure ratio is raised to
      the power of 2 before its logarithm is taken.
      
      A number of limitations exist for this model:
          
          * Density dependence is that of an ideal gas.
          * If calculating the pressure drop, the average gas density cannot
            be known immediately; iteration must be used to correct this.
          * The friction factor depends on both the gas density and velocity,
            so it should be solved for iteratively as well. It changes throughout
            the pipe as the gas expands and velocity increases.
          * The model is not easily adapted to include elevation effects due to 
            the acceleration term included in it.
          * As the gas expands, it will change temperature slightly, further
            altering the density and friction factor.
           
      There are many commercial packages which perform the actual direct 
      integration of the flow, such as OLGA Dynamic Multiphase Flow Simulator,
      or ASPEN Hydraulics.
      
      This expression has also been presented with the ideal gas assumption
      directly incorporated into it [4]_ (note R is the specific gas constant, in 
      units of J/kg/K):
          
      .. math::
          \dot m^2 = \frac{\left(\frac{\pi D^2}{4}\right)^2
          \left(P_1^2-P_2^2\right)}{RT\left(f_d\frac{L}{D} + 2\ln\frac{P_1}{P_2}
          \right)}
  
      Examples
      --------
      >>> isothermal_gas(rho=11.3, fd=0.00185, P1=1E6, P2=9E5, L=1000, D=0.5)
      145.4847572636031
  
      References
      ----------
      .. [1] Crane Co. Flow of Fluids Through Valves, Fittings, and Pipe. Crane,
         2009.
      .. [2] Kim, J. and Singh, N. "A Novel Equation for Isothermal Pipe Flow.".
         Chemical Engineering, June 2012, http://www.chemengonline.com/a-novel-equation-for-isothermal-pipe-flow/?printmode=1
      .. [3] Wilkes, James O. Fluid Mechanics for Chemical Engineers with
         Microfluidics and CFD. 2 edition. Upper Saddle River, NJ: Prentice Hall,
         2005.
      .. [4] Rennels, Donald C., and Hobart M. Hudson. Pipe Flow: A Practical
         and Comprehensive Guide. 1st edition. Hoboken, N.J: Wiley, 2012.
      '''
      if m is None and (None not in [P1, P2, L, D]):
          Pcf = P_isothermal_critical_flow(P=P1, fd=fd, D=D, L=L)
          if P2 < Pcf:
              raise Exception('Given outlet pressure is not physically possible \
  due to the formation of choked flow at P2=%f, specified outlet pressure was %f' % (Pcf, P2))
          if P2 > P1:
              raise Exception('Specified outlet pressure is larger than the \
  inlet pressure; fluid will flow backwards.')
          return (0.0625*pi*pi*D**4*rho/(P1*(fd*L/D + 2.0*log(P1/P2)))*(P1*P1 - P2*P2))**0.5
      elif L is None and (None not in [P1, P2, D, m]):
          return D*(pi*pi*D**4*rho*(P1*P1 - P2*P2) - 32.0*P1*m*m*log(P1/P2))/(16.0*P1*fd*m*m)
      elif P1 is None and (None not in [L, P2, D, m]):
          Pcf = P_upstream_isothermal_critical_flow(P=P2, fd=fd, D=D, L=L)
  
          def to_solve(P1):
              return m - isothermal_gas(rho, fd, P1=P1, P2=P2, L=L, D=D)
          try:
              # Use the explicit solution for P2 with different P1 guesses;
              # newton doesn't like solving for m.
              def to_solve_P2_basis(P1):
                  return abs(P2 - isothermal_gas(rho, fd, m=m, P1=P1, P2=None, L=L, D=D))
              P1 = newton(to_solve_P2_basis, (P2+Pcf)/2.)
              assert P2 <= P1
              return P1
          except:
              try:
                  return ridder(to_solve, a=P2, b=Pcf)
              except:
                  m_max = isothermal_gas(rho, fd, P1=Pcf, P2=P2, L=L, D=D)
                  raise Exception('The desired mass flow rate of %f kg/s cannot '
  'be achieved with the specified downstream pressure; the maximum flowrate is '
  '%f kg/s at an upstream pressure of %f Pa' %(m, m_max, Pcf))
      elif P2 is None and (None not in [L, P1, D, m]):
          try:
              Pcf = P_isothermal_critical_flow(P=P1, fd=fd, D=D, L=L)
              m_max = isothermal_gas(rho, fd, P1=P1, P2=Pcf, L=L, D=D)
              assert m <= m_max
  
              C = fd*L/D
              B = (pi/4*D**2)**2*rho
              arg = -B/m**2*P1*exp(-(-C*m**2+B*P1)/m**2)
              # Consider the two real branches of the lambertw function.
              # The k=-1 branch produces the higher P2 values; the k=0 branch is
              # physically impossible.
              lambert_ans = float(lambertw(arg, k=-1).real)
              # Large overflow problem here; also divide by zero problems!
              # Fail and try a numerical solution if it doesn't work.
              assert not math.isinf(lambert_ans)
              P2 = P1/exp((-C*m**2+lambert_ans*m**2+B*P1)/m**2/2.)
              assert P2 < P1
              return P2
          except:
              Pcf = P_isothermal_critical_flow(P=P1, fd=fd, D=D, L=L)
              def to_solve(P2):
                  return m - isothermal_gas(rho, fd, P1=P1, P2=P2, L=L, D=D)
  #                return abs(m - isothermal_gas(rho, fd, P1=P1, P2=P2, L=L, D=D))
  #            return fminbound(to_solve, x1=Pcf, x2=P1)
              try:
                  return ridder(to_solve, a=Pcf, b=P1)
  #                m_max = isothermal_gas(rho, fd, P1=P1, P2=Pcf, L=L, D=D)
  #                assert m < m_max
  #                assert P > Pcv
  #                return m
              except:
                  m_max = isothermal_gas(rho, fd, P1=P1, P2=Pcf, L=L, D=D)
                  raise Exception('The desired mass flow rate cannot be achieved '
  'with the specified upstream pressure of %f Pa; the maximum flowrate is %f '
  'kg/s at a downstream pressure of %f' %(P1, m_max, Pcf))
              # A solver which respects its boundaries is required here.
              # ridder cuts the time down from 2 ms to 200 mircoseconds.
              # Is is believed Pcf and P1 will always bracked the root, however
              # leave the commented code for testing
      elif D is None and (None not in [P2, P1, L, m]):
          def to_solve(D):
              return m - isothermal_gas(rho, fd, P1=P1, P2=P2, L=L, D=D)
          return newton(to_solve, 0.1)
      else:
          raise Exception('This function solves for either mass flow, upstream \
  pressure, downstream pressure, diameter, or length; all other inputs \
  must be provided.')