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