turbo-design 1.3.8__py3-none-any.whl → 1.3.10__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.
Files changed (48) hide show
  1. {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/METADATA +2 -1
  2. turbo_design-1.3.10.dist-info/RECORD +46 -0
  3. {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.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 +238 -155
  8. turbodesign/compressor_math.py +386 -0
  9. turbodesign/compressor_spool.py +941 -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 +158 -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/row_factory.py +129 -0
  40. turbodesign/solve_radeq.py +9 -10
  41. turbodesign/{td_math.py → turbine_math.py} +144 -185
  42. turbodesign/turbine_spool.py +1219 -0
  43. turbo_design-1.3.8.dist-info/RECORD +0 -33
  44. turbodesign/compressorspool.py +0 -60
  45. turbodesign/loss/turbine/fixedpressureloss.py +0 -25
  46. turbodesign/rotor.py +0 -38
  47. turbodesign/spool.py +0 -317
  48. turbodesign/turbinespool.py +0 -543
@@ -1,86 +1,17 @@
1
- from typing import List, Optional
1
+ from typing import List, Optional, Tuple
2
+ import warnings
2
3
  import numpy as np
3
4
  import numpy.typing as npt
5
+ from .isentropic import IsenP, IsenT
6
+ from turbodesign.loss import losstype
4
7
  from .bladerow import BladeRow, compute_gas_constants
5
8
  from .enums import RowType, LossType
9
+ from .outlet import OutletType
6
10
  from scipy.integrate import trapezoid
11
+ from scipy.optimize import minimize
7
12
  from .passage import Passage
8
13
  from .isentropic import IsenP
9
-
10
- def T0_coolant_weighted_average(row:BladeRow) -> float:
11
- """Calculate the new weighted Total Temperature array considering coolant
12
-
13
- Args:
14
- coolant (Coolant): Coolant
15
- massflow (np.ndarray): massflow mainstream
16
-
17
- Returns:
18
- float: Total Temperature drop
19
- """
20
-
21
- massflow = row.massflow
22
- total_massflow_no_coolant = row.total_massflow_no_coolant
23
- Cp = row.Cp
24
-
25
- Cpc = row.coolant.Cp
26
- T0c = row.coolant.T0
27
- massflow_coolant = row.coolant.massflow_percentage*total_massflow_no_coolant*row.massflow[1:]/row.massflow[-1]
28
- if massflow_coolant.mean()>0:
29
- if row.row_type == RowType.Stator:
30
- T0= row.T0
31
- dT0 = T0.copy() * 0
32
- T0_new = (massflow[1:]*Cp*T0[1:] + massflow_coolant*Cpc*T0c) \
33
- /(massflow[1:]*Cp + massflow_coolant*Cpc)
34
- dT0[1:] = T0_new - row.T0[1:]
35
- dT0[0] = dT0[1]
36
- else:
37
- T0R = row.T0R
38
- T0R_new = T0R.copy()
39
- Cp = row.Cp
40
- T0R_new[1:] = (massflow[1:]*Cp*T0R[1:] + massflow_coolant*Cpc*T0c) \
41
- /(massflow[1:]*Cp + massflow_coolant*Cpc)
42
- T0R_new[0] = T0R_new[1]
43
-
44
- T = T0R_new - row.W**2/(2*Cp) # Dont change the velocity triangle but adjust the static temperature
45
- T0_new = T+row.V**2/(2*Cp) # Use new static temperature to calculate the total temperature
46
- dT0 = T0_new - row.T0
47
- return dT0
48
- else:
49
- return row.T0*0
50
-
51
- def compute_massflow(row:BladeRow) -> None:
52
- """Populates row.massflow and row.calculated_massflow
53
-
54
- Calculated_massflow is massflow[-1]
55
-
56
- Args:
57
- row (BladeRow): current blade row. All quantities are at exit
58
- upstream (BladeRow): upstream blade row. All quantities are at exit
59
- """
60
- massflow_fraction = np.linspace(0,1,len(row.percent_hub_shroud))
61
- massflow = row.percent_hub_shroud*0
62
- total_area = 0
63
- for j in range(1,len(row.percent_hub_shroud)):
64
- Vm = (row.Vm[j]+row.Vm[j-1])/2
65
- rho = (row.rho[j]+row.rho[j-1])/2
66
- if np.abs((row.x[j]-row.x[j-1]))<1E-5: # Axial Machines
67
- total_area += np.pi*(row.r[j]**2-row.r[j-1]**2)
68
- massflow[j] = Vm * rho * np.pi* (row.r[j]**2-row.r[j-1]**2) + massflow[j-1]
69
- else: # Radial Machines
70
- dx = row.x[j]-row.x[j-1]
71
- S = (row.r[j]-row.r[j-1])
72
- C = np.sqrt(1+((row.r[j]-row.r[j-1])/dx)**2)
73
- area = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
74
- total_area += area
75
- massflow[j] = Vm * rho *area + massflow[j-1]
76
-
77
- row.total_massflow_no_coolant = massflow[-1]
78
- if row.coolant != None:
79
- massflow += massflow_fraction*row.coolant.massflow_percentage*row.total_massflow_no_coolant # Take into account the coolant massflow
80
- row.massflow = massflow
81
- row.calculated_massflow = massflow[-1]
82
- row.total_massflow = massflow[-1]
83
- row.area = total_area
14
+ from .flow_math import compute_massflow, compute_streamline_areas, compute_power
84
15
 
85
16
  def compute_reynolds(rows:List[BladeRow],passage:Passage):
86
17
  """Calculates the Reynolds Number
@@ -90,7 +21,7 @@ def compute_reynolds(rows:List[BladeRow],passage:Passage):
90
21
  passage (Passage): Passage
91
22
  """
92
23
 
93
- for i in range(1,len(rows)):
24
+ for i in range(1,len(rows)-1):
94
25
  row = rows[i]
95
26
  xr = passage.get_xr_slice(0.5,(rows[i-1].location,row.percent_hub))
96
27
  dx = np.diff(xr[:,0])
@@ -105,39 +36,11 @@ def compute_reynolds(rows:List[BladeRow],passage:Passage):
105
36
  V = row.V.mean()
106
37
  rho = row.rho.mean()
107
38
  mu = row.mu
108
- row.Reynolds = c*V*rho/mu
39
+ row.Reynolds = row.axial_chord*V*rho/mu
109
40
  row.mprime = mp
110
41
  row.axial_chord = max(c,1E-12) # Axial chord
111
42
  # row.num_blades = int(2*np.pi*row.r.mean() / row.pitch_to_chord * row.axial_chord)
112
43
 
113
- def compute_power(row:BladeRow,upstream:BladeRow) -> None:
114
- """Calculates the power
115
-
116
- Args:
117
- row (BladeRow): _description_
118
- upstream (BladeRow): _description_
119
- """
120
- if row.row_type == RowType.Stator:
121
- row.power = 0
122
- row.eta_static = 0
123
- row.eta_total = 0
124
- row.stage_loading = 0
125
- row.euler_power = 0
126
- row.T_is = 0 * row.T0
127
- row.T0_is = 0 * row.T0 # Make it an array
128
- else:
129
- P0_P = (upstream.P0/row.P).mean()
130
- row.T_is = upstream.T0 * (1/P0_P)**((row.gamma-1)/row.gamma)
131
- a = np.sqrt(row.gamma*row.R*row.T_is)
132
- row.T0_is = row.T_is * (1+(row.gamma-1)/2*(row.V/a)**2)
133
-
134
- row.power = row.massflow[-1] * (row.Cp * (upstream.T0 - row.T0)).mean()
135
- # row.power = sum(v * w for v, w in zip(row.power[1:], np.diff(row.massflow))) # Massflow weighted average
136
- row.eta_static = row.power/ (row.massflow[-1]*row.Cp*(upstream.T0.mean()-row.T_is.mean()))
137
- row.eta_total = (upstream.T0.mean() - row.T0.mean()) / (upstream.T0.mean() - row.T0_is.mean())
138
- row.stage_loading = row.Cp*(upstream.T0.mean() - row.T0.mean())/row.U.mean()**2
139
- row.euler_power = row.massflow[-1]* (upstream.U*upstream.Vt - row.U*row.Vt).mean()
140
-
141
44
  def compute_quantities(row:BladeRow,upstream:BladeRow):
142
45
  """Calculation of all quantites after radial equilibrium has been solved assuming we know the static pressure at the exit.
143
46
 
@@ -151,14 +54,14 @@ def compute_quantities(row:BladeRow,upstream:BladeRow):
151
54
  row (BladeRow): current blade row. All quantities are at exit
152
55
  upstream (BladeRow): upstream blade row. All quantities are at exit
153
56
  """
154
-
155
57
  if row.row_type == RowType.Rotor:
156
58
  Cp_avg = (row.Cp+upstream.Cp)/2
157
59
  # Factor any coolant added and changes in streamline radius
158
60
  row.T0R = upstream.T0R - T0_coolant_weighted_average(row) # - (upstream.U**2-row.U**2)/(2*Cp_avg)
159
61
  row.P = upstream.P0_stator_inlet/row.P0_P
160
-
161
- if row.loss_function.loss_type == LossType.Pressure:
62
+
63
+ loss_type = getattr(row.loss_function, "loss_type", None)
64
+ if loss_type == LossType.Pressure:
162
65
  # This affects the velocity triangle
163
66
  row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
164
67
  row.T = (row.P/row.P0R)**((row.gamma-1)/row.gamma) * row.T0R
@@ -167,7 +70,7 @@ def compute_quantities(row:BladeRow,upstream:BladeRow):
167
70
  row.power = np.trapezoid(row.power_distribution,row.r-row.r[0])
168
71
  row.power_mean = row.massflow[-1] * row.Cp * (upstream.T0.mean()-row.T0.mean())
169
72
 
170
- elif row.loss_function.loss_type == LossType.Enthalpy:
73
+ elif loss_type == LossType.Enthalpy:
171
74
  ' For Enthalpy related loss, assume the static quantities do not change '
172
75
  row.T = (row.P/row.P0R)**((row.gamma-1)/row.gamma) * row.T0R
173
76
  row.T0 = (1+(row.gamma-1)/2 * row.M**2) * row.T
@@ -195,7 +98,8 @@ def compute_quantities(row:BladeRow,upstream:BladeRow):
195
98
 
196
99
  elif row.row_type == RowType.Stator:
197
100
  ' For the stator we already assume the upstream P0 already applied '
198
- if row.loss_function == LossType.Pressure:
101
+ loss_type = getattr(row.loss_function, "loss_type", None)
102
+ if loss_type == LossType.Pressure:
199
103
  row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
200
104
  else:
201
105
  row.P0 = upstream.P0
@@ -205,32 +109,45 @@ def compute_quantities(row:BladeRow,upstream:BladeRow):
205
109
  row.T0R = row.T + row.W**2 / (2*row.Cp)
206
110
  row.P0R = row.P*(row.T0R/row.T)**((row.gamma)/(row.gamma-1))
207
111
 
208
- def stator_calc(row:BladeRow,upstream:BladeRow,downstream:Optional[BladeRow]=None,calculate_vm:bool=True):
112
+ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:Optional[BladeRow]=None,calculate_vm:bool=True, outlet_type:OutletType=OutletType.static_pressure):
209
113
  """Given P0, T0, P, alpha2 of stator calculate all other quantities
210
114
 
211
115
  Usage:
212
116
  Set row.P0 = upstream.P0 - any pressure loss
213
117
  row.T0 = upstream.T0 - any cooling
214
- row.P = row.rp*(row.P0 - rotor.P) + rotor.P
215
- Set alpha2
216
-
118
+ row.P = row.rp*(row.P0 - rotor.P) + rotor.P
119
+ Set alpha2
120
+
217
121
  Args:
218
122
  row (BladeRow): Stator Row
219
- upstream (BladeRow): Stator or Rotor Row
123
+ upstream (BladeRow): Stator or Rotor Row
220
124
  downstream (BladeRow): Stator or Rotor Row. Defaults to None
221
-
125
+ calculate_vm (bool): True to calculate the meridional velocity. False, do not calculate this and let radeq calculate it
126
+ outlet_type (OutletType): OutletType.static_pressure if static conditions defined at outlet, OutletType.total_pressure if total conditions defined
222
127
  """
223
- ## degree of reaction (rp) is assumed
224
- # downstream.P = upstream.P0 * 1/downstream.P0_P
225
- # if downstream is not None:
226
- # # Use the upstream P0 value then later factor in the loss
227
- # row.P = downstream.rp*(upstream.P0 - downstream.P) + downstream.P
228
- # else:
229
- # row.P = upstream.P
230
-
231
- # Static Pressure is assumed
232
- row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
233
-
128
+
129
+ # Static Pressure is assumed
130
+ T0_coolant = 0
131
+ if row.coolant is not None:
132
+ T0_coolant = T0_coolant_weighted_average(row)
133
+ row.T0 = upstream.T0 - T0_coolant
134
+
135
+ loss_type = getattr(row.loss_function, "loss_type", None)
136
+ if loss_type == LossType.Pressure:
137
+ row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P) # When static conditions are defined, use it to calculate P0
138
+
139
+
140
+ elif loss_type == LossType.Enthalpy:
141
+ b = row.total_area * row.P0 / np.sqrt(row.T0) * np.sqrt(row.gamma/row.R)
142
+ solve_for_M = upstream.total_massflow / b
143
+ fun = lambda M : np.abs(solve_for_M - M*(1+(row.gamma-1)/2 * M**2) ** (-(row.gamma+1)/(2*(row.gamma-1))))
144
+ M_subsonic = minimize(fun,0.1, method='L-BFGS-B', bounds=[0,1])
145
+ M_supersonic = minimize(fun,1.5, method='L-BFGS-B', bounds=[1,5])
146
+ row.M = M_subsonic
147
+ row.T = row.T0/IsenT(M_subsonic,row.gamma)
148
+ a = np.sqrt(row.T*row.gamma*row.R)
149
+ row.P = row.total_massflow * row.R*row.T / (row.total_area * row.M * a) # Use the massflow to find static pressure
150
+
234
151
  if downstream is not None:
235
152
  row.P0_P = float((row.P0/downstream.P).mean())
236
153
  row.rp = ((row.P-downstream.P)/(upstream.P0-downstream.P)).mean()
@@ -239,7 +156,6 @@ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:Optional[BladeRow]=Non
239
156
  row.M = ((row.P0/row.P)**((row.gamma-1)/row.gamma) - 1) * 2/(row.gamma-1)
240
157
  row.M = np.sqrt(row.M)
241
158
  T0_T = (1+(row.gamma-1)/2 * row.M**2)
242
- row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
243
159
  row.T = row.T0/T0_T
244
160
  row.V = row.M*np.sqrt(row.gamma*row.R*row.T)
245
161
  row.Vm = row.V*np.cos(row.alpha2)
@@ -251,7 +167,6 @@ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:Optional[BladeRow]=Non
251
167
  row.Vr = row.Vm*np.sin(row.phi)
252
168
  row.Vt = row.Vm*np.tan(row.alpha2)
253
169
  row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
254
-
255
170
  row.T = row.P/(row.R*row.rho) # We know P, this is a guess
256
171
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
257
172
 
@@ -263,12 +178,14 @@ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:Optional[BladeRow]=Non
263
178
  row.Wt = row.Vt-row.U
264
179
  row.P0_stator_inlet = upstream.P0
265
180
 
266
- def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
267
- """Calculates quantities given beta2
181
+ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True,outlet_type:OutletType=OutletType.static_pressure):
182
+ """Calculates quantities given beta2
268
183
 
269
184
  Args:
270
185
  row (BladeRow): Rotor Row
271
186
  upstream (BladeRow): Stator Row or Rotor Row
187
+ calculate_vm (bool): True to calculate the meridional velocity. False, do not calculate this and let radeq calculate it
188
+ outlet_type (OutletType): OutletType.static_pressure if static conditions defined at outlet, OutletType.total_pressure if total conditions defined
272
189
  """
273
190
  def _log_rotor_failure(reason:str):
274
191
  def _fmt(val):
@@ -294,10 +211,7 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
294
211
  ## P0_P is assumed
295
212
  # row.P = row.P0_stator_inlet*1/row.P0_P
296
213
 
297
- # Static Pressure is assumed
298
- row.P0_P = (row.P0_stator_inlet/row.P).mean()
299
214
  upstream_radius = upstream.r
300
- row.U = row.omega*row.r
301
215
  # Upstream Relative Frame Calculations
302
216
  upstream.U = upstream.rpm*np.pi/30 * upstream_radius # rad/s
303
217
  upstream.Wt = upstream.Vt - upstream.U
@@ -306,17 +220,23 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
306
220
  upstream.T0R = upstream.T+upstream.W**2/(2*upstream.Cp)
307
221
  upstream.P0R = upstream.P * (upstream.T0R/upstream.T)**((upstream.gamma)/(upstream.gamma-1))
308
222
  upstream.M_rel = upstream.W/np.sqrt(upstream.gamma*upstream.R*upstream.T)
309
-
310
223
  upstream_rothalpy = upstream.T0R*upstream.Cp - 0.5*upstream.U**2 # H01R - 1/2 U1^2
224
+ row.U = row.omega*row.r
225
+
311
226
  if np.any(upstream_rothalpy < 0):
312
227
  print('U is too high, reduce RPM or radius')
228
+
313
229
  # Rotor Exit Calculations
314
230
  row.beta1 = upstream.beta2
315
231
  #row.Yp # Evaluated earlier
316
232
  row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
317
-
233
+
234
+
318
235
  # Total Relative Temperature stays constant through the rotor. Adjust for change in radius from rotor inlet to exit
319
- row.T0R = upstream.T0R # (upstream_rothalpy + 0.5*row.U**2)/row.Cp # - T0_coolant_weighted_average(row)
236
+ T0_coolant = 0
237
+ if row.coolant is not None:
238
+ T0_coolant = T0_coolant_weighted_average(row)
239
+ row.T0R = (upstream_rothalpy + 0.5*row.U**2)/row.Cp - T0_coolant
320
240
  P0R_P = row.P0R / row.P
321
241
  T0R_T = P0R_P**((row.gamma-1)/row.gamma)
322
242
  row.T = (row.T0R/T0R_T) # Exit static temperature
@@ -334,8 +254,8 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
334
254
  reason += "; Yp > 0.3 This could be a problem with the loss model;"
335
255
  _log_rotor_failure(reason)
336
256
  raise ValueError(f'nan detected')
337
- row.Vr = row.W*np.sin(row.phi)
338
257
  row.Vm = row.W*np.cos(row.beta2)
258
+ row.Vr = row.Vm*np.sin(row.phi)
339
259
  row.Wt = row.W*np.sin(row.beta2)
340
260
  row.Vx = row.Vm*np.cos(row.phi)
341
261
  row.Vt = row.Wt + row.U
@@ -343,26 +263,34 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
343
263
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
344
264
  row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
345
265
  row.T0 = row.T + row.V**2/(2*row.Cp)
346
- row.P0 = row.P*(row.T0/row.T)**(row.gamma/(row.gamma-1))
347
266
  row.alpha2 = np.arctan2(row.Vt,row.Vm)
348
- else: # We know Vm, P0, T0
267
+ else: # We know Vm from radeq, beta2 from blade angle, T0R from rothalpy
349
268
  row.Vr = row.Vm*np.sin(row.phi)
350
269
  row.Vx = row.Vm*np.cos(row.phi)
351
-
352
- row.W = np.sqrt(2*row.Cp*(row.T0R-row.T))
270
+
271
+ # Compute W from velocity triangle (not thermodynamics) to close the triangle
272
+ row.W = row.Vm / np.cos(row.beta2)
353
273
  row.Wt = row.W*np.sin(row.beta2)
354
- row.U = row.omega * row.r
355
- row.Vt = row.Wt+row.U
356
-
274
+ row.U = row.omega * row.r
275
+ row.Vt = row.Wt + row.U
276
+
357
277
  row.alpha2 = np.arctan2(row.Vt,row.Vm)
358
278
  row.V = np.sqrt(row.Vm**2*(1+np.tan(row.alpha2)**2))
359
-
279
+
280
+ # Update T from energy conservation in rotating frame
281
+ row.T = row.T0R - row.W**2 / (2*row.Cp)
282
+
360
283
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
361
- T0_T = (1+(row.gamma-1)/2 * row.M**2)
362
- row.P0 = row.P * T0_T**(row.gamma/(row.gamma-1))
363
-
284
+
285
+ # Compute T0 first, then derive P0 from P0R to keep the velocity triangle consistent.
286
+ # P0/P0R = (T0/T0R)^(gamma/(gamma-1)) always holds since both reference the same static state.
287
+ # Using P0R (from the loss model) avoids dependence on the boundary-condition P, which may
288
+ # not be consistent with T after radeq adjusts Vm.
289
+ row.T0 = row.T + row.V**2/(2*row.Cp)
290
+ row.P0 = row.P0R * (row.T0 / row.T0R) ** (row.gamma / (row.gamma - 1))
291
+ row.P0_P = (row.P0_stator_inlet/row.P).mean()
292
+
364
293
  row.M_rel = row.W/np.sqrt(row.gamma*row.R*row.T)
365
- row.T0 = row.T+row.V**2/(2*row.Cp)
366
294
 
367
295
  def inlet_calc(row:BladeRow):
368
296
  """Calculates the conditions for the Inlet
@@ -370,40 +298,71 @@ def inlet_calc(row:BladeRow):
370
298
  Args:
371
299
  row (BladeRow): _description_
372
300
  """
373
-
374
- area = row.Vm.copy()*0
375
301
  # Estimate the density
376
- row.T = row.T0
377
- row.P = row.P0
378
- row.rho = row.P/(row.T*row.R)
379
- total_area = 0
380
- for iter in range(5): # Lets converge the Mach and Total and Static pressures
381
- for j in range(1,len(row.percent_hub_shroud)):
382
- rho = row.rho[j]
383
- tube_massflow = row.massflow[j]-row.massflow[j-1]
384
- if np.abs((row.x[j]-row.x[j-1]))<1E-6: # Axial Machines
385
- total_area += np.pi*(row.r[j]**2-row.r[j-1]**2)
386
- row.Vm[j] = tube_massflow/(rho*np.pi*(row.r[j]**2-row.r[j-1]**2))
387
- else: # Radial Machines
388
- dx = row.x[j]-row.x[j-1]
389
- S = (row.r[j]-row.r[j-1])
390
- C = np.sqrt(1+((row.r[j]-row.r[j-1])/dx)**2)
391
- area[j] = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
392
- total_area += area[j]
393
- row.Vm[j] = tube_massflow/(rho*area[j])
394
- avg_mach = np.mean(row.M)
395
- row.Vm[0] = 1/(len(row.Vm)-1)*row.Vm[1:].sum() # Initialize the value at the hub to not upset the mean
396
- row.Vr = row.Vm*np.sin(row.phi)
397
- row.Vt = row.Vm*np.tan(row.alpha2)
398
- row.V = np.sqrt(row.Vt**2+row.Vm**2)
399
- # Fine tune the Temperature and Pressure and density
400
- row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
401
- row.T = row.T0 * 1/(1+(row.gamma-1)/2*row.M**2)
402
- row.P = row.P0 * (row.T/row.T0)**(row.gamma/(row.gamma-1))
403
- compute_gas_constants(row)
302
+ row.T = row.T0 * 1/(1+(row.gamma-1)/2*row.M**2)
303
+ row.P = row.P0 * (row.T/row.T0)**(row.gamma/(row.gamma-1))
304
+ compute_gas_constants(row)
404
305
 
405
- if np.mean(row.M)>0.5:
406
- raise ValueError(f"High inlet mach can lead to errors iter:{iter} Mach:{avg_mach}")
306
+ total_area, streamline_area = compute_streamline_areas(row)
307
+ row.total_area = total_area
308
+ row.area = streamline_area
309
+ Vm_tube = np.zeros(len(row.percent_hub_shroud)-1)
310
+
311
+ Vm_mean = row.total_massflow / (row.rho.mean()*row.total_area)
312
+ for j in range(1,len(row.percent_hub_shroud)):
313
+ rho = row.rho[j]
314
+ Vm_tube[j-1] = (row.massflow[j]-row.massflow[j-1])/(rho*row.area[j])
315
+
316
+ # Recover per-streamline Vm from tube-averaged Vm_tube assuming piecewise linear variation
317
+ row.Vm[0] = Vm_tube[0] if len(Vm_tube) else Vm_mean
318
+ for j in range(1, len(row.percent_hub_shroud)):
319
+ rho_bar = 0.5 * (row.rho[j] + row.rho[j-1])
320
+ Vm_tube_j = (row.massflow[j] - row.massflow[j-1]) / (rho_bar * row.area[j] + 1e-12)
321
+ row.Vm[j] = 2 * Vm_tube_j - row.Vm[j-1]
322
+
323
+ row.Vr = row.Vm*np.sin(row.phi)
324
+ row.V = row.M*np.sqrt(row.gamma*row.R*row.T)
325
+ row.Vt = row.V*np.sin(row.alpha2)
326
+ row.Vx = row.Vm*np.cos(row.phi)
327
+
328
+
329
+ def T0_coolant_weighted_average(row:BladeRow) -> npt.NDArray:
330
+ """Calculate the new weighted Total Temperature array considering coolant
331
+
332
+ Args:
333
+ row (BladeRow): Coolant
334
+ massflow (np.ndarray): massflow mainstream
335
+
336
+ Returns:
337
+ float: Total Temperature drop
338
+ """
407
339
 
408
- if np.mean(row.M)<0.01:
409
- print(f"Unusually slow flow:{iter} Mach:{avg_mach}")
340
+ massflow = row.massflow
341
+ total_massflow_no_coolant = row.total_massflow_no_coolant
342
+ Cp = row.Cp
343
+
344
+ Cpc = row.coolant.Cp
345
+ T0c = row.coolant.T0
346
+ massflow_coolant = row.coolant.massflow_percentage*total_massflow_no_coolant*row.massflow[1:]/row.massflow[-1]
347
+ if massflow_coolant.mean()>0:
348
+ if row.row_type == RowType.Stator:
349
+ T0= row.T0
350
+ dT0 = T0.copy() * 0
351
+ T0_new = (massflow[1:]*Cp*T0[1:] + massflow_coolant*Cpc*T0c) \
352
+ /(massflow[1:]*Cp + massflow_coolant*Cpc)
353
+ dT0[1:] = T0_new - row.T0[1:]
354
+ dT0[0] = dT0[1]
355
+ else: # Rotor use relative total temperature
356
+ T0R = row.T0R
357
+ T0R_new = T0R.copy()
358
+ Cp = row.Cp
359
+ T0R_new[1:] = (massflow[1:]*Cp*T0R[1:] + massflow_coolant*Cpc*T0c) \
360
+ /(massflow[1:]*Cp + massflow_coolant*Cpc)
361
+ T0R_new[0] = T0R_new[1]
362
+
363
+ T = T0R_new - row.W**2/(2*Cp) # Dont change the velocity triangle but adjust the static temperature
364
+ T0_new = T+row.V**2/(2*Cp) # Use new static temperature to calculate the total temperature
365
+ dT0 = T0_new - row.T0
366
+ return dT0
367
+ else:
368
+ return row.T0*0