turbo-design 1.3.7__py3-none-any.whl → 1.3.9__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.

Files changed (49) hide show
  1. {turbo_design-1.3.7.dist-info → turbo_design-1.3.9.dist-info}/METADATA +2 -1
  2. turbo_design-1.3.9.dist-info/RECORD +46 -0
  3. {turbo_design-1.3.7.dist-info → turbo_design-1.3.9.dist-info}/WHEEL +1 -1
  4. turbodesign/__init__.py +57 -4
  5. turbodesign/agf.py +346 -0
  6. turbodesign/arrayfuncs.py +31 -1
  7. turbodesign/bladerow.py +237 -155
  8. turbodesign/compressor_math.py +374 -0
  9. turbodesign/compressor_spool.py +837 -0
  10. turbodesign/coolant.py +18 -6
  11. turbodesign/deviation/__init__.py +5 -0
  12. turbodesign/deviation/axial_compressor.py +3 -0
  13. turbodesign/deviation/carter_deviation.py +79 -0
  14. turbodesign/deviation/deviation_base.py +20 -0
  15. turbodesign/deviation/fixed_deviation.py +42 -0
  16. turbodesign/enums.py +5 -6
  17. turbodesign/flow_math.py +159 -0
  18. turbodesign/inlet.py +126 -56
  19. turbodesign/isentropic.py +59 -15
  20. turbodesign/loss/__init__.py +3 -1
  21. turbodesign/loss/compressor/OTAC_README.md +39 -0
  22. turbodesign/loss/compressor/__init__.py +54 -0
  23. turbodesign/loss/compressor/diffusion.py +61 -0
  24. turbodesign/loss/compressor/lieblein.py +1 -0
  25. turbodesign/loss/compressor/otac.py +799 -0
  26. turbodesign/loss/compressor/references/schobeiri-2012-shock-loss-model-for-transonic-and-supersonic-axial-compressors-with-curved-blades.pdf +0 -0
  27. turbodesign/loss/fixedpolytropic.py +27 -0
  28. turbodesign/loss/fixedpressureloss.py +30 -0
  29. turbodesign/loss/losstype.py +2 -30
  30. turbodesign/loss/turbine/TD2.py +25 -29
  31. turbodesign/loss/turbine/__init__.py +0 -1
  32. turbodesign/loss/turbine/ainleymathieson.py +6 -5
  33. turbodesign/loss/turbine/craigcox.py +6 -5
  34. turbodesign/loss/turbine/fixedefficiency.py +8 -7
  35. turbodesign/loss/turbine/kackerokapuu.py +7 -5
  36. turbodesign/loss/turbine/traupel.py +17 -16
  37. turbodesign/outlet.py +81 -22
  38. turbodesign/passage.py +98 -63
  39. turbodesign/radeq.py +3 -2
  40. turbodesign/row_factory.py +129 -0
  41. turbodesign/solve_radeq.py +9 -10
  42. turbodesign/{td_math.py → turbine_math.py} +125 -175
  43. turbodesign/turbine_spool.py +984 -0
  44. turbo_design-1.3.7.dist-info/RECORD +0 -33
  45. turbodesign/compressorspool.py +0 -60
  46. turbodesign/loss/turbine/fixedpressureloss.py +0 -25
  47. turbodesign/rotor.py +0 -38
  48. turbodesign/spool.py +0 -317
  49. turbodesign/turbinespool.py +0 -543
@@ -1,543 +0,0 @@
1
- from typing import List, Optional
2
- from cantera.composite import Solution
3
- from .bladerow import BladeRow, interpolate_streamline_radii
4
- from .enums import RowType, MassflowConstraint, LossType, PassageType
5
- from .spool import Spool
6
- import json, copy
7
- from .passage import Passage
8
- from scipy.interpolate import interp1d
9
- import numpy as np
10
- import numpy.typing as npt
11
- from .td_math import inlet_calc,rotor_calc, stator_calc, compute_massflow, compute_power, compute_gas_constants, compute_reynolds
12
- from .solve_radeq import adjust_streamlines, radeq
13
- from scipy.optimize import minimize_scalar, differential_evolution, fmin_slsqp
14
- from .inlet import Inlet
15
- from .outlet import Outlet
16
- from pyturbo.helper import convert_to_ndarray
17
-
18
- class TurbineSpool(Spool):
19
-
20
- def __init__(self,passage:Passage,
21
- massflow:float,rows:List[BladeRow],
22
- num_streamlines:int=3,
23
- fluid:Optional[Solution]=Solution('air.yaml'),
24
- rpm:float=-1,
25
- massflow_constraint:MassflowConstraint=MassflowConstraint.MatchMassFlow): # type: ignore
26
- """Initializes a Turbine Spool
27
-
28
- Args:
29
- passage (Passage): Passage defining hub and shroud
30
- massflow (float): massflow at spool inlet
31
- rows (List[BladeRow], optional): List of blade rows. Defaults to List[BladeRow].
32
- num_streamlines (int, optional): number of streamlines. Defaults to 3.
33
- fluid (ct.Solution, optional): cantera gas solution. Defaults to ct.Solution('air.yaml').
34
- rpm (float, optional): RPM for the entire spool Optional, you can also set rpm of the blade rows individually. Defaults to -1.
35
- massflow_constraint (MassflowConstraint, optional): MatchMassflow - Matches the massflow defined in the spool. BalanceMassflow - Balances the massflow between BladeRows, matches the lowest massflow.
36
-
37
- """
38
- super().__init__(passage, massflow, rows,num_streamlines, fluid, rpm)
39
- self.massflow_constraint = massflow_constraint
40
- pass
41
-
42
- def initialize_quantities(self):
43
- """Initializes the massflow throughout the rows
44
- """
45
- # Massflow from inlet already defined
46
-
47
- # Inlet
48
- W0 = self.massflow
49
- inlet = self.blade_rows[0]
50
- if self.fluid:
51
- inlet.initialize_fluid(self.fluid) # type: ignore
52
- else:
53
- inlet.initialize_fluid(R=self.blade_rows[1].R, # type: ignore
54
- gamma=self.blade_rows[1].gamma,
55
- Cp=self.blade_rows[1].Cp)
56
-
57
- inlet.total_massflow = W0
58
- inlet.total_massflow_no_coolant = W0
59
- inlet.massflow = np.linspace(0,1,self.num_streamlines)*W0
60
-
61
- inlet.initialize_inputs(self.num_streamlines)
62
- inlet.initialize_velocity(self.passage,self.num_streamlines) # type: ignore
63
- interpolate_streamline_radii(inlet,self.passage,self.num_streamlines)
64
-
65
- compute_gas_constants(inlet,self.fluid)
66
- inlet_calc(inlet)
67
-
68
- for row in self.blade_rows:
69
- interpolate_streamline_radii(row,self.passage,self.num_streamlines)
70
-
71
- outlet = self.blade_rows[-1]
72
- for j in range(self.num_streamlines):
73
- P0 = inlet.get_total_pressure(inlet.percent_hub_shroud[j]) # type: ignore
74
- percents = np.zeros(shape=(len(self.blade_rows)-2)) + 0.3
75
- percents[-1] = 1
76
- Ps_range = outlet_pressure(percents=percents,inletP0=inlet.P0[j],outletP=outlet.P[j]) # type: ignore
77
- for i in range(1,len(self.blade_rows)-1):
78
- self.blade_rows[i].P[j] = Ps_range[i-1]
79
-
80
- # Pass T0 and P0 to all the other blade_rows
81
- for i in range(1,len(self.blade_rows)-1):
82
- upstream = self.blade_rows[i-1] # Inlet conditions solved before this step
83
- if i+1<len(self.blade_rows):
84
- downstream = self.blade_rows[i+1]
85
- else:
86
- downstream = None
87
-
88
- row = self.blade_rows[i]
89
- if (row.coolant is not None):
90
- T0c = self.blade_rows[i].coolant.T0
91
- P0c = self.blade_rows[i].coolant.P0
92
- W0c = self.blade_rows[i].coolant.massflow_percentage * self.massflow
93
- Cpc = self.blade_rows[i].coolant.Cp
94
- else:
95
- T0c = 100
96
- P0c = 0
97
- W0c = 0
98
- Cpc = 0
99
-
100
- T0 = upstream.T0
101
- P0 = upstream.P0
102
- Cp = upstream.Cp
103
-
104
- T0 = (W0*Cp*T0 + W0c*Cpc*T0c)/(Cpc * W0c + Cp*W0)
105
- P0 = (W0*Cp*P0 + W0c*Cpc*P0c)/(Cpc * W0c + Cp*W0)
106
- Cp = (W0*Cp + W0c*Cpc)/(W0c + W0) # Weighted
107
-
108
- if row.row_type == RowType.Stator:
109
- T0 = upstream.T0
110
- else:
111
- T0 = upstream.T0 - row.power / (Cp*(W0 + W0c))
112
-
113
- W0 += W0c
114
- row.T0 = T0
115
- row.P0 = P0
116
- row.Cp = Cp
117
- row.total_massflow = W0
118
- row.massflow = np.linspace(0,1,self.num_streamlines)*row.total_massflow
119
- # Pass Quantities: rho, P0, T0
120
-
121
- row.rho = upstream.rho
122
- row.gamma = upstream.gamma
123
- row.R = upstream.R
124
-
125
- if row.row_type == RowType.Stator:
126
- stator_calc(row,upstream,downstream) # type: ignore
127
- compute_massflow(row)
128
- elif row.row_type == RowType.Rotor:
129
- rotor_calc(row,upstream)
130
- compute_massflow(row)
131
- compute_power(row,upstream)
132
-
133
-
134
- def solve(self):
135
- """
136
- Solve for the exit flow angles to match the massflow distribution at the stage exit
137
- """
138
- self.initialize_streamlines()
139
- self.initialize_quantities()
140
-
141
- if self.massflow_constraint ==MassflowConstraint.MatchMassFlow:
142
- self.__match_massflow() # Matches massflow by changing turning angle
143
- elif self.massflow_constraint == MassflowConstraint.BalanceMassFlow:
144
- self.__balance_massflow() # Balances massflow by changing row exit static pressure
145
-
146
-
147
- def __match_massflow(self):
148
- """ Matches the massflow between streamtubes by changing exit angles. Doesn't use radial equilibrium.
149
- """
150
- for _ in range(3):
151
- # Step 1: Solve a blade row for exit angle to maintain massflow
152
- for i in range(len(self.blade_rows)):
153
- row = self.blade_rows[i]
154
- # Upstream Row
155
- if i == 0:
156
- upstream = self.blade_rows[i]
157
- else:
158
- upstream = self.blade_rows[i-1]
159
- if i<len(self.blade_rows)-1:
160
- downstream = self.blade_rows[i+1]
161
- else:
162
- downstream = None
163
-
164
- if row.row_type == RowType.Stator:
165
- bounds = [0,80]
166
- else:# row.row_type == RowType.Rotor:
167
- bounds = [-80,0]
168
- if row.row_type != RowType.Inlet:
169
- for j in range(1,self.num_streamlines):
170
- res = minimize_scalar(massflow_loss_function, bounds=bounds,args=(j,row,upstream,downstream),tol=1E-3)
171
- if row.row_type == RowType.Rotor:
172
- row.beta2[j] = np.radians(res.x)
173
- # Initialize the value at the hub to not upset the mean
174
- row.beta2[0] = 1/(len(row.beta2)-1)*row.beta2[1:].sum()
175
- elif row.row_type == RowType.Stator:
176
- row.alpha2[j] = np.radians(res.x)
177
- row.alpha2[0] = 1/(len(row.alpha2)-1)*row.alpha2[1:].sum()
178
- compute_gas_constants(upstream,self.fluid)
179
- compute_gas_constants(row,self.fluid)
180
-
181
-
182
- # Step 3: Adjust streamlines to evenly divide massflow
183
- adjust_streamlines(self.blade_rows,self.passage)
184
- compute_reynolds(self.blade_rows,self.passage)
185
-
186
- @staticmethod # Private static method
187
- def __massflow_std__(blade_rows:List[BladeRow]):
188
- """Returns the standard deviation of the massflow
189
-
190
- Args:
191
- blade_rows (List[BladeRow]): List of blade rows
192
-
193
- Returns:
194
- _type_: _description_
195
- """
196
- total_massflow = list(); s = 0; massflow_stage = list()
197
- stage_ids = list(set([row.stage_id for row in blade_rows if row.stage_id>=0]))
198
- for row in blade_rows: # Ignore inlet and outlet
199
- total_massflow.append(row.total_massflow_no_coolant)
200
- sign = 1
201
- for s in stage_ids:
202
- for row in blade_rows:
203
- if row.stage_id == s and row.row_type == RowType.Rotor:
204
- massflow_stage.append(sign*row.total_massflow_no_coolant)
205
- sign*=-1
206
- if len(stage_ids) % 2 == 1:
207
- massflow_stage.append(massflow_stage[-1]*sign)
208
- deviation = np.std(total_massflow)*2
209
- if deviation>1.0:
210
- print("high massflow deviation detected")
211
- return np.std(total_massflow)*2 # + abs(sum(massflow_stage)) # Equation 28
212
-
213
- def __balance_massflow(self):
214
- """ Balances the massflow between rows. Use radial equilibrium.
215
-
216
- Types of stages:
217
- 1. Stator - Rotor | Stator - Rotor
218
- 2. Rotor | Stator - Rotor | Stator - Rotor
219
- 3. Stator - Rotor | CounterRotating | Stator - Rotor
220
- 4. Rotor-Counter Rotating | Stator - Rotor
221
- 5. Counter Rotating - Rotor | Stator - Rotor
222
-
223
- Steps:
224
- 1. Split the blade rows into stages stator-rotor pairs or rotor rotor or rotor
225
- 2. Change degree of reaction to match the total massflow
226
- 3. Adjust the streamlines for each blade row to balance the massflow
227
- """
228
-
229
-
230
-
231
- # Balance the massflow between Stages
232
- def balance_massflows(x0:List[float],blade_rows:List[BladeRow],P0:npt.NDArray,P:npt.NDArray,balance_mean_pressure:bool=True):
233
- """Balance Massflows.
234
-
235
- Steps:
236
- 1. Balance the mean static pressure in between the blade rows. X0 = [0.2,0.5,...] size = num_rows
237
- P = outlet static pressure
238
-
239
- 2. Keep the mean
240
-
241
- Args:
242
- x0 (List[float]): Percentage of P0 exiting each row
243
- blade_rows (List[List[BladeRow]]): _description_
244
- P0 (npt.NDArray): _description_
245
- P (npt.NDArray): (1) Outlet Static Pressure. (2)
246
- balance_mean_pressure (bool, optional): _description_. Defaults to True.
247
-
248
- Returns:
249
- _type_: _description_
250
- """
251
- # blade_rows_backup = copy.deepcopy(blade_rows)
252
- # try:
253
- if balance_mean_pressure:
254
- for j in range(self.num_streamlines):
255
- Ps = outlet_pressure(x0,P0[j],P[j])
256
- for i in range(1,len(blade_rows)-2):
257
- blade_rows[i].P[j] = float(Ps[i-1]) # type: ignore
258
- blade_rows[-2].P = P # type: ignore
259
- else:
260
- for i in range(1,len(blade_rows)-1):
261
- for j in range(self.num_streamlines):
262
- blade_rows[i].P[j] = P[j]*x0[(i-1)*self.num_streamlines+j] # type: ignore # x0 size = num_streamlines -1
263
- # try:
264
- calculate_massflows(blade_rows,True,self.fluid)
265
- print(x0)
266
- return self.__massflow_std__(blade_rows[1:-1]) # do not consider inlet and outlet
267
- # except Exception as e:
268
- # print(e)
269
- # finally:
270
- # blade_rows = blade_rows_backup
271
- # return np.inf # Return a high error
272
-
273
-
274
- # Break apart the rows to stages
275
- outlet_P=list(); outlet_P_guess = list() # Outlet P is the bounds, outlet_p_guess is the guessed values
276
-
277
- for i in range(1,len(self.blade_rows)-2):
278
- outlet_P.append(self.blade_rows[i].inlet_to_outlet_pratio)
279
- outlet_P_guess.append(np.mean(self.blade_rows[i].inlet_to_outlet_pratio))
280
-
281
- print(f"Looping to converge massflow")
282
- past_err = -100; loop_iter = 0; err = 0.001
283
- while (np.abs((err-past_err)/err)>0.05) and loop_iter<10:
284
- if len(outlet_P) == 1:
285
- # x = balance_massflows(0.22896832148169688,self.blade_rows,self.blade_rows[0].P0,self.blade_rows[-1].P)
286
- res = minimize_scalar(fun=balance_massflows,args=(self.blade_rows,self.blade_rows[0].P0,self.blade_rows[-1].P),bounds=outlet_P[0],tol=0.001,options={'disp': True},method='bounded')
287
- x = res.x
288
- print(x)
289
- else:
290
- x = fmin_slsqp(func=balance_massflows,args=(self.blade_rows,self.blade_rows[0].P0,self.blade_rows[-1].P),
291
- bounds=outlet_P, x0=outlet_P_guess,epsilon=0.001,iter=100) # ,tol=0.001,options={'disp': True})
292
- outlet_P_guess = x
293
-
294
- # Adjust the inlet: Set the massflow
295
- self.blade_rows[0].massflow = np.linspace(0,1,self.num_streamlines)*self.blade_rows[1].total_massflow_no_coolant
296
- self.blade_rows[0].total_massflow_no_coolant = self.blade_rows[1].total_massflow_no_coolant
297
- self.blade_rows[0].total_massflow = self.blade_rows[1].total_massflow_no_coolant
298
- self.blade_rows[0].calculated_massflow = self.blade_rows[0].total_massflow_no_coolant
299
- inlet_calc(self.blade_rows[0]) # adjust the inlet to match massflow
300
-
301
- if self.adjust_streamlines:
302
- adjust_streamlines(self.blade_rows[:-1],self.passage)
303
-
304
- self.blade_rows[-1].transfer_quantities(self.blade_rows[-2]) # This would be the outlet
305
- self.blade_rows[-1].P = self.blade_rows[-1].get_static_pressure(self.blade_rows[-1].percent_hub_shroud)
306
-
307
- past_err = err
308
- err = self.__massflow_std__(self.blade_rows)
309
- loop_iter += 1
310
- print(f"Loop {loop_iter} massflow convergenced error:{err}")
311
-
312
- # calculate Reynolds number
313
- compute_reynolds(self.blade_rows,self.passage)
314
-
315
-
316
- def export_properties(self,filename:str="turbine_spool.json"):
317
- """Export the spool object to json
318
-
319
- Args:
320
- filename (str, optional): name of export file. Defaults to "spool.json".
321
- """
322
- blade_rows = list()
323
- degree_of_reaction = list()
324
- total_total_efficiency = list()
325
- total_static_efficiency = list()
326
- stage_loading = list()
327
- euler_power = list()
328
- enthalpy_power = list()
329
- x_streamline = np.zeros((self.num_streamlines,len(self.blade_rows)))
330
- r_streamline = np.zeros((self.num_streamlines,len(self.blade_rows)))
331
- massflow = list()
332
- for indx,row in enumerate(self.blade_rows):
333
- blade_rows.append(row.to_dict()) # Appending data
334
- if row.row_type == RowType.Rotor:
335
- # Calculation for these are specific to Turbines
336
- degree_of_reaction.append(((self.blade_rows[indx-1].P- row.P)/(self.blade_rows[indx-2].P-row.P)).mean())
337
-
338
- total_total_efficiency.append(row.eta_total)
339
- total_static_efficiency.append(row.eta_static)
340
-
341
- stage_loading.append(row.stage_loading)
342
- euler_power.append(row.euler_power)
343
- enthalpy_power.append(row.power)
344
- if row.row_type!=RowType.Inlet and row.row_type!=RowType.Outlet:
345
- massflow.append(row.massflow[-1])
346
-
347
- for j,p in enumerate(row.percent_hub_shroud):
348
- t,x,r = self.passage.get_streamline(p)
349
- x_streamline[j,indx] = float(interp1d(t,x)(row.percent_hub))
350
- r_streamline[j,indx] = float(interp1d(t,r)(row.percent_hub))
351
-
352
- Pratio_Total_Total = np.mean(self.blade_rows[0].P0 / self.blade_rows[-2].P0)
353
- Pratio_Total_Static = np.mean(self.blade_rows[0].P0 / self.blade_rows[-2].P)
354
- FlowFunction = np.mean(massflow)*np.sqrt(self.blade_rows[0].T0)*self.blade_rows[0].P0/1000 # kg sqrt(K)/(sec kPa)
355
- CorrectedSpeed = self.rpm * np.pi/30 / np.sqrt(self.blade_rows[0].T0.mean()) # rad/s * 1/sqrt(K)
356
- EnergyFunction = (self.blade_rows[0].T0 - self.blade_rows[-2].T0) * 0.5* (self.blade_rows[0].Cp + self.blade_rows[-2].Cp) / self.blade_rows[0].T0 # J/(KgK)
357
- EnergyFunction = np.mean(EnergyFunction)
358
- data = {
359
- "blade_rows": blade_rows,
360
- "massflow":np.mean(massflow),
361
- "rpm":self.rpm,
362
- "r_streamline":r_streamline.tolist(),
363
- "x_streamline":x_streamline.tolist(),
364
- "rhub":self.passage.rhub_pts.tolist(),
365
- "rshroud":self.passage.rshroud_pts.tolist(),
366
- "xhub":self.passage.xhub_pts.tolist(),
367
- "xshroud":self.passage.xshroud_pts.tolist(),
368
- "num_streamlines":self.num_streamlines,
369
- "euler_power": euler_power,
370
- "enthalpy_power":enthalpy_power,
371
- "total-total_efficiency":total_total_efficiency,
372
- "total-static_efficiency":total_static_efficiency,
373
- "stage_loading":stage_loading,
374
- "degree_of_reaction":degree_of_reaction,
375
- "Pratio_Total_Total":Pratio_Total_Total,
376
- "Pratio_Total_Static":Pratio_Total_Static,
377
- "FlowFunction":FlowFunction,
378
- "CorrectedSpeed":CorrectedSpeed,
379
- "EnergyFunction":EnergyFunction
380
- }
381
- # Dump all the Python objects into a single JSON file.
382
- class NumpyEncoder(json.JSONEncoder):
383
- def default(self, obj):
384
- if isinstance(obj, np.ndarray):
385
- return obj.tolist()
386
- return super().default(obj)
387
-
388
- with open(filename, "w") as f:
389
- json.dump(data, f, indent=4,cls=NumpyEncoder)
390
-
391
-
392
- def calculate_massflows(blade_rows:List[BladeRow],calculate_vm:bool=False,fluid:Optional[Solution]=None):
393
- """Calculates the massflow
394
-
395
- Args:
396
- blade_rows (List[BladeRow]): _description_
397
- passage (Passage): _description_
398
- calculate_vm (bool, optional): _description_. Defaults to False.
399
- """
400
- for i in range(1,len(blade_rows)-1):
401
- row = blade_rows[i]
402
- # Upstream Row
403
- if i == 0:
404
- upstream = blade_rows[i]
405
- else:
406
- upstream = blade_rows[i-1]
407
- if i<len(blade_rows)-1:
408
- downstream = blade_rows[i+1]
409
-
410
- # Pressure loss = shift in entropy which affects the total pressure of the row
411
- if row.row_type == RowType.Inlet:
412
- row.Yp = 0
413
- else:
414
- if row.loss_function.loss_type == LossType.Pressure:
415
- row.Yp = row.loss_function(row,upstream)
416
- for _ in range(2):
417
- if row.row_type == RowType.Rotor:
418
- rotor_calc(row,upstream,calculate_vm=True)
419
- # Finds Equilibrium between Vm, P0, T0
420
- row = radeq(row,upstream,downstream)
421
- compute_gas_constants(row,fluid)
422
- rotor_calc(row,upstream,calculate_vm=False)
423
- elif row.row_type == RowType.Stator:
424
- stator_calc(row,upstream,downstream,calculate_vm=True)
425
- # Finds Equilibrium between Vm, P0, T0
426
- row = radeq(row,upstream,downstream)
427
- compute_gas_constants(row,fluid)
428
- stator_calc(row,upstream,downstream,calculate_vm=False)
429
- compute_gas_constants(row,fluid)
430
- compute_massflow(row)
431
- compute_power(row,upstream)
432
-
433
- elif row.loss_function.loss_type == LossType.Enthalpy:
434
- if row.row_type == RowType.Rotor:
435
- row.Yp = 0
436
- rotor_calc(row,upstream,calculate_vm=calculate_vm)
437
- eta_total = float(row.loss_function(row,upstream))
438
- def find_yp(Yp,row,upstream):
439
- row.Yp = Yp
440
- rotor_calc(row,upstream,calculate_vm=True)
441
- row = radeq(row,upstream)
442
- compute_gas_constants(row,fluid)
443
- rotor_calc(row,upstream,calculate_vm=False)
444
- return abs(row.eta_total - eta_total)
445
-
446
- res = minimize_scalar(find_yp,bounds=[0,0.6],args=(row,upstream))
447
- row.Yp = res.x
448
- elif row.row_type == RowType.Stator:
449
- row.Yp = 0
450
- stator_calc(row,upstream,downstream,calculate_vm=True)
451
- row = radeq(row,upstream)
452
- row = compute_gas_constants(row,fluid)
453
- stator_calc(row,upstream,downstream,calculate_vm=False)
454
- row = compute_gas_constants(row,fluid)
455
- compute_massflow(row)
456
- compute_power(row,upstream)
457
-
458
- def massflow_loss_function(exit_angle:float,index:int,row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,fluid:Solution=None):
459
- """Finds the blade exit angles that balance the massflow throughout the stage
460
-
461
- Args:
462
- exit_angle (float): exit flow angle of the rotor row
463
- index (int): streamline index for the current row
464
- row (BladeRow): current blade row
465
- upstream (BladeRow): upstream blade row
466
- downstream (BladeRow): downstream blade row
467
-
468
- Returns:
469
- float: massflow loss
470
- """
471
- # Pressure loss = shift in entropy which affects the total pressure of the row
472
- if row.row_type == RowType.Inlet:
473
- row.Yp = 0
474
- else:
475
- if row.loss_function.loss_type == LossType.Pressure:
476
- row.Yp = row.loss_function(row,upstream)
477
- if row.row_type == RowType.Rotor:
478
- row.beta2[index] = np.radians(exit_angle)
479
- rotor_calc(row,upstream)
480
- elif row.row_type == RowType.Stator:
481
- row.alpha2[index] = np.radians(exit_angle)
482
- stator_calc(row,upstream,downstream)
483
- upstream = compute_gas_constants(upstream,fluid)
484
- row = compute_gas_constants(row,fluid)
485
- elif row.loss_function.loss_type == LossType.Enthalpy:
486
- # Search for pressure loss that results in the correct total temperature drop
487
- if row.row_type == RowType.Rotor:
488
- row.Yp = 0
489
- row.beta2[index] = np.radians(exit_angle)
490
- rotor_calc(row,upstream)
491
- T0_drop = row.loss_function(row,upstream)
492
- T0_target = row.T0.mean()-T0_drop
493
- def find_yp(Yp):
494
- row.Yp = Yp
495
- rotor_calc(row,upstream)
496
- upstream = compute_gas_constants(upstream,fluid)
497
- row = compute_gas_constants(row,fluid)
498
- return abs(row.T0.mean() - T0_target)
499
- res = minimize_scalar(find_yp,bounds=[0,0.6])
500
- row.Yp = res.x
501
- elif row.row_type == RowType.Stator:
502
- row.Yp = 0
503
- row.alpha2[index] = np.radians(exit_angle)
504
- stator_calc(row,upstream,downstream)
505
- upstream = compute_gas_constants(upstream,fluid)
506
- row = compute_gas_constants(row,fluid)
507
-
508
-
509
- # if use_radeq:
510
- # row = radeq(row,upstream) # Finds Equilibrium between Vm, P0, T0
511
-
512
- compute_massflow(row)
513
- compute_power(row,upstream)
514
-
515
- if row.row_type!=RowType.Inlet:
516
- # if row.row_type == RowType.Rotor:
517
- T3_is = upstream.T0 * (1/row.P0_P)**((row.gamma-1)/row.gamma)
518
- # else:
519
- # T3_is = upstream.T0 * (row.P0/row.P)**((row.gamma-1)/row.gamma)
520
- a = np.sqrt(row.gamma*row.R*T3_is)
521
- T03_is = T3_is * (1+(row.gamma-1)/2*(row.V/a)**2)
522
- row.eta_total = (upstream.T0.mean() - row.T0.mean())/(upstream.T0.mean()-T03_is.mean())
523
-
524
- return np.abs(row.total_massflow*index/(len(row.massflow)-1) - row.massflow[index])
525
-
526
- def outlet_pressure(percents:List[float],inletP0:float,outletP:float) -> npt.NDArray:
527
- """Given a list of percents from 0 to 1 for each row, output each row's outlet static pressure
528
-
529
- Args:
530
- percents (List[float]): List of floats as percents [[0 to 1],[0 to 1]]
531
- inletP0 (float): Inlet Total Pressure
532
- outletP (float): Outlet Static Pressure
533
-
534
- Returns:
535
- npt.NDArray: Array of static pressures
536
- """
537
- percents = convert_to_ndarray(percents)
538
- Ps = np.zeros((len(percents),))
539
- for i in range(len(percents)):
540
- Ps[i] = float(interp1d((0,1),(inletP0,outletP))(percents[i]))
541
- inletP0 = Ps[i]
542
- return Ps
543
-