turbo-design 1.3.8__tar.gz → 1.3.9__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.
- {turbo_design-1.3.8 → turbo_design-1.3.9}/PKG-INFO +2 -1
- {turbo_design-1.3.8 → turbo_design-1.3.9}/pyproject.toml +3 -2
- turbo_design-1.3.9/turbodesign/__init__.py +62 -0
- turbo_design-1.3.9/turbodesign/agf.py +346 -0
- turbo_design-1.3.9/turbodesign/arrayfuncs.py +49 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/bladerow.py +237 -155
- turbo_design-1.3.9/turbodesign/compressor_math.py +374 -0
- turbo_design-1.3.9/turbodesign/compressor_spool.py +837 -0
- turbo_design-1.3.9/turbodesign/coolant.py +22 -0
- turbo_design-1.3.9/turbodesign/deviation/__init__.py +5 -0
- turbo_design-1.3.9/turbodesign/deviation/axial_compressor.py +3 -0
- turbo_design-1.3.9/turbodesign/deviation/carter_deviation.py +79 -0
- turbo_design-1.3.9/turbodesign/deviation/deviation_base.py +20 -0
- turbo_design-1.3.9/turbodesign/deviation/fixed_deviation.py +42 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/enums.py +5 -6
- turbo_design-1.3.9/turbodesign/flow_math.py +159 -0
- turbo_design-1.3.9/turbodesign/inlet.py +250 -0
- turbo_design-1.3.9/turbodesign/isentropic.py +126 -0
- turbo_design-1.3.9/turbodesign/loss/__init__.py +3 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/OTAC_README.md +39 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/__init__.py +55 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/diffusion.py +61 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/lieblein.py +1 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/otac.py +799 -0
- turbo_design-1.3.9/turbodesign/loss/compressor/references/schobeiri-2012-shock-loss-model-for-transonic-and-supersonic-axial-compressors-with-curved-blades.pdf +0 -0
- turbo_design-1.3.9/turbodesign/loss/fixedpolytropic.py +27 -0
- turbo_design-1.3.9/turbodesign/loss/fixedpressureloss.py +30 -0
- turbo_design-1.3.9/turbodesign/loss/losstype.py +27 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/TD2.py +25 -29
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/__init__.py +0 -1
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/ainleymathieson.py +6 -5
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/craigcox.py +6 -5
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/fixedefficiency.py +8 -7
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/kackerokapuu.py +7 -5
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/loss/turbine/traupel.py +17 -16
- turbo_design-1.3.9/turbodesign/outlet.py +117 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/passage.py +98 -63
- turbo_design-1.3.9/turbodesign/row_factory.py +129 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/solve_radeq.py +9 -10
- turbo_design-1.3.8/turbodesign/td_math.py → turbo_design-1.3.9/turbodesign/turbine_math.py +126 -175
- turbo_design-1.3.9/turbodesign/turbine_spool.py +984 -0
- turbo_design-1.3.8/turbodesign/__init__.py +0 -9
- turbo_design-1.3.8/turbodesign/arrayfuncs.py +0 -19
- turbo_design-1.3.8/turbodesign/compressorspool.py +0 -60
- turbo_design-1.3.8/turbodesign/coolant.py +0 -10
- turbo_design-1.3.8/turbodesign/inlet.py +0 -180
- turbo_design-1.3.8/turbodesign/isentropic.py +0 -82
- turbo_design-1.3.8/turbodesign/loss/__init__.py +0 -1
- turbo_design-1.3.8/turbodesign/loss/compressor/__init__.py +0 -1
- turbo_design-1.3.8/turbodesign/loss/losstype.py +0 -55
- turbo_design-1.3.8/turbodesign/loss/turbine/fixedpressureloss.py +0 -25
- turbo_design-1.3.8/turbodesign/outlet.py +0 -58
- turbo_design-1.3.8/turbodesign/rotor.py +0 -38
- turbo_design-1.3.8/turbodesign/spool.py +0 -317
- turbo_design-1.3.8/turbodesign/turbinespool.py +0 -543
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/cantera_gas/co2.yaml +0 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/lossinterp.py +0 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/radeq.py +0 -0
- {turbo_design-1.3.8 → turbo_design-1.3.9}/turbodesign/stage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: turbo-design
|
|
3
|
-
Version: 1.3.
|
|
3
|
+
Version: 1.3.9
|
|
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
|
|
@@ -20,3 +20,4 @@ Requires-Dist: pandas
|
|
|
20
20
|
Requires-Dist: plot3d
|
|
21
21
|
Requires-Dist: pyturbo-aero
|
|
22
22
|
Requires-Dist: scipy
|
|
23
|
+
Requires-Dist: shapely
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "turbo-design"
|
|
3
|
-
version = "1.3.
|
|
3
|
+
version = "1.3.9"
|
|
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 = [
|
|
@@ -17,9 +17,10 @@ findiff = "*"
|
|
|
17
17
|
matplotlib = "*"
|
|
18
18
|
pandas = "*"
|
|
19
19
|
python = ">=3.9"
|
|
20
|
+
shapely = "*"
|
|
20
21
|
|
|
21
22
|
[poetry.group.dev.dependencies]
|
|
22
|
-
python = ">=3.
|
|
23
|
+
python = ">=3.12"
|
|
23
24
|
|
|
24
25
|
[build-system]
|
|
25
26
|
requires = ["poetry>=1.1.2"]
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
from .turbine_spool import TurbineSpool
|
|
2
|
+
from .stage import Stage
|
|
3
|
+
from .enums import LossType, RowType, PassageType
|
|
4
|
+
from .inlet import Inlet
|
|
5
|
+
from .bladerow import BladeRow
|
|
6
|
+
from .coolant import Coolant
|
|
7
|
+
from .lossinterp import LossInterp
|
|
8
|
+
from .passage import Passage
|
|
9
|
+
from .outlet import Outlet
|
|
10
|
+
from .deviation import DeviationBaseClass, FixedDeviation
|
|
11
|
+
from .row_factory import make_blade_row, make_rotor_row, make_stator_row
|
|
12
|
+
from .agf import AGF_Setup, Inlet_bcs, Outlet_bcs, Settings, Clearance, Domain, read_agf, plot_airfoil_inputs, plot_airfoil_inputs_2D
|
|
13
|
+
|
|
14
|
+
# turbodesign/__init__.py
|
|
15
|
+
from importlib import import_module
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
"TurbineSpool", "Stage", "Inlet", "Outlet", "BladeRow", "Coolant",
|
|
19
|
+
"Passage", "RowType", "PassageType", "LossType",
|
|
20
|
+
"LossInterp", "DeviationBaseClass", "FixedDeviation",
|
|
21
|
+
"make_blade_row", "make_rotor_row", "make_stator_row",
|
|
22
|
+
"FixedPolytropicEfficiency",
|
|
23
|
+
"AGF_Setup", "Inlet_bcs", "Outlet_bcs", "Settings", "Clearance", "Domain", "read_agf",
|
|
24
|
+
"plot_airfoil_inputs", "plot_airfoil_inputs_2D",
|
|
25
|
+
]
|
|
26
|
+
|
|
27
|
+
_module_map = {
|
|
28
|
+
"TurbineSpool": ("turbodesign.turbine_spool", "TurbineSpool"),
|
|
29
|
+
"Stage": ("turbodesign.stage", "Stage"),
|
|
30
|
+
"Inlet": ("turbodesign.inlet", "Inlet"),
|
|
31
|
+
"Outlet": ("turbodesign.outlet", "Outlet"),
|
|
32
|
+
"BladeRow": ("turbodesign.bladerow", "BladeRow"),
|
|
33
|
+
"Coolant": ("turbodesign.coolant", "Coolant"),
|
|
34
|
+
"Passage": ("turbodesign.passage", "Passage"),
|
|
35
|
+
"LossType": ("turbodesign.enums", "LossType"),
|
|
36
|
+
"RowType": ("turbodesign.enums", "RowType"),
|
|
37
|
+
"PassageType": ("turbodesign.enums", "PassageType"),
|
|
38
|
+
"LossInterp": ("turbodesign.lossinterp", "LossInterp"),
|
|
39
|
+
"FixedPolytropicEfficiency": ("turbodesign.loss.fixedpolytropic", "FixedPolytropicEfficiency"),
|
|
40
|
+
"DeviationBaseClass": ("turbodesign.deviation", "DeviationBaseClass"),
|
|
41
|
+
"FixedDeviation": ("turbodesign.deviation", "FixedDeviation"),
|
|
42
|
+
"make_blade_row": ("turbodesign.row_factory", "make_blade_row"),
|
|
43
|
+
"make_rotor_row": ("turbodesign.row_factory", "make_rotor_row"),
|
|
44
|
+
"make_stator_row": ("turbodesign.row_factory", "make_stator_row"),
|
|
45
|
+
"AGF_Setup": ("turbodesign.agf", "AGF_Setup"),
|
|
46
|
+
"Inlet_bcs": ("turbodesign.agf", "Inlet_bcs"),
|
|
47
|
+
"Outlet_bcs": ("turbodesign.agf", "Outlet_bcs"),
|
|
48
|
+
"Settings": ("turbodesign.agf", "Settings"),
|
|
49
|
+
"Clearance": ("turbodesign.agf", "Clearance"),
|
|
50
|
+
"Domain": ("turbodesign.agf", "Domain"),
|
|
51
|
+
"read_agf": ("turbodesign.agf", "read_agf"),
|
|
52
|
+
"plot_airfoil_inputs": ("turbodesign.agf", "plot_airfoil_inputs"),
|
|
53
|
+
"plot_airfoil_inputs_2D": ("turbodesign.agf", "plot_airfoil_inputs_2D"),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
def __getattr__(name: str):
|
|
57
|
+
try:
|
|
58
|
+
mod_name, attr = _module_map[name]
|
|
59
|
+
except KeyError:
|
|
60
|
+
raise AttributeError(name)
|
|
61
|
+
mod = import_module(mod_name)
|
|
62
|
+
return getattr(mod, attr)
|
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
"""AGF generation and parsing utilities."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, asdict
|
|
4
|
+
from typing import List
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import numpy.typing as npt
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class Inlet_bcs:
|
|
13
|
+
ptin: float
|
|
14
|
+
ttin: float
|
|
15
|
+
pspan: float
|
|
16
|
+
machin: float
|
|
17
|
+
alpin: float
|
|
18
|
+
phiin: float
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@dataclass
|
|
22
|
+
class Outlet_bcs:
|
|
23
|
+
rpm: float
|
|
24
|
+
gamma: float
|
|
25
|
+
psout: float
|
|
26
|
+
twall: float
|
|
27
|
+
molwt: float
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class Domain:
|
|
32
|
+
xhup: float # xhub upstream
|
|
33
|
+
rhup: float # rhub upstream
|
|
34
|
+
xtup: float # xtip upstream
|
|
35
|
+
rtup: float # rtip upstream
|
|
36
|
+
|
|
37
|
+
xhdw: float # xhub downwind
|
|
38
|
+
rhdw: float # rhub downwind
|
|
39
|
+
xtdw: float # xtip downwind
|
|
40
|
+
rtdw: float # rtip downwind
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass
|
|
44
|
+
class Settings:
|
|
45
|
+
nprof: int = 1 # number of spanwise points that define pitched average inlet profile
|
|
46
|
+
ifang: int = 10 # 0 - adiabatic wall, 1 - temperature wall
|
|
47
|
+
hbl: float = 1 # Inlet hub boundary layer thickness as percent span
|
|
48
|
+
tbl: float = 1 # Inlet tip boundary layer thickness as percent span
|
|
49
|
+
|
|
50
|
+
nblades: int = 8 # number of blades
|
|
51
|
+
npts: int = 300 # number of points per blade
|
|
52
|
+
nspans: int = 3 # number of spans/sections
|
|
53
|
+
ity: int = 5 # What format are the blades in.
|
|
54
|
+
# ITY 5 = x rth r
|
|
55
|
+
# ITY 7 = x1,theta1,r1,x2,theta2,r2
|
|
56
|
+
# ITY 10 = x1,y1,z1,x2,y2,z2
|
|
57
|
+
iym: int = 0 # 0 = do not flip airfoil along x-axis
|
|
58
|
+
tcls: int = 1 # 1 = has tip clearance
|
|
59
|
+
hcls: int = 0 # 0 = no hub clearance
|
|
60
|
+
lete: int = 10 # do not modify leading edge or trailing edge
|
|
61
|
+
isplit: int = 0 # no splitters
|
|
62
|
+
|
|
63
|
+
nht: int = 1 # Number of axial points defining the endwall
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@dataclass
|
|
67
|
+
class Clearance:
|
|
68
|
+
tlecl: float # tip leading edge clearance
|
|
69
|
+
tmccl: float # tip mid clearance
|
|
70
|
+
ttecl: float # tip te clearance
|
|
71
|
+
hlecl: float = 0 # hub le clearance
|
|
72
|
+
hmccl: float = 0 # hub mid clearance
|
|
73
|
+
htecl: float = 0 # hub te clearance
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class AGF_Setup:
|
|
77
|
+
def __init__(self, template_file: str = "template.agf", name="radial-turbine"):
|
|
78
|
+
self.agf_template = template_file
|
|
79
|
+
self.name = name
|
|
80
|
+
self.endwall: str = ""
|
|
81
|
+
self.sections: str = ""
|
|
82
|
+
self.clearance: Clearance
|
|
83
|
+
self.settings: Settings
|
|
84
|
+
self.domain: Domain
|
|
85
|
+
self.inlet: Inlet_bcs
|
|
86
|
+
self.outlet: Outlet_bcs
|
|
87
|
+
|
|
88
|
+
def add_passage(self, hub: npt.NDArray, shroud: npt.NDArray):
|
|
89
|
+
"""Add passage hub/shroud geometry."""
|
|
90
|
+
domain = Domain(
|
|
91
|
+
xhup=hub[0, 0],
|
|
92
|
+
rhup=hub[0, 1],
|
|
93
|
+
xtup=shroud[0, 0],
|
|
94
|
+
rtup=shroud[0, 1],
|
|
95
|
+
xhdw=hub[-1, 0],
|
|
96
|
+
rhdw=hub[-1, 1],
|
|
97
|
+
xtdw=shroud[-1, 0],
|
|
98
|
+
rtdw=shroud[-1, 1],
|
|
99
|
+
)
|
|
100
|
+
endwall = []
|
|
101
|
+
for i in range(hub.shape[0]):
|
|
102
|
+
xl = hub[i, 0]
|
|
103
|
+
rl = hub[i, 1]
|
|
104
|
+
xu = shroud[i, 0]
|
|
105
|
+
ru = shroud[i, 1]
|
|
106
|
+
if i < hub.shape[0] - 1:
|
|
107
|
+
endwall.append(f"{xl:.4f} {rl:.4f} {xu:.4f} {ru:.4f}\n")
|
|
108
|
+
else:
|
|
109
|
+
endwall.append(f"{xl:.4f} {rl:.4f} {xu:.4f} {ru:.4f}")
|
|
110
|
+
self.endwall = "".join(endwall)
|
|
111
|
+
self.domain = domain
|
|
112
|
+
self.settings.nht = hub.shape[0]
|
|
113
|
+
|
|
114
|
+
def add_blade(self, ss: npt.NDArray, ps: npt.NDArray, IsDuct: bool = False):
|
|
115
|
+
"""Add the blade geometry."""
|
|
116
|
+
sections = []
|
|
117
|
+
if IsDuct:
|
|
118
|
+
self.settings.nspans = 0
|
|
119
|
+
else:
|
|
120
|
+
self.settings.nspans = ss.shape[0]
|
|
121
|
+
section_indx = 1
|
|
122
|
+
|
|
123
|
+
for i in range(ss.shape[0]):
|
|
124
|
+
x = np.hstack([ss[i, :, 0], ps[i, 1:-1, 0]])
|
|
125
|
+
y = np.hstack([ss[i, :, 1], ps[i, 1:-1, 1]])
|
|
126
|
+
z = np.hstack([ss[i, :, 2], ps[i, 1:-1, 2]])
|
|
127
|
+
n = len(x) # number of points
|
|
128
|
+
self.settings.npts = n
|
|
129
|
+
r = np.sqrt(y**2 + z**2)
|
|
130
|
+
th = np.arctan2(y, z)
|
|
131
|
+
rth = r * th
|
|
132
|
+
sections.append(f"*SECTION\t{section_indx}\n")
|
|
133
|
+
sections.append(f"- SECTION - {section_indx}\t{n}\n")
|
|
134
|
+
sections.append(">----RAD------XOFF------YOFF------ROTD----CONEANGLE----\n")
|
|
135
|
+
sections.append("0.0000\t0.0000\t0.0000\t0.0000\t0.0000\n")
|
|
136
|
+
sections.append("x rth r\n")
|
|
137
|
+
for j in range(len(x)):
|
|
138
|
+
sections.append(f"{x[j]:0.6f} {rth[j]:0.6f} {r[j]:0.6f}\n")
|
|
139
|
+
section_indx += 1
|
|
140
|
+
|
|
141
|
+
self.sections = "".join(sections)
|
|
142
|
+
|
|
143
|
+
def add_clearance(self, clearance: Clearance):
|
|
144
|
+
self.clearance = clearance
|
|
145
|
+
|
|
146
|
+
def add_settings(self, settings: Settings):
|
|
147
|
+
self.settings = settings
|
|
148
|
+
|
|
149
|
+
def add_inlet(self, inlet: Inlet_bcs):
|
|
150
|
+
self.inlet = inlet
|
|
151
|
+
|
|
152
|
+
def add_outlet(self, outlet: Outlet_bcs):
|
|
153
|
+
self.outlet = outlet
|
|
154
|
+
|
|
155
|
+
def build(self, output_filename: str = "stator.agf"):
|
|
156
|
+
with open(self.agf_template, "r") as file:
|
|
157
|
+
file_content = file.read()
|
|
158
|
+
file_content = file_content.replace("[name]", f"{self.name}")
|
|
159
|
+
|
|
160
|
+
domain_dict = asdict(self.domain)
|
|
161
|
+
clearance_dict = asdict(self.clearance)
|
|
162
|
+
settings_dict = asdict(self.settings)
|
|
163
|
+
inlet_dict = asdict(self.inlet)
|
|
164
|
+
outlet_dict = asdict(self.outlet)
|
|
165
|
+
|
|
166
|
+
with open(output_filename, "w") as f:
|
|
167
|
+
for k, v in domain_dict.items():
|
|
168
|
+
file_content = file_content.replace(f"[{k}]", f"{v:0.4f}")
|
|
169
|
+
|
|
170
|
+
for k, v in clearance_dict.items():
|
|
171
|
+
file_content = file_content.replace(f"[{k}]", f"{v}")
|
|
172
|
+
|
|
173
|
+
for k, v in settings_dict.items():
|
|
174
|
+
file_content = file_content.replace(f"[{k}]", f"{v}")
|
|
175
|
+
|
|
176
|
+
for k, v in inlet_dict.items():
|
|
177
|
+
file_content = file_content.replace(f"[{k}]", f"{v:0.4f}")
|
|
178
|
+
|
|
179
|
+
for k, v in outlet_dict.items():
|
|
180
|
+
file_content = file_content.replace(f"[{k}]", f"{v:0.4f}")
|
|
181
|
+
|
|
182
|
+
if self.sections:
|
|
183
|
+
file_content = file_content.replace("[sections]", self.sections)
|
|
184
|
+
|
|
185
|
+
file_content = file_content.replace("[endwall]", self.endwall)
|
|
186
|
+
f.write(file_content)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def read_agf(file_path: str) -> dict:
|
|
190
|
+
"""
|
|
191
|
+
Parse an AGF file and reconstruct the data classes used to build it.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
dict with keys:
|
|
195
|
+
settings (Settings)
|
|
196
|
+
clearance (Clearance)
|
|
197
|
+
domain (Domain)
|
|
198
|
+
inlet (Inlet_bcs)
|
|
199
|
+
outlet (Outlet_bcs)
|
|
200
|
+
hub (np.ndarray): shape (nht, 2) of x, r (hub)
|
|
201
|
+
shroud (np.ndarray): shape (nht, 2) of x, r (shroud)
|
|
202
|
+
sections (np.ndarray): shape (n_sections, npts, 3) of x, rth, r
|
|
203
|
+
"""
|
|
204
|
+
with open(file_path, "r") as f:
|
|
205
|
+
lines = [ln.strip() for ln in f.readlines()]
|
|
206
|
+
|
|
207
|
+
def find_after(marker: str) -> List[str]:
|
|
208
|
+
for idx, ln in enumerate(lines):
|
|
209
|
+
if ln.startswith(marker):
|
|
210
|
+
return lines[idx + 1].split()
|
|
211
|
+
return []
|
|
212
|
+
|
|
213
|
+
# Settings and counts
|
|
214
|
+
nb_vals = find_after("*NBLADE")
|
|
215
|
+
nblades, npts, nspans, ity, iym, tcls, hcls, lete, isplit = [int(float(v)) for v in nb_vals]
|
|
216
|
+
|
|
217
|
+
units_vals = find_after("*UNITS")
|
|
218
|
+
_, nprof, ifang, hbl, tbl, tfree, lfree = units_vals
|
|
219
|
+
nprof = int(nprof)
|
|
220
|
+
ifang = int(ifang)
|
|
221
|
+
|
|
222
|
+
settings = Settings(
|
|
223
|
+
nprof=nprof,
|
|
224
|
+
ifang=ifang,
|
|
225
|
+
hbl=float(hbl),
|
|
226
|
+
tbl=float(tbl),
|
|
227
|
+
nblades=nblades,
|
|
228
|
+
npts=npts,
|
|
229
|
+
nspans=nspans,
|
|
230
|
+
ity=int(ity),
|
|
231
|
+
iym=int(iym),
|
|
232
|
+
tcls=int(tcls),
|
|
233
|
+
hcls=int(hcls),
|
|
234
|
+
lete=int(lete),
|
|
235
|
+
isplit=int(isplit),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
rpm_vals = find_after("*RPMS")
|
|
239
|
+
rpm, gamma, psout, twall, molwt = [float(v) for v in rpm_vals]
|
|
240
|
+
outlet = Outlet_bcs(rpm=rpm, gamma=gamma, psout=psout, twall=twall, molwt=molwt)
|
|
241
|
+
|
|
242
|
+
pspan_vals = find_after("*PSPAN")
|
|
243
|
+
pspan, machin, ptin, ttin, alpin, phiin = [float(v) for v in pspan_vals]
|
|
244
|
+
inlet = Inlet_bcs(ptin=ptin, ttin=ttin, pspan=pspan, machin=machin, alpin=alpin, phiin=phiin)
|
|
245
|
+
|
|
246
|
+
xhup_vals = find_after("*XHUP")
|
|
247
|
+
xhup, rhup, xtup, rtup = [float(v) for v in xhup_vals]
|
|
248
|
+
xhdw_vals = find_after("*XHDW")
|
|
249
|
+
xhdw, rhdw, xtdw, rtdw = [float(v) for v in xhdw_vals]
|
|
250
|
+
domain = Domain(xhup=xhup, rhup=rhup, xtup=xtup, rtup=rtup, xhdw=xhdw, rhdw=rhdw, xtdw=xtdw, rtdw=rtdw)
|
|
251
|
+
|
|
252
|
+
tlecl_vals = find_after("*TLECL")
|
|
253
|
+
hlecl_vals = find_after("*HLECL")
|
|
254
|
+
clearance = Clearance(
|
|
255
|
+
tlecl=float(tlecl_vals[0]) if tlecl_vals else 0.0,
|
|
256
|
+
tmccl=float(tlecl_vals[1]) if len(tlecl_vals) > 1 else 0.0,
|
|
257
|
+
ttecl=float(tlecl_vals[2]) if len(tlecl_vals) > 2 else 0.0,
|
|
258
|
+
hlecl=float(hlecl_vals[0]) if hlecl_vals else 0.0,
|
|
259
|
+
hmccl=float(hlecl_vals[1]) if len(hlecl_vals) > 1 else 0.0,
|
|
260
|
+
htecl=float(hlecl_vals[2]) if len(hlecl_vals) > 2 else 0.0,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
nht_vals = find_after("*NHT")
|
|
264
|
+
if nht_vals:
|
|
265
|
+
settings.nht = int(float(nht_vals[0]))
|
|
266
|
+
|
|
267
|
+
# Endwall parsing
|
|
268
|
+
hub_pts: List[List[float]] = []
|
|
269
|
+
shroud_pts: List[List[float]] = []
|
|
270
|
+
if "*ENDWALL" in lines:
|
|
271
|
+
start = lines.index("*ENDWALL") + 2 # skip header and column line
|
|
272
|
+
idx = start
|
|
273
|
+
while idx < len(lines) and not lines[idx].startswith("*SECTION"):
|
|
274
|
+
parts = [p for p in lines[idx].split() if p]
|
|
275
|
+
if len(parts) == 4:
|
|
276
|
+
xl, rl, xu, ru = [float(p) for p in parts]
|
|
277
|
+
hub_pts.append([xl, rl])
|
|
278
|
+
shroud_pts.append([xu, ru])
|
|
279
|
+
idx += 1
|
|
280
|
+
hub_arr = np.array(hub_pts) if hub_pts else np.zeros((0, 2))
|
|
281
|
+
shroud_arr = np.array(shroud_pts) if shroud_pts else np.zeros((0, 2))
|
|
282
|
+
|
|
283
|
+
# Sections parsing
|
|
284
|
+
sections: List[np.ndarray] = []
|
|
285
|
+
idx = 0
|
|
286
|
+
while idx < len(lines):
|
|
287
|
+
line = lines[idx]
|
|
288
|
+
if line.startswith("*SECTION"):
|
|
289
|
+
idx += 1 # - SECTION - line
|
|
290
|
+
section_info = lines[idx].split()
|
|
291
|
+
npts_section = int(section_info[-1])
|
|
292
|
+
idx += 3 # skip offset/cone headers to column header
|
|
293
|
+
idx += 1 # column header line
|
|
294
|
+
pts = []
|
|
295
|
+
for _ in range(npts_section):
|
|
296
|
+
parts = lines[idx].split()
|
|
297
|
+
if len(parts) >= 3:
|
|
298
|
+
pts.append([float(parts[0]), float(parts[1]), float(parts[2])])
|
|
299
|
+
idx += 1
|
|
300
|
+
sections.append(np.array(pts))
|
|
301
|
+
continue
|
|
302
|
+
idx += 1
|
|
303
|
+
|
|
304
|
+
sections_arr = np.array(sections) if sections else np.zeros((0, 0, 3))
|
|
305
|
+
|
|
306
|
+
return {
|
|
307
|
+
"settings": settings,
|
|
308
|
+
"clearance": clearance,
|
|
309
|
+
"domain": domain,
|
|
310
|
+
"inlet": inlet,
|
|
311
|
+
"outlet": outlet,
|
|
312
|
+
"hub": hub_arr,
|
|
313
|
+
"shroud": shroud_arr,
|
|
314
|
+
"sections": sections_arr,
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def plot_airfoil_inputs(nsections: int, npts: int):
|
|
319
|
+
xthr = np.zeros(shape=(nsections, npts, 3)) # section_num x theta r
|
|
320
|
+
with open("AIRFOIL.INPUTS", "r") as f:
|
|
321
|
+
[f.readline() for _ in range(4)] # skip first 4 lines
|
|
322
|
+
for i in range(nsections):
|
|
323
|
+
for j in range(npts):
|
|
324
|
+
line = f.readline()
|
|
325
|
+
temp = [float(p) for p in line.split(" ") if p]
|
|
326
|
+
xthr[i, j, 0] = temp[0]
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
def plot_airfoil_inputs_2D(nsections: int, npts: int):
|
|
330
|
+
xthr = np.zeros(shape=(nsections, npts, 3))
|
|
331
|
+
with open("AIRFOIL.INPUTS", "r") as f:
|
|
332
|
+
[f.readline() for _ in range(4)]
|
|
333
|
+
for i in range(nsections):
|
|
334
|
+
for j in range(npts):
|
|
335
|
+
line = f.readline()
|
|
336
|
+
temp = [float(p) for p in line.split(" ") if p]
|
|
337
|
+
xthr[i, j, 0] = temp[0]
|
|
338
|
+
xthr[i, j, 1] = temp[1]
|
|
339
|
+
xthr[i, j, 2] = temp[2]
|
|
340
|
+
[f.readline() for _ in range(2)]
|
|
341
|
+
for i in range(nsections):
|
|
342
|
+
plt.plot(xthr[i, :, 0], xthr[i, :, 1], "r", label=f"section {i}")
|
|
343
|
+
plt.axis("equal")
|
|
344
|
+
plt.xlabel("x-axial")
|
|
345
|
+
plt.ylabel("y")
|
|
346
|
+
plt.show()
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
from typing import List
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def convert_to_list(x:float) -> List[float]:
|
|
6
|
+
return x if isinstance(x, list) else [x]
|
|
7
|
+
|
|
8
|
+
def convert_to_ndarray(t) -> np.ndarray:
|
|
9
|
+
"""Converts a scalar or list to a numpy array
|
|
10
|
+
Args:
|
|
11
|
+
t (float,list): [description]
|
|
12
|
+
Returns:
|
|
13
|
+
np.ndarray: variable as an array
|
|
14
|
+
"""
|
|
15
|
+
if hasattr(t, "default_factory"):
|
|
16
|
+
try:
|
|
17
|
+
t = t.default_factory()
|
|
18
|
+
except Exception:
|
|
19
|
+
t = np.array([0.0])
|
|
20
|
+
if type(t) is not np.ndarray and type(t) is not list: # Scalar
|
|
21
|
+
t = np.array([t],dtype=float)
|
|
22
|
+
elif (type(t) is list):
|
|
23
|
+
t = np.array(t,dtype=float)
|
|
24
|
+
return t
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def safe_interpolate(values, src_r, dst_r, default: float = 0.0, radians: bool = False, interp_func=None):
|
|
28
|
+
"""Safely convert, default, and interpolate quantities onto target radii."""
|
|
29
|
+
from .bladerow import interpolate_quantities # local import to avoid cycles
|
|
30
|
+
|
|
31
|
+
arr = convert_to_ndarray(values)
|
|
32
|
+
if hasattr(arr, "default_factory"):
|
|
33
|
+
arr = arr.default_factory()
|
|
34
|
+
if arr.size == 0:
|
|
35
|
+
arr = np.array([default], dtype=float)
|
|
36
|
+
src = convert_to_ndarray(src_r)
|
|
37
|
+
dst = convert_to_ndarray(dst_r)
|
|
38
|
+
if src.size == 0:
|
|
39
|
+
src = np.linspace(0, 1, len(arr))
|
|
40
|
+
if arr.size > 1 and src.size != arr.size:
|
|
41
|
+
src = np.linspace(0, 1, len(arr))
|
|
42
|
+
if arr.size == 1:
|
|
43
|
+
arr = arr[0] * np.ones_like(dst)
|
|
44
|
+
else:
|
|
45
|
+
f = interp_func if interp_func is not None else interpolate_quantities
|
|
46
|
+
arr = f(arr, src, dst)
|
|
47
|
+
if radians:
|
|
48
|
+
arr = np.radians(arr)
|
|
49
|
+
return arr
|