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.
@@ -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