turbo-design 1.0.11__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of turbo-design might be problematic. Click here for more details.

Files changed (32) hide show
  1. {turbo_design-1.0.11 → turbo_design-1.1.0}/PKG-INFO +1 -1
  2. {turbo_design-1.0.11 → turbo_design-1.1.0}/pyproject.toml +1 -1
  3. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/bladerow.py +5 -2
  4. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/passage.py +48 -11
  5. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/radeq.py +6 -4
  6. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/spool.py +9 -3
  7. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/td_math.py +22 -19
  8. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/turbinespool.py +23 -21
  9. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/__init__.py +0 -0
  10. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/arrayfuncs.py +0 -0
  11. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/cantera_gas/co2.yaml +0 -0
  12. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/compressorspool.py +0 -0
  13. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/coolant.py +0 -0
  14. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/enums.py +0 -0
  15. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/inlet.py +0 -0
  16. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/isentropic.py +0 -0
  17. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/__init__.py +0 -0
  18. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/compressor/__init__.py +0 -0
  19. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/losstype.py +0 -0
  20. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/TD2.py +0 -0
  21. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/__init__.py +0 -0
  22. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/ainleymathieson.py +0 -0
  23. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/craigcox.py +0 -0
  24. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/fixedefficiency.py +0 -0
  25. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/fixedpressureloss.py +0 -0
  26. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/kackerokapuu.py +0 -0
  27. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/loss/turbine/traupel.py +0 -0
  28. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/lossinterp.py +0 -0
  29. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/outlet.py +0 -0
  30. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/rotor.py +0 -0
  31. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/solve_radeq.py +0 -0
  32. {turbo_design-1.0.11 → turbo_design-1.1.0}/turbodesign/stage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: turbo-design
3
- Version: 1.0.11
3
+ Version: 1.1.0
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
  [tool.poetry]
2
2
  name = "turbo-design"
3
- version = "1.0.11"
3
+ version = "1.1.0"
4
4
  description = "TurboDesign is a library used to design turbines and compressors using radial equilibrium."
5
5
  authors = ["Paht Juangphanich <paht.juangphanich@nasa.gov>"]
6
6
  packages = [
@@ -117,7 +117,7 @@ class BladeRow:
117
117
  _te_s:float = 0.08
118
118
  _tip_clearance:float = 0 # Clearance as a percentage of span or blade height
119
119
 
120
- _inlet_to_outlet_pratio = [0.06,0.7]
120
+ _inlet_to_outlet_pratio = [0.06,0.95]
121
121
 
122
122
  @property
123
123
  def inlet_to_outlet_pratio(self) -> Tuple[float,float]:
@@ -432,7 +432,10 @@ class BladeRow:
432
432
  val (float): new trailing edge to pitch ratio
433
433
  """
434
434
  self._te_s = val
435
-
435
+
436
+ def __repr__(self):
437
+ 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"
438
+
436
439
  def to_dict(self):
437
440
 
438
441
  data = {
@@ -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):
@@ -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
@@ -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
 
@@ -255,21 +255,18 @@ def stator_calc(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,calculat
255
255
  row.T0 = upstream.T0 - T0_coolant_weighted_average(row)
256
256
  row.T = row.T0/T0_T
257
257
  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)
258
+ row.Vx = row.Vm*np.cos(row.phi)
261
259
  row.Vr = row.V*np.sin(row.phi)
262
260
  row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
263
- else: # We know Vm, P0, T0
261
+ row.Vt = row.Vm*np.tan(row.alpha2)
262
+ else: # We know Vm, P0, T0, P
264
263
  row.Vx = row.Vm*np.cos(row.phi)
265
264
  row.Vr = row.Vm*np.sin(row.phi)
266
- row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
265
+ row.Vt = row.Vm*np.tan(row.alpha2)
267
266
  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)
267
+
268
+ row.T = row.P/(row.R*row.rho) # We know P, this is a guess
269
+ row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
273
270
 
274
271
  if upstream.row_type == RowType.Rotor:
275
272
  row.alpha1 = upstream.alpha2 # Upstream rotor absolute frame flow angle
@@ -297,13 +294,14 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
297
294
  upstream.U = upstream.rpm*np.pi/30 * upstream_radius # rad/s
298
295
  upstream.Wt = upstream.Vt - upstream.U
299
296
  upstream.W = np.sqrt(upstream.Vx**2 + upstream.Wt**2 + upstream.Vr**2)
300
- upstream.beta2 = np.arctan2(upstream.Wt,upstream.Vx)
297
+ upstream.beta2 = np.arctan2(upstream.Wt,upstream.Vm)
301
298
  upstream.T0R = upstream.T+upstream.W**2/(2*upstream.Cp)
302
299
  upstream.P0R = upstream.P * (upstream.T0R/upstream.T)**((upstream.gamma)/(upstream.gamma-1))
303
300
  upstream.M_rel = upstream.W/np.sqrt(upstream.gamma*upstream.R*upstream.T)
304
301
 
305
302
  upstream_rothalpy = upstream.T0R*upstream.Cp - 0.5*upstream.U**2 # H01R - 1/2 U1^2
306
-
303
+ if np.any(upstream_rothalpy < 0):
304
+ print('U is too high, reduce RPM or radius')
307
305
  # Rotor Exit Calculations
308
306
  row.beta1 = upstream.beta2
309
307
  #row.Yp # Evaluated earlier
@@ -320,26 +318,26 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
320
318
  # Need to adjust T
321
319
  print(f'nan detected: check flow path. Turbine inlet cut should be horizontal')
322
320
  row.Vr = row.W*np.sin(row.phi)
323
- row.Wt = np.sqrt(row.W**2 - row.Vr**2) * np.sin(row.beta2)
321
+ row.Vm = row.W*np.cos(row.beta2)
322
+ row.Wt = row.W*np.sin(row.beta2)
323
+ row.Vx = row.Vm*np.cos(row.phi)
324
324
  row.Vt = row.Wt + row.U
325
- ww = row.W*np.cos(row.phi)
326
- row.Vx = ww*np.cos(row.beta2)
327
325
  row.V = np.sqrt(row.Vr**2+row.Vt**2+row.Vx**2)
328
326
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
329
327
  row.Vm = np.sqrt(row.Vx**2+row.Vr**2)
330
328
  row.T0 = row.T + row.V**2/(2*row.Cp)
331
329
  row.P0 = row.P*(row.T0/row.T)**(row.gamma/(row.gamma-1))
332
- row.alpha2 = np.arctan2(row.Vt,row.Vx)
330
+ row.alpha2 = np.arctan2(row.Vt,row.Vm)
333
331
  else: # We know Vm, P0, T0
334
332
  row.Vr = row.Vm*np.sin(row.phi)
335
333
  row.Vx = row.Vm*np.cos(row.phi)
336
334
 
337
335
  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)
336
+ row.Wt = row.W*np.sin(row.beta2)
339
337
  row.U = row.omega * row.r
340
338
  row.Vt = row.Wt+row.U
341
339
 
342
- row.alpha2 = np.arctan2(row.Vt,row.Vx)
340
+ row.alpha2 = np.arctan2(row.Vt,row.Vm)
343
341
  row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
344
342
 
345
343
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
@@ -366,7 +364,7 @@ def inlet_calc(row:BladeRow):
366
364
  row.P = row.P0
367
365
  row.rho = row.P/(row.T*row.R)
368
366
  total_area = 0
369
- for _ in range(5): # Lets converge the Mach and Total and Static pressures
367
+ for iter in range(5): # Lets converge the Mach and Total and Static pressures
370
368
  for j in range(1,len(row.percent_hub_shroud)):
371
369
  rho = row.rho[j]
372
370
  tube_massflow = row.massflow[j]-row.massflow[j-1]
@@ -380,6 +378,11 @@ def inlet_calc(row:BladeRow):
380
378
  area[j] = 2*np.pi*C*(S/2*dx**2+row.r[j-1]*dx)
381
379
  total_area += area[j]
382
380
  row.Vm[j] = tube_massflow/(rho*area[j])
381
+ avg_mach = np.mean(row.M)
382
+ if np.mean(row.M)>0.5:
383
+ print(f"High inlet mach can lead to errors iter:{iter} Mach:{avg_mach}")
384
+ if np.mean(row.M)<0.01:
385
+ print(f"Unusually slow flow:{iter} Mach:{avg_mach}")
383
386
  row.Vm[0] = 1/(len(row.Vm)-1)*row.Vm[1:].sum() # Initialize the value at the hub to not upset the mean
384
387
  row.Vr = row.Vm*np.sin(row.phi)
385
388
  row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
@@ -206,7 +206,9 @@ class TurbineSpool(Spool):
206
206
  sign*=-1
207
207
  if len(stage_ids) % 2 == 1:
208
208
  massflow_stage.append(massflow_stage[-1]*sign)
209
-
209
+ deviation = np.std(total_massflow)*2
210
+ if deviation>1.0:
211
+ print("high massflow deviation detected")
210
212
  return np.std(total_massflow)*2 # + abs(sum(massflow_stage)) # Equation 28
211
213
 
212
214
  # Balance the massflow between Stages
@@ -229,27 +231,27 @@ class TurbineSpool(Spool):
229
231
  Returns:
230
232
  _type_: _description_
231
233
  """
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:
234
+ # try:
235
+ if balance_mean_pressure:
236
+ for j in range(self.num_streamlines):
237
+ Ps_range = outlet_pressure(x0,P0[j],P[j])
240
238
  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:
239
+ blade_rows[i].P[j] = Ps_range[i-1]
240
+ blade_rows[-1].P = P
241
+ else:
248
242
  for i in range(1,len(blade_rows)-1):
249
243
  for j in range(self.num_streamlines):
250
- blade_rows[i].P[j] = P[j]
251
- calculate_massflows(blade_rows,True)
252
- return 10
244
+ blade_rows[i].P[j] = P[j]*x0[(i-1)*self.num_streamlines+j] # x0 size = num_streamlines -1
245
+
246
+ calculate_massflows(blade_rows,True)
247
+ print(x0)
248
+ return calculate_error(blade_rows)
249
+ # except:
250
+ # for i in range(1,len(blade_rows)-1):
251
+ # for j in range(self.num_streamlines):
252
+ # blade_rows[i].P[j] = P[j]
253
+ # calculate_massflows(blade_rows,True)
254
+ # return 10
253
255
  # Break apart the rows to stages
254
256
  outlet_P=list(); outlet_P_guess = list() # Outlet P is the bounds, outlet_p_guess is the guessed values
255
257
 
@@ -534,11 +536,11 @@ def outlet_pressure(percents:List[float],inletP0:float,outletP:float) -> npt.NDA
534
536
  maxP = inletP0
535
537
  minP = outletP
536
538
  if isinstance(percents, float):
537
- Ps = [percents*(minP-maxP)+maxP]
539
+ Ps = [percents*(maxP-minP)+minP]
538
540
  else:
539
541
  Ps = np.zeros(shape=(len(percents),1)); i = 0
540
542
  for p in percents:
541
- Ps[i] = p*(minP-maxP)+maxP
543
+ Ps[i] = p*(maxP - minP) + minP
542
544
  maxP = Ps[i]
543
545
  i+=1
544
546
  return Ps