turbo-design 1.3.0__tar.gz → 1.3.2__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 (55) hide show
  1. {turbo_design-1.3.0 → turbo_design-1.3.2}/PKG-INFO +1 -1
  2. {turbo_design-1.3.0 → turbo_design-1.3.2}/pyproject.toml +1 -1
  3. turbo_design-1.3.2/turbodesign/.DS_Store +0 -0
  4. turbo_design-1.3.2/turbodesign/loss/.DS_Store +0 -0
  5. turbo_design-1.3.2/turbodesign/loss/__pycache__/__init__.cpython-310.pyc +0 -0
  6. turbo_design-1.3.2/turbodesign/loss/__pycache__/__init__.cpython-312.pyc +0 -0
  7. turbo_design-1.3.2/turbodesign/loss/__pycache__/losstype.cpython-310.pyc +0 -0
  8. turbo_design-1.3.2/turbodesign/loss/__pycache__/losstype.cpython-312.pyc +0 -0
  9. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/TD2.cpython-310.pyc +0 -0
  10. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/TD2.cpython-312.pyc +0 -0
  11. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/__init__.cpython-310.pyc +0 -0
  12. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/__init__.cpython-312.pyc +0 -0
  13. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/ainleymathieson.cpython-310.pyc +0 -0
  14. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/ainleymathieson.cpython-312.pyc +0 -0
  15. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/craigcox.cpython-310.pyc +0 -0
  16. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/craigcox.cpython-312.pyc +0 -0
  17. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/fixedefficiency.cpython-310.pyc +0 -0
  18. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/fixedefficiency.cpython-312.pyc +0 -0
  19. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/fixedpressureloss.cpython-310.pyc +0 -0
  20. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/fixedpressureloss.cpython-312.pyc +0 -0
  21. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/kackerokapuu.cpython-310.pyc +0 -0
  22. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/kackerokapuu.cpython-312.pyc +0 -0
  23. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/traupel.cpython-310.pyc +0 -0
  24. turbo_design-1.3.2/turbodesign/loss/turbine/__pycache__/traupel.cpython-312.pyc +0 -0
  25. turbo_design-1.3.2/turbodesign/radeq.py +160 -0
  26. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/solve_radeq.py +1 -1
  27. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/td_math.py +3 -3
  28. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/turbinespool.py +68 -70
  29. turbo_design-1.3.0/turbodesign/radeq.py +0 -286
  30. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/__init__.py +0 -0
  31. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/arrayfuncs.py +0 -0
  32. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/bladerow.py +0 -0
  33. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/cantera_gas/co2.yaml +0 -0
  34. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/compressorspool.py +0 -0
  35. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/coolant.py +0 -0
  36. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/enums.py +0 -0
  37. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/inlet.py +0 -0
  38. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/isentropic.py +0 -0
  39. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/__init__.py +0 -0
  40. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/compressor/__init__.py +0 -0
  41. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/losstype.py +0 -0
  42. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/TD2.py +0 -0
  43. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/__init__.py +0 -0
  44. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/ainleymathieson.py +0 -0
  45. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/craigcox.py +0 -0
  46. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/fixedefficiency.py +0 -0
  47. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/fixedpressureloss.py +0 -0
  48. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/kackerokapuu.py +0 -0
  49. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/loss/turbine/traupel.py +0 -0
  50. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/lossinterp.py +0 -0
  51. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/outlet.py +0 -0
  52. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/passage.py +0 -0
  53. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/rotor.py +0 -0
  54. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/spool.py +0 -0
  55. {turbo_design-1.3.0 → turbo_design-1.3.2}/turbodesign/stage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: turbo-design
3
- Version: 1.3.0
3
+ Version: 1.3.2
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.3.0"
3
+ version = "1.3.2"
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 = [
Binary file
@@ -0,0 +1,160 @@
1
+ from scipy.interpolate import interp1d,PchipInterpolator
2
+ from scipy.integrate import solve_ivp
3
+ import numpy as np
4
+ import numdifftools as nd
5
+ from .bladerow import BladeRow
6
+ from .enums import RowType
7
+ import math
8
+
9
+ def radeq(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None) -> BladeRow:
10
+ """Solves the radial equilibrium equation for axial machines and returns the convergence.
11
+
12
+ Note:
13
+ This function will give you T0, P0, Vm as a function of the radius.
14
+
15
+ Args:
16
+ row (BladeRow): Current row
17
+ upstream (BladeRow): Previous row
18
+ downstream (BladeRow): Next row
19
+
20
+ Returns:
21
+ BladeRow: current row with T0, P0, and Vm calculated
22
+ """
23
+ row_radius = row.r # Use these for gradient
24
+ up_radius = upstream.r
25
+
26
+ def ode_radeq_streamtube(r:np.ndarray,y:np.ndarray):
27
+ """Solves the radial equilibrium equation for a streamtube
28
+
29
+ Args:
30
+ r (np.ndarray): radius not as a percent
31
+ y (np.ndarray): Array containing [P0,Vt,VtU,T0]
32
+
33
+ """
34
+ P0 = y[0]
35
+ T0 = y[1]
36
+ Vm = y[2]
37
+ r = row.r.mean()+r
38
+ if r>row_radius[-1]:
39
+ return [0,0,0]
40
+ elif r<row_radius[0]:
41
+ return [0,0,0]
42
+
43
+ Cp = row.Cp
44
+ phi = interp1d(row_radius, row.phi)(r)
45
+ alpha = interp1d(row_radius, row.alpha2)(r)
46
+ T = interp1d(row_radius, row.T)(r)
47
+ P = interp1d(row_radius, row.P)(r)
48
+ rm = interp1d(row_radius, row.rm)(r)
49
+ rho = row.rho.mean()
50
+
51
+ if (row.row_type == RowType.Rotor):
52
+ omega = row.rpm*np.pi/30
53
+ U = omega*r
54
+ else:
55
+ omega = 0
56
+ U = 0
57
+ gamma = row.gamma
58
+
59
+ # Solve the Radial Equlibrium
60
+ Vt = Vm*np.tan(alpha)
61
+ Vr = Vm*np.sin(phi)
62
+ # Estimations
63
+ dVm_dr = float(interp1d(row_radius, np.gradient(row.Vm, row_radius))(r))
64
+ up_Vm = interp1d(row_radius, upstream.Vm)(r)
65
+
66
+ if downstream:
67
+ if downstream.row_type == RowType.Outlet:
68
+ down_Vm = Vm
69
+ else:
70
+ down_Vm = interp1d(row_radius, downstream.Vm)(r)
71
+ else:
72
+ down_Vm = Vm
73
+ up_m = interp1d(row_radius, upstream.m)(r)
74
+
75
+ # Get a rough guess of dVm/dm
76
+ if downstream!=None:
77
+ down_m = interp1d(row_radius, downstream.m)(r)
78
+ row_m = interp1d(row_radius, row.m)(r)
79
+ if down_m != row_m:
80
+ func_Vm_m = PchipInterpolator([up_m, row_m, down_m],[up_Vm, Vm, down_Vm])
81
+ else:
82
+ func_Vm_m = PchipInterpolator([up_m, row_m],[up_Vm, Vm])
83
+ else:
84
+ func_Vm_m = PchipInterpolator([up_m, row_m],[up_Vm, Vm])
85
+ dVm_dm = func_Vm_m.derivative()(row_m)
86
+
87
+ # Upstream
88
+ dT_dr = float(interp1d(row_radius, np.gradient(row.T,row_radius))(r))
89
+ dP_dr = float(interp1d(row_radius, np.gradient(row.P,row_radius))(r))
90
+ P = float(interp1d(row_radius, np.gradient(row.P,row_radius))(r))
91
+ dT0_dr = dT_dr + Vm/Cp * (1 + np.tan(alpha)**2)*dVm_dr
92
+ dP0_dr = dP_dr * (T0/T)**(gamma/(gamma-1)) + P*gamma/(gamma-1) * (T0/T)**(1/(gamma-1)) * (T*dT0_dr-T0*dT_dr)/T**2
93
+ # dT0_dr = float(interp1d(row.percent_hub_shroud, np.gradient(row.T0,row_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0])))
94
+ # dP0_dr = float(interp1d(row.percent_hub_shroud, np.gradient(row.P0,row_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0])))
95
+
96
+ C = (1 + np.tan(alpha)**2) * Vm**2/(2*Cp*T0)
97
+ if (C>1) & ((gamma/(gamma-1))<2):
98
+ raise Exception("Invalid value of C {C}, change reduce alpha or Vm")
99
+ B = (1-C)**(gamma/(gamma-1))
100
+ A = -P0 * gamma/(gamma-1) * (1-C)**(1/(gamma-1)) * (1 + np.tan(alpha)**2)/(2*Cp)
101
+
102
+ eqn15_rhs = Vt**2/r - Vm**2/rm*np.sin(phi) - Vr*dVm_dm # right hand side of equation 15
103
+ eqn15_rhs_simple = Vt**2/r # right hand side of equation 15 simplified for axial machines
104
+
105
+ epsilon = 1e-10 # or another small threshold
106
+ if abs(rm) > epsilon:
107
+ dVm_dr = T0/(2*Vm*A) * (rho*eqn15_rhs - B*dP0_dr) + Vm/(2*T0) * dT0_dr # Eqn 21
108
+ else:
109
+ dVm_dr = T0/(2*Vm*A) * (rho*eqn15_rhs_simple - B*dP0_dr) + Vm/(2*T0) * dT0_dr # Eqn 21, simple
110
+
111
+ ydot = np.array([dP0_dr,dT0_dr,dVm_dr])
112
+
113
+ return ydot
114
+
115
+ T0 = row.T0
116
+
117
+ P0 = row.P0
118
+ Vm = row.Vm
119
+
120
+ # Estimate the Vt based on a given turning angle
121
+ mean_radius = row_radius.mean()
122
+ tip_radius = row_radius.max()
123
+ hub_radius = row_radius.min()
124
+
125
+ T0m = interp1d(row.percent_hub_shroud,T0)(0.5);
126
+ P0m = interp1d(row.percent_hub_shroud,P0)(0.5); Vmm = interp1d(row.percent_hub_shroud,Vm)(0.5)
127
+ # We are solving for the values of these quantities at row exit
128
+ ics = np.array([P0m,T0m,Vmm])
129
+
130
+ # hub_to_tip = np.linspace(hub_radius,tip_radius)
131
+ # res = odeint(ode_radeq_streamtube, ics, hub_to_tip, tfirst=True)
132
+
133
+ # P0_new = interp1d(hub_to_tip,res[:,0])(row_radius)
134
+ # T0_new = interp1d(hub_to_tip,res[:,1])(row_radius)
135
+ # Vm_new = interp1d(hub_to_tip,res[:,2])(row_radius)
136
+
137
+ # mean_radius_to_tip = np.linspace(0,tip_radius-mean_radius,len(row_radius)*5)
138
+ res1 = solve_ivp(ode_radeq_streamtube, t_span =[0, tip_radius-mean_radius], y0 = ics)
139
+
140
+ # mean_radius_to_hub = np.linspace(0,hub_radius-mean_radius,len(row_radius)*5)
141
+ res2 = solve_ivp(ode_radeq_streamtube, t_span = [hub_radius-mean_radius,0], y0 = ics)
142
+
143
+ mid_to_tip_vals = res1.y.transpose()
144
+ mid_to_tip_r = res1.t + mean_radius
145
+ mid_to_hub_vals = res2.y.transpose()
146
+ mid_to_hub_r = res2.t + mean_radius
147
+ mid_to_hub_vals = np.flipud(mid_to_hub_vals)
148
+ hub_to_tip_vals = np.concatenate([mid_to_hub_vals[:-1,:],mid_to_tip_vals])
149
+
150
+ r = np.concatenate([mid_to_hub_r[:-1], mid_to_tip_r])
151
+
152
+ P0_new = interp1d(r,hub_to_tip_vals[:,0])(row_radius)
153
+ T0_new = interp1d(r,hub_to_tip_vals[:,1])(row_radius)
154
+ Vm_new = interp1d(r,hub_to_tip_vals[:,2])(row_radius)
155
+
156
+ row.P0 = P0_new
157
+ row.T0 = T0_new
158
+ row.Vm = Vm_new
159
+ return row
160
+
@@ -1,5 +1,5 @@
1
1
  from typing import List, Tuple
2
- from .radeq import radeq, radeq_normalized
2
+ from .radeq import radeq
3
3
  from .enums import LossType, RowType, PowerType, MassflowConstraint
4
4
  from .bladerow import BladeRow
5
5
  from .td_math import compute_gas_constants
@@ -296,7 +296,7 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
296
296
  row.P0R = upstream.P0R - row.Yp*(upstream.P0R-row.P)
297
297
 
298
298
  # Total Relative Temperature stays constant through the rotor. Adjust for change in radius from rotor inlet to exit
299
- row.T0R = (upstream_rothalpy + 0.5*row.U**2)/row.Cp - T0_coolant_weighted_average(row)
299
+ row.T0R = upstream.T0R # (upstream_rothalpy + 0.5*row.U**2)/row.Cp # - T0_coolant_weighted_average(row)
300
300
  P0R_P = row.P0R / row.P
301
301
  T0R_T = P0R_P**((row.gamma-1)/row.gamma)
302
302
  row.T = (row.T0R/T0R_T) # Exit static temperature
@@ -326,7 +326,7 @@ def rotor_calc(row:BladeRow,upstream:BladeRow,calculate_vm:bool=True):
326
326
  row.Vt = row.Wt+row.U
327
327
 
328
328
  row.alpha2 = np.arctan2(row.Vt,row.Vm)
329
- row.V = np.sqrt(row.Vx**2 + row.Vr**2 + row.Vt**2)
329
+ row.V = np.sqrt(row.Vm**2*(1+np.tan(row.alpha2)**2))
330
330
 
331
331
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
332
332
  T0_T = (1+(row.gamma-1)/2 * row.M**2)
@@ -369,7 +369,7 @@ def inlet_calc(row:BladeRow):
369
369
  raise ValueError(f"Unusually slow flow:{iter} Mach:{avg_mach}")
370
370
  row.Vm[0] = 1/(len(row.Vm)-1)*row.Vm[1:].sum() # Initialize the value at the hub to not upset the mean
371
371
  row.Vr = row.Vm*np.sin(row.phi)
372
- row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
372
+ row.Vt = row.Vm*np.tan(row.alpha2)
373
373
  row.V = np.sqrt(row.Vt**2+row.Vm**2)
374
374
  # Fine tune the Temperature and Pressure and density
375
375
  row.M = row.V/np.sqrt(row.gamma*row.R*row.T)
@@ -13,6 +13,7 @@ from .solve_radeq import adjust_streamlines, radeq
13
13
  from scipy.optimize import minimize_scalar, differential_evolution, fmin_slsqp
14
14
  from .inlet import Inlet
15
15
  from .outlet import Outlet
16
+ from pyturbo.helper import convert_to_ndarray
16
17
 
17
18
  class TurbineSpool(Spool):
18
19
 
@@ -193,7 +194,7 @@ class TurbineSpool(Spool):
193
194
  """
194
195
  total_massflow = list(); s = 0; massflow_stage = list()
195
196
  stage_ids = list(set([row.stage_id for row in blade_rows if row.stage_id>=0]))
196
- for row in blade_rows[1:-1]: # Ignore inlet and outlet
197
+ for row in blade_rows: # Ignore inlet and outlet
197
198
  total_massflow.append(row.total_massflow_no_coolant)
198
199
  sign = 1
199
200
  for s in stage_ids:
@@ -237,7 +238,7 @@ class TurbineSpool(Spool):
237
238
  2. Keep the mean
238
239
 
239
240
  Args:
240
- x0 (List[float]): _description_
241
+ x0 (List[float]): Percentage of P0 exiting each row
241
242
  blade_rows (List[List[BladeRow]]): _description_
242
243
  P0 (npt.NDArray): _description_
243
244
  P (npt.NDArray): (1) Outlet Static Pressure. (2)
@@ -250,9 +251,9 @@ class TurbineSpool(Spool):
250
251
  # try:
251
252
  if balance_mean_pressure:
252
253
  for j in range(self.num_streamlines):
253
- Ps_range = outlet_pressure(x0,P0[j],P[j])
254
+ Ps = outlet_pressure(x0,P0[j],P[j])
254
255
  for i in range(1,len(blade_rows)-2):
255
- blade_rows[i].P[j] = Ps_range[i-1]
256
+ blade_rows[i].P[j] = float(Ps[i-1])
256
257
  blade_rows[-2].P = P
257
258
  else:
258
259
  for i in range(1,len(blade_rows)-1):
@@ -261,7 +262,7 @@ class TurbineSpool(Spool):
261
262
  # try:
262
263
  calculate_massflows(blade_rows,True,self.fluid)
263
264
  print(x0)
264
- return self.__massflow_std__(blade_rows)
265
+ return self.__massflow_std__(blade_rows[1:-1]) # do not consider inlet and outlet
265
266
  # except Exception as e:
266
267
  # print(e)
267
268
  # finally:
@@ -278,9 +279,9 @@ class TurbineSpool(Spool):
278
279
 
279
280
  print(f"Looping to converge massflow")
280
281
  past_err = -100; loop_iter = 0; err = 0.001
281
- while np.abs((err-past_err)/err)>0.05:
282
+ while (np.abs((err-past_err)/err)>0.05) and loop_iter<10:
282
283
  if len(outlet_P) == 1:
283
- # x = balance_massflows(0.658,self.blade_rows[:-1],self.blade_rows[0].P0,self.blade_rows[-1].P)
284
+ # x = balance_massflows(0.22896832148169688,self.blade_rows,self.blade_rows[0].P0,self.blade_rows[-1].P)
284
285
  res = minimize_scalar(fun=balance_massflows,args=(self.blade_rows,self.blade_rows[0].P0,self.blade_rows[-1].P),bounds=outlet_P[0],tol=0.001,options={'disp': True},method='bounded')
285
286
  x = res.x
286
287
  print(x)
@@ -302,7 +303,9 @@ class TurbineSpool(Spool):
302
303
  self.blade_rows[-1].transfer_quantities(self.blade_rows[-2])
303
304
  self.blade_rows[-1].P = self.blade_rows[-1].get_static_pressure(self.blade_rows[-1].percent_hub_shroud)
304
305
 
306
+ past_err = err
305
307
  err = self.__massflow_std__(self.blade_rows)
308
+ loop_iter += 1
306
309
  print(f"Loop {loop_iter} massflow convergenced error:{err}")
307
310
 
308
311
  # calculate Reynolds number
@@ -393,64 +396,63 @@ def calculate_massflows(blade_rows:List[BladeRow],calculate_vm:bool=False,fluid:
393
396
  passage (Passage): _description_
394
397
  calculate_vm (bool, optional): _description_. Defaults to False.
395
398
  """
396
- for p in range(3):
397
- for i in range(1,len(blade_rows)-1):
398
- row = blade_rows[i]
399
- # Upstream Row
400
- if i == 0:
401
- upstream = blade_rows[i]
402
- else:
403
- upstream = blade_rows[i-1]
404
- if i<len(blade_rows)-1:
405
- downstream = blade_rows[i+1]
406
-
407
- # Pressure loss = shift in entropy which affects the total pressure of the row
408
- if row.row_type == RowType.Inlet:
409
- row.Yp = 0
410
- else:
411
- if row.loss_function.loss_type == LossType.Pressure:
412
- row.Yp = row.loss_function(row,upstream)
413
- for _ in range(2):
414
- if row.row_type == RowType.Rotor:
415
- rotor_calc(row,upstream,calculate_vm=True)
416
- # Finds Equilibrium between Vm, P0, T0
417
- row = radeq(row,upstream,downstream)
418
- compute_gas_constants(row,fluid)
419
- rotor_calc(row,upstream,calculate_vm=False)
420
- elif row.row_type == RowType.Stator:
421
- stator_calc(row,upstream,downstream,calculate_vm=True)
422
- # Finds Equilibrium between Vm, P0, T0
423
- row = radeq(row,upstream,downstream)
424
- compute_gas_constants(row,fluid)
425
- stator_calc(row,upstream,downstream,calculate_vm=False)
426
- compute_gas_constants(row,fluid)
427
- compute_massflow(row)
428
- compute_power(row,upstream)
429
-
430
- elif row.loss_function.loss_type == LossType.Enthalpy:
399
+ for i in range(1,len(blade_rows)-1):
400
+ row = blade_rows[i]
401
+ # Upstream Row
402
+ if i == 0:
403
+ upstream = blade_rows[i]
404
+ else:
405
+ upstream = blade_rows[i-1]
406
+ if i<len(blade_rows)-1:
407
+ downstream = blade_rows[i+1]
408
+
409
+ # Pressure loss = shift in entropy which affects the total pressure of the row
410
+ if row.row_type == RowType.Inlet:
411
+ row.Yp = 0
412
+ else:
413
+ if row.loss_function.loss_type == LossType.Pressure:
414
+ row.Yp = row.loss_function(row,upstream)
415
+ for _ in range(2):
431
416
  if row.row_type == RowType.Rotor:
432
- row.Yp = 0
433
- rotor_calc(row,upstream,calculate_vm=calculate_vm)
434
- eta_total = float(row.loss_function(row,upstream))
435
- def find_yp(Yp,row,upstream):
436
- row.Yp = Yp
437
- rotor_calc(row,upstream,calculate_vm=True)
438
- row = radeq(row,upstream)
439
- compute_gas_constants(row,fluid)
440
- rotor_calc(row,upstream,calculate_vm=False)
441
- return abs(row.eta_total - eta_total)
442
-
443
- res = minimize_scalar(find_yp,bounds=[0,0.6],args=(row,upstream))
444
- row.Yp = res.x
417
+ rotor_calc(row,upstream,calculate_vm=True)
418
+ # Finds Equilibrium between Vm, P0, T0
419
+ row = radeq(row,upstream,downstream)
420
+ compute_gas_constants(row,fluid)
421
+ rotor_calc(row,upstream,calculate_vm=False)
445
422
  elif row.row_type == RowType.Stator:
446
- row.Yp = 0
447
423
  stator_calc(row,upstream,downstream,calculate_vm=True)
448
- row = radeq(row,upstream)
449
- row = compute_gas_constants(row,fluid)
424
+ # Finds Equilibrium between Vm, P0, T0
425
+ row = radeq(row,upstream,downstream)
426
+ compute_gas_constants(row,fluid)
450
427
  stator_calc(row,upstream,downstream,calculate_vm=False)
451
- row = compute_gas_constants(row,fluid)
428
+ compute_gas_constants(row,fluid)
452
429
  compute_massflow(row)
453
430
  compute_power(row,upstream)
431
+
432
+ elif row.loss_function.loss_type == LossType.Enthalpy:
433
+ if row.row_type == RowType.Rotor:
434
+ row.Yp = 0
435
+ rotor_calc(row,upstream,calculate_vm=calculate_vm)
436
+ eta_total = float(row.loss_function(row,upstream))
437
+ def find_yp(Yp,row,upstream):
438
+ row.Yp = Yp
439
+ rotor_calc(row,upstream,calculate_vm=True)
440
+ row = radeq(row,upstream)
441
+ compute_gas_constants(row,fluid)
442
+ rotor_calc(row,upstream,calculate_vm=False)
443
+ return abs(row.eta_total - eta_total)
444
+
445
+ res = minimize_scalar(find_yp,bounds=[0,0.6],args=(row,upstream))
446
+ row.Yp = res.x
447
+ elif row.row_type == RowType.Stator:
448
+ row.Yp = 0
449
+ stator_calc(row,upstream,downstream,calculate_vm=True)
450
+ row = radeq(row,upstream)
451
+ row = compute_gas_constants(row,fluid)
452
+ stator_calc(row,upstream,downstream,calculate_vm=False)
453
+ row = compute_gas_constants(row,fluid)
454
+ compute_massflow(row)
455
+ compute_power(row,upstream)
454
456
 
455
457
  def massflow_loss_function(exit_angle:float,index:int,row:BladeRow,upstream:BladeRow,downstream:BladeRow=None,fluid:Solution=None):
456
458
  """Finds the blade exit angles that balance the massflow throughout the stage
@@ -531,14 +533,10 @@ def outlet_pressure(percents:List[float],inletP0:float,outletP:float) -> npt.NDA
531
533
  Returns:
532
534
  npt.NDArray: Array of static pressures
533
535
  """
534
- maxP = inletP0
535
- minP = outletP
536
- if isinstance(percents, float):
537
- Ps = [percents*(maxP-minP)+minP]
538
- else:
539
- Ps = np.zeros(shape=(len(percents),1)); i = 0
540
- for p in percents:
541
- Ps[i] = p*(maxP - minP) + minP
542
- maxP = Ps[i]
543
- i+=1
544
- return Ps
536
+ percents = convert_to_ndarray(percents)
537
+ Ps = np.zeros((len(percents),))
538
+ for i in range(len(percents)):
539
+ Ps[i] = float(interp1d((0,1),(inletP0,outletP))(percents[i]))
540
+ inletP0 = Ps[i]
541
+ return Ps
542
+
@@ -1,286 +0,0 @@
1
- from scipy.interpolate import interp1d,PchipInterpolator
2
- from scipy.integrate import odeint
3
- import numpy as np
4
- import numdifftools as nd
5
- from .bladerow import BladeRow
6
- from .enums import RowType
7
-
8
-
9
- def radeq(row:BladeRow,upstream:BladeRow,downstream:BladeRow=None) -> BladeRow:
10
- """Solves the radial equilibrium equation for axial machines and returns the convergence.
11
-
12
- Note:
13
- This function will give you T0, P0, Vm as a function of the radius.
14
-
15
- Args:
16
- row (BladeRow): Current row
17
- upstream (BladeRow): Previous row
18
- downstream (BladeRow): Next row
19
-
20
- Returns:
21
- BladeRow: current row with T0, P0, and Vm calculated
22
- """
23
- row_radius = row.r # Use these for gradient
24
- up_radius = upstream.r
25
-
26
- def ode_radeq_streamtube(r:np.ndarray,y:np.ndarray):
27
- """Solves the radial equilibrium equation for a streamtube
28
-
29
- Args:
30
- r (np.ndarray): radius not as a percent
31
- y (np.ndarray): Array containing [P0,Vt,VtU,T0]
32
-
33
- """
34
- P0 = y[0]
35
- T0 = y[1]
36
- Vm = y[2]
37
- if r>row_radius[-1]:
38
- return [0,0,0]
39
- elif r<row_radius[0]:
40
- return [0,0,0]
41
-
42
- Cp = row.Cp
43
- phi = interp1d(row_radius, row.phi)(r)
44
- alpha = interp1d(row_radius, row.alpha2)(r)
45
- rm = interp1d(row_radius, row.rm)(r)
46
- rho = row.rho.mean()
47
-
48
- if (row.row_type == RowType.Rotor):
49
- omega = row.rpm*np.pi/30
50
- U = omega*r
51
- else:
52
- omega = 0
53
- U = 0
54
- gamma = row.gamma
55
-
56
- # Solve the Radial Equlibrium
57
- Vt = Vm*np.tan(alpha)
58
- Vr = Vm*np.sin(phi)
59
- # Estimations
60
- dVm_dr = float(interp1d(row_radius, np.gradient(row.Vm, row_radius))(r))
61
- dVt_dr = dVm_dr*np.tan(alpha)
62
- # dVr_dr = dVm_dr*np.sin(phi)
63
-
64
- up_Vm = interp1d(row_radius, upstream.Vm)(r)
65
- if downstream:
66
- if downstream.row_type == RowType.Outlet:
67
- down_Vm = Vm
68
- else:
69
- down_Vm = interp1d(row_radius, downstream.Vm)(r)
70
- else:
71
- down_Vm = Vm
72
- up_m = interp1d(row_radius, upstream.m)(r)
73
-
74
- # Get a rough guess of dVm/dm
75
- if downstream!=None:
76
- down_m = interp1d(row_radius, downstream.m)(r)
77
- row_m = interp1d(row_radius, row.m)(r)
78
- if down_m != row_m:
79
- func_Vm_m = PchipInterpolator([up_m, row_m, down_m],[up_Vm, Vm, down_Vm])
80
- else:
81
- func_Vm_m = PchipInterpolator([up_m, row_m],[up_Vm, Vm])
82
- else:
83
- func_Vm_m = PchipInterpolator([up_m, row_m],[up_Vm, Vm])
84
- dVm_dm = func_Vm_m.derivative()(row_m)
85
-
86
- # Upstream
87
- 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
88
- dP0up_dr = float(interp1d(upstream.percent_hub_shroud, np.gradient(upstream.P0,up_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0]))) # use percentage to get the T0 upstream value
89
-
90
- dP0_dr = float(interp1d(row.percent_hub_shroud, np.gradient(row.P0,row_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0]))) # use percentage to get
91
-
92
- U_up = float(interp1d(upstream.percent_hub_shroud,up_radius*omega)((r-row_radius[0])/(row_radius[-1]-row_radius[0]))) # use percentage to get the T0 upstream value
93
- dVtup_dr = float(interp1d(upstream.percent_hub_shroud,np.gradient(upstream.Vt,up_radius))((r-row_radius[0])/(row_radius[-1]-row_radius[0])))
94
- Vtup = float(interp1d(upstream.percent_hub_shroud,upstream.Vt)((r-row_radius[0])/(row_radius[-1]-row_radius[0])))
95
-
96
-
97
- dT0_dr = dT0up_dr - 1/row.Cp*(U_up*dVtup_dr + Vtup*omega - (U*dVt_dr+Vt*omega)) # Eqn 8
98
- # if row.loss_function.LossType == LossType.Pressure: # type: ignore
99
- # dP0_dr = dP0up_dr-row.Yp*(dP0up_dr - dP_dr) # Eqn 9
100
-
101
- C = Vm**2*(1+np.tan(alpha)**2)/(2*Cp*T0)
102
- if (C>1) & ((gamma/(gamma-1))<2):
103
- raise Exception("Invalid value of C {C}, change reduce alpha or Vm")
104
- B = (1-C)**(gamma/(gamma-1))
105
- A = P0 * gamma/(gamma-1) * (1-C)**(1/(gamma-1))
106
-
107
- epsilon = 1e-10 # or another small threshold
108
- if abs(rm) > epsilon:
109
- dVm_dr = Cp*T0/(Vm*A*(1+np.tan(alpha)**2)) * (rho*(Vt**2/r + Vm**2/rm * np.cos(phi) - Vr*dVm_dm) - B*dP0_dr) + Vm/(2*T0) *dT0_dr # Eqn 6
110
- else:
111
- dVm_dr = Cp*T0/(Vm*A*(1+np.tan(alpha)**2)) * (rho*(Vt**2/r - Vr*dVm_dm) - B*dP0_dr) + Vm/(2*T0) *dT0_dr # Eqn 6
112
-
113
- ydot = np.array([dP0_dr,dT0_dr,dVm_dr])
114
-
115
- return ydot
116
-
117
- T0 = row.T0
118
-
119
- P0 = row.P0
120
- Vm = row.Vm
121
-
122
- # Estimate the Vt based on a given turning angle
123
- mean_radius = row_radius.mean()
124
- tip_radius = row_radius[-1]
125
- hub_radius = row_radius[0]
126
-
127
- T0m = interp1d(row.percent_hub_shroud,T0)(0.5);
128
- P0m = interp1d(row.percent_hub_shroud,P0)(0.5); Vmm = interp1d(row.percent_hub_shroud,Vm)(0.5)
129
- # We are solving for the values of these quantities at row exit
130
- ics = np.array([P0m,T0m,Vmm])
131
-
132
- rm_to_tip = np.linspace(mean_radius,tip_radius)
133
- res1 = odeint(ode_radeq_streamtube, ics, rm_to_tip, tfirst=True)
134
-
135
- rm_to_hub = np.flip(np.linspace(hub_radius,mean_radius))
136
- res2 = odeint(ode_radeq_streamtube, ics, rm_to_hub, tfirst=True)
137
-
138
- res2 = np.flipud(res2)
139
- res = np.concatenate([res2[:-1,:],res1])
140
- r = np.concatenate([np.flip(rm_to_hub)[:-1], rm_to_tip])
141
-
142
- P0_new = interp1d(r,res[:,0])(row_radius)
143
- T0_new = interp1d(r,res[:,1])(row_radius)
144
- Vm_new = interp1d(r,res[:,2])(row_radius)
145
-
146
- row.P0 = P0_new
147
- row.T0 = T0_new
148
- row.Vm = Vm_new
149
- if row.row_type == RowType.Rotor:
150
- # U(VT1-VT2) = Power/massflow; VT2 = VT1 - Power/massflow
151
- row.Vt = upstream.Vt-row.power/(row.total_massflow*row.U)
152
- row.alpha2 = np.arctan2(row.Vt,row.Vx)
153
- elif row.row_type == RowType.Stator:
154
- row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
155
- row.Vr = row.Vm*np.sin(row.phi)
156
- row.Vx = row.Vm*np.cos(row.phi)
157
-
158
- return row
159
-
160
-
161
- def radeq_normalized(row:BladeRow,upstream:BladeRow) -> BladeRow:
162
- """Solves the radial equilibrium equation for axial or radial machines and returns the convergence.
163
-
164
- Args:
165
- row (BladeRow): Current row
166
- upstream (BladeRow): Previous row
167
-
168
- Returns:
169
- BladeRow: current row with Vt, T0, P0, and Vm calculated
170
- """
171
- _,row_radius = row.streamline.get_point(row.percent_hub_shroud) # Use these for gradient
172
- _,up_radius = upstream.streamline.get_point(upstream.percent_hub_shroud)
173
-
174
- def ode_radeq_streamtube(t:np.ndarray,y:np.ndarray):
175
- """Solves the radial equilibrium equation for a streamtube
176
-
177
- Args:
178
- t (np.ndarray): percent from hub to shroud
179
- y (np.ndarray): Array containing [P0,Vt,VtU,T0]
180
-
181
- """
182
- P0 = y[0]
183
- T0 = y[1]
184
- Vm = y[2]
185
- if t>1:
186
- return [0,0,0]
187
- elif t<0:
188
- return [0,0,0]
189
-
190
- _,r = row.streamline.get_point()
191
- Cp = row.Cp
192
- # Interpolate angle of inclination (phi), exit flow angle (alpha), radius of curvature (rm) at a particular percentage from hub to shroud
193
- phi = interp1d(row.percent_hub_shroud, row.phi)(t)
194
- alpha = interp1d(row.percent_hub_shroud, row.alpha2)(t)
195
- rm = interp1d(row.percent_hub_shroud,row.rm)(t)
196
- rho = interp1d(row.percent_hub_shroud,row.rho)(t)
197
-
198
-
199
- if (row.row_type == RowType.Rotor):
200
- omega = row.rpm*np.pi/30
201
- U = omega*r
202
- else:
203
- omega = 0
204
- U = 0
205
- gamma = row.gamma
206
-
207
- # Solve the Radial Equlibrium
208
- Vt = Vm*np.cos(phi)*np.tan(alpha)
209
- Vr = float(interp1d(row.percent_hub_shroud, row.Vr)(t))
210
- # Estimations: need the radius of the streamline to compute gradients
211
- dVm_dr = interp1d(row_radius,np.gradient(row.Vm, row_radius))(r)
212
- dVt_dr = dVm_dr*np.cos(phi)*np.tan(alpha)
213
-
214
- dVm_dm = interp1d(row_radius, np.gradient(Vm, r)) + interp1d(x, np.gradient(Vm, r))
215
-
216
- # Upstream: We interpolate the gradient based on the percentage from hub to shroud
217
- dT0up_dr = interp1d(upstream.percent_hub_shroud,
218
- np.gradient(upstream.T0,up_radius))(t)
219
- dP0up_dr = interp1d(upstream.percent_hub_shroud,
220
- np.gradient(upstream.P0,up_radius))(t)
221
- dP0_dr = interp1d(upstream.percent_hub_shroud,
222
- np.gradient(upstream.P0,up_radius))(t)
223
-
224
- U_up = interp1d(upstream.percent_hub_shroud,up_radius*omega)(t) # use percentage to get the T0 upstream value
225
- dVtup_dr = interp1d(upstream.percent_hub_shroud,np.gradient(upstream.Vt,up_radius))(t)
226
- Vtup = interp1d(upstream.percent_hub_shroud,upstream.Vt)(t)
227
-
228
- dP_dr = interp1d(row.percent_hub_shroud,np.gradient(row.P,row_radius))(t)
229
- dT0_dr = dT0up_dr - 1/row.Cp*(U_up*dVtup_dr + Vtup*omega - (U*dVt_dr+Vt*omega)) # Eqn 8
230
- # if row.loss_function.LossType == LossType.Pressure: # type: ignore
231
- # dP0_dr = dP0up_dr-row.Yp*(dP0up_dr - dP_dr) # Eqn 9
232
-
233
- C = Vm**2*(1+np.cos(phi)**2 * np.tan(alpha)**2)/(2*Cp*T0)
234
- B = (1-C)**(gamma/(gamma-1))
235
- A = P0 * gamma/(gamma-1) * (1-C)**(1/(gamma-1))
236
- dVm_dr = 1/(2*Vm*A) * (rho*(Vt/r - Vm**2/rm * np.cos(phi)-Vr*dVm_dm) - dP0_dr*B) + 1/(2*T0) *dT0_dr # Eqn 6
237
-
238
- ydot = np.array([dP0_dr,dT0_dr,dVm_dr])
239
-
240
- return ydot
241
-
242
- T0 = row.T0
243
- P0 = row.P0
244
- Vm = row.Vm
245
-
246
- # Estimate the Vt based on a given turning angle
247
- _, mean_radius = row.streamline.get_point(0.5)
248
- _, tip_radius = row.streamline.get_point(1)
249
- _, hub_radius = row.streamline.get_point(0)
250
-
251
- T0m = interp1d(row.percent_hub_shroud,T0)(0.5);
252
- P0m = interp1d(row.percent_hub_shroud,P0)(0.5);
253
- Vmm = interp1d(row.percent_hub_shroud,Vm)(0.5)
254
- # We are solving for the values of these quantities at row exit
255
- ics = np.array([P0m,T0m,Vmm])
256
-
257
- mid_to_tip = np.linspace(0,1)
258
- res1 = odeint(ode_radeq_streamtube, ics, mid_to_tip, tfirst=True) # Results
259
-
260
- mid_to_hub = np.flip(np.linspace(hub_radius,mean_radius))
261
- res2 = odeint(ode_radeq_streamtube, ics, mid_to_hub, tfirst=True) # Results
262
-
263
- res2 = np.flipud(res2)
264
- res = np.concatenate([res2[:-1,:],res1]) # Combine the results
265
- t = np.concatenate([np.flip(mid_to_hub)[:-1], mid_to_tip])
266
-
267
- P0_new = interp1d(t,res[:,0])(row.percent_hub_shroud)
268
- T0_new = interp1d(t,res[:,1])(row.percent_hub_shroud)
269
- Vm_new = interp1d(t,res[:,2])(row.percent_hub_shroud)
270
-
271
- row.P0 = P0_new
272
- row.T0 = T0_new
273
- row.Vm = Vm_new
274
- if row.row_type == RowType.Rotor:
275
- # U(VT1-VT2) = Power/massflow; VT2 = VT1 - Power/massflow
276
- row.Vt = upstream.Vt-row.power/(row.total_massflow*row.U)
277
- row.alpha2 = np.arctan2(row.Vt,row.Vx)
278
- elif row.row_type == RowType.Stator:
279
- row.Vt = row.Vm*np.cos(row.phi)*np.tan(row.alpha2)
280
- row.Vr = row.Vm*np.sin(row.phi)
281
- row.Vx = row.Vm*np.cos(row.phi)
282
-
283
- return row
284
-
285
-
286
-