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,152 +1,118 @@
1
1
  """
2
- This script contain the definition of geometrical objects needed to build the geometry.
2
+ This module contains the definition of geometrical objects needed to build the geometry.
3
3
  """
4
4
 
5
+ import math
6
+ import sys
5
7
  from operator import attrgetter
8
+
6
9
  import gmsh
7
10
  import numpy as np
8
- import math
9
- import sys
10
11
 
11
12
 
12
13
  class Point:
13
- """
14
- A class to represent the point geometrical object of gmsh
15
-
16
- ...
14
+ """A class to represent a point geometrical object in gmsh.
17
15
 
18
16
  Attributes
19
17
  ----------
20
18
  x : float
21
- position in x
19
+ Position in x
22
20
  y : float
23
- position in y
21
+ Position in y
24
22
  z : float
25
- position in z
23
+ Position in z
26
24
  mesh_size : float
27
- If mesh_size is > 0, add a meshing constraint
28
- at that point
25
+ Meshing constraint size at this point (if > 0)
29
26
  """
30
27
 
31
28
  def __init__(self, x, y, z, mesh_size):
32
-
33
29
  self.x = x
34
30
  self.y = y
35
31
  self.z = z
36
32
  self.mesh_size = mesh_size
37
33
  self.dim = 0
38
-
39
34
  # create the gmsh object and store the tag of the geometric object
40
- self.tag = gmsh.model.geo.addPoint(
41
- self.x, self.y, self.z, self.mesh_size)
35
+ self.tag = gmsh.model.geo.addPoint(self.x, self.y, self.z, self.mesh_size)
42
36
 
43
37
  def rotation(self, angle, origin, axis):
44
- """
45
- Method to rotate the object Point
46
- ...
38
+ """Rotate the point around an axis.
39
+
47
40
  Parameters
48
41
  ----------
49
42
  angle : float
50
- angle of rotation in rad
43
+ Angle of rotation in radians
51
44
  origin : tuple
52
- tuple of point (x,y,z) which is the origin of the rotation
45
+ Tuple of (x, y, z) defining the rotation origin
53
46
  axis : tuple
54
- tuple of point (x,y,z) which represent the axis of rotation
47
+ Tuple of (x, y, z) defining the rotation axis
55
48
  """
56
- gmsh.model.geo.rotate(
57
- [(self.dim, self.tag)],
58
- *origin,
59
- *axis,
60
- angle,
61
- )
49
+ gmsh.model.geo.rotate([(self.dim, self.tag)], *origin, *axis, angle)
62
50
 
63
51
  def translation(self, vector):
64
- """
65
- Method to translate the object Point
66
-
67
- ...
68
-
52
+ """Translate the point.
53
+
69
54
  Parameters
70
55
  ----------
71
- direction : tuple
72
- tuple of point (x,y,z) which represent the direction of the translation
56
+ vector : tuple
57
+ Tuple of (x, y, z) defining the translation vector
73
58
  """
74
59
  gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
75
60
 
76
61
 
77
62
  class Line:
78
- """
79
- A class to represent the Line geometrical object of gmsh
80
-
81
- ...
63
+ """A class to represent a line geometrical object in gmsh.
82
64
 
83
65
  Attributes
84
66
  ----------
85
67
  start_point : Point
86
- first point of the line
68
+ First point of the line
87
69
  end_point : Point
88
- second point of the line
70
+ Second point of the line
89
71
  """
90
72
 
91
73
  def __init__(self, start_point, end_point):
92
74
  self.start_point = start_point
93
75
  self.end_point = end_point
94
-
95
76
  self.dim = 1
96
-
97
77
  # create the gmsh object and store the tag of the geometric object
98
- self.tag = gmsh.model.geo.addLine(
99
- self.start_point.tag, self.end_point.tag)
78
+ self.tag = gmsh.model.geo.addLine(self.start_point.tag, self.end_point.tag)
100
79
 
101
80
  def rotation(self, angle, origin, axis):
102
- """
103
- Method to rotate the object Line
104
- ...
105
-
81
+ """Rotate the line around an axis.
82
+
106
83
  Parameters
107
84
  ----------
108
85
  angle : float
109
- angle of rotation in rad
86
+ Angle of rotation in radians
110
87
  origin : tuple
111
- tuple of point (x,y,z) which is the origin of the rotation
88
+ Tuple of (x, y, z) defining the rotation origin
112
89
  axis : tuple
113
- tuple of point (x,y,z) which represent the axis of rotation
90
+ Tuple of (x, y, z) defining the rotation axis
114
91
  """
115
- gmsh.model.geo.rotate(
116
- [(self.dim, self.tag)],
117
- *origin,
118
- *axis,
119
- angle,
120
- )
92
+ gmsh.model.geo.rotate([(self.dim, self.tag)], *origin, *axis, angle)
121
93
 
122
94
  def translation(self, vector):
123
- """
124
- Method to translate the object Line
125
- ...
126
-
95
+ """Translate the line.
96
+
127
97
  Parameters
128
98
  ----------
129
- direction : tuple
130
- tuple of point (x,y,z) which represent the direction of the translation
99
+ vector : tuple
100
+ Tuple of (x, y, z) defining the translation vector
131
101
  """
132
102
  gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
133
103
 
134
104
 
135
105
  class Spline:
136
- """
137
- A class to represent the Spine geometrical object of gmsh
138
-
139
- ...
106
+ """A class to represent a Spline geometrical object in gmsh.
140
107
 
141
108
  Attributes
142
109
  ----------
143
- points_list : list(Point)
144
- list of Point object forming the Spline
110
+ point_list : list of Point
111
+ List of Point objects forming the Spline
145
112
  """
146
113
 
147
114
  def __init__(self, point_list):
148
115
  self.point_list = point_list
149
-
150
116
  # generate the Lines tag list to follow
151
117
  self.tag_list = [point.tag for point in self.point_list]
152
118
  self.dim = 1
@@ -154,48 +120,36 @@ class Spline:
154
120
  self.tag = gmsh.model.geo.addSpline(self.tag_list)
155
121
 
156
122
  def rotation(self, angle, origin, axis):
157
- """
158
- Method to rotate the object Spline
159
-
160
- Rotate the spline itself (curve, startpoint, endpoint), then rotate the intermediate points
161
- ...
162
-
123
+ """Rotate the spline around an axis.
124
+
125
+ Rotates the spline curve and all intermediate points.
126
+
163
127
  Parameters
164
128
  ----------
165
129
  angle : float
166
- angle of rotation in rad
130
+ Angle of rotation in radians
167
131
  origin : tuple
168
- tuple of point (x,y,z) which is the origin of the rotation
132
+ Tuple of (x, y, z) defining the rotation origin
169
133
  axis : tuple
170
- tuple of point (x,y,z) which represent the axis of rotation
134
+ Tuple of (x, y, z) defining the rotation axis
171
135
  """
172
- gmsh.model.geo.rotate(
173
- [(self.dim, self.tag)],
174
- *origin,
175
- *axis,
176
- angle,
177
- )
178
-
179
- [
136
+ gmsh.model.geo.rotate([(self.dim, self.tag)], *origin, *axis, angle)
137
+ for interm_point in self.point_list[1:-1]:
180
138
  interm_point.rotation(angle, origin, axis)
181
- for interm_point in self.point_list[1:-1]
182
- ]
183
139
 
184
140
  def translation(self, vector):
185
- """
186
- Method to translate the object Line
187
-
188
- Translate the spline itself (curve, startpoint,endpoint), then translate the indermediate points
189
- ...
190
-
141
+ """Translate the spline.
142
+
143
+ Translates the spline curve and all intermediate points.
144
+
191
145
  Parameters
192
146
  ----------
193
- direction : tuple
194
- tuple of point (x,y,z) which represent the direction of the translation
147
+ vector : tuple
148
+ Tuple of (x, y, z) defining the translation vector
195
149
  """
196
150
  gmsh.model.geo.translate([(self.dim, self.tag)], *vector)
197
- [interm_point.translation(vector)
198
- for interm_point in self.point_list[1:-1]]
151
+ for interm_point in self.point_list[1:-1]:
152
+ interm_point.translation(vector)
199
153
 
200
154
 
201
155
  class CurveLoop:
@@ -606,11 +560,12 @@ class AirfoilSpline:
606
560
  boundary condition
607
561
  """
608
562
 
609
- def __init__(self, point_cloud, mesh_size, name="airfoil"):
563
+ def __init__(self, point_cloud, mesh_size, name, is_flap=False):
610
564
 
611
565
  self.name = name
612
566
  self.dim = 1
613
567
  self.mesh_size = mesh_size
568
+ self.is_flap = is_flap
614
569
 
615
570
  # Generate Points object from the point_cloud
616
571
  self.points = [
@@ -630,11 +585,17 @@ class AirfoilSpline:
630
585
  vertical = False
631
586
  # If two (in the end) are so close in coordinate x, they are vertical and not just neighbouring point (examples below)
632
587
  # 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:
588
+ # Tollerance is the distance between the two points, might be necessary to change it
589
+ if is_flap:
590
+ tollerance = 0.001
591
+ else:
592
+ tollerance = 0.0001
593
+
594
+ if self.points[self.te_indx-1].x > self.te.x-tollerance:
634
595
  te_up_indx = self.te_indx-1
635
596
  te_down_indx = self.te_indx
636
597
  vertical = True
637
- elif self.points[self.te_indx+1].x > self.te.x-0.0001:
598
+ elif self.points[self.te_indx+1].x > self.te.x-tollerance:
638
599
  te_up_indx = self.te_indx
639
600
  te_down_indx = self.te_indx+1
640
601
  vertical = True
@@ -694,37 +655,66 @@ class AirfoilSpline:
694
655
 
695
656
  def gen_skin(self):
696
657
  """
697
- Method to generate the three splines forming the foil, Only call this function when the points
698
- of the airfoil are in their final position
658
+ Method to generate the three splines forming the foil.
659
+ Only call this function when the points of the airfoil are in their final position.
660
+
661
+ For airfoils: discretizes into upper, lower, and front splines based on x=0.05 threshold.
662
+ For flaps: discretizes into upper, lower, and front splines based on proximity to leading edge.
699
663
  -------
700
664
  """
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
665
 
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
- )
666
+ if getattr(self, "is_flap", False):
667
+ # For flap: find the leading edge and trailing edge
668
+ le_index = min(enumerate(self.points), key=lambda x: x[1].x)[0]
669
+ te_index = max(enumerate(self.points), key=lambda x: x[1].x)[0]
670
+
671
+ n = len(self.points)
672
+ # Define front region width (approximately 10% of total points, at least 3)
673
+ front_width = max(3, n // 10)
674
+ k1 = (le_index - front_width // 2) % n
675
+ k2 = (le_index + front_width // 2) % n
676
+
677
+ # For a flap, points typically go: TE -> lower surface -> LE -> upper surface -> TE
678
+ # Create continuous splines that form a closed loop:
679
+ # lower: TE to LE, front: LE region, upper: LE to TE
680
+
681
+ if te_index < le_index:
682
+ # Normal case: TE is before LE
683
+ # Lower: from TE to k1 (before LE front region)
684
+ # Front: from k1 to k2 around LE
685
+ # Upper: from k2 (after LE front region) to TE
686
+ self.lower_spline = Spline(self.points[te_index:k1+1])
687
+ self.front_spline = Spline(self.points[k1:k2+1])
688
+ self.upper_spline = Spline(self.points[k2:] + self.points[:te_index+1])
689
+ else:
690
+ # TE is after LE (wraps around)
691
+ # Lower: from TE to k1 (wrapping)
692
+ # Front: from k1 to k2 around LE
693
+ # Upper: from k2 to TE
694
+ self.lower_spline = Spline(self.points[te_index:] + self.points[:k1+1])
695
+ self.front_spline = Spline(self.points[k1:k2+1])
696
+ self.upper_spline = Spline(self.points[k2:te_index+1])
719
697
 
720
- # Create a spline for the front part of the airfoil
721
- self.front_spline = Spline(self.points[k2:]+self.points[:k1+1])
698
+ else:
699
+ # For regular airfoils: find points at x > 0.05
700
+ debut = True
701
+ for p in self.points:
702
+ if p.x > 0.049 and debut:
703
+ k1 = self.points.index(p)
704
+ debut = False
705
+ if p.x <= 0.049 and not debut:
706
+ k2 = self.points.index(p)-1
707
+ break
708
+
709
+ self.upper_spline = Spline(self.points[k1: self.te_indx + 1])
710
+ self.lower_spline = Spline(self.points[self.te_indx:k2+1])
711
+ self.front_spline = Spline(self.points[k2:] + self.points[:k1 + 1])
722
712
 
723
713
  return k1, k2
724
714
 
725
715
  def gen_skin_struct(self, k1, k2):
726
716
  """
727
- Method to generate the two splines forming the foil for structural mesh, Only call this function when the points
717
+ Method to generate the two splines forming the foil for structured mesh, Only call this function when the points
728
718
  of the airfoil are in their final position
729
719
  -------
730
720
  """
@@ -871,10 +861,36 @@ def outofbounds(airfoil, box, radius, blthick):
871
861
 
872
862
  class CType:
873
863
  """
874
- A class to represent a C-type structured mesh.
864
+ A class to represent a C-type mesh domain with optional structured meshing.
865
+ Can be used for both fully structured meshes and as a structured farfield
866
+ for hybrid (unstructured) meshes.
875
867
  """
876
868
 
877
- def __init__(self, airfoil_spline, dx_trail, dy, mesh_size, height, ratio, aoa):
869
+ def __init__(self, airfoil_spline, dx_trail, dy, mesh_size, height=None,
870
+ ratio=None, aoa=0, structured=True):
871
+ """
872
+ Initialize a C-type mesh domain.
873
+
874
+ Parameters
875
+ ----------
876
+ airfoil_spline : AirfoilSpline
877
+ The airfoil spline object
878
+ dx_trail : float
879
+ Length of trailing domain extension [m]
880
+ dy : float
881
+ Total height of the domain [m]
882
+ mesh_size : float
883
+ Mesh size for the domain
884
+ height : float, optional
885
+ Height of first boundary layer (for structured mesh only)
886
+ ratio : float, optional
887
+ Growth ratio of boundary layer (for structured mesh only)
888
+ aoa : float, optional
889
+ Angle of attack in radians (default 0)
890
+ structured : bool, optional
891
+ If True, create transfinite curves and surfaces for structured mesh
892
+ (default True)
893
+ """
878
894
  z = 0
879
895
  self.airfoil_spline = airfoil_spline
880
896
 
@@ -890,6 +906,7 @@ class CType:
890
906
  self.firstheight = height
891
907
  self.ratio = ratio
892
908
  self.aoa = aoa
909
+ self.structured = structured
893
910
 
894
911
  # First compute k1 & k2 the first coordinate after 0.041 (up & down)
895
912
  debut = True
@@ -901,10 +918,19 @@ class CType:
901
918
  k2 = airfoil_spline.points.index(p)-1
902
919
  break
903
920
 
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]
921
+ # Only call gen_skin_struct if creating structured mesh
922
+ # For unstructured, the airfoil already has proper splines from gen_skin()
923
+ if self.structured:
924
+ upper_spline_back, lower_spline_back = self.airfoil_spline.gen_skin_struct(
925
+ k1, k2)
926
+ self.le_upper_point = airfoil_spline.points[k1]
927
+ self.le_lower_point = airfoil_spline.points[k2]
928
+ else:
929
+ # For unstructured mesh, use the regular splines already generated
930
+ self.le_upper_point = airfoil_spline.points[k1]
931
+ self.le_lower_point = airfoil_spline.points[k2]
932
+ upper_spline_back = airfoil_spline.upper_spline
933
+ lower_spline_back = airfoil_spline.lower_spline
908
934
 
909
935
  # Create the new front spline (from the two front parts)
910
936
  upper_points_front = airfoil_spline.points[:k1+1]
@@ -1010,161 +1036,189 @@ class CType:
1010
1036
  # --------------------------------------
1011
1037
  # L6 L5
1012
1038
 
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:
1039
+ # Store flag and parameters for later use
1040
+ self.structured = structured
1041
+ self.k1 = k1
1042
+ self.k2 = k2
1043
+
1044
+ if self.structured:
1045
+ # Only create structured mesh if requested
1046
+ # Now we compute all of the parameters to have smooth mesh around mesh size
1047
+
1048
+ # HEIGHT
1049
+ # Compute number of nodes needed to have the desired first layer height (=nb of layer (N) +1)
1050
+ # Computation : we have that dy/2 is total height, and let a=first layer height
1051
+ # dy/2= a + a*ratio + a*ratio^2 + ... + a*ratio^(N-1) and rearrange to get the following equation
1052
+ nb_points_y = 3+int(math.log(1+dy/2/height*(ratio-1))/math.log(ratio))
1053
+ progression_y = ratio
1054
+ progression_y_inv = 1/ratio
1055
+
1056
+ # WAKE
1057
+ # Set a progression to adapt slightly the wake (don't need as much precision further away from airfoil)
1058
+ progression_wake = 1/1.025
1059
+ progression_wake_inv = 1.025
1060
+ # Set number of points in x direction at wake to get desired meshsize on the one next to airfoil
1061
+ # (solve dx_trail = meshsize + meshsize*1.02 + meshsize*1.02^2 + ... + meshsize*1.02^(N-1) with N=nb of intervals)
1062
+ nb_points_wake = int(
1063
+ math.log(1+dx_trail*0.025/mesh_size_end)/math.log(1.025))+1
1064
+
1065
+ # AIRFOIL CENTER
1066
+ # 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
1067
+ if mesh_size_end > 0.05:
1068
+ coeffdiv = 4
1069
+ elif mesh_size_end >= 0.03:
1070
+ coeffdiv = 3
1071
+ else:
1072
+ coeffdiv = 2
1073
+ a, b, l = mesh_size_end/coeffdiv, mesh_size_end, airfoil_spline.te.x
1074
+ # 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)
1075
+ ratio_airfoil = (l-a)/(l-b)
1076
+ if l-b < 0:
1077
+ nb_airfoil = 3
1078
+ else:
1079
+ nb_airfoil = max(3, int(math.log(b/a)/math.log(ratio_airfoil))+2)
1080
+
1081
+ # AIRFOIL FRONT
1082
+ # Now we can try to put the good number of point on the front to have a good mesh
1083
+ # First we estimate the length of the spline
1084
+ x, y, v, w = airfoil_spline.points[k1].x, airfoil_spline.points[
1085
+ k2].y, airfoil_spline.points[k1].x, airfoil_spline.points[k2].y
1086
+ c1, c2 = airfoil_spline.le.x, airfoil_spline.le.y
1087
+ estim_length = (math.sqrt((x-c1)*(x-c1)+(y-c2)*(y-c2)) +
1088
+ math.sqrt((v-c1)*(v-c1)+(w-c2)*(w-c2)))+0.01
1089
+ # 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)
1090
+ nb_airfoil_front = max(
1091
+ 4, int(estim_length/mesh_size_end*coeffdiv*3))+4
1092
+
1093
+ # Now we set all the corresponding transfinite curve we need (with our coefficient computed before)
1094
+
1095
+ # transfinite curve A
1066
1096
  gmsh.model.geo.mesh.setTransfiniteCurve(
1067
- spline_front, nb_airfoil_front, "Bump", 12)
1068
- else:
1097
+ self.lines[7].tag, nb_points_y, "Progression", progression_y_inv) # same for plane E
1098
+ if mesh_size_end < 0.04:
1099
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1100
+ spline_front, nb_airfoil_front, "Bump", 12)
1101
+ else:
1102
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1103
+ spline_front, nb_airfoil_front, "Bump", 7)
1104
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1105
+ self.lines[0].tag, nb_points_y, "Progression", progression_y) # same for plane B
1106
+ # 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)
1107
+ coef = 8/3*(pt1x+pt7x)/2+31/3
1108
+ if dy < 6:
1109
+ coef = (coef+2)/3
1110
+ if dy <= 3:
1111
+ coef = (coef + 2)/3
1069
1112
  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)
1113
+ self.circle_arc, nb_airfoil_front, "Bump", 1/coef)
1081
1114
 
1082
1115
  # 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
1116
  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:
1117
+ self.lines[8].tag, nb_points_y, "Progression", progression_y) # same for plane C
1092
1118
  gmsh.model.geo.mesh.setTransfiniteCurve(
1093
- self.lines[1].tag, nb_airfoil, "Progression", 1/math.sqrt(ratio_airfoil))
1094
- else:
1119
+ upper_spline_back.tag, nb_airfoil, "Progression", ratio_airfoil)
1120
+ # For L1, we adapt depeding if the curve is much longer than 1 or not (if goes "far in the front")
1121
+ if pt1x < airfoil_spline.le.x-1.5:
1122
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1123
+ self.lines[1].tag, nb_airfoil, "Progression", 1/ratio_airfoil)
1124
+ elif pt1x < airfoil_spline.le.x-0.7:
1125
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1126
+ self.lines[1].tag, nb_airfoil, "Progression", 1/math.sqrt(ratio_airfoil))
1127
+ else:
1128
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1129
+ self.lines[1].tag, nb_airfoil)
1130
+
1131
+ # transfinite curve C
1095
1132
  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:
1133
+ self.lines[2].tag, nb_points_wake, "Progression", progression_wake_inv)
1119
1134
  gmsh.model.geo.mesh.setTransfiniteCurve(
1120
- self.lines[6].tag, nb_airfoil, "Progression", ratio_airfoil)
1121
- elif pt7x < airfoil_spline.le.x-0.4:
1135
+ self.lines[3].tag, nb_points_y, "Progression", progression_y_inv)
1122
1136
  gmsh.model.geo.mesh.setTransfiniteCurve(
1123
- self.lines[6].tag, nb_airfoil, "Progression", math.sqrt(ratio_airfoil))
1124
- else:
1137
+ self.lines[10].tag, nb_points_wake, "Progression", progression_wake) # same for plane D
1138
+
1139
+ # transfinite curve D
1125
1140
  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)
1141
+ self.lines[9].tag, nb_points_y, "Progression", progression_y) # same for plane E
1142
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1143
+ self.lines[4].tag, nb_points_y, "Progression", progression_y)
1144
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1145
+ self.lines[5].tag, nb_points_wake, "Progression", progression_wake)
1146
+
1147
+ # transfinite curve E
1148
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1149
+ lower_spline_back.tag, nb_airfoil, "Progression", 1/ratio_airfoil)
1150
+ # For L6, we adapt depeding if the line is much longer than 1 or not (if goes "far in the front")
1151
+ if pt7x < airfoil_spline.le.x-1.5:
1152
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1153
+ self.lines[6].tag, nb_airfoil, "Progression", ratio_airfoil)
1154
+ elif pt7x < airfoil_spline.le.x-0.4:
1155
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1156
+ self.lines[6].tag, nb_airfoil, "Progression", math.sqrt(ratio_airfoil))
1157
+ else:
1158
+ gmsh.model.geo.mesh.setTransfiniteCurve(
1159
+ self.lines[6].tag, nb_airfoil)
1160
+
1161
+ # Now we add the surfaces
1162
+
1163
+ # transfinite surface A (forces structured mesh)
1164
+ c1 = gmsh.model.geo.addCurveLoop(
1165
+ [self.lines[7].tag, spline_front, self.lines[0].tag, - self.circle_arc])
1166
+ surf1 = gmsh.model.geo.addPlaneSurface([c1])
1167
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf1)
1168
+
1169
+ # transfinite surface B
1170
+ c2 = gmsh.model.geo.addCurveLoop(
1171
+ [self.lines[0].tag, self.lines[1].tag, - self.lines[8].tag, - upper_spline_back.tag])
1172
+ surf2 = gmsh.model.geo.addPlaneSurface([c2])
1173
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf2)
1174
+
1175
+ # transfinite surface C
1176
+ c3 = gmsh.model.geo.addCurveLoop(
1177
+ [self.lines[8].tag, self.lines[2].tag, self.lines[3].tag, self.lines[10].tag])
1178
+ surf3 = gmsh.model.geo.addPlaneSurface([c3])
1179
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf3)
1180
+
1181
+ # transfinite surface D
1182
+ c4 = gmsh.model.geo.addCurveLoop(
1183
+ [- self.lines[9].tag, - self.lines[10].tag, self.lines[4].tag, self.lines[5].tag])
1184
+ surf4 = gmsh.model.geo.addPlaneSurface([c4])
1185
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf4)
1186
+
1187
+ # transfinite surface E
1188
+ c5 = gmsh.model.geo.addCurveLoop(
1189
+ [self.lines[7].tag, - lower_spline_back.tag, self.lines[9].tag, self.lines[6].tag])
1190
+ surf5 = gmsh.model.geo.addPlaneSurface([c5])
1191
+ gmsh.model.geo.mesh.setTransfiniteSurface(surf5)
1192
+ self.curveloops = [c1, c2, c3, c4, c5]
1193
+ self.surfaces = [surf1, surf2, surf3, surf4, surf5]
1194
+
1195
+ # Lastly, recombine surface to create quadrilateral elements
1196
+ gmsh.model.geo.mesh.setRecombine(2, surf1, 90)
1197
+ gmsh.model.geo.mesh.setRecombine(2, surf2, 90)
1198
+ gmsh.model.geo.mesh.setRecombine(2, surf3, 90)
1199
+ gmsh.model.geo.mesh.setRecombine(2, surf4, 90)
1200
+ gmsh.model.geo.mesh.setRecombine(2, surf5, 90)
1201
+ else:
1202
+ # For non-structured (hybrid) mesh, create C-type farfield boundary
1203
+ # Only create the outer curve loop - PlaneSurface will use it as a hole
1204
+ # Outer loop: circle_arc (p7→p1) + lines[1:7] (p1→p2→p3→p4→p5→p6→p7)
1205
+ curve_loop = gmsh.model.geo.addCurveLoop(
1206
+ [self.circle_arc] +
1207
+ [self.lines[i].tag for i in range(1, 7)]
1208
+ )
1209
+ self.surfaces = []
1210
+ self.curveloops = [curve_loop]
1211
+
1212
+ def close_loop(self):
1213
+ """
1214
+ Return the outer boundary curve loop for the farfield domain.
1215
+
1216
+ Returns
1217
+ -------
1218
+ tag : int
1219
+ Tag of the first (outer) curve loop
1220
+ """
1221
+ return self.curveloops[0]
1168
1222
 
1169
1223
  def define_bc(self):
1170
1224
  """
@@ -1186,4 +1240,4 @@ class CType:
1186
1240
 
1187
1241
  # Surface
1188
1242
  self.bc = gmsh.model.addPhysicalGroup(2, self.surfaces)
1189
- gmsh.model.setPhysicalName(2, self.bc, "fluid")
1243
+ gmsh.model.setPhysicalName(2, self.bc, "fluid")