turbo-design 1.3.8__py3-none-any.whl → 1.3.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/METADATA +2 -1
  2. turbo_design-1.3.10.dist-info/RECORD +46 -0
  3. {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/WHEEL +1 -1
  4. turbodesign/__init__.py +57 -4
  5. turbodesign/agf.py +346 -0
  6. turbodesign/arrayfuncs.py +31 -1
  7. turbodesign/bladerow.py +238 -155
  8. turbodesign/compressor_math.py +386 -0
  9. turbodesign/compressor_spool.py +941 -0
  10. turbodesign/coolant.py +18 -6
  11. turbodesign/deviation/__init__.py +5 -0
  12. turbodesign/deviation/axial_compressor.py +3 -0
  13. turbodesign/deviation/carter_deviation.py +79 -0
  14. turbodesign/deviation/deviation_base.py +20 -0
  15. turbodesign/deviation/fixed_deviation.py +42 -0
  16. turbodesign/enums.py +5 -6
  17. turbodesign/flow_math.py +158 -0
  18. turbodesign/inlet.py +126 -56
  19. turbodesign/isentropic.py +59 -15
  20. turbodesign/loss/__init__.py +3 -1
  21. turbodesign/loss/compressor/OTAC_README.md +39 -0
  22. turbodesign/loss/compressor/__init__.py +54 -0
  23. turbodesign/loss/compressor/diffusion.py +61 -0
  24. turbodesign/loss/compressor/lieblein.py +1 -0
  25. turbodesign/loss/compressor/otac.py +799 -0
  26. turbodesign/loss/compressor/references/schobeiri-2012-shock-loss-model-for-transonic-and-supersonic-axial-compressors-with-curved-blades.pdf +0 -0
  27. turbodesign/loss/fixedpolytropic.py +27 -0
  28. turbodesign/loss/fixedpressureloss.py +30 -0
  29. turbodesign/loss/losstype.py +2 -30
  30. turbodesign/loss/turbine/TD2.py +25 -29
  31. turbodesign/loss/turbine/__init__.py +0 -1
  32. turbodesign/loss/turbine/ainleymathieson.py +6 -5
  33. turbodesign/loss/turbine/craigcox.py +6 -5
  34. turbodesign/loss/turbine/fixedefficiency.py +8 -7
  35. turbodesign/loss/turbine/kackerokapuu.py +7 -5
  36. turbodesign/loss/turbine/traupel.py +17 -16
  37. turbodesign/outlet.py +81 -22
  38. turbodesign/passage.py +98 -63
  39. turbodesign/row_factory.py +129 -0
  40. turbodesign/solve_radeq.py +9 -10
  41. turbodesign/{td_math.py → turbine_math.py} +144 -185
  42. turbodesign/turbine_spool.py +1219 -0
  43. turbo_design-1.3.8.dist-info/RECORD +0 -33
  44. turbodesign/compressorspool.py +0 -60
  45. turbodesign/loss/turbine/fixedpressureloss.py +0 -25
  46. turbodesign/rotor.py +0 -38
  47. turbodesign/spool.py +0 -317
  48. turbodesign/turbinespool.py +0 -543
@@ -0,0 +1,27 @@
1
+ from turbodesign.arrayfuncs import convert_to_ndarray
2
+ from .losstype import LossBaseClass
3
+ from ..enums import LossType
4
+ import numpy.typing as npt
5
+ import numpy as np
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..bladerow import BladeRow # for type hints only
10
+
11
+
12
+ class FixedPolytropicEfficiency(LossBaseClass):
13
+ """Return a fixed polytropic efficiency target for a row (η_poly)."""
14
+
15
+ eta_poly: npt.NDArray
16
+
17
+ def __init__(self, eta_poly: float | npt.ArrayLike):
18
+ super().__init__(LossType.Polytropic)
19
+ self.eta_poly = convert_to_ndarray(eta_poly)
20
+
21
+ def __call__(self, row: "BladeRow", upstream: "BladeRow") -> npt.NDArray: # noqa: ARG002
22
+ eta = self.eta_poly
23
+ if eta.size == 1:
24
+ eta = eta * np.ones_like(row.r) # type: ignore[arg-type]
25
+ elif eta.shape != row.r.shape:
26
+ eta = np.asarray(eta).reshape(row.r.shape) # type: ignore[attr-defined]
27
+ return eta
@@ -0,0 +1,30 @@
1
+ from turbodesign.arrayfuncs import convert_to_ndarray
2
+ from .losstype import LossBaseClass
3
+ from ..enums import LossType
4
+ import numpy.typing as npt
5
+ import numpy as np
6
+ from scipy.interpolate import interp1d
7
+ from typing import TYPE_CHECKING
8
+ if TYPE_CHECKING:
9
+ from ..bladerow import BladeRow # for type hints only
10
+
11
+ class FixedPressureLoss(LossBaseClass):
12
+ """Fixed pressure loss coefficient (scalar or spanwise array)."""
13
+
14
+ pressure_loss: npt.NDArray
15
+
16
+ def __init__(self, pressure_loss: float | npt.ArrayLike):
17
+ super().__init__(LossType.Pressure)
18
+ self.pressure_loss = convert_to_ndarray(pressure_loss)
19
+
20
+ def __call__(self, row: "BladeRow", upstream: "BladeRow") -> npt.NDArray:
21
+ """Outputs the fixed pressure loss."""
22
+ loss = self.pressure_loss
23
+ if loss.size == 1:
24
+ loss = loss * np.ones_like(row.r) # type: ignore
25
+ elif loss.shape != row.r.shape:
26
+ if len(row.r) == 1:
27
+ return loss.mean()
28
+ else:
29
+ return interp1d(np.linspace(0,1,len(loss)),loss)(row.percent_hub_shroud)
30
+ return loss
@@ -1,6 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
2
  from typing import Any, Dict, Iterable, Tuple
3
3
  from ..lossinterp import LossInterp
4
+ import numpy.typing as npt
4
5
  import os
5
6
  from ..enums import LossType
6
7
 
@@ -17,39 +18,10 @@ class LossBaseClass(ABC):
17
18
  self._loss_type = lossType
18
19
 
19
20
  @abstractmethod
20
- def __call__(self, row:Any, upstream:Any) -> float:
21
+ def __call__(self, row:Any, upstream:Any) -> npt.NDArray:
21
22
  """Evaluate the loss for the supplied blade row."""
22
23
  raise NotImplementedError
23
-
24
-
25
24
 
26
25
  @property
27
26
  def loss_type(self):
28
27
  return self._loss_type
29
-
30
-
31
- class CompositeLossModel(LossBaseClass):
32
- """Combines multiple loss models of the same type."""
33
-
34
- def __init__(self, models: Iterable[LossBaseClass]):
35
- models_tuple: Tuple[LossBaseClass, ...] = tuple(models)
36
- if not models_tuple:
37
- raise ValueError("CompositeLossModel requires at least one loss model.")
38
-
39
- loss_type = models_tuple[0].loss_type
40
- for model in models_tuple[1:]:
41
- if model.loss_type != loss_type:
42
- raise ValueError("All loss models must share the same LossType.")
43
-
44
- super().__init__(loss_type)
45
- self._models: Tuple[LossBaseClass, ...] = models_tuple
46
-
47
- def __call__(self, row: Any, upstream: Any) -> float:
48
- total_loss = 0.0
49
- for model in self._models:
50
- total_loss += float(model(row, upstream))
51
- return total_loss
52
-
53
- @property
54
- def models(self) -> Tuple[LossBaseClass, ...]:
55
- return self._models
@@ -4,7 +4,8 @@ from ...enums import RowType, LossType
4
4
  from typing import Any, Callable, Dict, List, Union
5
5
  from ...bladerow import BladeRow
6
6
  from scipy.stats import linregress
7
- import numpy as np
7
+ import numpy as np
8
+ import numpy.typing as npt
8
9
  from ..losstype import LossBaseClass
9
10
 
10
11
 
@@ -19,44 +20,40 @@ class TD2(LossBaseClass):
19
20
  def LossType(self):
20
21
  return self._loss_type
21
22
 
22
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
23
- """TD2-2_Manual equations 12a and 12b. This is the loss equation used inside original TD2.
24
- #! Wrong very wrong from the book definition
25
- Total Pressure Loss (Y) = (P_in - P_ex) / (P_ex - P_ex,static)
26
- Where P_in and P_ex are total quantities
27
-
28
- Authors Comments:
29
- Given the assumptions, the use of this loss correlation should be limited to starting a solution.
23
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
24
+ """TD2-2 manual equations 12a/12b for total pressure loss coefficient.
25
+
26
+ The implementation mirrors the original TD2 code path, which differs from
27
+ the textbook definition but preserves legacy behavior. Use primarily for
28
+ initial estimates.
30
29
 
31
30
  Assumptions:
32
- 1. Rotor and stator loss coefficients were equal when their relative design requirements are identical
33
- 2. Stage reaction at meanline is 50%
34
- 3. Axial velocity was constant through the stage
35
- 4. Stator exit Mach number is 0.8
31
+ 1. Rotor and stator loss coefficients are equal when design requirements match.
32
+ 2. Stage reaction at meanline is 50%.
33
+ 3. Axial velocity is constant through the stage.
34
+ 4. Stator exit Mach number is 0.8.
36
35
 
37
36
  Args:
38
- beta_in (float): Inlet flow angle in degrees.
39
- beta_ex (float): Exit flow angle in degrees.
40
- V_ratio (float): Ratio if inlet velocity magnitude with relative exit velocity magnitude
37
+ row (BladeRow): Blade row being evaluated.
38
+ upstream (BladeRow): Upstream blade row supplying inlet conditions.
41
39
 
42
40
  Returns:
43
- float: Total Pressure loss coefficient
41
+ numpy.ndarray: Total pressure loss coefficient repeated across ``row.r``.
44
42
  """
45
- beta_in = row.beta1.mean() # absolute flow angle entering the blade row
46
- beta_ex = row.beta2.mean() # flow angle leaving the blade row
43
+ beta_in = row.beta1.mean() # Inlet flow angle at the mean radius
44
+ beta_ex = row.beta2.mean() # Exit flow angle at the mean radius
47
45
  if row.row_type == RowType.Stator:
48
46
  V_ratio = row.V.mean() / upstream.V.mean() # Vin/Vex Equation 12a and 12b. Relative to Stator
49
47
  elif row.row_type == RowType.Rotor:
50
48
  V_ratio = row.W.mean() /upstream.W.mean() # Vin/Vex Equation 12a and 12b. Relative to Rotor
51
49
 
52
50
  a = [0.055, 0.15, 0.6, 0.6, 0.8, 0.03, 0.157255, 3.6] # Coefficients from pw3hp1.json
53
- # mu should be in
54
51
  if V_ratio < a[2]:
55
52
  Y = abs(math.tan(beta_in) - math.tan(beta_ex)) / (a[3] + a[4]*math.cos(beta_ex)) * (a[5] + a[6]*V_ratio**a[7])
56
53
  else:
57
54
  Y = abs(math.tan(beta_in) - math.tan(beta_ex)) / (a[3] + a[4]*math.cos(beta_ex)) * (a[0] + a[1]*(V_ratio - a[2]))
58
55
 
59
- return Y
56
+ return Y+row.r*0
60
57
 
61
58
  class TD2_Reynolds_Correction(LossBaseClass):
62
59
 
@@ -69,19 +66,18 @@ class TD2_Reynolds_Correction(LossBaseClass):
69
66
  def LossType(self):
70
67
  return self._loss_type
71
68
 
72
- def __call__(self,upstream:BladeRow, row:BladeRow) -> float:
73
- """TD2_Pressure_Loss with Reynolds Correction Factor. This is from NASA SP-290 (Vol.1, p.62)
69
+ def __call__(self,upstream:BladeRow, row:BladeRow) -> npt.NDArray:
70
+ """Apply TD2 Reynolds correction (NASA SP-290 Vol.1, p.62).
74
71
 
75
- The correlations come from td2-2.f Line 2771
76
- WYECOR(I)=WYECOR(I)*(0.35+0.65*18.21)/(0.35+0.65*(FLWP/VISC/RST(MEAN))**(0.2))
77
-
78
- I have assumed that 18.21 is some reference reynolds number. There is very little documentation on what these numbers are. The entire fortran code is frustrating and probably should never have happened in the first place.
72
+ The correction follows td2-2.f line 2771:
73
+ WYECOR = WYECOR*(0.35+0.65*18.21)/(0.35+0.65*(FLWP/VISC/RST(MEAN))**0.2)
79
74
 
80
75
  Args:
81
- row (BladeRow): Current Blade Row
76
+ upstream (BladeRow): Upstream blade row supplying inlet conditions.
77
+ row (BladeRow): Blade row receiving the correction.
82
78
 
83
79
  Returns:
84
- float: Total Pressure loss coefficient
80
+ numpy.ndarray: Reynolds-corrected total pressure loss coefficient.
85
81
  """
86
82
  Y = self.TD2(upstream,row)
87
83
  A = 0.35
@@ -3,6 +3,5 @@ from .craigcox import CraigCox
3
3
  from .TD2 import TD2, TD2_Reynolds_Correction
4
4
  from .ainleymathieson import AinleyMathieson
5
5
  from .fixedefficiency import FixedEfficiency
6
- from .fixedpressureloss import FixedPressureLoss
7
6
  from .kackerokapuu import KackerOkapuu
8
7
  from .traupel import Traupel
@@ -1,5 +1,6 @@
1
1
  import pickle, os
2
2
  from typing import Dict
3
+ import numpy.typing as npt
3
4
  from ...bladerow import BladeRow, sutherland
4
5
  from ...lossinterp import LossInterp
5
6
  from ...enums import RowType, LossType
@@ -37,7 +38,7 @@ class AinleyMathieson(LossBaseClass):
37
38
  with open(path.absolute(),'rb') as f:
38
39
  self.data = pickle.load(f) # type: ignore
39
40
 
40
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
41
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
41
42
  """Ainley Mathieson predicts the pressure loss of a turbine nozzle or rotor. Since these correlations are from Cascade experiments, the user should be familar with the reynolds and mach number requirements for each equation and figure. Using something outside the bounds can give inaccuare approximations of loss. Additionally these correlations were done on unoptimized blades so efficiencies maybe lower than what's attainable. Massflow can also be affected because exit P0 is affected.
42
43
 
43
44
  This code will attempt use the correct equations and warn the user if mach number is out of range.
@@ -47,11 +48,11 @@ class AinleyMathieson(LossBaseClass):
47
48
  beta: blade angle relative to axial direction
48
49
 
49
50
  Args:
50
- upstream (BladeRow): Upstream blade row
51
- row (BladeRow): downstream blade row
51
+ row (BladeRow): Blade row being evaluated.
52
+ upstream (BladeRow): Upstream blade row providing inlet conditions.
52
53
 
53
54
  Returns:
54
- float: Pressure Loss at zero incidence
55
+ numpy.ndarray: Pressure loss at zero incidence matching ``row.r``.
55
56
  """
56
57
  Kp = 12300 # Ft / (pdl C); 1 pdl = 0.138254954376 N :(
57
58
  At = row.throat
@@ -119,5 +120,5 @@ class AinleyMathieson(LossBaseClass):
119
120
  Yp_i0 = (Yp_beta0 + (beta1/alpha2)**2 *(Yp_beta1_eq_alpha2 - Yp_beta0)) *(t_c/0.2)**(-beta1/alpha2) # Fig 4 and Eqn 5
120
121
 
121
122
  Yt = Yp_i0 + Y_secondary_clearance
122
- return Yt # Profile loss at zero incidence
123
+ return Yt+row.r*0 # Profile loss at zero incidence
123
124
 
@@ -1,5 +1,6 @@
1
1
  import pickle, os
2
2
  from typing import Dict
3
+ import numpy.typing as npt
3
4
  from ...bladerow import BladeRow, sutherland
4
5
  from ...lossinterp import LossInterp
5
6
  from ...enums import RowType, LossType
@@ -34,7 +35,7 @@ class CraigCox(LossBaseClass):
34
35
  self.C = 1/(200*32.2*778.16) # https://www.sciencedirect.com/science/article/pii/S2666202721000574
35
36
 
36
37
 
37
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
38
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
38
39
  """Craig and Cox uses the enthalpy definition of loss to calculate the loss of a turbine stage.
39
40
 
40
41
  Note:
@@ -67,11 +68,11 @@ class CraigCox(LossBaseClass):
67
68
  - (\delta x_p)_m from Figure 8
68
69
 
69
70
  Args:
70
- upstream (BladeRow): Upstream blade row
71
- row (BladeRow): downstream blade row
71
+ row (BladeRow): Downstream blade row being evaluated.
72
+ upstream (BladeRow): Upstream blade row providing inlet conditions.
72
73
 
73
74
  Returns:
74
- float: Stage Efficiency
75
+ numpy.ndarray | int: Stage efficiency; returns 0 for stators, spanwise array for rotors.
75
76
  """
76
77
  if row.row_type == RowType.Stator:
77
78
  return 0
@@ -191,5 +192,5 @@ class CraigCox(LossBaseClass):
191
192
 
192
193
  # According to Equation 3, Group 1 loss is an enthalpy loss Cp*T0. Need to convert to Pressure Loss
193
194
  eta_total = (upstream.T0.mean() - row.T0.mean())/(upstream.T0.mean()-(row.T0.mean()-T0_Loss))
194
- return eta_total
195
+ return eta_total + row.r*0
195
196
 
@@ -1,6 +1,7 @@
1
1
  from ...bladerow import BladeRow, sutherland
2
2
  from ...enums import RowType, LossType
3
3
  from ..losstype import LossBaseClass
4
+ import numpy.typing as npt
4
5
 
5
6
  class FixedEfficiency(LossBaseClass):
6
7
  efficiency:float
@@ -12,18 +13,18 @@ class FixedEfficiency(LossBaseClass):
12
13
  self.efficiency = efficiency
13
14
 
14
15
 
15
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
16
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
16
17
  """Fixed efficiency loss
17
18
 
18
19
  Args:
19
- upstream (BladeRow): Upstream blade row
20
- row (BladeRow): downstream blade row
20
+ row (BladeRow): Blade row being evaluated.
21
+ upstream (BladeRow): Upstream blade row (unused, kept for API parity).
21
22
 
22
23
  Returns:
23
- float: Stage Efficiency
24
+ numpy.ndarray: Spanwise efficiency array; zeros for stators, fixed value for rotors.
24
25
  """
25
26
  if row.row_type == RowType.Stator:
26
- return 0
27
+ return row.r*0
27
28
  else:
28
- return self.efficiency
29
-
29
+ return self.efficiency + row.r*0
30
+
@@ -4,6 +4,7 @@ from ...bladerow import BladeRow, sutherland
4
4
  from ...lossinterp import LossInterp
5
5
  from ...enums import RowType, LossType
6
6
  import numpy as np
7
+ import numpy.typing as npt
7
8
  import pathlib
8
9
  from ..losstype import LossBaseClass
9
10
  import requests
@@ -15,6 +16,7 @@ def _mean_value(value):
15
16
 
16
17
  class KackerOkapuu(LossBaseClass):
17
18
  UseCFM:bool = False
19
+
18
20
  def __init__(self,UseCFM:bool=False):
19
21
  """KackerOkapuu model is an improvement to the Ainley Mathieson model.
20
22
 
@@ -44,7 +46,7 @@ class KackerOkapuu(LossBaseClass):
44
46
  self.UseCFM = UseCFM
45
47
 
46
48
 
47
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
49
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
48
50
  """Kacker Okapuu is an updated version of Ainley Mathieson and Dunham Came. This tool uses the pressure loss definition.
49
51
 
50
52
  Note:
@@ -54,11 +56,11 @@ class KackerOkapuu(LossBaseClass):
54
56
  Kacker, S. C., and U. Okapuu. "A mean line prediction method for axial flow turbine efficiency." (1982): 111-119.
55
57
 
56
58
  Args:
57
- upstream (BladeRow): Upstream blade row
58
- row (BladeRow): downstream blade row
59
+ row (BladeRow): Blade row being evaluated.
60
+ upstream (BladeRow): Upstream blade row providing inlet conditions.
59
61
 
60
62
  Returns:
61
- float: Pressure Loss
63
+ numpy.ndarray: Pressure loss coefficient array matching ``row.r``.
62
64
  """
63
65
  # Get the Inlet incoming mach number relative to the blade
64
66
  c = row.chord
@@ -162,5 +164,5 @@ class KackerOkapuu(LossBaseClass):
162
164
  f_re = (Rec/1E6)**-0.2
163
165
 
164
166
  Yt = Yp*f_re + Ys + Ytet + Ytc
165
- return Yt
167
+ return Yt+row.r*0
166
168
 
@@ -4,6 +4,7 @@ from ...bladerow import BladeRow, sutherland
4
4
  from ...lossinterp import LossInterp
5
5
  from ...enums import RowType, LossType
6
6
  import numpy as np
7
+ import numpy.typing as npt
7
8
  import pathlib
8
9
  from ..losstype import LossBaseClass
9
10
  import requests
@@ -23,15 +24,15 @@ class Traupel(LossBaseClass):
23
24
  with open(path.absolute(),'rb') as f:
24
25
  self.data = pickle.load(f) # type: ignore
25
26
 
26
- def __call__(self,row:BladeRow, upstream:BladeRow) -> float:
27
- """Enthalpy loss is computed for the entire stage.
27
+ def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
28
+ """Compute Traupel stage enthalpy efficiency from an upstream/downstream pair.
28
29
 
29
30
  Args:
30
- upstream (BladeRow): Stator Row
31
- row (BladeRow): Rotor Row
31
+ row (BladeRow): Blade row being evaluated (stator or rotor).
32
+ upstream (BladeRow): Upstream blade row supplying inlet conditions.
32
33
 
33
34
  Returns:
34
- float: Efficiency
35
+ numpy.ndarray: Spanwise efficiency array matching ``row.r``.
35
36
  """
36
37
 
37
38
  alpha1 = 90-np.degrees(upstream.alpha1.mean())
@@ -52,10 +53,10 @@ class Traupel(LossBaseClass):
52
53
 
53
54
  H = self.data['Fig07'](float(alpha1-beta2), float(alpha2-beta3))
54
55
 
55
- zeta_s = F*g/h_stator # (h1-h1s)/(0.5*c1s**2) # no idea what h1s or h2s is
56
- zeta_r = F*g/h_rotor # (h2-h2s)/(0.5*w2s**2)
57
- x_p_stator = self.data['Fig01'](float(alpha1), float(alpha2)) # not sure if this is the right figure
58
- x_p_rotor = self.data['Fig01'](float(beta2), float(beta3)) # not sure if this is the right figure
56
+ zeta_s = F*g/h_stator # Stator loss factor scaled by pitch-to-span
57
+ zeta_r = F*g/h_rotor # Rotor loss factor scaled by pitch-to-span
58
+ x_p_stator = self.data['Fig01'](float(alpha1), float(alpha2))
59
+ x_p_rotor = self.data['Fig01'](float(beta2), float(beta3))
59
60
  zeta_p_stator = self.data['Fig02'](float(alpha1), float(alpha2))
60
61
  x_m_stator = self.data['Fig03_0'](float(np.mean(upstream.M)))
61
62
  zeta_p_rotor = self.data['Fig02'](float(beta2), float(beta3))
@@ -72,12 +73,12 @@ class Traupel(LossBaseClass):
72
73
  x_delta_rotor = self.data['Fig05'](float(ssen_beta2), float(beta3))
73
74
  zeta_delta_rotor = self.data['Fig04'](float(ssen_beta2), float(beta3))
74
75
 
75
- Dm = 2* (upstream.r[-1] + upstream.r[0])/2 # Is this the mean diameter? I dont know
76
+ Dm = 2* (upstream.r[-1] + upstream.r[0])/2 # Mean diameter used for annulus friction
76
77
  zeta_f = 0.5 * (h_stator/Dm)**2
77
78
 
78
79
  zeta_pr_stator = zeta_p_stator * x_p_stator * x_m_stator * x_delta_stator + zeta_delta_stator + zeta_f
79
80
 
80
- Dm = 2* (row.r[-1] + row.r[0])/2 # Is this the mean diameter? I dont know
81
+ Dm = 2* (row.r[-1] + row.r[0])/2 # Mean diameter used for annulus friction
81
82
  zeta_f = 0.5 * (h_rotor/Dm)**2
82
83
 
83
84
  zeta_pr_rotor = zeta_p_rotor * x_p_rotor * x_m_rotor * x_delta_rotor + zeta_delta_rotor + zeta_f
@@ -85,15 +86,15 @@ class Traupel(LossBaseClass):
85
86
  if row.row_type == RowType.Stator:
86
87
  zeta_cl = 0
87
88
  else:
88
- zeta_cl = self.data['Fig08'](float(row.tip_clearance)) # For simplicity assume unshrouded blade
89
+ zeta_cl = self.data['Fig08'](float(row.tip_clearance)) # Clearance loss for unshrouded blades
89
90
 
90
- zeta_z = 0 # Do not factor this in, a bit complicated
91
- # 1 - (internal) - (external)
91
+ zeta_z = 0 # Disk friction loss not modeled
92
+ # 1 - (internal) - (external)
92
93
  zeta_v = 0
93
94
  zeta_off = 0
94
- eta_stator = 1- (zeta_pr_stator + zeta_s + 0 + zeta_z) - (zeta_r+zeta_v) - zeta_off # Presentation slide 9
95
+ eta_stator = 1- (zeta_pr_stator + zeta_s + 0 + zeta_z) - (zeta_r+zeta_v) - zeta_off # Per Traupel formulation
95
96
  eta_rotor = 1 - (zeta_pr_rotor + zeta_r + zeta_cl + zeta_z) - (zeta_r+zeta_v) - zeta_off
96
- return eta_stator+eta_rotor
97
+ return (eta_stator+eta_rotor) + row.r*0
97
98
 
98
99
 
99
100
 
turbodesign/outlet.py CHANGED
@@ -1,43 +1,89 @@
1
1
  from dataclasses import dataclass, field
2
- from typing import List, Union
2
+ from enum import Enum
3
+ from typing import List, Optional, Union
4
+
3
5
  from .enums import RowType
4
- from .bladerow import BladeRow, compute_gas_constants, interpolate_quantities
6
+ from .bladerow import BladeRow
5
7
  from .arrayfuncs import convert_to_ndarray
6
8
  import numpy as np
7
9
  import numpy.typing as npt
8
- import copy
9
10
  from scipy.interpolate import interp1d
10
11
 
12
+ class OutletType(Enum):
13
+ static_pressure = 1
14
+ total_pressure = 2
15
+ massflow_static_pressure = 3
11
16
 
12
17
  class Outlet(BladeRow):
13
18
  P_fun:interp1d
14
-
15
- def __init__(self,P:Union[float,List[float]],percent_radii:Union[List[float],float],num_streamlines:int=3,location:float=1):
16
- """Initialize the outlet
19
+ P0_fun:interp1d
20
+ num_streamlines:int
21
+ outlet_type: OutletType = OutletType.static_pressure
22
+ percent_hub_shroud:npt.NDArray
23
+
24
+ def __init__(self,num_streamlines:int=3,location:float=1):
25
+ """Initialize the outlet with streamlines and a location as a percentage along the hub
17
26
 
18
27
  Args:
19
- P (Union[float,List[float]]): List of static pressure profile at outlet
20
- percent_radii (List[float]): Percent Radius from 0 to 1 or just put 0.5 and it uses all of it
28
+ num_streamlines (int, optional): _description_. Defaults to 3.
29
+ location (float, optional): Location as percentage along hub curve. Defaults to 1.
30
+ """
31
+ super().__init__(hub_location=location, row_type=RowType.Outlet, stage_id=-1)
32
+ self.loss_function = None
33
+ self.location = location
34
+ self.num_streamlines = num_streamlines
35
+ # Evenly spaced from hub (0) to shroud (1)
36
+ self.percent_hub_shroud = np.linspace(0, 1, self.num_streamlines)
37
+
38
+ def init_static(self,P:Union[List[float],float],percent_radii:Union[List[float],float],massflow:float | None = None):
39
+ """Initialize turbine inputs
40
+
41
+ Args:
42
+ P (float): Exit static pressure [Pa]
43
+ percent_radii (Union[List[float],float]): percent radii where the exit static pressure is defined.
44
+ massflow (float | None): initialize with a massflow. Note this is only valid if you set the solver to solve to MassflowConstraint.AngleMatch
21
45
  """
22
- self.P = convert_to_ndarray(P)
23
46
  self.percent_hub_shroud = convert_to_ndarray(percent_radii)
24
47
  if len(self.percent_hub_shroud)==1:
25
- self.percent_hub_shroud = np.arange(0,1,num_streamlines)
26
- self.P = self.P[0]+0*self.percent_hub_shroud*0
48
+ self.percent_hub_shroud = np.linspace(0,1,self.num_streamlines)
49
+ self.P = convert_to_ndarray(P)
50
+ self.P = self.P[0]+0*self.percent_hub_shroud*0
27
51
  self.P_fun = interp1d(self.percent_hub_shroud,self.P)
28
- self.row_type = RowType.Outlet
29
- self.loss_function = None
30
- self.location = location
31
-
32
-
52
+
53
+ if massflow is not None:
54
+ self.total_massflow = massflow
55
+ self.outlet_type = OutletType.massflow_static_pressure
56
+ else:
57
+ self.outlet_type = OutletType.static_pressure
58
+
59
+ def init_total(self,P0:Union[List[float],float],percent_radii:Union[List[float],float]):
60
+ """Initialize compressor inputs
61
+
62
+ Args:
63
+ P0 (Union[List[float],float]): Exit Total Pressure (this will be matched)
64
+ percent_radii (Union[List[float],float]): percent radii where exit total pressure is defined
65
+ """
66
+
67
+ self.percent_hub_shroud = convert_to_ndarray(percent_radii)
68
+ self.IsCompressor = True
69
+ self.P0 = convert_to_ndarray(P0)
70
+ self.P0 = self.P0[0]+0*self.percent_hub_shroud*0
71
+ self.P0_fun = interp1d(self.percent_hub_shroud,self.P0)
72
+ self.IsCompressor = True
73
+ self.outlet_type = OutletType.total_pressure
74
+ self.P = self.P0 # Do this first but we will adjust
75
+
33
76
  def transfer_quantities(self,upstream:BladeRow):
34
77
  """Transfer quantities from upstream row to outlet while maintaining the outlet static pressure
35
78
 
36
79
  Args:
37
- upstream (BladeRow): Upstream row, for turbines this is a rotor, for compressors this is a stator.
80
+ upstream (BladeRow): Upstream row, for turbines this is a rotor, for compressors this is a stator.
38
81
  """
39
- self.__dict__ = upstream.__dict__.copy() # Copies P and hub shroud percentage
40
- self.P_fun = interp1d(self.percent_hub_shroud,self.P)
82
+ self.__dict__ = upstream.__dict__.copy() # Copies P and hub shroud percentage
83
+ if self.outlet_type == OutletType.static_pressure:
84
+ self.P_fun = interp1d(self.percent_hub_shroud,self.P)
85
+ else: # Compressor
86
+ self.P0_fun = interp1d(self.percent_hub_shroud,self.P0)
41
87
  self.row_type = RowType.Outlet
42
88
 
43
89
 
@@ -45,14 +91,27 @@ class Outlet(BladeRow):
45
91
  """Returns the static pressure at a certain percent hub_shroud
46
92
 
47
93
  Args:
48
- percent_hub_shroud (Union[float,npt.NDArray]): _description_
94
+ percent_hub_shroud (Union[float,npt.NDArray]): value or array from 0 to 1 where you want the exit static pressure
49
95
 
50
96
  Returns:
51
- _type_: _description_
97
+ npt.NDArray: Returns an array of static pressure
52
98
  """
53
99
  if type(percent_hub_shroud) == float:
54
100
  return float(self.P_fun(percent_hub_shroud))
55
101
  else:
56
102
  return self.P_fun(percent_hub_shroud)
103
+
104
+ def get_total_pressure(self, percent_hub_shroud:Union[float,npt.NDArray]):
105
+ """Returns the total pressure at a certain percent hub shroud
106
+
107
+ Args:
108
+ percent_hub_shroud (Union[float,npt.NDArray]): value or array from 0 to 1 where you want the exit total pressure
109
+
110
+ Returns:
111
+ npt.NDArray: Returns an array of total pressure
112
+ """
113
+ if type(percent_hub_shroud) == float:
114
+ return float(self.P0_fun(percent_hub_shroud))
115
+ else:
116
+ return self.P0_fun(percent_hub_shroud)
57
117
 
58
-