def tabulate_solid(chemical, Tmin=None, Tmax=None, pts=10): pd = pandas() chem = Chemical(chemical) (rhos, Cps) = [[] for i in range(2)] if not Tmin: # pragma: no cover if chem.Tm: Tmin = min(chem.Tm-100, 1e-2) else: Tmin = 150. if not Tmax: # pragma: no cover if chem.Tm: Tmax = chem.Tm else: Tmax = 350 Ts = np.linspace(Tmin, Tmax, pts) for T in Ts: chem = Chemical(chemical, T=T) rhos.append(chem.rhos) Cps.append(chem.Cps) data = OrderedDict() data['Density, kg/m^3'] = rhos data['Constant-pressure heat capacity, J/kg/K'] = Cps df = pd.DataFrame(data, index=Ts) df.index.name = 'T, K' return df
def test_bisplev(): from fluids.numerics import bisplev as my_bisplev from scipy.interpolate import bisplev tck = [ np.array([ 0.0, 0.0, 0.0, 0.0, 0.0213694, 0.0552542, 0.144818, 0.347109, 0.743614, 0.743614, 0.743614, 0.743614 ]), np.array([0.0, 0.0, 0.25, 0.5, 0.75, 1.0, 1.0]), np.array([ 1.0001228445490002, 0.9988161050974387, 0.9987070557919563, 0.9979385859402731, 0.9970983069823832, 0.96602540121758, 0.955136014969614, 0.9476842472211648, 0.9351143114374392, 0.9059649602818451, 0.9218915266550902, 0.9086000082864022, 0.8934758292610783, 0.8737960765592091, 0.83185251064324, 0.8664296734965998, 0.8349705397843921, 0.809133298969704, 0.7752206120745123, 0.7344035693011536, 0.817047920445813, 0.7694560150930563, 0.7250979336267909, 0.6766754605968431, 0.629304180420512, 0.7137237030611423, 0.6408238328161417, 0.5772000233279148, 0.504889627280836, 0.440579886434288, 0.6239736474980684, 0.5273646894226224, 0.43995388722059986, 0.34359277007615313, 0.26986439252143746, 0.5640689738382749, 0.4540959882735219, 0.35278120580740957, 0.24364672351604122, 0.1606942128340308 ]), 3, 1 ] my_tck = [ tck[0].tolist(), tck[1].tolist(), tck[2].tolist(), tck[3], tck[4] ] xs = np.linspace(0, 1, 10) zs = np.linspace(0, 1, 10) ys_scipy = bisplev(xs, zs, tck) ys = my_bisplev(xs, zs, my_tck) assert_allclose(ys, ys_scipy) ys_scipy = bisplev(0.5, .7, tck) ys = my_bisplev(.5, .7, my_tck) assert_allclose(ys, ys_scipy)
def test_linspace(): from fluids.numerics import linspace calc = linspace(-3, 10, endpoint=True, num=8) expect = np.linspace(-3, 10, endpoint=True, num=8) assert_allclose(calc, expect) calc = linspace(-3, 10, endpoint=False, num=20) expect = np.linspace(-3, 10, endpoint=False, num=20) assert_allclose(calc, expect) calc = linspace(0, 1e-10, endpoint=False, num=3) expect = np.linspace(0, 1e-10, endpoint=False, num=3) assert_allclose(calc, expect) calc = linspace(0, 1e-10, endpoint=False, num=2) expect = np.linspace(0, 1e-10, endpoint=False, num=2) assert_allclose(calc, expect) calc = linspace(0, 1e-10, endpoint=False, num=1) expect = np.linspace(0, 1e-10, endpoint=False, num=1) assert_allclose(calc, expect) calc, calc_step = linspace(0, 1e-10, endpoint=False, num=2, retstep=True) expect, expect_step = np.linspace(0, 1e-10, endpoint=False, num=2, retstep=True) assert_allclose(calc, expect) assert_allclose(calc_step, expect_step) calc, calc_step = linspace(0, 1e-10, endpoint=False, num=1, retstep=True) expect, expect_step = np.linspace(0, 1e-10, endpoint=False, num=1, retstep=True) assert_allclose(calc, expect) assert isnan(calc_step) # Cannot compare against numpy expect_step - it did not use to give nan in older versions calc, calc_step = linspace(100, 1000, endpoint=False, num=21, retstep=True) expect, expect_step = np.linspace(100, 1000, endpoint=False, num=21, retstep=True) assert_allclose(calc, expect) assert_allclose(calc_step, expect_step)
def tabulate_liq(chemical, Tmin=None, Tmax=None, pts=10): pd = pandas() chem = Chemical(chemical) (rhos, Cps, mugs, kgs, Prs, alphas, isobarics, JTs, Psats, sigmas, Hvaps, permittivities) = [[] for i in range(12)] if not Tmin: # pragma: no cover if chem.Tm: Tmin = chem.Tm else: Tmin = 273.15 if not Tmax: # pragma: no cover if chem.Tc: Tmax = chem.Tc else: Tmax = 450 Ts = np.linspace(Tmin, Tmax, pts) for T in Ts: chem = Chemical(chemical, T=T) rhos.append(chem.rhol) Cps.append(chem.Cpl) mugs.append(chem.mul) kgs.append(chem.kl) Prs.append(chem.Prl) alphas.append(chem.alphal) isobarics.append(chem.isobaric_expansion_l) JTs.append(chem.JTg) Psats.append(chem.Psat) Hvaps.append(chem.Hvap) sigmas.append(chem.sigma) permittivities.append(chem.permittivity) data = OrderedDict() data['Saturation pressure, Pa'] = Psats data['Density, kg/m^3'] = rhos data['Constant-pressure heat capacity, J/kg/K'] = Cps data['Heat of vaporization, J/kg'] = Hvaps data['Viscosity, Pa*s'] = mugs data['Thermal conductivity, W/m/K'] = kgs data['Surface tension, N/m'] = sigmas data['Prandtl number'] = Prs data['Thermal diffusivity, m^2/s'] = alphas data['Isobaric expansion, 1/K'] = isobarics data['Joule-Thompson expansion coefficient, K/Pa'] = JTs data['PermittivityLiquid'] = permittivities df = pd.DataFrame(data, index=Ts) df.index.name = 'T, K' return df
def validate_prop(self, prop, phase, evaluated_points=30): phase_key = '_g' if phase == 'g' else '_l' name = prop + phase_key if prop in ['CP0MOLAR']: name = prop pts = getattr(self, name).points predictor = getattr(self, name) for i in range(len(pts) - 1): Ts = np.linspace(pts[i], pts[i + 1], evaluated_points) # print(Ts[0], Ts[-1]) prop_approx = [predictor(T) for T in Ts] prop_calc = [ CoolProp_T_dependent_property(T, self.CAS, prop, phase) for T in Ts ] # print(prop_approx) # print(prop_calc) # The approximators do give differences at the very low value side # so we need atol=1E-9 # print(prop, self.CAS, prop_approx[0], prop_calc[0]) try: assert_close1d(prop_approx, prop_calc, rtol=1E-7, atol=1E-9) except: '''There are several cases that assert_allclose doesn't deal with well for some reason. We could increase rtol, but instead the relative errors are used here to check everything is as desidred. Some example errors this won't trip on but assert_allclose does are: 1.00014278827e-08 1.62767956613e-06 -0.0 -1.63895899641e-16 -4.93284549625e-15 ''' prop_calc = np.array(prop_calc) prop_approx = np.array(prop_approx) errs = abs((prop_calc - prop_approx) / prop_calc) try: assert max(errs) < 2E-6 except: print( '%s %s failed with mean relative error %s and maximum relative error %s' % (self.CAS, prop, str(np.mean(errs)), str(max(errs))))
def tabulate_gas(chemical, Tmin=None, Tmax=None, pts=10): chem = Chemical(chemical) pd = pandas() (rhos, Cps, Cvs, mugs, kgs, Prs, alphas, isobarics, isentropics, JTs) = [[] for i in range(10)] if not Tmin: # pragma: no cover if chem.Tm: Tmin = chem.Tm else: Tmin = 273.15 if not Tmax: # pragma: no cover if chem.Tc: Tmax = chem.Tc else: Tmax = 450 Ts = np.linspace(Tmin, Tmax, pts) for T in Ts: chem = Chemical(chemical, T=T) rhos.append(chem.rhog) Cps.append(chem.Cpg) Cvs.append(chem.Cvg) mugs.append(chem.mug) kgs.append(chem.kg) Prs.append(chem.Prg) alphas.append(chem.alphag) isobarics.append(chem.isobaric_expansion_g) isentropics.append(chem.isentropic_exponent) JTs.append(chem.JTg) data = OrderedDict() data['Density, kg/m^3'] = rhos data['Constant-pressure heat capacity, J/kg/K'] = Cps data['Constant-volume heat capacity, J/kg/K'] = Cvs data['Viscosity, Pa*s'] = mugs data['Thermal conductivity, W/m/K'] = kgs data['Prandtl number'] = Prs data['Thermal diffusivity, m^2/s'] = alphas data['Isobaric expansion, 1/K'] = isobarics data['Isentropic exponent'] = isentropics data['Joule-Thompson expansion coefficient, K/Pa'] = JTs df = pd.DataFrame(data, index=Ts) # add orient='index' df.index.name = 'T, K' return df
def test_diff(): from fluids.numerics import diff test_arrs = [ np.ones(10), np.zeros(10), np.arange(1, 10), np.arange(1, 10) * 25.1241251, (np.arange(1, 10)**1.2), (10.1 + np.arange(1, 10)**20), (10.1 + np.linspace(-100, -10, 9)), (np.logspace(-10, -100, 19)**1.241), (np.logspace(10, 100, 15)**1.241) ] for test_arr in test_arrs: arr = test_arr.tolist() for n in range(5): diff_np = np.diff(arr, n=n) diff_py = diff(arr, n=n) assert_allclose(diff_np, diff_py) assert tuple(diff([1, 2, 3], n=0)) == tuple([1, 2, 3]) with pytest.raises(Exception): diff([1, 2, 3], n=-1)
def test_splev(): from fluids.numerics import py_splev from scipy.interpolate import splev # Originally Dukler_XA_tck tck = [ np.array([ -2.4791105294648372, -2.4791105294648372, -2.4791105294648372, -2.4791105294648372, 0.14360803483759585, 1.7199938263676038, 1.7199938263676038, 1.7199938263676038, 1.7199938263676038 ]), np.array([ 0.21299880246561081, 0.16299733301915248, -0.042340970712679615, -1.9967836909384598, -2.9917366639619414, 0.0, 0.0, 0.0, 0.0 ]), 3 ] my_tck = [tck[0].tolist(), tck[1].tolist(), tck[2]] xs = np.linspace(-3, 2, 100).tolist() # test extrapolation ys_scipy = splev(xs, tck, ext=0) ys = [py_splev(xi, my_tck, ext=0) for xi in xs] assert_allclose(ys, ys_scipy) # test truncating to side values ys_scipy = splev(xs, tck, ext=3) ys = [py_splev(xi, my_tck, ext=3) for xi in xs] assert_allclose(ys, ys_scipy) # Test returning zeros for bad values ys_scipy = splev(xs, tck, ext=1) ys = [py_splev(xi, my_tck, ext=1) for xi in xs] assert_allclose(ys, ys_scipy) # Test raising an error when extrapolating is not allowed with pytest.raises(ValueError): py_splev(xs[0], my_tck, ext=2) with pytest.raises(ValueError): splev(xs[0], my_tck, ext=2)
def test_interp(): from fluids.numerics import interp # Real world test data a = [ 0.29916, 0.29947, 0.31239, 0.31901, 0.32658, 0.33729, 0.34202, 0.34706, 0.35903, 0.36596, 0.37258, 0.38487, 0.38581, 0.40125, 0.40535, 0.41574, 0.42425, 0.43401, 0.44788, 0.45259, 0.47181, 0.47309, 0.49354, 0.49924, 0.51653, 0.5238, 0.53763, 0.54806, 0.55684, 0.57389, 0.58235, 0.59782, 0.60156, 0.62265, 0.62649, 0.64948, 0.65099, 0.6687, 0.67587, 0.68855, 0.69318, 0.70618, 0.71333, 0.72351, 0.74954, 0.74965 ] b = [ 0.164534, 0.164504, 0.163591, 0.163508, 0.163439, 0.162652, 0.162224, 0.161866, 0.161238, 0.160786, 0.160295, 0.15928, 0.159193, 0.157776, 0.157467, 0.156517, 0.155323, 0.153835, 0.151862, 0.151154, 0.14784, 0.147613, 0.144052, 0.14305, 0.140107, 0.138981, 0.136794, 0.134737, 0.132847, 0.129303, 0.127637, 0.124758, 0.124006, 0.119269, 0.118449, 0.113605, 0.113269, 0.108995, 0.107109, 0.103688, 0.102529, 0.099567, 0.097791, 0.095055, 0.087681, 0.087648 ] xs = np.linspace(0.29, 0.76, 100) ys = [interp(xi, a, b) for xi in xs.tolist()] ys_numpy = np.interp(xs, a, b) assert_allclose(ys, ys_numpy, atol=1e-12, rtol=1e-11) # Test custom extrapolation method xs = [1, 2, 3] ys = [.1, .2, .3] assert_close(interp(3.5, xs, ys, extrapolate=True), .35, rtol=1e-15) assert_close(interp(0, xs, ys, extrapolate=True), 0, rtol=1e-15) assert_close(interp(-1, xs, ys, extrapolate=True), -.1, rtol=1e-15) assert_close(interp(-100, xs, ys, extrapolate=True), -10, rtol=1e-15) assert_close(interp(10, xs, ys, extrapolate=True), 1, rtol=1e-15) assert_close(interp(10.0**30, xs, ys, extrapolate=True), 10.0**29, rtol=1e-15)
def integrate_drag_sphere(D, rhop, rho, mu, t, V=0, Method=None, distance=False): r'''Integrates the velocity and distance traveled by a particle moving at a speed which will converge to its terminal velocity. Performs an integration of the following expression for acceleration: .. math:: a = \frac{g(\rho_p-\rho_f)}{\rho_p} - \frac{3C_D \rho_f u^2}{4D \rho_p} 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] t : float Time to integrate the particle to, [s] V : float Initial velocity of the particle, [m/s] Method : string, optional A string of the function name to use, as in the dictionary drag_sphere_correlations distance : bool, optional Whether or not to calculate the distance traveled and return it as well Returns ------- v : float Velocity of falling sphere after time `t` [m/s] x : float, returned only if `distance` == True Distance traveled by the falling sphere in time `t`, [m] Notes ----- This can be relatively slow as drag correlations can be complex. There are analytical solutions available for the Stokes law regime (Re < 0.3). They were obtained from Wolfram Alpha. [1]_ was not used in the derivation, but also describes the derivation fully. .. math:: V(t) = \frac{\exp(-at) (V_0 a + b(\exp(at) - 1))}{a} .. math:: x(t) = \frac{\exp(-a t)\left[V_0 a(\exp(a t) - 1) + b\exp(a t)(a t-1) + b\right]}{a^2} .. math:: a = \frac{18\mu_f}{D^2\rho_p} .. math:: b = \frac{g(\rho_p-\rho_f)}{\rho_p} The analytical solution will automatically be used if the initial and terminal velocity is show the particle's behavior to be laminar. Note that this behavior requires that the terminal velocity of the particle be solved for - this adds slight (1%) overhead for the cases where particles are not laminar. Examples -------- >>> integrate_drag_sphere(D=0.001, rhop=2200., rho=1.2, mu=1.78E-5, t=0.5, ... V=30, distance=True) (9.686465044053476, 7.8294546436299175) References ---------- .. [1] Timmerman, Peter, and Jacobus P. van der Weele. "On the Rise and Fall of a Ball with Linear or Quadratic Drag." American Journal of Physics 67, no. 6 (June 1999): 538-46. https://doi.org/10.1119/1.19320. ''' laminar_initial = Reynolds(V=V, rho=rho, D=D, mu=mu) < 0.01 v_laminar_end_assumed = v_terminal(D=D, rhop=rhop, rho=rho, mu=mu, Method=Method) laminar_end = Reynolds(V=v_laminar_end_assumed, rho=rho, D=D, mu=mu) < 0.01 if Method == 'Stokes' or (laminar_initial and laminar_end and Method is None): try: t1 = 18.0 * mu / (D * D * rhop) t2 = g * (rhop - rho) / rhop V_end = exp(-t1 * t) * (t1 * V + t2 * (exp(t1 * t) - 1.0)) / t1 x_end = exp(-t1 * t) * (V * t1 * (exp(t1 * t) - 1.0) + t2 * exp(t1 * t) * (t1 * t - 1.0) + t2) / (t1 * t1) if distance: return V_end, x_end else: return V_end except OverflowError: # It is only necessary to integrate to terminal velocity t_to_terminal = time_v_terminal_Stokes(D, rhop, rho, mu, V0=V, tol=1e-9) if t_to_terminal > t: raise Exception('Should never happen') V_end, x_end = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu, t=t_to_terminal, V=V, Method='Stokes', distance=True) # terminal velocity has been reached - V does not change, but x does # No reason to believe this isn't working even though it isn't # matching the ode solver if distance: return V_end, x_end + V_end * (t - t_to_terminal) else: return V_end # This is a serious problem for small diameters # It would be possible to step slowly, using smaller increments # of time to avlid overflows. However, this unfortunately quickly # gets much, exponentially, slower than just using odeint because # for example solving 10000 seconds might require steps of .0001 # seconds at a diameter of 1e-7 meters. # x = 0.0 # subdivisions = 10 # dt = t/subdivisions # for i in range(subdivisions): # V, dx = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu, # t=dt, V=V, distance=True, # Method=Method) # x += dx # if distance: # return V, x # else: # return V Re_ish = rho * D / mu c1 = g * (rhop - rho) / rhop c2 = -0.75 * rho / (D * rhop) def dv_dt(V, t): if V == 0: # 64/Re goes to infinity, but gets multiplied by 0 squared. t2 = 0.0 else: # t2 = c2*V*V*Stokes(Re_ish*V) t2 = c2 * V * V * drag_sphere(Re_ish * V, Method=Method) return c1 + t2 # Number of intervals for the solution to be solved for; the integrator # doesn't care what we give it, but a large number of intervals are needed # For an accurate integration of the particle's distance traveled pts = 1000 if distance else 2 ts = np.linspace(0, t, pts) # Delayed import of necessaray functions from scipy.integrate import odeint, cumtrapz # Perform the integration Vs = odeint(dv_dt, [V], ts) # V_end = float(Vs[-1]) if distance: # Calculate the distance traveled x = float(cumtrapz(np.ravel(Vs), ts)[-1]) return V_end, x else: return V_end
def integrate_drag_sphere(D, rhop, rho, mu, t, V=0, Method=None, distance=False): r'''Integrates the velocity and distance traveled by a particle moving at a speed which will converge to its terminal velocity. Performs an integration of the following expression for acceleration: .. math:: a = \frac{g(\rho_p-\rho_f)}{\rho_p} - \frac{3C_D \rho_f u^2}{4D \rho_p} 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] t : float Time to integrate the particle to, [s] V : float Initial velocity of the particle, [m/s] Method : string, optional A string of the function name to use, as in the dictionary drag_sphere_correlations distance : bool, optional Whether or not to calculate the distance traveled and return it as well Returns ------- v : float Velocity of falling sphere after time `t` [m/s] x : float, returned only if `distance` == True Distance traveled by the falling sphere in time `t`, [m] Notes ----- This can be relatively slow as drag correlations can be complex. There are analytical solutions available for the Stokes law regime (Re < 0.3). They were obtained from Wolfram Alpha. [1]_ was not used in the derivation, but also describes the derivation fully. .. math:: V(t) = \frac{\exp(-at) (V_0 a + b(\exp(at) - 1))}{a} .. math:: x(t) = \frac{\exp(-a t)\left[V_0 a(\exp(a t) - 1) + b\exp(a t)(a t-1) + b\right]}{a^2} .. math:: a = \frac{18\mu_f}{D^2\rho_p} .. math:: b = \frac{g(\rho_p-\rho_f)}{\rho_p} The analytical solution will automatically be used if the initial and terminal velocity is show the particle's behavior to be laminar. Note that this behavior requires that the terminal velocity of the particle be solved for - this adds slight (1%) overhead for the cases where particles are not laminar. Examples -------- >>> integrate_drag_sphere(D=0.001, rhop=2200., rho=1.2, mu=1.78E-5, t=0.5, ... V=30, distance=True) (9.686465044053476, 7.8294546436299175) References ---------- .. [1] Timmerman, Peter, and Jacobus P. van der Weele. "On the Rise and Fall of a Ball with Linear or Quadratic Drag." American Journal of Physics 67, no. 6 (June 1999): 538-46. https://doi.org/10.1119/1.19320. ''' laminar_initial = Reynolds(V=V, rho=rho, D=D, mu=mu) < 0.01 v_laminar_end_assumed = v_terminal(D=D, rhop=rhop, rho=rho, mu=mu, Method=Method) laminar_end = Reynolds(V=v_laminar_end_assumed, rho=rho, D=D, mu=mu) < 0.01 if Method == 'Stokes' or (laminar_initial and laminar_end and Method is None): try: t1 = 18.0*mu/(D*D*rhop) t2 = g*(rhop-rho)/rhop V_end = exp(-t1*t)*(t1*V + t2*(exp(t1*t) - 1.0))/t1 x_end = exp(-t1*t)*(V*t1*(exp(t1*t) - 1.0) + t2*exp(t1*t)*(t1*t - 1.0) + t2)/(t1*t1) if distance: return V_end, x_end else: return V_end except OverflowError: # It is only necessary to integrate to terminal velocity t_to_terminal = time_v_terminal_Stokes(D, rhop, rho, mu, V0=V, tol=1e-9) if t_to_terminal > t: raise Exception('Should never happen') V_end, x_end = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu, t=t_to_terminal, V=V, Method='Stokes', distance=True) # terminal velocity has been reached - V does not change, but x does # No reason to believe this isn't working even though it isn't # matching the ode solver if distance: return V_end, x_end + V_end*(t - t_to_terminal) else: return V_end # This is a serious problem for small diameters # It would be possible to step slowly, using smaller increments # of time to avlid overflows. However, this unfortunately quickly # gets much, exponentially, slower than just using odeint because # for example solving 10000 seconds might require steps of .0001 # seconds at a diameter of 1e-7 meters. # x = 0.0 # subdivisions = 10 # dt = t/subdivisions # for i in range(subdivisions): # V, dx = integrate_drag_sphere(D=D, rhop=rhop, rho=rho, mu=mu, # t=dt, V=V, distance=True, # Method=Method) # x += dx # if distance: # return V, x # else: # return V Re_ish = rho*D/mu c1 = g*(rhop-rho)/rhop c2 = -0.75*rho/(D*rhop) def dv_dt(V, t): if V == 0: # 64/Re goes to infinity, but gets multiplied by 0 squared. t2 = 0.0 else: # t2 = c2*V*V*Stokes(Re_ish*V) t2 = c2*V*V*drag_sphere(Re_ish*V, Method=Method) return c1 + t2 # Number of intervals for the solution to be solved for; the integrator # doesn't care what we give it, but a large number of intervals are needed # For an accurate integration of the particle's distance traveled pts = 1000 if distance else 2 ts = np.linspace(0, t, pts) # Delayed import of necessaray functions from scipy.integrate import odeint, cumtrapz # Perform the integration Vs = odeint(dv_dt, [V], ts) # V_end = float(Vs[-1]) if distance: # Calculate the distance traveled x = float(cumtrapz(np.ravel(Vs), ts)[-1]) return V_end, x else: return V_end