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.
@@ -1,17 +1,109 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
1
+ """Main module for GMSH airfoil 2D mesh generation."""
3
2
 
4
3
  import argparse
5
4
  import math
6
5
  import sys
7
6
  from pathlib import Path
8
- import numpy as np
9
7
 
10
8
  import gmsh
11
9
  from gmshairfoil2d.airfoil_func import (NACA_4_digit_geom, get_airfoil_points,
12
- get_all_available_airfoil_names)
10
+ get_all_available_airfoil_names, read_airfoil_from_file)
13
11
  from gmshairfoil2d.geometry_def import (AirfoilSpline, Circle, PlaneSurface,
14
12
  Rectangle, outofbounds, CType)
13
+ from gmshairfoil2d.config_handler import read_config, merge_config_with_args, create_example_config
14
+
15
+
16
+ def _calculate_spline_length(spline):
17
+ """Calculate the length of a spline based on its points.
18
+
19
+ Parameters
20
+ ----------
21
+ spline : object
22
+ Spline object with point_list attribute
23
+
24
+ Returns
25
+ -------
26
+ float
27
+ Total length of the spline
28
+ """
29
+ if not hasattr(spline, 'point_list') or not spline or not spline.point_list:
30
+ return 0
31
+
32
+ points = spline.point_list
33
+ if len(points) < 2:
34
+ return 0
35
+
36
+ return sum(
37
+ math.sqrt((points[i].x - points[i+1].x)**2 +
38
+ (points[i].y - points[i+1].y)**2)
39
+ for i in range(len(points) - 1)
40
+ )
41
+
42
+
43
+ def apply_transfinite_to_surfaces(airfoil_obj, airfoil_mesh_size, name=""):
44
+ """
45
+ Apply transfinite meshing to all three splines (upper, lower, and front) of an
46
+ airfoil or flap object for smooth cell transitions based on edge lengths.
47
+
48
+ The key is to distribute nodes proportionally to each edge's length:
49
+ - longer edges get more points
50
+ - all edges get consistent cell sizing at junctions
51
+
52
+ Parameters
53
+ ----------
54
+ airfoil_obj : AirfoilSpline
55
+ The airfoil or flap object containing front_spline, upper_spline, lower_spline
56
+ airfoil_mesh_size : float
57
+ The target mesh size to maintain consistent cell dimensions
58
+ name : str, optional
59
+ Name of the object (for logging purposes)
60
+ """
61
+ # Calculate the length of each spline
62
+ l_front = _calculate_spline_length(airfoil_obj.front_spline) if hasattr(airfoil_obj, 'front_spline') else 0
63
+ l_upper = _calculate_spline_length(airfoil_obj.upper_spline) if hasattr(airfoil_obj, 'upper_spline') else 0
64
+ l_lower = _calculate_spline_length(airfoil_obj.lower_spline) if hasattr(airfoil_obj, 'lower_spline') else 0
65
+
66
+ # Calculate total perimeter
67
+ total_length = l_front + l_upper + l_lower
68
+
69
+ if total_length == 0:
70
+ print(f"Warning: {name} has zero total length, skipping transfinite meshing")
71
+ return
72
+
73
+ # Distribute points proportionally to edge lengths
74
+ # Target cell size should be approximately airfoil_mesh_size on all edges
75
+ total_points = max(20, int(total_length / airfoil_mesh_size))
76
+
77
+ # Distribute points based on proportion of each edge length
78
+ # Front gets a multiplier for higher density at leading edge (Bump effect)
79
+ front_multiplier = 2 # 100% extra density for front region
80
+ weighted_length = l_front * front_multiplier + l_upper + l_lower
81
+
82
+ nb_points_front = max(15, int((l_front * front_multiplier / weighted_length) * total_points))
83
+ nb_points_upper = max(15, int((l_upper / weighted_length) * total_points))
84
+ nb_points_lower = max(15, int((l_lower / weighted_length) * total_points))
85
+
86
+ # Apply transfinite curves
87
+ if hasattr(airfoil_obj, 'front_spline') and airfoil_obj.front_spline:
88
+ gmsh.model.mesh.setTransfiniteCurve(
89
+ airfoil_obj.front_spline.tag, nb_points_front, "Bump", 10)
90
+
91
+ if hasattr(airfoil_obj, 'upper_spline') and airfoil_obj.upper_spline:
92
+ gmsh.model.mesh.setTransfiniteCurve(airfoil_obj.upper_spline.tag, nb_points_upper)
93
+
94
+ if hasattr(airfoil_obj, 'lower_spline') and airfoil_obj.lower_spline:
95
+ gmsh.model.mesh.setTransfiniteCurve(airfoil_obj.lower_spline.tag, nb_points_lower)
96
+
97
+ if name:
98
+ # Calculate actual cell sizes for info
99
+ front_cell_size = l_front / (nb_points_front - 1) if nb_points_front > 1 else 0
100
+ upper_cell_size = l_upper / (nb_points_upper - 1) if nb_points_upper > 1 else 0
101
+ lower_cell_size = l_lower / (nb_points_lower - 1) if nb_points_lower > 1 else 0
102
+
103
+ print(f"Applied transfinite meshing to {name}:")
104
+ print(f" - Front spline: {nb_points_front:3d} points, length={l_front:.4f}, cell size ~ {front_cell_size:.6f}")
105
+ print(f" - Upper spline: {nb_points_upper:3d} points, length={l_upper:.4f}, cell size ~ {upper_cell_size:.6f}")
106
+ print(f" - Lower spline: {nb_points_lower:3d} points, length={l_lower:.4f}, cell size ~ {lower_cell_size:.6f}")
15
107
 
16
108
 
17
109
  def main():
@@ -24,6 +116,26 @@ def main():
24
116
  ),
25
117
  )
26
118
 
119
+ parser.add_argument(
120
+ "--config",
121
+ type=str,
122
+ metavar="PATH",
123
+ help="Path to YAML configuration file",
124
+ )
125
+
126
+ parser.add_argument(
127
+ "--save_config",
128
+ type=str,
129
+ metavar="PATH",
130
+ help="Save configuration to a YAML file",
131
+ )
132
+
133
+ parser.add_argument(
134
+ "--example_config",
135
+ action="store_true",
136
+ help="Create an example configuration file (config_example.yaml)",
137
+ )
138
+
27
139
  parser.add_argument(
28
140
  "--list",
29
141
  action="store_true",
@@ -46,6 +158,20 @@ def main():
46
158
  help="Name of an airfoil profile in the database (database available with the --list argument)",
47
159
  )
48
160
 
161
+ parser.add_argument(
162
+ "--airfoil_path",
163
+ type=str,
164
+ metavar="PATH",
165
+ help="Path to a custom .dat file with airfoil coordinates",
166
+ )
167
+
168
+ parser.add_argument(
169
+ "--flap_path",
170
+ type=str,
171
+ metavar="PATH",
172
+ help="Path to a custom .dat file with flap coordinates",
173
+ )
174
+
49
175
  parser.add_argument(
50
176
  "--aoa",
51
177
  type=float,
@@ -54,6 +180,14 @@ def main():
54
180
  default=0.0,
55
181
  )
56
182
 
183
+ parser.add_argument(
184
+ "--deflection",
185
+ type=float,
186
+ nargs="?",
187
+ help="Angle of flap deflection [deg] (default: 0 [deg])",
188
+ default=0.0,
189
+ )
190
+
57
191
  parser.add_argument(
58
192
  "--farfield",
59
193
  type=float,
@@ -62,6 +196,13 @@ def main():
62
196
  default=10,
63
197
  help="Create a circular farfield mesh of given radius [m] (default 10m)",
64
198
  )
199
+
200
+ parser.add_argument(
201
+ "--farfield_ctype",
202
+ action="store_true",
203
+ help="Generate a structured circular farfield (CType) for hybrid meshes",
204
+ )
205
+
65
206
  parser.add_argument(
66
207
  "--box",
67
208
  type=str,
@@ -69,13 +210,23 @@ def main():
69
210
  nargs="?",
70
211
  help="Create a box mesh of dimensions [length]x[height] [m]",
71
212
  )
213
+
72
214
  parser.add_argument(
73
215
  "--airfoil_mesh_size",
74
216
  type=float,
75
217
  metavar="SIZE",
76
218
  nargs="?",
77
219
  default=0.01,
78
- help="Mesh size of the airfoil contour [m] (default 0.01m) (for normal, bl and structural)",
220
+ help="Mesh size of the airfoil contour [m] (default 0.01m) (for normal, bl and structured)",
221
+ )
222
+
223
+ parser.add_argument(
224
+ "--flap_mesh_size",
225
+ type=float,
226
+ metavar="SIZE",
227
+ nargs="?",
228
+ default=None,
229
+ help="Mesh size of the flap contour [m] (if not provided, defaults to 85%% of airfoil_mesh_size)",
79
230
  )
80
231
 
81
232
  parser.add_argument(
@@ -99,7 +250,7 @@ def main():
99
250
  metavar="HEIGHT",
100
251
  nargs="?",
101
252
  default=3e-5,
102
- help="Height of the first layer [m] (default 3e-5m) (for bl and structural)",
253
+ help="Height of the first layer [m] (default 3e-5m) (for bl and structured)",
103
254
  )
104
255
 
105
256
  parser.add_argument(
@@ -108,7 +259,7 @@ def main():
108
259
  metavar="RATIO",
109
260
  nargs="?",
110
261
  default=1.2,
111
- help="Growth ratio of layers (default 1.2) (for bl and structural)",
262
+ help="Growth ratio of layers (default 1.2) (for bl and structured)",
112
263
  )
113
264
 
114
265
  parser.add_argument(
@@ -129,16 +280,16 @@ def main():
129
280
  )
130
281
 
131
282
  parser.add_argument(
132
- "--structural",
283
+ "--structured",
133
284
  action="store_true",
134
- help="Generate a structural mesh",
285
+ help="Generate a structured mesh",
135
286
  )
136
287
  parser.add_argument(
137
288
  "--arg_struc",
138
289
  type=str,
139
290
  metavar="[LxL]",
140
291
  default="10x10",
141
- help="Parameters for the structural mesh [wake length (axis x)]x[total height (axis y)] [m] (default 10x10)",
292
+ help="Parameters for the structured mesh [wake length (axis x)]x[total height (axis y)] [m] (default 10x10)",
142
293
  )
143
294
 
144
295
  parser.add_argument(
@@ -157,6 +308,24 @@ def main():
157
308
  )
158
309
  args = parser.parse_args()
159
310
 
311
+ # Handle configuration file operations
312
+ if args.example_config:
313
+ create_example_config()
314
+ sys.exit()
315
+
316
+ # Load configuration from file if provided
317
+ if args.config:
318
+ try:
319
+ config_dict = read_config(args.config)
320
+ args = merge_config_with_args(config_dict, args)
321
+ print(f"Configuration loaded from: {args.config}")
322
+ except FileNotFoundError as e:
323
+ print(f"Error: {e}", file=sys.stderr)
324
+ sys.exit(1)
325
+ except Exception as e:
326
+ print(f"Error reading configuration: {e}", file=sys.stderr)
327
+ sys.exit(1)
328
+
160
329
  if len(sys.argv) == 1:
161
330
  parser.print_help()
162
331
  sys.exit()
@@ -167,6 +336,17 @@ def main():
167
336
 
168
337
  # Airfoil choice
169
338
  cloud_points = None
339
+ airfoil_name = None
340
+
341
+ # Check that only one airfoil source is specified
342
+ airfoil_sources = [args.naca, args.airfoil, args.airfoil_path]
343
+ specified_sources = [s for s in airfoil_sources if s is not None]
344
+
345
+ if len(specified_sources) > 1:
346
+ print("\nError: Only one airfoil source can be specified at a time!")
347
+ print("Choose one of: --naca, --airfoil, or --airfoil_path\n")
348
+ sys.exit(1)
349
+
170
350
  if args.naca:
171
351
  airfoil_name = args.naca
172
352
  cloud_points = NACA_4_digit_geom(airfoil_name)
@@ -175,9 +355,17 @@ def main():
175
355
  airfoil_name = args.airfoil
176
356
  cloud_points = get_airfoil_points(airfoil_name)
177
357
 
358
+ if args.airfoil_path:
359
+ airfoil_name = Path(args.airfoil_path).stem
360
+ cloud_points = read_airfoil_from_file(args.airfoil_path)
361
+
362
+ if args.flap_path:
363
+ airfoil_name = Path(args.airfoil_path).stem
364
+ flap_points = read_airfoil_from_file(args.flap_path)
365
+
178
366
  if cloud_points is None:
179
367
  print("\nNo airfoil profile specified, exiting")
180
- print("You must use --naca or --airfoil\n")
368
+ print("You must use --naca, --airfoil, or --airfoil_path\n")
181
369
  parser.print_help()
182
370
  sys.exit()
183
371
 
@@ -200,12 +388,22 @@ def main():
200
388
 
201
389
  # Airfoil
202
390
  airfoil = AirfoilSpline(
203
- cloud_points, args.airfoil_mesh_size)
391
+ cloud_points, args.airfoil_mesh_size, name="airfoil")
204
392
  airfoil.rotation(aoa, (0.5, 0, 0), (0, 0, 1))
205
393
  gmsh.model.geo.synchronize()
206
394
 
207
- # If structural, all is done in CType
208
- if args.structural:
395
+ if args.flap_path:
396
+ # Use flap_mesh_size if provided, otherwise use 85% of airfoil_mesh_size
397
+ flap_mesh_size = args.flap_mesh_size if args.flap_mesh_size else args.airfoil_mesh_size * 0.85
398
+ flap = AirfoilSpline(
399
+ flap_points, flap_mesh_size, name="flap", is_flap=True)
400
+ flap.rotation(aoa, (0.5, 0, 0), (0, 0, 1))
401
+ if args.deflection:
402
+ flap.rotation(-args.deflection * (math.pi / 180), (flap.le.x, flap.le.y, 0), (0, 0, 1))
403
+ gmsh.model.geo.synchronize()
404
+
405
+ # If structured, all is done in CType
406
+ if args.structured:
209
407
  dx_wake, dy = [float(value)for value in args.arg_struc.split("x")]
210
408
  mesh = CType(airfoil, dx_wake, dy,
211
409
  args.airfoil_mesh_size, args.first_layer, args.ratio, aoa)
@@ -213,6 +411,8 @@ def main():
213
411
 
214
412
  else:
215
413
  k1, k2 = airfoil.gen_skin()
414
+ if args.flap_path:
415
+ k1_flap, k2_flap = flap.gen_skin()
216
416
  # Choose the parameters for bl (when exist)
217
417
  if not args.no_bl:
218
418
  N = args.nb_layers
@@ -228,7 +428,13 @@ def main():
228
428
  outofbounds(airfoil, args.box, args.farfield, d[-1])
229
429
 
230
430
  # External domain
231
- if args.box:
431
+ if args.farfield_ctype:
432
+ # Use C-type farfield (unstructured) for hybrid meshes
433
+ ext_domain = CType(
434
+ airfoil, args.farfield, args.farfield, args.ext_mesh_size,
435
+ structured=args.structured
436
+ )
437
+ elif args.box:
232
438
  length, width = [float(value) for value in args.box.split("x")]
233
439
  ext_domain = Rectangle(0.5, 0, 0, length, width,
234
440
  mesh_size=args.ext_mesh_size)
@@ -238,13 +444,21 @@ def main():
238
444
  gmsh.model.geo.synchronize()
239
445
 
240
446
  # Create the surface for the mesh
241
- surface = PlaneSurface([ext_domain, airfoil])
447
+ if args.flap_path:
448
+ surface = PlaneSurface([ext_domain, airfoil, flap])
449
+
450
+ else:
451
+ surface = PlaneSurface([ext_domain, airfoil])
452
+
242
453
  gmsh.model.geo.synchronize()
243
454
 
244
455
  # Create the boundary layer
245
456
  if not args.no_bl:
246
457
  curv = [airfoil.upper_spline.tag,
247
458
  airfoil.lower_spline.tag, airfoil.front_spline.tag]
459
+ if args.flap_path:
460
+ curv += [flap.upper_spline.tag,
461
+ flap.lower_spline.tag, flap.front_spline.tag]
248
462
 
249
463
  # Creates a new mesh field of type 'BoundaryLayer' and assigns it an ID (f).
250
464
  f = gmsh.model.mesh.field.add('BoundaryLayer')
@@ -260,8 +474,13 @@ def main():
260
474
  gmsh.model.mesh.field.setNumber(f, 'Quads', 1)
261
475
 
262
476
  # 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])
477
+ if args.flap_path:
478
+ print(f"airfoil.te.tag, flap.te.tag = {airfoil.te.tag, flap.te.tag}")
479
+ gmsh.model.mesh.field.setNumbers(
480
+ f, "FanPointsList", [airfoil.te.tag, flap.te.tag])
481
+ else:
482
+ gmsh.model.mesh.field.setNumbers(
483
+ f, "FanPointsList", [airfoil.te.tag])
265
484
 
266
485
  gmsh.model.mesh.field.setAsBoundaryLayer(f)
267
486
 
@@ -269,23 +488,26 @@ def main():
269
488
  ext_domain.define_bc()
270
489
  surface.define_bc()
271
490
  airfoil.define_bc()
491
+ if args.flap_path:
492
+ flap.define_bc()
272
493
 
273
494
  gmsh.model.geo.synchronize()
274
495
 
275
496
  # Choose the parameters of the mesh : we want the mesh size according to the points and not curvature (doesn't work with farfield)
276
497
  gmsh.option.setNumber("Mesh.MeshSizeFromPoints", 1)
277
498
  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)
499
+
500
+ if not args.structured and not args.no_bl:
501
+ # Apply transfinite meshing to all three splines (front, upper, lower)
502
+ # for consistent cell sizing around the airfoil/flap surfaces
503
+
504
+ # Apply to airfoil
505
+ apply_transfinite_to_surfaces(airfoil, args.airfoil_mesh_size, name="Airfoil")
506
+
507
+ # Apply to flap if present
508
+ if args.flap_path:
509
+ apply_transfinite_to_surfaces(flap, args.airfoil_mesh_size, name="Flap")
510
+
289
511
  # Choose the nbs of points in the fan at the te:
290
512
  # Compute coef : between 10 and 25, 15 when usual mesh size but adapted to mesh size
291
513
  coef = max(10, min(25, 15*0.01/args.airfoil_mesh_size))
@@ -300,11 +522,25 @@ def main():
300
522
  gmsh.fltk.run()
301
523
 
302
524
  # Mesh file name and output
525
+ if airfoil_name:
526
+ airfoil_name = airfoil_name.replace(".dat", "")
527
+
528
+ if args.flap_path:
529
+ airfoil_name = airfoil_name + "_flap"
530
+
303
531
  mesh_path = Path(
304
532
  args.output, f"mesh_airfoil_{airfoil_name}.{args.format}")
305
533
  gmsh.write(str(mesh_path))
306
534
  gmsh.finalize()
307
535
 
536
+ # Save configuration if requested
537
+ if args.save_config:
538
+ # Remove None values and internal arguments from the config dict
539
+ config_dict = {k: v for k, v in vars(args).items()
540
+ if v is not None and v is not False
541
+ and k not in ['config', 'save_config', 'example_config', 'list']}
542
+ from gmshairfoil2d.config_handler import write_config
543
+ write_config(config_dict, args.save_config)
308
544
 
309
545
  if __name__ == "__main__":
310
546
  main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gmshairfoil2d
3
- Version: 0.2.2
3
+ Version: 0.2.31
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
@@ -14,9 +14,9 @@ Requires-Python: >=3.11.0
14
14
  Description-Content-Type: text/markdown
15
15
  License-File: LICENSE
16
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
17
+ Requires-Dist: numpy>=2.2.4
18
+ Requires-Dist: pytest>=8.3.4
19
+ Requires-Dist: requests>=2.26.0
20
20
  Dynamic: author
21
21
  Dynamic: author-email
22
22
  Dynamic: classifier
@@ -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) (for normal, bl
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 structural)
78
- --ratio [RATIO] Growth ratio of layers (default 1.2) (for bl and structural)
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
- --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)
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 defauld chord lenght is 1 meter.
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,88 @@ 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
  ![GMSH user interface with the 2D mesh](images/example_mesh.png)
107
151
 
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):
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
- 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:
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
- ![GMSH user interface with the 2D mesh, rectangular box](images/example_mesh_box.png)
174
+ Or using config file:
121
175
 
176
+ ```bash
177
+ gmshairfoil2d --config example/example3_e211_box.cfg
178
+ ```
122
179
 
180
+ ![GMSH user interface with the 2D mesh, rectangular box](images/example_mesh_box.png)
181
+
182
+ ### Example 4: CH10SM with box and boundary layer
123
183
 
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) :
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
  ![GMSH result with 2D mesh with boundary layer, rectangular box](images/example_ch10sm_bl.png)
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
+ ![GMSH result with 2D structured mesh](images/example_structured_naca4220.png)
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
- 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 :
221
+ Or using config file:
134
222
 
135
223
  ```bash
136
- gmshairfoil2d --naca 4220 --airfoil_mesh_size 0.08 --ui --structural --first_layer 0.01 --arg_struc 6x7 --aoa 6
224
+ gmshairfoil2d --config example/example6_custom_airfoil_flap.cfg
137
225
  ```
138
226
 
139
- ![GMSH result with 2D structural mesh](images/example_structural_naca4220.png)
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.
228
+ ![GMSH result with 2D mesh around a custom flapped airfoil](images/example_flapped_airfoil.png)
@@ -0,0 +1,16 @@
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,,