turbo-design 1.0.0__py2.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.
- turbo_design-1.0.0.dist-info/METADATA +25 -0
- turbo_design-1.0.0.dist-info/RECORD +33 -0
- turbo_design-1.0.0.dist-info/WHEEL +4 -0
- turbodesign/__init__.py +9 -0
- turbodesign/arrayfuncs.py +19 -0
- turbodesign/bladerow.py +614 -0
- turbodesign/cantera_gas/co2.yaml +36 -0
- turbodesign/compressorspool.py +60 -0
- turbodesign/coolant.py +10 -0
- turbodesign/enums.py +36 -0
- turbodesign/inlet.py +146 -0
- turbodesign/isentropic.py +82 -0
- turbodesign/loss/__init__.py +1 -0
- turbodesign/loss/compressor/__init__.py +1 -0
- turbodesign/loss/losstype.py +25 -0
- turbodesign/loss/turbine/TD2.py +142 -0
- turbodesign/loss/turbine/__init__.py +8 -0
- turbodesign/loss/turbine/ainleymathieson.py +118 -0
- turbodesign/loss/turbine/craigcox.py +189 -0
- turbodesign/loss/turbine/fixedefficiency.py +29 -0
- turbodesign/loss/turbine/fixedpressureloss.py +25 -0
- turbodesign/loss/turbine/kackerokapuu.py +124 -0
- turbodesign/loss/turbine/traupel.py +95 -0
- turbodesign/lossinterp.py +178 -0
- turbodesign/outlet.py +56 -0
- turbodesign/passage.py +198 -0
- turbodesign/radeq.py +255 -0
- turbodesign/rotor.py +38 -0
- turbodesign/solve_radeq.py +37 -0
- turbodesign/spool.py +289 -0
- turbodesign/stage.py +7 -0
- turbodesign/td_math.py +388 -0
- turbodesign/turbinespool.py +466 -0
turbodesign/spool.py
ADDED
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
# type: ignore[arg-type, reportUnknownArgumentType]
|
|
2
|
+
from dataclasses import field
|
|
3
|
+
import json
|
|
4
|
+
from typing import Dict, List, Union
|
|
5
|
+
import matplotlib.pyplot as plt
|
|
6
|
+
from .bladerow import BladeRow
|
|
7
|
+
import numpy as np
|
|
8
|
+
import numpy.typing as npt
|
|
9
|
+
from .enums import RowType, MassflowConstraint
|
|
10
|
+
from cantera.composite import Solution
|
|
11
|
+
from .loss.turbine import TD2
|
|
12
|
+
from pyturbo.helper import line2D
|
|
13
|
+
from scipy.interpolate import interp1d
|
|
14
|
+
from .passage import Passage
|
|
15
|
+
from .inlet import Inlet
|
|
16
|
+
from .outlet import Outlet
|
|
17
|
+
|
|
18
|
+
class Spool:
|
|
19
|
+
blade_rows: List[BladeRow] = []
|
|
20
|
+
massflow:float=0
|
|
21
|
+
rpm:float = 0
|
|
22
|
+
|
|
23
|
+
passage:Passage
|
|
24
|
+
t_streamline: npt.NDArray = field(default_factory=lambda: np.zeros((10,)))
|
|
25
|
+
num_streamlines:int=0
|
|
26
|
+
|
|
27
|
+
# Fluid entering the spool
|
|
28
|
+
_fluid: Solution = Solution("air.yaml")
|
|
29
|
+
|
|
30
|
+
massflow_constraint: MassflowConstraint
|
|
31
|
+
|
|
32
|
+
# Inlet Conditions
|
|
33
|
+
def __init__(self,passage:Passage,
|
|
34
|
+
massflow:float,rows=List[BladeRow],
|
|
35
|
+
num_streamlines:int=3,
|
|
36
|
+
fluid:Solution=Solution('air.yaml'),
|
|
37
|
+
rpm:float=-1,
|
|
38
|
+
massflow_constraint:MassflowConstraint=MassflowConstraint.MatchMassFlow):
|
|
39
|
+
"""Initializes a Spool
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
Note:
|
|
43
|
+
When it comes to massflow, the exit angle of the blade rows along with the upstream total pressure is used to set the massflow through the spool. If turning is too high then massflow is limited and cannot.
|
|
44
|
+
This code gives you the option of setting a massflow and varying the exit angles to match that particular massflow. (MatchMassflow)
|
|
45
|
+
Or keeping the exit angle and modifying the inlet condition to set a specific massflow through the stage. (BalanceMassflow)
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
passage (Passage): Passage defining hub and shroud
|
|
49
|
+
massflow (float): massflow at spool inlet
|
|
50
|
+
rows (List[BladeRow], optional): List of blade rows. Defaults to List[BladeRow].
|
|
51
|
+
num_streamlines (int, optional): number of streamlines. Defaults to 3.
|
|
52
|
+
gas (ct.Solution, optional): cantera gas solution. Defaults to ct.Solution('air.yaml').
|
|
53
|
+
rpm (float, optional): RPM for the entire spool Optional, you can also set rpm of the blade rows individually. Defaults to -1.
|
|
54
|
+
massflow_constraint (MassflowConstraint, optional): MatchMassflow - Matches the massflow defined in the spool. BalanceMassflow - Balances the massflow between BladeRows, matches the lowest massflow.
|
|
55
|
+
"""
|
|
56
|
+
self.passage = passage
|
|
57
|
+
self.fluid = fluid
|
|
58
|
+
self.massflow = massflow
|
|
59
|
+
self.num_streamlines = num_streamlines
|
|
60
|
+
self.blade_rows = rows
|
|
61
|
+
self.massflow_constraint = massflow_constraint
|
|
62
|
+
|
|
63
|
+
self.rpm = rpm
|
|
64
|
+
|
|
65
|
+
for i in range(len(self.blade_rows)):
|
|
66
|
+
'''Defining RPM in the bladerows
|
|
67
|
+
There's 2 ways of setting the RPM.
|
|
68
|
+
1. Conventional: Stator-Rotor-Stator-Rotor-etc. Set the RPM equally across all
|
|
69
|
+
2. Counter Rotation: We either use the RPM already persecribed for each blade row.
|
|
70
|
+
'''
|
|
71
|
+
if (type(self.blade_rows[i]) != Inlet) and (type(self.blade_rows[i]) != Outlet):
|
|
72
|
+
self.blade_rows[i].fluid = self.fluid
|
|
73
|
+
self.blade_rows[i].rpm = rpm
|
|
74
|
+
self.blade_rows[i].axial_chord = self.blade_rows[i].axial_location * self.passage.hub_length
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def fluid(self):
|
|
79
|
+
return self._fluid
|
|
80
|
+
|
|
81
|
+
@fluid.setter
|
|
82
|
+
def fluid(self,newFluid:Solution):
|
|
83
|
+
"""Change the type of gas used in the spool
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
new_gas (ct.Solution.Solution): New gas mixture
|
|
87
|
+
"""
|
|
88
|
+
self._fluid = newFluid
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def set_blade_row_rpm(self,index:int,rpm:float):
|
|
92
|
+
"""sets the rpm of a particular blade row"""
|
|
93
|
+
self.blade_rows[index].rpm = rpm
|
|
94
|
+
|
|
95
|
+
def set_blade_row_type(self,blade_row_index:int,rowType:RowType):
|
|
96
|
+
"""Sets a blade row to be a different type.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
blade_row_index (int): Index of blade row. Keep in mind Blade row 0 is inlet, -1 = outlet
|
|
100
|
+
"""
|
|
101
|
+
self.blade_rows[blade_row_index].row_type = rowType
|
|
102
|
+
|
|
103
|
+
def set_blade_row_exit_angles(self,radius:Dict[int,List[float]],beta:Dict[int,List[float]],IsSupersonic:bool=False):
|
|
104
|
+
"""Set the intended exit flow angles for the spool.
|
|
105
|
+
|
|
106
|
+
Useful if you already have a geometry that you want to simulate
|
|
107
|
+
|
|
108
|
+
You can set the values for each blade row or simply the inlet and exit
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
radius (np.ndarray): Matrix containing [[r1,r2,r3],[r1,r2,r3]] blade_rows x radial locations
|
|
112
|
+
beta (Dict[int,List[float]]): Metal Angle of the geometry, it is assumed that the fluid will leave at this angle.
|
|
113
|
+
IsSupersonic(bool): if solution is supersonic at spool exit
|
|
114
|
+
|
|
115
|
+
Example:
|
|
116
|
+
# Station 0 has angles of [73.1,74.2,75.4]
|
|
117
|
+
# Station 5 has angles of [69,69,69]
|
|
118
|
+
beta = {0: [73.1,74.2,75.4],
|
|
119
|
+
5: [69,69,69]}
|
|
120
|
+
|
|
121
|
+
"""
|
|
122
|
+
for k,v in radius.items():
|
|
123
|
+
self.blade_rows[k].radii_geom = v
|
|
124
|
+
for k,v in beta.items():
|
|
125
|
+
self.blade_rows[k].beta_geom = v
|
|
126
|
+
self.blade_rows[k].beta_fixed = True
|
|
127
|
+
for br in self.blade_rows:
|
|
128
|
+
if not IsSupersonic:
|
|
129
|
+
br.solution_type = SolutionType.subsonic
|
|
130
|
+
else:
|
|
131
|
+
br.solution_type = SolutionType.supersonic
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def initialize_streamlines(self):
|
|
135
|
+
"""Initialize streamline and compute the curvature.
|
|
136
|
+
This function is called once
|
|
137
|
+
"""
|
|
138
|
+
|
|
139
|
+
for row in self.blade_rows:
|
|
140
|
+
row.phi = np.zeros((self.num_streamlines,))
|
|
141
|
+
row.rm = np.zeros((self.num_streamlines,))
|
|
142
|
+
row.r = np.zeros((self.num_streamlines,))
|
|
143
|
+
|
|
144
|
+
t_radial = np.linspace(0,1,self.num_streamlines)
|
|
145
|
+
self.calculate_streamline_curvature(row,t_radial)
|
|
146
|
+
|
|
147
|
+
# Set the loss function if it's not set
|
|
148
|
+
if (row.loss_function == None):
|
|
149
|
+
row.loss_function = TD2()
|
|
150
|
+
|
|
151
|
+
def calculate_streamline_curvature(self,row:BladeRow,t_radial:Union[List[float],npt.NDArray]):
|
|
152
|
+
"""Called to calculate new streamline curvature
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
row (BladeRow): blade row
|
|
156
|
+
t_radial (Union[List[float],npt.NDArray]): normalized streamline radial locations
|
|
157
|
+
"""
|
|
158
|
+
for i,tr in enumerate(t_radial):
|
|
159
|
+
t_streamline, x_streamline, r_streamline = self.passage.get_streamline(tr)
|
|
160
|
+
phi, rm, r = self.passage.streamline_curvature(x_streamline,r_streamline)
|
|
161
|
+
row.phi[i] = float(interp1d(t_streamline,phi)(row.axial_location))
|
|
162
|
+
row.rm[i] = float(interp1d(t_streamline,rm)(row.axial_location))
|
|
163
|
+
row.r[i] = float(interp1d(t_streamline,r)(row.axial_location))
|
|
164
|
+
|
|
165
|
+
def solve(self):
|
|
166
|
+
raise NotImplementedError('Solve is not implemented')
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def plot(self):
|
|
170
|
+
"""Plots the hub, shroud, and all the streamlines
|
|
171
|
+
"""
|
|
172
|
+
plt.figure(num=1,clear=True,dpi=150,figsize=(15,10))
|
|
173
|
+
plt.plot(self.passage.xhub_pts,self.passage.rhub_pts,label='hub',linestyle='solid',linewidth=2,color='black')
|
|
174
|
+
plt.plot(self.passage.xshroud_pts,self.passage.rshroud_pts,label='shroud',linestyle='solid',linewidth=2,color='black')
|
|
175
|
+
|
|
176
|
+
hub_length = np.sum(np.sqrt(np.diff(self.passage.xhub_pts)**2 + np.diff(self.passage.rhub_pts)**2))
|
|
177
|
+
# Populate the streamlines
|
|
178
|
+
x_streamline = np.zeros((self.num_streamlines,len(self.blade_rows)))
|
|
179
|
+
r_streamline = np.zeros((self.num_streamlines,len(self.blade_rows)))
|
|
180
|
+
for i in range(len(self.blade_rows)):
|
|
181
|
+
x_streamline[:,i] = self.blade_rows[i].x
|
|
182
|
+
r_streamline[:,i] = self.blade_rows[i].r
|
|
183
|
+
|
|
184
|
+
for i in range(1,len(self.blade_rows)-1): # plot dashed lines for each steamline
|
|
185
|
+
plt.plot(x_streamline[:,i],r_streamline[:,i],'--b',linewidth=1.5)
|
|
186
|
+
|
|
187
|
+
for i in range(len(self.blade_rows)):
|
|
188
|
+
|
|
189
|
+
row = self.blade_rows[i]
|
|
190
|
+
plt.plot(row.x,row.r,linestyle='dashed',linewidth=1.5,color='blue',alpha=0.4)
|
|
191
|
+
plt.plot(x_streamline[:,i],r_streamline[:,i],'or')
|
|
192
|
+
|
|
193
|
+
if i == 0:
|
|
194
|
+
pass # Inlet
|
|
195
|
+
else: # i>0
|
|
196
|
+
upstream = self.blade_rows[i-1]
|
|
197
|
+
if upstream.row_type== RowType.Inlet:
|
|
198
|
+
cut_line1,_,_ = self.passage.get_cutting_line((row.axial_location*hub_length +(0.5*row.blade_to_blade_gap*row.axial_chord) - row.axial_chord)/hub_length)
|
|
199
|
+
else:
|
|
200
|
+
cut_line1,_,_ = self.passage.get_cutting_line((upstream.axial_location*hub_length)/hub_length)
|
|
201
|
+
cut_line2,_,_ = self.passage.get_cutting_line((row.axial_location*hub_length-(0.5*row.blade_to_blade_gap*row.axial_chord))/hub_length)
|
|
202
|
+
|
|
203
|
+
if self.blade_rows[i].row_type == RowType.Stator:
|
|
204
|
+
x1,r1 = cut_line1.get_point(np.linspace(0,1,10))
|
|
205
|
+
plt.plot(x1,r1,'m')
|
|
206
|
+
x2,r2 = cut_line2.get_point(np.linspace(0,1,10))
|
|
207
|
+
plt.plot(x2,r2,'m')
|
|
208
|
+
x_text = (x1+x2)/2; r_text = (r1+r2)/2
|
|
209
|
+
plt.text(x_text.mean(),r_text.mean(),"Stator",fontdict={"fontsize":"xx-large"})
|
|
210
|
+
elif self.blade_rows[i].row_type == RowType.Rotor:
|
|
211
|
+
x1,r1 = cut_line1.get_point(np.linspace(0,1,10))
|
|
212
|
+
plt.plot(x1,r1,color='brown')
|
|
213
|
+
x2,r2 = cut_line2.get_point(np.linspace(0,1,10))
|
|
214
|
+
plt.plot(x2,r2,color='brown')
|
|
215
|
+
x_text = (x1+x2)/2; r_text = (r1+r2)/2
|
|
216
|
+
plt.text(x_text.mean(),r_text.mean(),"Rotor",fontdict={"fontsize":"xx-large"})
|
|
217
|
+
plt.axis('scaled')
|
|
218
|
+
plt.savefig(f"Meridional.png",transparent=False,dpi=150)
|
|
219
|
+
plt.show()
|
|
220
|
+
|
|
221
|
+
def plot_velocity_triangles(self):
|
|
222
|
+
"""Plots the velocity triangles for each blade row
|
|
223
|
+
Made for turbines
|
|
224
|
+
"""
|
|
225
|
+
prop = dict(arrowstyle="-|>,head_width=0.4,head_length=0.8",
|
|
226
|
+
shrinkA=0,shrinkB=0)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
for j in range(self.num_streamlines):
|
|
230
|
+
x_start = 0
|
|
231
|
+
y_max = 0; y_min = 0
|
|
232
|
+
plt.figure(num=1,clear=True)
|
|
233
|
+
for i in range(1,len(self.blade_rows)):
|
|
234
|
+
row = self.blade_rows[i]
|
|
235
|
+
x_end = x_start + row.Vx.mean()
|
|
236
|
+
dx = x_end - x_start
|
|
237
|
+
|
|
238
|
+
Vt = row.Vt[j]
|
|
239
|
+
Wt = row.Wt[j]
|
|
240
|
+
U = row.U[j]
|
|
241
|
+
|
|
242
|
+
y_max = (Vt if Vt>y_max else y_max)
|
|
243
|
+
y_max = (Wt if Wt>y_max else y_max)
|
|
244
|
+
|
|
245
|
+
y_min = (Vt if Vt<y_min else y_min)
|
|
246
|
+
y_min = (Wt if Wt<y_min else y_min)
|
|
247
|
+
|
|
248
|
+
# V
|
|
249
|
+
plt.annotate("", xy=(x_end,Vt), xytext=(x_start,0), arrowprops=prop)
|
|
250
|
+
plt.text((x_start+x_end)/2,Vt/2*1.1,"V",fontdict={"fontsize":"xx-large"})
|
|
251
|
+
|
|
252
|
+
# W
|
|
253
|
+
plt.annotate("", xy=(x_end,Wt), xytext=(x_start,0), arrowprops=prop)
|
|
254
|
+
plt.text((x_start+x_end)/2,Wt/2*1.1,"W",fontdict={"fontsize":"xx-large"})
|
|
255
|
+
|
|
256
|
+
if (abs(row.Vt[j]) > abs(row.Wt[j])):
|
|
257
|
+
# Shift Vt to right just a bit so we can see it
|
|
258
|
+
plt.annotate("", xy=(x_end,Wt), xytext=(x_end,0), arrowprops=prop) # Wt
|
|
259
|
+
plt.text(x_end+dx*0.1,Wt/2,"Wt",fontdict={"fontsize":"xx-large"})
|
|
260
|
+
|
|
261
|
+
plt.annotate("", xy=(x_end,U+Wt), xytext=(x_end,Wt), arrowprops=prop) # U
|
|
262
|
+
plt.text(x_end+dx*0.1,(Wt+U)/2,"U",fontdict={"fontsize":"xx-large"})
|
|
263
|
+
else:
|
|
264
|
+
# Shift Wt to right just a bit so we can see it
|
|
265
|
+
plt.annotate("", xy=(x_end,Vt), xytext=(x_end,0), arrowprops=prop) # Vt
|
|
266
|
+
plt.text(x_end+dx*0.1,Vt/2,"Vt",fontdict={"fontsize":"xx-large"})
|
|
267
|
+
|
|
268
|
+
plt.annotate("", xy=(x_end,Wt+U), xytext=(x_end,Wt), arrowprops=prop) # U
|
|
269
|
+
plt.text(x_end+dx*0.1,Wt+U/2,"U",fontdict={"fontsize":"xx-large"})
|
|
270
|
+
|
|
271
|
+
plt.text((x_start+x_end)/2,-y_max*0.95,row.row_type.name,fontdict={"fontsize":"xx-large"})
|
|
272
|
+
x_start += row.Vx[j]
|
|
273
|
+
plt.axis([0,x_end+dx, y_min, y_max])
|
|
274
|
+
plt.title(f"Velocity Triangles for Streamline {j}")
|
|
275
|
+
plt.savefig(f"streamline_{j:04d}.png",transparent=False,dpi=150)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def JSON_to_Spool(filename:str="spool.json"):
|
|
282
|
+
"""Reads a JSON file with the properties
|
|
283
|
+
#! Need to instantiate and return spool. Fluid isn't loaded fyi
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
filename (str, optional): json filename with properties . Defaults to "spool.json".
|
|
287
|
+
"""
|
|
288
|
+
with open(filename, "r") as f:
|
|
289
|
+
data = json.load(f)
|
turbodesign/stage.py
ADDED
turbodesign/td_math.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
from typing import List, Tuple
|
|
2
|
+
import numpy as np
|
|
3
|
+
import math
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
from .bladerow import BladeRow, compute_gas_constants
|
|
6
|
+
from .enums import RowType, LossType
|
|
7
|
+
|
|
8
|
+
def T0_coolant_weighted_average(row:BladeRow) -> float:
|
|
9
|
+
"""Calculate the new weighted Total Temperature array considering coolant
|
|
10
|
+
|
|
11
|
+
Args:
|
|
12
|
+
coolant (Coolant): Coolant
|
|
13
|
+
massflow (np.ndarray): massflow mainstream
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
float: Total Temperature drop
|
|
17
|
+
"""
|
|
18
|
+
row.coolant.fluid.TP = row.coolant.T0, row.coolant.P0
|
|
19
|
+
massflow = row.massflow
|
|
20
|
+
total_massflow_no_coolant = row.total_massflow_no_coolant
|
|
21
|
+
Cp = row.Cp
|
|
22
|
+
Cpc = row.coolant.fluid.cp
|
|
23
|
+
T0c = row.coolant.T0
|
|
24
|
+
massflow_coolant = row.coolant.massflow_percentage*total_massflow_no_coolant*row.massflow[1:]/row.massflow[-1]
|
|
25
|
+
if massflow_coolant.mean()>0:
|
|
26
|
+
if row.row_type == RowType.Stator:
|
|
27
|
+
T0= row.T0
|
|
28
|
+
dT0 = T0.copy() * 0
|
|
29
|
+
T0_new = (massflow[1:]*Cp*T0[1:] + massflow_coolant*Cpc*T0c) \
|
|
30
|
+
/(massflow[1:]*Cp + massflow_coolant*Cpc)
|
|
31
|
+
dT0[1:] = T0_new - row.T0[1:]
|
|
32
|
+
dT0[0] = dT0[1]
|
|
33
|
+
else:
|
|
34
|
+
T0R = row.T0R
|
|
35
|
+
T0R_new = T0R.copy()
|
|
36
|
+
T0R_new[1:] = (massflow[1:]*Cp*T0R[1:] + massflow_coolant*Cpc*T0c) \
|
|
37
|
+
/(massflow[1:]*row.fluid.cp + massflow_coolant*Cpc)
|
|
38
|
+
T0R_new[0] = T0R_new[1]
|
|
39
|
+
|
|
40
|
+
T = T0R_new - row.W**2/(2*Cp) # Dont change the velocity triangle but adjust the static temperature
|
|
41
|
+
T0_new = T+row.V**2/(2*Cp) # Use new static temperature to calculate the total temperature
|
|
42
|
+
dT0 = T0_new - row.T0
|
|
43
|
+
return dT0
|
|
44
|
+
else:
|
|
45
|
+
return row.T0*0
|
|
46
|
+
|
|
47
|
+
def compute_massflow(row:BladeRow) -> None:
|
|
48
|
+
"""Populates row.massflow and row.calculated_massflow
|
|
49
|
+
|
|
50
|
+
Calculated_massflow is massflow[-1]
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
row (BladeRow): current blade row. All quantities are at exit
|
|
54
|
+
upstream (BladeRow): upstream blade row. All quantities are at exit
|
|
55
|
+
"""
|
|
56
|
+
massflow_fraction = np.linspace(0,1,len(row.percent_hub_shroud))
|
|
57
|
+
massflow = row.percent_hub_shroud*0
|
|
58
|
+
total_area = 0
|
|
59
|
+
for j in range(1,len(row.percent_hub_shroud)):
|
|
60
|
+
Vm = row.Vm[j]
|
|
61
|
+
rho = row.rho[j]
|
|
62
|
+
if np.abs((row.x[j]-row.x[j-1]))<1E-12: # Axial Machines
|
|
63
|
+
total_area += np.pi*(row.r[j]**2-row.r[j-1]**2)
|
|
64
|
+
massflow[j] = Vm * rho * np.pi* (row.r[j]**2-row.r[j-1]**2) + massflow[j-1]
|
|
65
|
+
else: # Radial Machines
|
|
66
|
+
dx = row.x[j]-row.x[j-1]
|
|
67
|
+
S = (row.r[j]-row.r[j-1])
|
|
68
|
+
C = np.sqrt(1+((row.r[j]-row.r[j-1])/dx)**2)
|
|
69
|
+
area = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
|
|
70
|
+
total_area += area
|
|
71
|
+
massflow[j] = Vm * rho *area + massflow[j-1]
|
|
72
|
+
|
|
73
|
+
row.total_massflow_no_coolant = massflow[-1]
|
|
74
|
+
if row.coolant != None:
|
|
75
|
+
massflow += massflow_fraction*row.coolant.massflow_percentage*row.total_massflow_no_coolant # Take into account the coolant massflow
|
|
76
|
+
row.massflow = massflow
|
|
77
|
+
row.calculated_massflow = massflow[-1]
|
|
78
|
+
row.area = total_area
|
|
79
|
+
|
|
80
|
+
def compute_power(row:BladeRow,upstream:BladeRow) -> None:
|
|
81
|
+
"""Calculates the power
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
row (BladeRow): _description_
|
|
85
|
+
upstream (BladeRow): _description_
|
|
86
|
+
"""
|
|
87
|
+
if row.row_type == RowType.Stator:
|
|
88
|
+
row.power = 0
|
|
89
|
+
row.eta_static = 0
|
|
90
|
+
row.eta_total = 0
|
|
91
|
+
row.stage_loading = 0
|
|
92
|
+
row.euler_power = 0
|
|
93
|
+
row.T_is = 0
|
|
94
|
+
row.T0_is = 0
|
|
95
|
+
else:
|
|
96
|
+
row.T_is = row.T0R*(row.P/upstream.P0R)**((row.gamma-1)/row.gamma)
|
|
97
|
+
row.T0_is = row.T_is*(1+(row.gamma-1)/2*row.M**2)
|
|
98
|
+
row.power = row.massflow[-1] * row.Cp * (upstream.T0.mean() - row.T0.mean())
|
|
99
|
+
row.eta_static = row.power/ (row.massflow[-1]*row.Cp*(upstream.T0.mean()-row.T0_is.mean()))
|
|
100
|
+
row.eta_total = row.power / (row.massflow[-1]*row.Cp * (upstream.T0.mean()-row.T0_is.mean()))
|
|
101
|
+
row.stage_loading = row.Cp*(upstream.T0.mean() - row.T0.mean())/row.U.mean()**2
|
|
102
|
+
row.euler_power = (row.massflow[-1]*((row.U+upstream.U)/2).mean()*(upstream.Vt-row.Vt).mean())
|
|
103
|
+
|
|
104
|
+
def compute_quantities(row:BladeRow,upstream:BladeRow):
|
|
105
|
+
"""Calculation of all quantites after radial equilibrium has been solved assuming we know the static pressure at the exit.
|
|
106
|
+
|
|
107
|
+
Note:
|
|
108
|
+
Radial Equilibrium gives P0, T0, Vm. This code assumes the loss either enthalpy or pressure loss has already been calculated
|
|
109
|
+
|
|
110
|
+
compute_velocity has been called so we know W, Wt, V, Vt, U, M, M_rel
|
|
111
|
+
|
|
112
|
+
Static Pressure and Temperature should come from Total Temperature and Pressure + Velocity
|
|
113
|
+
Args:
|
|
114
|
+
row (BladeRow): current blade row. All quantities are at exit
|
|
115
|
+
upstream (BladeRow): upstream blade row. All quantities are at exit
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
if row.row_type == RowType.Rotor:
|
|
119
|
+
Cp_avg = (row.Cp+upstream.Cp)/2
|
|
120
|
+
# Factor any coolant added and changes in streamline radius
|
|
121
|
+
row.T0R = upstream.T0R - T0_coolant_weighted_average(row) - (upstream.U**2-row.U**2)/(2*Cp_avg)
|
|
122
|
+
row.P = upstream.P0_stator_inlet/row.P0_P
|
|
123
|
+
|
|
124
|
+
if row.loss_function.loss_type == LossType.Pressure:
|
|
125
|
+
# This affects the velocity triangle
|
|
126
|
+
row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
|
|
127
|
+
row.T = (row.P/row.P0R)**((row.gamma-1)/row.gamma) * row.T0R
|
|
128
|
+
row.T0 = (1+(row.gamma-1)/2 * row.M**2) * row.T
|
|
129
|
+
row.power_distribution = row.massflow * row.Cp * (upstream.T0 - row.T0)
|
|
130
|
+
row.power = np.trapz(row.power_distribution,row.r-row.r[0])
|
|
131
|
+
row.power_mean = row.massflow[-1] * row.Cp * (upstream.T0.mean()-row.T0.mean())
|
|
132
|
+
|
|
133
|
+
elif row.loss_function.loss_type == LossType.Enthalpy:
|
|
134
|
+
' For Enthalpy related loss, assume the static quantities do not change '
|
|
135
|
+
row.T = (row.P/row.P0R)**((row.gamma-1)/row.gamma) * row.T0R
|
|
136
|
+
row.T0 = (1+(row.gamma-1)/2 * row.M**2) * row.T
|
|
137
|
+
|
|
138
|
+
def calculate_power(T0:npt.NDArray):
|
|
139
|
+
row.power_distribution = row.massflow * row.Cp * (upstream.T0 - T0)
|
|
140
|
+
row.power = np.trapz(row.power_distribution,row.r-row.r[0])
|
|
141
|
+
row.power_mean = row.massflow[-1] * row.Cp * (upstream.T0.mean() - T0.mean())
|
|
142
|
+
|
|
143
|
+
# Factor in T0R_drop. Convert T0R drop to absolute terms
|
|
144
|
+
T_drop = (upstream.T0R - row.T0R) - row.W**2/(2*row.Cp) # row.T0R contains the drop
|
|
145
|
+
T0_drop = T_drop*(1+(row.gamma-1)/2*row.M**2)
|
|
146
|
+
|
|
147
|
+
T0 = upstream.T0 - row.power/row.eta_total/(row.total_massflow*row.Cp) + T0_drop
|
|
148
|
+
return T0
|
|
149
|
+
|
|
150
|
+
T0 = row.T0.copy()
|
|
151
|
+
for _ in range(5): # interate on the convergence of T0_drop
|
|
152
|
+
T0 = calculate_power(T0)
|
|
153
|
+
|
|
154
|
+
row.T0 = T0
|
|
155
|
+
row.T = row.T0-row.W**2/(2*row.Cp)
|
|
156
|
+
row.P0R = row.P * (row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
157
|
+
row.P0 = row.P * (row.T0/row.T)**((row.gamma)/(row.gamma-1))
|
|
158
|
+
|
|
159
|
+
elif row.row_type == RowType.Stator:
|
|
160
|
+
' For the stator we already assume the upstream P0 already applied '
|
|
161
|
+
if row.loss_function == LossType.Pressure:
|
|
162
|
+
row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
|
|
163
|
+
else:
|
|
164
|
+
row.P0 = upstream.P0
|
|
165
|
+
row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
|
|
166
|
+
row.T = row.T0 * (1+(row.gamma-1)/2*row.M**2)
|
|
167
|
+
row.P = row.P0 * (row.T/row.T0)**((row.gamma)/(row.gamma-1))
|
|
168
|
+
row.T0R = row.T + row.W**2 / (2*row.Cp)
|
|
169
|
+
row.P0R = row.P*(row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
170
|
+
|
|
171
|
+
def compute_quantities_power(row:BladeRow,upstream:BladeRow):
|
|
172
|
+
"""Calculation of all quantites after radial equilibrium has been solved assuming we know the power at the exit
|
|
173
|
+
|
|
174
|
+
Note:
|
|
175
|
+
Radial Equilibrium gives P0, T0, Vm. This code assumes the loss either enthalpy or pressure loss has already been calculated
|
|
176
|
+
|
|
177
|
+
compute_velocity has been called so we know W, Wt, V, Vt, U, M, M_rel
|
|
178
|
+
|
|
179
|
+
Static Pressure and Temperature should come from Total Temperature and Pressure + Velocity
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
row (BladeRow): current blade row. All quantities are at exit
|
|
183
|
+
upstream (BladeRow): upstream blade row. All quantities are at exit
|
|
184
|
+
|
|
185
|
+
"""
|
|
186
|
+
if row.row_type == RowType.Rotor:
|
|
187
|
+
Cp_avg = (row.Cp+upstream.Cp)/2
|
|
188
|
+
row.T0R = upstream.T0R - T0_coolant_weighted_average(row) - (upstream.U**2-row.U**2)/(2*Cp_avg)
|
|
189
|
+
|
|
190
|
+
# Factor in T0R_drop. Convert T0R drop to absolute terms
|
|
191
|
+
T_drop = (upstream.T0R - row.T0R) - row.W**2/(2*row.Cp) # row.T0R contains the drop
|
|
192
|
+
T0_drop = T_drop*(1+(row.gamma-1)/2*row.M**2)
|
|
193
|
+
|
|
194
|
+
# Adjust Total Temperature to match power
|
|
195
|
+
T0 = upstream.T0 - row.power/row.eta_total/(row.total_massflow*row.Cp) + T0_drop
|
|
196
|
+
|
|
197
|
+
if row.loss_function == LossType.Pressure:
|
|
198
|
+
row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
|
|
199
|
+
row.T0 = T0
|
|
200
|
+
row.T = row.T0/(1+(row.gamma-1)/2*row.M**2)
|
|
201
|
+
row.P = row.P0R*(row.T/row.T0R)**(row.gamma/(row.gamma-1))
|
|
202
|
+
|
|
203
|
+
elif row.loss_function == LossType.Enthalpy:
|
|
204
|
+
row.P0 = row.P*(T0/row.T)**(row.gamma/(row.gamma-1))
|
|
205
|
+
row.T = T0 - row.W**2/(2*row.Cp) + T_drop
|
|
206
|
+
row.T0 = T0
|
|
207
|
+
row.P = row.P0*(row.T0/row.T)**(row.gamma/(row.gamma-1))
|
|
208
|
+
row.P0R = row.P * (row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
209
|
+
|
|
210
|
+
elif row.row_type == RowType.Stator:
|
|
211
|
+
row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
|
|
212
|
+
if row.loss_function == LossType.Pressure:
|
|
213
|
+
row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
|
|
214
|
+
else:
|
|
215
|
+
row.P0 = upstream.P0
|
|
216
|
+
row.T = row.T0 * (1+(row.gamma-1)/2*row.M**2)
|
|
217
|
+
row.P = row.P0 * (row.T/row.T0)**((row.gamma)/(row.gamma-1))
|
|
218
|
+
row.T0R = row.T + row.W**2 / (2*row.Cp)
|
|
219
|
+
row.P0R = row.P*(row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
220
|
+
|
|
221
|
+
def stator_calc(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,calculate_vm:bool=True):
|
|
222
|
+
"""Given P0, T0, P, alpha2 of stator calculate all other quantities
|
|
223
|
+
|
|
224
|
+
Usage:
|
|
225
|
+
Set row.P0 = upstream.P0 - any pressure loss
|
|
226
|
+
row.T0 = upstream.T0 - any cooling
|
|
227
|
+
row.P = row.rp*(row.P0 - rotor.P) + rotor.P
|
|
228
|
+
Set alpha2
|
|
229
|
+
|
|
230
|
+
Args:
|
|
231
|
+
row (BladeRow): Stator Row
|
|
232
|
+
upstream (BladeRow): Stator or Rotor Row
|
|
233
|
+
downstream (BladeRow): Stator or Rotor Row. Defaults to None
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
## degree of reaction (rp) is assumed
|
|
237
|
+
# downstream.P = upstream.P0 * 1/downstream.P0_P
|
|
238
|
+
# if downstream is not None:
|
|
239
|
+
# # Use the upstream P0 value then later factor in the loss
|
|
240
|
+
# row.P = downstream.rp*(upstream.P0 - downstream.P) + downstream.P
|
|
241
|
+
# else:
|
|
242
|
+
# row.P = upstream.P
|
|
243
|
+
|
|
244
|
+
# Static Pressure is assumed
|
|
245
|
+
row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
|
|
246
|
+
row.P0_P = row.P0/downstream.P
|
|
247
|
+
if downstream is not None:
|
|
248
|
+
row.rp = (row.P-downstream.P)/(upstream.P0-downstream.P)
|
|
249
|
+
|
|
250
|
+
if calculate_vm:
|
|
251
|
+
row.M = ((row.P0/row.P)**((row.gamma-1)/row.gamma) - 1) * 2/(row.gamma-1)
|
|
252
|
+
row.M = np.sqrt(row.M)
|
|
253
|
+
T0_T = (1+(row.gamma-1)/2 * row.M.mean()**2)
|
|
254
|
+
row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
|
|
255
|
+
row.T = row.T0/T0_T
|
|
256
|
+
row.V = row.M*np.sqrt(row.gamma*row.R*row.T)
|
|
257
|
+
VV = row.V*np.cos(row.phi)
|
|
258
|
+
row.Vx = VV*np.cos(row.alpha2)
|
|
259
|
+
row.Vt = VV*np.sin(row.alpha2)
|
|
260
|
+
row.Vr = row.V*np.sin(row.phi)
|
|
261
|
+
row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
|
|
262
|
+
else: # We know Vm, P0, T0
|
|
263
|
+
row.Vx = row.Vm*np.cos(row.phi)
|
|
264
|
+
row.Vr = row.Vm*np.sin(row.phi)
|
|
265
|
+
row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
|
|
266
|
+
row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
|
|
267
|
+
for _ in range(3): # Mach is a function of T and T is a function of Mach
|
|
268
|
+
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
269
|
+
T0_T = (1+(row.gamma-1)/2 * row.M**2)
|
|
270
|
+
row.P = row.P0 *(1/T0_T)**(row.gamma/(row.gamma-1))
|
|
271
|
+
row.T = row.T0 * (1/T0_T)
|
|
272
|
+
|
|
273
|
+
if upstream.row_type == RowType.Rotor:
|
|
274
|
+
row.alpha1 = upstream.alpha2 # Upstream rotor absolute frame flow angle
|
|
275
|
+
row.beta1 = upstream.beta2
|
|
276
|
+
row.rho = row.P/(row.R*row.T)
|
|
277
|
+
row.U = row.omega*row.r
|
|
278
|
+
row.P0_stator_inlet = upstream.P0
|
|
279
|
+
|
|
280
|
+
def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
|
|
281
|
+
"""Calculates quantities given beta2
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
row (BladeRow): Rotor Row
|
|
285
|
+
upstream (BladeRow): Stator Row or Rotor Row
|
|
286
|
+
"""
|
|
287
|
+
row.P0_stator_inlet = upstream.P0_stator_inlet
|
|
288
|
+
## P0_P is assumed
|
|
289
|
+
# row.P = row.P0_stator_inlet*1/row.P0_P
|
|
290
|
+
|
|
291
|
+
# Static Pressure is assumed
|
|
292
|
+
row.P0_P = row.P0_stator_inlet/row.P
|
|
293
|
+
upstream_radius = upstream.r
|
|
294
|
+
row.U = row.omega*row.r
|
|
295
|
+
# Upstream Relative Frame Calculations
|
|
296
|
+
upstream.U = upstream.rpm*np.pi/30 * upstream_radius # rad/s
|
|
297
|
+
upstream.Wt = upstream.Vt - upstream.U
|
|
298
|
+
upstream.W = np.sqrt(upstream.Vx**2 + upstream.Wt**2 + upstream.Vr**2)
|
|
299
|
+
upstream.beta2 = np.arctan2(upstream.Wt,upstream.Vx)
|
|
300
|
+
upstream.T0R = upstream.T+upstream.W**2/(2*upstream.Cp)
|
|
301
|
+
upstream.P0R = upstream.P * (upstream.T0R/upstream.T)**((upstream.gamma)/(upstream.gamma-1))
|
|
302
|
+
upstream.M_rel = upstream.W/np.sqrt(upstream.gamma*upstream.R*upstream.T)
|
|
303
|
+
|
|
304
|
+
upstream_rothalpy = upstream.T0R*upstream.Cp - 0.5*upstream.U**2 # H01R - 1/2 U1^2
|
|
305
|
+
|
|
306
|
+
# Rotor Exit Calculations
|
|
307
|
+
row.beta1 = upstream.beta2
|
|
308
|
+
#row.Yp # Evaluated earlier
|
|
309
|
+
row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
|
|
310
|
+
|
|
311
|
+
# Total Relative Temperature stays constant through the rotor. Adjust for change in radius from rotor inlet to exit
|
|
312
|
+
row.T0R = (upstream_rothalpy + 0.5*row.U**2)/row.Cp - T0_coolant_weighted_average(row)
|
|
313
|
+
P0R_P = row.P0R / row.P
|
|
314
|
+
T0R_T = P0R_P**((row.gamma-1)/row.gamma)
|
|
315
|
+
row.T = (row.T0R/T0R_T) # Exit static temperature
|
|
316
|
+
if calculate_vm: # Calculates the T0 at the exit
|
|
317
|
+
row.W = np.sqrt(2*row.Cp*(row.T0R-row.T)) #! nan popups here a lot for radial machines
|
|
318
|
+
if np.isnan(np.sum(row.W)):
|
|
319
|
+
# Need to adjust T
|
|
320
|
+
print(f'nan detected: check flow path. Turbine inlet cut should be horizontal')
|
|
321
|
+
row.Vr = row.W*np.sin(row.phi)
|
|
322
|
+
row.Wt = np.sqrt(row.W**2 - row.Vr**2) * np.sin(row.beta2)
|
|
323
|
+
row.Vt = row.Wt + row.U
|
|
324
|
+
ww = row.W*np.cos(row.phi)
|
|
325
|
+
row.Vx = ww*np.cos(row.beta2)
|
|
326
|
+
row.V = np.sqrt(row.Vr**2+row.Vt**2+row.Vx**2)
|
|
327
|
+
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
328
|
+
row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
|
|
329
|
+
row.T0 = row.T + row.V**2/(2*row.Cp)
|
|
330
|
+
row.P0 = row.P*(row.T0/row.T)**(row.gamma/(row.gamma-1))
|
|
331
|
+
row.alpha2 = np.arctan2(row.Vt,row.Vx)
|
|
332
|
+
else: # We know Vm, P0, T0
|
|
333
|
+
row.Vr = row.Vm*np.sin(row.phi)
|
|
334
|
+
row.Vx = row.Vm*np.cos(row.phi)
|
|
335
|
+
|
|
336
|
+
row.W = np.sqrt(2*row.Cp*(row.T0R-row.T))
|
|
337
|
+
row.Wt = np.sqrt(row.W**2 - row.Vr**2) * np.sin(row.beta2)
|
|
338
|
+
row.U = row.omega * row.r
|
|
339
|
+
row.Vt = row.Wt+row.U
|
|
340
|
+
|
|
341
|
+
row.alpha2 = np.arctan2(row.Vt,row.Vx)
|
|
342
|
+
row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
|
|
343
|
+
|
|
344
|
+
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
345
|
+
T0_T = (1+(row.gamma-1)/2 * row.M**2)
|
|
346
|
+
row.P0 = row.P * T0_T**(row.gamma/(row.gamma-1))
|
|
347
|
+
|
|
348
|
+
row.M_rel = row.W/np.sqrt(row.gamma*row.R*row.T)
|
|
349
|
+
row.T0 = row.T+row.V**2/(2*row.Cp)
|
|
350
|
+
|
|
351
|
+
T3_is = upstream.T0 * (1/row.P0_P)**((row.gamma-1)/row.gamma)
|
|
352
|
+
a = np.sqrt(row.gamma*row.R*T3_is)
|
|
353
|
+
T03_is = T3_is * (1+(row.gamma-1)/2*(row.V/a)**2)
|
|
354
|
+
row.eta_total = (upstream.T0.mean() - row.T0.mean())/(upstream.T0.mean()-T03_is.mean())
|
|
355
|
+
|
|
356
|
+
def inlet_calc(row:BladeRow):
|
|
357
|
+
"""Calculates the conditions for the Inlet
|
|
358
|
+
|
|
359
|
+
Args:
|
|
360
|
+
row (BladeRow): _description_
|
|
361
|
+
"""
|
|
362
|
+
area = row.Vm.copy()*0
|
|
363
|
+
row.T = row.T0
|
|
364
|
+
row.P = row.P0
|
|
365
|
+
total_area = 0
|
|
366
|
+
for _ in range(5): # Lets converge the Mach and Total and Static pressures
|
|
367
|
+
for j in range(1,len(row.percent_hub_shroud)):
|
|
368
|
+
rho = row.rho[j]
|
|
369
|
+
tube_massflow = row.massflow[j]-row.massflow[j-1]
|
|
370
|
+
if np.abs((row.x[j]-row.x[j-1]))<1E-12: # Axial Machines
|
|
371
|
+
total_area += np.pi*(row.r[j]**2-row.r[j-1]**2)
|
|
372
|
+
row.Vm[j] = tube_massflow/(rho*np.pi*(row.r[j]**2-row.r[j-1]**2))
|
|
373
|
+
else: # Radial Machines
|
|
374
|
+
dx = row.x[j]-row.x[j-1]
|
|
375
|
+
S = (row.r[j]-row.r[j-1])
|
|
376
|
+
C = np.sqrt(1+((row.r[j]-row.r[j-1])/dx)**2)
|
|
377
|
+
area[j] = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
|
|
378
|
+
total_area += area[j]
|
|
379
|
+
row.Vm[j] = tube_massflow/(rho*area[j])
|
|
380
|
+
row.Vm[0] = 1/(len(row.Vm)-1)*row.Vm[1:].sum() # Initialize the value at the hub to not upset the mean
|
|
381
|
+
row.Vr = row.Vm*np.sin(row.phi)
|
|
382
|
+
row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
|
|
383
|
+
row.V = np.sqrt(row.Vt**2+row.Vm**2)
|
|
384
|
+
|
|
385
|
+
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
386
|
+
row.T = row.T0 * 1/(1+(row.gamma-1)/2*row.M**2)
|
|
387
|
+
row.P = row.P0 * (row.T/row.T0)**(row.gamma/(row.gamma-1))
|
|
388
|
+
compute_gas_constants(row)
|