turbo-design 1.0.11__py3-none-any.whl → 1.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {turbo_design-1.0.11.dist-info → turbo_design-1.1.1.dist-info}/METADATA +1 -1
- {turbo_design-1.0.11.dist-info → turbo_design-1.1.1.dist-info}/RECORD +9 -9
- turbodesign/bladerow.py +15 -5
- turbodesign/passage.py +48 -11
- turbodesign/radeq.py +6 -4
- turbodesign/spool.py +9 -3
- turbodesign/td_math.py +50 -69
- turbodesign/turbinespool.py +32 -26
- {turbo_design-1.0.11.dist-info → turbo_design-1.1.1.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
turbodesign/__init__.py,sha256=N8Nu0I1vrlEHYJZZ7yhhg-FtbNbLyrgjy7hoicygUqg,325
|
|
2
2
|
turbodesign/arrayfuncs.py,sha256=GHIlTyLfeNsNCQQoh5m1aXK3iyvU6RjdYQipkqpU_ps,519
|
|
3
|
-
turbodesign/bladerow.py,sha256=
|
|
3
|
+
turbodesign/bladerow.py,sha256=Ie3TDxaLzOSM38dQ0xNyjFEwHWbHXMq1FcksTVEvkqc,24540
|
|
4
4
|
turbodesign/cantera_gas/co2.yaml,sha256=M2o_RzxV9B9rDkgkXJC-l3voKraFZguTZuKKt4F7S_c,887
|
|
5
5
|
turbodesign/compressorspool.py,sha256=z8ZVczJ-EdZvIqqZArC6UdwC5MbU2SZh_MT2CGq5avY,2600
|
|
6
6
|
turbodesign/coolant.py,sha256=nuj7ryUXuBhuOPt2jHjHwtEM_LVno7A8Wm14P0lJn-8,436
|
|
@@ -20,14 +20,14 @@ turbodesign/loss/turbine/kackerokapuu.py,sha256=hUmZdyA1hAhN3_xbwBhka2dyk0Aq9rqq
|
|
|
20
20
|
turbodesign/loss/turbine/traupel.py,sha256=aZxFE9JDcCfi1gy-fqS1SxjjlhuDG-H-g3LPurty1M0,4164
|
|
21
21
|
turbodesign/lossinterp.py,sha256=B2KEobp-nD9jwD6UINgBmTlH9kKyWg3UNvXxqfUsr-k,6198
|
|
22
22
|
turbodesign/outlet.py,sha256=SwTwoL6XnWlsrE5A6wLxu3qXkydDDUsdKKeBbJm4mrQ,2069
|
|
23
|
-
turbodesign/passage.py,sha256=
|
|
24
|
-
turbodesign/radeq.py,sha256=
|
|
23
|
+
turbodesign/passage.py,sha256=eZGaB5Gq9Frm80TTDJaLm1TxJV2-_fHHIeqjPuvtb8E,9550
|
|
24
|
+
turbodesign/radeq.py,sha256=RCsiQzcHvR5dozK6XG0eysAhXYewF0BY_qX0f1knURo,10091
|
|
25
25
|
turbodesign/rotor.py,sha256=tHl9o5H4aQ6Etd4gqa8Ime1UK7k0de4GLt5Yb1sJdGs,1376
|
|
26
26
|
turbodesign/solve_radeq.py,sha256=2VGzFSVkyN0rTPFHaCQiD3v166cdEs39x7x7RuzLgmw,1886
|
|
27
|
-
turbodesign/spool.py,sha256=
|
|
27
|
+
turbodesign/spool.py,sha256=h4m--HR5g8o01Kkhl4Mbvx7WORQez3YpncDE7XxbNaw,14172
|
|
28
28
|
turbodesign/stage.py,sha256=UP45sDKDLsAkO_WfDWJ6kqXU7cYKh_4QO01QZnSN1oQ,166
|
|
29
|
-
turbodesign/td_math.py,sha256=
|
|
30
|
-
turbodesign/turbinespool.py,sha256=
|
|
31
|
-
turbo_design-1.
|
|
32
|
-
turbo_design-1.
|
|
33
|
-
turbo_design-1.
|
|
29
|
+
turbodesign/td_math.py,sha256=En8g8AcldaZN9qmG33wrt2EuTqKfSQ8UO2PWod6a0IE,15970
|
|
30
|
+
turbodesign/turbinespool.py,sha256=U_TMv5XNTlRWP9NJWTaaFWfNeTEGEuSVWt3HDi8QyFI,26124
|
|
31
|
+
turbo_design-1.1.1.dist-info/METADATA,sha256=C1hmGnuG5wJahAn1UDbHQPQCwfUeGK4Mx3XgwGucUHg,734
|
|
32
|
+
turbo_design-1.1.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
|
33
|
+
turbo_design-1.1.1.dist-info/RECORD,,
|
turbodesign/bladerow.py
CHANGED
|
@@ -97,6 +97,8 @@ class BladeRow:
|
|
|
97
97
|
phi:npt.NDArray = field(default_factory=lambda: np.array([0])) # Inclination angle x,r plane. AY td2.f
|
|
98
98
|
rm: npt.NDArray = field(default_factory=lambda: np.array([0])) # Curvature
|
|
99
99
|
incli_curve_radii: npt.NDArray = field(default_factory=lambda: np.array([0])) # radius at which curvature was evaluated
|
|
100
|
+
mprime:npt.NDArray = field(default_factory=lambda: np.array([0])) # Mprime distance
|
|
101
|
+
axial_chord:float = 0
|
|
100
102
|
|
|
101
103
|
Yp: float = 0 # Pressure loss
|
|
102
104
|
power:float = 0 # Watts
|
|
@@ -104,7 +106,8 @@ class BladeRow:
|
|
|
104
106
|
P0_P:float = 0 # Total to Static Pressure Ratio
|
|
105
107
|
Power_Type:PowerType
|
|
106
108
|
euler_power:float = 0
|
|
107
|
-
|
|
109
|
+
Reynolds:float = 0
|
|
110
|
+
|
|
108
111
|
# Used for loss calculations
|
|
109
112
|
_blade_to_blade_gap:float = 0.025 # Gap between blade in terms of percent chord.
|
|
110
113
|
|
|
@@ -117,8 +120,8 @@ class BladeRow:
|
|
|
117
120
|
_te_s:float = 0.08
|
|
118
121
|
_tip_clearance:float = 0 # Clearance as a percentage of span or blade height
|
|
119
122
|
|
|
120
|
-
_inlet_to_outlet_pratio = [0.06,0.
|
|
121
|
-
|
|
123
|
+
_inlet_to_outlet_pratio = [0.06,0.95]
|
|
124
|
+
|
|
122
125
|
@property
|
|
123
126
|
def inlet_to_outlet_pratio(self) -> Tuple[float,float]:
|
|
124
127
|
"""This is what is varied by the optimization.
|
|
@@ -432,7 +435,10 @@ class BladeRow:
|
|
|
432
435
|
val (float): new trailing edge to pitch ratio
|
|
433
436
|
"""
|
|
434
437
|
self._te_s = val
|
|
435
|
-
|
|
438
|
+
|
|
439
|
+
def __repr__(self):
|
|
440
|
+
return f"{self.row_type.name:0.2f}' P0:{np.mean(self.P0):0.2f} T0:{np.mean(self.T0):0.2f} P:{np.mean(self.P):0.2f} massflow:{self.total_massflow_no_coolant}:0.3f"
|
|
441
|
+
|
|
436
442
|
def to_dict(self):
|
|
437
443
|
|
|
438
444
|
data = {
|
|
@@ -471,6 +477,7 @@ class BladeRow:
|
|
|
471
477
|
"P":self.P.tolist(),
|
|
472
478
|
"T":self.T.tolist(),
|
|
473
479
|
"rho":self.rho.tolist(),
|
|
480
|
+
"mu":self.mu,
|
|
474
481
|
"Yp":self.Yp,
|
|
475
482
|
"Power":self.power,
|
|
476
483
|
"P0_P": self.P0_P,
|
|
@@ -479,7 +486,10 @@ class BladeRow:
|
|
|
479
486
|
"euler_power":self.euler_power,
|
|
480
487
|
"axial_chord":self.axial_chord,
|
|
481
488
|
"aspect_ratio":self.aspect_ratio,
|
|
482
|
-
"area": self.area
|
|
489
|
+
"area": self.area,
|
|
490
|
+
"mprime":self.mprime[-1],
|
|
491
|
+
"Reynolds":self.Reynolds,
|
|
492
|
+
"axial_chord":self.axial_chord
|
|
483
493
|
}
|
|
484
494
|
|
|
485
495
|
return data
|
turbodesign/passage.py
CHANGED
|
@@ -104,26 +104,42 @@ class Passage:
|
|
|
104
104
|
|
|
105
105
|
"""
|
|
106
106
|
phi = np.zeros(shape=x_streamline.shape)
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
107
|
+
r = np.zeros(shape=x_streamline.shape)
|
|
108
|
+
radius_curvature = np.zeros(shape=x_streamline.shape)
|
|
109
|
+
# Have to make sure there isn't a divide by zero which could happen if there is a vertical line somewhere
|
|
110
|
+
indices = np.where(np.abs(np.diff(x_streamline))>np.finfo(float).eps)[0]
|
|
111
|
+
|
|
112
|
+
d_dx = FinDiff(0,x_streamline[indices[0]:indices[-1]],1)
|
|
113
|
+
d2_dx2 = FinDiff(0,x_streamline[indices[0]:indices[-1]],2)
|
|
114
|
+
dr_dx = d_dx(r_streamline[indices[0]:indices[-1]])
|
|
115
|
+
d2r_dx2 = d2_dx2(r_streamline[indices[0]:indices[-1]])
|
|
114
116
|
|
|
115
|
-
radius_curvature = np.power((1+np.power(dr_dx,2)),1.5)
|
|
116
|
-
radius_curvature = np.divide(radius_curvature, np.abs(d2r_dx2))
|
|
117
|
+
radius_curvature[indices[0]:indices[-1]] = np.power((1+np.power(dr_dx,2)),1.5)
|
|
118
|
+
radius_curvature[indices[0]:indices[-1]] = np.divide(radius_curvature[indices[0]:indices[-1]], np.abs(d2r_dx2))
|
|
117
119
|
radius_curvature = np.nan_to_num(radius_curvature,nan=0)
|
|
118
120
|
|
|
121
|
+
def vertical_line_phi(start:int,end:int):
|
|
122
|
+
# Lets get the phi and slope for the vertical parts
|
|
123
|
+
for i in range(start,end):
|
|
124
|
+
dx = x_streamline[i] - x_streamline[i-1]
|
|
125
|
+
dr = r_streamline[i] - r_streamline[i-1]
|
|
126
|
+
if (dr < 0) & (np.abs(dx) < np.finfo(float).eps):
|
|
127
|
+
phi[i-1] = -np.pi/2
|
|
128
|
+
elif (dr > 0) & (np.abs(dx) < np.finfo(float).eps):
|
|
129
|
+
phi[i-1] = np.pi/2
|
|
130
|
+
radius_curvature[i-1] = 1000000 # Initialize to high number, used in radeq.
|
|
131
|
+
|
|
132
|
+
vertical_line_phi(1,indices[0])
|
|
133
|
+
vertical_line_phi(indices[1],len(x_streamline))
|
|
134
|
+
|
|
119
135
|
rm = radius_curvature # https://www.cuemath.com/radius-of-curvature-formula/ should be 1/curvature
|
|
120
|
-
phi = np.arctan(dr_dx)
|
|
136
|
+
phi[indices[0]:indices[-1]] = np.arctan(dr_dx)
|
|
121
137
|
r = r_streamline
|
|
122
138
|
|
|
123
139
|
return phi, rm, r
|
|
124
140
|
|
|
125
141
|
def get_cutting_line(self, t_hub:float) -> line2D:
|
|
126
|
-
"""Gets the cutting line
|
|
142
|
+
"""Gets the cutting line perpendicular to hub and shroud
|
|
127
143
|
|
|
128
144
|
Args:
|
|
129
145
|
t_hub (float): percentage along the axial direction
|
|
@@ -168,6 +184,27 @@ class Passage:
|
|
|
168
184
|
rshroud = self.rshroud(t_shroud)
|
|
169
185
|
return line2D([xhub,rhub],[xshroud,rshroud]), t_hub, t_shroud
|
|
170
186
|
|
|
187
|
+
def get_xr_slice(self,t_span:float,axial_location:float):
|
|
188
|
+
"""Returns the xr coordinates of a streamline, a line that is parallel to both hub and shroud
|
|
189
|
+
|
|
190
|
+
Args:
|
|
191
|
+
t_span (float): _description_
|
|
192
|
+
axial_location (float): _description_
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
np.NDArray: _description_
|
|
196
|
+
"""
|
|
197
|
+
t_hub = np.linspace(0,axial_location,100)
|
|
198
|
+
|
|
199
|
+
shroud_pts_cyl = np.vstack([self.xshroud(t_hub),self.rshroud(t_hub)]).transpose()
|
|
200
|
+
hub_pts_cyl = np.vstack([self.xhub(t_hub),self.rhub(t_hub)]).transpose()
|
|
201
|
+
n = len(t_hub)
|
|
202
|
+
|
|
203
|
+
xr = np.zeros((n,2))
|
|
204
|
+
for j in range(n):
|
|
205
|
+
l = line2D(hub_pts_cyl[j,:],shroud_pts_cyl[j,:])
|
|
206
|
+
xr[j,0],xr[j,1] = l.get_point(t_span)
|
|
207
|
+
return xr
|
|
171
208
|
|
|
172
209
|
@property
|
|
173
210
|
def hub_length(self):
|
turbodesign/radeq.py
CHANGED
|
@@ -52,12 +52,12 @@ def radeq(row:BladeRow,upstream:BladeRow) -> BladeRow:
|
|
|
52
52
|
gamma = row.gamma
|
|
53
53
|
|
|
54
54
|
# Solve the Radial Equlibrium
|
|
55
|
-
Vt = Vm*np.
|
|
55
|
+
Vt = Vm*np.tan(alpha)
|
|
56
56
|
Vr = Vm*np.sin(phi)
|
|
57
57
|
# Estimations
|
|
58
58
|
dVm_dr = float(interp1d(row_radius, np.gradient(row.Vm, row_radius))(r))
|
|
59
|
-
dVt_dr = dVm_dr*np.
|
|
60
|
-
dVr_dr =
|
|
59
|
+
dVt_dr = dVm_dr*np.tan(alpha)
|
|
60
|
+
dVr_dr = dVm_dr*np.sin(phi)
|
|
61
61
|
|
|
62
62
|
# Upstream
|
|
63
63
|
dT0up_dr = float(interp1d(upstream.percent_hub_shroud, np.gradient(upstream.T0,up_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0]))) # use percentage to get the T0 upstream value
|
|
@@ -74,7 +74,9 @@ def radeq(row:BladeRow,upstream:BladeRow) -> BladeRow:
|
|
|
74
74
|
# if row.loss_function.LossType == LossType.Pressure: # type: ignore
|
|
75
75
|
# dP0_dr = dP0up_dr-row.Yp*(dP0up_dr - dP_dr) # Eqn 9
|
|
76
76
|
|
|
77
|
-
C = Vm**2*(1+np.
|
|
77
|
+
C = Vm**2*(1+np.tan(alpha)**2)/(2*Cp*T0)
|
|
78
|
+
if (C>1) & ((gamma/(gamma-1))<2):
|
|
79
|
+
raise Exception("Invalid value of C {C}, change reduce alpha or Vm")
|
|
78
80
|
B = (1-C)**(gamma/(gamma-1))
|
|
79
81
|
A = P0 * gamma/(gamma-1) * (1-C)**(1/(gamma-1))
|
|
80
82
|
dVm_dr = 1/(2*Vm*A) * (rho*(Vt/r - Vm**2/rm * np.cos(phi) - Vr*dVr_dr) - dP0_dr*B) + 1/(2*T0) *dT0_dr # Eqn 6
|
turbodesign/spool.py
CHANGED
|
@@ -251,7 +251,7 @@ class Spool:
|
|
|
251
251
|
plt.figure(num=1,clear=True)
|
|
252
252
|
for i in range(1,len(self.blade_rows)-1): # Don't plot inlet or outlet
|
|
253
253
|
row = self.blade_rows[i]
|
|
254
|
-
x_end = x_start + row.
|
|
254
|
+
x_end = x_start + row.Vm.mean()
|
|
255
255
|
dx = x_end - x_start
|
|
256
256
|
|
|
257
257
|
Vt = row.Vt[j]
|
|
@@ -287,9 +287,15 @@ class Spool:
|
|
|
287
287
|
plt.annotate("", xy=(x_end,Wt+U), xytext=(x_end,Wt), arrowprops=prop) # U
|
|
288
288
|
plt.text(x_end+dx*0.1,Wt+U/2,"U",fontdict={"fontsize":"xx-large"})
|
|
289
289
|
|
|
290
|
-
|
|
291
|
-
|
|
290
|
+
if -np.sign(Vt)>0:
|
|
291
|
+
y = y_min
|
|
292
|
+
else:
|
|
293
|
+
y = y_max
|
|
294
|
+
plt.text((x_start+x_end)/2,-np.sign(Vt)*y*0.95,row.row_type.name,fontdict={"fontsize":"xx-large"})
|
|
295
|
+
x_start += row.Vm[j]
|
|
292
296
|
plt.axis([0,x_end+dx, y_min, y_max])
|
|
297
|
+
plt.ylabel("Tangental Velocity [m/s]")
|
|
298
|
+
plt.xlabel("Vm [m/s]")
|
|
293
299
|
plt.title(f"Velocity Triangles for Streamline {j}")
|
|
294
300
|
plt.savefig(f"streamline_{j:04d}.png",transparent=False,dpi=150)
|
|
295
301
|
|
turbodesign/td_math.py
CHANGED
|
@@ -5,6 +5,7 @@ import numpy.typing as npt
|
|
|
5
5
|
from .bladerow import BladeRow, compute_gas_constants
|
|
6
6
|
from .enums import RowType, LossType
|
|
7
7
|
from scipy.integrate import trapezoid
|
|
8
|
+
from .passage import Passage
|
|
8
9
|
|
|
9
10
|
def T0_coolant_weighted_average(row:BladeRow) -> float:
|
|
10
11
|
"""Calculate the new weighted Total Temperature array considering coolant
|
|
@@ -78,6 +79,33 @@ def compute_massflow(row:BladeRow) -> None:
|
|
|
78
79
|
row.calculated_massflow = massflow[-1]
|
|
79
80
|
row.area = total_area
|
|
80
81
|
|
|
82
|
+
def compute_reynolds(rows:List[BladeRow],passage:Passage):
|
|
83
|
+
"""Calculates the Reynolds Number
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
rows (List[BladeRow]): Blade row to calculate the Reynolds number
|
|
87
|
+
passage (Passage): Passage
|
|
88
|
+
"""
|
|
89
|
+
for row in rows:
|
|
90
|
+
xr = passage.get_xr_slice(0.5,row.axial_location)
|
|
91
|
+
dx = np.diff(xr[:,0])
|
|
92
|
+
dr = np.diff(xr[:,1])
|
|
93
|
+
c = np.sum(np.sqrt(dx**2+dr**2))
|
|
94
|
+
mp = [2/(xr[i,1]+xr[i-1,1])*np.sqrt(dr[i-1]**2 + dx[i-1]**2) for i in range(1,len(xr[:,1]))]
|
|
95
|
+
mp = np.hstack([[0],np.cumsum(mp)])
|
|
96
|
+
|
|
97
|
+
if row.row_type == RowType.Rotor:
|
|
98
|
+
V = row.W.mean()
|
|
99
|
+
else:
|
|
100
|
+
V = row.V.mean()
|
|
101
|
+
rho = row.rho.mean()
|
|
102
|
+
mu = row.mu
|
|
103
|
+
row.Reynolds = c*V*rho/mu
|
|
104
|
+
row.mprime = mp
|
|
105
|
+
row.axial_chord = c
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
81
109
|
def compute_power(row:BladeRow,upstream:BladeRow) -> None:
|
|
82
110
|
"""Calculates the power
|
|
83
111
|
|
|
@@ -169,56 +197,6 @@ def compute_quantities(row:BladeRow,upstream:BladeRow):
|
|
|
169
197
|
row.T0R = row.T + row.W**2 / (2*row.Cp)
|
|
170
198
|
row.P0R = row.P*(row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
171
199
|
|
|
172
|
-
def compute_quantities_power(row:BladeRow,upstream:BladeRow):
|
|
173
|
-
"""Calculation of all quantites after radial equilibrium has been solved assuming we know the power at the exit
|
|
174
|
-
|
|
175
|
-
Note:
|
|
176
|
-
Radial Equilibrium gives P0, T0, Vm. This code assumes the loss either enthalpy or pressure loss has already been calculated
|
|
177
|
-
|
|
178
|
-
compute_velocity has been called so we know W, Wt, V, Vt, U, M, M_rel
|
|
179
|
-
|
|
180
|
-
Static Pressure and Temperature should come from Total Temperature and Pressure + Velocity
|
|
181
|
-
|
|
182
|
-
Args:
|
|
183
|
-
row (BladeRow): current blade row. All quantities are at exit
|
|
184
|
-
upstream (BladeRow): upstream blade row. All quantities are at exit
|
|
185
|
-
|
|
186
|
-
"""
|
|
187
|
-
if row.row_type == RowType.Rotor:
|
|
188
|
-
Cp_avg = (row.Cp+upstream.Cp)/2
|
|
189
|
-
row.T0R = upstream.T0R - T0_coolant_weighted_average(row) - (upstream.U**2-row.U**2)/(2*Cp_avg)
|
|
190
|
-
|
|
191
|
-
# Factor in T0R_drop. Convert T0R drop to absolute terms
|
|
192
|
-
T_drop = (upstream.T0R - row.T0R) - row.W**2/(2*row.Cp) # row.T0R contains the drop
|
|
193
|
-
T0_drop = T_drop*(1+(row.gamma-1)/2*row.M**2)
|
|
194
|
-
|
|
195
|
-
# Adjust Total Temperature to match power
|
|
196
|
-
T0 = upstream.T0 - row.power/row.eta_total/(row.total_massflow*row.Cp) + T0_drop
|
|
197
|
-
|
|
198
|
-
if row.loss_function == LossType.Pressure:
|
|
199
|
-
row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
|
|
200
|
-
row.T0 = T0
|
|
201
|
-
row.T = row.T0/(1+(row.gamma-1)/2*row.M**2)
|
|
202
|
-
row.P = row.P0R*(row.T/row.T0R)**(row.gamma/(row.gamma-1))
|
|
203
|
-
|
|
204
|
-
elif row.loss_function == LossType.Enthalpy:
|
|
205
|
-
row.P0 = row.P*(T0/row.T)**(row.gamma/(row.gamma-1))
|
|
206
|
-
row.T = T0 - row.W**2/(2*row.Cp) + T_drop
|
|
207
|
-
row.T0 = T0
|
|
208
|
-
row.P = row.P0*(row.T0/row.T)**(row.gamma/(row.gamma-1))
|
|
209
|
-
row.P0R = row.P * (row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
210
|
-
|
|
211
|
-
elif row.row_type == RowType.Stator:
|
|
212
|
-
row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
|
|
213
|
-
if row.loss_function == LossType.Pressure:
|
|
214
|
-
row.P0 = upstream.P0 - row.Yp*(upstream.P0-row.P)
|
|
215
|
-
else:
|
|
216
|
-
row.P0 = upstream.P0
|
|
217
|
-
row.T = row.T0 * (1+(row.gamma-1)/2*row.M**2)
|
|
218
|
-
row.P = row.P0 * (row.T/row.T0)**((row.gamma)/(row.gamma-1))
|
|
219
|
-
row.T0R = row.T + row.W**2 / (2*row.Cp)
|
|
220
|
-
row.P0R = row.P*(row.T0R/row.T)**((row.gamma)/(row.gamma-1))
|
|
221
|
-
|
|
222
200
|
def stator_calc(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,calculate_vm:bool=True):
|
|
223
201
|
"""Given P0, T0, P, alpha2 of stator calculate all other quantities
|
|
224
202
|
|
|
@@ -255,21 +233,18 @@ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,calculat
|
|
|
255
233
|
row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
|
|
256
234
|
row.T = row.T0/T0_T
|
|
257
235
|
row.V = row.M*np.sqrt(row.gamma*row.R*row.T)
|
|
258
|
-
|
|
259
|
-
row.Vx = VV*np.cos(row.alpha2)
|
|
260
|
-
row.Vt = VV*np.sin(row.alpha2)
|
|
236
|
+
row.Vx = row.Vm*np.cos(row.phi)
|
|
261
237
|
row.Vr = row.V*np.sin(row.phi)
|
|
262
238
|
row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
|
|
263
|
-
|
|
239
|
+
row.Vt = row.Vm*np.tan(row.alpha2)
|
|
240
|
+
else: # We know Vm, P0, T0, P
|
|
264
241
|
row.Vx = row.Vm*np.cos(row.phi)
|
|
265
242
|
row.Vr = row.Vm*np.sin(row.phi)
|
|
266
|
-
row.Vt = row.Vm*np.
|
|
243
|
+
row.Vt = row.Vm*np.tan(row.alpha2)
|
|
267
244
|
row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
row.P = row.P0 *(1/T0_T)**(row.gamma/(row.gamma-1))
|
|
272
|
-
row.T = row.T0 * (1/T0_T)
|
|
245
|
+
|
|
246
|
+
row.T = row.P/(row.R*row.rho) # We know P, this is a guess
|
|
247
|
+
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
273
248
|
|
|
274
249
|
if upstream.row_type == RowType.Rotor:
|
|
275
250
|
row.alpha1 = upstream.alpha2 # Upstream rotor absolute frame flow angle
|
|
@@ -297,13 +272,14 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
|
|
|
297
272
|
upstream.U = upstream.rpm*np.pi/30 * upstream_radius # rad/s
|
|
298
273
|
upstream.Wt = upstream.Vt - upstream.U
|
|
299
274
|
upstream.W = np.sqrt(upstream.Vx**2 + upstream.Wt**2 + upstream.Vr**2)
|
|
300
|
-
upstream.beta2 = np.arctan2(upstream.Wt,upstream.
|
|
275
|
+
upstream.beta2 = np.arctan2(upstream.Wt,upstream.Vm)
|
|
301
276
|
upstream.T0R = upstream.T+upstream.W**2/(2*upstream.Cp)
|
|
302
277
|
upstream.P0R = upstream.P * (upstream.T0R/upstream.T)**((upstream.gamma)/(upstream.gamma-1))
|
|
303
278
|
upstream.M_rel = upstream.W/np.sqrt(upstream.gamma*upstream.R*upstream.T)
|
|
304
279
|
|
|
305
280
|
upstream_rothalpy = upstream.T0R*upstream.Cp - 0.5*upstream.U**2 # H01R - 1/2 U1^2
|
|
306
|
-
|
|
281
|
+
if np.any(upstream_rothalpy < 0):
|
|
282
|
+
print('U is too high, reduce RPM or radius')
|
|
307
283
|
# Rotor Exit Calculations
|
|
308
284
|
row.beta1 = upstream.beta2
|
|
309
285
|
#row.Yp # Evaluated earlier
|
|
@@ -320,26 +296,26 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
|
|
|
320
296
|
# Need to adjust T
|
|
321
297
|
print(f'nan detected: check flow path. Turbine inlet cut should be horizontal')
|
|
322
298
|
row.Vr = row.W*np.sin(row.phi)
|
|
323
|
-
row.
|
|
299
|
+
row.Vm = row.W*np.cos(row.beta2)
|
|
300
|
+
row.Wt = row.W*np.sin(row.beta2)
|
|
301
|
+
row.Vx = row.Vm*np.cos(row.phi)
|
|
324
302
|
row.Vt = row.Wt + row.U
|
|
325
|
-
ww = row.W*np.cos(row.phi)
|
|
326
|
-
row.Vx = ww*np.cos(row.beta2)
|
|
327
303
|
row.V = np.sqrt(row.Vr**2+row.Vt**2+row.Vx**2)
|
|
328
304
|
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
329
305
|
row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
|
|
330
306
|
row.T0 = row.T + row.V**2/(2*row.Cp)
|
|
331
307
|
row.P0 = row.P*(row.T0/row.T)**(row.gamma/(row.gamma-1))
|
|
332
|
-
row.alpha2 = np.arctan2(row.Vt,row.
|
|
308
|
+
row.alpha2 = np.arctan2(row.Vt,row.Vm)
|
|
333
309
|
else: # We know Vm, P0, T0
|
|
334
310
|
row.Vr = row.Vm*np.sin(row.phi)
|
|
335
311
|
row.Vx = row.Vm*np.cos(row.phi)
|
|
336
312
|
|
|
337
313
|
row.W = np.sqrt(2*row.Cp*(row.T0R-row.T))
|
|
338
|
-
row.Wt =
|
|
314
|
+
row.Wt = row.W*np.sin(row.beta2)
|
|
339
315
|
row.U = row.omega * row.r
|
|
340
316
|
row.Vt = row.Wt+row.U
|
|
341
317
|
|
|
342
|
-
row.alpha2 = np.arctan2(row.Vt,row.
|
|
318
|
+
row.alpha2 = np.arctan2(row.Vt,row.Vm)
|
|
343
319
|
row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
|
|
344
320
|
|
|
345
321
|
row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
|
|
@@ -366,7 +342,7 @@ def inlet_calc(row:BladeRow):
|
|
|
366
342
|
row.P = row.P0
|
|
367
343
|
row.rho = row.P/(row.T*row.R)
|
|
368
344
|
total_area = 0
|
|
369
|
-
for
|
|
345
|
+
for iter in range(5): # Lets converge the Mach and Total and Static pressures
|
|
370
346
|
for j in range(1,len(row.percent_hub_shroud)):
|
|
371
347
|
rho = row.rho[j]
|
|
372
348
|
tube_massflow = row.massflow[j]-row.massflow[j-1]
|
|
@@ -380,6 +356,11 @@ def inlet_calc(row:BladeRow):
|
|
|
380
356
|
area[j] = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
|
|
381
357
|
total_area += area[j]
|
|
382
358
|
row.Vm[j] = tube_massflow/(rho*area[j])
|
|
359
|
+
avg_mach = np.mean(row.M)
|
|
360
|
+
if np.mean(row.M)>0.5:
|
|
361
|
+
print(f"High inlet mach can lead to errors iter:{iter} Mach:{avg_mach}")
|
|
362
|
+
if np.mean(row.M)<0.01:
|
|
363
|
+
print(f"Unusually slow flow:{iter} Mach:{avg_mach}")
|
|
383
364
|
row.Vm[0] = 1/(len(row.Vm)-1)*row.Vm[1:].sum() # Initialize the value at the hub to not upset the mean
|
|
384
365
|
row.Vr = row.Vm*np.sin(row.phi)
|
|
385
366
|
row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
|
turbodesign/turbinespool.py
CHANGED
|
@@ -8,7 +8,7 @@ from .passage import Passage
|
|
|
8
8
|
from scipy.interpolate import interp1d
|
|
9
9
|
import numpy as np
|
|
10
10
|
import numpy.typing as npt
|
|
11
|
-
from .td_math import inlet_calc,rotor_calc, stator_calc, compute_massflow, compute_power, compute_gas_constants
|
|
11
|
+
from .td_math import inlet_calc,rotor_calc, stator_calc, compute_massflow, compute_power, compute_gas_constants, compute_reynolds
|
|
12
12
|
from .solve_radeq import adjust_streamlines, radeq
|
|
13
13
|
from scipy.optimize import minimize_scalar, minimize, fmin_slsqp
|
|
14
14
|
from .inlet import Inlet
|
|
@@ -136,7 +136,7 @@ class TurbineSpool(Spool):
|
|
|
136
136
|
self.__match_massflow()
|
|
137
137
|
elif self.massflow_constraint == MassflowConstraint.BalanceMassFlow:
|
|
138
138
|
self.__balance_massflow()
|
|
139
|
-
|
|
139
|
+
|
|
140
140
|
|
|
141
141
|
def __match_massflow(self):
|
|
142
142
|
""" Matches the massflow between streamtubes by changing exit angles. Doesn't use radial equilibrium.
|
|
@@ -171,10 +171,11 @@ class TurbineSpool(Spool):
|
|
|
171
171
|
row.alpha2[0] = 1/(len(row.alpha2)-1)*row.alpha2[1:].sum()
|
|
172
172
|
upstream = compute_gas_constants(upstream)
|
|
173
173
|
row = compute_gas_constants(row)
|
|
174
|
-
|
|
174
|
+
|
|
175
175
|
|
|
176
176
|
# Step 3: Adjust streamlines to evenly divide massflow
|
|
177
177
|
adjust_streamlines(self.blade_rows,self.passage)
|
|
178
|
+
compute_reynolds(self.blade_rows,self.passage)
|
|
178
179
|
|
|
179
180
|
def __balance_massflow(self):
|
|
180
181
|
""" Balances the massflow between rows. Use radial equilibrium.
|
|
@@ -206,7 +207,9 @@ class TurbineSpool(Spool):
|
|
|
206
207
|
sign*=-1
|
|
207
208
|
if len(stage_ids) % 2 == 1:
|
|
208
209
|
massflow_stage.append(massflow_stage[-1]*sign)
|
|
209
|
-
|
|
210
|
+
deviation = np.std(total_massflow)*2
|
|
211
|
+
if deviation>1.0:
|
|
212
|
+
print("high massflow deviation detected")
|
|
210
213
|
return np.std(total_massflow)*2 # + abs(sum(massflow_stage)) # Equation 28
|
|
211
214
|
|
|
212
215
|
# Balance the massflow between Stages
|
|
@@ -229,27 +232,27 @@ class TurbineSpool(Spool):
|
|
|
229
232
|
Returns:
|
|
230
233
|
_type_: _description_
|
|
231
234
|
"""
|
|
232
|
-
try:
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
for i in range(1,len(blade_rows)-1):
|
|
237
|
-
blade_rows[i].P[j] = Ps_range[i-1]
|
|
238
|
-
blade_rows[-1].P = P
|
|
239
|
-
else:
|
|
235
|
+
# try:
|
|
236
|
+
if balance_mean_pressure:
|
|
237
|
+
for j in range(self.num_streamlines):
|
|
238
|
+
Ps_range = outlet_pressure(x0,P0[j],P[j])
|
|
240
239
|
for i in range(1,len(blade_rows)-1):
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
calculate_massflows(blade_rows,True)
|
|
245
|
-
print(x0)
|
|
246
|
-
return calculate_error(blade_rows)
|
|
247
|
-
except:
|
|
240
|
+
blade_rows[i].P[j] = Ps_range[i-1]
|
|
241
|
+
blade_rows[-1].P = P
|
|
242
|
+
else:
|
|
248
243
|
for i in range(1,len(blade_rows)-1):
|
|
249
244
|
for j in range(self.num_streamlines):
|
|
250
|
-
blade_rows[i].P[j] = P[j]
|
|
251
|
-
|
|
252
|
-
|
|
245
|
+
blade_rows[i].P[j] = P[j]*x0[(i-1)*self.num_streamlines+j] # x0 size = num_streamlines -1
|
|
246
|
+
|
|
247
|
+
calculate_massflows(blade_rows,True)
|
|
248
|
+
print(x0)
|
|
249
|
+
return calculate_error(blade_rows)
|
|
250
|
+
# except:
|
|
251
|
+
# for i in range(1,len(blade_rows)-1):
|
|
252
|
+
# for j in range(self.num_streamlines):
|
|
253
|
+
# blade_rows[i].P[j] = P[j]
|
|
254
|
+
# calculate_massflows(blade_rows,True)
|
|
255
|
+
# return 10
|
|
253
256
|
# Break apart the rows to stages
|
|
254
257
|
outlet_P=list(); outlet_P_guess = list() # Outlet P is the bounds, outlet_p_guess is the guessed values
|
|
255
258
|
|
|
@@ -282,7 +285,10 @@ class TurbineSpool(Spool):
|
|
|
282
285
|
self.blade_rows[-1].P = self.blade_rows[-1].get_static_pressure(self.blade_rows[-1].percent_hub_shroud)
|
|
283
286
|
err = calculate_error(self.blade_rows[:-1])
|
|
284
287
|
print(f"Massflow convergenced error:{err}")
|
|
285
|
-
|
|
288
|
+
|
|
289
|
+
# calculate Reynolds number
|
|
290
|
+
compute_reynolds(self.blade_rows,self.passage)
|
|
291
|
+
|
|
286
292
|
# finetune = True
|
|
287
293
|
# if finetune:
|
|
288
294
|
# print('Finetune static pressure between stages')
|
|
@@ -451,7 +457,7 @@ def calculate_massflows(blade_rows:List[BladeRow],calculate_vm:bool=False):
|
|
|
451
457
|
row = compute_gas_constants(row)
|
|
452
458
|
compute_massflow(row)
|
|
453
459
|
compute_power(row,upstream)
|
|
454
|
-
|
|
460
|
+
|
|
455
461
|
def massflow_loss_function(exit_angle:float,index:int,row:BladeRow,upstream:BladeRow,downstream:BladeRow=None):
|
|
456
462
|
"""Finds the blade exit angles that balance the massflow throughout the stage
|
|
457
463
|
|
|
@@ -534,11 +540,11 @@ def outlet_pressure(percents:List[float],inletP0:float,outletP:float) -> npt.NDA
|
|
|
534
540
|
maxP = inletP0
|
|
535
541
|
minP = outletP
|
|
536
542
|
if isinstance(percents, float):
|
|
537
|
-
Ps = [percents*(minP
|
|
543
|
+
Ps = [percents*(maxP-minP)+minP]
|
|
538
544
|
else:
|
|
539
545
|
Ps = np.zeros(shape=(len(percents),1)); i = 0
|
|
540
546
|
for p in percents:
|
|
541
|
-
Ps[i] = p*(minP
|
|
547
|
+
Ps[i] = p*(maxP - minP) + minP
|
|
542
548
|
maxP = Ps[i]
|
|
543
549
|
i+=1
|
|
544
550
|
return Ps
|
|
File without changes
|