gmshairfoil2d 0.1.4__py3-none-any.whl → 0.2.1__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.
@@ -5,12 +5,14 @@ import argparse
5
5
  import math
6
6
  import sys
7
7
  from pathlib import Path
8
+ import numpy as np
8
9
 
9
10
  import gmsh
10
11
  from gmshairfoil2d.airfoil_func import (NACA_4_digit_geom, get_airfoil_points,
11
12
  get_all_available_airfoil_names)
12
13
  from gmshairfoil2d.geometry_def import (AirfoilSpline, Circle, PlaneSurface,
13
- Rectangle)
14
+ Rectangle, outofbounds, CType)
15
+
14
16
 
15
17
  def main():
16
18
  # Instantiate the parser
@@ -33,7 +35,7 @@ def main():
33
35
  type=str,
34
36
  metavar="4DIGITS",
35
37
  nargs="?",
36
- help="NACA airfoil 4 digit (default 0012)",
38
+ help="NACA airfoil 4 digit",
37
39
  )
38
40
 
39
41
  parser.add_argument(
@@ -73,7 +75,7 @@ def main():
73
75
  metavar="SIZE",
74
76
  nargs="?",
75
77
  default=0.01,
76
- help="Mesh size of the airfoil countour [m] (default 0.01m)",
78
+ help="Mesh size of the airfoil contour [m] (default 0.01m) (for normal, bl and structural)",
77
79
  )
78
80
 
79
81
  parser.add_argument(
@@ -85,12 +87,58 @@ def main():
85
87
  help="Mesh size of the external domain [m] (default 0.2m)",
86
88
  )
87
89
 
90
+ parser.add_argument(
91
+ "--no_bl",
92
+ action="store_true",
93
+ help="Do the unstructured meshing (with triangles), without a boundary layer",
94
+ )
95
+
96
+ parser.add_argument(
97
+ "--first_layer",
98
+ type=float,
99
+ metavar="HEIGHT",
100
+ nargs="?",
101
+ default=3e-5,
102
+ help="Height of the first layer [m] (default 3e-5m) (for bl and structural)",
103
+ )
104
+
105
+ parser.add_argument(
106
+ "--ratio",
107
+ type=float,
108
+ metavar="RATIO",
109
+ nargs="?",
110
+ default=1.2,
111
+ help="Growth ratio of layers (default 1.2) (for bl and structural)",
112
+ )
113
+
114
+ parser.add_argument(
115
+ "--nb_layers",
116
+ type=int,
117
+ metavar="INT",
118
+ nargs="?",
119
+ default=35,
120
+ help="Total number of layers in the boundary layer (default 35)",
121
+ )
122
+
88
123
  parser.add_argument(
89
124
  "--format",
90
125
  type=str,
91
126
  nargs="?",
92
127
  default="su2",
93
- help="format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2, dat (default su2)",
128
+ help="Format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2, dat (default su2)",
129
+ )
130
+
131
+ parser.add_argument(
132
+ "--structural",
133
+ action="store_true",
134
+ help="Generate a structural mesh",
135
+ )
136
+ parser.add_argument(
137
+ "--arg_struc",
138
+ type=str,
139
+ metavar="[LxL]",
140
+ default="10x10",
141
+ help="Parameters for the structural mesh [wake length (axis x)]x[total height (axis y)] [m] (default 10x10)",
94
142
  )
95
143
 
96
144
  parser.add_argument(
@@ -99,13 +147,14 @@ def main():
99
147
  metavar="PATH",
100
148
  nargs="?",
101
149
  default=".",
102
- help="output path for the mesh file (default : current dir)",
150
+ help="Output path for the mesh file (default : current dir)",
103
151
  )
104
152
 
105
153
  parser.add_argument(
106
- "--ui", action="store_true", help="Open GMSH user interface to see the mesh"
154
+ "--ui",
155
+ action="store_true",
156
+ help="Open GMSH user interface to see the mesh",
107
157
  )
108
-
109
158
  args = parser.parse_args()
110
159
 
111
160
  if len(sys.argv) == 1:
@@ -132,42 +181,127 @@ def main():
132
181
  parser.print_help()
133
182
  sys.exit()
134
183
 
184
+ # Make the points all start by the (0,0) (or minimum of coord x when not exactly 0) and go clockwise
185
+ # --> to be easier to deal with after (in airfoilspline)
186
+ le = min(p[0] for p in cloud_points)
187
+ for p in cloud_points:
188
+ if p[0] == le:
189
+ debut = cloud_points.index(p)
190
+ cloud_points = cloud_points[debut:]+cloud_points[:debut]
191
+ if cloud_points[1][1] < cloud_points[0][1]:
192
+ cloud_points.reverse()
193
+ cloud_points = cloud_points[-1:] + cloud_points[:-1]
194
+
135
195
  # Angle of attack
136
196
  aoa = -args.aoa * (math.pi / 180)
137
197
 
138
198
  # Generate Geometry
139
199
  gmsh.initialize()
140
200
 
141
- # External domain
142
- if args.box:
143
- length, width = [float(value) for value in args.box.split("x")]
144
- ext_domain = Rectangle(0.5, 0, 0, length, width, mesh_size=args.ext_mesh_size)
145
- else:
146
- ext_domain = Circle(0.5, 0, 0, radius=args.farfield, mesh_size=args.ext_mesh_size)
147
-
148
201
  # Airfoil
149
- airfoil = AirfoilSpline(cloud_points, args.airfoil_mesh_size)
202
+ airfoil = AirfoilSpline(
203
+ cloud_points, args.airfoil_mesh_size)
150
204
  airfoil.rotation(aoa, (0.5, 0, 0), (0, 0, 1))
151
- airfoil.gen_skin()
205
+ gmsh.model.geo.synchronize()
206
+
207
+ # If structural, all is done in CType
208
+ if args.structural:
209
+ dx_wake, dy = [float(value)for value in args.arg_struc.split("x")]
210
+ mesh = CType(airfoil, dx_wake, dy,
211
+ args.airfoil_mesh_size, args.first_layer, args.ratio, aoa)
212
+ mesh.define_bc()
213
+
214
+ else:
215
+ k1, k2 = airfoil.gen_skin()
216
+ # Choose the parameters for bl (when exist)
217
+ if not args.no_bl:
218
+ N = args.nb_layers
219
+ r = args.ratio
220
+ d = [args.first_layer]
221
+ # Construct the vector of cumulative distance of each layer from airfoil
222
+ for i in range(1, N):
223
+ d.append(d[-1] - (-d[0]) * r**i)
224
+ else:
225
+ d = [0]
226
+
227
+ # Need to check that the layers or airfoil do not go outside the box/circle (d[-1] is the total height of bl)
228
+ outofbounds(airfoil, args.box, args.farfield, d[-1])
229
+
230
+ # External domain
231
+ if args.box:
232
+ length, width = [float(value) for value in args.box.split("x")]
233
+ ext_domain = Rectangle(0.5, 0, 0, length, width,
234
+ mesh_size=args.ext_mesh_size)
235
+ else:
236
+ ext_domain = Circle(0.5, 0, 0, radius=args.farfield,
237
+ mesh_size=args.ext_mesh_size)
238
+ gmsh.model.geo.synchronize()
239
+
240
+ # Create the surface for the mesh
241
+ surface = PlaneSurface([ext_domain, airfoil])
242
+ gmsh.model.geo.synchronize()
243
+
244
+ # Create the boundary layer
245
+ if not args.no_bl:
246
+ curv = [airfoil.upper_spline.tag,
247
+ airfoil.lower_spline.tag, airfoil.front_spline.tag]
248
+
249
+ # Creates a new mesh field of type 'BoundaryLayer' and assigns it an ID (f).
250
+ f = gmsh.model.mesh.field.add('BoundaryLayer')
251
+
252
+ # Add the curves where we apply the boundary layer (around the airfoil for us)
253
+ gmsh.model.mesh.field.setNumbers(f, 'CurvesList', curv)
254
+ gmsh.model.mesh.field.setNumber(f, 'Size', d[0]) # size 1st layer
255
+ gmsh.model.mesh.field.setNumber(f, 'Ratio', r) # Growth ratio
256
+ # Total thickness of boundary layer
257
+ gmsh.model.mesh.field.setNumber(f, 'Thickness', d[-1])
258
+
259
+ # Forces to use quads and not triangle when =1 (i.e. true)
260
+ gmsh.model.mesh.field.setNumber(f, 'Quads', 1)
261
+
262
+ # Enter the points where we want a "fan" (points must be at end on line)(only te for us)
263
+ gmsh.model.mesh.field.setNumbers(
264
+ f, "FanPointsList", [airfoil.te.tag])
265
+
266
+ gmsh.model.mesh.field.setAsBoundaryLayer(f)
267
+
268
+ # Define boundary conditions (name the curves)
269
+ ext_domain.define_bc()
270
+ surface.define_bc()
271
+ airfoil.define_bc()
152
272
 
153
- # Generate domain
154
- surface_domain = PlaneSurface([ext_domain, airfoil])
273
+ gmsh.model.geo.synchronize()
155
274
 
156
- # Synchronize and generate BC marker
157
- gmsh.model.occ.synchronize()
158
- ext_domain.define_bc()
159
- airfoil.define_bc()
160
- surface_domain.define_bc()
275
+ # Choose the parameters of the mesh : we want the mesh size according to the points and not curvature (doesn't work with farfield)
276
+ gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 1)
277
+ gmsh.option.setNumber("Mesh.MeshSizeFromCurvature", 0)
278
+ if not args.structural and not args.no_bl:
279
+ # Add transfinite line on the front to get more point in the middle (where the curvature of the le makes it usually more spaced)
280
+ x, y, v, w = airfoil.points[k1].x, airfoil.points[k2].y, airfoil.points[k1].x, airfoil.points[k2].y
281
+ c1, c2 = airfoil.le.x, airfoil.le.y
282
+ # To get an indication of numbers of points needed, compute approximate length of curve of front spline
283
+ l = (math.sqrt((x-c1)*(x-c1)+(y-c2)*(y-c2)) +
284
+ math.sqrt((v-c1)*(v-c1)+(w-c2)*(w-c2)))
285
+ # As points will be more near than mesh size on the front, need more points
286
+ nb_points = int(3.5*l/args.airfoil_mesh_size)
287
+ gmsh.model.mesh.setTransfiniteCurve(
288
+ airfoil.front_spline.tag, nb_points, "Bump", 10)
289
+ # Choose the nbs of points in the fan at the te:
290
+ # Compute coef : between 10 and 25, 15 when usual mesh size but adapted to mesh size
291
+ coef = max(10, min(25, 15*0.01/args.airfoil_mesh_size))
292
+ gmsh.option.setNumber("Mesh.BoundaryLayerFanElements", coef)
161
293
 
162
294
  # Generate mesh
163
295
  gmsh.model.mesh.generate(2)
296
+ gmsh.model.mesh.optimize("Laplace2D", 5)
164
297
 
165
298
  # Open user interface of GMSH
166
299
  if args.ui:
167
300
  gmsh.fltk.run()
168
301
 
169
302
  # Mesh file name and output
170
- mesh_path = Path(args.output, f"mesh_airfoil_{airfoil_name}.{args.format}")
303
+ mesh_path = Path(
304
+ args.output, f"mesh_airfoil_{airfoil_name}.{args.format}")
171
305
  gmsh.write(str(mesh_path))
172
306
  gmsh.finalize()
173
307
 
@@ -0,0 +1,139 @@
1
+ Metadata-Version: 2.4
2
+ Name: gmshairfoil2d
3
+ Version: 0.2.1
4
+ Summary: Python tool to generate 2D mesh around an airfoil
5
+ Home-page: https://github.com/cfsengineering/GMSH-Airfoil-2D
6
+ Author: Giacomo Benedetti
7
+ Author-email: giacomo.benedetti@cfse.ch
8
+ Keywords: airfoil,2D,mesh,cfd,gmsh
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Intended Audience :: Developers
13
+ Requires-Python: >=3.11.0
14
+ Description-Content-Type: text/markdown
15
+ License-File: LICENSE
16
+ Requires-Dist: gmsh>=4.14
17
+ Requires-Dist: numpy>=1.20.3
18
+ Requires-Dist: pytest==7.1.3
19
+ Requires-Dist: requests==2.26.0
20
+ Dynamic: author
21
+ Dynamic: author-email
22
+ Dynamic: classifier
23
+ Dynamic: description
24
+ Dynamic: description-content-type
25
+ Dynamic: home-page
26
+ Dynamic: keywords
27
+ Dynamic: license-file
28
+ Dynamic: requires-dist
29
+ Dynamic: requires-python
30
+ Dynamic: summary
31
+
32
+ [![Pytest](https://github.com/cfsengineering/GMSH-Airfoil-2D/actions/workflows/pytest.yml/badge.svg?branch=main)](https://github.com/cfsengineering/GMSH-Airfoil-2D/actions/workflows/pytest.yml)
33
+ [![PyPi version](https://img.shields.io/pypi/v/gmshairfoil2d.svg)](https://pypi.python.org/pypi/gmshairfoil2d)
34
+ [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/cfsengineering/GMSH-Airfoil-2D/blob/main/LICENSE)
35
+ [![Black code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
36
+
37
+ # GMSH-Airfoil-2D
38
+
39
+ Python tool to genreate 2D unstructured, hybrid and structured mesh around an airfoil with [GMSH](https://gmsh.info/) in one command line.
40
+
41
+ ## Installation
42
+
43
+ You can install this package from PyPi:
44
+
45
+ ```bash
46
+ pip install gmshairfoil2d
47
+ ```
48
+
49
+ Or you can clone and install this repository with the following commands:
50
+
51
+ ```bash
52
+ git clone https://github.com/cfsengineering/GMSH-Airfoil-2D.git
53
+ cd GMSH-Airfoil-2D
54
+ pip install -e .
55
+ ```
56
+
57
+ ## Usage
58
+
59
+ ```text
60
+ gmshairfoil2d -h
61
+
62
+ optional arguments:
63
+ -h, --help Show this help message and exit
64
+ --list Display all airfoil available in the database :
65
+ https://m-selig.ae.illinois.edu/ads/coord_database.html
66
+ --naca [4DIGITS] NACA airfoil 4 digit
67
+ --airfoil [NAME] Name of an airfoil profile in the database (database available with
68
+ the --list argument)
69
+ --aoa [AOA] Angle of attack [deg] (default: 0 [deg])
70
+ --farfield [RADIUS] Create a circular farfield mesh of given radius [m] (default 10m)
71
+ --box [LENGTHxWIDTH] Create a box mesh of dimensions [length]x[height] [m]
72
+ --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) (for normal, bl
74
+ and structural)
75
+ --no_bl Do the unstructured meshing (with triangles), without a boundary
76
+ layer
77
+ --first_layer [HEIGHT] Height of the first layer [m] (default 3e-5m) (for bl and structural)
78
+ --ratio [RATIO] Growth ratio of layers (default 1.2) (for bl and structural)
79
+ --nb_layers [INT] Total number of layers in the boundary layer (default 35)
80
+ --format [FORMAT] Format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2,
81
+ dat (default su2)
82
+ --structural Generate a structural mesh
83
+ --arg_struc [LxLxL] Parameters for the structural mesh [leading (axis x)]x[wake (axis
84
+ x)]x[total height (axis y)] [m] (default 1x10x10)
85
+ --output [PATH] Output path for the mesh file (default : current dir)
86
+ --ui Open GMSH user interface to see the mesh
87
+
88
+ ```
89
+
90
+ ## Examples of use
91
+
92
+ To check all airfoil available in the [database](https://m-selig.ae.illinois.edu/ads/coord_database.html):
93
+
94
+ ```bash
95
+ gmshairfoil2d --list
96
+ ```
97
+
98
+ For all the following examples, the defauld chord lenght is 1 meter.
99
+
100
+ To create a circular farfield mesh around a NACA0012 of 10m of radius and see the result with GMSH user interface:
101
+
102
+ ```bash
103
+ gmshairfoil2d --naca 0012 --farfield 10 --ui --no_bl
104
+ ```
105
+
106
+ ![GMSH user interface with the 2D mesh](images/example_mesh.png)
107
+
108
+ To create a circular farfield mesh with boudary layer around a Drela DAE11 airfoil (the name in the database is "dae11") of 4m or radius with a mesh size of 0.005m on the airfoil (but to not open on the interface):
109
+
110
+ ```bash
111
+ gmshairfoil2d --airfoil dae11 --farfield 4 --airfoil_mesh_size 0.005
112
+ ```
113
+
114
+ To create mesh around a Eppler E220 airfoil (the name in the database is "e211") with an angle of attack of 8 degree in a box of 12x4m (lenght x height) and save it as a vtk mesh and see the result with GMSH user interface:
115
+
116
+ ```bash
117
+ gmshairfoil2d --airfoil e211 --aoa 8 --box 12x4 --format vtk --ui --no_bl
118
+ ```
119
+
120
+ ![GMSH user interface with the 2D mesh, rectangular box](images/example_mesh_box.png)
121
+
122
+
123
+
124
+ To create a boxed mesh around a Chuch Hollinger CH 10-48-13 smoothed 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
+
126
+ ```bash
127
+ gmshairfoil2d --airfoil ch10sm --ui --box 2x1.4
128
+ ```
129
+
130
+ ![GMSH result with 2D mesh with boundary layer, rectangular box](images/example_ch10sm_bl.png)
131
+
132
+
133
+ To create a structural mesh around a Naca 4220 airfoil (the 4 digits code is obviously "4220"), 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 :
134
+
135
+ ```bash
136
+ gmshairfoil2d --naca 4220 --airfoil_mesh_size 0.08 --ui --structural --first_layer 0.01 --arg_struc 6x7 --aoa 6
137
+ ```
138
+
139
+ ![GMSH result with 2D structural mesh](images/example_structural_naca4220.png)
@@ -0,0 +1,13 @@
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.1.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.1.dist-info/METADATA,sha256=REQivSAWF4rI7yHcVlG51fej84VmHcU_IZXicocvInI,5861
10
+ gmshairfoil2d-0.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ gmshairfoil2d-0.2.1.dist-info/entry_points.txt,sha256=6OBSsEXNhTICrsEGgfg30RGIkKRFXELizYtfZfT1_zk,67
12
+ gmshairfoil2d-0.2.1.dist-info/top_level.txt,sha256=OUzQHTQIzJHlW1k6tm_9PLfE4eEWkwH0oOYNUuUXekw,20
13
+ gmshairfoil2d-0.2.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.4)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
tests/__init__.py ADDED
File without changes
@@ -0,0 +1,77 @@
1
+ import pickle
2
+ from pathlib import Path
3
+ from unittest.mock import patch, Mock
4
+
5
+ import gmshairfoil2d.__init__
6
+ from gmshairfoil2d.airfoil_func import (NACA_4_digit_geom, get_airfoil_file,
7
+ get_all_available_airfoil_names)
8
+ from pytest import approx
9
+
10
+ LIB_DIR = Path(gmshairfoil2d.__init__.__file__).parents[1]
11
+
12
+ database_dir = Path(LIB_DIR, "database")
13
+ test_data_dir = Path(LIB_DIR, "tests", "test_data")
14
+
15
+ def test_get_all_available_airfoil_names(monkeypatch):
16
+ class MockResponse:
17
+ def __init__(self):
18
+ self.status_code = 200
19
+ self.text = (
20
+ '<html>'
21
+ '<a href="coord/naca0010.dat">naca0010</a>'
22
+ '<a href="coord/naca0018.dat">naca0018</a>'
23
+ '<a href="coord/falcon.dat">falcon</a>'
24
+ '<a href="coord/goe510.dat">goe510</a>'
25
+ '<a href="coord/e1210.dat">e1210</a>'
26
+ '</html>'
27
+ )
28
+
29
+ monkeypatch.setattr("gmshairfoil2d.airfoil_func.requests.get", lambda *args, **kwargs: MockResponse())
30
+
31
+ current_airfoil_list = get_all_available_airfoil_names()
32
+
33
+ expected_airfoil = ["naca0010", "naca0018", "falcon", "goe510", "e1210"]
34
+ for foil in expected_airfoil:
35
+ assert foil in current_airfoil_list
36
+
37
+
38
+ def test_get_airfoil_file(monkeypatch, tmp_path):
39
+
40
+ fake_text = "0.0 0.0\n0.5 0.1\n1.0 0.0"
41
+
42
+ class MockResponse:
43
+ def __init__(self):
44
+ self.status_code = 200
45
+ self.text = fake_text
46
+ self.content = fake_text.encode('utf-8')
47
+
48
+ monkeypatch.setattr("gmshairfoil2d.airfoil_func.requests.get", lambda *args, **kwargs: MockResponse())
49
+
50
+ monkeypatch.setattr("gmshairfoil2d.airfoil_func.database_dir", tmp_path)
51
+
52
+ profile = "naca0010"
53
+ expected_path = tmp_path / f"{profile}.dat"
54
+
55
+ get_airfoil_file(profile)
56
+
57
+ assert expected_path.exists()
58
+ assert expected_path.read_text() == fake_text
59
+
60
+
61
+ def test_NACA_4_digit_geom():
62
+ with open(Path(test_data_dir, "naca0012.txt"), "rb") as f:
63
+ naca0012 = pickle.load(f)
64
+ with open(Path(test_data_dir, "naca4412.txt"), "rb") as f:
65
+ naca4412 = pickle.load(f)
66
+
67
+ """
68
+ Test if the NACA0012 profil and NACA4412 profil are correctly generated
69
+ """
70
+
71
+ assert all(
72
+ [a == approx(b, 1e-3) for a, b in zip(naca0012, NACA_4_digit_geom("0012"))]
73
+ )
74
+
75
+ assert all(
76
+ [a == approx(b, 1e-3) for a, b in zip(naca4412, NACA_4_digit_geom("4412"))]
77
+ )
@@ -0,0 +1,41 @@
1
+ from pathlib import Path
2
+
3
+ import gmsh
4
+ import gmshairfoil2d.__init__
5
+ from gmshairfoil2d.geometry_def import Circle, PlaneSurface, Rectangle
6
+
7
+ LIB_DIR = Path(gmshairfoil2d.__init__.__file__).parents[1]
8
+ test_data_dir = Path(LIB_DIR, "tests", "test_data")
9
+
10
+
11
+ def test_mesh_rectangle():
12
+ """
13
+ Test if a simple generated mesh formed of a square domain with a circular
14
+ hole inside is meshed correctly
15
+
16
+ """
17
+
18
+ # Generate Geometry :
19
+ gmsh.initialize()
20
+ box = Rectangle(0, 0, 0, 1, 1, mesh_size=0.5)
21
+ surface_domain = PlaneSurface([box])
22
+
23
+ gmsh.model.geo.synchronize()
24
+ box.define_bc()
25
+ surface_domain.define_bc()
26
+
27
+ # Generate mesh
28
+ gmsh.model.mesh.generate(2)
29
+ gmsh.write(str(Path(test_data_dir, "mesh_test.su2")))
30
+ gmsh.finalize()
31
+
32
+ # Test if the generated mesh is correct
33
+ with open(Path(test_data_dir, "mesh_test.su2"), "r") as f:
34
+ mesh_test = f.read()
35
+ with open(Path(test_data_dir, "mesh.su2"), "r") as f:
36
+ mesh_origin = f.read()
37
+
38
+ assert mesh_test == mesh_origin
39
+
40
+
41
+ test_mesh_rectangle()
@@ -1,106 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: gmshairfoil2d
3
- Version: 0.1.4
4
- Summary: Python tool to genreate 2D mesh around an airfoil
5
- Home-page: https://github.com/cfsengineering/GMSH-Airfoil-2D
6
- Author: Aidan Jungo
7
- Author-email: aidan.jungo@cfse.ch
8
- License: Apache License 2.0
9
- Keywords: airfoil,2D,mesh,cfd,gmsh
10
- Classifier: Programming Language :: Python :: 3
11
- Classifier: Programming Language :: Python :: 3.6
12
- Classifier: Programming Language :: Python :: 3.7
13
- Classifier: Programming Language :: Python :: 3.8
14
- Classifier: Programming Language :: Python :: 3.9
15
- Classifier: Programming Language :: Python :: 3.10
16
- Classifier: License :: OSI Approved :: Apache Software License
17
- Classifier: Operating System :: OS Independent
18
- Classifier: Intended Audience :: Developers
19
- Requires-Python: >=3.6.0
20
- Description-Content-Type: text/markdown
21
- License-File: LICENSE
22
- Requires-Dist: gmsh (==4.9.5)
23
- Requires-Dist: numpy (>=1.20.3)
24
- Requires-Dist: pytest (==7.1.3)
25
- Requires-Dist: requests (==2.26.0)
26
-
27
- [![Pytest](https://github.com/cfsengineering/GMSH-Airfoil-2D/actions/workflows/pytest.yml/badge.svg?branch=main)](https://github.com/cfsengineering/GMSH-Airfoil-2D/actions/workflows/pytest.yml)
28
- [![PyPi version](https://img.shields.io/pypi/v/gmshairfoil2d.svg)](https://pypi.python.org/pypi/gmshairfoil2d)
29
- [![License](https://img.shields.io/badge/license-Apache%202-blue.svg)](https://github.com/cfsengineering/GMSH-Airfoil-2D/blob/main/LICENSE)
30
- [![Black code style](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
31
-
32
- # GMSH-Airfoil-2D
33
-
34
- Python tool to genreate 2D unstructured mesh around an airfoil with [GMSH](https://gmsh.info/) in one command line.
35
-
36
- ## Installation
37
-
38
- You can install this package from PyPi:
39
-
40
- ```bash
41
- pip install gmshairfoil2d
42
- ```
43
-
44
- Or you can clone and install this repository with the following commands:
45
-
46
- ```bash
47
- git clone https://github.com/cfsengineering/GMSH-Airfoil-2D.git
48
- cd GMSH-Airfoil-2D
49
- pip install -e .
50
- ```
51
-
52
- ## Usage
53
-
54
- ```text
55
- gmshairfoil2d -h
56
-
57
- optional arguments:
58
- -h, --help show this help message and exit
59
- --list Display all airfoil available in the database :
60
- https://m-selig.ae.illinois.edu/ads/coord_database.html
61
- --naca [4DIGITS] NACA airfoil 4 digit (default 0012)
62
- --airfoil [NAME] Name of an airfoil profile in the database (database available with
63
- the --list argument)
64
- --aoa [AOA] Angle of attack [deg] (default: 0 [deg])
65
- --farfield [RADIUS] Create a circular farfield mesh of given radius [m] (default 10m)
66
- --box [LENGTHxWIDTH] Create a box mesh of dimensions [length]x[height] [m]
67
- --airfoil_mesh_size [SIZE] Mesh size of the airfoil countour [m] (default 0.01m)
68
- --ext_mesh_size [SIZE] Mesh size of the external domain [m] (default 0.2m)
69
- --format [FORMAT] format of the mesh file, e.g: msh, vtk, wrl, stl, mesh, cgns, su2,
70
- dat (default su2)
71
- --output [PATH] output path for the mesh file (default : current dir)
72
- --ui Open GMSH user interface to see the mesh
73
-
74
- ```
75
-
76
- ## Examples of use
77
-
78
- To check all airfoil available in the [database](https://m-selig.ae.illinois.edu/ads/coord_database.html):
79
-
80
- ```bash
81
- gmshairfoil2d --list
82
- ```
83
-
84
- For all the following examples, the defauld chord lenght is 1 meter.
85
-
86
- To create a circular farfield mesh around a NACA0012 of 10m of radius and see the result with GMSH user interface:
87
-
88
- ```bash
89
- gmshairfoil2d --naca 0012 --farfield 10 --ui
90
- ```
91
-
92
- ![GMSH user interface with the 2D mesh](images/example_mesh.png)
93
-
94
- To create a circular farfield mesh around a Drela DAE11 airfoil (the name in the database is "dae11") of 20m or radius with a mesh size of 0.005m on the airfoil:
95
-
96
- ```bash
97
- gmshairfoil2d --airfoil dae11 --farfield 20 --airfoil_mesh_size 0.005
98
- ```
99
-
100
- To create mesh around a Eppler E220 airfoil (the name in the database is "e211") with an angle of attack of 8 degree in a box of 12x4m (lenght x hight) and save it as a vtk mesh and see the result with GMSH user interface:
101
-
102
- ```bash
103
- gmshairfoil2d --airfoil e211 --aoa 8 --box 12x4 --format vtk --ui
104
- ```
105
-
106
- ![GMSH user interface with the 2D mesh, rectangular box](images/example_mesh_box.png)
@@ -1,10 +0,0 @@
1
- gmshairfoil2d/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- gmshairfoil2d/airfoil_func.py,sha256=twMt4y9PeMeBSawWv-D5jLzkKMJzEshL-BiG86tSGvk,5275
3
- gmshairfoil2d/geometry_def.py,sha256=UQPJXxCG04HZTwhb-zf2fx-NjkH5wrdPRsE8GhYVOnE,19793
4
- gmshairfoil2d/gmshairfoil2d.py,sha256=3zBnCxD0-myVcIUhacFgkP1elJZVUSAyCtOfrI_2FyQ,4589
5
- gmshairfoil2d-0.1.4.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
6
- gmshairfoil2d-0.1.4.dist-info/METADATA,sha256=U9696xMGLGf0Xm5dPQ3HcIyHgA_UlEhLw6cUWSL73iw,4306
7
- gmshairfoil2d-0.1.4.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
8
- gmshairfoil2d-0.1.4.dist-info/entry_points.txt,sha256=6OBSsEXNhTICrsEGgfg30RGIkKRFXELizYtfZfT1_zk,67
9
- gmshairfoil2d-0.1.4.dist-info/top_level.txt,sha256=MhXEKx8aoWrGbkyrGaDGJOCv3GER7nzzSfz0UsvI2NQ,14
10
- gmshairfoil2d-0.1.4.dist-info/RECORD,,