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.
- {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/METADATA +2 -1
- turbo_design-1.3.10.dist-info/RECORD +46 -0
- {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/WHEEL +1 -1
- turbodesign/__init__.py +57 -4
- turbodesign/agf.py +346 -0
- turbodesign/arrayfuncs.py +31 -1
- turbodesign/bladerow.py +238 -155
- turbodesign/compressor_math.py +386 -0
- turbodesign/compressor_spool.py +941 -0
- turbodesign/coolant.py +18 -6
- turbodesign/deviation/__init__.py +5 -0
- turbodesign/deviation/axial_compressor.py +3 -0
- turbodesign/deviation/carter_deviation.py +79 -0
- turbodesign/deviation/deviation_base.py +20 -0
- turbodesign/deviation/fixed_deviation.py +42 -0
- turbodesign/enums.py +5 -6
- turbodesign/flow_math.py +158 -0
- turbodesign/inlet.py +126 -56
- turbodesign/isentropic.py +59 -15
- turbodesign/loss/__init__.py +3 -1
- turbodesign/loss/compressor/OTAC_README.md +39 -0
- turbodesign/loss/compressor/__init__.py +54 -0
- turbodesign/loss/compressor/diffusion.py +61 -0
- turbodesign/loss/compressor/lieblein.py +1 -0
- turbodesign/loss/compressor/otac.py +799 -0
- turbodesign/loss/compressor/references/schobeiri-2012-shock-loss-model-for-transonic-and-supersonic-axial-compressors-with-curved-blades.pdf +0 -0
- turbodesign/loss/fixedpolytropic.py +27 -0
- turbodesign/loss/fixedpressureloss.py +30 -0
- turbodesign/loss/losstype.py +2 -30
- turbodesign/loss/turbine/TD2.py +25 -29
- turbodesign/loss/turbine/__init__.py +0 -1
- turbodesign/loss/turbine/ainleymathieson.py +6 -5
- turbodesign/loss/turbine/craigcox.py +6 -5
- turbodesign/loss/turbine/fixedefficiency.py +8 -7
- turbodesign/loss/turbine/kackerokapuu.py +7 -5
- turbodesign/loss/turbine/traupel.py +17 -16
- turbodesign/outlet.py +81 -22
- turbodesign/passage.py +98 -63
- turbodesign/row_factory.py +129 -0
- turbodesign/solve_radeq.py +9 -10
- turbodesign/{td_math.py → turbine_math.py} +144 -185
- turbodesign/turbine_spool.py +1219 -0
- turbo_design-1.3.8.dist-info/RECORD +0 -33
- turbodesign/compressorspool.py +0 -60
- turbodesign/loss/turbine/fixedpressureloss.py +0 -25
- turbodesign/rotor.py +0 -38
- turbodesign/spool.py +0 -317
- 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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
224
|
-
#
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
362
|
-
|
|
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
|
-
|
|
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
|
-
|
|
406
|
-
|
|
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
|
-
|
|
409
|
-
|
|
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
|