Skip to content

Commit ad9db52

Browse files
committed
First proper commit for Orifice Sizing .ipynb notebook with interactive plots
1 parent 2244dba commit ad9db52

8 files changed

+1126
-1
lines changed

HEMCalcs.py

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
import CoolProp.CoolProp as CP
4+
from matplotlib.cm import ScalarMappable
5+
from matplotlib.colors import Normalize
6+
from mpl_toolkits.mplot3d import Axes3D
7+
8+
def A(d):
9+
return np.pi * (d / 2) ** 2
10+
11+
def mHEM(A, rho2, h1, h2, N, Cd=0.66):
12+
return N * A * Cd * rho2 * np.sqrt(2*(h1 - h2))
13+
14+
15+
T1 = 10 # Upstream temperature (C)
16+
P2 = 20e5 # Downstream pressure (Pa)
17+
N = 12 # Number of orrifaces
18+
19+
def HEM_CP(T1, P2, subst='NitrousOxide'):
20+
h1 = CP.PropsSI('H', 'T', T1 + 273.15, 'Q', 0, subst)
21+
s1 = CP.PropsSI('S', 'T', T1 + 273.15, 'Q', 0, subst)
22+
23+
# Find downstream enthalpy for liquid with upstream entropy and downstream pressure
24+
h2 = CP.PropsSI('H', 'P', P2, 'S', s1, subst)
25+
26+
rho2 = CP.PropsSI('D', 'P', P2, 'S', s1, subst)
27+
return h1, h2, rho2
28+
29+
def plotting(d, T, N):
30+
h1, h2, rho2 = HEM_CP(T, P2)
31+
32+
m_HEM_vectorized = np.vectorize(mHEM)
33+
return m_HEM_vectorized(A(d), rho2, h1, h2, N)
34+
35+
def HEMmassflowrate():
36+
temps = np.linspace(-10, 32, 500)
37+
d = np.linspace(0.1, 2.5, 100) / 1000
38+
39+
40+
plt.figure()
41+
# set colors to viridis colormap
42+
colormap = plt.cm.viridis
43+
color = iter(colormap(np.linspace(0, 1, len(temps))))
44+
45+
for T in temps:
46+
m = plotting(d, T, N)
47+
plt.plot(d * 1000, m, label=f'{T:.1f} C', color=next(color), linewidth=.3, alpha=1)
48+
49+
sm = ScalarMappable(cmap=colormap, norm=Normalize(vmin=min(temps), vmax=max(temps)))
50+
sm.set_array([])
51+
ax = plt.gca()
52+
cbar = plt.colorbar(sm, ax=ax)
53+
cbar.set_label('Upstream Temperature (C)')
54+
plt.xlabel('Diameter (mm)')
55+
plt.ylabel('Mass Flow Rate (kg/s)')
56+
plt.title('Mass Flow Rate vs Diameter vs Temperature')
57+
plt.show()
58+
59+
60+
def NormalisedHEM():
61+
# Plot Mass Flow Rate vs Upstream temperature for d =0.5mm and N = 1
62+
temps = np.linspace(-10, 70, 500)
63+
d = 1
64+
N = 1
65+
66+
m = plotting(d, temps, N) / 0.66
67+
plt.figure()
68+
plt.plot(temps, m/1e4)
69+
plt.xlabel('Upstream Temperature (C)')
70+
plt.ylabel(r'Normalised mass Flow Rate $\left( \frac{kg}{m^2} \right)$ x10$^{-4}$')
71+
plt.title('Mass Flow Rate vs Upstream Temperature')
72+
plt.tight_layout()
73+
plt.show()
74+
75+
# Plot Mass Flow Rate vs diameter(0 to 2) for ambient temperature of 20degC and N = 12
76+
temps = 20
77+
d = np.linspace(0.1, 2.5, 100) / 1000
78+
N = 12
79+
80+
m = plotting(d, temps, N)
81+
plt.figure()
82+
plt.plot(d * 1000, m)
83+
plt.xlabel('Diameter (mm)')
84+
plt.ylabel('Mass Flow Rate (kg/s)')
85+
plt.title('Mass Flow Rate vs Diameter')
86+
plt.tight_layout()
87+
plt.show()

NHNECalcs.py

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import numpy as np
2+
import matplotlib.pyplot as plt
3+
import CoolProp.CoolProp as CP
4+
import ipywidgets as widgets
5+
6+
from HEMCalcs import plotting, A
7+
from SPICalcs import CalcCPI, m_CPI
8+
9+
def NHNEPlot(T, N, kap):
10+
d = np.linspace(0.1, 6, 1000) / 1000
11+
Pc = 20e5
12+
13+
mNom = 1.36
14+
mHEM = plotting(d, T, N)
15+
mSPI = CalcCPI(T, 'NitrousOxide', Pc, N, d)[0]
16+
mkappa = (1 / (1 + kap)) * mSPI + (1 - (kap / (1 + kap))) * mHEM
17+
18+
plt.plot(d * 1000, mHEM, label=r'$\dot{m}_{HEM}$')
19+
plt.plot(d * 1000, mSPI, label=r'$\dot{m}_{SPI}$')
20+
plt.plot(d * 1000, mkappa, label=f'$\\dot{{m}}_{{\\kappa}}$, $\\kappa={kap}$')
21+
plt.axhline(mNom, color='r', linestyle='--', label=f'$\\dot{{m}}_{{Design}}={mNom}$ kg/s')
22+
23+
plt.fill_between(d * 1000, mHEM, mSPI, color='lightgrey', alpha=0.5)
24+
25+
idx_HEM = np.argmin(np.abs(mHEM - mNom))
26+
idx_SPI = np.argmin(np.abs(mSPI - mNom))
27+
idx_kappa = np.argmin(np.abs(mkappa - mNom))
28+
29+
d_HEM = d[idx_HEM] * 1000
30+
d_SPI = d[idx_SPI] * 1000
31+
d_kappa = d[idx_kappa] * 1000
32+
33+
plt.plot(d_HEM, mNom, color='tab:blue', marker='o', label=f'$d_{{HEM}}={d_HEM:.2f}$ mm')
34+
plt.plot(d_SPI, mNom, color='tab:orange', marker='o', label=f'$d_{{SPI}}={d_SPI:.2f}$ mm')
35+
plt.plot(d_kappa, mNom, color='tab:green', marker='o', label=f'$d_{{\\kappa}}={d_kappa:.2f}$ mm')
36+
plt.xlabel('Diameter (mm)')
37+
plt.ylabel('Mass Flow Rate (kg/s)')
38+
plt.title(f'Mass Flow Rate vs Diameter\n {N} Orifices at $T_{{tank}} =${T}°C')
39+
plt.xlim(0, 2.5)
40+
plt.ylim(0, 2)
41+
plt.tight_layout()
42+
plt.legend()
43+
44+
45+
# Create sliders for the temperature, the number of orifices and the kappa value
46+
def NHNESliders():
47+
T_slider = widgets.FloatSlider(
48+
value=30,
49+
min=-10,
50+
max=60,
51+
step=0.5,
52+
description=r'$T_{\text{tank}}$ (°C)',
53+
continuous_update=False
54+
)
55+
N_slider = widgets.IntSlider(
56+
value=12,
57+
min=1,
58+
max=50,
59+
step=1,
60+
description='N',
61+
continuous_update=False
62+
)
63+
kap_slider = widgets.FloatSlider(
64+
value=1.4,
65+
min=0,
66+
max=2,
67+
step=0.01,
68+
description=r'$\kappa$',
69+
continuous_update=False
70+
)
71+
return T_slider, N_slider, kap_slider
72+

NitrousDensityPlots.py

+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
import CoolProp.CoolProp as CP
2+
import matplotlib.pyplot as plt
3+
import numpy as np
4+
import ipywidgets as widgets
5+
from IPython.display import display
6+
from matplotlib.ticker import FuncFormatter
7+
8+
# Define the subst
9+
substance = 'NitrousOxide'
10+
11+
# Define temperature range (in Kelvin)
12+
T_min = CP.PropsSI(substance, 'Tmin') + 0.01 # slightly above the minimum temperature
13+
T_max = CP.PropsSI(substance, 'Tcrit') # critical temperature
14+
temperatures = np.linspace(T_min, T_max, int(1e3))
15+
16+
def get_saturations(T):
17+
liquid_density = []
18+
vapor_density = []
19+
saturation_pressures = []
20+
21+
# Calculate densities and pressures at each temperature
22+
for T in temperatures:
23+
ld = CP.PropsSI('D', 'T', T, 'Q', 0, substance) # liquid density
24+
vd = CP.PropsSI('D', 'T', T, 'Q', 1, substance) # vapor density
25+
psat = CP.PropsSI('P', 'T', T, 'Q', 0, substance) / 1e5 # saturation pressure
26+
27+
liquid_density.append(ld)
28+
vapor_density.append(vd)
29+
saturation_pressures.append(psat)
30+
31+
return liquid_density, vapor_density, saturation_pressures
32+
33+
def plot_curves(isoT=15, isoP=60):
34+
isoT += 273.15
35+
36+
# Create figure and axes
37+
fig, axs = plt.subplots(1, 2, figsize=(15, 6), sharey=True)
38+
39+
# Get saturation data
40+
liquid_density, vapor_density, saturation_pressures = get_saturations(temperatures)
41+
42+
"""Density vs Temperature"""
43+
# Plot saturation curves with Temperature on x-axis
44+
axs[0].plot(temperatures, liquid_density, label='Saturated Liquid Density')
45+
axs[0].plot(temperatures, vapor_density, label='Saturated Vapor Density')
46+
axs[0].fill_between(temperatures, liquid_density, vapor_density, color='gray', alpha=0.2)
47+
48+
# Add critical point
49+
T_critical = CP.PropsSI('Tcrit', substance)
50+
D_critical = CP.PropsSI('rhocrit', substance)
51+
# print(f'T_critical = {T_critical:.2f} K')
52+
# print(f'D_critical = {D_critical:.2f} kg/m^3')
53+
axs[0].plot(T_critical, D_critical, 'kx', label='Critical Point')
54+
55+
# Plot isobar
56+
isoP *= 1e5
57+
isoP_Ts = np.linspace(260,370, 500)
58+
try:
59+
rho = CP.PropsSI('D', 'P', isoP, 'T', isoP_Ts, substance)
60+
axs[0].plot(isoP_Ts, rho, label=f'Isobar at {(isoP / 1e5):.1f} bar')
61+
T_sat = CP.PropsSI('T', 'P', isoP, 'Q', 0, substance)
62+
axs[0].axvline(T_sat, color='gray', linestyle='--', label=f'$T_{{sat}}$ = {T_sat-273.15:.1f} °C')
63+
# Plot saturation points
64+
D_V = CP.PropsSI('D', 'P', isoP, 'Q', 1, substance)
65+
D_L = CP.PropsSI('D', 'P', isoP, 'Q', 0, substance)
66+
T_V = CP.PropsSI('T', 'P', isoP, 'Q', 1, substance)
67+
T_L = CP.PropsSI('T', 'P', isoP, 'Q', 0, substance)
68+
axs[0].plot(T_V, D_V, 'ro', label=f'$\\rho_{{V, sat}}$ = {D_V:.2f} kg/m$^3$')
69+
axs[0].plot(T_L, D_L, 'bo', label=f'$\\rho_{{L, sat}}$ = {D_L:.2f} kg/m$^3$')
70+
71+
# # Annotate the density of the saturated liquid and vapor use LaTeX formatting and put into boxes
72+
# axs[0].annotate(f'{D_L:.2f} kg/m$^3$', xy=(T_L, 900))
73+
# axs[0].annotate(f'{D_V:.2f} kg/m$^3$', xy=(T_V, 0))
74+
except:
75+
pass
76+
77+
# Label plot
78+
79+
axs[0].set_title('Density vs Temperature')
80+
81+
# Example values for the limits and increments in Celsius
82+
x_min_celsius = -10 # Minimum value in Celsius
83+
x_max_celsius = 60 # Maximum value in Celsius
84+
increments_celsius = 10 # Increment step in Celsius
85+
86+
# Convert these values to Kelvin for setting limits and ticks
87+
x_min_kelvin = x_min_celsius + 273.15
88+
x_max_kelvin = x_max_celsius + 273.15
89+
increments_kelvin = increments_celsius
90+
91+
# Set the limits for the x-axis
92+
axs[0].set_xlim(x_min_kelvin, x_max_kelvin)
93+
94+
# Set the ticks for the x-axis
95+
# np.arange creates an array from x_min_kelvin to x_max_kelvin with a step of increments_kelvin
96+
axs[0].set_xticks(np.arange(x_min_kelvin, x_max_kelvin + 1, increments_kelvin))
97+
98+
# Modify the x-axis to show temperature in degrees Celsius
99+
axs[0].xaxis.set_major_formatter(FuncFormatter(lambda val, pos: f'{(val - 273.15):.0f}'))
100+
101+
# Set the label for the x-axis
102+
axs[0].set_xlabel('Temperature (°C)')
103+
104+
105+
106+
axs[0].set_ylabel(f'Density kg/m$^3$')
107+
axs[0].legend()
108+
109+
110+
"""Density vs Pressure"""
111+
# Plot saturation curves with Pressure on x-axis
112+
axs[1].plot(saturation_pressures, liquid_density, label='Saturated Liquid Density')
113+
axs[1].plot(saturation_pressures, vapor_density, label='Saturated Vapor Density')
114+
axs[1].fill_between(saturation_pressures, liquid_density, vapor_density, color='gray', alpha=0.2)
115+
116+
P_critical = CP.PropsSI('Pcrit', substance) / 1e5
117+
# print(f'P_critical = {P_critical:.2f} bar')
118+
axs[1].plot(P_critical, D_critical, 'kx', label='Critical Point')
119+
120+
# Plot isotherm
121+
isoT_Ps = np.linspace(1e4, 90e5, 500)
122+
try:
123+
rho = CP.PropsSI('D', 'T', isoT, 'P', isoT_Ps, substance)
124+
isoT_Ps_bar = [pressure / 1e5 for pressure in isoT_Ps] # Convert to bar
125+
axs[1].plot(isoT_Ps_bar, rho, label=f'Isotherm at {isoT-273.15:.1f} °C')
126+
P_sat = CP.PropsSI('P', 'T', isoT, 'Q', 0, substance)
127+
axs[1].axvline(P_sat / 1e5, color='gray', linestyle='--', label=f'$P_{{sat}}$ = {P_sat / 1e5:.2f} bar')
128+
129+
# Plot saturation points
130+
D_V = CP.PropsSI('D', 'T', isoT, 'Q', 1, substance)
131+
D_L = CP.PropsSI('D', 'T', isoT, 'Q', 0, substance)
132+
P_V = CP.PropsSI('P', 'T', isoT, 'Q', 1, substance) / 1e5
133+
P_L = CP.PropsSI('P', 'T', isoT, 'Q', 0, substance) / 1e5
134+
axs[1].plot(P_V, D_V, 'ro', label=f'$\\rho_{{V, sat}}$ = {D_V:.2f} kg/m$^3$')
135+
axs[1].plot(P_L, D_L, 'bo', label=f'$\\rho_{{L, sat}}$ = {D_L:.2f} kg/m$^3$')
136+
137+
# # Annotate the density of the saturated liquid and vapor use LaTeX formatting and put into boxes
138+
# axs[1].annotate(f'{D_L:.2f} kg/m$^3$', xy=(P_L+2, D_L+50))
139+
# axs[1].annotate(f'{D_V:.2f} kg/m$^3$', xy=(P_V + 3, 0))
140+
except:
141+
pass
142+
143+
axs[1].set_title('Density vs Pressure')
144+
axs[1].set_xlabel('Pressure (bar)')
145+
axs[1].set_xlim(0, 90)
146+
axs[1].legend(loc = 'upper right')
147+
148+
# Display plot
149+
plt.suptitle(f'$N_{2}O$ Density Curves', fontsize=20)
150+
plt.tight_layout()
151+
plt.show()
152+
153+
def density_sliders():
154+
isoT_slider = widgets.FloatSlider(
155+
value=15,
156+
min=-10,
157+
max=60,
158+
step=0.5,
159+
description= r'$T_{\text{const}}$ (°C)',
160+
continuous_update=False
161+
)
162+
163+
isoP_slider = widgets.FloatSlider(
164+
value=40,
165+
min=1,
166+
max=90,
167+
step=.5,
168+
description= r'$P_{\text{const}}$ (bar)',
169+
continuous_update=False
170+
)
171+
return isoT_slider, isoP_slider
172+
173+
if __name__ == '__main__':
174+
plot_curves()

0 commit comments

Comments
 (0)