gmshairfoil2d 0.2.31__py3-none-any.whl → 0.2.33__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.
- gmshairfoil2d/__init__.py +1 -1
- gmshairfoil2d/airfoil_func.py +82 -23
- gmshairfoil2d/gmshairfoil2d.py +3 -2
- {gmshairfoil2d-0.2.31.dist-info → gmshairfoil2d-0.2.33.dist-info}/METADATA +1 -1
- gmshairfoil2d-0.2.33.dist-info/RECORD +16 -0
- tests/test_airfoil_func.py +4 -4
- gmshairfoil2d-0.2.31.dist-info/RECORD +0 -16
- {gmshairfoil2d-0.2.31.dist-info → gmshairfoil2d-0.2.33.dist-info}/WHEEL +0 -0
- {gmshairfoil2d-0.2.31.dist-info → gmshairfoil2d-0.2.33.dist-info}/entry_points.txt +0 -0
- {gmshairfoil2d-0.2.31.dist-info → gmshairfoil2d-0.2.33.dist-info}/licenses/LICENSE +0 -0
- {gmshairfoil2d-0.2.31.dist-info → gmshairfoil2d-0.2.33.dist-info}/top_level.txt +0 -0
gmshairfoil2d/__init__.py
CHANGED
gmshairfoil2d/airfoil_func.py
CHANGED
|
@@ -131,7 +131,7 @@ def get_airfoil_file(airfoil_name):
|
|
|
131
131
|
sys.exit(1)
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
def get_airfoil_points(airfoil_name):
|
|
134
|
+
def get_airfoil_points(airfoil_name: str) -> list[tuple[float, float, float]]:
|
|
135
135
|
"""Load airfoil points from the database.
|
|
136
136
|
|
|
137
137
|
Parameters
|
|
@@ -149,14 +149,25 @@ def get_airfoil_points(airfoil_name):
|
|
|
149
149
|
ValueError
|
|
150
150
|
If no valid points found for the airfoil
|
|
151
151
|
"""
|
|
152
|
+
if len(airfoil_name) == 4 and airfoil_name.isdigit():
|
|
153
|
+
return four_digit_naca_airfoil(
|
|
154
|
+
naca_name=airfoil_name,
|
|
155
|
+
)
|
|
156
|
+
|
|
152
157
|
get_airfoil_file(airfoil_name)
|
|
153
158
|
airfoil_file = Path(database_dir, f"{airfoil_name}.dat")
|
|
154
159
|
|
|
155
160
|
airfoil_points = []
|
|
156
161
|
with open(airfoil_file) as f:
|
|
157
162
|
for line in f:
|
|
163
|
+
line = line.strip()
|
|
164
|
+
if not line or line.startswith(('#', 'Airfoil')):
|
|
165
|
+
continue
|
|
166
|
+
parts = line.split()
|
|
167
|
+
if len(parts) != 2:
|
|
168
|
+
continue
|
|
158
169
|
try:
|
|
159
|
-
x, y = map(float,
|
|
170
|
+
x, y = map(float, parts)
|
|
160
171
|
except ValueError:
|
|
161
172
|
continue
|
|
162
173
|
if x > 1 and y > 1:
|
|
@@ -166,35 +177,83 @@ def get_airfoil_points(airfoil_name):
|
|
|
166
177
|
if not airfoil_points:
|
|
167
178
|
raise ValueError(f"No valid points found for airfoil {airfoil_name}")
|
|
168
179
|
|
|
169
|
-
|
|
170
|
-
|
|
180
|
+
def _dedupe_consecutive(points, tol=1e-9):
|
|
181
|
+
out = []
|
|
182
|
+
for x, y in points:
|
|
183
|
+
if not out:
|
|
184
|
+
out.append((x, y))
|
|
185
|
+
continue
|
|
186
|
+
if abs(x - out[-1][0]) <= tol and abs(y - out[-1][1]) <= tol:
|
|
187
|
+
continue
|
|
188
|
+
out.append((x, y))
|
|
189
|
+
return out
|
|
171
190
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
191
|
+
def _dedupe_any(points, tol=1e-9):
|
|
192
|
+
out = []
|
|
193
|
+
for x, y in points:
|
|
194
|
+
if any(abs(x - ux) <= tol and abs(y - uy) <= tol for ux, uy in out):
|
|
195
|
+
continue
|
|
196
|
+
out.append((x, y))
|
|
197
|
+
return out
|
|
177
198
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if lower_points and lower_points[0][0] == 0:
|
|
182
|
-
lower_points = lower_points[::-1]
|
|
199
|
+
tol = 1e-9
|
|
200
|
+
airfoil_points = _dedupe_consecutive(airfoil_points, tol=tol)
|
|
183
201
|
|
|
184
|
-
|
|
185
|
-
|
|
202
|
+
if len(airfoil_points) < 3:
|
|
203
|
+
raise ValueError(f"Not enough unique points for airfoil {airfoil_name}")
|
|
186
204
|
|
|
187
|
-
|
|
188
|
-
|
|
205
|
+
# Split into upper/lower when a LE point repeats (common in UIUC files)
|
|
206
|
+
min_x = min(x for x, _ in airfoil_points)
|
|
207
|
+
le_indices = [i for i, (x, _) in enumerate(airfoil_points) if abs(x - min_x) <= tol]
|
|
208
|
+
|
|
209
|
+
if len(le_indices) >= 2:
|
|
210
|
+
split_idx = le_indices[1]
|
|
211
|
+
upper = airfoil_points[:split_idx]
|
|
212
|
+
lower = airfoil_points[split_idx:]
|
|
213
|
+
else:
|
|
214
|
+
# Fallback: split at first maximum x (trailing edge)
|
|
215
|
+
max_x = max(x for x, _ in airfoil_points)
|
|
216
|
+
split_idx = next(i for i, (x, _) in enumerate(airfoil_points) if abs(x - max_x) <= tol)
|
|
217
|
+
upper = airfoil_points[:split_idx + 1]
|
|
218
|
+
lower = airfoil_points[split_idx + 1:]
|
|
219
|
+
|
|
220
|
+
def _ensure_le_to_te(points):
|
|
221
|
+
if len(points) < 2:
|
|
222
|
+
return points
|
|
223
|
+
return points if points[0][0] <= points[-1][0] else points[::-1]
|
|
224
|
+
|
|
225
|
+
upper = _ensure_le_to_te(upper)
|
|
226
|
+
lower = _ensure_le_to_te(lower)
|
|
227
|
+
|
|
228
|
+
# Build a closed loop starting at TE: TE->LE (upper reversed) then LE->TE (lower)
|
|
229
|
+
if upper and lower:
|
|
230
|
+
upper = upper[::-1]
|
|
231
|
+
loop = upper + lower[1:]
|
|
232
|
+
else:
|
|
233
|
+
loop = airfoil_points
|
|
234
|
+
|
|
235
|
+
# Remove duplicate closing point if present
|
|
236
|
+
if len(loop) > 1:
|
|
237
|
+
x0, y0 = loop[0]
|
|
238
|
+
x1, y1 = loop[-1]
|
|
239
|
+
if abs(x0 - x1) <= tol and abs(y0 - y1) <= tol:
|
|
240
|
+
loop.pop()
|
|
241
|
+
|
|
242
|
+
loop = _dedupe_any(loop, tol=tol)
|
|
243
|
+
|
|
244
|
+
if len(loop) < 3:
|
|
245
|
+
raise ValueError(f"Not enough unique points for airfoil {airfoil_name}")
|
|
246
|
+
|
|
247
|
+
return [(x, y, 0) for x, y in loop]
|
|
189
248
|
|
|
190
249
|
|
|
191
|
-
def
|
|
250
|
+
def four_digit_naca_airfoil(naca_name: str, nb_points: int = 100):
|
|
192
251
|
"""
|
|
193
252
|
Compute the profile of a NACA 4 digits airfoil
|
|
194
253
|
|
|
195
254
|
Parameters
|
|
196
255
|
----------
|
|
197
|
-
|
|
256
|
+
naca_name : str
|
|
198
257
|
4 digit of the NACA airfoil
|
|
199
258
|
nb_points : int, optional
|
|
200
259
|
number of points for the disrcetisation of
|
|
@@ -208,9 +267,9 @@ def NACA_4_digit_geom(NACA_name, nb_points=100):
|
|
|
208
267
|
theta_line = np.linspace(0, np.pi, nb_points)
|
|
209
268
|
x_line = 0.5 * (1 - np.cos(theta_line))
|
|
210
269
|
|
|
211
|
-
m = int(
|
|
212
|
-
p = int(
|
|
213
|
-
t = (int(
|
|
270
|
+
m = int(naca_name[0]) / 100
|
|
271
|
+
p = int(naca_name[1]) / 10
|
|
272
|
+
t = (int(naca_name[2]) * 10 + int(naca_name[3])) / 100
|
|
214
273
|
|
|
215
274
|
# thickness line
|
|
216
275
|
y_t = (
|
gmshairfoil2d/gmshairfoil2d.py
CHANGED
|
@@ -6,7 +6,7 @@ import sys
|
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
|
|
8
8
|
import gmsh
|
|
9
|
-
from gmshairfoil2d.airfoil_func import (
|
|
9
|
+
from gmshairfoil2d.airfoil_func import (four_digit_naca_airfoil, get_airfoil_points,
|
|
10
10
|
get_all_available_airfoil_names, read_airfoil_from_file)
|
|
11
11
|
from gmshairfoil2d.geometry_def import (AirfoilSpline, Circle, PlaneSurface,
|
|
12
12
|
Rectangle, outofbounds, CType)
|
|
@@ -349,7 +349,7 @@ def main():
|
|
|
349
349
|
|
|
350
350
|
if args.naca:
|
|
351
351
|
airfoil_name = args.naca
|
|
352
|
-
cloud_points =
|
|
352
|
+
cloud_points = four_digit_naca_airfoil(airfoil_name)
|
|
353
353
|
|
|
354
354
|
if args.airfoil:
|
|
355
355
|
airfoil_name = args.airfoil
|
|
@@ -542,5 +542,6 @@ def main():
|
|
|
542
542
|
from gmshairfoil2d.config_handler import write_config
|
|
543
543
|
write_config(config_dict, args.save_config)
|
|
544
544
|
|
|
545
|
+
|
|
545
546
|
if __name__ == "__main__":
|
|
546
547
|
main()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
gmshairfoil2d/__init__.py,sha256=Z6ecfFqBGVmNzzlviwe1cnuzG-ArA79kykCVP4eveRE,85
|
|
2
|
+
gmshairfoil2d/__main__.py,sha256=SdT5IPOCPld7yGCoC3cb6v55E9Ys3KeM1vrVocBqtG4,134
|
|
3
|
+
gmshairfoil2d/airfoil_func.py,sha256=ddJMWhsdnjXANw1yDjKIsV9kEz0sWDTXRJmLpOFGou4,9525
|
|
4
|
+
gmshairfoil2d/config_handler.py,sha256=WM10C_yxlAl6A0sAsgW9KbfIHnrWOhvwMbs7F_AQePI,5490
|
|
5
|
+
gmshairfoil2d/geometry_def.py,sha256=D3FuRfaZ_YgHTvDW3KWB6Cya_2tJEktFvZkTA13h7pQ,48800
|
|
6
|
+
gmshairfoil2d/gmshairfoil2d.py,sha256=2j3JqSsPyTzeV2nvaoBjqb18v-oD3tLM3dz8dabkAfI,18670
|
|
7
|
+
gmshairfoil2d-0.2.33.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
+
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
+
tests/test_airfoil_func.py,sha256=Q7I9_9v97V52M-qzhV42IJnCDntA5TV3z_gif2a7ua8,4182
|
|
10
|
+
tests/test_config_handler.py,sha256=TI0OvZRbsho8mjzf1kUXLQ-rm_6dGY4b6w9RxYDxS5A,6449
|
|
11
|
+
tests/test_geometry_def.py,sha256=Ox_ePu1sZs5dOZTsPj87b3b_gHwrC3UzyoJsuL-VeUk,1325
|
|
12
|
+
gmshairfoil2d-0.2.33.dist-info/METADATA,sha256=XyPHJ64FeiiQmNoVs0GJQ82AUUSjVtNqq-QhvZ4v9XM,8248
|
|
13
|
+
gmshairfoil2d-0.2.33.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
+
gmshairfoil2d-0.2.33.dist-info/entry_points.txt,sha256=6OBSsEXNhTICrsEGgfg30RGIkKRFXELizYtfZfT1_zk,67
|
|
15
|
+
gmshairfoil2d-0.2.33.dist-info/top_level.txt,sha256=OUzQHTQIzJHlW1k6tm_9PLfE4eEWkwH0oOYNUuUXekw,20
|
|
16
|
+
gmshairfoil2d-0.2.33.dist-info/RECORD,,
|
tests/test_airfoil_func.py
CHANGED
|
@@ -3,7 +3,7 @@ from pathlib import Path
|
|
|
3
3
|
from unittest.mock import patch, Mock
|
|
4
4
|
|
|
5
5
|
import gmshairfoil2d.__init__
|
|
6
|
-
from gmshairfoil2d.airfoil_func import (
|
|
6
|
+
from gmshairfoil2d.airfoil_func import (four_digit_naca_airfoil, get_airfoil_file,
|
|
7
7
|
get_all_available_airfoil_names, read_airfoil_from_file)
|
|
8
8
|
from pytest import approx
|
|
9
9
|
|
|
@@ -58,7 +58,7 @@ def test_get_airfoil_file(monkeypatch, tmp_path):
|
|
|
58
58
|
assert expected_path.read_text() == fake_text
|
|
59
59
|
|
|
60
60
|
|
|
61
|
-
def
|
|
61
|
+
def test_four_digit_naca_airfoil():
|
|
62
62
|
with open(Path(test_data_dir, "naca0012.txt"), "rb") as f:
|
|
63
63
|
naca0012 = pickle.load(f)
|
|
64
64
|
with open(Path(test_data_dir, "naca4412.txt"), "rb") as f:
|
|
@@ -69,11 +69,11 @@ def test_NACA_4_digit_geom():
|
|
|
69
69
|
"""
|
|
70
70
|
|
|
71
71
|
assert all(
|
|
72
|
-
[a == approx(b, 1e-3) for a, b in zip(naca0012,
|
|
72
|
+
[a == approx(b, 1e-3) for a, b in zip(naca0012, four_digit_naca_airfoil("0012"))]
|
|
73
73
|
)
|
|
74
74
|
|
|
75
75
|
assert all(
|
|
76
|
-
[a == approx(b, 1e-3) for a, b in zip(naca4412,
|
|
76
|
+
[a == approx(b, 1e-3) for a, b in zip(naca4412, four_digit_naca_airfoil("4412"))]
|
|
77
77
|
)
|
|
78
78
|
|
|
79
79
|
def test_read_airfoil_from_file(tmp_path):
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
gmshairfoil2d/__init__.py,sha256=VEFFbq6EClONvPaNYBMGUnR64J06nJaO_ErPZi6WyRE,84
|
|
2
|
-
gmshairfoil2d/__main__.py,sha256=SdT5IPOCPld7yGCoC3cb6v55E9Ys3KeM1vrVocBqtG4,134
|
|
3
|
-
gmshairfoil2d/airfoil_func.py,sha256=UGugj2Djp-7JTv_AzEwmQcMQNMtqHSVzAdIIxZNwDyQ,7545
|
|
4
|
-
gmshairfoil2d/config_handler.py,sha256=WM10C_yxlAl6A0sAsgW9KbfIHnrWOhvwMbs7F_AQePI,5490
|
|
5
|
-
gmshairfoil2d/geometry_def.py,sha256=D3FuRfaZ_YgHTvDW3KWB6Cya_2tJEktFvZkTA13h7pQ,48800
|
|
6
|
-
gmshairfoil2d/gmshairfoil2d.py,sha256=acEOhx8IlhRkXfFP-C-3rtqlslXgbIW276Xcz3k5Tq8,18657
|
|
7
|
-
gmshairfoil2d-0.2.31.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
8
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
tests/test_airfoil_func.py,sha256=uCzaCbr4BHfKCHF_9gEQ7hzgwQSt7rEZRrRCbEOtOlw,4158
|
|
10
|
-
tests/test_config_handler.py,sha256=TI0OvZRbsho8mjzf1kUXLQ-rm_6dGY4b6w9RxYDxS5A,6449
|
|
11
|
-
tests/test_geometry_def.py,sha256=Ox_ePu1sZs5dOZTsPj87b3b_gHwrC3UzyoJsuL-VeUk,1325
|
|
12
|
-
gmshairfoil2d-0.2.31.dist-info/METADATA,sha256=lwQWTpntG0cPpQJdyMWms0OnQBeG6Oyd-li_fZ4fB44,8248
|
|
13
|
-
gmshairfoil2d-0.2.31.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
14
|
-
gmshairfoil2d-0.2.31.dist-info/entry_points.txt,sha256=6OBSsEXNhTICrsEGgfg30RGIkKRFXELizYtfZfT1_zk,67
|
|
15
|
-
gmshairfoil2d-0.2.31.dist-info/top_level.txt,sha256=OUzQHTQIzJHlW1k6tm_9PLfE4eEWkwH0oOYNUuUXekw,20
|
|
16
|
-
gmshairfoil2d-0.2.31.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|