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
turbodesign/isentropic.py
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
+
from typing import Union
|
|
1
2
|
import numpy as np
|
|
3
|
+
import numpy.typing as npt
|
|
2
4
|
import math
|
|
3
5
|
|
|
6
|
+
ArrayLike = Union[float, npt.NDArray[np.float64]]
|
|
4
7
|
|
|
5
|
-
|
|
8
|
+
|
|
9
|
+
def _maybe_return_scalar(result: npt.NDArray[np.float64], *inputs: object) -> ArrayLike:
|
|
10
|
+
"""Return a float when all driving inputs were scalar, else an ndarray."""
|
|
11
|
+
if all(np.isscalar(inp) for inp in inputs):
|
|
12
|
+
return float(np.asarray(result))
|
|
13
|
+
return np.asarray(result, dtype=float)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def IsenP(M:ArrayLike,gamma:float) -> ArrayLike:
|
|
6
17
|
"""Computes the ratio P0/Ps
|
|
7
18
|
|
|
8
19
|
Args:
|
|
@@ -12,10 +23,12 @@ def IsenP(M:np.ndarray,gamma:float) -> float:
|
|
|
12
23
|
Returns:
|
|
13
24
|
float: P0/P ratio
|
|
14
25
|
"""
|
|
15
|
-
|
|
26
|
+
M_arr = np.asarray(M, dtype=float)
|
|
27
|
+
result = np.power((1+(gamma-1)/2.0 * M_arr*M_arr),gamma/(gamma-1))
|
|
28
|
+
return _maybe_return_scalar(result, M)
|
|
16
29
|
|
|
17
30
|
|
|
18
|
-
def FindMachP0P(P0_P:
|
|
31
|
+
def FindMachP0P(P0_P:ArrayLike,gamma:float) -> ArrayLike:
|
|
19
32
|
"""Finds the mach number given a P0/P ratio
|
|
20
33
|
|
|
21
34
|
Args:
|
|
@@ -26,14 +39,14 @@ def FindMachP0P(P0_P:np.ndarray,gamma:float) -> float:
|
|
|
26
39
|
float: [description]
|
|
27
40
|
"""
|
|
28
41
|
n = (gamma-1)/gamma
|
|
29
|
-
|
|
30
|
-
|
|
42
|
+
P0_P_arr = np.asarray(P0_P, dtype=float)
|
|
43
|
+
c = 2.0/(gamma-1) * (np.power(P0_P_arr,n) - 1.0)
|
|
31
44
|
M = np.sqrt(c)
|
|
32
|
-
return M
|
|
45
|
+
return _maybe_return_scalar(M, P0_P)
|
|
33
46
|
|
|
34
47
|
|
|
35
48
|
|
|
36
|
-
def IsenT(M:
|
|
49
|
+
def IsenT(M:ArrayLike,gamma:float) -> ArrayLike:
|
|
37
50
|
"""Computes T0/Ts
|
|
38
51
|
|
|
39
52
|
Args:
|
|
@@ -43,10 +56,12 @@ def IsenT(M:np.ndarray,gamma:float) -> float:
|
|
|
43
56
|
Returns:
|
|
44
57
|
float: Ratio of T0/Ts
|
|
45
58
|
"""
|
|
46
|
-
|
|
59
|
+
M_arr = np.asarray(M, dtype=float)
|
|
60
|
+
result = (1.0+(gamma-1.0)/2.0 *M_arr*M_arr)
|
|
61
|
+
return _maybe_return_scalar(result, M)
|
|
47
62
|
|
|
48
63
|
|
|
49
|
-
def A_As(M:
|
|
64
|
+
def A_As(M:ArrayLike,gamma:float) -> ArrayLike:
|
|
50
65
|
"""Computes the ratio of Area to Throat Area give a given mach number and gamma
|
|
51
66
|
|
|
52
67
|
Args:
|
|
@@ -58,11 +73,13 @@ def A_As(M:np.ndarray,gamma:float) -> float:
|
|
|
58
73
|
"""
|
|
59
74
|
a = (gamma+1.0)/(2.0*(gamma-1.0))
|
|
60
75
|
temp1 = np.power((gamma+1.0)/2.0,a)
|
|
61
|
-
|
|
62
|
-
|
|
76
|
+
M_arr = np.asarray(M, dtype=float)
|
|
77
|
+
temp2 = np.power((1+(gamma-1)/2*M_arr*M_arr),-a)/M_arr
|
|
78
|
+
result = temp1*temp2
|
|
79
|
+
return _maybe_return_scalar(result, M)
|
|
63
80
|
|
|
64
81
|
|
|
65
|
-
def Massflow(P0:
|
|
82
|
+
def Massflow(P0:ArrayLike,T0:ArrayLike,A:ArrayLike,M:ArrayLike,gamma:float,R:float=287) -> ArrayLike:
|
|
66
83
|
"""Massflow rate calculation
|
|
67
84
|
|
|
68
85
|
Args:
|
|
@@ -76,7 +93,34 @@ def Massflow(P0:float,T0:float,A:float,M:float,gamma:float,R:float=287):
|
|
|
76
93
|
Returns:
|
|
77
94
|
float: Nusselt Number
|
|
78
95
|
"""
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
P0_arr = np.asarray(P0, dtype=float)
|
|
97
|
+
T0_arr = np.asarray(T0, dtype=float)
|
|
98
|
+
A_arr = np.asarray(A, dtype=float)
|
|
99
|
+
M_arr = np.asarray(M, dtype=float)
|
|
100
|
+
gamma_val = float(gamma)
|
|
101
|
+
R_val = float(R)
|
|
102
|
+
mdot = A_arr * P0_arr/np.sqrt(T0_arr) * np.sqrt(gamma_val/R_val) * M_arr \
|
|
103
|
+
*np.power(1.0+(gamma_val-1.0)/2.0 * M_arr*M_arr, -(gamma_val+1.0)/(2.0*(gamma_val-1.0)))
|
|
104
|
+
return _maybe_return_scalar(mdot, P0, T0, A, M)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def solve_for_mach(M: float, massflow: float, P0: float, T0: float, area: float, gamma: float, R: float) -> float:
|
|
108
|
+
"""Residual between desired and estimated massflow for a guessed Mach number.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
M (float): Mach number guess (dimensionless).
|
|
112
|
+
massflow (float): Target massflow [kg/s].
|
|
113
|
+
P0 (float): Total pressure [Pa].
|
|
114
|
+
T0 (float): Total temperature [K].
|
|
115
|
+
area (float): Flow area [m^2].
|
|
116
|
+
gamma (float): Specific heat ratio Cp/Cv [-].
|
|
117
|
+
R (float): Gas constant [J/(kg·K)].
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
float: Absolute massflow residual [kg/s].
|
|
121
|
+
"""
|
|
122
|
+
expo = -(gamma + 1.0) / (2.0 * (gamma - 1.0))
|
|
81
123
|
|
|
82
|
-
|
|
124
|
+
estimate = area* P0/np.sqrt(T0)*np.sqrt(gamma / R)*M*np.power(1.0 + (gamma - 1.0) / 2.0 * M * M, expo)
|
|
125
|
+
residual = np.abs(massflow - estimate)
|
|
126
|
+
return residual
|
turbodesign/loss/__init__.py
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
OTAC compressor loss models
|
|
2
|
+
===========================
|
|
3
|
+
|
|
4
|
+
The classes in `otac.py` mirror the legacy OTAC `*.int` loss models. They are
|
|
5
|
+
currently placeholders that return zeros and emit a warning so the loss API can
|
|
6
|
+
be wired without failing.
|
|
7
|
+
|
|
8
|
+
When you translate an OTAC model, these mappings are a good starting point:
|
|
9
|
+
|
|
10
|
+
- `Fl_IR` → upstream `BladeRow` (inlet to the current row)
|
|
11
|
+
- `Fl_OR` → current `BladeRow` (outlet of the current row)
|
|
12
|
+
- `V`, `W`, `U`, `Vz`, `Vr`, `Vtheta` → `row.V`, `row.W`, `row.U`, `row.Vx`, `row.Vr`, `row.Vt`
|
|
13
|
+
- `alpha`, `beta` (flow angles) → `row.alpha1/alpha2` (absolute), `row.beta1/beta2` (relative)
|
|
14
|
+
- `ts`, `Tt`, `T` → static temperature `row.T`, total temperature `row.T0`
|
|
15
|
+
- `Ps`, `Pt`, `P` → static pressure `row.P`, total pressure `row.P0`
|
|
16
|
+
- `rho`, `rhos`, `rhot` → density `row.rho`
|
|
17
|
+
- `mu` → dynamic viscosity `row.mu`
|
|
18
|
+
- `ht` (total enthalpy) → `row.Cp * row.T0`
|
|
19
|
+
- `MN`, `M` → Mach number `row.M` (absolute) or `row.M_rel` (relative)
|
|
20
|
+
- `radius`, `radiusTipInlet`, `radiusExit` → `row.r` entries (`row.r[0]` hub, `row.r[-1]` tip)
|
|
21
|
+
- `bwidth`, `pitch`, `chord`, `throat` → `row.pitch`, `row.chord`, `row.throat`
|
|
22
|
+
|
|
23
|
+
Most OTAC models assume enthalpy loss; set the loss type accordingly in the
|
|
24
|
+
class `__init__`. If a model clamps the loss (e.g., to 25–50% of the available
|
|
25
|
+
enthalpy rise), keep those guards to avoid runaway penalties.
|
|
26
|
+
|
|
27
|
+
Recommended workflow:
|
|
28
|
+
|
|
29
|
+
1. Pick a model, open the matching `otac/*.int` file, and translate `calculate`
|
|
30
|
+
into a vectorized NumPy computation inside the corresponding class in
|
|
31
|
+
`otac.py`.
|
|
32
|
+
2. Replace the base class with `LossBaseClass` directly once implemented
|
|
33
|
+
(remove `_OTACStub` inheritance) and drop the warning.
|
|
34
|
+
3. Use `row`/`upstream` attributes from `BladeRow` for inputs; add any new
|
|
35
|
+
geometry fields you need to `BladeRow` with sensible defaults.
|
|
36
|
+
4. Return an array shaped like `row.r` (use `np.full_like(row.r, value)` when
|
|
37
|
+
the loss is a scalar).
|
|
38
|
+
|
|
39
|
+
This file is intentionally brief; keep notes here as you refine the mappings.
|
|
@@ -1 +1,55 @@
|
|
|
1
|
+
from .otac import (
|
|
2
|
+
AxialCompressorAungier,
|
|
3
|
+
AxialCompressorEntropy,
|
|
4
|
+
AxialCompressorWrightMiller,
|
|
5
|
+
AxialTurbineAinleyMathiesonOTAC,
|
|
6
|
+
AxialTurbineKackerOkapuuOTAC,
|
|
7
|
+
DiffuserVanelessStanitz,
|
|
8
|
+
ImpellerBladeLoadingAungier,
|
|
9
|
+
ImpellerBladeLoadingCoppage,
|
|
10
|
+
ImpellerClearanceJansen,
|
|
11
|
+
ImpellerDiscFrictionDaily,
|
|
12
|
+
ImpellerIncidenceAungier,
|
|
13
|
+
ImpellerIncidenceConrad,
|
|
14
|
+
ImpellerLeakageAungier,
|
|
15
|
+
ImpellerMixingAungier,
|
|
16
|
+
ImpellerMixingJohnston,
|
|
17
|
+
ImpellerPrescribed,
|
|
18
|
+
ImpellerRecirculationAungier,
|
|
19
|
+
ImpellerRecirculationOh,
|
|
20
|
+
ImpellerSkinFrictionCoppage,
|
|
21
|
+
ImpellerSkinFrictionJansen,
|
|
22
|
+
ImpellerVarious,
|
|
23
|
+
NASA23B20,
|
|
24
|
+
NASA74A,
|
|
25
|
+
RadialInput,
|
|
26
|
+
)
|
|
27
|
+
from .diffusion import DiffusionLoss
|
|
1
28
|
|
|
29
|
+
__all__ = [
|
|
30
|
+
"AxialCompressorAungier",
|
|
31
|
+
"AxialCompressorEntropy",
|
|
32
|
+
"AxialCompressorWrightMiller",
|
|
33
|
+
"AxialTurbineAinleyMathiesonOTAC",
|
|
34
|
+
"AxialTurbineKackerOkapuuOTAC",
|
|
35
|
+
"DiffuserVanelessStanitz",
|
|
36
|
+
"ImpellerBladeLoadingAungier",
|
|
37
|
+
"ImpellerBladeLoadingCoppage",
|
|
38
|
+
"ImpellerClearanceJansen",
|
|
39
|
+
"ImpellerDiscFrictionDaily",
|
|
40
|
+
"ImpellerIncidenceAungier",
|
|
41
|
+
"ImpellerIncidenceConrad",
|
|
42
|
+
"ImpellerLeakageAungier",
|
|
43
|
+
"ImpellerMixingAungier",
|
|
44
|
+
"ImpellerMixingJohnston",
|
|
45
|
+
"ImpellerPrescribed",
|
|
46
|
+
"ImpellerRecirculationAungier",
|
|
47
|
+
"ImpellerRecirculationOh",
|
|
48
|
+
"ImpellerSkinFrictionCoppage",
|
|
49
|
+
"ImpellerSkinFrictionJansen",
|
|
50
|
+
"ImpellerVarious",
|
|
51
|
+
"NASA23B20",
|
|
52
|
+
"NASA74A",
|
|
53
|
+
"RadialInput",
|
|
54
|
+
"DiffusionLoss",
|
|
55
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
from typing import Optional, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import numpy.typing as npt
|
|
5
|
+
|
|
6
|
+
from ..losstype import LossBaseClass
|
|
7
|
+
from ...enums import LossType, RowType
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from ...bladerow import BladeRow # for type hints only
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class DiffusionLoss(LossBaseClass):
|
|
14
|
+
"""Pressure-loss model based on diffusion factor.
|
|
15
|
+
|
|
16
|
+
Computes a spanwise diffusion factor and maps it to a pressure-loss
|
|
17
|
+
coefficient Yp via a simple linear ramp above a threshold.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
def __init__(self, df_limit: float = 0.45, df_knee: float = 0.6, df_max: float = 0.9, yp_at_max: float = 0.08):
|
|
21
|
+
"""
|
|
22
|
+
Args:
|
|
23
|
+
df_limit: Diffusion factor below which loss is negligible.
|
|
24
|
+
df_knee: Diffusion factor where loss starts ramping up noticeably.
|
|
25
|
+
df_max: Diffusion factor at which Yp reaches ``yp_at_max``.
|
|
26
|
+
yp_at_max: Pressure-loss coefficient when ``df_max`` is reached.
|
|
27
|
+
"""
|
|
28
|
+
super().__init__(LossType.Pressure)
|
|
29
|
+
self.df_limit = df_limit
|
|
30
|
+
self.df_knee = df_knee
|
|
31
|
+
self.df_max = df_max
|
|
32
|
+
self.yp_at_max = yp_at_max
|
|
33
|
+
|
|
34
|
+
def __call__(self, row: "BladeRow", upstream: "BladeRow") -> npt.NDArray:
|
|
35
|
+
"""Return pressure-loss coefficient Yp based on diffusion factor."""
|
|
36
|
+
# Choose absolute vs relative velocities depending on row type
|
|
37
|
+
if row.row_type == RowType.Rotor:
|
|
38
|
+
V1 = upstream.W
|
|
39
|
+
V2 = row.W
|
|
40
|
+
Vt1 = upstream.Wt
|
|
41
|
+
Vt2 = row.Wt
|
|
42
|
+
U = upstream.U
|
|
43
|
+
else:
|
|
44
|
+
V1 = upstream.V
|
|
45
|
+
V2 = row.V
|
|
46
|
+
Vt1 = upstream.Vt
|
|
47
|
+
Vt2 = row.Vt
|
|
48
|
+
U = 0.0
|
|
49
|
+
|
|
50
|
+
V1_mag = np.maximum(np.abs(V1), 1e-6)
|
|
51
|
+
V2_mag = np.abs(V2)
|
|
52
|
+
dVt = Vt2 - Vt1
|
|
53
|
+
|
|
54
|
+
df = 1.0 - V2_mag / V1_mag + dVt / np.maximum(np.abs(U), 1e-6)
|
|
55
|
+
|
|
56
|
+
# Map diffusion factor to Yp
|
|
57
|
+
Yp = np.zeros_like(row.percent_hub_shroud, dtype=float)
|
|
58
|
+
ramp = (df - self.df_limit) / max(self.df_knee - self.df_limit, 1e-6)
|
|
59
|
+
ramp = np.clip(ramp, 0.0, 1.0)
|
|
60
|
+
Yp = ramp * (self.yp_at_max * np.clip((df - self.df_limit) / max(self.df_max - self.df_limit, 1e-6), 0.0, 1.0))
|
|
61
|
+
return Yp
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Profile Loss Lieblein
|