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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: turbo-design
3
- Version: 1.0.11
3
+ Version: 1.1.1
4
4
  Summary: TurboDesign is a library used to design turbines and compressors using radial equilibrium.
5
5
  Author: Paht Juangphanich
6
6
  Author-email: paht.juangphanich@nasa.gov
@@ -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=F94KAARzGd4edOgs2Cd6dSRteyYCR5U45687anYY-As,24042
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=LVX9Ige4WwvSUrLH4G2Crw0DyWEiDK81a66_iNEa05Y,7597
24
- turbodesign/radeq.py,sha256=4unpFad6hvgaYwYLBNI8o9bszyvoNiizYjMqRX0Mjcc,10012
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=x0o1tQKxOlV2ZRtdx-I24FidnEDpdrvjCThahd9s0Sc,13960
27
+ turbodesign/spool.py,sha256=h4m--HR5g8o01Kkhl4Mbvx7WORQez3YpncDE7XxbNaw,14172
28
28
  turbodesign/stage.py,sha256=UP45sDKDLsAkO_WfDWJ6kqXU7cYKh_4QO01QZnSN1oQ,166
29
- turbodesign/td_math.py,sha256=10nPvAnLdXq8cRG8k27rZZ3fUew8hJkeBsbX4EA2xG0,17396
30
- turbodesign/turbinespool.py,sha256=s9cZpK9245pjUYI5hOo81SsO8UqG-TVUJlSN7SQu7yw,25877
31
- turbo_design-1.0.11.dist-info/METADATA,sha256=jha8nSuwVylWqH-ShW2qXTiFIYb5NAeiGOzvtS8_l4M,735
32
- turbo_design-1.0.11.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
33
- turbo_design-1.0.11.dist-info/RECORD,,
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.7]
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
- rm = phi.copy()
108
- r = phi.copy()
109
-
110
- d_dx = FinDiff(0,x_streamline,1)
111
- d2_dx2 = FinDiff(0,x_streamline,2)
112
- dr_dx = d_dx(r_streamline)
113
- d2r_dx2 = d2_dx2(r_streamline)
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 between hub and shroud
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.cos(phi)*np.tan(alpha)
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.cos(phi)*np.tan(alpha)
60
- dVr_dr = 0 #dVm_dr*np.sin(phi)
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.cos(phi)**2 * np.tan(alpha)**2)/(2*Cp*T0)
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.Vx.mean()
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
- plt.text((x_start+x_end)/2,-y_max*0.95,row.row_type.name,fontdict={"fontsize":"xx-large"})
291
- x_start += row.Vx[j]
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
- VV = row.V*np.cos(row.phi)
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
- else: # We know Vm, P0, T0
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.cos(row.phi)*np.tan(row.alpha2)
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
- for _ in range(3): # Mach is a function of T and T is a function of Mach
269
- row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
270
- T0_T = (1+(row.gamma-1)/2 * row.M**2)
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.Vx)
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.Wt = np.sqrt(row.W**2 - row.Vr**2) * np.sin(row.beta2)
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.Vx)
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 = np.sqrt(row.W**2 - row.Vr**2) * np.sin(row.beta2)
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.Vx)
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 _ in range(5): # Lets converge the Mach and Total and Static pressures
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)
@@ -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
- if balance_mean_pressure:
234
- for j in range(self.num_streamlines):
235
- Ps_range = outlet_pressure(x0,P0[j],P[j])
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
- for j in range(self.num_streamlines):
242
- blade_rows[i].P[j] = P[j]*x0[(i-1)*self.num_streamlines+j] # x0 size = num_streamlines -1
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
- calculate_massflows(blade_rows,True)
252
- return 10
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-maxP)+maxP]
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-maxP)+maxP
547
+ Ps[i] = p*(maxP - minP) + minP
542
548
  maxP = Ps[i]
543
549
  i+=1
544
550
  return Ps