gmshairfoil2d 0.2.2__py3-none-any.whl → 0.2.31__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 +3 -0
- gmshairfoil2d/__main__.py +6 -0
- gmshairfoil2d/airfoil_func.py +126 -71
- gmshairfoil2d/config_handler.py +198 -0
- gmshairfoil2d/geometry_def.py +331 -277
- gmshairfoil2d/gmshairfoil2d.py +265 -29
- {gmshairfoil2d-0.2.2.dist-info → gmshairfoil2d-0.2.31.dist-info}/METADATA +108 -19
- gmshairfoil2d-0.2.31.dist-info/RECORD +16 -0
- {gmshairfoil2d-0.2.2.dist-info → gmshairfoil2d-0.2.31.dist-info}/WHEEL +1 -1
- tests/test_airfoil_func.py +52 -1
- tests/test_config_handler.py +260 -0
- tests/test_geometry_def.py +11 -7
- gmshairfoil2d-0.2.2.dist-info/RECORD +0 -13
- {gmshairfoil2d-0.2.2.dist-info → gmshairfoil2d-0.2.31.dist-info}/entry_points.txt +0 -0
- {gmshairfoil2d-0.2.2.dist-info → gmshairfoil2d-0.2.31.dist-info}/licenses/LICENSE +0 -0
- {gmshairfoil2d-0.2.2.dist-info → gmshairfoil2d-0.2.31.dist-info}/top_level.txt +0 -0
tests/test_airfoil_func.py
CHANGED
|
@@ -4,7 +4,7 @@ from unittest.mock import patch, Mock
|
|
|
4
4
|
|
|
5
5
|
import gmshairfoil2d.__init__
|
|
6
6
|
from gmshairfoil2d.airfoil_func import (NACA_4_digit_geom, get_airfoil_file,
|
|
7
|
-
get_all_available_airfoil_names)
|
|
7
|
+
get_all_available_airfoil_names, read_airfoil_from_file)
|
|
8
8
|
from pytest import approx
|
|
9
9
|
|
|
10
10
|
LIB_DIR = Path(gmshairfoil2d.__init__.__file__).parents[1]
|
|
@@ -75,3 +75,54 @@ def test_NACA_4_digit_geom():
|
|
|
75
75
|
assert all(
|
|
76
76
|
[a == approx(b, 1e-3) for a, b in zip(naca4412, NACA_4_digit_geom("4412"))]
|
|
77
77
|
)
|
|
78
|
+
|
|
79
|
+
def test_read_airfoil_from_file(tmp_path):
|
|
80
|
+
"""
|
|
81
|
+
Test reading airfoil coordinates from a .dat file
|
|
82
|
+
"""
|
|
83
|
+
# Create a simple test airfoil file
|
|
84
|
+
airfoil_content = """NACA 0012 Test Airfoil
|
|
85
|
+
100 100
|
|
86
|
+
1.000000 0.000000
|
|
87
|
+
0.975000 0.003000
|
|
88
|
+
0.900000 0.008000
|
|
89
|
+
0.500000 0.012000
|
|
90
|
+
0.100000 0.008000
|
|
91
|
+
0.025000 0.003000
|
|
92
|
+
0.000000 0.000000
|
|
93
|
+
0.025000 -0.003000
|
|
94
|
+
0.100000 -0.008000
|
|
95
|
+
0.500000 -0.012000
|
|
96
|
+
0.900000 -0.008000
|
|
97
|
+
0.975000 -0.003000
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
test_file = tmp_path / "test_airfoil.dat"
|
|
101
|
+
test_file.write_text(airfoil_content)
|
|
102
|
+
|
|
103
|
+
# Read the airfoil
|
|
104
|
+
points = read_airfoil_from_file(str(test_file))
|
|
105
|
+
|
|
106
|
+
# Check that points were read
|
|
107
|
+
assert len(points) > 0
|
|
108
|
+
|
|
109
|
+
# Check that points are tuples with 3 coordinates (x, y, 0)
|
|
110
|
+
for point in points:
|
|
111
|
+
assert len(point) == 3
|
|
112
|
+
assert point[2] == 0 # z coordinate should be 0
|
|
113
|
+
|
|
114
|
+
# Check that we have some leading edge points (x near 0)
|
|
115
|
+
x_coords = [p[0] for p in points]
|
|
116
|
+
assert any(x < 0.05 for x in x_coords)
|
|
117
|
+
|
|
118
|
+
# Check that we have trailing edge points (x near 1)
|
|
119
|
+
assert any(x > 0.95 for x in x_coords)
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def test_read_airfoil_from_file_not_found():
|
|
123
|
+
"""
|
|
124
|
+
Test that FileNotFoundError is raised for non-existent files
|
|
125
|
+
"""
|
|
126
|
+
import pytest
|
|
127
|
+
with pytest.raises(FileNotFoundError):
|
|
128
|
+
read_airfoil_from_file("/non/existent/path/airfoil.dat")
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
|
|
4
|
+
"""
|
|
5
|
+
Tests for the configuration file handler
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pytest
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from gmshairfoil2d.config_handler import read_config, write_config, merge_config_with_args
|
|
11
|
+
import argparse
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_read_config_basic(tmp_path):
|
|
15
|
+
"""
|
|
16
|
+
Test reading basic configuration from file
|
|
17
|
+
"""
|
|
18
|
+
config_content = """# Test configuration file
|
|
19
|
+
naca= 0012
|
|
20
|
+
aoa= 5.0
|
|
21
|
+
farfield= 10
|
|
22
|
+
format= su2
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
config_file = tmp_path / "test_config.cfg"
|
|
26
|
+
config_file.write_text(config_content)
|
|
27
|
+
|
|
28
|
+
config = read_config(str(config_file))
|
|
29
|
+
|
|
30
|
+
assert config['naca'] == '0012'
|
|
31
|
+
assert config['aoa'] == 5.0
|
|
32
|
+
assert config['farfield'] == 10
|
|
33
|
+
assert config['format'] == 'su2'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def test_read_config_empty_values(tmp_path):
|
|
37
|
+
"""
|
|
38
|
+
Test that empty values are skipped
|
|
39
|
+
"""
|
|
40
|
+
config_content = """naca= 0012
|
|
41
|
+
airfoil=
|
|
42
|
+
aoa= 5.0
|
|
43
|
+
output=
|
|
44
|
+
format= su2
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
config_file = tmp_path / "test_config.cfg"
|
|
48
|
+
config_file.write_text(config_content)
|
|
49
|
+
|
|
50
|
+
config = read_config(str(config_file))
|
|
51
|
+
|
|
52
|
+
# Should have the parameters with values
|
|
53
|
+
assert 'naca' in config
|
|
54
|
+
assert 'aoa' in config
|
|
55
|
+
assert 'format' in config
|
|
56
|
+
|
|
57
|
+
# Should NOT have the empty parameters
|
|
58
|
+
assert 'airfoil' not in config
|
|
59
|
+
assert 'output' not in config
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def test_read_config_boolean_values(tmp_path):
|
|
63
|
+
"""
|
|
64
|
+
Test that boolean values are correctly converted
|
|
65
|
+
"""
|
|
66
|
+
config_content = """no_bl= False
|
|
67
|
+
structured= True
|
|
68
|
+
ui= false
|
|
69
|
+
"""
|
|
70
|
+
|
|
71
|
+
config_file = tmp_path / "test_config.cfg"
|
|
72
|
+
config_file.write_text(config_content)
|
|
73
|
+
|
|
74
|
+
config = read_config(str(config_file))
|
|
75
|
+
|
|
76
|
+
assert config['no_bl'] is False
|
|
77
|
+
assert config['structured'] is True
|
|
78
|
+
assert config['ui'] is False
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def test_read_config_numeric_conversion(tmp_path):
|
|
82
|
+
"""
|
|
83
|
+
Test that numeric values are correctly converted to float/int
|
|
84
|
+
"""
|
|
85
|
+
config_content = """nb_layers= 35
|
|
86
|
+
aoa= 5.5
|
|
87
|
+
first_layer= 3e-05
|
|
88
|
+
ratio= 1.2
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
config_file = tmp_path / "test_config.cfg"
|
|
92
|
+
config_file.write_text(config_content)
|
|
93
|
+
|
|
94
|
+
config = read_config(str(config_file))
|
|
95
|
+
|
|
96
|
+
assert config['nb_layers'] == 35
|
|
97
|
+
assert isinstance(config['nb_layers'], int)
|
|
98
|
+
assert config['aoa'] == 5.5
|
|
99
|
+
assert isinstance(config['aoa'], float)
|
|
100
|
+
assert config['first_layer'] == 3e-05
|
|
101
|
+
assert isinstance(config['first_layer'], float)
|
|
102
|
+
assert config['ratio'] == 1.2
|
|
103
|
+
assert isinstance(config['ratio'], float)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def test_read_config_comments_ignored(tmp_path):
|
|
107
|
+
"""
|
|
108
|
+
Test that comment lines are ignored
|
|
109
|
+
"""
|
|
110
|
+
config_content = """# This is a comment
|
|
111
|
+
naca= 0012
|
|
112
|
+
# Another comment
|
|
113
|
+
aoa= 5.0
|
|
114
|
+
# Even more comments here
|
|
115
|
+
"""
|
|
116
|
+
|
|
117
|
+
config_file = tmp_path / "test_config.cfg"
|
|
118
|
+
config_file.write_text(config_content)
|
|
119
|
+
|
|
120
|
+
config = read_config(str(config_file))
|
|
121
|
+
|
|
122
|
+
assert len(config) == 2
|
|
123
|
+
assert config['naca'] == '0012'
|
|
124
|
+
assert config['aoa'] == 5.0
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def test_read_config_file_not_found():
|
|
128
|
+
"""
|
|
129
|
+
Test that FileNotFoundError is raised for non-existent files
|
|
130
|
+
"""
|
|
131
|
+
with pytest.raises(FileNotFoundError):
|
|
132
|
+
read_config("/non/existent/config.cfg")
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def test_write_config(tmp_path):
|
|
136
|
+
"""
|
|
137
|
+
Test writing configuration to file
|
|
138
|
+
"""
|
|
139
|
+
config_dict = {
|
|
140
|
+
'naca': '0012',
|
|
141
|
+
'aoa': 5.0,
|
|
142
|
+
'farfield': 10,
|
|
143
|
+
'format': 'su2',
|
|
144
|
+
'structured': False
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
output_file = tmp_path / "output_config.cfg"
|
|
148
|
+
write_config(config_dict, str(output_file))
|
|
149
|
+
|
|
150
|
+
# Verify file was created
|
|
151
|
+
assert output_file.exists()
|
|
152
|
+
|
|
153
|
+
# Read back and verify
|
|
154
|
+
content = output_file.read_text()
|
|
155
|
+
assert 'naca= 0012' in content
|
|
156
|
+
assert 'aoa= 5.0' in content
|
|
157
|
+
assert 'farfield= 10' in content
|
|
158
|
+
assert 'format= su2' in content
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def test_write_config_with_none_values(tmp_path):
|
|
162
|
+
"""
|
|
163
|
+
Test writing configuration with None values
|
|
164
|
+
"""
|
|
165
|
+
config_dict = {
|
|
166
|
+
'naca': '0012',
|
|
167
|
+
'airfoil': None,
|
|
168
|
+
'aoa': 5.0
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
output_file = tmp_path / "output_config.cfg"
|
|
172
|
+
write_config(config_dict, str(output_file))
|
|
173
|
+
|
|
174
|
+
content = output_file.read_text()
|
|
175
|
+
assert 'airfoil=' in content
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def test_merge_config_with_args():
|
|
179
|
+
"""
|
|
180
|
+
Test merging configuration with command-line arguments
|
|
181
|
+
"""
|
|
182
|
+
config_dict = {
|
|
183
|
+
'naca': '0012',
|
|
184
|
+
'aoa': 5.0,
|
|
185
|
+
'farfield': 10,
|
|
186
|
+
'format': 'su2'
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Create mock args with defaults
|
|
190
|
+
parser = argparse.ArgumentParser()
|
|
191
|
+
parser.add_argument('--naca', default=None)
|
|
192
|
+
parser.add_argument('--aoa', type=float, default=None)
|
|
193
|
+
parser.add_argument('--farfield', type=float, default=10)
|
|
194
|
+
parser.add_argument('--format', default=None)
|
|
195
|
+
parser.add_argument('--output', default='.')
|
|
196
|
+
|
|
197
|
+
args = parser.parse_args([])
|
|
198
|
+
|
|
199
|
+
# Merge config into args
|
|
200
|
+
merged_args = merge_config_with_args(config_dict, args)
|
|
201
|
+
|
|
202
|
+
assert merged_args.naca == '0012'
|
|
203
|
+
assert merged_args.aoa == 5.0
|
|
204
|
+
assert merged_args.format == 'su2'
|
|
205
|
+
assert merged_args.output == '.' # Default not overridden
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def test_merge_config_cli_precedence():
|
|
209
|
+
"""
|
|
210
|
+
Test that CLI arguments take precedence over config file
|
|
211
|
+
"""
|
|
212
|
+
config_dict = {
|
|
213
|
+
'naca': '0012',
|
|
214
|
+
'aoa': 5.0,
|
|
215
|
+
'format': 'su2'
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
parser = argparse.ArgumentParser()
|
|
219
|
+
parser.add_argument('--naca', default=None)
|
|
220
|
+
parser.add_argument('--aoa', type=float, default=None)
|
|
221
|
+
parser.add_argument('--format', default=None)
|
|
222
|
+
|
|
223
|
+
# Simulate CLI providing aoa explicitly
|
|
224
|
+
args = parser.parse_args(['--aoa', '10.0'])
|
|
225
|
+
|
|
226
|
+
merged_args = merge_config_with_args(config_dict, args)
|
|
227
|
+
|
|
228
|
+
# Config values applied
|
|
229
|
+
assert merged_args.naca == '0012'
|
|
230
|
+
assert merged_args.format == 'su2'
|
|
231
|
+
|
|
232
|
+
# CLI value takes precedence
|
|
233
|
+
assert merged_args.aoa == 10.0
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def test_read_write_roundtrip(tmp_path):
|
|
237
|
+
"""
|
|
238
|
+
Test that writing and reading back produces the same config
|
|
239
|
+
"""
|
|
240
|
+
original_config = {
|
|
241
|
+
'naca': '2412',
|
|
242
|
+
'aoa': 3.5,
|
|
243
|
+
'farfield': 15,
|
|
244
|
+
'format': 'cgns',
|
|
245
|
+
'nb_layers': 50,
|
|
246
|
+
'first_layer': 1e-04
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
config_file = tmp_path / "roundtrip_config.cfg"
|
|
250
|
+
|
|
251
|
+
# Write
|
|
252
|
+
write_config(original_config, str(config_file))
|
|
253
|
+
|
|
254
|
+
# Read back
|
|
255
|
+
read_back_config = read_config(str(config_file))
|
|
256
|
+
|
|
257
|
+
# Verify all values match
|
|
258
|
+
for key, value in original_config.items():
|
|
259
|
+
assert key in read_back_config
|
|
260
|
+
assert read_back_config[key] == value
|
tests/test_geometry_def.py
CHANGED
|
@@ -29,13 +29,17 @@ def test_mesh_rectangle():
|
|
|
29
29
|
gmsh.write(str(Path(test_data_dir, "mesh_test.su2")))
|
|
30
30
|
gmsh.finalize()
|
|
31
31
|
|
|
32
|
-
# Test if the generated mesh is correct
|
|
32
|
+
# Test if the generated mesh is correct by checking key properties
|
|
33
|
+
# (exact floating point comparison varies with GMSH version)
|
|
33
34
|
with open(Path(test_data_dir, "mesh_test.su2"), "r") as f:
|
|
34
35
|
mesh_test = f.read()
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
# Verify mesh structure is created correctly
|
|
38
|
+
assert "NDIME= 2" in mesh_test
|
|
39
|
+
assert "NELEM= 14" in mesh_test
|
|
40
|
+
assert "NPOIN= 12" in mesh_test
|
|
41
|
+
assert "NMARK= 3" in mesh_test
|
|
42
|
+
assert "MARKER_TAG= inlet" in mesh_test
|
|
43
|
+
assert "MARKER_TAG= outlet" in mesh_test
|
|
44
|
+
assert "MARKER_TAG= wall" in mesh_test
|
|
37
45
|
|
|
38
|
-
assert mesh_test == mesh_origin
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
test_mesh_rectangle()
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
gmshairfoil2d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
gmshairfoil2d/airfoil_func.py,sha256=6DJTp4uwLL_9_75weVFw69lHZYmVsa-FY2916iBAuXU,5694
|
|
3
|
-
gmshairfoil2d/geometry_def.py,sha256=IaJ-5pBAfXHI-fHvCoeIlXmu6kJpKYdEQq_cVdacK9I,44388
|
|
4
|
-
gmshairfoil2d/gmshairfoil2d.py,sha256=9GoKpHDytiUOvM0dX-YNDVb25GxG5KaZjxq5emo-IVw,9998
|
|
5
|
-
gmshairfoil2d-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
6
|
-
tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
-
tests/test_airfoil_func.py,sha256=s6byg1p66I79PhFK-r9Ke4KcI_XaTNqIYKB3K1ZjQEc,2465
|
|
8
|
-
tests/test_geometry_def.py,sha256=ogy8YmigkwQ118duDXXGRZPZb_DS9qnkTuzzaF5jmb8,1054
|
|
9
|
-
gmshairfoil2d-0.2.2.dist-info/METADATA,sha256=t5AtDapRtaMv-zLgvarUxEUHzQ9HnRk11U_OkUfONZk,5861
|
|
10
|
-
gmshairfoil2d-0.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
-
gmshairfoil2d-0.2.2.dist-info/entry_points.txt,sha256=6OBSsEXNhTICrsEGgfg30RGIkKRFXELizYtfZfT1_zk,67
|
|
12
|
-
gmshairfoil2d-0.2.2.dist-info/top_level.txt,sha256=OUzQHTQIzJHlW1k6tm_9PLfE4eEWkwH0oOYNUuUXekw,20
|
|
13
|
-
gmshairfoil2d-0.2.2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|