gmshairfoil2d 0.2.1__tar.gz → 0.2.3__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.
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/PKG-INFO +104 -16
- gmshairfoil2d-0.2.3/README.md +196 -0
- gmshairfoil2d-0.2.3/gmshairfoil2d/__init__.py +3 -0
- gmshairfoil2d-0.2.3/gmshairfoil2d/__main__.py +6 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d/airfoil_func.py +126 -71
- gmshairfoil2d-0.2.3/gmshairfoil2d/config_handler.py +198 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d/geometry_def.py +331 -277
- gmshairfoil2d-0.2.3/gmshairfoil2d/gmshairfoil2d.py +546 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/PKG-INFO +104 -16
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/SOURCES.txt +3 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/setup.py +1 -1
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/tests/test_airfoil_func.py +52 -1
- gmshairfoil2d-0.2.3/tests/test_config_handler.py +260 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/tests/test_geometry_def.py +11 -7
- gmshairfoil2d-0.2.1/README.md +0 -108
- gmshairfoil2d-0.2.1/gmshairfoil2d/gmshairfoil2d.py +0 -310
- gmshairfoil2d-0.2.1/tests/__init__.py +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/LICENSE +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/dependency_links.txt +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/entry_points.txt +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/requires.txt +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/gmshairfoil2d.egg-info/top_level.txt +0 -0
- {gmshairfoil2d-0.2.1 → gmshairfoil2d-0.2.3}/setup.cfg +0 -0
- {gmshairfoil2d-0.2.1/gmshairfoil2d → gmshairfoil2d-0.2.3/tests}/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gmshairfoil2d
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.3
|
|
4
4
|
Summary: Python tool to generate 2D mesh around an airfoil
|
|
5
5
|
Home-page: https://github.com/cfsengineering/GMSH-Airfoil-2D
|
|
6
6
|
Author: Giacomo Benedetti
|
|
@@ -61,32 +61,68 @@ gmshairfoil2d -h
|
|
|
61
61
|
|
|
62
62
|
optional arguments:
|
|
63
63
|
-h, --help Show this help message and exit
|
|
64
|
+
--config [PATH] Path to configuration file (key=value format)
|
|
65
|
+
--save_config [PATH] Save current configuration to file
|
|
66
|
+
--example_config Create an example configuration file
|
|
64
67
|
--list Display all airfoil available in the database :
|
|
65
68
|
https://m-selig.ae.illinois.edu/ads/coord_database.html
|
|
66
69
|
--naca [4DIGITS] NACA airfoil 4 digit
|
|
67
70
|
--airfoil [NAME] Name of an airfoil profile in the database (database available with
|
|
68
71
|
the --list argument)
|
|
72
|
+
--airfoil_path [PATH] Path to custom airfoil .dat file
|
|
73
|
+
--flap_path [PATH] Path to custom flap .dat file
|
|
69
74
|
--aoa [AOA] Angle of attack [deg] (default: 0 [deg])
|
|
75
|
+
--deflection [DEFLECTION] Flap deflection angle [deg] (default: 0 [deg])
|
|
70
76
|
--farfield [RADIUS] Create a circular farfield mesh of given radius [m] (default 10m)
|
|
77
|
+
--farfield_ctype Generate a C-type structured farfield for hybrid meshes
|
|
71
78
|
--box [LENGTHxWIDTH] Create a box mesh of dimensions [length]x[height] [m]
|
|
72
79
|
--airfoil_mesh_size [SIZE] Mesh size of the airfoil contour [m] (default 0.01m)
|
|
73
|
-
--ext_mesh_size [SIZE] Mesh size of the external domain [m] (default 0.2m)
|
|
74
|
-
and structural)
|
|
80
|
+
--ext_mesh_size [SIZE] Mesh size of the external domain [m] (default 0.2m)
|
|
75
81
|
--no_bl Do the unstructured meshing (with triangles), without a boundary
|
|
76
82
|
layer
|
|
77
|
-
--first_layer [HEIGHT] Height of the first layer [m] (default 3e-5m) (for bl and
|
|
78
|
-
--ratio [RATIO] Growth ratio of layers (default 1.2) (for bl and
|
|
83
|
+
--first_layer [HEIGHT] Height of the first layer [m] (default 3e-5m) (for bl and structured)
|
|
84
|
+
--ratio [RATIO] Growth ratio of layers (default 1.2) (for bl and structured)
|
|
79
85
|
--nb_layers [INT] Total number of layers in the boundary layer (default 35)
|
|
80
86
|
--format [FORMAT] Format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2,
|
|
81
87
|
dat (default su2)
|
|
82
|
-
--
|
|
83
|
-
--arg_struc [
|
|
84
|
-
|
|
88
|
+
--structured Generate a structured mesh
|
|
89
|
+
--arg_struc [LxL] Parameters for the structured mesh [wake length (axis x)]x[total
|
|
90
|
+
height (axis y)] [m] (default 10x10)
|
|
85
91
|
--output [PATH] Output path for the mesh file (default : current dir)
|
|
86
92
|
--ui Open GMSH user interface to see the mesh
|
|
87
93
|
|
|
88
94
|
```
|
|
89
95
|
|
|
96
|
+
## Configuration Files
|
|
97
|
+
|
|
98
|
+
Instead of using command-line arguments, you can use configuration files for batch processing. Configuration files use a simple `key=value` format with support for comments.
|
|
99
|
+
|
|
100
|
+
**Create an example configuration file:**
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
gmshairfoil2d --example_config
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
**Use a configuration file:**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
gmshairfoil2d --config my_config.cfg
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Override config values with CLI arguments:**
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
gmshairfoil2d --config my_config.cfg --aoa 5.0
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Save current configuration to file:**
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
gmshairfoil2d --naca 0012 --aoa 5.0 --save_config my_config.cfg
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
For more details, see [example/CONFIG_README.md](example/CONFIG_README.md) and ready-to-use examples in [example/EXAMPLES_README.md](example/EXAMPLES_README.md).
|
|
125
|
+
|
|
90
126
|
## Examples of use
|
|
91
127
|
|
|
92
128
|
To check all airfoil available in the [database](https://m-selig.ae.illinois.edu/ads/coord_database.html):
|
|
@@ -95,7 +131,9 @@ To check all airfoil available in the [database](https://m-selig.ae.illinois.edu
|
|
|
95
131
|
gmshairfoil2d --list
|
|
96
132
|
```
|
|
97
133
|
|
|
98
|
-
For all the following examples, the
|
|
134
|
+
For all the following examples, the default chord length is 1 meter.
|
|
135
|
+
|
|
136
|
+
### Example 1: NACA0012 with circular farfield
|
|
99
137
|
|
|
100
138
|
To create a circular farfield mesh around a NACA0012 of 10m of radius and see the result with GMSH user interface:
|
|
101
139
|
|
|
@@ -103,37 +141,87 @@ To create a circular farfield mesh around a NACA0012 of 10m of radius and see th
|
|
|
103
141
|
gmshairfoil2d --naca 0012 --farfield 10 --ui --no_bl
|
|
104
142
|
```
|
|
105
143
|
|
|
144
|
+
Or using config file:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
gmshairfoil2d --config example/example1_naca0012.cfg
|
|
148
|
+
```
|
|
149
|
+
|
|
106
150
|

|
|
107
151
|
|
|
108
|
-
|
|
152
|
+
### Example 2: DAE11 with boundary layer
|
|
153
|
+
|
|
154
|
+
To create a circular farfield mesh with boundary layer around a Drela DAE11 airfoil (the name in the database is "dae11") of 4m of radius with a mesh size of 0.005m on the airfoil:
|
|
109
155
|
|
|
110
156
|
```bash
|
|
111
157
|
gmshairfoil2d --airfoil dae11 --farfield 4 --airfoil_mesh_size 0.005
|
|
112
158
|
```
|
|
113
159
|
|
|
114
|
-
|
|
160
|
+
Or using config file:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
gmshairfoil2d --config example/example2_dae11.cfg
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Example 3: E211 in box with angle of attack
|
|
167
|
+
|
|
168
|
+
To create mesh around an Eppler E211 airfoil (the name in the database is "e211") with an angle of attack of 8 degree in a box of 12x4m (length x height) and save it as a VTK mesh:
|
|
115
169
|
|
|
116
170
|
```bash
|
|
117
171
|
gmshairfoil2d --airfoil e211 --aoa 8 --box 12x4 --format vtk --ui --no_bl
|
|
118
172
|
```
|
|
119
173
|
|
|
120
|
-
|
|
174
|
+
Or using config file:
|
|
121
175
|
|
|
176
|
+
```bash
|
|
177
|
+
gmshairfoil2d --config example/example3_e211_box.cfg
|
|
178
|
+
```
|
|
122
179
|
|
|
180
|
+

|
|
181
|
+
|
|
182
|
+
### Example 4: CH10SM with box and boundary layer
|
|
123
183
|
|
|
124
|
-
To create a boxed mesh around a
|
|
184
|
+
To create a boxed mesh around a Chuck Hollinger CH10SM airfoil (the name in the database is "ch10sm"), using the boundary layer with default parameters (first layer of height 3e-5, 35 layers and growth ratio of 1.2):
|
|
125
185
|
|
|
126
186
|
```bash
|
|
127
187
|
gmshairfoil2d --airfoil ch10sm --ui --box 2x1.4
|
|
128
188
|
```
|
|
129
189
|
|
|
190
|
+
Or using config file:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
gmshairfoil2d --config example/example4_ch10sm_bl.cfg
|
|
194
|
+
```
|
|
195
|
+
|
|
130
196
|

|
|
131
197
|
|
|
198
|
+
### Example 5: NACA4220 structured mesh
|
|
199
|
+
|
|
200
|
+
To create a structured mesh around a NACA4220 airfoil with first layer height of 0.01, mesh size of 0.08, wake length of 6, height of 7, and angle of attack of 6 degrees:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
gmshairfoil2d --naca 4220 --airfoil_mesh_size 0.08 --ui --structured --first_layer 0.01 --arg_struc 6x7 --aoa 6
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
Or using config file:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
gmshairfoil2d --config example/example5_naca4220_structured.cfg
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+

|
|
213
|
+
### Example 6: Custom airfoil from file
|
|
214
|
+
|
|
215
|
+
To create a mesh around a custom airfoil profile with a deflected flap:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
gmshairfoil2d --airfoil_path tests/test_data/NLR_7301.dat --flap_path tests/test_data/Flap_NLR_7301.dat --deflection 10 --box 4x3 --ui --no_bl
|
|
219
|
+
```
|
|
132
220
|
|
|
133
|
-
|
|
221
|
+
Or using config file:
|
|
134
222
|
|
|
135
223
|
```bash
|
|
136
|
-
|
|
224
|
+
gmshairfoil2d --config example/example6_custom_airfoil_flap.cfg
|
|
137
225
|
```
|
|
138
226
|
|
|
139
|
-
|
|
227
|
+
This example shows how to load custom airfoil and flap profiles from external .dat files and generate a mesh with flap deflection, useful for using proprietary or custom-designed airfoil geometries.
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
[](https://github.com/cfsengineering/GMSH-Airfoil-2D/actions/workflows/pytest.yml)
|
|
2
|
+
[](https://pypi.python.org/pypi/gmshairfoil2d)
|
|
3
|
+
[](https://github.com/cfsengineering/GMSH-Airfoil-2D/blob/main/LICENSE)
|
|
4
|
+
[](https://github.com/psf/black)
|
|
5
|
+
|
|
6
|
+
# GMSH-Airfoil-2D
|
|
7
|
+
|
|
8
|
+
Python tool to genreate 2D unstructured, hybrid and structured mesh around an airfoil with [GMSH](https://gmsh.info/) in one command line.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
You can install this package from PyPi:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pip install gmshairfoil2d
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Or you can clone and install this repository with the following commands:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
git clone https://github.com/cfsengineering/GMSH-Airfoil-2D.git
|
|
22
|
+
cd GMSH-Airfoil-2D
|
|
23
|
+
pip install -e .
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```text
|
|
29
|
+
gmshairfoil2d -h
|
|
30
|
+
|
|
31
|
+
optional arguments:
|
|
32
|
+
-h, --help Show this help message and exit
|
|
33
|
+
--config [PATH] Path to configuration file (key=value format)
|
|
34
|
+
--save_config [PATH] Save current configuration to file
|
|
35
|
+
--example_config Create an example configuration file
|
|
36
|
+
--list Display all airfoil available in the database :
|
|
37
|
+
https://m-selig.ae.illinois.edu/ads/coord_database.html
|
|
38
|
+
--naca [4DIGITS] NACA airfoil 4 digit
|
|
39
|
+
--airfoil [NAME] Name of an airfoil profile in the database (database available with
|
|
40
|
+
the --list argument)
|
|
41
|
+
--airfoil_path [PATH] Path to custom airfoil .dat file
|
|
42
|
+
--flap_path [PATH] Path to custom flap .dat file
|
|
43
|
+
--aoa [AOA] Angle of attack [deg] (default: 0 [deg])
|
|
44
|
+
--deflection [DEFLECTION] Flap deflection angle [deg] (default: 0 [deg])
|
|
45
|
+
--farfield [RADIUS] Create a circular farfield mesh of given radius [m] (default 10m)
|
|
46
|
+
--farfield_ctype Generate a C-type structured farfield for hybrid meshes
|
|
47
|
+
--box [LENGTHxWIDTH] Create a box mesh of dimensions [length]x[height] [m]
|
|
48
|
+
--airfoil_mesh_size [SIZE] Mesh size of the airfoil contour [m] (default 0.01m)
|
|
49
|
+
--ext_mesh_size [SIZE] Mesh size of the external domain [m] (default 0.2m)
|
|
50
|
+
--no_bl Do the unstructured meshing (with triangles), without a boundary
|
|
51
|
+
layer
|
|
52
|
+
--first_layer [HEIGHT] Height of the first layer [m] (default 3e-5m) (for bl and structured)
|
|
53
|
+
--ratio [RATIO] Growth ratio of layers (default 1.2) (for bl and structured)
|
|
54
|
+
--nb_layers [INT] Total number of layers in the boundary layer (default 35)
|
|
55
|
+
--format [FORMAT] Format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2,
|
|
56
|
+
dat (default su2)
|
|
57
|
+
--structured Generate a structured mesh
|
|
58
|
+
--arg_struc [LxL] Parameters for the structured mesh [wake length (axis x)]x[total
|
|
59
|
+
height (axis y)] [m] (default 10x10)
|
|
60
|
+
--output [PATH] Output path for the mesh file (default : current dir)
|
|
61
|
+
--ui Open GMSH user interface to see the mesh
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Configuration Files
|
|
66
|
+
|
|
67
|
+
Instead of using command-line arguments, you can use configuration files for batch processing. Configuration files use a simple `key=value` format with support for comments.
|
|
68
|
+
|
|
69
|
+
**Create an example configuration file:**
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gmshairfoil2d --example_config
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Use a configuration file:**
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
gmshairfoil2d --config my_config.cfg
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Override config values with CLI arguments:**
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
gmshairfoil2d --config my_config.cfg --aoa 5.0
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**Save current configuration to file:**
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
gmshairfoil2d --naca 0012 --aoa 5.0 --save_config my_config.cfg
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
For more details, see [example/CONFIG_README.md](example/CONFIG_README.md) and ready-to-use examples in [example/EXAMPLES_README.md](example/EXAMPLES_README.md).
|
|
94
|
+
|
|
95
|
+
## Examples of use
|
|
96
|
+
|
|
97
|
+
To check all airfoil available in the [database](https://m-selig.ae.illinois.edu/ads/coord_database.html):
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
gmshairfoil2d --list
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
For all the following examples, the default chord length is 1 meter.
|
|
104
|
+
|
|
105
|
+
### Example 1: NACA0012 with circular farfield
|
|
106
|
+
|
|
107
|
+
To create a circular farfield mesh around a NACA0012 of 10m of radius and see the result with GMSH user interface:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
gmshairfoil2d --naca 0012 --farfield 10 --ui --no_bl
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Or using config file:
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
gmshairfoil2d --config example/example1_naca0012.cfg
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+

|
|
120
|
+
|
|
121
|
+
### Example 2: DAE11 with boundary layer
|
|
122
|
+
|
|
123
|
+
To create a circular farfield mesh with boundary layer around a Drela DAE11 airfoil (the name in the database is "dae11") of 4m of radius with a mesh size of 0.005m on the airfoil:
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
gmshairfoil2d --airfoil dae11 --farfield 4 --airfoil_mesh_size 0.005
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Or using config file:
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
gmshairfoil2d --config example/example2_dae11.cfg
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### Example 3: E211 in box with angle of attack
|
|
136
|
+
|
|
137
|
+
To create mesh around an Eppler E211 airfoil (the name in the database is "e211") with an angle of attack of 8 degree in a box of 12x4m (length x height) and save it as a VTK mesh:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
gmshairfoil2d --airfoil e211 --aoa 8 --box 12x4 --format vtk --ui --no_bl
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Or using config file:
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
gmshairfoil2d --config example/example3_e211_box.cfg
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+

|
|
150
|
+
|
|
151
|
+
### Example 4: CH10SM with box and boundary layer
|
|
152
|
+
|
|
153
|
+
To create a boxed mesh around a Chuck Hollinger CH10SM airfoil (the name in the database is "ch10sm"), using the boundary layer with default parameters (first layer of height 3e-5, 35 layers and growth ratio of 1.2):
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
gmshairfoil2d --airfoil ch10sm --ui --box 2x1.4
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Or using config file:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
gmshairfoil2d --config example/example4_ch10sm_bl.cfg
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+

|
|
166
|
+
|
|
167
|
+
### Example 5: NACA4220 structured mesh
|
|
168
|
+
|
|
169
|
+
To create a structured mesh around a NACA4220 airfoil with first layer height of 0.01, mesh size of 0.08, wake length of 6, height of 7, and angle of attack of 6 degrees:
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
gmshairfoil2d --naca 4220 --airfoil_mesh_size 0.08 --ui --structured --first_layer 0.01 --arg_struc 6x7 --aoa 6
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Or using config file:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
gmshairfoil2d --config example/example5_naca4220_structured.cfg
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+

|
|
182
|
+
### Example 6: Custom airfoil from file
|
|
183
|
+
|
|
184
|
+
To create a mesh around a custom airfoil profile with a deflected flap:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
gmshairfoil2d --airfoil_path tests/test_data/NLR_7301.dat --flap_path tests/test_data/Flap_NLR_7301.dat --deflection 10 --box 4x3 --ui --no_bl
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Or using config file:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
gmshairfoil2d --config example/example6_custom_airfoil_flap.cfg
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
This example shows how to load custom airfoil and flap profiles from external .dat files and generate a mesh with flap deflection, useful for using proprietary or custom-designed airfoil geometries.
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import sys
|
|
1
2
|
from pathlib import Path
|
|
2
3
|
|
|
3
4
|
import numpy as np
|
|
4
5
|
import requests
|
|
5
|
-
import sys
|
|
6
6
|
|
|
7
7
|
import gmshairfoil2d.__init__
|
|
8
8
|
|
|
@@ -10,6 +10,71 @@ LIB_DIR = Path(gmshairfoil2d.__init__.__file__).parents[1]
|
|
|
10
10
|
database_dir = Path(LIB_DIR, "database")
|
|
11
11
|
|
|
12
12
|
|
|
13
|
+
def read_airfoil_from_file(file_path):
|
|
14
|
+
"""Read airfoil coordinates from a .dat file.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
file_path : str or Path
|
|
19
|
+
Path to airfoil data file
|
|
20
|
+
|
|
21
|
+
Returns
|
|
22
|
+
-------
|
|
23
|
+
list
|
|
24
|
+
List of unique (x, y, 0) points sorted by original order
|
|
25
|
+
|
|
26
|
+
Raises
|
|
27
|
+
------
|
|
28
|
+
FileNotFoundError
|
|
29
|
+
If file does not exist
|
|
30
|
+
ValueError
|
|
31
|
+
If no valid airfoil points found
|
|
32
|
+
"""
|
|
33
|
+
file_path = Path(file_path)
|
|
34
|
+
if not file_path.exists():
|
|
35
|
+
raise FileNotFoundError(f"File {file_path} not found.")
|
|
36
|
+
|
|
37
|
+
airfoil_points = []
|
|
38
|
+
with open(file_path, 'r') as f:
|
|
39
|
+
for line in f:
|
|
40
|
+
line = line.strip()
|
|
41
|
+
if not line or line.startswith(('#', 'Airfoil')):
|
|
42
|
+
continue
|
|
43
|
+
parts = line.split()
|
|
44
|
+
if len(parts) != 2:
|
|
45
|
+
continue
|
|
46
|
+
try:
|
|
47
|
+
x, y = map(float, parts)
|
|
48
|
+
except ValueError:
|
|
49
|
+
continue
|
|
50
|
+
if x > 1 and y > 1:
|
|
51
|
+
continue
|
|
52
|
+
airfoil_points.append((x, y))
|
|
53
|
+
|
|
54
|
+
if not airfoil_points:
|
|
55
|
+
raise ValueError(f"No valid airfoil points found in {file_path}")
|
|
56
|
+
|
|
57
|
+
# Split upper and lower surfaces
|
|
58
|
+
try:
|
|
59
|
+
split_index = next(i for i, (x, y) in enumerate(airfoil_points) if x >= 1.0)
|
|
60
|
+
except StopIteration:
|
|
61
|
+
split_index = len(airfoil_points) // 2
|
|
62
|
+
|
|
63
|
+
upper_points = airfoil_points[:split_index + 1]
|
|
64
|
+
lower_points = airfoil_points[split_index + 1:]
|
|
65
|
+
|
|
66
|
+
# Ensure lower points start from trailing edge
|
|
67
|
+
if lower_points and lower_points[0][0] == 0.0:
|
|
68
|
+
lower_points = lower_points[::-1]
|
|
69
|
+
|
|
70
|
+
# Combine and remove duplicates
|
|
71
|
+
x_up, y_up = zip(*upper_points) if upper_points else ([], [])
|
|
72
|
+
x_lo, y_lo = zip(*lower_points) if lower_points else ([], [])
|
|
73
|
+
|
|
74
|
+
cloud_points = [(x, y, 0) for x, y in zip([*x_up, *x_lo], [*y_up, *y_lo])]
|
|
75
|
+
return sorted(set(cloud_points), key=cloud_points.index)
|
|
76
|
+
|
|
77
|
+
|
|
13
78
|
def get_all_available_airfoil_names():
|
|
14
79
|
"""
|
|
15
80
|
Request the airfoil list available at m-selig.ae.illinois.edu
|
|
@@ -34,102 +99,92 @@ def get_all_available_airfoil_names():
|
|
|
34
99
|
|
|
35
100
|
def get_airfoil_file(airfoil_name):
|
|
36
101
|
"""
|
|
37
|
-
Request the airfoil .dat file
|
|
38
|
-
database folder
|
|
102
|
+
Request the airfoil .dat file from m-selig.ae.illinois.edu and store it in database folder.
|
|
39
103
|
|
|
40
104
|
Parameters
|
|
41
105
|
----------
|
|
42
|
-
airfoil_name :
|
|
43
|
-
|
|
106
|
+
airfoil_name : str
|
|
107
|
+
Name of the airfoil
|
|
108
|
+
|
|
109
|
+
Raises
|
|
110
|
+
------
|
|
111
|
+
SystemExit
|
|
112
|
+
If airfoil not found or network error occurs
|
|
44
113
|
"""
|
|
45
|
-
|
|
46
114
|
if not database_dir.exists():
|
|
47
115
|
database_dir.mkdir()
|
|
48
116
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
117
|
+
file_path = Path(database_dir, f"{airfoil_name}.dat")
|
|
118
|
+
if file_path.exists():
|
|
119
|
+
return
|
|
52
120
|
|
|
121
|
+
url = f"https://m-selig.ae.illinois.edu/ads/coord/{airfoil_name}.dat"
|
|
53
122
|
try:
|
|
54
|
-
|
|
55
|
-
if
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
except requests.exceptions.RequestException
|
|
123
|
+
response = requests.get(url, timeout=10)
|
|
124
|
+
if response.status_code != 200:
|
|
125
|
+
print(f"❌ Error: Could not find airfoil '{airfoil_name}' on UIUC database.")
|
|
126
|
+
sys.exit(1)
|
|
127
|
+
with open(file_path, "wb") as f:
|
|
128
|
+
f.write(response.content)
|
|
129
|
+
except requests.exceptions.RequestException:
|
|
61
130
|
print(f"❌ Network Error: Could not connect to the database. Check your internet.")
|
|
62
|
-
import sys
|
|
63
131
|
sys.exit(1)
|
|
64
132
|
|
|
65
|
-
file_path = Path(database_dir, f"{airfoil_name}.dat")
|
|
66
|
-
|
|
67
|
-
if not file_path.exists():
|
|
68
|
-
with open(file_path, "wb") as f:
|
|
69
|
-
f.write(r.content)
|
|
70
|
-
|
|
71
133
|
|
|
72
134
|
def get_airfoil_points(airfoil_name):
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
135
|
+
"""Load airfoil points from the database.
|
|
136
|
+
|
|
137
|
+
Parameters
|
|
138
|
+
----------
|
|
139
|
+
airfoil_name : str
|
|
140
|
+
Name of the airfoil in the database
|
|
141
|
+
|
|
142
|
+
Returns
|
|
143
|
+
-------
|
|
144
|
+
list
|
|
145
|
+
List of unique (x, y, 0) points
|
|
146
|
+
|
|
147
|
+
Raises
|
|
148
|
+
------
|
|
149
|
+
ValueError
|
|
150
|
+
If no valid points found for the airfoil
|
|
151
|
+
"""
|
|
81
152
|
get_airfoil_file(airfoil_name)
|
|
82
|
-
airfoil_file = Path(database_dir, airfoil_name
|
|
153
|
+
airfoil_file = Path(database_dir, f"{airfoil_name}.dat")
|
|
83
154
|
|
|
155
|
+
airfoil_points = []
|
|
84
156
|
with open(airfoil_file) as f:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
if x > 1 and y > 1:
|
|
97
|
-
upper_len = int(x)
|
|
98
|
-
lower_len = int(y)
|
|
99
|
-
continue
|
|
100
|
-
|
|
101
|
-
# Catch the x, y coordinates
|
|
102
|
-
airfoil_points.append((x, y))
|
|
157
|
+
for line in f:
|
|
158
|
+
try:
|
|
159
|
+
x, y = map(float, line.strip().split())
|
|
160
|
+
except ValueError:
|
|
161
|
+
continue
|
|
162
|
+
if x > 1 and y > 1:
|
|
163
|
+
continue
|
|
164
|
+
airfoil_points.append((x, y))
|
|
165
|
+
|
|
166
|
+
if not airfoil_points:
|
|
167
|
+
raise ValueError(f"No valid points found for airfoil {airfoil_name}")
|
|
103
168
|
|
|
104
169
|
n_points = len(airfoil_points)
|
|
170
|
+
upper_len = n_points // 2
|
|
105
171
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
if x == y == 0:
|
|
112
|
-
upper_len = i
|
|
113
|
-
break
|
|
114
|
-
else:
|
|
115
|
-
reverse_lower = True
|
|
172
|
+
# Try to find split point at (0, 0)
|
|
173
|
+
for i, (x, y) in enumerate(airfoil_points):
|
|
174
|
+
if x == y == 0:
|
|
175
|
+
upper_len = i
|
|
176
|
+
break
|
|
116
177
|
|
|
117
178
|
upper_points = airfoil_points[:upper_len]
|
|
118
179
|
lower_points = airfoil_points[upper_len:]
|
|
119
|
-
|
|
120
|
-
if
|
|
180
|
+
|
|
181
|
+
if lower_points and lower_points[0][0] == 0:
|
|
121
182
|
lower_points = lower_points[::-1]
|
|
122
183
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
x_up, y_up = zip(*[points for points in upper_points])
|
|
126
|
-
x_lo, y_lo = zip(*[points for points in lower_points])
|
|
127
|
-
|
|
128
|
-
x = [*x_up, *x_lo]
|
|
129
|
-
y = [*y_up, *y_lo]
|
|
184
|
+
x_up, y_up = zip(*upper_points) if upper_points else ([], [])
|
|
185
|
+
x_lo, y_lo = zip(*lower_points) if lower_points else ([], [])
|
|
130
186
|
|
|
131
|
-
cloud_points = [(x
|
|
132
|
-
# remove duplicated points
|
|
187
|
+
cloud_points = [(x, y, 0) for x, y in zip([*x_up, *x_lo], [*y_up, *y_lo])]
|
|
133
188
|
return sorted(set(cloud_points), key=cloud_points.index)
|
|
134
189
|
|
|
135
190
|
|