gmshairfoil2d 0.1.4__py3-none-any.whl → 0.2__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.
@@ -6,6 +6,7 @@ from operator import attrgetter
6
6
  import gmsh
7
7
  import numpy as np
8
8
  import math
9
+ import sys
9
10
 
10
11
 
11
12
  class Point:
@@ -32,18 +33,17 @@ class Point:
32
33
  self.x = x
33
34
  self.y = y
34
35
  self.z = z
35
-
36
36
  self.mesh_size = mesh_size
37
37
  self.dim = 0
38
38
 
39
39
  # create the gmsh object and store the tag of the geometric object
40
- self.tag = gmsh.model.occ.addPoint(self.x, self.y, self.z, self.mesh_size)
40
+ self.tag = gmsh.model.geo.addPoint(
41
+ self.x, self.y, self.z, self.mesh_size)
41
42
 
42
43
  def rotation(self, angle, origin, axis):
43
44
  """
44
- Methode to rotate the object Point
45
+ Method to rotate the object Point
45
46
  ...
46
-
47
47
  Parameters
48
48
  ----------
49
49
  angle : float
@@ -53,7 +53,7 @@ class Point:
53
53
  axis : tuple
54
54
  tuple of point (x,y,z) which represent the axis of rotation
55
55
  """
56
- gmsh.model.occ.rotate(
56
+ gmsh.model.geo.rotate(
57
57
  [(self.dim, self.tag)],
58
58
  *origin,
59
59
  *axis,
@@ -62,7 +62,8 @@ class Point:
62
62
 
63
63
  def translation(self, vector):
64
64
  """
65
- Methode to translate the object Point
65
+ Method to translate the object Point
66
+
66
67
  ...
67
68
 
68
69
  Parameters
@@ -70,7 +71,7 @@ class Point:
70
71
  direction : tuple
71
72
  tuple of point (x,y,z) which represent the direction of the translation
72
73
  """
73
- gmsh.model.occ.translate([(self.dim, self.tag)], *vector)
74
+ gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
74
75
 
75
76
 
76
77
  class Line:
@@ -94,11 +95,12 @@ class Line:
94
95
  self.dim = 1
95
96
 
96
97
  # create the gmsh object and store the tag of the geometric object
97
- self.tag = gmsh.model.occ.addLine(self.start_point.tag, self.end_point.tag)
98
+ self.tag = gmsh.model.geo.addLine(
99
+ self.start_point.tag, self.end_point.tag)
98
100
 
99
101
  def rotation(self, angle, origin, axis):
100
102
  """
101
- Methode to rotate the object Line
103
+ Method to rotate the object Line
102
104
  ...
103
105
 
104
106
  Parameters
@@ -110,7 +112,7 @@ class Line:
110
112
  axis : tuple
111
113
  tuple of point (x,y,z) which represent the axis of rotation
112
114
  """
113
- gmsh.model.occ.rotate(
115
+ gmsh.model.geo.rotate(
114
116
  [(self.dim, self.tag)],
115
117
  *origin,
116
118
  *axis,
@@ -119,7 +121,7 @@ class Line:
119
121
 
120
122
  def translation(self, vector):
121
123
  """
122
- Methode to translate the object Line
124
+ Method to translate the object Line
123
125
  ...
124
126
 
125
127
  Parameters
@@ -127,7 +129,7 @@ class Line:
127
129
  direction : tuple
128
130
  tuple of point (x,y,z) which represent the direction of the translation
129
131
  """
130
- gmsh.model.occ.translate([(self.dim, self.tag)], *vector)
132
+ gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
131
133
 
132
134
 
133
135
  class Spline:
@@ -145,17 +147,17 @@ class Spline:
145
147
  def __init__(self, point_list):
146
148
  self.point_list = point_list
147
149
 
148
- # generate the Lines tag list to folow
150
+ # generate the Lines tag list to follow
149
151
  self.tag_list = [point.tag for point in self.point_list]
150
152
  self.dim = 1
151
153
  # create the gmsh object and store the tag of the geometric object
152
- self.tag = gmsh.model.occ.addSpline(self.tag_list)
154
+ self.tag = gmsh.model.geo.addSpline(self.tag_list)
153
155
 
154
156
  def rotation(self, angle, origin, axis):
155
157
  """
156
- Methode to rotate the object Spline
158
+ Method to rotate the object Spline
157
159
 
158
- Rotate the spline itself (curve, starpoint,endpoint), then rotate the indermediate points
160
+ Rotate the spline itself (curve, startpoint, endpoint), then rotate the intermediate points
159
161
  ...
160
162
 
161
163
  Parameters
@@ -167,7 +169,7 @@ class Spline:
167
169
  axis : tuple
168
170
  tuple of point (x,y,z) which represent the axis of rotation
169
171
  """
170
- gmsh.model.occ.rotate(
172
+ gmsh.model.geo.rotate(
171
173
  [(self.dim, self.tag)],
172
174
  *origin,
173
175
  *axis,
@@ -181,9 +183,9 @@ class Spline:
181
183
 
182
184
  def translation(self, vector):
183
185
  """
184
- Methode to translate the object Line
186
+ Method to translate the object Line
185
187
 
186
- Translate the spline itself (curve, starpoint,endpoint), then translate the indermediate points
188
+ Translate the spline itself (curve, startpoint,endpoint), then translate the indermediate points
187
189
  ...
188
190
 
189
191
  Parameters
@@ -191,8 +193,9 @@ class Spline:
191
193
  direction : tuple
192
194
  tuple of point (x,y,z) which represent the direction of the translation
193
195
  """
194
- gmsh.model.occ.translate([(self.dim, self.tag)], *vector)
195
- [interm_point.translation(vector) for interm_point in self.point_list[1:-1]]
196
+ gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
197
+ [interm_point.translation(vector)
198
+ for interm_point in self.point_list[1:-1]]
196
199
 
197
200
 
198
201
  class CurveLoop:
@@ -207,17 +210,40 @@ class CurveLoop:
207
210
  ----------
208
211
  line_list : list(Line)
209
212
  List of Line object, in the order of the wanted CurveLoop and closed
213
+ Possibility to give either the tags directly, or the object Line
210
214
  """
211
215
 
212
216
  def __init__(self, line_list):
213
217
 
214
218
  self.line_list = line_list
215
219
  self.dim = 1
216
-
217
- # generate the Lines tag list to folow
220
+ # generate the Lines tag list to follow
218
221
  self.tag_list = [line.tag for line in self.line_list]
219
222
  # create the gmsh object and store the tag of the geometric object
220
- self.tag = gmsh.model.occ.addCurveLoop(self.tag_list)
223
+ self.tag = gmsh.model.geo.addCurveLoop(self.tag_list)
224
+
225
+ def close_loop(self):
226
+ """
227
+ Method to form a close loop with the current geometrical object. In our case,
228
+ we already have it so just return the tag
229
+
230
+ Returns
231
+ -------
232
+ _ : int
233
+ return the tag of the CurveLoop object
234
+ """
235
+ return self.tag
236
+
237
+ def define_bc(self):
238
+ """
239
+ Method that define the marker of the CurveLoop (when used as boundary layer boundary)
240
+ for the boundary condition
241
+ -------
242
+ """
243
+
244
+ self.bc = gmsh.model.addPhysicalGroup(self.dim, [self.tag])
245
+ self.physical_name = gmsh.model.setPhysicalName(
246
+ self.dim, self.bc, "top of boundary layer")
221
247
 
222
248
 
223
249
  class Circle:
@@ -232,7 +258,7 @@ class Circle:
232
258
  position of the center in x
233
259
  yc : float
234
260
  position of the center in y
235
- z : float
261
+ zc : float
236
262
  position in z
237
263
  radius : float
238
264
  radius of the circle
@@ -251,22 +277,39 @@ class Circle:
251
277
  self.mesh_size = mesh_size
252
278
  self.dim = 1
253
279
 
254
- # create a structured arcCricle to merge in one curveloop
255
- self.distribution = math.floor((np.pi * 2 * self.radius) / self.mesh_size)
280
+ # create multiples ArcCircle to merge in one circle
281
+
282
+ # first compute how many points on the circle (for the meshing to be alined with the points)
283
+ self.distribution = math.floor(
284
+ (np.pi * 2 * self.radius) / self.mesh_size)
285
+ realmeshsize = (np.pi * 2 * self.radius)/self.distribution
286
+
287
+ # Create the center of the circle
288
+ center = Point(self.xc, self.yc, self.zc, realmeshsize)
289
+
290
+ # Create all the points for the circle
291
+ points = []
292
+ for i in range(0, self.distribution):
293
+ angle = 2 * np.pi / self.distribution * i
294
+ p = Point(self.xc+self.radius*math.cos(angle), self.yc+self.radius *
295
+ math.sin(angle), self.zc, realmeshsize)
296
+ points.append(p)
297
+ # Add the first point last for continuity when creating the arcs
298
+ points.append(points[0])
299
+
300
+ # Create arcs between two neighbouring points to create a circle
256
301
  self.arcCircle_list = [
257
- gmsh.model.occ.addCircle(
258
- self.xc,
259
- self.yc,
260
- self.zc,
261
- self.radius,
262
- angle1=2 * np.pi / self.distribution * i,
263
- angle2=2 * np.pi / self.distribution * (1 + i),
302
+ gmsh.model.geo.addCircleArc(
303
+ points[i].tag,
304
+ center.tag,
305
+ points[i+1].tag,
264
306
  )
265
307
  for i in range(0, self.distribution)
266
308
  ]
309
+
267
310
  # Remove the duplicated points generated by the arcCircle
268
- gmsh.model.occ.synchronize()
269
- gmsh.model.occ.removeAllDuplicates()
311
+ gmsh.model.geo.synchronize()
312
+ gmsh.model.geo.removeAllDuplicates()
270
313
 
271
314
  def close_loop(self):
272
315
  """
@@ -277,7 +320,7 @@ class Circle:
277
320
  _ : int
278
321
  return the tag of the CurveLoop object
279
322
  """
280
- return gmsh.model.occ.addCurveLoop(self.arcCircle_list)
323
+ return gmsh.model.geo.addCurveLoop(self.arcCircle_list)
281
324
 
282
325
  def define_bc(self):
283
326
  """
@@ -287,11 +330,12 @@ class Circle:
287
330
  """
288
331
 
289
332
  self.bc = gmsh.model.addPhysicalGroup(self.dim, self.arcCircle_list)
290
- self.physical_name = gmsh.model.setPhysicalName(self.dim, self.bc, "farfield")
333
+ self.physical_name = gmsh.model.setPhysicalName(
334
+ self.dim, self.bc, "farfield")
291
335
 
292
336
  def rotation(self, angle, origin, axis):
293
337
  """
294
- Methode to rotate the object Circle
338
+ Method to rotate the object Circle
295
339
  ...
296
340
 
297
341
  Parameters
@@ -304,7 +348,7 @@ class Circle:
304
348
  tuple of point (x,y,z) which represent the axis of rotation
305
349
  """
306
350
  [
307
- gmsh.model.occ.rotate(
351
+ gmsh.model.geo.rotate(
308
352
  [(self.dim, arccircle)],
309
353
  *origin,
310
354
  *axis,
@@ -315,7 +359,7 @@ class Circle:
315
359
 
316
360
  def translation(self, vector):
317
361
  """
318
- Methode to translate the object Circle
362
+ Method to translate the object Circle
319
363
  ...
320
364
 
321
365
  Parameters
@@ -324,7 +368,7 @@ class Circle:
324
368
  tuple of point (x,y,z) which represent the direction of the translation
325
369
  """
326
370
  [
327
- gmsh.model.occ.translate([(self.dim, arccircle)], *vector)
371
+ gmsh.model.geo.translate([(self.dim, arccircle)], *vector)
328
372
  for arccircle in self.arcCircle_list
329
373
  ]
330
374
 
@@ -362,14 +406,18 @@ class Rectangle:
362
406
 
363
407
  self.mesh_size = mesh_size
364
408
  self.dim = 1
365
-
366
409
  # Generate the 4 corners of the rectangle
367
410
  self.points = [
368
- Point(self.xc - self.dx / 2, self.yc - self.dy / 2, z, self.mesh_size),
369
- Point(self.xc + self.dx / 2, self.yc - self.dy / 2, z, self.mesh_size),
370
- Point(self.xc + self.dx / 2, self.yc + self.dy / 2, z, self.mesh_size),
371
- Point(self.xc - self.dx / 2, self.yc + self.dy / 2, z, self.mesh_size),
411
+ Point(self.xc - self.dx / 2, self.yc -
412
+ self.dy / 2, z, self.mesh_size),
413
+ Point(self.xc + self.dx / 2, self.yc -
414
+ self.dy / 2, z, self.mesh_size),
415
+ Point(self.xc + self.dx / 2, self.yc +
416
+ self.dy / 2, z, self.mesh_size),
417
+ Point(self.xc - self.dx / 2, self.yc +
418
+ self.dy / 2, z, self.mesh_size),
372
419
  ]
420
+ gmsh.model.geo.synchronize()
373
421
 
374
422
  # Generate the 4 lines of the rectangle
375
423
  self.lines = [
@@ -379,6 +427,8 @@ class Rectangle:
379
427
  Line(self.points[3], self.points[0]),
380
428
  ]
381
429
 
430
+ gmsh.model.geo.synchronize()
431
+
382
432
  def close_loop(self):
383
433
  """
384
434
  Method to form a close loop with the current geometrical object
@@ -400,10 +450,12 @@ class Rectangle:
400
450
  -------
401
451
  """
402
452
 
403
- self.bc_in = gmsh.model.addPhysicalGroup(self.dim, [self.lines[3].tag], tag=-1)
453
+ self.bc_in = gmsh.model.addPhysicalGroup(
454
+ self.dim, [self.lines[3].tag], tag=-1)
404
455
  gmsh.model.setPhysicalName(self.dim, self.bc_in, "inlet")
405
456
 
406
- self.bc_out = gmsh.model.addPhysicalGroup(self.dim, [self.lines[1].tag])
457
+ self.bc_out = gmsh.model.addPhysicalGroup(
458
+ self.dim, [self.lines[1].tag])
407
459
  gmsh.model.setPhysicalName(self.dim, self.bc_out, "outlet")
408
460
 
409
461
  self.bc_wall = gmsh.model.addPhysicalGroup(
@@ -415,7 +467,7 @@ class Rectangle:
415
467
 
416
468
  def rotation(self, angle, origin, axis):
417
469
  """
418
- Methode to rotate the object Rectangle
470
+ Method to rotate the object Rectangle
419
471
  ...
420
472
 
421
473
  Parameters
@@ -431,7 +483,7 @@ class Rectangle:
431
483
 
432
484
  def translation(self, vector):
433
485
  """
434
- Methode to translate the object Rectangle
486
+ Method to translate the object Rectangle
435
487
  ...
436
488
 
437
489
  Parameters
@@ -453,9 +505,9 @@ class Airfoil:
453
505
  point_cloud : list(list(float))
454
506
  List of points forming the airfoil in the order,
455
507
  each point is a list containing in the order
456
- its postion x,y,z
508
+ its position x,y,z
457
509
  mesh_size : float
458
- attribute given for the class Point,Note that a mesh size larger
510
+ attribute given for the class Point, Note that a mesh size larger
459
511
  than the resolution given by the cloud of points
460
512
  will not be taken into account
461
513
  name : str
@@ -507,7 +559,7 @@ class Airfoil:
507
559
 
508
560
  def rotation(self, angle, origin, axis):
509
561
  """
510
- Methode to rotate the object CurveLoop
562
+ Method to rotate the object CurveLoop
511
563
  ...
512
564
 
513
565
  Parameters
@@ -523,7 +575,7 @@ class Airfoil:
523
575
 
524
576
  def translation(self, vector):
525
577
  """
526
- Methode to translate the object CurveLoop
578
+ Method to translate the object CurveLoop
527
579
  ...
528
580
 
529
581
  Parameters
@@ -544,60 +596,147 @@ class AirfoilSpline:
544
596
  point_cloud : list(list(float))
545
597
  List of points forming the airfoil in the order,
546
598
  each point is a list containing in the order
547
- its postion x,y,z
599
+ its position x,y,z
548
600
  mesh_size : float
549
- attribute given for the class Point,Note that a mesh size larger
601
+ attribute given for the class Point, (Note that a mesh size larger
550
602
  than the resolution given by the cloud of points
551
- will not be taken into account
603
+ will not be taken into account --> Not implemented)
552
604
  name : str
553
605
  name of the marker that will be associated to the airfoil
554
606
  boundary condition
555
607
  """
556
608
 
557
- def __init__(self, point_cloud, mesh_size, name="airfoil"):
609
+ def __init__(self, point_cloud, mesh_size, name="airfoil"):
558
610
 
559
611
  self.name = name
560
612
  self.dim = 1
613
+ self.mesh_size = mesh_size
561
614
 
562
615
  # Generate Points object from the point_cloud
563
616
  self.points = [
564
617
  Point(point_cord[0], point_cord[1], point_cord[2], mesh_size)
565
618
  for point_cord in point_cloud
566
619
  ]
620
+
567
621
  # Find leading and trailing edge location
568
622
  # in space
569
623
  self.le = min(self.points, key=attrgetter("x"))
570
624
  self.te = max(self.points, key=attrgetter("x"))
571
-
572
625
  # in the list of point
573
- self.le_indx = self.points.index(self.le)
574
626
  self.te_indx = self.points.index(self.te)
627
+ self.le_indx = self.points.index(self.le)
628
+
629
+ # Check if the airfoil end in a single point, or with two different points (vertical of each other)
630
+ vertical = False
631
+ # If two (in the end) are so close in coordinate x, they are vertical and not just neighbouring point (examples below)
632
+ # Just need to check if the one before or after and then label them correctly
633
+ if self.points[self.te_indx-1].x > self.te.x-0.0001:
634
+ te_up_indx = self.te_indx-1
635
+ te_down_indx = self.te_indx
636
+ vertical = True
637
+ elif self.points[self.te_indx+1].x > self.te.x-0.0001:
638
+ te_up_indx = self.te_indx
639
+ te_down_indx = self.te_indx+1
640
+ vertical = True
641
+
642
+ # If end with two points, add one point in the prolongation of the curves to get pointy edge
643
+ if vertical:
644
+ # Compute the prolongation of the last segment and their meetpoint : it will be the new point
645
+ x, y, z, w = self.points[te_up_indx].x, self.points[
646
+ te_up_indx].y, self.points[te_down_indx].x, self.points[te_down_indx].y
647
+ a, b, c, d = x - self.points[te_up_indx-1].x, y-self.points[te_up_indx -
648
+ 1].y, z-self.points[te_down_indx+1].x, w-self.points[te_down_indx+1].y
649
+ #
650
+ # We have that (x,y) are the coordinates of te_up and (z,w) the coordinates of te_down
651
+ # p1 is the point before te_up and p2 the point after te_down
652
+ # \
653
+ # . p1
654
+ # \ vector from p1 to (x,y) is (a,b)
655
+ # \
656
+ # . (x,y)
657
+ # p2
658
+ # .----->. . <- want to compute this point (which will be our new te)
659
+ # (z,w)
660
+ # vector from p2 to (z,w) is (c,d)
661
+ #
662
+
663
+ nothing = False
664
+ # We compute mu: solution from the system (x,y)+lambda(a,b)=(z,w)+mu(c,d) that gives us the intersection point
665
+ if b*c-a*d != 0:
666
+ mu = (b*(x-z)+a*(w-y))/(b*c-a*d)
667
+ else:
668
+ # only happens with vr7b and vr8b (parallel edges so no solutions)
669
+ mu = 10000000
670
+ # will be treated in the else
671
+
672
+ # Now to be coherent, we want the pointy edge to have a x coordinate between te.x and 0.1 further
673
+ if z+mu*c <= self.te.x + 0.1 and z+mu*c > self.te.x:
674
+ new = Point(z+mu*c, w+mu*d, 0, self.mesh_size)
675
+
676
+ # If not, it can be in the wrong direction (like with oaf095) or absurdly far (like with hh02), and so we constrain it
677
+ # (happens with roughly 40 airfoils, most because not well discretized)
678
+
679
+ # First we take off the ones in this case (new would be so close) are the one not well coded with two really close horizontally points (like s2091). So we just don't do anything
680
+ elif z+mu*c > self.te.x-0.001 and z+mu*c <= self.te.x:
681
+ nothing = True
682
+ else:
683
+ # if too far or behind, we take the point p in the middle of (x,y) and (z,w), and add the vector mean of (a,b) and (c,d) until we reach 1.05 (or te +0.05 to be precise)
684
+ px, py = (x+z)/2, (y+w)/2
685
+ e, f = (a+c)/2, (b+d)/2
686
+ lambd = (self.te.x + 0.05-px)/e
687
+ new = Point(self.te.x + 0.05, py+lambd*f, 0, self.mesh_size)
688
+
689
+ if not nothing:
690
+ # Now insert the point in th list of points and change the index for te to be the new point
691
+ self.points.insert(te_down_indx, new)
692
+ self.te = new
693
+ self.te_indx = te_down_indx
575
694
 
576
695
  def gen_skin(self):
577
696
  """
578
- Method to generate the two splines forming the foil, Only call this function when the points
697
+ Method to generate the three splines forming the foil, Only call this function when the points
579
698
  of the airfoil are in their final position
580
699
  -------
581
700
  """
582
- # Create the Splines depending on the le and te location in point_cloud
583
- if self.le_indx < self.te_indx:
584
- # create a spline from the leading edge to the trailing edge
585
- self.upper_spline = Spline(self.points[self.le_indx : self.te_indx + 1])
586
- # create a spline from the trailing edge to the leading edge
587
- self.lower_spline = Spline(
588
- self.points[self.te_indx :] + self.points[: (self.le_indx) + 1]
589
- )
701
+ # Find the first point after 0.049 in the upper band lower spline
702
+ debut = True
703
+ for p in self.points:
704
+ if p.x > 0.049 and debut:
705
+ k1 = self.points.index(p)
706
+ debut = False
707
+ if p.x <= 0.049 and not debut:
708
+ k2 = self.points.index(p)-1
709
+ break
710
+
711
+ # create a spline from the up middle point to the trailing edge (up part)
712
+ self.upper_spline = Spline(
713
+ self.points[k1: self.te_indx + 1])
714
+
715
+ # create a spline from the trailing edge to the up down point (down part)
716
+ self.lower_spline = Spline(
717
+ self.points[self.te_indx:k2+1]
718
+ )
590
719
 
591
- else:
592
- # create a spline from the leading edge to the trailing edge
593
- self.upper_spline = Spline(
594
- self.points[self.le_indx :] + self.points[: (self.te_indx + 1)]
595
- )
596
- # create a spline from the trailing edge to the leading edge
597
- self.lower_spline = Spline(self.points[self.te_indx : self.le_indx + 1])
720
+ # Create a spline for the front part of the airfoil
721
+ self.front_spline = Spline(self.points[k2:]+self.points[:k1+1])
722
+
723
+ return k1, k2
598
724
 
725
+ def gen_skin_struct(self, k1, k2):
726
+ """
727
+ Method to generate the two splines forming the foil for structural mesh, Only call this function when the points
728
+ of the airfoil are in their final position
729
+ -------
730
+ """
731
+ # create a spline from the up middle point to the trailing edge (up part)
732
+ self.upper_spline = Spline(
733
+ self.points[k1: self.te_indx + 1])
734
+
735
+ # create a spline from the trailing edge to the up down point (down part)
736
+ self.lower_spline = Spline(
737
+ self.points[self.te_indx:k2+1]
738
+ )
599
739
  return self.upper_spline, self.lower_spline
600
- # form the curvedloop
601
740
 
602
741
  def close_loop(self):
603
742
  """
@@ -608,7 +747,7 @@ class AirfoilSpline:
608
747
  _ : int
609
748
  return the tag of the CurveLoop object
610
749
  """
611
- return CurveLoop([self.upper_spline, self.lower_spline]).tag
750
+ return CurveLoop([self.upper_spline, self.lower_spline, self.front_spline]).tag
612
751
 
613
752
  def define_bc(self):
614
753
  """
@@ -617,13 +756,14 @@ class AirfoilSpline:
617
756
  """
618
757
 
619
758
  self.bc = gmsh.model.addPhysicalGroup(
620
- self.dim, [self.upper_spline.tag, self.lower_spline.tag]
759
+ self.dim, [self.upper_spline.tag,
760
+ self.lower_spline.tag, self.front_spline.tag]
621
761
  )
622
762
  gmsh.model.setPhysicalName(self.dim, self.bc, self.name)
623
763
 
624
764
  def rotation(self, angle, origin, axis):
625
765
  """
626
- Methode to rotate the object AirfoilSpline
766
+ Method to rotate the object AirfoilSpline
627
767
  ...
628
768
 
629
769
  Parameters
@@ -636,10 +776,11 @@ class AirfoilSpline:
636
776
  tuple of point (x,y,z) which represent the axis of rotation
637
777
  """
638
778
  [point.rotation(angle, origin, axis) for point in self.points]
779
+ gmsh.model.geo.synchronize()
639
780
 
640
781
  def translation(self, vector):
641
782
  """
642
- Methode to translate the object AirfoilSpline
783
+ Method to translate the object AirfoilSpline
643
784
  ...
644
785
 
645
786
  Parameters
@@ -654,7 +795,6 @@ class PlaneSurface:
654
795
  """
655
796
  A class to represent the PlaneSurface geometrical object of gmsh
656
797
 
657
-
658
798
  ...
659
799
 
660
800
  Attributes
@@ -671,12 +811,12 @@ class PlaneSurface:
671
811
 
672
812
  self.geom_objects = geom_objects
673
813
  # close_loop() will form a close loop object and return its tag
674
- self.tag_list = [geom_object.close_loop() for geom_object in self.geom_objects]
675
-
814
+ self.tag_list = [geom_object.close_loop()
815
+ for geom_object in self.geom_objects]
676
816
  self.dim = 2
677
817
 
678
818
  # create the gmsh object and store the tag of the geometric object
679
- self.tag = gmsh.model.occ.addPlaneSurface(self.tag_list)
819
+ self.tag = gmsh.model.geo.addPlaneSurface(self.tag_list)
680
820
 
681
821
  def define_bc(self):
682
822
  """
@@ -685,3 +825,365 @@ class PlaneSurface:
685
825
  """
686
826
  self.ps = gmsh.model.addPhysicalGroup(self.dim, [self.tag])
687
827
  gmsh.model.setPhysicalName(self.dim, self.ps, "fluid")
828
+
829
+
830
+ def outofbounds(airfoil, box, radius, blthick):
831
+ """Method that checks if the boundary layer or airfoil goes out of the box/farfield
832
+ (which is a problem for meshing later)
833
+
834
+ Args:
835
+ cloud_points (AirfoilSpline):
836
+ The AirfoilSpline containing the points
837
+ box (string):
838
+ the box arguments received by the parser (float x float)
839
+ radius (float):
840
+ radius of the farfield
841
+ blthick (float):
842
+ total thickness of the boundary layer (0 for mesh without bl)
843
+ """
844
+ if box:
845
+ length, width = [float(value) for value in box.split("x")]
846
+ # Compute the min and max values in the x and y directions
847
+ minx = min(p.x for p in airfoil.points)
848
+ maxx = max(p.x for p in airfoil.points)
849
+ miny = min(p.y for p in airfoil.points)
850
+ maxy = max(p.y for p in airfoil.points)
851
+ # Check :
852
+ # If the max-0.5 (which is just recentering the airfoil in 0)+bl thickness value is bigger than length/2 --> too far right.
853
+ # Same with min and left. (minx & maxx should be 0 & 1 but we recompute to be sure)
854
+ # Same in y.
855
+ if abs(maxx-0.5)+abs(blthick) > length/2 or abs(minx-0.5)+abs(blthick) > length/2 or abs(maxy)+abs(blthick) > width/2 or abs(miny)+abs(blthick) > width/2:
856
+ print("\nThe boundary layer or airfoil is bigger than the box, exiting")
857
+ print(
858
+ "You must change the boundary layer parameters or choose a bigger box\n")
859
+ sys.exit()
860
+ else:
861
+ # Compute the further from (0.5,0,0) a point is (norm of (x-0.5,y))
862
+ maxr = math.sqrt(max((p.x-0.5)*(p.x-0.5)+p.y*p.y
863
+ for p in airfoil.points))
864
+ # Check if furthest + bl is bigger than radius
865
+ if maxr+abs(blthick) > radius:
866
+ print("\nThe boundary layer or airfoil is bigger than the circle, exiting")
867
+ print(
868
+ "You must change the boundary layer parameters or choose a bigger radius\n")
869
+ sys.exit()
870
+
871
+
872
+ class CType:
873
+ """
874
+ A class to represent a C-type structured mesh.
875
+ """
876
+
877
+ def __init__(self, airfoil_spline, dx_trail, dy, mesh_size, height, ratio, aoa):
878
+ z = 0
879
+ self.airfoil_spline = airfoil_spline
880
+
881
+ self.dx_trail = dx_trail
882
+ self.dy = dy
883
+
884
+ self.mesh_size = mesh_size
885
+ # Because all the computations are based on the mesh size at the trailing edge which is the biggest accross the whole airfoil, we take it bigger
886
+ # so that the mesh size is right mostly on the middle of the airfoil
887
+ mesh_size_end = mesh_size*2
888
+ self.mesh_size_end = mesh_size_end
889
+
890
+ self.firstheight = height
891
+ self.ratio = ratio
892
+ self.aoa = aoa
893
+
894
+ # First compute k1 & k2 the first coordinate after 0.041 (up & down)
895
+ debut = True
896
+ for p in airfoil_spline.points:
897
+ if p.x > 0.041 and debut:
898
+ k1 = airfoil_spline.points.index(p)
899
+ debut = False
900
+ if p.x <= 0.041 and not debut:
901
+ k2 = airfoil_spline.points.index(p)-1
902
+ break
903
+
904
+ upper_spline_back, lower_spline_back = self.airfoil_spline.gen_skin_struct(
905
+ k1, k2)
906
+ self.le_upper_point = airfoil_spline.points[k1]
907
+ self.le_lower_point = airfoil_spline.points[k2]
908
+
909
+ # Create the new front spline (from the two front parts)
910
+ upper_points_front = airfoil_spline.points[:k1+1]
911
+ lower_points_front = airfoil_spline.points[k2:]
912
+ points_front = lower_points_front + upper_points_front
913
+ points_front_tag = [point.tag for point in points_front]
914
+ spline_front = gmsh.model.geo.addSpline(points_front_tag)
915
+ self.spline_front, self.upper_spline_back, self.lower_spline_back = spline_front, upper_spline_back, lower_spline_back
916
+
917
+ # Create points on the outside domain (& center point)
918
+ # p1 p2 p3
919
+ # -------------------------------------------
920
+ # / \ | |
921
+ # / \ | |
922
+ # / \ | | *1 : dx_wake
923
+ # / /00000000000000\ | | *2 : dy (total height)
924
+ # ( (0000000(p0)0000000)|-----------------| p4
925
+ # \ \00000000000000/ | *1 |*2
926
+ # \ / | |
927
+ # \ / | |
928
+ # \ / | |
929
+ # ------------------------------------------- p5
930
+ # p7 p6
931
+
932
+ # We want the line to p1 to be perpendicular to airfoil for better boundary layer, and same for p2
933
+ # We compute the normal to the line linking the points before and after our point of separation (point[k1]&point[k2])
934
+ xup, yup, xdown, ydown = airfoil_spline.points[k1].x, airfoil_spline.points[
935
+ k1].y, airfoil_spline.points[k2].x, airfoil_spline.points[k2].y
936
+ xupbefore, yupbefore, xupafter, yupafter = airfoil_spline.points[
937
+ k1-1].x, airfoil_spline.points[k1-1].y, airfoil_spline.points[k1+1].x, airfoil_spline.points[k1+1].y
938
+ xdownbefore, ydownbefore, xdownafter, ydownafter = airfoil_spline.points[
939
+ k2-1].x, airfoil_spline.points[k2-1].y, airfoil_spline.points[k2+1].x, airfoil_spline.points[k2+1].y
940
+ directionupx, directionupy, directiondownx, directiondowny = yupbefore - \
941
+ yupafter, xupafter-xupbefore, ydownafter-ydownbefore, xdownbefore-xdownafter
942
+ # As the points coordinates we get are not rotated, we need to change it by hand
943
+ cos, sin = math.cos(aoa), math.sin(aoa)
944
+ directionupx, directionupy, directiondownx, directiondowny = cos*directionupx-sin * directionupy, sin * \
945
+ directionupx+cos * directionupy, cos*directiondownx-sin * \
946
+ directiondowny, sin*directiondownx+cos * directiondowny
947
+ xup, yup, xdown, ydown = cos*xup-sin*yup, sin*xup + \
948
+ cos*yup, cos*xdown-sin*ydown, sin*xdown+cos*ydown
949
+
950
+ # Then compute where the line in this direction going from point[k1] intersect the line y=dy/2 (i.e. the horizontal line where we want L1)
951
+ pt1x, pt1y, pt7x, pt7y = xup+(dy/2-yup)/directionupy*directionupx, dy/2, xdown + \
952
+ (0-dy/2-ydown)/directiondowny*directiondownx, -dy/2
953
+ # Check that the line doesn't go "back" or "too far", and constrain it to go between le-0.05*dy and le-3.5
954
+ pt1x = max(min(pt1x, airfoil_spline.le.x-0.05*dy),
955
+ airfoil_spline.le.x-3.5)
956
+ pt7x = max(min(pt7x, airfoil_spline.le.x-0.05*dy),
957
+ airfoil_spline.le.x-3.5)
958
+ # Compute the center of the circle : we want a x coordinate of 0.5, and compute cy so that p1 and p7 are at same distance from the (0.5,cy)
959
+ centery = (pt1y+pt7y)/2 + (0.5-(pt1x+pt7x)/2)/(pt1y-pt7y)*(pt7x-pt1x)
960
+
961
+ # Create the 8 points we wanted
962
+ self.points = [
963
+ Point(0.5, centery, z, self.mesh_size_end), # 0
964
+ Point(pt1x, pt1y, z, self.mesh_size_end), # 1
965
+ Point(self.airfoil_spline.te.x, self.dy /
966
+ 2, z, self.mesh_size_end), # 2
967
+ Point(self.airfoil_spline.te.x + self.dx_trail,
968
+ self.dy / 2, z, self.mesh_size_end), # 3
969
+ Point(self.airfoil_spline.te.x + self.dx_trail,
970
+ self.airfoil_spline.te.y, z, self.mesh_size_end), # 4
971
+ Point(self.airfoil_spline.te.x + self.dx_trail, -
972
+ self.dy / 2, z, self.mesh_size_end), # 5
973
+ Point(self.airfoil_spline.te.x, -
974
+ self.dy / 2, z, self.mesh_size_end), # 6
975
+ Point(pt7x, pt7y, z, self.mesh_size_end), # 7
976
+ ]
977
+
978
+ # Create all the lines : outside and surface separation
979
+ self.lines = [
980
+ Line(self.le_upper_point, self.points[1]), # 0
981
+ Line(self.points[1], self.points[2]), # 1
982
+ Line(self.points[2], self.points[3]), # 2
983
+ Line(self.points[3], self.points[4]), # 3
984
+ Line(self.points[4], self.points[5]), # 4
985
+ Line(self.points[5], self.points[6]), # 5
986
+ Line(self.points[6], self.points[7]), # 6
987
+ Line(self.points[7], self.le_lower_point), # 7
988
+ Line(self.airfoil_spline.te, self.points[2]), # 8
989
+ Line(self.airfoil_spline.te, self.points[6]), # 9
990
+ Line(self.points[4], self.airfoil_spline.te), # 10
991
+ ]
992
+
993
+ # Circle arc for C shape at the front
994
+ self.circle_arc = gmsh.model.geo.addCircleArc(
995
+ self.points[7].tag, self.points[0].tag, self.points[1].tag)
996
+
997
+ # planar surfaces for structured grid are named from A-E
998
+ # straight lines are numbered from L0 to L10
999
+ #
1000
+ # --------------------------------------
1001
+ # / \ L1 | L2 |
1002
+ # circ / \L0 B | C |
1003
+ # / A \ L8| |L3 *1 : dx_wake
1004
+ # / /00000000000000\ | *1 | *2 : dy
1005
+ # ( (000000000000000000)|-------------|
1006
+ # \ \00000000000000/ | L10 |*2
1007
+ # \ / | |
1008
+ # \ /L7 E L9| D |L4
1009
+ # \ / | |
1010
+ # --------------------------------------
1011
+ # L6 L5
1012
+
1013
+ # Now we compute all of the parameters to have smooth mesh around mesh size
1014
+
1015
+ # HEIGHT
1016
+ # Compute number of nodes needed to have the desired first layer height (=nb of layer (N) +1)
1017
+ # Computation : we have that dy/2 is total height, and let a=first layer height
1018
+ # dy/2= a + a*ratio + a*ratio^2 + ... + a*ratio^(N-1) and rearrange to get the following equation
1019
+ nb_points_y = 3+int(math.log(1+dy/2/height*(ratio-1))/math.log(ratio))
1020
+ progression_y = ratio
1021
+ progression_y_inv = 1/ratio
1022
+
1023
+ # WAKE
1024
+ # Set a progression to adapt slightly the wake (don't need as much precision further away from airfoil)
1025
+ progression_wake = 1/1.025
1026
+ progression_wake_inv = 1.025
1027
+ # Set number of points in x direction at wake to get desired meshsize on the one next to airfoil
1028
+ # (solve dx_trail = meshsize + meshsize*1.02 + meshsize*1.02^2 + ... + meshsize*1.02^(N-1) with N=nb of intervals)
1029
+ nb_points_wake = int(
1030
+ math.log(1+dx_trail*0.025/mesh_size_end)/math.log(1.025))+1
1031
+
1032
+ # AIRFOIL CENTER
1033
+ # Set number of points on upper and lower part of airfoil. Want mesh size at the end (b) to be meshsizeend, and at the front (a) meshsizeend/coef to be more coherent with airfoilfront
1034
+ if mesh_size_end > 0.05:
1035
+ coeffdiv = 4
1036
+ elif mesh_size_end >= 0.03:
1037
+ coeffdiv = 3
1038
+ else:
1039
+ coeffdiv = 2
1040
+ a, b, l = mesh_size_end/coeffdiv, mesh_size_end, airfoil_spline.te.x
1041
+ # So compute ratio and nb of points accordingly: (solve l=a+a*r+a*r^2+a*r^(N-1) and a*r^(N-1)=b, and N=nb of intervals=nb of points-1)
1042
+ ratio_airfoil = (l-a)/(l-b)
1043
+ if l-b < 0:
1044
+ nb_airfoil = 3
1045
+ else:
1046
+ nb_airfoil = max(3, int(math.log(b/a)/math.log(ratio_airfoil))+2)
1047
+
1048
+ # AIRFOIL FRONT
1049
+ # Now we can try to put the good number of point on the front to have a good mesh
1050
+ # First we estimate the length of the spline
1051
+ x, y, v, w = airfoil_spline.points[k1].x, airfoil_spline.points[
1052
+ k2].y, airfoil_spline.points[k1].x, airfoil_spline.points[k2].y
1053
+ c1, c2 = airfoil_spline.le.x, airfoil_spline.le.y
1054
+ estim_length = (math.sqrt((x-c1)*(x-c1)+(y-c2)*(y-c2)) +
1055
+ math.sqrt((v-c1)*(v-c1)+(w-c2)*(w-c2)))+0.01
1056
+ # Compute nb of points if they were all same size, multiply par a factor (3) to have an okay number (and good when apply bump)
1057
+ nb_airfoil_front = max(
1058
+ 4, int(estim_length/mesh_size_end*coeffdiv*3))+4
1059
+
1060
+ # Now we set all the corresponding transfinite curve we need (with our coefficient computed before)
1061
+
1062
+ # transfinite curve A
1063
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1064
+ self.lines[7].tag, nb_points_y, "Progression", progression_y_inv) # same for plane E
1065
+ if mesh_size_end < 0.04:
1066
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1067
+ spline_front, nb_airfoil_front, "Bump", 12)
1068
+ else:
1069
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1070
+ spline_front, nb_airfoil_front, "Bump", 7)
1071
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1072
+ self.lines[0].tag, nb_points_y, "Progression", progression_y) # same for plane B
1073
+ # Because of different length of L1 and L6, need a bigger coefficient when point 1 and 7 are really far (coef is 1 when far and 9 when close)
1074
+ coef = 8/3*(pt1x+pt7x)/2+31/3
1075
+ if dy < 6:
1076
+ coef = (coef+2)/3
1077
+ if dy <= 3:
1078
+ coef = (coef + 2)/3
1079
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1080
+ self.circle_arc, nb_airfoil_front, "Bump", 1/coef)
1081
+
1082
+ # transfinite curve B
1083
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1084
+ self.lines[8].tag, nb_points_y, "Progression", progression_y) # same for plane C
1085
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1086
+ upper_spline_back.tag, nb_airfoil, "Progression", ratio_airfoil)
1087
+ # For L1, we adapt depeding if the curve is much longer than 1 or not (if goes "far in the front")
1088
+ if pt1x < airfoil_spline.le.x-1.5:
1089
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1090
+ self.lines[1].tag, nb_airfoil, "Progression", 1/ratio_airfoil)
1091
+ elif pt1x < airfoil_spline.le.x-0.7:
1092
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1093
+ self.lines[1].tag, nb_airfoil, "Progression", 1/math.sqrt(ratio_airfoil))
1094
+ else:
1095
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1096
+ self.lines[1].tag, nb_airfoil)
1097
+
1098
+ # transfinite curve C
1099
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1100
+ self.lines[2].tag, nb_points_wake, "Progression", progression_wake_inv)
1101
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1102
+ self.lines[3].tag, nb_points_y, "Progression", progression_y_inv)
1103
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1104
+ self.lines[10].tag, nb_points_wake, "Progression", progression_wake) # same for plane D
1105
+
1106
+ # transfinite curve D
1107
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1108
+ self.lines[9].tag, nb_points_y, "Progression", progression_y) # same for plane E
1109
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1110
+ self.lines[4].tag, nb_points_y, "Progression", progression_y)
1111
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1112
+ self.lines[5].tag, nb_points_wake, "Progression", progression_wake)
1113
+
1114
+ # transfinite curve E
1115
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1116
+ lower_spline_back.tag, nb_airfoil, "Progression", 1/ratio_airfoil)
1117
+ # For L6, we adapt depeding if the line is much longer than 1 or not (if goes "far in the front")
1118
+ if pt7x < airfoil_spline.le.x-1.5:
1119
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1120
+ self.lines[6].tag, nb_airfoil, "Progression", ratio_airfoil)
1121
+ elif pt7x < airfoil_spline.le.x-0.4:
1122
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1123
+ self.lines[6].tag, nb_airfoil, "Progression", math.sqrt(ratio_airfoil))
1124
+ else:
1125
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1126
+ self.lines[6].tag, nb_airfoil)
1127
+
1128
+ # Now we add the surfaces
1129
+
1130
+ # transfinite surface A (forces structured mesh)
1131
+ c1 = gmsh.model.geo.addCurveLoop(
1132
+ [self.lines[7].tag, spline_front, self.lines[0].tag, - self.circle_arc])
1133
+ surf1 = gmsh.model.geo.addPlaneSurface([c1])
1134
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf1)
1135
+
1136
+ # transfinite surface B
1137
+ c2 = gmsh.model.geo.addCurveLoop(
1138
+ [self.lines[0].tag, self.lines[1].tag, - self.lines[8].tag, - upper_spline_back.tag])
1139
+ surf2 = gmsh.model.geo.addPlaneSurface([c2])
1140
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf2)
1141
+
1142
+ # transfinite surface C
1143
+ c3 = gmsh.model.geo.addCurveLoop(
1144
+ [self.lines[8].tag, self.lines[2].tag, self.lines[3].tag, self.lines[10].tag])
1145
+ surf3 = gmsh.model.geo.addPlaneSurface([c3])
1146
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf3)
1147
+
1148
+ # transfinite surface D
1149
+ c4 = gmsh.model.geo.addCurveLoop(
1150
+ [- self.lines[9].tag, - self.lines[10].tag, self.lines[4].tag, self.lines[5].tag])
1151
+ surf4 = gmsh.model.geo.addPlaneSurface([c4])
1152
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf4)
1153
+
1154
+ # transfinite surface E
1155
+ c5 = gmsh.model.geo.addCurveLoop(
1156
+ [self.lines[7].tag, - lower_spline_back.tag, self.lines[9].tag, self.lines[6].tag])
1157
+ surf5 = gmsh.model.geo.addPlaneSurface([c5])
1158
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf5)
1159
+ self.curveloops = [c1, c2, c3, c4, c5]
1160
+ self.surfaces = [surf1, surf2, surf3, surf4, surf5]
1161
+
1162
+ # Lastly, recombine surface to create quadrilateral elements
1163
+ gmsh.model.geo.mesh.setRecombine(2, surf1, 90)
1164
+ gmsh.model.geo.mesh.setRecombine(2, surf2, 90)
1165
+ gmsh.model.geo.mesh.setRecombine(2, surf3, 90)
1166
+ gmsh.model.geo.mesh.setRecombine(2, surf4, 90)
1167
+ gmsh.model.geo.mesh.setRecombine(2, surf5, 90)
1168
+
1169
+ def define_bc(self):
1170
+ """
1171
+ Method that define the domain marker of the surface, the airfoil and the farfield
1172
+ -------
1173
+ """
1174
+
1175
+ # Airfoil
1176
+ self.bc = gmsh.model.addPhysicalGroup(
1177
+ 1, [self.upper_spline_back.tag,
1178
+ self.lower_spline_back.tag, self.spline_front]
1179
+ )
1180
+ gmsh.model.setPhysicalName(1, self.bc, "airfoil")
1181
+
1182
+ # Farfield
1183
+ self.bc = gmsh.model.addPhysicalGroup(1, [self.lines[1].tag, self.lines[2].tag,
1184
+ self.lines[3].tag, self.lines[4].tag, self.lines[5].tag, self.lines[6].tag, self.circle_arc])
1185
+ gmsh.model.setPhysicalName(1, self.bc, "farfield")
1186
+
1187
+ # Surface
1188
+ self.bc = gmsh.model.addPhysicalGroup(2, self.surfaces)
1189
+ gmsh.model.setPhysicalName(2, self.bc, "fluid")