thermofluids 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- thermofluids/Base/Quantity.py +16 -0
- thermofluids/Base/Statepoint.py +1191 -0
- thermofluids/Base/__init__.py +0 -0
- thermofluids/Base/thermofluids_registry.txt +5 -0
- thermofluids/Fluids/Drag.py +169 -0
- thermofluids/Fluids/Moody.py +57 -0
- thermofluids/Fluids/PressureDrop.py +322 -0
- thermofluids/Fluids/Re.py +88 -0
- thermofluids/Fluids/__init__.py +4 -0
- thermofluids/HeatTransfer/Gr.py +12 -0
- thermofluids/HeatTransfer/NTU.py +45 -0
- thermofluids/HeatTransfer/Nusselt.py +59 -0
- thermofluids/HeatTransfer/Ra.py +22 -0
- thermofluids/HeatTransfer/TwoPhase.py +249 -0
- thermofluids/HeatTransfer/__init__.py +6 -0
- thermofluids/HeatTransfer/lmtd.py +32 -0
- thermofluids/ProcessEquipment/Column.py +1004 -0
- thermofluids/ProcessEquipment/Compressor.py +167 -0
- thermofluids/ProcessEquipment/Ejector.py +145 -0
- thermofluids/ProcessEquipment/HeatExchanger.py +109 -0
- thermofluids/ProcessEquipment/IsoCompressor.py +124 -0
- thermofluids/ProcessEquipment/Pipe.py +21 -0
- thermofluids/ProcessEquipment/Pump.py +109 -0
- thermofluids/ProcessEquipment/Turbine.py +53 -0
- thermofluids/ProcessEquipment/Valve.py +35 -0
- thermofluids/ProcessEquipment/__init__.py +3 -0
- thermofluids/Utilities/FlowDiagram.py +111 -0
- thermofluids/Utilities/__init__.py +1 -0
- thermofluids/__init__.py +3 -0
- thermofluids-2.0.0.dist-info/METADATA +29 -0
- thermofluids-2.0.0.dist-info/RECORD +32 -0
- thermofluids-2.0.0.dist-info/WHEEL +4 -0
|
File without changes
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
from warnings import warn
|
|
2
|
+
from ..Base.Quantity import Quantity
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def c_d(shape):
|
|
6
|
+
"""
|
|
7
|
+
https://en.wikipedia.org/wiki/Drag_coefficient
|
|
8
|
+
:param shape:
|
|
9
|
+
:return:
|
|
10
|
+
"""
|
|
11
|
+
if shape == 'sphere':
|
|
12
|
+
cd = 0.47
|
|
13
|
+
elif shape == 'half sphere':
|
|
14
|
+
cd = 0.42
|
|
15
|
+
elif shape == 'cone':
|
|
16
|
+
cd = 0.5
|
|
17
|
+
elif shape == 'cube':
|
|
18
|
+
cd = 1.05
|
|
19
|
+
elif shape == 'angled cube':
|
|
20
|
+
cd = 0.8
|
|
21
|
+
elif shape == 'long cylinder':
|
|
22
|
+
cd = 0.82
|
|
23
|
+
elif shape == 'short cylinder':
|
|
24
|
+
cd = 1.15
|
|
25
|
+
elif shape == 'streamlined':
|
|
26
|
+
cd = 0.04
|
|
27
|
+
elif shape == 'half streamlined':
|
|
28
|
+
cd = 0.09
|
|
29
|
+
else:
|
|
30
|
+
raise ValueError(f'No drag coefficient available for {shape}. Please select from sphere, half sphere, cone, '
|
|
31
|
+
f'cube, angled cube, long cylinder, short cylinder, streamlined, half streamlined.')
|
|
32
|
+
return cd
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def f_drag(shape, density, velocity, area=None):
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
:param shape: Shape of the object <str>
|
|
39
|
+
:param density: Density of the fluid <Quantity, kg/m^3>
|
|
40
|
+
:param velocity: Velocity of the fluid relative to the thing getting dragged <Quantity, m/s>
|
|
41
|
+
:param area: Optional, frontal area of the object <Quantity, m^2>
|
|
42
|
+
:return: Pressure on the object (without area specified) or force on the object (with area specified)
|
|
43
|
+
"""
|
|
44
|
+
cd = c_d(shape.lower())
|
|
45
|
+
|
|
46
|
+
if area == None:
|
|
47
|
+
return 0.5 * cd * density * velocity**2. # return as pressure
|
|
48
|
+
else:
|
|
49
|
+
return 0.5 * cd * density * velocity**2. * area # return as force
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _settling_vel_stokes(rho_solid, rho_liquid, visc_liquid, d_solid):
|
|
53
|
+
g = Quantity(9.81, 'm*s^-2')
|
|
54
|
+
if rho_solid > rho_liquid:
|
|
55
|
+
return 2. / 9. * (rho_solid - rho_liquid) / visc_liquid * g * (d_solid / 2.) ** 2.
|
|
56
|
+
else:
|
|
57
|
+
return 2. / 9. * (rho_liquid - rho_solid) / visc_liquid * g * (d_solid / 2.) ** 2.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _settling_vel_laminar(rho_solid, rho_liquid, d_solid, re):
|
|
61
|
+
from math import exp, log
|
|
62
|
+
g = Quantity(9.81, 'm*s^-2')
|
|
63
|
+
cd = exp(0.0453 * log(re)**2. - 0.8379 * log(re) + 2.8568) # fit to 25 < Re < 4000 from https://en.wikipedia.org/wiki/Drag_coefficient#/media/File:Drag_coefficient_on_a_sphere_vs._Reynolds_number_-_main_trends.svg
|
|
64
|
+
if rho_solid > rho_liquid:
|
|
65
|
+
return (4. / (3. * cd) * d_solid * g * (rho_solid - rho_liquid) / rho_liquid) ** 0.5
|
|
66
|
+
else:
|
|
67
|
+
return (4. / (3. * cd) * d_solid * g * (rho_liquid - rho_solid) / rho_liquid) ** 0.5
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _settling_vel_turbulent(rho_solid, rho_liquid, d_solid, re):
|
|
71
|
+
g = Quantity(9.81, 'm*s^-2')
|
|
72
|
+
cd = 0.47
|
|
73
|
+
if rho_solid > rho_liquid:
|
|
74
|
+
return (4. / (3. * cd) * d_solid * g * (rho_solid - rho_liquid) / rho_liquid) ** 0.5
|
|
75
|
+
else:
|
|
76
|
+
return (4. / (3. * cd) * d_solid * g * (rho_liquid - rho_solid) / rho_liquid) ** 0.5
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def settling_vel(statepoint, d_solid, rho_solid, d_h_ext, re='auto', detailed=False):
|
|
80
|
+
"""
|
|
81
|
+
This function calculates the settling velocity for a solid particle in a flowing liquid. If the fluid is moving
|
|
82
|
+
slower than the settling velocity, the particle will tend to settle out. If its moving faster, it will tend to
|
|
83
|
+
remain entrained.
|
|
84
|
+
There are three regimes:
|
|
85
|
+
|
|
86
|
+
1. Stokes flow (Re<10: force balance is Stokes drag vs gravity
|
|
87
|
+
2. Laminar flow (10<Re<3000): force balance is hydrodynamic drag vs gravity
|
|
88
|
+
3. Turbulent flow (Re>3000): force balance is hydrodynamic drag vs gravity
|
|
89
|
+
|
|
90
|
+
:param statepoint: statepoint instance of the fluid containing the solids
|
|
91
|
+
:param d_solid: diameter of the solid particle
|
|
92
|
+
:param rho_solid: density of the solid particle
|
|
93
|
+
:param d_h_ext: hydraulic diameter of the flow area
|
|
94
|
+
:param re: Reynolds number, if known. If left default='auto', it will be calculated from the settling velocity
|
|
95
|
+
:return: settling velocity [m/s]
|
|
96
|
+
"""
|
|
97
|
+
# assert rho_solid > statepoint.rho, "Not currently set up to handle solids less dense than fluid"
|
|
98
|
+
|
|
99
|
+
if re != 'auto':
|
|
100
|
+
if re < 10.: # Stokes flow
|
|
101
|
+
v = _settling_vel_stokes(rho_solid=rho_solid, rho_liquid=statepoint.rho, visc_liquid=statepoint.visc, d_solid=d_solid)
|
|
102
|
+
elif re < 3000.: # Laminar flow
|
|
103
|
+
v = _settling_vel_laminar(rho_solid=rho_solid, rho_liquid=statepoint.rho, d_solid=d_solid, re=re)
|
|
104
|
+
else: # Turbulent flow
|
|
105
|
+
v = _settling_vel_turbulent(rho_solid=rho_solid, rho_liquid=statepoint.rho, d_solid=d_solid, re=re)
|
|
106
|
+
else:
|
|
107
|
+
v = Quantity(0.01, 'm*s^-1')
|
|
108
|
+
res = 1.
|
|
109
|
+
n_iter = 0.
|
|
110
|
+
while res > 0.01:
|
|
111
|
+
v_last = v
|
|
112
|
+
re = statepoint.rho * v * d_h_ext / statepoint.visc
|
|
113
|
+
# print(statepoint.rho, v, d_h_ext, statepoint.visc, re)
|
|
114
|
+
if re < 10.: # Stokes flow
|
|
115
|
+
v = _settling_vel_stokes(rho_solid=rho_solid, rho_liquid=statepoint.rho, visc_liquid=statepoint.visc, d_solid=d_solid)
|
|
116
|
+
elif re < 3000.: # Laminar flow
|
|
117
|
+
v = _settling_vel_laminar(rho_solid=rho_solid, rho_liquid=statepoint.rho, d_solid=d_solid, re=re)
|
|
118
|
+
else: # Turbulent flow
|
|
119
|
+
v = _settling_vel_turbulent(rho_solid=rho_solid, rho_liquid=statepoint.rho, d_solid=d_solid, re=re)
|
|
120
|
+
res = abs((v - v_last) / v)
|
|
121
|
+
n_iter += 1
|
|
122
|
+
if n_iter > 1000:
|
|
123
|
+
warn(f'Settling velocity calculation did not converge. Last two iterations gave v={v} and v={v_last}')
|
|
124
|
+
break
|
|
125
|
+
# print 'Residual: ', res, v, re
|
|
126
|
+
|
|
127
|
+
if detailed:
|
|
128
|
+
if re < 10:
|
|
129
|
+
print('\tSettling velocity in Stokes flow regime')
|
|
130
|
+
elif re < 3000:
|
|
131
|
+
print('\tSettling velocity in laminar flow regime')
|
|
132
|
+
else:
|
|
133
|
+
print('\tSettling velocity in turbulent flow regime. Warning- this is not well characterized in this model')
|
|
134
|
+
|
|
135
|
+
return v
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
if __name__ == '__main__':
|
|
141
|
+
import matplotlib.pyplot as plt
|
|
142
|
+
from ..Base.Statepoint import Statepoint
|
|
143
|
+
|
|
144
|
+
rho = Quantity(1000., 'kg*m^-3')
|
|
145
|
+
u = Quantity(5., 'm*s^-1')
|
|
146
|
+
A = Quantity(3.14 / 4. * 0.0254**2., 'm^2')
|
|
147
|
+
# print f_drag(shape='sphere', density=rho, velocity=u, area=A)
|
|
148
|
+
|
|
149
|
+
sp = Statepoint(fluid='INCOMP::TD12', t=0., p=1.)
|
|
150
|
+
fig1 = plt.figure()
|
|
151
|
+
ax1 = fig1.add_subplot(111)
|
|
152
|
+
ds = [0.01, 0.003, 0.001, 0.00033, 0.0001]
|
|
153
|
+
for d in ds:
|
|
154
|
+
vs = []
|
|
155
|
+
re_array = list(range(4000))
|
|
156
|
+
for re in re_array:
|
|
157
|
+
v = settling_vel(statepoint=sp,
|
|
158
|
+
d_solid=Quantity(d, 'm'),
|
|
159
|
+
rho_solid=Quantity(1000., 'kg*m^-3'),
|
|
160
|
+
d_h_ext=Quantity(0.1, 'm'),
|
|
161
|
+
re=re)
|
|
162
|
+
vs.append(v)
|
|
163
|
+
|
|
164
|
+
plt.plot(re_array, vs)
|
|
165
|
+
plt.legend(ds, title='Ice size', )
|
|
166
|
+
plt.xlabel('Re')
|
|
167
|
+
plt.ylabel('Settling velocity [m/s]')
|
|
168
|
+
plt.ylim(0, 0.5)
|
|
169
|
+
plt.show()
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""
|
|
2
|
+
From http://nullege.com/codes/show/src@l@i@libtsl-HEAD@mr_darcy.py
|
|
3
|
+
Edited by Luke Erickson to include transition region
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from warnings import warn
|
|
7
|
+
from math import log10
|
|
8
|
+
from ..Base.Quantity import Quantity
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def frictionfactor(Re,relrough):
|
|
12
|
+
"""
|
|
13
|
+
Returns the Darcy friction factor based on reynolds # and relative roughness.
|
|
14
|
+
"""
|
|
15
|
+
if type(Re) is Quantity:
|
|
16
|
+
assert Re.units == '', f'Re must be unitless but has units {Re.units}'
|
|
17
|
+
if type(relrough) is Quantity:
|
|
18
|
+
assert relrough.units == '', f'Relative roughness must be unitless but has units {relrough.units}'
|
|
19
|
+
if relrough > 0.05:
|
|
20
|
+
warn('Moody diagram only valid for relative roughness < 0.05. Received value %f' % relrough)
|
|
21
|
+
|
|
22
|
+
regime = _flowregime(Re,relrough)
|
|
23
|
+
|
|
24
|
+
if regime=='laminar':
|
|
25
|
+
return _laminar(Re)
|
|
26
|
+
elif regime=='turbulent':
|
|
27
|
+
return _turbulent(Re,relrough)
|
|
28
|
+
elif regime=='transition':
|
|
29
|
+
return _transition(Re, relrough)
|
|
30
|
+
|
|
31
|
+
def _flowregime(Re,relrough):
|
|
32
|
+
if Re < 2300.:
|
|
33
|
+
return "laminar"
|
|
34
|
+
elif Re > 4000.:
|
|
35
|
+
return "turbulent"
|
|
36
|
+
else:
|
|
37
|
+
return "transition"
|
|
38
|
+
|
|
39
|
+
def _laminar(Re):
|
|
40
|
+
return 64.0 / Re
|
|
41
|
+
|
|
42
|
+
def _turbulent(Re,relrough):
|
|
43
|
+
# Uses Serghides's Solution to approximate the Colebrook eqn
|
|
44
|
+
a=-2*log10(relrough/3.7 + 12.0/Re)
|
|
45
|
+
b=-2*log10(relrough/3.7 + 2.51*a/Re)
|
|
46
|
+
c=-2*log10(relrough/3.7 + 2.51*b/Re)
|
|
47
|
+
return (a-(b-a)**2/(c-2*b+a))**-2.0 #returns f
|
|
48
|
+
|
|
49
|
+
def _transition(Re, relrough):
|
|
50
|
+
"""
|
|
51
|
+
Assume linear interpolation between laminar and turbulent regions
|
|
52
|
+
"""
|
|
53
|
+
warn("Using interpolated friction factor in high uncertainly transition Re region")
|
|
54
|
+
|
|
55
|
+
turb = _turbulent(Re, relrough)
|
|
56
|
+
lam = _laminar(Re)
|
|
57
|
+
return lam + (Re.magnitude-2300.) / (4000.-2300.) * (turb - lam)
|
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
from math import sqrt, log, sin, cos, tan
|
|
2
|
+
|
|
3
|
+
from thermofluids import Quantity
|
|
4
|
+
from thermofluids.Fluids.Moody import frictionfactor
|
|
5
|
+
from thermofluids.Fluids.Re import Re
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def pressure_drop(statepoint, roughness, L, d1, d2=None, v=None, shape='round', props='REFPROP', units='bar', detailed=False):
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
:param statepoint:
|
|
12
|
+
:param roughness:
|
|
13
|
+
:param L:
|
|
14
|
+
:param d1:
|
|
15
|
+
:param d2:
|
|
16
|
+
:param shape:
|
|
17
|
+
:param props:
|
|
18
|
+
:return:
|
|
19
|
+
"""
|
|
20
|
+
# if not isinstance(L, Quantity):
|
|
21
|
+
# L = Quantity(L, 'm')
|
|
22
|
+
# if not isinstance(d1, Quantity):
|
|
23
|
+
# d1 = Quantity(d1, 'm')
|
|
24
|
+
# if not isinstance(d2, Quantity):
|
|
25
|
+
# d2 = Quantity(d2, 'm')
|
|
26
|
+
# if not isinstance(roughness, Quantity):
|
|
27
|
+
# roughness = Quantity(roughness, 'm')
|
|
28
|
+
# assert type(shape) in (str, None), 'shape needs to be a string, got %r' % shape
|
|
29
|
+
|
|
30
|
+
if props=='ideal':
|
|
31
|
+
if statepoint.fluid.lower()=='air':
|
|
32
|
+
rho = statepoint.p.to('bar').magnitude * 1.e5 / (287.058 * (statepoint.t.magnitude+273.15))
|
|
33
|
+
else:
|
|
34
|
+
raise Exception('No ideal density calcs available for this fluid')
|
|
35
|
+
else:
|
|
36
|
+
rho = statepoint.rho
|
|
37
|
+
|
|
38
|
+
m = statepoint.m
|
|
39
|
+
|
|
40
|
+
if type(shape) is str:
|
|
41
|
+
if shape.lower() in ('round', 'circle'):
|
|
42
|
+
d_h = d1
|
|
43
|
+
v = m / rho / (3.14/4.*d_h**2.)
|
|
44
|
+
elif shape.lower() == 'square':
|
|
45
|
+
d_h = d1
|
|
46
|
+
v = m / rho / d_h**2.
|
|
47
|
+
elif shape.lower() == 'rectangle':
|
|
48
|
+
if d2 == None:
|
|
49
|
+
raise Exception("d2 must be set for rectangular pressure drop calcs")
|
|
50
|
+
d_h = 4.*d1*d2 / (2. * d1 + 2. * d2)
|
|
51
|
+
v = m / rho / (d1 * d2)
|
|
52
|
+
elif shape.lower() == 'pillow':
|
|
53
|
+
#use this for a mueller plate, assumes d1 is pillow height and d2 is pillow width but they can acutally be put in any which way
|
|
54
|
+
if d2 == None:
|
|
55
|
+
raise Exception("d2 must be set for pillow pressure drop calcs")
|
|
56
|
+
A = d1*d2*2.
|
|
57
|
+
P = ((d1/2.)**2 + (d2/2.)**2)**(0.5)*4.
|
|
58
|
+
d_h = 4. * A / P
|
|
59
|
+
v = m / rho / A
|
|
60
|
+
elif shape.lower() == 'annulus':
|
|
61
|
+
if d2 == None:
|
|
62
|
+
raise Exception("d2 must be set for annular pressure drop calcs")
|
|
63
|
+
d_h = d1 - d2
|
|
64
|
+
v = m / rho / (3.14 / 4. * (d1**2. - d2**2.))
|
|
65
|
+
elif shape.lower() == 'shell-and-tube':
|
|
66
|
+
d_h = d1
|
|
67
|
+
v = v
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError(f"No calculations available for shape type {shape}")
|
|
70
|
+
elif shape is None:
|
|
71
|
+
d_h = d1
|
|
72
|
+
assert v is not None, 'Must assign either shape and dimensions or velocity'
|
|
73
|
+
else:
|
|
74
|
+
raise TypeError("Shape parameter must be string or None")
|
|
75
|
+
|
|
76
|
+
if detailed:
|
|
77
|
+
print('\t\tHydraulic diameter: ', d_h)
|
|
78
|
+
print('\t\tVelocity: ', v)
|
|
79
|
+
|
|
80
|
+
re = Re(statepoint, d_h, shape='rectangle', v=v)
|
|
81
|
+
if detailed:
|
|
82
|
+
print('\t\tRe: ', re)
|
|
83
|
+
|
|
84
|
+
ff = frictionfactor(re, roughness/d_h)
|
|
85
|
+
if detailed:
|
|
86
|
+
print('\t\tff: ', ff)
|
|
87
|
+
|
|
88
|
+
dP = (ff * rho / 2. * v**2 / d_h * L)
|
|
89
|
+
if detailed:
|
|
90
|
+
print('\t\tdP: ', dP)
|
|
91
|
+
|
|
92
|
+
if units == 'bar':
|
|
93
|
+
return dP * Quantity(1.e-5, 'bar^1*m^1*s^2*kg^-1')
|
|
94
|
+
if units in ('Pa', 'kg/m*s^2', 'kg*m^-1*s^-2'):
|
|
95
|
+
return dP
|
|
96
|
+
if units == 'kPa':
|
|
97
|
+
return dP * Quantity(1.e-3, 'kPa*kg^-1*m*s^2')
|
|
98
|
+
if units == 'psi':
|
|
99
|
+
return dP * Quantity(0.000145, 'psi*kg^-1*m*s^2')
|
|
100
|
+
if units == 'm':
|
|
101
|
+
return dP / (Quantity(9.81, 'm*s^-2') * statepoint.rho)
|
|
102
|
+
if units == 'in':
|
|
103
|
+
return (dP / (Quantity(9.81, 'm*s^-2') * statepoint.rho)).to('in')
|
|
104
|
+
else:
|
|
105
|
+
raise ValueError('No conversion for units ' + str(units))
|
|
106
|
+
|
|
107
|
+
def pressure_drop_sudden_expansion(statepoint, d1, d2,roughness, units='bar'):
|
|
108
|
+
"""
|
|
109
|
+
Calculates the pressure drop through a sudden expansion
|
|
110
|
+
Based on https://neutrium.net/fluid_flow/pressure-loss-from-fittings-expansion-and-reduction-in-pipe-size/
|
|
111
|
+
:param Re: Reynolds number of the inlet flow [-]
|
|
112
|
+
:param d1: diameter of inlet flow [m]
|
|
113
|
+
:param d2: diameter of the outlet flow [m]
|
|
114
|
+
:param ff: Darcy friction factor [-]
|
|
115
|
+
:param units: return units
|
|
116
|
+
:return: pressure drop [return units]
|
|
117
|
+
"""
|
|
118
|
+
v = statepoint.m / statepoint.rho / (3.14 / 4. * d1 ** 2.)
|
|
119
|
+
re = Re(statepoint, d1, shape='round', v=v)
|
|
120
|
+
ff = frictionfactor(re, roughness / d1)
|
|
121
|
+
|
|
122
|
+
# raise Exception('This model seems real dubious- value shouldn\'t get so big at high diameter ratios')
|
|
123
|
+
if re < 4000:
|
|
124
|
+
K = 2. * (1. - (d1 / d2)**4.)
|
|
125
|
+
else:
|
|
126
|
+
K = (1. + 0.8 * ff) * (1. - (d1 / d2)**2.)**2.
|
|
127
|
+
|
|
128
|
+
vel = statepoint.m / statepoint.rho / (3.14 / 4. * d1**2.)
|
|
129
|
+
dP = 0.5 * K * statepoint.rho * vel**2.
|
|
130
|
+
dP.to(units)
|
|
131
|
+
return dP
|
|
132
|
+
|
|
133
|
+
def pressure_drop_sudden_contraction(statepoint, roughness, d1, d2, units='bar'):
|
|
134
|
+
"""
|
|
135
|
+
Calculates the pressure drop through a sudden contraction
|
|
136
|
+
Based on https://neutrium.net/fluid_flow/pressure-loss-from-fittings-expansion-and-reduction-in-pipe-size/
|
|
137
|
+
:param Re: Reynolds number of the inlet flow [-]
|
|
138
|
+
:param d1: diameter of inlet flow [m]
|
|
139
|
+
:param d2: diameter of the outlet flow [m]
|
|
140
|
+
:param ff: Darcy friction factor [-]
|
|
141
|
+
:param units: return units
|
|
142
|
+
:return: pressure drop [return units]
|
|
143
|
+
"""
|
|
144
|
+
v = statepoint.m / statepoint.rho / (3.14 / 4. * d1 ** 2.)
|
|
145
|
+
re = Re(statepoint, d1, shape='round', v=v)
|
|
146
|
+
ff = frictionfactor(re, roughness / d1)
|
|
147
|
+
#raise Exception('This model seems real dubious- value shouldn\'t get so big at high diameter ratios')
|
|
148
|
+
if re < 2500:
|
|
149
|
+
K = (1.2 + 160. / re) * ((d1 / d2)**4. - 1.)
|
|
150
|
+
else:
|
|
151
|
+
K = (0.6 + 0.48 * ff) * (d1 / d2)**2. * ((d1 / d2)**2. - 1.)
|
|
152
|
+
|
|
153
|
+
dP = 0.5 * K * statepoint.rho * v**2.
|
|
154
|
+
dP.to(units)
|
|
155
|
+
print('Kc: ', K, 'Re: ', Re, 'ff: ', ff)
|
|
156
|
+
return dP
|
|
157
|
+
|
|
158
|
+
def pressure_drop_tapered_contraction(statepoint, Re, d1, d2, ff, angle, units='bar'):
|
|
159
|
+
"""
|
|
160
|
+
Calculates the pressure drop through a sudden contraction
|
|
161
|
+
Based on https://neutrium.net/fluid_flow/pressure-loss-from-fittings-expansion-and-reduction-in-pipe-size/
|
|
162
|
+
:param Re: Reynolds number of the inlet flow [-]
|
|
163
|
+
:param d1: diameter of inlet flow [m]
|
|
164
|
+
:param d2: diameter of the outlet flow [m]
|
|
165
|
+
:param ff: Darcy friction factor [-]
|
|
166
|
+
:param units: return units
|
|
167
|
+
:param angle: angle between the axis of the pipe and the wall of the taper [degrees]
|
|
168
|
+
----\
|
|
169
|
+
( \
|
|
170
|
+
angle ( \\_____
|
|
171
|
+
flow----> ------ _____
|
|
172
|
+
/
|
|
173
|
+
/
|
|
174
|
+
____/
|
|
175
|
+
:return: pressure drop [return units]
|
|
176
|
+
"""
|
|
177
|
+
raise Exception('This model seems real dubious- value shouldn\'t get so big at high diameter ratios')
|
|
178
|
+
dP_base = pressure_drop_sudden_contraction(statepoint=statepoint, Re=Re, d1=d1, d2=d2, ff=ff, units=units)
|
|
179
|
+
if angle < 22.5:
|
|
180
|
+
dP = dP_base * 1.6 * sin(angle * 3.14 / 180.)
|
|
181
|
+
else:
|
|
182
|
+
dP = dP_base * sqrt(sin(angle * 3.14 / 180.))
|
|
183
|
+
return dP
|
|
184
|
+
|
|
185
|
+
def pressure_drop_tapered_expansion(statepoint, Re, d1, d2, ff, angle, units='bar'):
|
|
186
|
+
"""
|
|
187
|
+
Calculates the pressure drop through a sudden contraction
|
|
188
|
+
Based on https://neutrium.net/fluid_flow/pressure-loss-from-fittings-expansion-and-reduction-in-pipe-size/
|
|
189
|
+
:param Re: Reynolds number of the inlet flow [-]
|
|
190
|
+
:param d1: diameter of inlet flow [m]
|
|
191
|
+
:param d2: diameter of the outlet flow [m]
|
|
192
|
+
:param ff: Darcy friction factor [-]
|
|
193
|
+
:param units: return units
|
|
194
|
+
:param angle: angle between the axis of the pipe and the wall of the taper [degrees]
|
|
195
|
+
----\
|
|
196
|
+
( \
|
|
197
|
+
angle ( \\_____
|
|
198
|
+
------ _____ <--- flow
|
|
199
|
+
/
|
|
200
|
+
/
|
|
201
|
+
____/
|
|
202
|
+
:return: pressure drop [return units]
|
|
203
|
+
"""
|
|
204
|
+
raise Exception('This model seems real dubious- value shouldn\'t get so big at high diameter ratios')
|
|
205
|
+
dP_base = pressure_drop_sudden_expansion(statepoint=statepoint, Re=Re, d1=d1, d2=d2, ff=ff, units=units)
|
|
206
|
+
if angle < 22.5:
|
|
207
|
+
dP = dP_base * 2.6 * sin(angle * 3.14 / 180.)
|
|
208
|
+
else:
|
|
209
|
+
dP = dP_base
|
|
210
|
+
return dP
|
|
211
|
+
|
|
212
|
+
def pressure_drop_fitting(statepoint, fitting_type, d1, roughness, units='bar'):
|
|
213
|
+
"""
|
|
214
|
+
Calculates the pressure drop through an elbow
|
|
215
|
+
Based on https://neutrium.net/fluid_flow/pressure-loss-from-fittings-equivalent-length-method/
|
|
216
|
+
Secondary source for L/d values: http://www.metropumps.com/ResourcesFrictionLossData.pdf
|
|
217
|
+
:param statepoint: statepoint instance of fluid flowing through pipe
|
|
218
|
+
:param fitting_type: 'elbow', 'long elbow', '45 elbow'
|
|
219
|
+
:param d1: Hydraulic diameter [m]
|
|
220
|
+
:param roughness: pipe roughness [m]
|
|
221
|
+
:param units: return units [str]
|
|
222
|
+
:return: pressure drop [return units]
|
|
223
|
+
"""
|
|
224
|
+
ks = {'elbow-threaded': 30.,
|
|
225
|
+
'elbow-welded': 20., # r/d = 1
|
|
226
|
+
'long elbow-threaded': 16.,
|
|
227
|
+
'long elbow-welded': 12., # r/d = 2
|
|
228
|
+
'45 elbow': 16.,
|
|
229
|
+
'tee-straight': 20.,
|
|
230
|
+
'tee-branch': 60.,
|
|
231
|
+
'ball valve': 3.,
|
|
232
|
+
'gate valve': 8.,
|
|
233
|
+
'butterfly valve': 45., # Source 2, derived from k ratio of butterfly valve to gate valve
|
|
234
|
+
'pipe entrance flush': 0.5, # Source 2
|
|
235
|
+
'pipe exit': 1. # Source 2
|
|
236
|
+
}
|
|
237
|
+
try:
|
|
238
|
+
Ldeq = ks[fitting_type]
|
|
239
|
+
except:
|
|
240
|
+
raise ValueError('No minor loss coefficient for %s. Please select from %s' % (fitting_type, list(ks.keys())))
|
|
241
|
+
dP = pressure_drop(statepoint=statepoint, roughness=roughness, L=d1 * Ldeq, d1=d1, units=units)
|
|
242
|
+
|
|
243
|
+
return dP.to(units)
|
|
244
|
+
|
|
245
|
+
def pressure_drop_packed_bed(statepoint, L, d,A_cross,epsilon, units='bar'):
|
|
246
|
+
velocity = statepoint.m/(A_cross*statepoint.rho)
|
|
247
|
+
dP = L*(150.*statepoint.visc*velocity*(1-epsilon)**2/(d**2*epsilon**3) + 1.75*statepoint.rho*velocity**2*(1-epsilon)/(d*epsilon**3))
|
|
248
|
+
|
|
249
|
+
return dP.to(units)
|
|
250
|
+
|
|
251
|
+
def pressure_drop_corrugated_plates(statepoint, length, width, spacing, chevron_angle, detailed=False, units='bar'):
|
|
252
|
+
"""
|
|
253
|
+
Calcualtes the pressure drop through a channel of corrugated plates, such as in a brazed plate heat exchanger
|
|
254
|
+
Based on http://achp.sourceforge.net/ACHPComponents/PlateHeatExchanger.html#single-phase-flow
|
|
255
|
+
Seems to match Alfa Lavals pressure drop calculations pretty well
|
|
256
|
+
:param statepoint: a statepoint instance of the fluid flowing through the hx
|
|
257
|
+
:param length: plate length (in direction of flow)
|
|
258
|
+
:param width: plate width (perpendicular to direction of flow)
|
|
259
|
+
:param spacing: spacing between the plates [m]
|
|
260
|
+
:param chevron_angle: Angle between flow direction and chevron lines in radians. pi/2 is perpendicular to flow, 0 is parallel to flow
|
|
261
|
+
:return: pressure drop in specified units
|
|
262
|
+
"""
|
|
263
|
+
assert chevron_angle < 3.14/2., 'Chevron angle must be in radians, not degrees, and between 0 and pi/2'
|
|
264
|
+
|
|
265
|
+
vel = statepoint.m / (statepoint.rho * spacing * width)
|
|
266
|
+
if detailed:
|
|
267
|
+
print('\tVelocity: ', vel)
|
|
268
|
+
d_h = 2. * width * spacing / (width + spacing)
|
|
269
|
+
re = statepoint.rho * vel * d_h / statepoint.visc
|
|
270
|
+
if detailed:
|
|
271
|
+
print('\tReynolds number: ', re)
|
|
272
|
+
|
|
273
|
+
zeta0 = 1. / (1.8 * log(re) - 1.5) ** 2.
|
|
274
|
+
zeta1 = 3.8 * 39. / re ** 0.289
|
|
275
|
+
ff = 1. / (cos(chevron_angle) / sqrt(0.18 * tan(chevron_angle) + 0.36 * sin(chevron_angle) + zeta0 / cos(chevron_angle)) +
|
|
276
|
+
(1. - cos(chevron_angle)) / sqrt(zeta1)) ** 2.
|
|
277
|
+
if detailed:
|
|
278
|
+
print('\tFriction factor: ', ff)
|
|
279
|
+
hg = ff * re ** 2. / 2.
|
|
280
|
+
if detailed:
|
|
281
|
+
print('\tHagen number: ', hg)
|
|
282
|
+
dP_hg = hg * statepoint.visc ** 2. * length / (statepoint.rho * d_h ** 3.)
|
|
283
|
+
if detailed:
|
|
284
|
+
print('\tPressure drop: ', dP_hg)
|
|
285
|
+
return dP_hg.to(units)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def flow_at_dp(statepoint, dP, roughness, L, d1, d2=None, v=None, shape='round', props='REFPROP', detailed=False):
|
|
289
|
+
from thermofluids.Base import Statepoint
|
|
290
|
+
sp = Statepoint(fluid=statepoint.fluid, h=statepoint.h, p=statepoint.p, m=0.1)
|
|
291
|
+
res = 1.
|
|
292
|
+
while res > 0.001:
|
|
293
|
+
dP_check = pressure_drop(sp, roughness, L, d1, d2, v, shape, props, units=dP.units, detailed=detailed)
|
|
294
|
+
sp.set_m(sp.m * (dP / dP_check)**0.5)
|
|
295
|
+
res = abs(dP_check - dP) / dP.magnitude
|
|
296
|
+
if detailed:
|
|
297
|
+
print(sp.m, dP, dP_check, res)
|
|
298
|
+
|
|
299
|
+
return sp.m
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
if __name__ == '__main__':
|
|
303
|
+
from thermofluids.Base import Statepoint
|
|
304
|
+
from thermofluids.Base import Quantity
|
|
305
|
+
|
|
306
|
+
s = Statepoint(fluid='air')
|
|
307
|
+
s.set_t(0.)
|
|
308
|
+
s.set_p(1.)
|
|
309
|
+
s.set_m(3.)
|
|
310
|
+
|
|
311
|
+
# print PressureDrop(statepoint=s, roughness=1.e-6, L=100., d1=0.1, d2=1., shape='rectangle')
|
|
312
|
+
d1 = Quantity(0.1, 'm')
|
|
313
|
+
d2 = Quantity(0.2, 'm')
|
|
314
|
+
# print pressure_drop_sudden_expansion(statepoint=s, Re=6000., d1=d1, d2=d2, ff=1.e-5)
|
|
315
|
+
|
|
316
|
+
s = Statepoint(fluid='water', t=10., p=1., m=0.116)
|
|
317
|
+
dP = Quantity(0.5, 'bar')
|
|
318
|
+
roughness = Quantity(1.e-4, 'm')
|
|
319
|
+
L = Quantity(1., 'm')
|
|
320
|
+
d = Quantity(0.25, 'in').to('m')
|
|
321
|
+
s.set_m(flow_at_dp(statepoint=s, dP=dP, roughness=roughness, L=L, d1=d, detailed=True))
|
|
322
|
+
print('Pressure drop check: ', pressure_drop(statepoint=s, roughness=roughness, L=L, d1=d))
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
from warnings import warn
|
|
2
|
+
|
|
3
|
+
from thermofluids import Quantity, Statepoint
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def Re(statepoint, d_h, d_2=None, shape='round', v=None):
|
|
7
|
+
assert type(statepoint) is Statepoint, 'Statepoint must be a statepoint instance'
|
|
8
|
+
assert type(d_h) is Quantity, 'Hydraulic diameter must a Quantity instance'
|
|
9
|
+
assert d_h.check('[length]'), f'Hydraulic diameter must have units of length but has units {d_h.units}'
|
|
10
|
+
if v == None:
|
|
11
|
+
if shape=='round' or shape=='circle':
|
|
12
|
+
v = statepoint.m / statepoint.rho / (3.14/4.*d_h**2.)
|
|
13
|
+
d_h_x = d_h
|
|
14
|
+
elif shape=='square':
|
|
15
|
+
v = statepoint.m / statepoint.rho / d_h**2.
|
|
16
|
+
d_h_x = d_h
|
|
17
|
+
elif shape=='rectangle':
|
|
18
|
+
v = statepoint.m / statepoint.rho / (d_h * d_2)
|
|
19
|
+
d_h_x = 2. * d_h * d_2 / (d_h + d_2)
|
|
20
|
+
elif shape=='annulus':
|
|
21
|
+
assert d_2 is not None, 'd_2 must be set for annulus'
|
|
22
|
+
assert d_h > d_2, 'd_h must be greater than d_2 for an annulus'
|
|
23
|
+
v = statepoint.m / statepoint.rho / (3.14 / 4. * (d_h**2. - d_2**2.))
|
|
24
|
+
d_h_x = d_h - d_2
|
|
25
|
+
else:
|
|
26
|
+
raise Exception("No velocity calc available for shape '%s'. Please provide a velocity as an input" % shape)
|
|
27
|
+
else:
|
|
28
|
+
d_h_x = d_h
|
|
29
|
+
|
|
30
|
+
rho = statepoint.rho
|
|
31
|
+
try:
|
|
32
|
+
mu = statepoint.visc
|
|
33
|
+
except ValueError as e:
|
|
34
|
+
newFluid = statepoint.fluid.split(':')[1].split('&')[0].split('[')[0]
|
|
35
|
+
warn('Using %s instead because ' % newFluid + e.message)
|
|
36
|
+
rho = statepoint.rho
|
|
37
|
+
if 0. <= statepoint.x <= 1.:
|
|
38
|
+
# if the fluid is saturated, use the same pressure and quality with just the first fluid, not a mixture
|
|
39
|
+
newSP = Statepoint(fluid=newFluid, p=statepoint.p, x=statepoint.x)
|
|
40
|
+
mu = newSP.visc
|
|
41
|
+
else:
|
|
42
|
+
# if the fluid is not saturated, set it to the same pressure and the same delta from the saturation temp
|
|
43
|
+
tSatOld = Statepoint(fluid=statepoint.fluid, p=statepoint.p, x=0.).t
|
|
44
|
+
tSatNew = Statepoint(fluid=newFluid, p=statepoint.p, x=0.).t
|
|
45
|
+
newSP = Statepoint(fluid=newFluid, p=statepoint.p, t=tSatNew + statepoint.t - tSatOld)
|
|
46
|
+
mu = newSP.visc
|
|
47
|
+
|
|
48
|
+
return rho * v * d_h_x / mu
|
|
49
|
+
|
|
50
|
+
def Re_ext(statepoint, vel, d1, d2=None, shape=None):
|
|
51
|
+
"""
|
|
52
|
+
Reynolds number for external flow
|
|
53
|
+
:param statepoint: statepoint of fluid flowing over object
|
|
54
|
+
:param vel: velocty m/s
|
|
55
|
+
:param d1: dimension 1 [m]
|
|
56
|
+
:param d2: dimension 2 [m]
|
|
57
|
+
:param shape: not implemented
|
|
58
|
+
:return:
|
|
59
|
+
"""
|
|
60
|
+
# TODO: expand to calculate d_h for different geometries rho = statepoint.rho
|
|
61
|
+
try:
|
|
62
|
+
mu = statepoint.visc
|
|
63
|
+
rho = statepoint.rho
|
|
64
|
+
except ValueError as e:
|
|
65
|
+
newFluid = statepoint.fluid.split(':')[1].split('&')[0].split('[')[0]
|
|
66
|
+
warn('Using %s instead because ' % newFluid + e.message)
|
|
67
|
+
rho = statepoint.rho
|
|
68
|
+
if 0. <= statepoint.x <= 1.:
|
|
69
|
+
# if the fluid is saturated, use the same pressure and quality with just the first fluid, not a mixture
|
|
70
|
+
newSP = Statepoint(fluid=newFluid, p=statepoint.p, x=statepoint.x)
|
|
71
|
+
mu = newSP.visc
|
|
72
|
+
else:
|
|
73
|
+
# if the fluid is not saturated, set it to the same pressure and the same delta from the saturation temp
|
|
74
|
+
tSatOld = Statepoint(fluid=statepoint.fluid, p=statepoint.p, x=0.).t
|
|
75
|
+
tSatNew = Statepoint(fluid=newFluid, p=statepoint.p, x=0.).t
|
|
76
|
+
newSP = Statepoint(fluid=newFluid, p=statepoint.p, t=tSatNew + statepoint.t - tSatOld)
|
|
77
|
+
mu = newSP.visc
|
|
78
|
+
|
|
79
|
+
return rho * vel * d1 / mu
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
if __name__ == "__main__":
|
|
83
|
+
|
|
84
|
+
s = Statepoint(fluid='water')
|
|
85
|
+
s.set_t(25.)
|
|
86
|
+
s.set_p(1.)
|
|
87
|
+
s.set_m(0.01)
|
|
88
|
+
print(Re(statepoint=s, d_h=Quantity(0.01, 'm')))
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
from thermofluids.Fluids.Drag import f_drag, settling_vel
|
|
2
|
+
from thermofluids.Fluids.Moody import frictionfactor
|
|
3
|
+
from thermofluids.Fluids.PressureDrop import pressure_drop, pressure_drop_fitting, pressure_drop_corrugated_plates, pressure_drop_packed_bed,pressure_drop_sudden_expansion,pressure_drop_sudden_contraction
|
|
4
|
+
from thermofluids.Fluids.Re import Re, Re_ext
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from thermofluids.Base import Quantity, Statepoint
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def Gr(statepoint, L, t_wall):
|
|
5
|
+
assert isinstance(statepoint, Statepoint)
|
|
6
|
+
assert isinstance(L, Quantity)
|
|
7
|
+
assert isinstance(t_wall, Quantity)
|
|
8
|
+
|
|
9
|
+
g = Quantity(9.81, 'm*s^-2')
|
|
10
|
+
gr = statepoint.rho ** 2. * g * statepoint.beta * L ** 3. * (t_wall - statepoint.t) / statepoint.visc ** 2.
|
|
11
|
+
|
|
12
|
+
return gr
|