def to(self, zs, T=None, P=None, V=None): new = self.__class__.__new__(self.__class__) new.zs = zs new.N = self.N new.HeatCapacityGases = self.HeatCapacityGases new.model = model = self.model new.Hfs = self.Hfs new.Gfs = self.Gfs new.Sfs = self.Sfs if T is not None: new.T = T if P is not None: new.P = P Z = Z_from_virial_density_form(T, P, new.B(), new.C()) new._V = Z * self.R * T / P elif V is not None: P = new.P = self.R * T * (V * V + V * new.B() + new.C()) / (V * V * V) new._V = V elif P is not None and V is not None: new.P = P # PV specified, solve for T def err(T): # Solve for P matching; probably there is a better solution here that does not # require the cubic solution but this works for now # TODO: instead of using self.to_TP_zs to allow calculating B and C, # they should be functional new_tmp = self.to_TP_zs(T=T, P=P, zs=zs) B = new_tmp.B() C = new_tmp.C() x2 = V * V + V * B + C x3 = self.R / (V * V * V) P_err = T * x2 * x3 - P dP_dT = x3 * (T * (V * new_tmp.dB_dT() + new_tmp.dC_dT()) + x2) return P_err, dP_dT T_ig = P * V / self.R # guess T = newton(err, T_ig, fprime=True, xtol=1e-15) new.T = T else: raise ValueError("Two of T, P, or V are needed") return new
def solve_prop_poly_fit(self, goal): poly_fit_Tmin, poly_fit_Tmax = self.poly_fit_Tmin, self.poly_fit_Tmax poly_fit_Tmin_slope, poly_fit_Tmax_slope = self.poly_fit_Tmin_slope, self.poly_fit_Tmax_slope poly_fit_Tmin_value, poly_fit_Tmax_value = self.poly_fit_Tmin_value, self.poly_fit_Tmax_value coeffs = self.poly_fit_coeffs T_low = log(goal * exp(poly_fit_Tmin * poly_fit_Tmin_slope - poly_fit_Tmin_value)) / poly_fit_Tmin_slope if T_low <= poly_fit_Tmin: return T_low T_high = log(goal * exp(poly_fit_Tmax * poly_fit_Tmax_slope - poly_fit_Tmax_value)) / poly_fit_Tmax_slope if T_high >= poly_fit_Tmax: return T_high else: lnPGoal = log(goal) def to_solve(T): # dPsat and Psat are both in log basis dPsat = Psat = 0.0 for c in coeffs: dPsat = T * dPsat + Psat Psat = T * Psat + c return Psat - lnPGoal, dPsat # Guess with the two extrapolations from the linear fits # By definition both guesses are in the range of they would have been returned if T_low > poly_fit_Tmax: T_low = poly_fit_Tmax if T_high < poly_fit_Tmin: T_high = poly_fit_Tmin T = newton(to_solve, 0.5 * (T_low + T_high), fprime=True, low=poly_fit_Tmin, high=poly_fit_Tmax) return T
def Thome(m, x, D, rhol, rhog, mul, mug, kl, kg, Cpl, Cpg, Hvap, sigma, Psat, Pc, q=None, Te=None): r'''Calculates heat transfer coefficient for film boiling of saturated fluid in any orientation of flow. Correlation is as developed in [1]_ and [2]_, and also reviewed [3]_. This is a complicated model, but expected to have more accuracy as a result. Either the heat flux or excess temperature is required for the calculation of heat transfer coefficient. The solution for a specified excess temperature is solved numerically, making it slow. .. math:: h(z) = \frac{t_l}{\tau} h_l(z) +\frac{t_{film}}{\tau} h_{film}(z) + \frac{t_{dry}}{\tau} h_{g}(z) .. math:: h_{l/g}(z) = (Nu_{lam}^4 + Nu_{trans}^4)^{1/4} k/D .. math:: Nu_{laminar} = 0.91 {Pr}^{1/3} \sqrt{ReD/L(z)} .. math:: Nu_{trans} = \frac{ (f/8) (Re-1000)Pr}{1+12.7 (f/8)^{1/2} (Pr^{2/3}-1)} \left[ 1 + \left( \frac{D}{L(z)}\right)^{2/3}\right] .. math:: f = (1.82 \log_{10} Re - 1.64 )^{-2} .. math:: L_l = \frac{\tau G_{tp}}{\rho_l}(1-x) .. math:: L_{dry} = v_p t_{dry} .. math:: t_l = \frac{\tau}{1 + \frac{\rho_l}{\rho_g}\frac{x}{1-x}} .. math:: t_v = \frac{\tau}{1 + \frac{\rho_g}{\rho_l}\frac{1-x}{x}} .. math:: \tau = \frac{1}{f_{opt}} .. math:: f_{opt} = \left(\frac{q}{q_{ref}}\right)^{n_f} .. math:: q_{ref} = 3328\left(\frac{P_{sat}}{P_c}\right)^{-0.5} .. math:: t_{dry,film} = \frac{\rho_l \Delta H_{vap}}{q}[\delta_0(z) - \delta_{min}] .. math:: \frac{\delta_0}{D} = C_{\delta 0}\left(3\sqrt{\frac{\nu_l}{v_p D}} \right)^{0.84}\left[(0.07Bo^{0.41})^{-8} + 0.1^{-8}\right]^{-1/8} .. math:: Bo = \frac{\rho_l D}{\sigma} v_p^2 .. math:: v_p = G_{tp} \left[\frac{x}{\rho_g} + \frac{1-x}{\rho_l}\right] .. math:: h_{film}(z) = \frac{2 k_l}{\delta_0(z) + \delta_{min}(z)} .. math:: \delta_{min} = 0.3\cdot 10^{-6} \text{m} .. math:: C_{\delta,0} = 0.29 .. math:: n_f = 1.74 if t dry film > tv: .. math:: \delta_{end}(x) = \delta(z, t_v) .. math:: t_{film} = t_v .. math:: t_{dry} = 0 Otherwise: .. math:: \delta_{end}(z) = \delta_{min} .. math:: t_{film} = t_{dry,film} .. math:: t_{dry} = t_v - t_{film} Parameters ---------- m : float Mass flow rate [kg/s] x : float Quality at the specific tube interval [] D : float Diameter of the tube [m] rhol : float Density of the liquid [kg/m^3] rhog : float Density of the gas [kg/m^3] mul : float Viscosity of liquid [Pa*s] mug : float Viscosity of gas [Pa*s] kl : float Thermal conductivity of liquid [W/m/K] kg : float Thermal conductivity of gas [W/m/K] Cpl : float Heat capacity of liquid [J/kg/K] Cpg : float Heat capacity of gas [J/kg/K] Hvap : float Heat of vaporization of liquid [J/kg] sigma : float Surface tension of liquid [N/m] Psat : float Vapor pressure of fluid, [Pa] Pc : float Critical pressure of fluid, [Pa] q : float, optional Heat flux to wall [W/m^2] Te : float, optional Excess temperature of wall, [K] Returns ------- h : float Heat transfer coefficient [W/m^2/K] Notes ----- [1]_ and [2]_ have been reviewed, and are accurately reproduced in [3]_. [1]_ used data from 7 studies, covering 7 fluids and Dh from 0.7-3.1 mm, heat flux from 0.5-17.8 W/cm^2, x from 0.01-0.99, and G from 50-564 kg/m^2/s. Liquid and/or gas slugs are both considered, and are hydrodynamically developing. `Ll` is the calculated length of liquid slugs, and `L_dry` is the same for vapor slugs. Because of the complexity of the model and that there is some logic in this function, `Te` as an input may lead to a different solution that the calculated `q` will in return. Examples -------- >>> Thome(m=1, x=0.4, D=0.3, rhol=567., rhog=18.09, kl=0.086, kg=0.2, ... mul=156E-6, mug=1E-5, Cpl=2300, Cpg=1400, sigma=0.02, Hvap=9E5, ... Psat=1E5, Pc=22E6, q=1E5) 1633.008836502032 References ---------- .. [1] Thome, J. R., V. Dupont, and A. M. Jacobi. "Heat Transfer Model for Evaporation in Microchannels. Part I: Presentation of the Model." International Journal of Heat and Mass Transfer 47, no. 14-16 (July 2004): 3375-85. doi:10.1016/j.ijheatmasstransfer.2004.01.006. .. [2] Dupont, V., J. R. Thome, and A. M. Jacobi. "Heat Transfer Model for Evaporation in Microchannels. Part II: Comparison with the Database." International Journal of Heat and Mass Transfer 47, no. 14-16 (July 2004): 3387-3401. doi:10.1016/j.ijheatmasstransfer.2004.01.007. .. [3] Bertsch, Stefan S., Eckhard A. Groll, and Suresh V. Garimella. "Review and Comparative Analysis of Studies on Saturated Flow Boiling in Small Channels." Nanoscale and Microscale Thermophysical Engineering 12, no. 3 (September 4, 2008): 187-227. doi:10.1080/15567260802317357. ''' if q is None and Te: to_solve = lambda q: q / Thome(m=m, x=x, D=D, rhol=rhol, rhog=rhog, kl=kl, kg=kg, mul=mul, mug=mug, Cpl=Cpl, Cpg=Cpg, sigma=sigma, Hvap=Hvap, Psat=Psat, Pc=Pc, q=q) - Te q = newton(to_solve, 1E4) return Thome(m=m, x=x, D=D, rhol=rhol, rhog=rhog, kl=kl, kg=kg, mul=mul, mug=mug, Cpl=Cpl, Cpg=Cpg, sigma=sigma, Hvap=Hvap, Psat=Psat, Pc=Pc, q=q) elif q is None and Te is None: raise Exception('Either q or Te is needed for this correlation') C_delta0 = 0.3E-6 G = m / (pi / 4 * D**2) Rel = G * D * (1 - x) / mul Reg = G * D * x / mug qref = 3328 * (Psat / Pc)**-0.5 fopt = (q / qref)**1.74 tau = 1. / fopt vp = G * (x / rhog + (1 - x) / rhol) Bo = rhol * D / sigma * vp**2 # Not standard definition nul = nu_mu_converter(rho=rhol, mu=mul) delta0 = D * 0.29 * (3 * (nul / vp / D)**0.5)**0.84 * ( (0.07 * Bo**0.41)**-8 + 0.1**-8)**(-1 / 8.) tl = tau / (1 + rhol / rhog * (x / (1. - x))) tv = tau / (1 + +rhog / rhol * ((1. - x) / x)) t_dry_film = rhol * Hvap / q * (delta0 - C_delta0) if t_dry_film > tv: t_film = tv delta_end = delta0 - q / rhol / Hvap * tv # what could time possibly be? t_dry = 0 else: t_film = t_dry_film delta_end = C_delta0 t_dry = tv - t_film Ll = tau * G / rhol * (1 - x) Ldry = t_dry * vp Prg = Prandtl(Cp=Cpg, k=kg, mu=mug) Prl = Prandtl(Cp=Cpl, k=kl, mu=mul) fg = (1.82 * log10(Reg) - 1.64)**-2 fl = (1.82 * log10(Rel) - 1.64)**-2 Nu_lam_Zl = 2 * 0.455 * (Prl)**(1 / 3.) * (D * Rel / Ll)**0.5 Nu_trans_Zl = turbulent_Gnielinski(Re=Rel, Pr=Prl, fd=fl) * (1 + (D / Ll)**(2 / 3.)) if Ldry == 0: Nu_lam_Zg, Nu_trans_Zg = 0, 0 else: Nu_lam_Zg = 2 * 0.455 * (Prg)**(1 / 3.) * (D * Reg / Ldry)**0.5 Nu_trans_Zg = turbulent_Gnielinski(Re=Reg, Pr=Prg, fd=fg) * (1 + (D / Ldry)**(2 / 3.)) h_Zg = kg / D * (Nu_lam_Zg**4 + Nu_trans_Zg**4)**0.25 h_Zl = kl / D * (Nu_lam_Zl**4 + Nu_trans_Zl**4)**0.25 h_film = 2 * kl / (delta0 + C_delta0) return tl / tau * h_Zl + t_film / tau * h_film + t_dry / tau * h_Zg
def v_terminal(D, rhop, rho, mu, Method=None): r'''Calculates terminal velocity of a falling sphere using any drag coefficient method supported by `drag_sphere`. The laminar solution for Re < 0.01 is first tried; if the resulting terminal velocity does not put it in the laminar regime, a numerical solution is used. .. math:: v_t = \sqrt{\frac{4 g d_p (\rho_p-\rho_f)}{3 C_D \rho_f }} Parameters ---------- D : float Diameter of the sphere, [m] rhop : float Particle density, [kg/m^3] rho : float Density of the surrounding fluid, [kg/m^3] mu : float Viscosity of the surrounding fluid [Pa*s] Method : string, optional A string of the function name to use, as in the dictionary drag_sphere_correlations Returns ------- v_t : float Terminal velocity of falling sphere [m/s] Notes ----- As there are no correlations implemented for Re > 1E6, an error will be raised if the numerical solver seeks a solution above that limit. The laminar solution is given in [1]_ and is: .. math:: v_t = \frac{g d_p^2 (\rho_p - \rho_f)}{18 \mu_f} Examples -------- >>> v_terminal(D=70E-6, rhop=2600., rho=1000., mu=1E-3) 0.004142497244531304 Example 7-1 in GPSA handbook, 13th edition: >>> from scipy.constants import * >>> v_terminal(D=150E-6, rhop=31.2*lb/foot**3, rho=2.07*lb/foot**3, mu=1.2e-05)/foot 0.4491992020345101 The answer reported there is 0.46 ft/sec. References ---------- .. [1] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. McGraw-Hill Professional, 2007. .. [2] Rushton, Albert, Anthony S. Ward, and Richard G. Holdich. Solid-Liquid Filtration and Separation Technology. 1st edition. Weinheim ; New York: Wiley-VCH, 1996. ''' '''The following would be the ideal implementation. The actual function is optimized for speed, not readability def err(V): Re = rho*V*D/mu Cd = Barati_high(Re) V2 = (4/3.*g*D*(rhop-rho)/rho/Cd)**0.5 return (V-V2) return fsolve(err, 1.)''' v_lam = g * D * D * (rhop - rho) / (18 * mu) Re_lam = Reynolds(V=v_lam, D=D, rho=rho, mu=mu) if Re_lam < 0.01 or Method == 'Stokes': return v_lam Re_almost = rho * D / mu main = 4 / 3. * g * D * (rhop - rho) / rho V_max = 1E6 / rho / D * mu # where the correlation breaks down, Re=1E6 def err(V): Cd = drag_sphere(Re_almost * V, Method=Method) return V - (main / Cd)**0.5 # Begin the solver with 1/100 th the velocity possible at the maximum # Reynolds number the correlation is good for return float(newton(err, V_max / 100, tol=1E-12))
def flash_wilson(zs, Tcs, Pcs, omegas, T=None, P=None, VF=None): r'''PVT flash model using Wilson's equation - useful for obtaining initial guesses for more rigorous models, or it can be used as its own model. Capable of solving with two of `T`, `P`, and `VF` for the other one; that results in three solve modes, but for `VF=1` and `VF=0`, there are additional solvers; for a total of seven solvers implemented. This model uses `flash_inner_loop` to solve the Rachford-Rice problem. .. math:: K_i = \frac{P_c}{P} \exp\left(5.37(1+\omega)\left[1 - \frac{T_c}{T} \right]\right) Parameters ---------- zs : list[float] Mole fractions of the phase being flashed, [-] Tcs : list[float] Critical temperatures of all species, [K] Pcs : list[float] Critical pressures of all species, [Pa] omegas : list[float] Acentric factors of all species, [-] T : float, optional Temperature, [K] P : float, optional Pressure, [Pa] VF : float, optional Molar vapor fraction, [-] Returns ------- T : float Temperature, [K] P : float Pressure, [Pa] VF : float Molar vapor fraction, [-] xs : list[float] Mole fractions of liquid phase, [-] ys : list[float] Mole fractions of vapor phase, [-] Notes ----- For the cases where `VF` is 1 or 0 and T is known, an explicit solution is used. For the same cases where `P` and `VF` are known, there is no explicit solution available. There is an internal `Tmax` parameter, set to 50000 K; which, in the event of convergence of the Secant method, is used as a bounded for a bounded solver. It is used in the PVF solvers. This typically allows pressures up to 2 GPa to be converged to. However, for narrow-boiling mixtures, the PVF failure may occur at much lower pressures. Examples -------- >>> Tcs = [305.322, 540.13] >>> Pcs = [4872200.0, 2736000.0] >>> omegas = [0.099, 0.349] >>> zs = [0.4, 0.6] >>> flash_wilson(zs=zs, Tcs=Tcs, Pcs=Pcs, omegas=omegas, T=300, P=1e5) (300, 100000.0, 0.42219453293637355, [0.020938815080034565, 0.9790611849199654], [0.9187741856225791, 0.08122581437742094]) ''' T_MAX = 50000.0 N = len(zs) # Assume T and P to begin with if T is not None and P is not None: # numba is failing its type inferences P_inv = 1.0 / P T_inv = 1.0 / T Ks = [0.0] * N for i in range(N): Ks[i] = P_inv * Pcs[i] * exp( (5.37 * (1.0 + omegas[i]) * (1.0 - Tcs[i] * T_inv))) # all_under_1, all_over_1 = True, True # for K in Ks: # if K < 1.0: # all_over_1 = False # else: # all_under_1 = False # if all_over_1: # raise ValueError("Fail") # elif all_under_1: # raise ValueError("Fail") ans = (T, P) + flash_inner_loop(zs=zs, Ks=Ks) return ans elif T is not None and VF is not None and VF == 0.0: ys = [0.0] * N P_bubble = 0.0 T_inv = 1.0 / T for i in range(N): v = zs[i] * Pcs[i] * exp( (5.37 * (1.0 + omegas[i]) * (1.0 - Tcs[i] * T_inv))) P_bubble += v ys[i] = v P_inv = 1.0 / P_bubble for i in range(N): ys[i] *= P_inv return (T, P_bubble, 0.0, zs, ys) elif T is not None and VF is not None and VF == 1.0: xs = [0.0] * N P_dew = 0. T_inv = 1.0 / T for i in range(N): v = zs[i] / (Pcs[i] * exp( (5.37 * (1.0 + omegas[i]) * (1.0 - Tcs[i] * T_inv)))) P_dew += v xs[i] = v P_dew = 1. / P_dew for i in range(N): xs[i] *= P_dew return (T, P_dew, 1.0, xs, zs) elif T is not None and VF is not None: # Solve for the pressure to create the desired vapor fraction P_bubble = 0.0 P_dew = 0. T_inv = 1.0 / T K_Ps = [0.0] * N for i in range(N): K_P = Pcs[i] * exp( (5.37 * (1.0 + omegas[i]) * (1.0 - Tcs[i] * T_inv))) P_bubble += zs[i] * K_P P_dew += zs[i] / K_P K_Ps[i] = K_P P_dew = 1. / P_dew '''Rachford-Rice esque solution in terms of pressure. from sympy import * N = 1 cmps = range(N) zs = z0, z1, z2, z3 = symbols('z0, z1, z2, z3') Ks_P = K0_P, K1_P, K2_P, K3_P = symbols('K0_P, K1_P, K2_P, K3_P') VF, P = symbols('VF, P') tot = 0 for i in cmps: tot += zs[i]*(Ks_P[i]/P - 1)/(1 + VF*(Ks_P[i]/P - 1)) cse([tot, diff(tot, P)], optimizations='basic') ''' P_guess = P_bubble + VF * (P_dew - P_bubble) # Linear interpolation P_calc = newton(err_Wilson_TVF, P_guess, fprime=True, bisection=True, low=P_dew, high=P_bubble, args=(N, VF, zs, K_Ps)) P_inv = 1.0 / P_calc xs = K_Ps ys = [0.0] * N for i in range(N): Ki = K_Ps[i] * P_inv xi = zs[i] / (1.0 + VF * (Ki - 1.0)) ys[i] = Ki * xi xs[i] = xi return (T, P_calc, VF, xs, ys) elif P is not None and VF is not None: P_inv = 1.0 / P Ks = [0.0] * N xs = [0.0] * N x50s = [5.37] * N for i in range(N): x50s[i] *= omegas[i] + 1.0 T_low, T_high = 1e100, 0.0 logP = log(P) for i in range(N): T_K_1 = Tcs[i] * x50s[i] / (x50s[i] - logP + log(Pcs[i])) if T_K_1 < T_low: T_low = T_K_1 if T_K_1 > T_high: T_high = T_K_1 if T_low < 0.0: T_low = 1e-12 if T_high <= 0.0: raise ValueError( "No temperature exists which makes Wilson K factor above 1 - decrease pressure" ) if T_high < 0.1 * T_MAX: T_guess = 0.5 * (T_low + T_high) else: T_guess = 0.0 for i in range(N): T_guess += zs[i] * Tcs[i] T_guess *= 0.666666 if T_guess < T_low: T_guess = T_low + 1.0 # Take a nominal step T_calc = newton( err_Wilson_PVF, T_guess, fprime=True, low=T_low, xtol=1e-13, bisection=True, args=(N, P_inv, VF, Tcs, Pcs, Ks, zs, xs, x50s)) # High bound not actually a bound, only low bound if 1e-10 < T_calc < T_MAX: ys = x50s for i in range(N): ys[i] = xs[i] * Ks[i] return (T_calc, P, VF, xs, ys) else: raise ValueError("Provide two of P, T, and VF")
def v_terminal(D, rhop, rho, mu, Method=None): r'''Calculates terminal velocity of a falling sphere using any drag coefficient method supported by `drag_sphere`. The laminar solution for Re < 0.01 is first tried; if the resulting terminal velocity does not put it in the laminar regime, a numerical solution is used. .. math:: v_t = \sqrt{\frac{4 g d_p (\rho_p-\rho_f)}{3 C_D \rho_f }} Parameters ---------- D : float Diameter of the sphere, [m] rhop : float Particle density, [kg/m^3] rho : float Density of the surrounding fluid, [kg/m^3] mu : float Viscosity of the surrounding fluid [Pa*s] Method : string, optional A string of the function name to use, as in the dictionary drag_sphere_correlations Returns ------- v_t : float Terminal velocity of falling sphere [m/s] Notes ----- As there are no correlations implemented for Re > 1E6, an error will be raised if the numerical solver seeks a solution above that limit. The laminar solution is given in [1]_ and is: .. math:: v_t = \frac{g d_p^2 (\rho_p - \rho_f)}{18 \mu_f} Examples -------- >>> v_terminal(D=70E-6, rhop=2600., rho=1000., mu=1E-3) 0.004142497244531304 Example 7-1 in GPSA handbook, 13th edition: >>> from scipy.constants import * >>> v_terminal(D=150E-6, rhop=31.2*lb/foot**3, rho=2.07*lb/foot**3, mu=1.2e-05)/foot 0.4491992020345101 The answer reported there is 0.46 ft/sec. References ---------- .. [1] Green, Don, and Robert Perry. Perry's Chemical Engineers' Handbook, Eighth Edition. McGraw-Hill Professional, 2007. .. [2] Rushton, Albert, Anthony S. Ward, and Richard G. Holdich. Solid-Liquid Filtration and Separation Technology. 1st edition. Weinheim ; New York: Wiley-VCH, 1996. ''' '''The following would be the ideal implementation. The actual function is optimized for speed, not readability def err(V): Re = rho*V*D/mu Cd = Barati_high(Re) V2 = (4/3.*g*D*(rhop-rho)/rho/Cd)**0.5 return (V-V2) return fsolve(err, 1.)''' v_lam = g*D*D*(rhop-rho)/(18*mu) Re_lam = Reynolds(V=v_lam, D=D, rho=rho, mu=mu) if Re_lam < 0.01 or Method == 'Stokes': return v_lam Re_almost = rho*D/mu main = 4/3.*g*D*(rhop-rho)/rho V_max = 1E6/rho/D*mu # where the correlation breaks down, Re=1E6 def err(V): Cd = drag_sphere(Re_almost*V, Method=Method) return V - (main/Cd)**0.5 # Begin the solver with 1/100 th the velocity possible at the maximum # Reynolds number the correlation is good for return float(newton(err, V_max/100, tol=1E-12))
def liquid_jet_pump_ancillary(rhop, rhos, Kp, Ks, d_nozzle=None, d_mixing=None, Qp=None, Qs=None, P1=None, P2=None): r'''Calculates the remaining variable in a liquid jet pump when solving for one if the inlet variables only and the rest of them are known. The equation comes from conservation of energy and momentum in the mixing chamber. The variable to be solved for must be one of `d_nozzle`, `d_mixing`, `Qp`, `Qs`, `P1`, or `P2`. .. math:: P_1 - P_2 = \frac{1}{2}\rho_pV_n^2(1+K_p) - \frac{1}{2}\rho_s V_3^2(1+K_s) Rearrange to express V3 in terms of Vn, and using the density ratio `C`, the expression becomes: .. math:: P_1 - P_2 = \frac{1}{2}\rho_p V_n^2\left[(1+K_p) - C(1+K_s) \left(\frac{MR}{1-R}\right)^2\right] Using the primary nozzle area and flow rate: .. math:: P_1 - P_2 = \frac{1}{2}\rho_p \left(\frac{Q_p}{A_n}\right)^2 \left[(1+K_p) - C(1+K_s) \left(\frac{MR}{1-R}\right)^2\right] For `P`, `P2`, `Qs`, and `Qp`, the equation can be rearranged explicitly for them. For `d_mixing` and `d_nozzle`, a bounded solver is used searching between 1E-9 m and 20 times the other diameter which was specified. Parameters ---------- rhop : float The density of the primary (motive) fluid, [kg/m^3] rhos : float The density of the secondary fluid (drawn from the vacuum chamber), [kg/m^3] Kp : float The primary nozzle loss coefficient, [-] Ks : float The secondary inlet loss coefficient, [-] d_nozzle : float, optional The inside diameter of the primary fluid's nozle, [m] d_mixing : float, optional The diameter of the mixing chamber, [m] Qp : float, optional The volumetric flow rate of the primary fluid, [m^3/s] Qs : float, optional The volumetric flow rate of the secondary fluid, [m^3/s] P1 : float, optional The pressure of the primary fluid entering its nozzle, [Pa] P2 : float, optional The pressure of the secondary fluid at the entry of the ejector, [Pa] Returns ------- solution : float The parameter not specified (one of `d_nozzle`, `d_mixing`, `Qp`, `Qs`, `P1`, or `P2`), (units of `m`, `m`, `m^3/s`, `m^3/s`, `Pa`, or `Pa` respectively) Notes ----- The following SymPy code was used to obtain the analytical formulas ( they are not shown here due to their length): >>> from sympy import * >>> A_nozzle, A_mixing, Qs, Qp, P1, P2, rhos, rhop, Ks, Kp = symbols('A_nozzle, A_mixing, Qs, Qp, P1, P2, rhos, rhop, Ks, Kp') >>> R = A_nozzle/A_mixing >>> M = Qs/Qp >>> C = rhos/rhop >>> rhs = rhop/2*(Qp/A_nozzle)**2*((1+Kp) - C*(1 + Ks)*((M*R)/(1-R))**2 ) >>> new = Eq(P1 - P2, rhs) >>> #solve(new, Qp) >>> #solve(new, Qs) >>> #solve(new, P1) >>> #solve(new, P2) Examples -------- Calculating primary fluid nozzle inlet pressure P1: >>> liquid_jet_pump_ancillary(rhop=998., rhos=1098., Ks=0.11, Kp=.04, ... P2=133600, Qp=0.01, Qs=0.01, d_mixing=0.045, d_nozzle=0.02238) 426434.60314398084 References ---------- .. [1] Ejectors and Jet Pumps. Design and Performance for Incompressible Liquid Flow. 85032. ESDU International PLC, 1985. ''' unknowns = sum(i is None for i in (d_nozzle, d_mixing, Qs, Qp, P1, P2)) if unknowns > 1: raise Exception('Too many unknowns') elif unknowns < 1: raise Exception('Overspecified') C = rhos/rhop if Qp is not None and Qs is not None: M = Qs/Qp if d_nozzle is not None: A_nozzle = pi/4*d_nozzle*d_nozzle if d_mixing is not None: A_mixing = pi/4*d_mixing*d_mixing R = A_nozzle/A_mixing if P1 is None: return rhop/2*(Qp/A_nozzle)**2*((1+Kp) - C*(1 + Ks)*((M*R)/(1-R))**2 ) + P2 elif P2 is None: return -rhop/2*(Qp/A_nozzle)**2*((1+Kp) - C*(1 + Ks)*((M*R)/(1-R))**2 ) + P1 elif Qs is None: try: return ((-2*A_nozzle**2*P1 + 2*A_nozzle**2*P2 + Kp*Qp**2*rhop + Qp**2*rhop)/(C*rhop*(Ks + 1)))**0.5*(A_mixing - A_nozzle)/A_nozzle except ValueError: return -1j elif Qp is None: return A_nozzle*((2*A_mixing**2*P1 - 2*A_mixing**2*P2 - 4*A_mixing*A_nozzle*P1 + 4*A_mixing*A_nozzle*P2 + 2*A_nozzle**2*P1 - 2*A_nozzle**2*P2 + C*Ks*Qs**2*rhop + C*Qs**2*rhop)/(rhop*(Kp + 1)))**0.5/(A_mixing - A_nozzle) elif d_nozzle is None: def err(d_nozzle): return P1 - liquid_jet_pump_ancillary(rhop=rhop, rhos=rhos, Kp=Kp, Ks=Ks, d_nozzle=d_nozzle, d_mixing=d_mixing, Qp=Qp, Qs=Qs, P1=None, P2=P2) return brenth(err, 1E-9, d_mixing*20) elif d_mixing is None: def err(d_mixing): return P1 - liquid_jet_pump_ancillary(rhop=rhop, rhos=rhos, Kp=Kp, Ks=Ks, d_nozzle=d_nozzle, d_mixing=d_mixing, Qp=Qp, Qs=Qs, P1=None, P2=P2) try: return brenth(err, 1E-9, d_nozzle*20) except: return newton(err, d_nozzle*2)
def Stichlmair_wet(Vg, Vl, rhog, rhol, mug, voidage, specific_area, C1, C2, C3, H=1): r'''Calculates dry pressure drop across a packed column, using the Stichlmair [1]_ correlation. Uses three regressed constants for each type of packing, and voidage and specific area. This model is for irrigated columns only. Pressure drop is given by: .. math:: \frac{\Delta P_{irr}}{H} = \frac{\Delta P_{dry}}{H}\left(\frac {1-\epsilon + h_T}{1-\epsilon}\right)^{(2+c)/3} \left(\frac{\epsilon}{\epsilon-h_T}\right)^{4.65} .. math:: h_T = h_0\left[1 + 20\left(\frac{\Delta Pirr}{H\rho_L g}\right)^2\right] .. math:: Fr_L = \frac{V_L^2 a}{g \epsilon^{4.65}} .. math:: h_0 = 0.555 Fr_L^{1/3} .. math:: c = \frac{-C_1/Re_g - C_2/(2Re_g^{0.5})}{f_0} .. math:: \Delta P_{dry} = \frac{3}{4} f_0 \frac{1-\epsilon}{\epsilon^{4.65}} \rho_G \frac{H}{d_p}V_g^2 .. math:: f_0 = \frac{C_1}{Re_g} + \frac{C_2}{Re_g^{0.5}} + C_3 .. math:: d_p = \frac{6(1-\epsilon)}{a} Parameters ---------- Vg : float Superficial velocity of gas, Q/A [m/s] Vl : float Superficial velocity of liquid, Q/A [m/s] rhog : float Density of gas [kg/m^3] rhol : float Density of liquid [kg/m^3] mug : float Viscosity of gas [Pa*s] voidage : float Voidage of bed of packing material [] specific_area : float Specific area of the packing material [m^2/m^3] C1 : float Packing-specific constant [] C2 : float Packing-specific constant [] C3 : float Packing-specific constant [] H : float, optional Height of packing [m] Returns ------- dP : float Pressure drop across irrigated packing [Pa] Notes ----- This model is used by most process simulation tools. If H is not provided, it defaults to 1. If Z is not provided, it defaults to 1. A numerical solver is used and needed by this model. Its initial guess is the dry pressure drop. Convergence problems may occur. The model as described in [1]_ appears to have a typo, and could not match the example. As described in [2]_, however, the model works. Examples -------- Example is from [1]_, matches. >>> Stichlmair_wet(Vg=0.4, Vl = 5E-3, rhog=5., rhol=1200., mug=5E-5, ... voidage=0.68, specific_area=260., C1=32., C2=7., C3=1.) 539.8768237253518 References ---------- .. [1] Stichlmair, J., J. L. Bravo, and J. R. Fair. "General Model for Prediction of Pressure Drop and Capacity of Countercurrent Gas/liquid Packed Columns." Gas Separation & Purification 3, no. 1 (March 1989): 19-28. doi:10.1016/0950-4214(89)80016-7. .. [2] Piche, Simon R., Faical Larachi, and Bernard P. A. Grandjean. "Improving the Prediction of Irrigated Pressure Drop in Packed Absorption Towers." The Canadian Journal of Chemical Engineering 79, no. 4 (August 1, 2001): 584-94. doi:10.1002/cjce.5450790417. ''' dp = 6.0*(1.0 - voidage)/specific_area Re = Vg*rhog*dp/mug f0 = C1/Re + C2/Re**0.5 + C3 dP_dry = 3/4.*f0*(1-voidage)/voidage**4.65*rhog*H/dp*Vg*Vg c = (-C1/Re - C2/(2*Re**0.5))/f0 Frl = Vl**2*specific_area/(g*voidage**4.65) h0 = 0.555*Frl**(1/3.) def to_zero(dP_irr): hT = h0*(1.0 + 20.0*(dP_irr/H/rhol/g)**2) err = dP_dry/H*((1-voidage+hT)/(1.0 - voidage))**((2.0 + c)/3.)*(voidage/(voidage-hT))**4.65 -dP_irr/H return err return float(newton(to_zero, dP_dry))