turbo-design 1.0.1__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.
Potentially problematic release.
This version of turbo-design might be problematic. Click here for more details.
- turbo_design-1.0.1.dist-info/METADATA +19 -0
- turbo_design-1.0.1.dist-info/RECORD +33 -0
- turbo_design-1.0.1.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/bladerow.py
ADDED
|
@@ -0,0 +1,614 @@
|
|
|
1
|
+
from dataclasses import field, Field
|
|
2
|
+
from typing import Any, Callable, List, Tuple, Union
|
|
3
|
+
from .enums import RowType, PowerType
|
|
4
|
+
import numpy as np
|
|
5
|
+
import numpy.typing as npt
|
|
6
|
+
from scipy.interpolate import interp1d
|
|
7
|
+
from .arrayfuncs import convert_to_ndarray
|
|
8
|
+
from cantera import Solution, composite
|
|
9
|
+
from .coolant import Coolant
|
|
10
|
+
from pyturbo.helper import line2D
|
|
11
|
+
from pyturbo.aero.airfoil2D import Airfoil2D
|
|
12
|
+
from .loss import LossBaseClass
|
|
13
|
+
from .passage import Passage
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BladeRow:
|
|
17
|
+
stage_id:int = 0
|
|
18
|
+
row_type: RowType = RowType.Stator
|
|
19
|
+
loss_function:LossBaseClass
|
|
20
|
+
cutting_line:line2D # Line perpendicular to the streamline
|
|
21
|
+
rp:float = 0.4 # Degree of Reaction
|
|
22
|
+
|
|
23
|
+
# Fluid
|
|
24
|
+
R: float = 287.15 # Ideal Gas constant J/(Kg K)
|
|
25
|
+
gamma: float = 1.33 # Ratio of Cp/Cv
|
|
26
|
+
Cp: float = 1019 # Cp J/(Kg*K)
|
|
27
|
+
Cv: float = 1019/1.14 # Cv J/(Kg*K)
|
|
28
|
+
_coolant:Coolant = None # type: ignore # Coolant Fluid
|
|
29
|
+
fluid: composite.Solution = Solution("air.yaml")
|
|
30
|
+
mu:float = 0
|
|
31
|
+
|
|
32
|
+
total_massflow:float = 0 # Massflow spool + all upstream cooling flow [kg/s]
|
|
33
|
+
massflow:npt.NDArray = field(default_factory=lambda: np.array([0])) # Massflow per radii
|
|
34
|
+
total_massflow_no_coolant:float = 0 # Inlet massflow
|
|
35
|
+
# ----------------------------------
|
|
36
|
+
|
|
37
|
+
# Streamline Properties
|
|
38
|
+
axial_location:float = 0 # Where blade row is defined along the hub.
|
|
39
|
+
percent_hub_shroud: npt.NDArray = field(default_factory=lambda: np.array([0])) # Percent streamline length from hub to shroud.
|
|
40
|
+
x: npt.NDArray = field(default_factory=lambda: np.array([0])) # x - coordinates (useful for computing axial chord)
|
|
41
|
+
r: npt.NDArray = field(default_factory=lambda: np.array([0])) # Radius - coordinates
|
|
42
|
+
area:float = 0
|
|
43
|
+
# Calculated massflow is the massflow computed after radial eq solver
|
|
44
|
+
calculated_massflow: float = 0
|
|
45
|
+
|
|
46
|
+
# Row Efficiency (calculated or specified)
|
|
47
|
+
eta_total:float = 0 # Total to Total
|
|
48
|
+
eta_static:float = 0 # Total to static
|
|
49
|
+
stage_loading:float = 0 # stage loading how much work done per stage
|
|
50
|
+
|
|
51
|
+
alpha1: npt.NDArray = field(default_factory=lambda: np.array([0])) # Blade inlet absolute flow angle
|
|
52
|
+
alpha2: npt.NDArray = field(default_factory=lambda: np.array([0])) # Blade exit absolute flow angle
|
|
53
|
+
|
|
54
|
+
beta1: npt.NDArray = field(default_factory=lambda: np.array([0])) # Blade inlet relative flow angle
|
|
55
|
+
beta2: npt.NDArray = field(default_factory=lambda: np.array([0])) # Blade exit relative flow angle
|
|
56
|
+
|
|
57
|
+
_beta1_metal:npt.NDArray = field(default_factory=lambda: np.array([0])) # blade inlet metal angle
|
|
58
|
+
beta1_metal_radii:npt.NDArray = field(default_factory=lambda: np.array([0])) # radii where metal angle is defined
|
|
59
|
+
|
|
60
|
+
_beta2_metal:npt.NDArray = field(default_factory=lambda: np.array([0])) # blade exit metal angle
|
|
61
|
+
beta2_metal_radii:npt.NDArray = field(default_factory=lambda: np.array([0])) # radii where metal angle is defined
|
|
62
|
+
|
|
63
|
+
beta1_fixed:bool = False # Geometry already defined. This affects the inlet flow angle
|
|
64
|
+
beta2_fixed:bool = False # Geometry already defined. This affects the exit flow angle
|
|
65
|
+
|
|
66
|
+
# Velocities
|
|
67
|
+
Vm: npt.NDArray = field(default_factory=lambda: np.array([0])) # Meridional velocity
|
|
68
|
+
Vx: npt.NDArray = field(default_factory=lambda: np.array([0])) # Axial Velocity
|
|
69
|
+
Vt: npt.NDArray = field(default_factory=lambda: np.array([0])) # Tangential Velocity
|
|
70
|
+
Vr:npt.NDArray = field(default_factory=lambda: np.array([0])) # Radial velocity
|
|
71
|
+
V: npt.NDArray = field(default_factory=lambda: np.array([0])) # Absolute Velocity in 3D coordinate system
|
|
72
|
+
V2: npt.NDArray = field(default_factory=lambda: np.array([0])) # Absolute Velocity in Theta-Axial plane
|
|
73
|
+
M: npt.NDArray = field(default_factory=lambda: np.array([0])) # Mach Number
|
|
74
|
+
M_rel: npt.NDArray = field(default_factory=lambda: np.array([0])) # Relative Mach Number
|
|
75
|
+
U: npt.NDArray = field(default_factory=lambda: np.array([0])) # Peripheral velocity
|
|
76
|
+
W: npt.NDArray = field(default_factory=lambda: np.array([0])) # Relative Velocity in Theta-Axial plane
|
|
77
|
+
Wt: npt.NDArray = field(default_factory=lambda: np.array([0])) # Relative Tangential Velocity
|
|
78
|
+
|
|
79
|
+
_rpm: float = 0
|
|
80
|
+
omega:float = 0 # angular velocity rad/s
|
|
81
|
+
|
|
82
|
+
P0_stator_inlet = field(default_factory=lambda: np.array([0])) # Every quantity is an exit quantity, This is used for efficiency calcs
|
|
83
|
+
T0_stator_inlet = field(default_factory=lambda: np.array([0])) # Every quantity is an exit quantity, This is used for efficiency calcs
|
|
84
|
+
P0: npt.NDArray = field(default_factory=lambda: np.array([0])) # Total Quantities
|
|
85
|
+
T0: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
86
|
+
T0_is:npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
87
|
+
P0R: npt.NDArray = field(default_factory=lambda: np.array([0])) # Relative Total Pressure (Pa)
|
|
88
|
+
T0R: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
89
|
+
|
|
90
|
+
# Static Quantities
|
|
91
|
+
P: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
92
|
+
T: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
93
|
+
T_is: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
94
|
+
rho: npt.NDArray = field(default_factory=lambda: np.array([0]))
|
|
95
|
+
|
|
96
|
+
# Related to streamline curvature
|
|
97
|
+
phi:npt.NDArray = field(default_factory=lambda: np.array([0])) # Inclination angle x,r plane. AY td2.f
|
|
98
|
+
rm: npt.NDArray = field(default_factory=lambda: np.array([0])) # Curvature
|
|
99
|
+
incli_curve_radii: npt.NDArray = field(default_factory=lambda: np.array([0])) # radius at which curvature was evaluated
|
|
100
|
+
|
|
101
|
+
Yp: float = 0 # Pressure loss
|
|
102
|
+
power:float = 0 # Watts
|
|
103
|
+
power_distribution:npt.NDArray # How power is divided by radius. Example: Equal distribution [0.33 0.33 0.33]. More at Tip [0.2,0.3,0.5]. More at Hub [0.6 0.5 ]
|
|
104
|
+
P0_P:float = 0 # Total to Static Pressure Ratio
|
|
105
|
+
Power_Type:PowerType
|
|
106
|
+
euler_power:float = 0
|
|
107
|
+
|
|
108
|
+
# Used for loss calculations
|
|
109
|
+
_blade_to_blade_gap:float = 0.025 # Gap between blade in terms of percent chord.
|
|
110
|
+
|
|
111
|
+
_aspect_ratio:float = 0.9 #
|
|
112
|
+
_pitch_to_chord:float = 0.7 # Pitch to chord ratio, used to determine number of blades and compute loss
|
|
113
|
+
|
|
114
|
+
_axial_chord:float = -1
|
|
115
|
+
_chord:float = -1
|
|
116
|
+
_stagger:float = 42
|
|
117
|
+
_te_s:float = 0.08
|
|
118
|
+
_tip_clearance:float = 0 # Clearance as a percentage of span or blade height
|
|
119
|
+
|
|
120
|
+
_inlet_to_outlet_pratio = [0.06,0.7]
|
|
121
|
+
|
|
122
|
+
@property
|
|
123
|
+
def inlet_to_outlet_pratio(self) -> Tuple[float,float]:
|
|
124
|
+
"""This is what is varied by the optimization.
|
|
125
|
+
The range is between [0 and 1] but you should
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
List[float]: _description_
|
|
129
|
+
"""
|
|
130
|
+
return self._inlet_to_outlet_pratio
|
|
131
|
+
|
|
132
|
+
@inlet_to_outlet_pratio.setter
|
|
133
|
+
def inlet_to_outlet_pratio(self,val:Tuple[float,float]=(0.06,0.7)):
|
|
134
|
+
"""Sets the inlet_to_outlet pratio of the blade row
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
val (Tuple[float,float], optional): guess value from [0,1], you should not use 0 or 1 though. Defaults to (0.06,0.7).
|
|
138
|
+
"""
|
|
139
|
+
self._inlet_to_outlet_pratio = val
|
|
140
|
+
|
|
141
|
+
@property
|
|
142
|
+
def blade_to_blade_gap(self) -> float:
|
|
143
|
+
"""Returns the blade to blade gap value
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
float: _description_
|
|
147
|
+
"""
|
|
148
|
+
return self._blade_to_blade_gap
|
|
149
|
+
|
|
150
|
+
@blade_to_blade_gap.setter
|
|
151
|
+
def blade_to_blade_gap(self,val:float):
|
|
152
|
+
"""Sets the blade to blade gap as a percent. This applies to the next row. So (row1) (row2) if (row1) gap is set to 0.25 then (row2) is offset by 0.25*row1.chord
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
val (float): percentage of chord to space out the blade
|
|
156
|
+
"""
|
|
157
|
+
self._blade_to_blade_gap = val
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def aspect_ratio(self):
|
|
161
|
+
return self._aspect_ratio
|
|
162
|
+
|
|
163
|
+
@aspect_ratio.setter
|
|
164
|
+
def aspect_ratio(self,val:float):
|
|
165
|
+
"""Sets the aspect ratio
|
|
166
|
+
|
|
167
|
+
Aspect ratio is defined as the height/chord not height/(axial chord)
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
val (float): new aspect ratio
|
|
171
|
+
"""
|
|
172
|
+
self._aspect_ratio = val
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def axial_chord(self) -> float:
|
|
176
|
+
"""Returns the mean axial chord defined in the x-direction
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
float: Axial Chord
|
|
180
|
+
"""
|
|
181
|
+
|
|
182
|
+
return self._axial_chord
|
|
183
|
+
|
|
184
|
+
@axial_chord.setter
|
|
185
|
+
def axial_chord(self,val:float):
|
|
186
|
+
self._axial_chord = val
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def pitch_to_chord(self) -> float:
|
|
191
|
+
"""Gets the pitch to chord ratio
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
float: pitch to chord ratio
|
|
195
|
+
"""
|
|
196
|
+
return self._pitch_to_chord
|
|
197
|
+
|
|
198
|
+
@pitch_to_chord.setter
|
|
199
|
+
def pitch_to_chord(self,val:float):
|
|
200
|
+
"""Set the pitch to chord ratio
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
val (float): new pitch to chord ratio. Typically stators are 0.8 to 0.95. Rotors 0.7 to 0.8
|
|
204
|
+
"""
|
|
205
|
+
self._pitch_to_chord = val
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def solidity(self) -> float:
|
|
209
|
+
"""Inverse of pitch to chord ratio
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
float: solidity value
|
|
213
|
+
"""
|
|
214
|
+
return 1/self._pitch_to_chord
|
|
215
|
+
|
|
216
|
+
@solidity.setter
|
|
217
|
+
def solidity(self,val:float):
|
|
218
|
+
"""Inverse of pitch to chord ratio
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
val (float): sets the inverse of pitch to chord ratio
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
float: solidity
|
|
225
|
+
"""
|
|
226
|
+
self._pitch_to_chord = 1/val
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def beta1_metal(self) -> npt.NDArray:
|
|
230
|
+
return np.degrees(self._beta1_metal)
|
|
231
|
+
|
|
232
|
+
@property
|
|
233
|
+
def beta2_metal(self) -> npt.NDArray:
|
|
234
|
+
return np.degrees(self._beta2_metal)
|
|
235
|
+
|
|
236
|
+
@property
|
|
237
|
+
def stagger(self) -> float:
|
|
238
|
+
"""Average stagger angle
|
|
239
|
+
|
|
240
|
+
Returns:
|
|
241
|
+
float: stagger angle
|
|
242
|
+
"""
|
|
243
|
+
return self._stagger
|
|
244
|
+
|
|
245
|
+
@stagger.setter
|
|
246
|
+
def stagger(self,val:float):
|
|
247
|
+
"""Set the stagger angle in degrees
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
val (float): stagger angle. Degrees
|
|
251
|
+
"""
|
|
252
|
+
self._stagger = val
|
|
253
|
+
|
|
254
|
+
@property
|
|
255
|
+
def chord(self) -> float:
|
|
256
|
+
"""Chord defined at mean radius
|
|
257
|
+
|
|
258
|
+
Returns:
|
|
259
|
+
float: axial chord
|
|
260
|
+
"""
|
|
261
|
+
return self.axial_chord / np.cos(np.radians(self.stagger))
|
|
262
|
+
|
|
263
|
+
@property
|
|
264
|
+
def pitch(self) -> float:
|
|
265
|
+
"""Returns the pitch which is the distance from blade to blade
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
float: pitch
|
|
269
|
+
"""
|
|
270
|
+
return self.pitch_to_chord*self.chord
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def throat(self) -> float:
|
|
274
|
+
"""Throat distance
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
float: throat
|
|
278
|
+
"""
|
|
279
|
+
if self.row_type == RowType.Stator:
|
|
280
|
+
return self.pitch*np.sin(np.pi/2-self.alpha2.mean())
|
|
281
|
+
else:
|
|
282
|
+
return self.pitch*np.sin(np.pi/2-self.beta2.mean())
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def num_blades(self) ->float:
|
|
286
|
+
"""returns the number of blades
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
float: number of blades
|
|
290
|
+
"""
|
|
291
|
+
return int(2*np.pi*self.r.mean() / self.pitch)
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def camber(self) -> float:
|
|
295
|
+
"""Estimates the camber of the blade using a bezier curve. This is not as accurate because thickness is not defined on suction and pressure sides.
|
|
296
|
+
|
|
297
|
+
Returns:
|
|
298
|
+
float: camber length
|
|
299
|
+
"""
|
|
300
|
+
if self.row_type == RowType.Stator:
|
|
301
|
+
a2d = Airfoil2D(np.degrees(self.alpha1.mean()),
|
|
302
|
+
np.degrees(self.alpha2.mean()),
|
|
303
|
+
self.axial_chord,
|
|
304
|
+
self.stagger)
|
|
305
|
+
else:
|
|
306
|
+
a2d = Airfoil2D(np.degrees(self.beta1.mean()),
|
|
307
|
+
np.degrees(self.beta2.mean()),
|
|
308
|
+
self.axial_chord,
|
|
309
|
+
self.stagger)
|
|
310
|
+
|
|
311
|
+
return a2d.camberBezier.get_curve_length()
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
def tip_clearance(self):
|
|
315
|
+
"""Tip clearance as a percentage of annulus height
|
|
316
|
+
"""
|
|
317
|
+
return self._tip_clearance
|
|
318
|
+
|
|
319
|
+
@tip_clearance.setter
|
|
320
|
+
def tip_clearance(self,val:float):
|
|
321
|
+
"""Sets the tip clearance
|
|
322
|
+
|
|
323
|
+
Args:
|
|
324
|
+
val (float): tip clearance as a percentage of annulus height.
|
|
325
|
+
"""
|
|
326
|
+
self._tip_clearance = val
|
|
327
|
+
|
|
328
|
+
def __init__(self,axial_location:float,row_type:RowType=RowType.Stator,stage_id:int = 0):
|
|
329
|
+
"""Initializes the blade row to be a particular type
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
axial_location (float): Location of the blade row as a percentage of the total axial length
|
|
333
|
+
row_type (RowType): Specifies the Type. Defaults to RowType.Stator
|
|
334
|
+
power (float, optional): power . Defaults to 0.
|
|
335
|
+
P0_P (float, optional): Total to Static Pressure Ratio
|
|
336
|
+
stage_id (int, optional): ID of the stage so if you have 9 stages, the id could be 9. It's used to separate the stages. Each stage will have it's own unique degree of reaction
|
|
337
|
+
"""
|
|
338
|
+
self.row_type = row_type
|
|
339
|
+
self.axial_location = axial_location
|
|
340
|
+
self.Yp = 0 # Loss
|
|
341
|
+
self.stage_id = stage_id
|
|
342
|
+
|
|
343
|
+
@beta1_metal.setter
|
|
344
|
+
def beta1_metal(self,beta1_metal:List[float],percent:List[float]=[]):
|
|
345
|
+
"""Sets the leading edge metal angle for the blade
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
beta1_metal (List[float]): blade leading edge angle
|
|
349
|
+
percent (List[float]): percent location of metal angles from hub to shroud.
|
|
350
|
+
"""
|
|
351
|
+
self._beta1_metal = np.radians(convert_to_ndarray(beta1_metal))
|
|
352
|
+
if len(percent) != len(beta1_metal):
|
|
353
|
+
percent = np.linspace(0,1,len(self._beta1_metal)).tolist()
|
|
354
|
+
self.beta1_metal_radii = convert_to_ndarray(percent)
|
|
355
|
+
self.beta1_fixed = True
|
|
356
|
+
self.beta1 = self.beta1_metal.copy()
|
|
357
|
+
|
|
358
|
+
@beta2_metal.setter
|
|
359
|
+
def beta2_metal(self,beta2_metal:List[float],percent:List[float]=[]):
|
|
360
|
+
"""Sets the trailing edge metal angle for the blade
|
|
361
|
+
|
|
362
|
+
Args:
|
|
363
|
+
beta2_metal (List[float]): Blade exit metal angle
|
|
364
|
+
percent (List[float]): percent location of metal angles from hub to shroud.
|
|
365
|
+
|
|
366
|
+
"""
|
|
367
|
+
self._beta2_metal = np.radians(convert_to_ndarray(beta2_metal))
|
|
368
|
+
if len(percent) != len(beta2_metal):
|
|
369
|
+
percent = np.linspace(0,1,len(self._beta2_metal)).tolist()
|
|
370
|
+
self.beta2_metal_radii = convert_to_ndarray(percent)
|
|
371
|
+
self.beta2_fixed = True
|
|
372
|
+
self.beta2 = self._beta2_metal.copy()
|
|
373
|
+
|
|
374
|
+
if self.row_type == RowType.Stator:
|
|
375
|
+
self.alpha2 = self._beta2_metal.copy()
|
|
376
|
+
|
|
377
|
+
@property
|
|
378
|
+
def rpm(self):
|
|
379
|
+
return self._rpm
|
|
380
|
+
|
|
381
|
+
@rpm.setter
|
|
382
|
+
def rpm(self,val:float):
|
|
383
|
+
self._rpm = val
|
|
384
|
+
self.omega = self._rpm * np.pi/30 # rev/min * 2pi rads/1 rev * 1 min/60 sec
|
|
385
|
+
|
|
386
|
+
@property
|
|
387
|
+
def coolant(self):
|
|
388
|
+
return self._coolant
|
|
389
|
+
|
|
390
|
+
@coolant.setter
|
|
391
|
+
def coolant(self,coolant:Coolant):
|
|
392
|
+
"""Add a coolant to the end of the blade row
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
coolant (Coolant): Coolant
|
|
396
|
+
"""
|
|
397
|
+
self._coolant = coolant
|
|
398
|
+
|
|
399
|
+
@property
|
|
400
|
+
def loss_model(self):
|
|
401
|
+
return self.loss_function
|
|
402
|
+
|
|
403
|
+
@loss_model.setter
|
|
404
|
+
def loss_model(self, model:Callable[[Any], Any]):
|
|
405
|
+
"""Add in custom loss model
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
model (function): custom loss function. Input will be of the format blade row
|
|
409
|
+
|
|
410
|
+
Example:
|
|
411
|
+
|
|
412
|
+
def mylossfunction(row:BladeRow) -> float
|
|
413
|
+
code to do something with machine learning
|
|
414
|
+
return pressure loss
|
|
415
|
+
"""
|
|
416
|
+
self.loss_function = model
|
|
417
|
+
|
|
418
|
+
@property
|
|
419
|
+
def te_pitch(self):
|
|
420
|
+
"""Trailing edge to pitch ratio
|
|
421
|
+
|
|
422
|
+
Returns:
|
|
423
|
+
float: trailing edge to pitch ratio
|
|
424
|
+
"""
|
|
425
|
+
return self._te_s
|
|
426
|
+
|
|
427
|
+
@te_pitch.setter
|
|
428
|
+
def te_pitch(self,val:float):
|
|
429
|
+
"""Set the trailing edge to pitch ratio. Typical values from 0.02 to 0.12
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
val (float): new trailing edge to pitch ratio
|
|
433
|
+
"""
|
|
434
|
+
self._te_s = val
|
|
435
|
+
|
|
436
|
+
def to_dict(self):
|
|
437
|
+
|
|
438
|
+
data = {
|
|
439
|
+
"StageID":self.stage_id,
|
|
440
|
+
"RowType":self.row_type.name,
|
|
441
|
+
"R":self.R,
|
|
442
|
+
"gamma":self.gamma,
|
|
443
|
+
"Cp":self.Cp,
|
|
444
|
+
"Cv":self.Cv,
|
|
445
|
+
"P0_P":self.P0_P,
|
|
446
|
+
"rp":self.rp,
|
|
447
|
+
"total_massflow":self.total_massflow,
|
|
448
|
+
"massflow":self.massflow.tolist(),
|
|
449
|
+
"calculated_massflow":self.calculated_massflow,
|
|
450
|
+
"alpha1":np.degrees(self.alpha1).tolist(),
|
|
451
|
+
"alpha2":np.degrees(self.alpha2).tolist(),
|
|
452
|
+
"beta1":np.degrees(self.beta1).tolist(),
|
|
453
|
+
"beta2":np.degrees(self.beta2).tolist(),
|
|
454
|
+
"beta1_metal":np.degrees(self._beta1_metal).tolist(),
|
|
455
|
+
"beta2_metal":np.degrees(self._beta2_metal).tolist(),
|
|
456
|
+
"Vm":self.Vm.tolist(),
|
|
457
|
+
"Vx":self.Vx.tolist(),
|
|
458
|
+
"Vr":self.Vr.tolist(),
|
|
459
|
+
"Vt":self.Vt.tolist(),
|
|
460
|
+
"V":self.V.tolist(),
|
|
461
|
+
"M":self.M.tolist(),
|
|
462
|
+
"M_rel":self.M_rel.tolist(),
|
|
463
|
+
"U":self.U.tolist(),
|
|
464
|
+
"W":self.W.tolist(),
|
|
465
|
+
"Wt":self.Wt.tolist(),
|
|
466
|
+
"omega":self.omega,
|
|
467
|
+
"P0":self.P0.tolist(),
|
|
468
|
+
"T0":self.T0.tolist(),
|
|
469
|
+
"P0R":self.P0R.tolist(),
|
|
470
|
+
"T0R":self.T0R.tolist(),
|
|
471
|
+
"P":self.P.tolist(),
|
|
472
|
+
"T":self.T.tolist(),
|
|
473
|
+
"rho":self.rho.tolist(),
|
|
474
|
+
"Yp":self.Yp,
|
|
475
|
+
"Power":self.power,
|
|
476
|
+
"P0_P": self.P0_P,
|
|
477
|
+
"eta_total":self.eta_total,
|
|
478
|
+
"eta_static":self.eta_static,
|
|
479
|
+
"euler_power":self.euler_power,
|
|
480
|
+
"axial_chord":self.axial_chord,
|
|
481
|
+
"aspect_ratio":self.aspect_ratio,
|
|
482
|
+
"area": self.area
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return data
|
|
486
|
+
|
|
487
|
+
#* Some functions related to blade row
|
|
488
|
+
def interpolate_streamline_radii(row:BladeRow,passage:Passage,num_streamlines:int=3):
|
|
489
|
+
"""Interpolate all quantities onto the streamline and allocates variables.
|
|
490
|
+
Run this after setting some initial conditions
|
|
491
|
+
|
|
492
|
+
Args:
|
|
493
|
+
r_streamline (npt.NDArray): Radii describing the streamline
|
|
494
|
+
passage (Passage): Passage object describing the geometry of the hub and shroud
|
|
495
|
+
num_streamlines (int): number of streamlines to consider
|
|
496
|
+
|
|
497
|
+
Returns:
|
|
498
|
+
(BladeRow): new row object with quantities interpolated
|
|
499
|
+
"""
|
|
500
|
+
row.cutting_line,_,_ = passage.get_cutting_line(row.axial_location)
|
|
501
|
+
row.x,row.r = row.cutting_line.get_point(np.linspace(0,1,num_streamlines))
|
|
502
|
+
streamline_percent_length = np.sqrt((row.r-row.r[0])**2+(row.x-row.x[0])**2)/row.cutting_line.length
|
|
503
|
+
|
|
504
|
+
# Flow angles
|
|
505
|
+
row._beta1_metal = row._beta1_metal.default_factory() if type(row._beta1_metal) == Field else row._beta1_metal
|
|
506
|
+
|
|
507
|
+
if row.row_type==RowType.Stator:
|
|
508
|
+
assert type(row._beta2_metal)!=Field,"Stator exit Flow angle must be set"
|
|
509
|
+
|
|
510
|
+
row._beta1_metal = interpolate_quantities(row._beta1_metal,row.beta1_metal_radii,streamline_percent_length)
|
|
511
|
+
row._beta2_metal = interpolate_quantities(row._beta2_metal,row.beta2_metal_radii,streamline_percent_length)
|
|
512
|
+
row.beta1_metal_radii = streamline_percent_length
|
|
513
|
+
row.beta2_metal_radii = streamline_percent_length
|
|
514
|
+
|
|
515
|
+
if type(row.percent_hub_shroud) == Field:
|
|
516
|
+
row.percent_hub_shroud = streamline_percent_length
|
|
517
|
+
else:
|
|
518
|
+
row.percent_hub_shroud = streamline_percent_length # Reset the radii to streamline radii
|
|
519
|
+
|
|
520
|
+
row.alpha1 = interpolate_quantities(row.alpha1,row.percent_hub_shroud,streamline_percent_length)
|
|
521
|
+
row.alpha2 = interpolate_quantities(row.alpha2,row.percent_hub_shroud,streamline_percent_length)
|
|
522
|
+
row.beta1 = interpolate_quantities(row.beta1,row.percent_hub_shroud,streamline_percent_length)
|
|
523
|
+
row.beta2 = interpolate_quantities(row.beta2,row.percent_hub_shroud,streamline_percent_length)
|
|
524
|
+
|
|
525
|
+
# Velocities
|
|
526
|
+
row.Vm = interpolate_quantities(row.Vm, row.percent_hub_shroud, streamline_percent_length)
|
|
527
|
+
row.Vx = interpolate_quantities(row.Vx, row.percent_hub_shroud, streamline_percent_length)
|
|
528
|
+
row.Vt = interpolate_quantities(row.Vt, row.percent_hub_shroud, streamline_percent_length)
|
|
529
|
+
row.Vr = interpolate_quantities(row.Vr, row.percent_hub_shroud, streamline_percent_length)
|
|
530
|
+
row.V = interpolate_quantities(row.V, row.percent_hub_shroud, streamline_percent_length)
|
|
531
|
+
row.V2 = interpolate_quantities(row.V2, row.percent_hub_shroud, streamline_percent_length)
|
|
532
|
+
row.M = interpolate_quantities(row.M, row.percent_hub_shroud, streamline_percent_length)
|
|
533
|
+
row.M_rel = interpolate_quantities(row.M_rel, row.percent_hub_shroud, streamline_percent_length)
|
|
534
|
+
row.U = interpolate_quantities(row.U, row.percent_hub_shroud, streamline_percent_length)
|
|
535
|
+
row.W = interpolate_quantities(row.W, row.percent_hub_shroud, streamline_percent_length)
|
|
536
|
+
row.Wt = interpolate_quantities(row.Wt, row.percent_hub_shroud, streamline_percent_length)
|
|
537
|
+
|
|
538
|
+
# Total Quantities
|
|
539
|
+
row.T0 = interpolate_quantities(row.T0,row.percent_hub_shroud,streamline_percent_length)
|
|
540
|
+
row.T0_is = interpolate_quantities(row.T0,row.percent_hub_shroud,streamline_percent_length)
|
|
541
|
+
row.P0 = interpolate_quantities(row.P0,row.percent_hub_shroud,streamline_percent_length)
|
|
542
|
+
row.P0_stator_inlet = interpolate_quantities(row.P0_stator_inlet,row.percent_hub_shroud,streamline_percent_length)
|
|
543
|
+
|
|
544
|
+
# Relative Quantities
|
|
545
|
+
row.P0R = interpolate_quantities(row.P0R,row.percent_hub_shroud,streamline_percent_length)
|
|
546
|
+
row.T0R = interpolate_quantities(row.T0R,row.percent_hub_shroud,streamline_percent_length)
|
|
547
|
+
|
|
548
|
+
# Static Quantities
|
|
549
|
+
row.P = interpolate_quantities(row.P,row.percent_hub_shroud,streamline_percent_length)
|
|
550
|
+
row.T = interpolate_quantities(row.T,row.percent_hub_shroud,streamline_percent_length)
|
|
551
|
+
row.T_is = interpolate_quantities(row.T_is,row.percent_hub_shroud,streamline_percent_length)
|
|
552
|
+
row.rho = interpolate_quantities(row.rho,row.percent_hub_shroud,streamline_percent_length)
|
|
553
|
+
|
|
554
|
+
if row.row_type == RowType.Inlet:
|
|
555
|
+
row.P0_fun = interp1d(row.percent_hub_shroud,row.P0)
|
|
556
|
+
row.T0_fun = interp1d(row.percent_hub_shroud,row.T0)
|
|
557
|
+
elif row.row_type == RowType.Outlet:
|
|
558
|
+
row.P_fun = interp1d(row.percent_hub_shroud,row.P)
|
|
559
|
+
|
|
560
|
+
return row
|
|
561
|
+
|
|
562
|
+
|
|
563
|
+
def sutherland(T:Union[float,npt.NDArray]) -> Union[float,npt.NDArray]:
|
|
564
|
+
"""Sutherland viscosity calculation used for reynolds number
|
|
565
|
+
|
|
566
|
+
Args:
|
|
567
|
+
T (float): Temperature in Kelvin
|
|
568
|
+
|
|
569
|
+
Returns:
|
|
570
|
+
float: Dynamic Viscosity (mu) in Pa*s
|
|
571
|
+
"""
|
|
572
|
+
S = 110.4
|
|
573
|
+
C1 = 1.458E-6
|
|
574
|
+
return C1*T**1.5 / (T+S)
|
|
575
|
+
|
|
576
|
+
def interpolate_quantities(q:npt.NDArray,r:npt.NDArray,r2:npt.NDArray):
|
|
577
|
+
"""Interpolates array q
|
|
578
|
+
|
|
579
|
+
Args:
|
|
580
|
+
q (npt.NDArray): quantities defined at radius r
|
|
581
|
+
r (npt.NDArray): radius where quantities `q` are defined
|
|
582
|
+
r2 (npt.NDArray): new radius to interpolate the quantities to e.g. streamline radius
|
|
583
|
+
|
|
584
|
+
Returns:
|
|
585
|
+
npt.NDArray: quantities interpolated onto r2
|
|
586
|
+
"""
|
|
587
|
+
if (type(q) == Field):
|
|
588
|
+
q = q.default_factory()
|
|
589
|
+
if (type(r) == Field):
|
|
590
|
+
r = r.default_factory()
|
|
591
|
+
if len(q)==1:
|
|
592
|
+
q2 = np.zeros(shape=r2.shape)
|
|
593
|
+
return q[0]+q2
|
|
594
|
+
else:
|
|
595
|
+
return interp1d(r,q,kind='linear')(r2)
|
|
596
|
+
|
|
597
|
+
def compute_gas_constants(row:BladeRow):
|
|
598
|
+
"""Calculates all the gas constants for a row.
|
|
599
|
+
This should be done if T or P change
|
|
600
|
+
"""
|
|
601
|
+
Tm = row.T.mean()
|
|
602
|
+
Pm = row.P.mean()
|
|
603
|
+
row.fluid.TP = Tm,Pm
|
|
604
|
+
row.Cp = row.fluid.cp
|
|
605
|
+
row.Cv = row.fluid.cv
|
|
606
|
+
row.R = row.Cp-row.Cv
|
|
607
|
+
row.gamma = row.Cp/row.Cv
|
|
608
|
+
row.mu = sutherland(Tm) # type: ignore
|
|
609
|
+
row.rho[:] = row.fluid.density
|
|
610
|
+
# i = 0
|
|
611
|
+
# for T,P in zip(row.T,row.P):
|
|
612
|
+
# row.rho[i] = P/(T*row.R)
|
|
613
|
+
# i+=1
|
|
614
|
+
return row
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
description: |-
|
|
2
|
+
CO2 phases based on the equation of state
|
|
3
|
+
|
|
4
|
+
units: {length: cm, time: s, quantity: mol, activation-energy: cal/mol}
|
|
5
|
+
|
|
6
|
+
phases:
|
|
7
|
+
- name: CO2-Ideal
|
|
8
|
+
thermo: ideal-gas
|
|
9
|
+
elements: [O, C]
|
|
10
|
+
species: [CO2]
|
|
11
|
+
kinetics: none
|
|
12
|
+
state: {T: 300.0, P: 1 atm}
|
|
13
|
+
|
|
14
|
+
- name: CO2-RK
|
|
15
|
+
thermo: Redlich-Kwong
|
|
16
|
+
elements: [O, C]
|
|
17
|
+
species: [CO2]
|
|
18
|
+
kinetics: none
|
|
19
|
+
state: {T: 300.0, P: 1 atm}
|
|
20
|
+
|
|
21
|
+
species:
|
|
22
|
+
- name: CO2
|
|
23
|
+
composition: {C: 1, O: 2}
|
|
24
|
+
thermo:
|
|
25
|
+
model: NASA7
|
|
26
|
+
temperature-ranges: [200.0, 1000.0, 3500.0]
|
|
27
|
+
data:
|
|
28
|
+
- [2.35677352, 0.00898459677, -7.12356269e-06, 2.45919022e-09, -1.43699548e-13,
|
|
29
|
+
-48371.9697, 9.90105222]
|
|
30
|
+
- [3.85746029, 0.00441437026, -2.21481404e-06, 5.23490188e-10, -4.72084164e-14,
|
|
31
|
+
-48759.166, 2.27163806]
|
|
32
|
+
equation-of-state:
|
|
33
|
+
model: Redlich-Kwong
|
|
34
|
+
units: {length: cm, pressure: bar, quantity: mol}
|
|
35
|
+
a: [7.54e7, -4.13e4]
|
|
36
|
+
b: 27.80
|