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.
- {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/METADATA +2 -1
- turbo_design-1.3.10.dist-info/RECORD +46 -0
- {turbo_design-1.3.8.dist-info → turbo_design-1.3.10.dist-info}/WHEEL +1 -1
- turbodesign/__init__.py +57 -4
- turbodesign/agf.py +346 -0
- turbodesign/arrayfuncs.py +31 -1
- turbodesign/bladerow.py +238 -155
- turbodesign/compressor_math.py +386 -0
- turbodesign/compressor_spool.py +941 -0
- turbodesign/coolant.py +18 -6
- turbodesign/deviation/__init__.py +5 -0
- turbodesign/deviation/axial_compressor.py +3 -0
- turbodesign/deviation/carter_deviation.py +79 -0
- turbodesign/deviation/deviation_base.py +20 -0
- turbodesign/deviation/fixed_deviation.py +42 -0
- turbodesign/enums.py +5 -6
- turbodesign/flow_math.py +158 -0
- turbodesign/inlet.py +126 -56
- turbodesign/isentropic.py +59 -15
- turbodesign/loss/__init__.py +3 -1
- turbodesign/loss/compressor/OTAC_README.md +39 -0
- turbodesign/loss/compressor/__init__.py +54 -0
- turbodesign/loss/compressor/diffusion.py +61 -0
- turbodesign/loss/compressor/lieblein.py +1 -0
- turbodesign/loss/compressor/otac.py +799 -0
- turbodesign/loss/compressor/references/schobeiri-2012-shock-loss-model-for-transonic-and-supersonic-axial-compressors-with-curved-blades.pdf +0 -0
- turbodesign/loss/fixedpolytropic.py +27 -0
- turbodesign/loss/fixedpressureloss.py +30 -0
- turbodesign/loss/losstype.py +2 -30
- turbodesign/loss/turbine/TD2.py +25 -29
- turbodesign/loss/turbine/__init__.py +0 -1
- turbodesign/loss/turbine/ainleymathieson.py +6 -5
- turbodesign/loss/turbine/craigcox.py +6 -5
- turbodesign/loss/turbine/fixedefficiency.py +8 -7
- turbodesign/loss/turbine/kackerokapuu.py +7 -5
- turbodesign/loss/turbine/traupel.py +17 -16
- turbodesign/outlet.py +81 -22
- turbodesign/passage.py +98 -63
- turbodesign/row_factory.py +129 -0
- turbodesign/solve_radeq.py +9 -10
- turbodesign/{td_math.py → turbine_math.py} +144 -185
- turbodesign/turbine_spool.py +1219 -0
- turbo_design-1.3.8.dist-info/RECORD +0 -33
- turbodesign/compressorspool.py +0 -60
- turbodesign/loss/turbine/fixedpressureloss.py +0 -25
- turbodesign/rotor.py +0 -38
- turbodesign/spool.py +0 -317
- turbodesign/turbinespool.py +0 -543
|
Binary file
|
|
@@ -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
|
turbodesign/loss/losstype.py
CHANGED
|
@@ -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) ->
|
|
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
|
turbodesign/loss/turbine/TD2.py
CHANGED
|
@@ -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) ->
|
|
23
|
-
"""TD2-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
|
33
|
-
2. Stage reaction at meanline is 50
|
|
34
|
-
3. Axial velocity
|
|
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
|
-
|
|
39
|
-
|
|
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
|
-
|
|
41
|
+
numpy.ndarray: Total pressure loss coefficient repeated across ``row.r``.
|
|
44
42
|
"""
|
|
45
|
-
beta_in = row.beta1.mean()
|
|
46
|
-
beta_ex = row.beta2.mean() # flow angle
|
|
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) ->
|
|
73
|
-
"""
|
|
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
|
|
76
|
-
WYECOR
|
|
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
|
-
|
|
76
|
+
upstream (BladeRow): Upstream blade row supplying inlet conditions.
|
|
77
|
+
row (BladeRow): Blade row receiving the correction.
|
|
82
78
|
|
|
83
79
|
Returns:
|
|
84
|
-
|
|
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) ->
|
|
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
|
-
|
|
51
|
-
|
|
51
|
+
row (BladeRow): Blade row being evaluated.
|
|
52
|
+
upstream (BladeRow): Upstream blade row providing inlet conditions.
|
|
52
53
|
|
|
53
54
|
Returns:
|
|
54
|
-
|
|
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) ->
|
|
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
|
-
|
|
71
|
-
|
|
71
|
+
row (BladeRow): Downstream blade row being evaluated.
|
|
72
|
+
upstream (BladeRow): Upstream blade row providing inlet conditions.
|
|
72
73
|
|
|
73
74
|
Returns:
|
|
74
|
-
|
|
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) ->
|
|
16
|
+
def __call__(self,row:BladeRow, upstream:BladeRow) -> npt.NDArray:
|
|
16
17
|
"""Fixed efficiency loss
|
|
17
18
|
|
|
18
19
|
Args:
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
row (BladeRow): Blade row being evaluated.
|
|
21
|
+
upstream (BladeRow): Upstream blade row (unused, kept for API parity).
|
|
21
22
|
|
|
22
23
|
Returns:
|
|
23
|
-
|
|
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) ->
|
|
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
|
-
|
|
58
|
-
|
|
59
|
+
row (BladeRow): Blade row being evaluated.
|
|
60
|
+
upstream (BladeRow): Upstream blade row providing inlet conditions.
|
|
59
61
|
|
|
60
62
|
Returns:
|
|
61
|
-
|
|
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) ->
|
|
27
|
-
"""
|
|
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
|
-
|
|
31
|
-
|
|
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
|
-
|
|
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 #
|
|
56
|
-
zeta_r = F*g/h_rotor
|
|
57
|
-
x_p_stator = self.data['Fig01'](float(alpha1), float(alpha2))
|
|
58
|
-
x_p_rotor = self.data['Fig01'](float(beta2), float(beta3))
|
|
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
|
|
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
|
|
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))
|
|
89
|
+
zeta_cl = self.data['Fig08'](float(row.tip_clearance)) # Clearance loss for unshrouded blades
|
|
89
90
|
|
|
90
|
-
zeta_z = 0
|
|
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
|
|
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
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from typing import List, Optional, Union
|
|
4
|
+
|
|
3
5
|
from .enums import RowType
|
|
4
|
-
from .bladerow import BladeRow
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
20
|
-
|
|
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.
|
|
26
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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.
|
|
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]):
|
|
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
|
-
|
|
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
|
-
|