Mesh Source Code


mesh.mesh.Mesh

Bases: ABC

This class implements an abstract meshing class.

Source code in aero_optim/mesh/mesh.py
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
class Mesh(ABC):
    """
    This class implements an abstract meshing class.
    """
    def __init__(self, config: dict, datfile: str = ""):
        """
        Instantiates the abstract Mesh object.

        **Input**

        - config (dict): the config file dictionary.
        - dat_file (str): path to input_geometry.dat.

        **Inner**

        - outdir (str): path/to/outputdirectory
        - outfile (str): the core name of all outputed files e.g. outfile.log, outfile.mesh, etc.
        - scale (float): geometry scaling factor.
        - header (int): the number of header lines in dat_file.
        - mesh_format (str): the mesh format (mesh or cgns).
        - bl (bool): whether to mesh the boundary layer (True) or not (False).
        - bl_thickness (float): the BL meshing cumulated thickness.
        - bl_ratio (float): the BL meshing growth ratio.
        - bl_size (float): the BL first element size.
        - bl_fan_elements (int): the number of BL fan elements.
        - mesh_order (int): the order of the mesh.
        - structured (bool): whether to recombine triangles (True) or not (False).
        - extrusion_layers (int): the number of extrusion layers when generating a 3D mesh.
        - extrusion_size (float): the total size of the extruded layers.
        - GUI (bool): whether to launch gmsh GUI (True) or not (False).
        - nview (int): the number of sub-windows in gmsh GUI.
        - quality (bool): whether to display quality metrics in gmsh GUI (True) or not (False).
        - pts (list[list[float]]): the geometry coordinates.
        - surf_tag (list[int]): flow-field elements tags used to recombine the mesh if structured.
        - non_corner_tags (list[int]): non-corner physical entity tags used to define 'Corners'.
        - lower_tag (list[int]): lower periodic tags to be identified as one.
        - lower_tag (list[int]): upper periodic tags to be identified as one.
        """
        self.config = config
        self.process_config()
        # study params
        self.dat_file: str = datfile if datfile else config["study"]["file"]
        self.outdir: str = config["study"]["outdir"]
        self.outfile = self.config["study"].get("outfile", self.dat_file.split("/")[-1][:-4])
        self.scale: int = config["study"].get("scale", 1)
        self.header: int = config["study"].get("header", 2)
        # mesh params (format & boundary layer)
        self.mesh_format: str = config["gmsh"]["mesh"].get("mesh_format", "mesh").lower()
        self.bl: bool = config["gmsh"]["mesh"].get("bl", False)
        self.bl_thickness: float = config["gmsh"]["mesh"].get("bl_thickness", 1e-3)
        self.bl_ratio: float = config["gmsh"]["mesh"].get("bl_ratio", 1.1)
        self.bl_size: float = config["gmsh"]["mesh"].get("bl_size", 1e-5)
        self.bl_fan_elements: int = config["gmsh"]["mesh"].get("bl_fan_elements", 10)
        self.mesh_order: int = config["gmsh"]["mesh"].get("order", 0)
        # mesh params (3d extrusion)
        self.structured: bool = config["gmsh"]["mesh"].get("structured", False)
        self.extrusion_layers: int = config["gmsh"]["mesh"].get("extrusion_layers", 0)
        self.extrusion_size: int = config["gmsh"]["mesh"].get("extrusion_size", 0.001)
        # gui options
        self.GUI: bool = config["gmsh"]["view"].get("GUI", True)
        self.nview: int = config["gmsh"]["view"].get("nview", 1)
        self.quality: bool = config["gmsh"]["view"].get("quality", False)
        # geometry coordinates loading
        self.pts: list[list[float]] = from_dat(self.dat_file, self.header, self.scale)
        # flow-field and non-corner tags (for recombination and corners definition)
        self.surf_tag: list[int] = []
        self.non_corner_tags: list[int] = []
        self.bottom_tags: list[int] = []
        self.top_tags: list[int] = []

    def get_nlayer(self) -> int:
        """
        **Returns** the number of layers required to reach bl_thickness given the growth bl_ratio
        and the first element size bl_size.
        """
        return math.ceil(
            math.log(1 - self.bl_thickness * (1 - self.bl_ratio) / self.bl_size)
            / math.log(self.bl_ratio) - 1
        )

    def build_mesh(self):
        """
        **Defines** the gmsh routine.
        """
        gmsh.initialize()
        gmsh.option.setNumber('General.Terminal', 0)
        gmsh.logger.start()
        gmsh.model.add("model")

        self.build_2dmesh()
        self.build_3dmesh() if self.extrusion_layers > 0 else 0

        if self.structured:
            [gmsh.model.geo.mesh.setRecombine(2, abs(id)) for id in self.surf_tag]
        gmsh.model.geo.synchronize()
        gmsh.model.mesh.generate(3) if self.extrusion_layers > 0 else gmsh.model.mesh.generate(2)
        if self.mesh_order:
            gmsh.model.mesh.setOrder(self.mesh_order)

        # visualization
        if self.quality:
            plot_quality()
        elt_type = "Mesh.Triangles" if not self.structured else "Mesh.Quadrangles"
        color = [
            ("General.BackgroundGradient", 255, 255, 255),
            (elt_type, 255, 0, 0)
        ]
        number = [
            ("Geometry.Points", 0),
            ("Geometry.Curves", 0),
            ("Mesh.ColorCarousel", 0),
        ]
        if not self.quality:
            number.append(("Mesh.SurfaceFaces", 1))
        set_display(color, number)
        split_view(self.nview) if self.nview > 1 else 0

        # output
        if self.GUI:
            gmsh.fltk.run()

    def write_mesh(self, mesh_dir: str = "") -> str:
        """
        **Writes** all output files: <file>.geo_unrolled, <file>.log, <file>.mesh and
        returns the mesh filename.

        - mesh_dir: the name of the directory where all gmsh generated files are saved.
        - format: whether to perform medit formatting (True) or not (False) of the mesh.
        - self.outfile: the core name of the outputed files e.g. outfile.log,
           outfile.mesh, etc.
        """
        mesh_dir = self.outdir if not mesh_dir else mesh_dir
        check_dir(mesh_dir)
        # .geo
        logger.info(f"writing {self.outfile}.geo_unrolled to {mesh_dir}")
        gmsh.write(os.path.join(mesh_dir, self.outfile + ".geo_unrolled"))
        # .mesh
        logger.info(f"writing {self.outfile}.{self.mesh_format} to {mesh_dir}")
        gmsh.write(os.path.join(mesh_dir, self.outfile + f".{self.mesh_format}"))
        # medit formatting
        if self.mesh_format == "mesh":
            logger.info(f"medit formatting of {self.outfile}.mesh")
            mesh_file = os.path.join(mesh_dir, self.outfile + ".mesh")
            mesh = open(mesh_file, "r").read().splitlines()
            if self.extrusion_layers == 0:
                mesh = self.reformat_2d(mesh)
            if self.non_corner_tags:
                mesh = self.add_corners(mesh)
            if self.bottom_tags and self.top_tags:
                mesh = self.merge_refs(mesh)
            with open(mesh_file, 'w') as ftw:
                ftw.write("\n".join(mesh))
        # .log
        log = gmsh.logger.get()
        log_file = open(os.path.join(mesh_dir, self.outfile + ".log"), "w")
        logger.info(f"writing {self.outfile}.log to {mesh_dir}")
        log_file.write("\n".join(log))
        # print summary
        summary = [line for line in log if "nodes" in line and "elements" in line][-1][6:]
        logger.info(f"GMSH summary: {summary}")
        # close gmsh
        gmsh.logger.stop()
        gmsh.finalize()
        return self.get_meshfile(mesh_dir)

    def reformat_2d(self, mesh: list[str]) -> list[str]:
        """
        **Fix** gmsh default .mesh format in 2D.
        """
        idx = get_mesh_kwd(mesh, "Dimension")
        mesh[idx] = " Dimension 2"
        del mesh[idx + 1]

        vert_idx = get_mesh_kwd(mesh, "Vertices")
        n_vert = int(mesh[vert_idx + 1])
        for id in range(vert_idx + 2, vert_idx + 2 + n_vert):
            line_data = list(map(float, mesh[id].split()))
            mesh[id] = " " * 4 + f"{line_data[0]:>20}" + \
                       " " * 4 + f"{line_data[1]:>20}" + \
                       " " * 4 + f"{int(line_data[-1]):>20}"
        return mesh

    def add_corners(self, mesh: list[str]) -> list[str]:
        """
        **Adds** Corners at the end of the mesh file.
        """
        c_vert: list[int] = []
        logger.debug(f"non-corner tags: {self.non_corner_tags}")

        vert_idx = get_mesh_kwd(mesh, "Vertices")
        n_vert = int(mesh[vert_idx + 1])
        for v_id, id in enumerate(range(vert_idx + 2, vert_idx + 2 + n_vert)):
            line_data = list(map(float, mesh[id].split()))
            if int(line_data[-1]) not in self.non_corner_tags:
                c_vert.append(v_id + 1)

        mesh = mesh[:-1] + ["Corners", str(len(c_vert))] + [str(v) for v in c_vert] + ["End"]
        return mesh

    def merge_refs(self, mesh: list[str]) -> list[str]:
        """
        **Merges** the periodic boundaries references on each side of the domain.
        """
        logger.debug(f"top tags: {self.top_tags} merged in ref: {max(self.top_tags)}")
        logger.debug(f"bottom tags: {self.bottom_tags} merged in ref: {min(self.bottom_tags)}")

        vert_idx = get_mesh_kwd(mesh, "Vertices")
        n_vert = int(mesh[vert_idx + 1])
        for id in range(vert_idx + 2, vert_idx + 2 + n_vert):
            line_data = list(map(float, mesh[id].split()))
            if int(line_data[-1]) in self.bottom_tags:
                line_data[-1] = min(self.bottom_tags)
            elif int(line_data[-1]) in self.top_tags:
                line_data[-1] = max(self.top_tags)
            mesh[id] = " " * 4 + f"{line_data[0]:>20}" + \
                       " " * 4 + f"{line_data[1]:>20}" + \
                       " " * 4 + f"{int(line_data[-1]):>20}"

        edge_idx = get_mesh_kwd(mesh, "Edges")
        n_edges = int(mesh[edge_idx + 1])
        for id in range(edge_idx + 2, edge_idx + 2 + n_edges):
            line_data = list(map(int, mesh[id].split()))
            if line_data[2] in self.bottom_tags:
                line_data[2] = min(self.bottom_tags)
            elif line_data[2] in self.top_tags:
                line_data[2] = max(self.top_tags)
            mesh[id] = " " + f"{line_data[0]}" + " " + f"{line_data[1]}" + " " + f"{line_data[2]}"
        return mesh

    def get_meshfile(self, mesh_dir: str) -> str:
        """
        **Returns** the path to the generated mesh.
        """
        return os.path.join(mesh_dir, self.outfile + f".{self.mesh_format}")

    @abstractmethod
    def process_config(self):
        """
        Makes sure the config file contains the required information.
        """

    @abstractmethod
    def build_2dmesh(self):
        """
        Builds the surface mesh of the computational domain.
        """

    def build_3dmesh(self):
        """
        Builds a 3D mesh by extrusion
        """
        raise Exception("build_3dmesh method not implemented")

__init__(config: dict, datfile: str = '')

Instantiates the abstract Mesh object.

Input

  • config (dict): the config file dictionary.
  • dat_file (str): path to input_geometry.dat.

Inner

  • outdir (str): path/to/outputdirectory
  • outfile (str): the core name of all outputed files e.g. outfile.log, outfile.mesh, etc.
  • scale (float): geometry scaling factor.
  • header (int): the number of header lines in dat_file.
  • mesh_format (str): the mesh format (mesh or cgns).
  • bl (bool): whether to mesh the boundary layer (True) or not (False).
  • bl_thickness (float): the BL meshing cumulated thickness.
  • bl_ratio (float): the BL meshing growth ratio.
  • bl_size (float): the BL first element size.
  • bl_fan_elements (int): the number of BL fan elements.
  • mesh_order (int): the order of the mesh.
  • structured (bool): whether to recombine triangles (True) or not (False).
  • extrusion_layers (int): the number of extrusion layers when generating a 3D mesh.
  • extrusion_size (float): the total size of the extruded layers.
  • GUI (bool): whether to launch gmsh GUI (True) or not (False).
  • nview (int): the number of sub-windows in gmsh GUI.
  • quality (bool): whether to display quality metrics in gmsh GUI (True) or not (False).
  • pts (list[list[float]]): the geometry coordinates.
  • surf_tag (list[int]): flow-field elements tags used to recombine the mesh if structured.
  • non_corner_tags (list[int]): non-corner physical entity tags used to define 'Corners'.
  • lower_tag (list[int]): lower periodic tags to be identified as one.
  • lower_tag (list[int]): upper periodic tags to be identified as one.
Source code in aero_optim/mesh/mesh.py
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
def __init__(self, config: dict, datfile: str = ""):
    """
    Instantiates the abstract Mesh object.

    **Input**

    - config (dict): the config file dictionary.
    - dat_file (str): path to input_geometry.dat.

    **Inner**

    - outdir (str): path/to/outputdirectory
    - outfile (str): the core name of all outputed files e.g. outfile.log, outfile.mesh, etc.
    - scale (float): geometry scaling factor.
    - header (int): the number of header lines in dat_file.
    - mesh_format (str): the mesh format (mesh or cgns).
    - bl (bool): whether to mesh the boundary layer (True) or not (False).
    - bl_thickness (float): the BL meshing cumulated thickness.
    - bl_ratio (float): the BL meshing growth ratio.
    - bl_size (float): the BL first element size.
    - bl_fan_elements (int): the number of BL fan elements.
    - mesh_order (int): the order of the mesh.
    - structured (bool): whether to recombine triangles (True) or not (False).
    - extrusion_layers (int): the number of extrusion layers when generating a 3D mesh.
    - extrusion_size (float): the total size of the extruded layers.
    - GUI (bool): whether to launch gmsh GUI (True) or not (False).
    - nview (int): the number of sub-windows in gmsh GUI.
    - quality (bool): whether to display quality metrics in gmsh GUI (True) or not (False).
    - pts (list[list[float]]): the geometry coordinates.
    - surf_tag (list[int]): flow-field elements tags used to recombine the mesh if structured.
    - non_corner_tags (list[int]): non-corner physical entity tags used to define 'Corners'.
    - lower_tag (list[int]): lower periodic tags to be identified as one.
    - lower_tag (list[int]): upper periodic tags to be identified as one.
    """
    self.config = config
    self.process_config()
    # study params
    self.dat_file: str = datfile if datfile else config["study"]["file"]
    self.outdir: str = config["study"]["outdir"]
    self.outfile = self.config["study"].get("outfile", self.dat_file.split("/")[-1][:-4])
    self.scale: int = config["study"].get("scale", 1)
    self.header: int = config["study"].get("header", 2)
    # mesh params (format & boundary layer)
    self.mesh_format: str = config["gmsh"]["mesh"].get("mesh_format", "mesh").lower()
    self.bl: bool = config["gmsh"]["mesh"].get("bl", False)
    self.bl_thickness: float = config["gmsh"]["mesh"].get("bl_thickness", 1e-3)
    self.bl_ratio: float = config["gmsh"]["mesh"].get("bl_ratio", 1.1)
    self.bl_size: float = config["gmsh"]["mesh"].get("bl_size", 1e-5)
    self.bl_fan_elements: int = config["gmsh"]["mesh"].get("bl_fan_elements", 10)
    self.mesh_order: int = config["gmsh"]["mesh"].get("order", 0)
    # mesh params (3d extrusion)
    self.structured: bool = config["gmsh"]["mesh"].get("structured", False)
    self.extrusion_layers: int = config["gmsh"]["mesh"].get("extrusion_layers", 0)
    self.extrusion_size: int = config["gmsh"]["mesh"].get("extrusion_size", 0.001)
    # gui options
    self.GUI: bool = config["gmsh"]["view"].get("GUI", True)
    self.nview: int = config["gmsh"]["view"].get("nview", 1)
    self.quality: bool = config["gmsh"]["view"].get("quality", False)
    # geometry coordinates loading
    self.pts: list[list[float]] = from_dat(self.dat_file, self.header, self.scale)
    # flow-field and non-corner tags (for recombination and corners definition)
    self.surf_tag: list[int] = []
    self.non_corner_tags: list[int] = []
    self.bottom_tags: list[int] = []
    self.top_tags: list[int] = []

add_corners(mesh: list[str]) -> list[str]

Adds Corners at the end of the mesh file.

Source code in aero_optim/mesh/mesh.py
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
def add_corners(self, mesh: list[str]) -> list[str]:
    """
    **Adds** Corners at the end of the mesh file.
    """
    c_vert: list[int] = []
    logger.debug(f"non-corner tags: {self.non_corner_tags}")

    vert_idx = get_mesh_kwd(mesh, "Vertices")
    n_vert = int(mesh[vert_idx + 1])
    for v_id, id in enumerate(range(vert_idx + 2, vert_idx + 2 + n_vert)):
        line_data = list(map(float, mesh[id].split()))
        if int(line_data[-1]) not in self.non_corner_tags:
            c_vert.append(v_id + 1)

    mesh = mesh[:-1] + ["Corners", str(len(c_vert))] + [str(v) for v in c_vert] + ["End"]
    return mesh

build_2dmesh() abstractmethod

Builds the surface mesh of the computational domain.

Source code in aero_optim/mesh/mesh.py
286
287
288
289
290
@abstractmethod
def build_2dmesh(self):
    """
    Builds the surface mesh of the computational domain.
    """

build_3dmesh()

Builds a 3D mesh by extrusion

Source code in aero_optim/mesh/mesh.py
292
293
294
295
296
def build_3dmesh(self):
    """
    Builds a 3D mesh by extrusion
    """
    raise Exception("build_3dmesh method not implemented")

build_mesh()

Defines the gmsh routine.

Source code in aero_optim/mesh/mesh.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
def build_mesh(self):
    """
    **Defines** the gmsh routine.
    """
    gmsh.initialize()
    gmsh.option.setNumber('General.Terminal', 0)
    gmsh.logger.start()
    gmsh.model.add("model")

    self.build_2dmesh()
    self.build_3dmesh() if self.extrusion_layers > 0 else 0

    if self.structured:
        [gmsh.model.geo.mesh.setRecombine(2, abs(id)) for id in self.surf_tag]
    gmsh.model.geo.synchronize()
    gmsh.model.mesh.generate(3) if self.extrusion_layers > 0 else gmsh.model.mesh.generate(2)
    if self.mesh_order:
        gmsh.model.mesh.setOrder(self.mesh_order)

    # visualization
    if self.quality:
        plot_quality()
    elt_type = "Mesh.Triangles" if not self.structured else "Mesh.Quadrangles"
    color = [
        ("General.BackgroundGradient", 255, 255, 255),
        (elt_type, 255, 0, 0)
    ]
    number = [
        ("Geometry.Points", 0),
        ("Geometry.Curves", 0),
        ("Mesh.ColorCarousel", 0),
    ]
    if not self.quality:
        number.append(("Mesh.SurfaceFaces", 1))
    set_display(color, number)
    split_view(self.nview) if self.nview > 1 else 0

    # output
    if self.GUI:
        gmsh.fltk.run()

get_meshfile(mesh_dir: str) -> str

Returns the path to the generated mesh.

Source code in aero_optim/mesh/mesh.py
274
275
276
277
278
def get_meshfile(self, mesh_dir: str) -> str:
    """
    **Returns** the path to the generated mesh.
    """
    return os.path.join(mesh_dir, self.outfile + f".{self.mesh_format}")

get_nlayer() -> int

Returns the number of layers required to reach bl_thickness given the growth bl_ratio and the first element size bl_size.

Source code in aero_optim/mesh/mesh.py
115
116
117
118
119
120
121
122
123
def get_nlayer(self) -> int:
    """
    **Returns** the number of layers required to reach bl_thickness given the growth bl_ratio
    and the first element size bl_size.
    """
    return math.ceil(
        math.log(1 - self.bl_thickness * (1 - self.bl_ratio) / self.bl_size)
        / math.log(self.bl_ratio) - 1
    )

merge_refs(mesh: list[str]) -> list[str]

Merges the periodic boundaries references on each side of the domain.

Source code in aero_optim/mesh/mesh.py
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
def merge_refs(self, mesh: list[str]) -> list[str]:
    """
    **Merges** the periodic boundaries references on each side of the domain.
    """
    logger.debug(f"top tags: {self.top_tags} merged in ref: {max(self.top_tags)}")
    logger.debug(f"bottom tags: {self.bottom_tags} merged in ref: {min(self.bottom_tags)}")

    vert_idx = get_mesh_kwd(mesh, "Vertices")
    n_vert = int(mesh[vert_idx + 1])
    for id in range(vert_idx + 2, vert_idx + 2 + n_vert):
        line_data = list(map(float, mesh[id].split()))
        if int(line_data[-1]) in self.bottom_tags:
            line_data[-1] = min(self.bottom_tags)
        elif int(line_data[-1]) in self.top_tags:
            line_data[-1] = max(self.top_tags)
        mesh[id] = " " * 4 + f"{line_data[0]:>20}" + \
                   " " * 4 + f"{line_data[1]:>20}" + \
                   " " * 4 + f"{int(line_data[-1]):>20}"

    edge_idx = get_mesh_kwd(mesh, "Edges")
    n_edges = int(mesh[edge_idx + 1])
    for id in range(edge_idx + 2, edge_idx + 2 + n_edges):
        line_data = list(map(int, mesh[id].split()))
        if line_data[2] in self.bottom_tags:
            line_data[2] = min(self.bottom_tags)
        elif line_data[2] in self.top_tags:
            line_data[2] = max(self.top_tags)
        mesh[id] = " " + f"{line_data[0]}" + " " + f"{line_data[1]}" + " " + f"{line_data[2]}"
    return mesh

process_config() abstractmethod

Makes sure the config file contains the required information.

Source code in aero_optim/mesh/mesh.py
280
281
282
283
284
@abstractmethod
def process_config(self):
    """
    Makes sure the config file contains the required information.
    """

reformat_2d(mesh: list[str]) -> list[str]

Fix gmsh default .mesh format in 2D.

Source code in aero_optim/mesh/mesh.py
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
def reformat_2d(self, mesh: list[str]) -> list[str]:
    """
    **Fix** gmsh default .mesh format in 2D.
    """
    idx = get_mesh_kwd(mesh, "Dimension")
    mesh[idx] = " Dimension 2"
    del mesh[idx + 1]

    vert_idx = get_mesh_kwd(mesh, "Vertices")
    n_vert = int(mesh[vert_idx + 1])
    for id in range(vert_idx + 2, vert_idx + 2 + n_vert):
        line_data = list(map(float, mesh[id].split()))
        mesh[id] = " " * 4 + f"{line_data[0]:>20}" + \
                   " " * 4 + f"{line_data[1]:>20}" + \
                   " " * 4 + f"{int(line_data[-1]):>20}"
    return mesh

write_mesh(mesh_dir: str = '') -> str

Writes all output files: .geo_unrolled, .log, .mesh and returns the mesh filename.

  • mesh_dir: the name of the directory where all gmsh generated files are saved.
  • format: whether to perform medit formatting (True) or not (False) of the mesh.
  • self.outfile: the core name of the outputed files e.g. outfile.log, outfile.mesh, etc.
Source code in aero_optim/mesh/mesh.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
def write_mesh(self, mesh_dir: str = "") -> str:
    """
    **Writes** all output files: <file>.geo_unrolled, <file>.log, <file>.mesh and
    returns the mesh filename.

    - mesh_dir: the name of the directory where all gmsh generated files are saved.
    - format: whether to perform medit formatting (True) or not (False) of the mesh.
    - self.outfile: the core name of the outputed files e.g. outfile.log,
       outfile.mesh, etc.
    """
    mesh_dir = self.outdir if not mesh_dir else mesh_dir
    check_dir(mesh_dir)
    # .geo
    logger.info(f"writing {self.outfile}.geo_unrolled to {mesh_dir}")
    gmsh.write(os.path.join(mesh_dir, self.outfile + ".geo_unrolled"))
    # .mesh
    logger.info(f"writing {self.outfile}.{self.mesh_format} to {mesh_dir}")
    gmsh.write(os.path.join(mesh_dir, self.outfile + f".{self.mesh_format}"))
    # medit formatting
    if self.mesh_format == "mesh":
        logger.info(f"medit formatting of {self.outfile}.mesh")
        mesh_file = os.path.join(mesh_dir, self.outfile + ".mesh")
        mesh = open(mesh_file, "r").read().splitlines()
        if self.extrusion_layers == 0:
            mesh = self.reformat_2d(mesh)
        if self.non_corner_tags:
            mesh = self.add_corners(mesh)
        if self.bottom_tags and self.top_tags:
            mesh = self.merge_refs(mesh)
        with open(mesh_file, 'w') as ftw:
            ftw.write("\n".join(mesh))
    # .log
    log = gmsh.logger.get()
    log_file = open(os.path.join(mesh_dir, self.outfile + ".log"), "w")
    logger.info(f"writing {self.outfile}.log to {mesh_dir}")
    log_file.write("\n".join(log))
    # print summary
    summary = [line for line in log if "nodes" in line and "elements" in line][-1][6:]
    logger.info(f"GMSH summary: {summary}")
    # close gmsh
    gmsh.logger.stop()
    gmsh.finalize()
    return self.get_meshfile(mesh_dir)

mesh.naca_base_mesh.NACABaseMesh

Bases: Mesh

This class implements a meshing routine for a naca profile.

The computational domain has the following structure:

            pt_hi_inlet
               !   top_side
               !      !
            *  * ------------- * pt_hi_outlet
      *  *  *                  |
   *  *                        |
   *                           |
*  *                           |
*                              |
*   <- arc_inlet               | <- outlet
*                              |
*  *                           |
   *                           |
   *  *                        |
      *  *  *                  |
            *  * ------------- * pt_low_outlet
               !        !
               !   bottom_side
            pt_low_inlet
Source code in aero_optim/mesh/naca_base_mesh.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
class NACABaseMesh(Mesh):
    """
    This class implements a meshing routine for a naca profile.

    The computational domain has the following structure:

                    pt_hi_inlet
                       !   top_side
                       !      !
                    *  * ------------- * pt_hi_outlet
              *  *  *                  |
           *  *                        |
           *                           |
        *  *                           |
        *                              |
        *   <- arc_inlet               | <- outlet
        *                              |
        *  *                           |
           *                           |
           *  *                        |
              *  *  *                  |
                    *  * ------------- * pt_low_outlet
                       !        !
                       !   bottom_side
                    pt_low_inlet

    """
    def __init__(self, config: dict, datfile: str = ""):
        """
        Instantiates the NACABaseMesh object.

        **Input**

        - config (dict): the config file dictionary.
        - dat_file (str): path to input_geometry.dat.

        **Inner**

        - dinlet (float): the radius of the inlet semi-circle.
        - doutlet (float): the distance between the airfoil trailing edge and the outlet.
        - offset (int): the leading edge portion defined in number of points from the leading edge.
        - nodes_inlet (int): the number of nodes to mesh the inlet.
        - nodes_outlet (int): the number of nodes to mesh the outlet.
        - snodes (int): the number of nodes to mesh the top and bottom sides.
        - le (int): the number of nodes to mesh the airfoil leading edge portion.
        - low (int): the number of nodes to mesh the airfoil trailing edge lower portion.
        - up (int): the number of nodes to mesh the airfoil trailing edge upper portion.
        """
        super().__init__(config, datfile)
        self.dinlet: float = self.config["gmsh"]["domain"].get("inlet", 2)
        self.doutlet: float = self.config["gmsh"]["domain"].get("outlet", 10)
        self.offset: int = self.config["gmsh"]["domain"].get("le_offset", 10)
        self.nodes_inlet: int = self.config["gmsh"]["mesh"].get("nodes_inlet", 100)
        self.nodes_outlet: int = self.config["gmsh"]["mesh"].get("nodes_outlet", 100)
        self.snodes: int = self.config["gmsh"]["mesh"].get("side_nodes", 100)
        self.le: int = self.config["gmsh"]["mesh"].get("le", 20)
        self.low: int = self.config["gmsh"]["mesh"].get("low", 70)
        self.up: int = self.config["gmsh"]["mesh"].get("up", 70)

    def process_config(self):
        logger.debug("processing config..")
        if "domain" not in self.config["gmsh"]:
            logger.debug(f"no <domain> entry in {self.config['gmsh']}, empty entry added")
            self.config["gmsh"]["domain"] = {}
        if "inlet" not in self.config["gmsh"]["domain"]:
            logger.debug(f"no <inlet> entry in {self.config['gmsh']['domain']}")
        if "outlet" not in self.config["gmsh"]["domain"]:
            logger.debug(f"no <outlet> entry in {self.config['gmsh']['domain']}")

    def split_naca(self) -> tuple[list[list[float]], list[list[float]]]:
        """
        **Returns** the upper and lower parts of the airfoil as ordered lists (wrt the x axis).

        Note:
            the trailing and leading edges are voluntarily excluded from both parts
            since the geometry is closed and these points must each have a unique tag.
        """
        start: int = min(self.idx_le, self.idx_te)
        end: int = max(self.idx_le, self.idx_te)
        if (
            max([p[1] for p in self.pts[start:end + 1]])
            > max([p[1] for p in self.pts[:start + 1] + self.pts[end:]])
        ):
            upper = [[p[0], p[1], p[2]] for p in self.pts[start + 1:end]]
            lower = [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[end + 1:]]
        else:
            lower = [[p[0], p[1], p[2]] for p in self.pts[start + 1:end]]
            upper = [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[end + 1:]]
        upper = sorted(upper, key=lambda x: (x[0]), reverse=True)
        lower = sorted(lower, key=lambda x: (x[0]))
        return upper, lower

    def build_bl(self, naca_tag: list[int], te_tag: int):
        """
        **Builds** the boundary layer around the airfoil.
        """
        self.f = gmsh.model.mesh.field.add('BoundaryLayer')
        gmsh.model.mesh.field.setNumbers(self.f, 'CurvesList', naca_tag)
        gmsh.model.mesh.field.setNumber(self.f, 'Size', self.bl_size)
        gmsh.model.mesh.field.setNumber(self.f, 'Ratio', self.bl_ratio)
        gmsh.model.mesh.field.setNumber(self.f, 'Quads', int(self.structured))
        gmsh.model.mesh.field.setNumber(self.f, 'Thickness', self.bl_thickness)
        gmsh.option.setNumber('Mesh.BoundaryLayerFanElements', self.bl_fan_elements)
        gmsh.model.mesh.field.setNumbers(self.f, 'FanPointsList', [te_tag])
        gmsh.model.mesh.field.setAsBoundaryLayer(self.f)

    def build_2dmesh(self):
        """
        **Builds** the surface mesh of the computational domain.
        """
        _, self.idx_le = min((p[0], idx) for (idx, p) in enumerate(self.pts))
        _, self.idx_te = max((p[0], idx) for (idx, p) in enumerate(self.pts))
        u_side, l_side = self.split_naca()

        # add points and lines for the naca lower and upper parts
        x_le, y_le = self.pts[self.idx_le][:2]
        x_te, y_te = self.pts[self.idx_te][:2]
        te_tag = gmsh.model.geo.addPoint(x_te, y_te, 0.)
        le_tag = gmsh.model.geo.addPoint(x_le, y_le, 0.)
        pt_u = [te_tag] + [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in u_side]
        pt_l = [le_tag] +\
               [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in l_side] + [te_tag]

        # airfoil boundary
        spline_low = gmsh.model.geo.addSpline(pt_l[self.offset:], tag=1)
        spline_le = gmsh.model.geo.addSpline(pt_u[-self.offset:] + pt_l[:self.offset + 1], tag=2)
        spline_up = gmsh.model.geo.addSpline(pt_u[:-self.offset + 1], tag=3)
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_low, self.low, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_le, self.le, "Bump", 2.)
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_up, self.up, "Progression", 1)
        naca_loop = gmsh.model.geo.addCurveLoop([spline_low, spline_le, spline_up])

        # boundary layer
        self.build_bl([spline_low, spline_le, spline_up], te_tag) if self.bl else 0

        # construction points
        pt_hi_inlet = gmsh.model.geo.addPoint(x_te, y_te + self.dinlet, 0.)
        pt_low_inlet = gmsh.model.geo.addPoint(x_te, y_te - self.dinlet, 0.)
        pt_hi_outlet = gmsh.model.geo.addPoint(x_te + self.doutlet, y_te + self.dinlet, 0.)
        pt_low_outlet = gmsh.model.geo.addPoint(x_te + self.doutlet, y_te - self.dinlet, 0.)

        # non-blade boundary lines
        arc_inlet = gmsh.model.geo.addCircleArc(pt_hi_inlet, te_tag, pt_low_inlet)
        top_side = gmsh.model.geo.addLine(pt_hi_inlet, pt_hi_outlet)
        bottom_side = gmsh.model.geo.addLine(pt_low_outlet, pt_low_inlet)
        outlet = gmsh.model.geo.addLine(pt_hi_outlet, pt_low_outlet)

        # transfinite curves on non-blade boundaries
        gmsh.model.geo.mesh.setTransfiniteCurve(arc_inlet, self.nodes_inlet, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(top_side, self.snodes, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(bottom_side, self.snodes, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(outlet, self.nodes_outlet, "Progression", 1)

        # closed curve loop and computational domain surface definition
        cloop = [-arc_inlet, top_side, outlet, bottom_side]
        boundary_loop = gmsh.model.geo.addCurveLoop(cloop)
        self.surf_tag = [gmsh.model.geo.addPlaneSurface([-boundary_loop, -naca_loop], tag=1000)]

        # define physical groups for boundary conditions
        gmsh.model.geo.addPhysicalGroup(1, [spline_low, spline_le, spline_up], tag=100)
        logger.debug(f"BC: Wall tags are {[spline_low, spline_le, spline_up]}")
        gmsh.model.geo.addPhysicalGroup(1, [arc_inlet], tag=200)
        logger.debug(f"BC: Inlet tags are {[arc_inlet]}")
        gmsh.model.geo.addPhysicalGroup(1, [outlet], tag=300)
        logger.debug(f"BC: Outlet tags are {[outlet]}")
        gmsh.model.geo.addPhysicalGroup(1, [top_side], tag=400)
        logger.debug(f"BC: Top tags are {[top_side]}")
        gmsh.model.geo.addPhysicalGroup(1, [bottom_side], tag=500)
        logger.debug(f"BC: Bottom tags are {[bottom_side]}")
        gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=600)

__init__(config: dict, datfile: str = '')

Instantiates the NACABaseMesh object.

Input

  • config (dict): the config file dictionary.
  • dat_file (str): path to input_geometry.dat.

Inner

  • dinlet (float): the radius of the inlet semi-circle.
  • doutlet (float): the distance between the airfoil trailing edge and the outlet.
  • offset (int): the leading edge portion defined in number of points from the leading edge.
  • nodes_inlet (int): the number of nodes to mesh the inlet.
  • nodes_outlet (int): the number of nodes to mesh the outlet.
  • snodes (int): the number of nodes to mesh the top and bottom sides.
  • le (int): the number of nodes to mesh the airfoil leading edge portion.
  • low (int): the number of nodes to mesh the airfoil trailing edge lower portion.
  • up (int): the number of nodes to mesh the airfoil trailing edge upper portion.
Source code in aero_optim/mesh/naca_base_mesh.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
def __init__(self, config: dict, datfile: str = ""):
    """
    Instantiates the NACABaseMesh object.

    **Input**

    - config (dict): the config file dictionary.
    - dat_file (str): path to input_geometry.dat.

    **Inner**

    - dinlet (float): the radius of the inlet semi-circle.
    - doutlet (float): the distance between the airfoil trailing edge and the outlet.
    - offset (int): the leading edge portion defined in number of points from the leading edge.
    - nodes_inlet (int): the number of nodes to mesh the inlet.
    - nodes_outlet (int): the number of nodes to mesh the outlet.
    - snodes (int): the number of nodes to mesh the top and bottom sides.
    - le (int): the number of nodes to mesh the airfoil leading edge portion.
    - low (int): the number of nodes to mesh the airfoil trailing edge lower portion.
    - up (int): the number of nodes to mesh the airfoil trailing edge upper portion.
    """
    super().__init__(config, datfile)
    self.dinlet: float = self.config["gmsh"]["domain"].get("inlet", 2)
    self.doutlet: float = self.config["gmsh"]["domain"].get("outlet", 10)
    self.offset: int = self.config["gmsh"]["domain"].get("le_offset", 10)
    self.nodes_inlet: int = self.config["gmsh"]["mesh"].get("nodes_inlet", 100)
    self.nodes_outlet: int = self.config["gmsh"]["mesh"].get("nodes_outlet", 100)
    self.snodes: int = self.config["gmsh"]["mesh"].get("side_nodes", 100)
    self.le: int = self.config["gmsh"]["mesh"].get("le", 20)
    self.low: int = self.config["gmsh"]["mesh"].get("low", 70)
    self.up: int = self.config["gmsh"]["mesh"].get("up", 70)

build_2dmesh()

Builds the surface mesh of the computational domain.

Source code in aero_optim/mesh/naca_base_mesh.py
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
def build_2dmesh(self):
    """
    **Builds** the surface mesh of the computational domain.
    """
    _, self.idx_le = min((p[0], idx) for (idx, p) in enumerate(self.pts))
    _, self.idx_te = max((p[0], idx) for (idx, p) in enumerate(self.pts))
    u_side, l_side = self.split_naca()

    # add points and lines for the naca lower and upper parts
    x_le, y_le = self.pts[self.idx_le][:2]
    x_te, y_te = self.pts[self.idx_te][:2]
    te_tag = gmsh.model.geo.addPoint(x_te, y_te, 0.)
    le_tag = gmsh.model.geo.addPoint(x_le, y_le, 0.)
    pt_u = [te_tag] + [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in u_side]
    pt_l = [le_tag] +\
           [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in l_side] + [te_tag]

    # airfoil boundary
    spline_low = gmsh.model.geo.addSpline(pt_l[self.offset:], tag=1)
    spline_le = gmsh.model.geo.addSpline(pt_u[-self.offset:] + pt_l[:self.offset + 1], tag=2)
    spline_up = gmsh.model.geo.addSpline(pt_u[:-self.offset + 1], tag=3)
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_low, self.low, "Progression", 1)
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_le, self.le, "Bump", 2.)
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_up, self.up, "Progression", 1)
    naca_loop = gmsh.model.geo.addCurveLoop([spline_low, spline_le, spline_up])

    # boundary layer
    self.build_bl([spline_low, spline_le, spline_up], te_tag) if self.bl else 0

    # construction points
    pt_hi_inlet = gmsh.model.geo.addPoint(x_te, y_te + self.dinlet, 0.)
    pt_low_inlet = gmsh.model.geo.addPoint(x_te, y_te - self.dinlet, 0.)
    pt_hi_outlet = gmsh.model.geo.addPoint(x_te + self.doutlet, y_te + self.dinlet, 0.)
    pt_low_outlet = gmsh.model.geo.addPoint(x_te + self.doutlet, y_te - self.dinlet, 0.)

    # non-blade boundary lines
    arc_inlet = gmsh.model.geo.addCircleArc(pt_hi_inlet, te_tag, pt_low_inlet)
    top_side = gmsh.model.geo.addLine(pt_hi_inlet, pt_hi_outlet)
    bottom_side = gmsh.model.geo.addLine(pt_low_outlet, pt_low_inlet)
    outlet = gmsh.model.geo.addLine(pt_hi_outlet, pt_low_outlet)

    # transfinite curves on non-blade boundaries
    gmsh.model.geo.mesh.setTransfiniteCurve(arc_inlet, self.nodes_inlet, "Progression", 1)
    gmsh.model.geo.mesh.setTransfiniteCurve(top_side, self.snodes, "Progression", 1)
    gmsh.model.geo.mesh.setTransfiniteCurve(bottom_side, self.snodes, "Progression", 1)
    gmsh.model.geo.mesh.setTransfiniteCurve(outlet, self.nodes_outlet, "Progression", 1)

    # closed curve loop and computational domain surface definition
    cloop = [-arc_inlet, top_side, outlet, bottom_side]
    boundary_loop = gmsh.model.geo.addCurveLoop(cloop)
    self.surf_tag = [gmsh.model.geo.addPlaneSurface([-boundary_loop, -naca_loop], tag=1000)]

    # define physical groups for boundary conditions
    gmsh.model.geo.addPhysicalGroup(1, [spline_low, spline_le, spline_up], tag=100)
    logger.debug(f"BC: Wall tags are {[spline_low, spline_le, spline_up]}")
    gmsh.model.geo.addPhysicalGroup(1, [arc_inlet], tag=200)
    logger.debug(f"BC: Inlet tags are {[arc_inlet]}")
    gmsh.model.geo.addPhysicalGroup(1, [outlet], tag=300)
    logger.debug(f"BC: Outlet tags are {[outlet]}")
    gmsh.model.geo.addPhysicalGroup(1, [top_side], tag=400)
    logger.debug(f"BC: Top tags are {[top_side]}")
    gmsh.model.geo.addPhysicalGroup(1, [bottom_side], tag=500)
    logger.debug(f"BC: Bottom tags are {[bottom_side]}")
    gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=600)

build_bl(naca_tag: list[int], te_tag: int)

Builds the boundary layer around the airfoil.

Source code in aero_optim/mesh/naca_base_mesh.py
101
102
103
104
105
106
107
108
109
110
111
112
113
def build_bl(self, naca_tag: list[int], te_tag: int):
    """
    **Builds** the boundary layer around the airfoil.
    """
    self.f = gmsh.model.mesh.field.add('BoundaryLayer')
    gmsh.model.mesh.field.setNumbers(self.f, 'CurvesList', naca_tag)
    gmsh.model.mesh.field.setNumber(self.f, 'Size', self.bl_size)
    gmsh.model.mesh.field.setNumber(self.f, 'Ratio', self.bl_ratio)
    gmsh.model.mesh.field.setNumber(self.f, 'Quads', int(self.structured))
    gmsh.model.mesh.field.setNumber(self.f, 'Thickness', self.bl_thickness)
    gmsh.option.setNumber('Mesh.BoundaryLayerFanElements', self.bl_fan_elements)
    gmsh.model.mesh.field.setNumbers(self.f, 'FanPointsList', [te_tag])
    gmsh.model.mesh.field.setAsBoundaryLayer(self.f)

split_naca() -> tuple[list[list[float]], list[list[float]]]

Returns the upper and lower parts of the airfoil as ordered lists (wrt the x axis).

Note

the trailing and leading edges are voluntarily excluded from both parts since the geometry is closed and these points must each have a unique tag.

Source code in aero_optim/mesh/naca_base_mesh.py
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
def split_naca(self) -> tuple[list[list[float]], list[list[float]]]:
    """
    **Returns** the upper and lower parts of the airfoil as ordered lists (wrt the x axis).

    Note:
        the trailing and leading edges are voluntarily excluded from both parts
        since the geometry is closed and these points must each have a unique tag.
    """
    start: int = min(self.idx_le, self.idx_te)
    end: int = max(self.idx_le, self.idx_te)
    if (
        max([p[1] for p in self.pts[start:end + 1]])
        > max([p[1] for p in self.pts[:start + 1] + self.pts[end:]])
    ):
        upper = [[p[0], p[1], p[2]] for p in self.pts[start + 1:end]]
        lower = [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[end + 1:]]
    else:
        lower = [[p[0], p[1], p[2]] for p in self.pts[start + 1:end]]
        upper = [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[end + 1:]]
    upper = sorted(upper, key=lambda x: (x[0]), reverse=True)
    lower = sorted(lower, key=lambda x: (x[0]))
    return upper, lower

mesh.naca_block_mesh.NACABlockMesh

Bases: NACABaseMesh

This class implements a blocking mesh routine for a naca profile based on:
https://github.com/ComputationalDomain/CMesh_rae69ck-il

Source code in aero_optim/mesh/naca_block_mesh.py
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
class NACABlockMesh(NACABaseMesh):
    """
    This class implements a blocking mesh routine for a naca profile based on:</br>
    https://github.com/ComputationalDomain/CMesh_rae69ck-il
    """
    def __init__(self, config: dict, datfile: str = ""):
        """
        Instantiates the BlockMesh object.

        **Input**

        - config (dict): the config file dictionary.
        - dat_file (str): path to input_geometry.dat.
        """
        super().__init__(config, datfile)

    def build_2dmesh(self):
        """
        **Builds** the surface mesh of the computational domain.

        **Inner**

        - R (float): radius of the outer circle.
        - d_out (float): distance to the outlet.
        - offset (int): offset from leading edge.
        - b_width (float): block_width.
        - n_inlet (int): nbr of lead edge & inlet nodes.
        - r_inlet (float): bump ratio of lead edge & inlet nodes.
        - n_vertical (int) : nbr of out & verti nodes.
        - r_vertical (float): out & vert growth.
        - n_airfoil (int): nbr of nodes on each sides.
        - r_airfoil (float): airfoil sides growth.
        - n_wake (int): nbr of nodes in the wake dir.
        - r_wake (float): wake growth.
        """
        R = self.dinlet
        d_out = self.doutlet
        offset = self.config["gmsh"]["domain"].get("le_offset", 10)
        b_width = self.config["gmsh"]["domain"].get("block_width", 10)
        n_inlet = self.config["gmsh"]["mesh"].get("n_inlet", 60)
        r_inlet = self.config["gmsh"]["mesh"].get("r_inlet", 1.)
        n_vertical = self.config["gmsh"]["mesh"].get("n_vertical", 90)
        r_vertical = self.config["gmsh"]["mesh"].get("r_vertical", 1 / 0.95)
        n_airfoil = self.config["gmsh"]["mesh"].get("n_airfoil", 50)
        r_airfoil = self.config["gmsh"]["mesh"].get("r_airfoil", 1)
        n_wake = self.config["gmsh"]["mesh"].get("n_wake", 100)
        r_wake = self.config["gmsh"]["mesh"].get("r_wake", 1 / 0.95)

        _, self.idx_le = min((p[0], idx) for (idx, p) in enumerate(self.pts))
        _, self.idx_te = max((p[0], idx) for (idx, p) in enumerate(self.pts))
        u_side, l_side = self.split_naca()

        # add points and lines for the naca lower and upper parts
        x_le, y_le = self.pts[self.idx_le][:2]
        x_te, y_te = self.pts[self.idx_te][:2]
        te_tag = gmsh.model.geo.addPoint(x_te, y_te, 0.)
        le_tag = gmsh.model.geo.addPoint(x_le, y_le, 0.)
        pt_u = [te_tag] + [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in u_side]
        pt_l = [le_tag] +\
               [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in l_side] + [te_tag]

        # airfoil boundary
        spline_low = gmsh.model.geo.addSpline(pt_l[offset:], tag=1)
        spline_le = gmsh.model.geo.addSpline(pt_u[-offset:] + pt_l[:offset + 1], tag=2)
        spline_up = gmsh.model.geo.addSpline(pt_u[:-offset + 1], tag=3)

        # domain and block construction points
        pt_229 = gmsh.model.geo.addPoint(x_te - b_width, R, 0., tag=229)
        pt_230 = gmsh.model.geo.addPoint(x_te - b_width, -R, 0., tag=230)
        pt_231 = gmsh.model.geo.addPoint(x_te, R, 0., tag=231)
        pt_232 = gmsh.model.geo.addPoint(x_te, -R, 0., tag=232)
        pt_233 = gmsh.model.geo.addPoint(d_out, R, 0., tag=233)
        pt_234 = gmsh.model.geo.addPoint(d_out, -R, 0., tag=234)
        pt_235 = gmsh.model.geo.addPoint(d_out, 0, 0., tag=235)

        # domain and block lines
        circle_4 = gmsh.model.geo.addCircleArc(pt_230, te_tag, pt_229)
        line_5 = gmsh.model.geo.addLine(pt_u[-offset], pt_229)
        line_6 = gmsh.model.geo.addLine(pt_l[offset], pt_230)
        line_7 = gmsh.model.geo.addLine(pt_229, pt_231)
        line_8 = gmsh.model.geo.addLine(pt_230, pt_232)
        line_9 = gmsh.model.geo.addLine(pt_231, pt_233)
        line_10 = gmsh.model.geo.addLine(pt_232, pt_234)
        line_11 = gmsh.model.geo.addLine(pt_235, pt_234)
        line_12 = gmsh.model.geo.addLine(pt_235, pt_233)
        line_13 = gmsh.model.geo.addLine(te_tag, pt_231)
        line_14 = gmsh.model.geo.addLine(te_tag, pt_232)
        line_15 = gmsh.model.geo.addLine(te_tag, pt_235)

        # meshing parameters
        gmsh.model.geo.mesh.setTransfiniteCurve(circle_4, n_inlet, "Bump", r_inlet)
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_le, n_inlet, "Bump", r_inlet)
        _ = [gmsh.model.geo.mesh.setTransfiniteCurve(lid, n_vertical, "Progression", r_vertical)
             for lid in [line_5, line_6, line_11, line_12, line_13, line_14]]
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_low, n_airfoil, "Bump", r_airfoil)
        gmsh.model.geo.mesh.setTransfiniteCurve(spline_up, n_airfoil, "Bump", r_airfoil)
        gmsh.model.geo.mesh.setTransfiniteCurve(line_7, n_airfoil, "Bump", r_airfoil)
        gmsh.model.geo.mesh.setTransfiniteCurve(line_8, n_airfoil, "Bump", r_airfoil)
        gmsh.model.geo.mesh.setTransfiniteCurve(line_15, n_wake, "Progression", r_wake)
        gmsh.model.geo.mesh.setTransfiniteCurve(line_9, n_wake, "Progression", r_wake)
        gmsh.model.geo.mesh.setTransfiniteCurve(line_10, n_wake, "Progression", r_wake)

        # domain and block surfaces
        cloop_1 = gmsh.model.geo.addCurveLoop([circle_4, -line_5, spline_le, line_6])
        surf_1 = gmsh.model.geo.addPlaneSurface([-cloop_1], tag=1001)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_1)

        cloop_2 = gmsh.model.geo.addCurveLoop([line_5, line_7, -line_13, spline_up])
        surf_2 = gmsh.model.geo.addPlaneSurface([-cloop_2], tag=1002)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_2)

        cloop_3 = gmsh.model.geo.addCurveLoop([line_13, line_9, -line_12, -line_15])
        surf_3 = gmsh.model.geo.addPlaneSurface([-cloop_3], tag=1003)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_3)

        cloop_4 = gmsh.model.geo.addCurveLoop([line_6, line_8, -line_14, -spline_low])
        surf_4 = gmsh.model.geo.addPlaneSurface([-cloop_4], tag=1004)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_4)

        cloop_5 = gmsh.model.geo.addCurveLoop([line_14, line_10, -line_11, -line_15])
        surf_5 = gmsh.model.geo.addPlaneSurface([-cloop_5], tag=1005)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_5)

        # physical groups
        self.surf_tag = [surf_1, surf_2, surf_3, -surf_5, -surf_4]
        gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100)
        gmsh.model.geo.addPhysicalGroup(1, [circle_4, line_7, line_8], tag=10)
        logger.debug(f"BC: Inlet tags are {[circle_4, line_7, line_8]}")
        gmsh.model.geo.addPhysicalGroup(1, [line_11, line_12], tag=20)
        logger.debug(f"BC: Outlet tags are {[line_11, line_12]}")
        gmsh.model.geo.addPhysicalGroup(1, [line_9, line_10], tag=40)
        logger.debug(f"BC: Side tags are {[line_9, line_10]}")
        gmsh.model.geo.addPhysicalGroup(1, [spline_up, spline_le, spline_low], tag=30)
        logger.debug(f"BC: Wall tags are {[spline_up, spline_le, spline_low]}")

__init__(config: dict, datfile: str = '')

Instantiates the BlockMesh object.

Input

  • config (dict): the config file dictionary.
  • dat_file (str): path to input_geometry.dat.
Source code in aero_optim/mesh/naca_block_mesh.py
14
15
16
17
18
19
20
21
22
23
def __init__(self, config: dict, datfile: str = ""):
    """
    Instantiates the BlockMesh object.

    **Input**

    - config (dict): the config file dictionary.
    - dat_file (str): path to input_geometry.dat.
    """
    super().__init__(config, datfile)

build_2dmesh()

Builds the surface mesh of the computational domain.

Inner

  • R (float): radius of the outer circle.
  • d_out (float): distance to the outlet.
  • offset (int): offset from leading edge.
  • b_width (float): block_width.
  • n_inlet (int): nbr of lead edge & inlet nodes.
  • r_inlet (float): bump ratio of lead edge & inlet nodes.
  • n_vertical (int) : nbr of out & verti nodes.
  • r_vertical (float): out & vert growth.
  • n_airfoil (int): nbr of nodes on each sides.
  • r_airfoil (float): airfoil sides growth.
  • n_wake (int): nbr of nodes in the wake dir.
  • r_wake (float): wake growth.
Source code in aero_optim/mesh/naca_block_mesh.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
def build_2dmesh(self):
    """
    **Builds** the surface mesh of the computational domain.

    **Inner**

    - R (float): radius of the outer circle.
    - d_out (float): distance to the outlet.
    - offset (int): offset from leading edge.
    - b_width (float): block_width.
    - n_inlet (int): nbr of lead edge & inlet nodes.
    - r_inlet (float): bump ratio of lead edge & inlet nodes.
    - n_vertical (int) : nbr of out & verti nodes.
    - r_vertical (float): out & vert growth.
    - n_airfoil (int): nbr of nodes on each sides.
    - r_airfoil (float): airfoil sides growth.
    - n_wake (int): nbr of nodes in the wake dir.
    - r_wake (float): wake growth.
    """
    R = self.dinlet
    d_out = self.doutlet
    offset = self.config["gmsh"]["domain"].get("le_offset", 10)
    b_width = self.config["gmsh"]["domain"].get("block_width", 10)
    n_inlet = self.config["gmsh"]["mesh"].get("n_inlet", 60)
    r_inlet = self.config["gmsh"]["mesh"].get("r_inlet", 1.)
    n_vertical = self.config["gmsh"]["mesh"].get("n_vertical", 90)
    r_vertical = self.config["gmsh"]["mesh"].get("r_vertical", 1 / 0.95)
    n_airfoil = self.config["gmsh"]["mesh"].get("n_airfoil", 50)
    r_airfoil = self.config["gmsh"]["mesh"].get("r_airfoil", 1)
    n_wake = self.config["gmsh"]["mesh"].get("n_wake", 100)
    r_wake = self.config["gmsh"]["mesh"].get("r_wake", 1 / 0.95)

    _, self.idx_le = min((p[0], idx) for (idx, p) in enumerate(self.pts))
    _, self.idx_te = max((p[0], idx) for (idx, p) in enumerate(self.pts))
    u_side, l_side = self.split_naca()

    # add points and lines for the naca lower and upper parts
    x_le, y_le = self.pts[self.idx_le][:2]
    x_te, y_te = self.pts[self.idx_te][:2]
    te_tag = gmsh.model.geo.addPoint(x_te, y_te, 0.)
    le_tag = gmsh.model.geo.addPoint(x_le, y_le, 0.)
    pt_u = [te_tag] + [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in u_side]
    pt_l = [le_tag] +\
           [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in l_side] + [te_tag]

    # airfoil boundary
    spline_low = gmsh.model.geo.addSpline(pt_l[offset:], tag=1)
    spline_le = gmsh.model.geo.addSpline(pt_u[-offset:] + pt_l[:offset + 1], tag=2)
    spline_up = gmsh.model.geo.addSpline(pt_u[:-offset + 1], tag=3)

    # domain and block construction points
    pt_229 = gmsh.model.geo.addPoint(x_te - b_width, R, 0., tag=229)
    pt_230 = gmsh.model.geo.addPoint(x_te - b_width, -R, 0., tag=230)
    pt_231 = gmsh.model.geo.addPoint(x_te, R, 0., tag=231)
    pt_232 = gmsh.model.geo.addPoint(x_te, -R, 0., tag=232)
    pt_233 = gmsh.model.geo.addPoint(d_out, R, 0., tag=233)
    pt_234 = gmsh.model.geo.addPoint(d_out, -R, 0., tag=234)
    pt_235 = gmsh.model.geo.addPoint(d_out, 0, 0., tag=235)

    # domain and block lines
    circle_4 = gmsh.model.geo.addCircleArc(pt_230, te_tag, pt_229)
    line_5 = gmsh.model.geo.addLine(pt_u[-offset], pt_229)
    line_6 = gmsh.model.geo.addLine(pt_l[offset], pt_230)
    line_7 = gmsh.model.geo.addLine(pt_229, pt_231)
    line_8 = gmsh.model.geo.addLine(pt_230, pt_232)
    line_9 = gmsh.model.geo.addLine(pt_231, pt_233)
    line_10 = gmsh.model.geo.addLine(pt_232, pt_234)
    line_11 = gmsh.model.geo.addLine(pt_235, pt_234)
    line_12 = gmsh.model.geo.addLine(pt_235, pt_233)
    line_13 = gmsh.model.geo.addLine(te_tag, pt_231)
    line_14 = gmsh.model.geo.addLine(te_tag, pt_232)
    line_15 = gmsh.model.geo.addLine(te_tag, pt_235)

    # meshing parameters
    gmsh.model.geo.mesh.setTransfiniteCurve(circle_4, n_inlet, "Bump", r_inlet)
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_le, n_inlet, "Bump", r_inlet)
    _ = [gmsh.model.geo.mesh.setTransfiniteCurve(lid, n_vertical, "Progression", r_vertical)
         for lid in [line_5, line_6, line_11, line_12, line_13, line_14]]
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_low, n_airfoil, "Bump", r_airfoil)
    gmsh.model.geo.mesh.setTransfiniteCurve(spline_up, n_airfoil, "Bump", r_airfoil)
    gmsh.model.geo.mesh.setTransfiniteCurve(line_7, n_airfoil, "Bump", r_airfoil)
    gmsh.model.geo.mesh.setTransfiniteCurve(line_8, n_airfoil, "Bump", r_airfoil)
    gmsh.model.geo.mesh.setTransfiniteCurve(line_15, n_wake, "Progression", r_wake)
    gmsh.model.geo.mesh.setTransfiniteCurve(line_9, n_wake, "Progression", r_wake)
    gmsh.model.geo.mesh.setTransfiniteCurve(line_10, n_wake, "Progression", r_wake)

    # domain and block surfaces
    cloop_1 = gmsh.model.geo.addCurveLoop([circle_4, -line_5, spline_le, line_6])
    surf_1 = gmsh.model.geo.addPlaneSurface([-cloop_1], tag=1001)
    gmsh.model.geo.mesh.setTransfiniteSurface(surf_1)

    cloop_2 = gmsh.model.geo.addCurveLoop([line_5, line_7, -line_13, spline_up])
    surf_2 = gmsh.model.geo.addPlaneSurface([-cloop_2], tag=1002)
    gmsh.model.geo.mesh.setTransfiniteSurface(surf_2)

    cloop_3 = gmsh.model.geo.addCurveLoop([line_13, line_9, -line_12, -line_15])
    surf_3 = gmsh.model.geo.addPlaneSurface([-cloop_3], tag=1003)
    gmsh.model.geo.mesh.setTransfiniteSurface(surf_3)

    cloop_4 = gmsh.model.geo.addCurveLoop([line_6, line_8, -line_14, -spline_low])
    surf_4 = gmsh.model.geo.addPlaneSurface([-cloop_4], tag=1004)
    gmsh.model.geo.mesh.setTransfiniteSurface(surf_4)

    cloop_5 = gmsh.model.geo.addCurveLoop([line_14, line_10, -line_11, -line_15])
    surf_5 = gmsh.model.geo.addPlaneSurface([-cloop_5], tag=1005)
    gmsh.model.geo.mesh.setTransfiniteSurface(surf_5)

    # physical groups
    self.surf_tag = [surf_1, surf_2, surf_3, -surf_5, -surf_4]
    gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100)
    gmsh.model.geo.addPhysicalGroup(1, [circle_4, line_7, line_8], tag=10)
    logger.debug(f"BC: Inlet tags are {[circle_4, line_7, line_8]}")
    gmsh.model.geo.addPhysicalGroup(1, [line_11, line_12], tag=20)
    logger.debug(f"BC: Outlet tags are {[line_11, line_12]}")
    gmsh.model.geo.addPhysicalGroup(1, [line_9, line_10], tag=40)
    logger.debug(f"BC: Side tags are {[line_9, line_10]}")
    gmsh.model.geo.addPhysicalGroup(1, [spline_up, spline_le, spline_low], tag=30)
    logger.debug(f"BC: Wall tags are {[spline_up, spline_le, spline_low]}")

mesh.cascade_mesh.CascadeMesh

Bases: Mesh

This class implements a mesh routine for a compressor cascade geometry.

Source code in aero_optim/mesh/cascade_mesh.py
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
class CascadeMesh(Mesh):
    """
    This class implements a mesh routine for a compressor cascade geometry.
    """
    def __init__(self, config: dict, datfile: str = ""):
        """
        Instantiates the CascadeMesh object.

        **Input**

        - config (dict): the config file dictionary.
        - dat_file (str): path to input_geometry.dat.

        **Inner**

        - doutlet (float): outlet distance to the blade trailing edge.
        - dlr_mesh (bool): builds the DLR provided mesh (True) or a simpler for adaptation (False).
        - bl_sizefar (float): boundary layer mesh size far from the curves.
        - nodes_inlet (int): the number of nodes to mesh the inlet.
        - nodes_outlet (int): the number of nodes to mesh the outlet.
        - snodes_inlet (int): the number of nodes to mesh the inlet top and bottom sides.
        - snodes_outlet (int): the number of nodes to mesh the outlet top and bottom sides.
        - c_snodes (int): the number of nodes to mesh the inner sides.
        - le (int): the number of nodes to mesh the blade leading edge portion.
        - te (int): the number of nodes to mesh the blade trailing edge lower portion.
        - nodes_sp2 (int): the number of nodes to mesh the 1st section of the blade suction side.
        - nodes_sp3 (int): the number of nodes to mesh the 2nd section of the blade suction side.
        - nodes_sp4 (int): the number of nodes to mesh the 3rd section of the blade suction side.
        - nodes_sp7 (int): the number of nodes to mesh the 1st section of the blade pressure side.
        - nodes_sp8 (int): the number of nodes to mesh the 2nd section of the blade pressure side.
        - nodes_ss (int): the number of nodes to mesh the suction side (dlr_mesh set to False).
        - nodes_ps (int): the number of nodes to mesh the pressure side (dlr_mesh set to False).
        - cyl_vin (float): cylinder field parameter Vin.
        - cyl_vout (float): cylinder field parameter Vout.
        - cyl_xaxis (float): cylinder field parameter Xaxis.
        - cyl_xcenter (float): cylinder field parameter Xcenter.

        Note:
            for the DLR configuration, the blade is split into 9 splines (clockwise from the tip):

            * 2 splines (1 and 9) for the leading edge parameterized with **le**
              i.e. each spline has **le**/2 nodes,
            * 2 splines (5 and 6) for the trailing edge parameterized with **te**
              i.e. each spline has **te**/2 nodes,
            * 3 splines for the suction side (2, 3, 4) of lengths 0.027, 0.038 and 0.061 m,
              and parameterized with **nodes_sp2**, **nodes_sp3** and **nodes_sp4**,
            * 2 splines for the pressure side (7, 8) of lengths 0.0526 and 0.0167 m,
              and parameterized with **nodes_sp7**, **nodes_sp8**

        """
        super().__init__(config, datfile)
        self.doutlet: float = self.config["gmsh"]["domain"].get("outlet", 6.3e-2)
        self.dlr_mesh: bool = config["gmsh"]["mesh"].get("DLR_mesh", False)
        self.bl_sizefar: float = config["gmsh"]["mesh"].get("bl_sizefar", 1e-5)
        self.nodes_inlet: int = self.config["gmsh"]["mesh"].get("nodes_inlet", 25)
        self.nodes_outlet: int = self.config["gmsh"]["mesh"].get("nodes_outlet", 17)
        self.snodes_inlet: int = self.config["gmsh"]["mesh"].get("side_nodes_inlet", 31)
        self.snodes_outlet: int = self.config["gmsh"]["mesh"].get("side_nodes_outlet", 31)
        self.c_snodes: int = self.config["gmsh"]["mesh"].get("curved_side_nodes", 7)
        self.le: int = self.config["gmsh"]["mesh"].get("le", 16)
        self.te: int = self.config["gmsh"]["mesh"].get("te", 16)
        self.nodes_sp2: int = self.config["gmsh"]["mesh"].get("nodes_sp2", 42)
        self.nodes_sp3: int = self.config["gmsh"]["mesh"].get("nodes_sp3", 42)
        self.nodes_sp4: int = self.config["gmsh"]["mesh"].get("nodes_sp4", 14)
        self.nodes_sp7: int = self.config["gmsh"]["mesh"].get("nodes_sp7", 57)
        self.nodes_sp8: int = self.config["gmsh"]["mesh"].get("nodes_sp8", 32)
        self.nodes_ss: int = self.config["gmsh"]["mesh"].get("nodes_ss", 400)
        self.nodes_ps: int = self.config["gmsh"]["mesh"].get("nodes_ps", 400)
        self.cyl_vin: float = self.config["gmsh"]["mesh"].get("cyl_vin", 8e-4)
        self.cyl_vout: float = self.config["gmsh"]["mesh"].get("cyl_vout", 5e-3)
        self.cyl_xaxis: float = self.config["gmsh"]["mesh"].get("cyl_xaxis", 1.675e-2)
        self.cyl_xcenter: float = self.config["gmsh"]["mesh"].get("cyl_xcenter", 8.364e-2)

    def process_config(self):
        logger.debug("processing config..")
        if "domain" not in self.config["gmsh"]:
            logger.debug(f"no <domain> entry in {self.config['gmsh']}, empty entry added")
            self.config["gmsh"]["domain"] = {}

    def reorder_blade(self) -> list[list[float]]:
        """
        **Returns** the blade profile after reordering.
        """
        d = np.sqrt([abs(x**2 + y**2) for x, y, _ in self.pts])
        start = np.argmin(d)
        if self.pts[start + 1][1] > self.pts[start][1]:
            return [[p[0], p[1], p[2]] for p in self.pts[start:] + self.pts[:start]]
        else:
            return [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[start:]]

    def build_bl(self, blade_tag: list[int]):
        """
        **Builds** the boundary layer around the blade.
        """
        f_bl = gmsh.model.mesh.field.add('BoundaryLayer')
        gmsh.model.mesh.field.setNumbers(f_bl, 'CurvesList', blade_tag)
        gmsh.model.mesh.field.setNumber(f_bl, 'Size', self.bl_size)
        gmsh.model.mesh.field.setNumber(f_bl, 'Ratio', self.bl_ratio)
        gmsh.model.mesh.field.setNumber(f_bl, 'Quads', int(self.structured))
        gmsh.model.mesh.field.setNumber(f_bl, 'Thickness', self.bl_thickness)
        gmsh.model.mesh.field.setNumber(f_bl, 'SizeFar', self.bl_sizefar)
        gmsh.model.mesh.field.setAsBoundaryLayer(f_bl)

    def build_cylinder_field(
            self, radius: float, VIn: float, VOut: float,
            XAxis: float, XCenter: float,
            YAxis: float, YCenter: float,
            ZAxis: float = 0.
    ) -> int:
        """
        **Builds** a cylinder field in the computational domain.
        """
        f_cyl = gmsh.model.mesh.field.add('Cylinder')
        gmsh.model.mesh.field.setNumber(f_cyl, 'Radius', radius)
        gmsh.model.mesh.field.setNumber(f_cyl, 'VIn', VIn)
        gmsh.model.mesh.field.setNumber(f_cyl, 'VOut', VOut)
        gmsh.model.mesh.field.setNumber(f_cyl, 'XAxis', XAxis)
        gmsh.model.mesh.field.setNumber(f_cyl, 'XCenter', XCenter)
        gmsh.model.mesh.field.setNumber(f_cyl, 'YAxis', YAxis)
        gmsh.model.mesh.field.setNumber(f_cyl, 'YCenter', YCenter)
        gmsh.model.mesh.field.setNumber(f_cyl, 'ZAxis', ZAxis)
        gmsh.model.mesh.field.setAsBackgroundMesh(f_cyl)
        return f_cyl

    def build_minaniso_field(self, tag: list[int]):
        """
        **Builds** a MinAniso field in the computational domain.
        """
        f_minaniso = gmsh.model.mesh.field.add('MinAniso')
        gmsh.model.mesh.field.setNumbers(f_minaniso, 'FieldsList', tag)
        gmsh.model.mesh.field.setAsBackgroundMesh(f_minaniso)

    def build_2dmesh(self):
        """
        **Builds** the surface mesh of the computational domain.
        """
        wall = self.reorder_blade()
        pt_wall = [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in wall]

        # blade splines and transfinite curves
        if self.dlr_mesh:
            spl_1 = gmsh.model.geo.addSpline(pt_wall[:35])
            spl_2 = gmsh.model.geo.addSpline(pt_wall[35 - 1:88])
            spl_3 = gmsh.model.geo.addSpline(pt_wall[88 - 1:129])
            spl_4 = gmsh.model.geo.addSpline(pt_wall[129 - 1:157])
            spl_5 = gmsh.model.geo.addSpline(pt_wall[157 - 1:168])
            spl_6 = gmsh.model.geo.addSpline(pt_wall[168 - 1:179])
            spl_7 = gmsh.model.geo.addSpline(pt_wall[179 - 1:245])
            spl_8 = gmsh.model.geo.addSpline(pt_wall[245 - 1:287])
            spl_9 = gmsh.model.geo.addSpline(pt_wall[287 - 1:322] + [pt_wall[0]])
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_1, self.le // 2, "Progression", 1.02)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_2, self.nodes_sp2, "Progression", 1.03)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_3, self.nodes_sp3, "Progression", 1)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_4, self.nodes_sp4, "Progression", 0.94)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_5, self.te // 2, "Progression", 0.97)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_6, self.te // 2, "Progression", 1.025)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_7, self.nodes_sp7, "Progression", 1.015)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_8, self.nodes_sp8, "Progression", 0.955)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_9, self.le // 2, "Progression", 0.9)
            spl_list = [spl_1, spl_2, spl_3, spl_4, spl_5, spl_6, spl_7, spl_8, spl_9]
        else:
            spl_le = gmsh.model.geo.addSpline(pt_wall[287 - 1:322] + [pt_wall[0]] + pt_wall[:35])
            spl_ss = gmsh.model.geo.addSpline(
                pt_wall[35 - 1:88] + pt_wall[88 - 1:129] + pt_wall[129 - 1:157]
            )
            spl_te = gmsh.model.geo.addSpline(pt_wall[157 - 1:168] + pt_wall[168 - 1:179])
            spl_ps = gmsh.model.geo.addSpline(pt_wall[179 - 1:245] + pt_wall[245 - 1:287])
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_le, self.le, "Progression", 1.0)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_ss, self.nodes_ss, "Progression", 1.0)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_te, self.te, "Progression", 1)
            gmsh.model.geo.mesh.setTransfiniteCurve(spl_ps, self.nodes_ps, "Progression", 1.0)
            spl_list = [spl_le, spl_ss, spl_te, spl_ps]
        blade_loop = gmsh.model.geo.addCurveLoop(spl_list)

        # domain construction points
        pt_323 = gmsh.model.geo.addPoint(-6e-2, -5e-2, 0.)
        pt_324 = gmsh.model.geo.addPoint(-1.5e-2, -2.5e-2, 0.)
        pt_325 = gmsh.model.geo.addPoint(0., -1.7e-2, 0.)
        pt_326 = gmsh.model.geo.addPoint(1.270973e-02, -1.164466e-02, 0.)
        pt_327 = gmsh.model.geo.addPoint(2.585445e-02, -7.360298e-03, 0.)
        pt_328 = gmsh.model.geo.addPoint(3.934429e-02, -4.053609e-03, 0.)
        pt_329 = gmsh.model.geo.addPoint(5.308943e-02, -1.631280e-03, 0.)
        pt_330 = gmsh.model.geo.addPoint(6.7e-2, 0., 0.)
        pt_331 = gmsh.model.geo.addPoint(6.7e-2 + self.doutlet, 0., 0.)
        pt_332 = gmsh.model.geo.addPoint(6.7e-2 + self.doutlet, 4.039e-2, 0.)
        pt_333 = gmsh.model.geo.addPoint(6.7e-2, 4.039e-2, 0.)
        pt_334 = gmsh.model.geo.addPoint(5.308943e-02, 3.875872e-02, 0.)
        pt_335 = gmsh.model.geo.addPoint(3.934429e-02, 3.633639e-02, 0.)
        pt_336 = gmsh.model.geo.addPoint(2.585445e-02, 3.302970e-02, 0.)
        pt_337 = gmsh.model.geo.addPoint(1.270973e-02, 2.874534e-02, 0.)
        pt_338 = gmsh.model.geo.addPoint(0., 2.339e-2, 0.)
        pt_339 = gmsh.model.geo.addPoint(-1.5e-2, 1.539e-2, 0.)
        pt_340 = gmsh.model.geo.addPoint(-6e-2, -9.61e-3, 0.)

        # domain construction lines
        l_10 = gmsh.model.geo.addLine(pt_340, pt_323, tag=10)
        l_11 = gmsh.model.geo.addLine(pt_323, pt_324, tag=11)
        l_12 = gmsh.model.geo.addLine(pt_339, pt_340, tag=12)
        if self.dlr_mesh:
            l_13 = gmsh.model.geo.addLine(pt_324, pt_339, tag=13)
        l_14 = gmsh.model.geo.addLine(pt_324, pt_325, tag=14)
        l_15 = gmsh.model.geo.addLine(pt_325, pt_326, tag=15)
        l_16 = gmsh.model.geo.addLine(pt_326, pt_327, tag=16)
        l_17 = gmsh.model.geo.addLine(pt_327, pt_328, tag=17)
        l_18 = gmsh.model.geo.addLine(pt_328, pt_329, tag=18)
        l_19 = gmsh.model.geo.addLine(pt_329, pt_330, tag=19)
        l_20 = gmsh.model.geo.addLine(pt_330, pt_331, tag=20)
        l_21 = gmsh.model.geo.addLine(pt_331, pt_332, tag=21)
        l_22 = gmsh.model.geo.addLine(pt_332, pt_333, tag=22)
        l_23 = gmsh.model.geo.addLine(pt_333, pt_334, tag=23)
        l_24 = gmsh.model.geo.addLine(pt_334, pt_335, tag=24)
        l_25 = gmsh.model.geo.addLine(pt_335, pt_336, tag=25)
        l_26 = gmsh.model.geo.addLine(pt_336, pt_337, tag=26)
        l_27 = gmsh.model.geo.addLine(pt_337, pt_338, tag=27)
        l_28 = gmsh.model.geo.addLine(pt_338, pt_339, tag=28)

        # transfinite curves on non-blade boundaries
        gmsh.model.geo.mesh.setTransfiniteCurve(l_10, self.nodes_inlet, "Progression", 1.)
        if self.dlr_mesh:
            gmsh.model.geo.mesh.setTransfiniteCurve(l_13, self.nodes_inlet, "Progression", 1.)
        gmsh.model.geo.mesh.setTransfiniteCurve(l_21, self.nodes_outlet, "Progression", 1.)
        # bottom / top periodicity
        self.bottom_tags = [l_11, l_14, l_15, l_16, l_17, l_18, l_19, l_20]
        self.top_tags = [l_12, l_28, l_27, l_26, l_25, l_24, l_23, l_22]
        # bottom non-curved side nodes
        gmsh.model.geo.mesh.setTransfiniteCurve(l_11, self.snodes_inlet, "Progression", 1.)
        gmsh.model.geo.mesh.setTransfiniteCurve(l_20, self.snodes_outlet, "Progression", 1.)
        # bottom curved side nodes
        _ = [gmsh.model.geo.mesh.setTransfiniteCurve(l_i, self.c_snodes, "Progression", 1.)
             for l_i in self.bottom_tags[1:-1]]
        # periodic boundaries /y direction
        gmsh.model.geo.synchronize()
        translation = [1, 0, 0, 0, 0, 1, 0, 0.04039, 0, 0, 1, 0, 0, 0, 0, 1]
        for tid, bid in zip(self.top_tags, self.bottom_tags):
            gmsh.model.mesh.setPeriodic(1, [tid], [bid], translation)

        # closed curve loop and computational domain surface definition
        if self.dlr_mesh:
            cloop_2 = gmsh.model.geo.addCurveLoop([l_10, l_11, l_12, l_13])
            cloop_3 = gmsh.model.geo.addCurveLoop(
                [-l_13, l_14, l_15, l_16, l_17, l_18, l_19, l_20,
                 l_21, l_22, l_23, l_24, l_25, l_26, l_27, l_28,]
            )
            surf_1 = gmsh.model.geo.addPlaneSurface([cloop_2], tag=1002)
            surf_2 = gmsh.model.geo.addPlaneSurface([cloop_3, blade_loop], tag=1003)
            gmsh.model.geo.mesh.setTransfiniteSurface(surf_1)
        else:
            cloop_3 = gmsh.model.geo.addCurveLoop(
                [l_10, l_11, l_14, l_15, l_16, l_17, l_18, l_19, l_20,
                 l_21, l_22, l_23, l_24, l_25, l_26, l_27, l_28, l_12]
            )
            surf_2 = gmsh.model.geo.addPlaneSurface([cloop_3, blade_loop], tag=1003)

        # Fields definition
        # Boundary Layer
        self.build_bl(spl_list) if self.bl else 0
        # Cylinder #1
        f_cyl1 = self.build_cylinder_field(
            9e-3,
            self.cyl_vin,
            self.cyl_vout,
            self.cyl_xaxis,
            self.cyl_xcenter,
            -1.171e-3,
            1.9754e-2
        )
        # Cylinder #2
        f_cyl2 = self.build_cylinder_field(
            1.62e-2, 1.6e-3, 5e-3, 2.01e-2, 8.699e-2, -1.406e-3, 1.9519e-2
        )
        # MinAniso
        self.build_minaniso_field([f_cyl1, f_cyl2])

        # define physical groups for boundary conditions
        self.surf_tag = [surf_1, surf_2] if self.dlr_mesh else [surf_2]
        if self.extrusion_layers == 0:
            gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100, name="fluid")
            gmsh.model.geo.addPhysicalGroup(1, [l_10], tag=10, name="inlet")
            logger.debug(f"2D BC: Inlet tags are {[l_10]}")
            gmsh.model.geo.addPhysicalGroup(1, [l_21], tag=20, name="outlet")
            logger.debug(f"2D BC: Outlet tags are {[l_21]}")
            gmsh.model.geo.addPhysicalGroup(1, spl_list, tag=30, name="wall")
            logger.debug(f"2D BC: Wall tags are {spl_list}")
            gmsh.model.geo.addPhysicalGroup(1, self.top_tags, tag=40, name="periodic_vert_l")
            logger.debug(f"2D BC: Top tags are {self.top_tags}")
            gmsh.model.geo.addPhysicalGroup(1, self.bottom_tags, tag=50, name="periodic_vert_r")
            logger.debug(f"2D BC: Bottom tags are {self.bottom_tags}")
        else:
            gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100, name="periodic_span_l")

        # non-corner points defined as flow-field, inner block line and wall nodes
        self.non_corner_tags.extend([abs(s_tag) for s_tag in self.surf_tag])
        self.non_corner_tags.extend([abs(s_tag) for s_tag in spl_list])
        if self.dlr_mesh:
            self.non_corner_tags.append(abs(l_13))

    def build_3dmesh(self):
        """
        **Performs** an extrusion along the z axis.
        - h_size (float): the total extruded depth.
        """
        h_size = self.extrusion_size
        self.ext_tag = [gmsh.model.geo.extrude(
            [(2, s)], 0, 0, h_size, [self.extrusion_layers], [1], True) for s in self.surf_tag]
        # retrieve extruded surfaces and volumes
        vol = [tu[-1] for tu in [self.ext_tag[0][1], self.ext_tag[1][1]]]
        top = [tu[-1] for tu in [self.ext_tag[0][0], self.ext_tag[1][0]]]
        # 1st block
        inlet = [self.ext_tag[0][2][-1]]
        perlo = [self.ext_tag[0][3][-1]]
        perup = [self.ext_tag[0][5][-1]]
        # 2nd block
        perlo += [tu[-1] for tu in self.ext_tag[1][3:10]]
        outlet = [self.ext_tag[1][10][-1]]
        perup += [tu[-1] for tu in self.ext_tag[1][11:18]]
        wall = [tu[-1] for tu in self.ext_tag[1][18:]]
        # create physical groups
        gmsh.model.geo.addPhysicalGroup(3, vol, tag=2000, name="fluid")
        logger.debug("3D BC: vol tag is 2000")
        gmsh.model.geo.addPhysicalGroup(2, top, tag=2001, name="periodic_span_r")
        logger.debug("3D BC: periodic_span_l tag is 100")
        logger.debug("3D BC: periodic_span_r tag is 2001")
        gmsh.model.geo.addPhysicalGroup(2, perlo, tag=2003, name="periodic_vert_l")
        logger.debug("3D BC: periodic_vert_l tag is 2003")
        gmsh.model.geo.addPhysicalGroup(2, perup, tag=2004, name="periodic_vert_r")
        logger.debug("3D BC: periodic_vert_r tag is 2004")
        gmsh.model.geo.addPhysicalGroup(2, inlet, tag=2005, name="inlet")
        logger.debug("3D BC: inlet tag is 2005")
        gmsh.model.geo.addPhysicalGroup(2, outlet, tag=2006, name="outlet")
        logger.debug("3D BC: outlet tag is 2006")
        gmsh.model.geo.addPhysicalGroup(2, wall, tag=2007, name="wall")
        logger.debug("3D BC: wall tag is 2007")
        # set 2 tags to none to prevent reformatting
        self.non_corner_tags = None
        self.bottom_tags = None
        self.top_tags = None

__init__(config: dict, datfile: str = '')

Instantiates the CascadeMesh object.

Input

  • config (dict): the config file dictionary.
  • dat_file (str): path to input_geometry.dat.

Inner

  • doutlet (float): outlet distance to the blade trailing edge.
  • dlr_mesh (bool): builds the DLR provided mesh (True) or a simpler for adaptation (False).
  • bl_sizefar (float): boundary layer mesh size far from the curves.
  • nodes_inlet (int): the number of nodes to mesh the inlet.
  • nodes_outlet (int): the number of nodes to mesh the outlet.
  • snodes_inlet (int): the number of nodes to mesh the inlet top and bottom sides.
  • snodes_outlet (int): the number of nodes to mesh the outlet top and bottom sides.
  • c_snodes (int): the number of nodes to mesh the inner sides.
  • le (int): the number of nodes to mesh the blade leading edge portion.
  • te (int): the number of nodes to mesh the blade trailing edge lower portion.
  • nodes_sp2 (int): the number of nodes to mesh the 1st section of the blade suction side.
  • nodes_sp3 (int): the number of nodes to mesh the 2nd section of the blade suction side.
  • nodes_sp4 (int): the number of nodes to mesh the 3rd section of the blade suction side.
  • nodes_sp7 (int): the number of nodes to mesh the 1st section of the blade pressure side.
  • nodes_sp8 (int): the number of nodes to mesh the 2nd section of the blade pressure side.
  • nodes_ss (int): the number of nodes to mesh the suction side (dlr_mesh set to False).
  • nodes_ps (int): the number of nodes to mesh the pressure side (dlr_mesh set to False).
  • cyl_vin (float): cylinder field parameter Vin.
  • cyl_vout (float): cylinder field parameter Vout.
  • cyl_xaxis (float): cylinder field parameter Xaxis.
  • cyl_xcenter (float): cylinder field parameter Xcenter.
Note

for the DLR configuration, the blade is split into 9 splines (clockwise from the tip):

  • 2 splines (1 and 9) for the leading edge parameterized with le i.e. each spline has le/2 nodes,
  • 2 splines (5 and 6) for the trailing edge parameterized with te i.e. each spline has te/2 nodes,
  • 3 splines for the suction side (2, 3, 4) of lengths 0.027, 0.038 and 0.061 m, and parameterized with nodes_sp2, nodes_sp3 and nodes_sp4,
  • 2 splines for the pressure side (7, 8) of lengths 0.0526 and 0.0167 m, and parameterized with nodes_sp7, nodes_sp8
Source code in aero_optim/mesh/cascade_mesh.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
def __init__(self, config: dict, datfile: str = ""):
    """
    Instantiates the CascadeMesh object.

    **Input**

    - config (dict): the config file dictionary.
    - dat_file (str): path to input_geometry.dat.

    **Inner**

    - doutlet (float): outlet distance to the blade trailing edge.
    - dlr_mesh (bool): builds the DLR provided mesh (True) or a simpler for adaptation (False).
    - bl_sizefar (float): boundary layer mesh size far from the curves.
    - nodes_inlet (int): the number of nodes to mesh the inlet.
    - nodes_outlet (int): the number of nodes to mesh the outlet.
    - snodes_inlet (int): the number of nodes to mesh the inlet top and bottom sides.
    - snodes_outlet (int): the number of nodes to mesh the outlet top and bottom sides.
    - c_snodes (int): the number of nodes to mesh the inner sides.
    - le (int): the number of nodes to mesh the blade leading edge portion.
    - te (int): the number of nodes to mesh the blade trailing edge lower portion.
    - nodes_sp2 (int): the number of nodes to mesh the 1st section of the blade suction side.
    - nodes_sp3 (int): the number of nodes to mesh the 2nd section of the blade suction side.
    - nodes_sp4 (int): the number of nodes to mesh the 3rd section of the blade suction side.
    - nodes_sp7 (int): the number of nodes to mesh the 1st section of the blade pressure side.
    - nodes_sp8 (int): the number of nodes to mesh the 2nd section of the blade pressure side.
    - nodes_ss (int): the number of nodes to mesh the suction side (dlr_mesh set to False).
    - nodes_ps (int): the number of nodes to mesh the pressure side (dlr_mesh set to False).
    - cyl_vin (float): cylinder field parameter Vin.
    - cyl_vout (float): cylinder field parameter Vout.
    - cyl_xaxis (float): cylinder field parameter Xaxis.
    - cyl_xcenter (float): cylinder field parameter Xcenter.

    Note:
        for the DLR configuration, the blade is split into 9 splines (clockwise from the tip):

        * 2 splines (1 and 9) for the leading edge parameterized with **le**
          i.e. each spline has **le**/2 nodes,
        * 2 splines (5 and 6) for the trailing edge parameterized with **te**
          i.e. each spline has **te**/2 nodes,
        * 3 splines for the suction side (2, 3, 4) of lengths 0.027, 0.038 and 0.061 m,
          and parameterized with **nodes_sp2**, **nodes_sp3** and **nodes_sp4**,
        * 2 splines for the pressure side (7, 8) of lengths 0.0526 and 0.0167 m,
          and parameterized with **nodes_sp7**, **nodes_sp8**

    """
    super().__init__(config, datfile)
    self.doutlet: float = self.config["gmsh"]["domain"].get("outlet", 6.3e-2)
    self.dlr_mesh: bool = config["gmsh"]["mesh"].get("DLR_mesh", False)
    self.bl_sizefar: float = config["gmsh"]["mesh"].get("bl_sizefar", 1e-5)
    self.nodes_inlet: int = self.config["gmsh"]["mesh"].get("nodes_inlet", 25)
    self.nodes_outlet: int = self.config["gmsh"]["mesh"].get("nodes_outlet", 17)
    self.snodes_inlet: int = self.config["gmsh"]["mesh"].get("side_nodes_inlet", 31)
    self.snodes_outlet: int = self.config["gmsh"]["mesh"].get("side_nodes_outlet", 31)
    self.c_snodes: int = self.config["gmsh"]["mesh"].get("curved_side_nodes", 7)
    self.le: int = self.config["gmsh"]["mesh"].get("le", 16)
    self.te: int = self.config["gmsh"]["mesh"].get("te", 16)
    self.nodes_sp2: int = self.config["gmsh"]["mesh"].get("nodes_sp2", 42)
    self.nodes_sp3: int = self.config["gmsh"]["mesh"].get("nodes_sp3", 42)
    self.nodes_sp4: int = self.config["gmsh"]["mesh"].get("nodes_sp4", 14)
    self.nodes_sp7: int = self.config["gmsh"]["mesh"].get("nodes_sp7", 57)
    self.nodes_sp8: int = self.config["gmsh"]["mesh"].get("nodes_sp8", 32)
    self.nodes_ss: int = self.config["gmsh"]["mesh"].get("nodes_ss", 400)
    self.nodes_ps: int = self.config["gmsh"]["mesh"].get("nodes_ps", 400)
    self.cyl_vin: float = self.config["gmsh"]["mesh"].get("cyl_vin", 8e-4)
    self.cyl_vout: float = self.config["gmsh"]["mesh"].get("cyl_vout", 5e-3)
    self.cyl_xaxis: float = self.config["gmsh"]["mesh"].get("cyl_xaxis", 1.675e-2)
    self.cyl_xcenter: float = self.config["gmsh"]["mesh"].get("cyl_xcenter", 8.364e-2)

build_2dmesh()

Builds the surface mesh of the computational domain.

Source code in aero_optim/mesh/cascade_mesh.py
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
def build_2dmesh(self):
    """
    **Builds** the surface mesh of the computational domain.
    """
    wall = self.reorder_blade()
    pt_wall = [gmsh.model.geo.addPoint(p[0], p[1], p[2]) for p in wall]

    # blade splines and transfinite curves
    if self.dlr_mesh:
        spl_1 = gmsh.model.geo.addSpline(pt_wall[:35])
        spl_2 = gmsh.model.geo.addSpline(pt_wall[35 - 1:88])
        spl_3 = gmsh.model.geo.addSpline(pt_wall[88 - 1:129])
        spl_4 = gmsh.model.geo.addSpline(pt_wall[129 - 1:157])
        spl_5 = gmsh.model.geo.addSpline(pt_wall[157 - 1:168])
        spl_6 = gmsh.model.geo.addSpline(pt_wall[168 - 1:179])
        spl_7 = gmsh.model.geo.addSpline(pt_wall[179 - 1:245])
        spl_8 = gmsh.model.geo.addSpline(pt_wall[245 - 1:287])
        spl_9 = gmsh.model.geo.addSpline(pt_wall[287 - 1:322] + [pt_wall[0]])
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_1, self.le // 2, "Progression", 1.02)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_2, self.nodes_sp2, "Progression", 1.03)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_3, self.nodes_sp3, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_4, self.nodes_sp4, "Progression", 0.94)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_5, self.te // 2, "Progression", 0.97)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_6, self.te // 2, "Progression", 1.025)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_7, self.nodes_sp7, "Progression", 1.015)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_8, self.nodes_sp8, "Progression", 0.955)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_9, self.le // 2, "Progression", 0.9)
        spl_list = [spl_1, spl_2, spl_3, spl_4, spl_5, spl_6, spl_7, spl_8, spl_9]
    else:
        spl_le = gmsh.model.geo.addSpline(pt_wall[287 - 1:322] + [pt_wall[0]] + pt_wall[:35])
        spl_ss = gmsh.model.geo.addSpline(
            pt_wall[35 - 1:88] + pt_wall[88 - 1:129] + pt_wall[129 - 1:157]
        )
        spl_te = gmsh.model.geo.addSpline(pt_wall[157 - 1:168] + pt_wall[168 - 1:179])
        spl_ps = gmsh.model.geo.addSpline(pt_wall[179 - 1:245] + pt_wall[245 - 1:287])
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_le, self.le, "Progression", 1.0)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_ss, self.nodes_ss, "Progression", 1.0)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_te, self.te, "Progression", 1)
        gmsh.model.geo.mesh.setTransfiniteCurve(spl_ps, self.nodes_ps, "Progression", 1.0)
        spl_list = [spl_le, spl_ss, spl_te, spl_ps]
    blade_loop = gmsh.model.geo.addCurveLoop(spl_list)

    # domain construction points
    pt_323 = gmsh.model.geo.addPoint(-6e-2, -5e-2, 0.)
    pt_324 = gmsh.model.geo.addPoint(-1.5e-2, -2.5e-2, 0.)
    pt_325 = gmsh.model.geo.addPoint(0., -1.7e-2, 0.)
    pt_326 = gmsh.model.geo.addPoint(1.270973e-02, -1.164466e-02, 0.)
    pt_327 = gmsh.model.geo.addPoint(2.585445e-02, -7.360298e-03, 0.)
    pt_328 = gmsh.model.geo.addPoint(3.934429e-02, -4.053609e-03, 0.)
    pt_329 = gmsh.model.geo.addPoint(5.308943e-02, -1.631280e-03, 0.)
    pt_330 = gmsh.model.geo.addPoint(6.7e-2, 0., 0.)
    pt_331 = gmsh.model.geo.addPoint(6.7e-2 + self.doutlet, 0., 0.)
    pt_332 = gmsh.model.geo.addPoint(6.7e-2 + self.doutlet, 4.039e-2, 0.)
    pt_333 = gmsh.model.geo.addPoint(6.7e-2, 4.039e-2, 0.)
    pt_334 = gmsh.model.geo.addPoint(5.308943e-02, 3.875872e-02, 0.)
    pt_335 = gmsh.model.geo.addPoint(3.934429e-02, 3.633639e-02, 0.)
    pt_336 = gmsh.model.geo.addPoint(2.585445e-02, 3.302970e-02, 0.)
    pt_337 = gmsh.model.geo.addPoint(1.270973e-02, 2.874534e-02, 0.)
    pt_338 = gmsh.model.geo.addPoint(0., 2.339e-2, 0.)
    pt_339 = gmsh.model.geo.addPoint(-1.5e-2, 1.539e-2, 0.)
    pt_340 = gmsh.model.geo.addPoint(-6e-2, -9.61e-3, 0.)

    # domain construction lines
    l_10 = gmsh.model.geo.addLine(pt_340, pt_323, tag=10)
    l_11 = gmsh.model.geo.addLine(pt_323, pt_324, tag=11)
    l_12 = gmsh.model.geo.addLine(pt_339, pt_340, tag=12)
    if self.dlr_mesh:
        l_13 = gmsh.model.geo.addLine(pt_324, pt_339, tag=13)
    l_14 = gmsh.model.geo.addLine(pt_324, pt_325, tag=14)
    l_15 = gmsh.model.geo.addLine(pt_325, pt_326, tag=15)
    l_16 = gmsh.model.geo.addLine(pt_326, pt_327, tag=16)
    l_17 = gmsh.model.geo.addLine(pt_327, pt_328, tag=17)
    l_18 = gmsh.model.geo.addLine(pt_328, pt_329, tag=18)
    l_19 = gmsh.model.geo.addLine(pt_329, pt_330, tag=19)
    l_20 = gmsh.model.geo.addLine(pt_330, pt_331, tag=20)
    l_21 = gmsh.model.geo.addLine(pt_331, pt_332, tag=21)
    l_22 = gmsh.model.geo.addLine(pt_332, pt_333, tag=22)
    l_23 = gmsh.model.geo.addLine(pt_333, pt_334, tag=23)
    l_24 = gmsh.model.geo.addLine(pt_334, pt_335, tag=24)
    l_25 = gmsh.model.geo.addLine(pt_335, pt_336, tag=25)
    l_26 = gmsh.model.geo.addLine(pt_336, pt_337, tag=26)
    l_27 = gmsh.model.geo.addLine(pt_337, pt_338, tag=27)
    l_28 = gmsh.model.geo.addLine(pt_338, pt_339, tag=28)

    # transfinite curves on non-blade boundaries
    gmsh.model.geo.mesh.setTransfiniteCurve(l_10, self.nodes_inlet, "Progression", 1.)
    if self.dlr_mesh:
        gmsh.model.geo.mesh.setTransfiniteCurve(l_13, self.nodes_inlet, "Progression", 1.)
    gmsh.model.geo.mesh.setTransfiniteCurve(l_21, self.nodes_outlet, "Progression", 1.)
    # bottom / top periodicity
    self.bottom_tags = [l_11, l_14, l_15, l_16, l_17, l_18, l_19, l_20]
    self.top_tags = [l_12, l_28, l_27, l_26, l_25, l_24, l_23, l_22]
    # bottom non-curved side nodes
    gmsh.model.geo.mesh.setTransfiniteCurve(l_11, self.snodes_inlet, "Progression", 1.)
    gmsh.model.geo.mesh.setTransfiniteCurve(l_20, self.snodes_outlet, "Progression", 1.)
    # bottom curved side nodes
    _ = [gmsh.model.geo.mesh.setTransfiniteCurve(l_i, self.c_snodes, "Progression", 1.)
         for l_i in self.bottom_tags[1:-1]]
    # periodic boundaries /y direction
    gmsh.model.geo.synchronize()
    translation = [1, 0, 0, 0, 0, 1, 0, 0.04039, 0, 0, 1, 0, 0, 0, 0, 1]
    for tid, bid in zip(self.top_tags, self.bottom_tags):
        gmsh.model.mesh.setPeriodic(1, [tid], [bid], translation)

    # closed curve loop and computational domain surface definition
    if self.dlr_mesh:
        cloop_2 = gmsh.model.geo.addCurveLoop([l_10, l_11, l_12, l_13])
        cloop_3 = gmsh.model.geo.addCurveLoop(
            [-l_13, l_14, l_15, l_16, l_17, l_18, l_19, l_20,
             l_21, l_22, l_23, l_24, l_25, l_26, l_27, l_28,]
        )
        surf_1 = gmsh.model.geo.addPlaneSurface([cloop_2], tag=1002)
        surf_2 = gmsh.model.geo.addPlaneSurface([cloop_3, blade_loop], tag=1003)
        gmsh.model.geo.mesh.setTransfiniteSurface(surf_1)
    else:
        cloop_3 = gmsh.model.geo.addCurveLoop(
            [l_10, l_11, l_14, l_15, l_16, l_17, l_18, l_19, l_20,
             l_21, l_22, l_23, l_24, l_25, l_26, l_27, l_28, l_12]
        )
        surf_2 = gmsh.model.geo.addPlaneSurface([cloop_3, blade_loop], tag=1003)

    # Fields definition
    # Boundary Layer
    self.build_bl(spl_list) if self.bl else 0
    # Cylinder #1
    f_cyl1 = self.build_cylinder_field(
        9e-3,
        self.cyl_vin,
        self.cyl_vout,
        self.cyl_xaxis,
        self.cyl_xcenter,
        -1.171e-3,
        1.9754e-2
    )
    # Cylinder #2
    f_cyl2 = self.build_cylinder_field(
        1.62e-2, 1.6e-3, 5e-3, 2.01e-2, 8.699e-2, -1.406e-3, 1.9519e-2
    )
    # MinAniso
    self.build_minaniso_field([f_cyl1, f_cyl2])

    # define physical groups for boundary conditions
    self.surf_tag = [surf_1, surf_2] if self.dlr_mesh else [surf_2]
    if self.extrusion_layers == 0:
        gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100, name="fluid")
        gmsh.model.geo.addPhysicalGroup(1, [l_10], tag=10, name="inlet")
        logger.debug(f"2D BC: Inlet tags are {[l_10]}")
        gmsh.model.geo.addPhysicalGroup(1, [l_21], tag=20, name="outlet")
        logger.debug(f"2D BC: Outlet tags are {[l_21]}")
        gmsh.model.geo.addPhysicalGroup(1, spl_list, tag=30, name="wall")
        logger.debug(f"2D BC: Wall tags are {spl_list}")
        gmsh.model.geo.addPhysicalGroup(1, self.top_tags, tag=40, name="periodic_vert_l")
        logger.debug(f"2D BC: Top tags are {self.top_tags}")
        gmsh.model.geo.addPhysicalGroup(1, self.bottom_tags, tag=50, name="periodic_vert_r")
        logger.debug(f"2D BC: Bottom tags are {self.bottom_tags}")
    else:
        gmsh.model.geo.addPhysicalGroup(2, self.surf_tag, tag=100, name="periodic_span_l")

    # non-corner points defined as flow-field, inner block line and wall nodes
    self.non_corner_tags.extend([abs(s_tag) for s_tag in self.surf_tag])
    self.non_corner_tags.extend([abs(s_tag) for s_tag in spl_list])
    if self.dlr_mesh:
        self.non_corner_tags.append(abs(l_13))

build_3dmesh()

Performs an extrusion along the z axis. - h_size (float): the total extruded depth.

Source code in aero_optim/mesh/cascade_mesh.py
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
def build_3dmesh(self):
    """
    **Performs** an extrusion along the z axis.
    - h_size (float): the total extruded depth.
    """
    h_size = self.extrusion_size
    self.ext_tag = [gmsh.model.geo.extrude(
        [(2, s)], 0, 0, h_size, [self.extrusion_layers], [1], True) for s in self.surf_tag]
    # retrieve extruded surfaces and volumes
    vol = [tu[-1] for tu in [self.ext_tag[0][1], self.ext_tag[1][1]]]
    top = [tu[-1] for tu in [self.ext_tag[0][0], self.ext_tag[1][0]]]
    # 1st block
    inlet = [self.ext_tag[0][2][-1]]
    perlo = [self.ext_tag[0][3][-1]]
    perup = [self.ext_tag[0][5][-1]]
    # 2nd block
    perlo += [tu[-1] for tu in self.ext_tag[1][3:10]]
    outlet = [self.ext_tag[1][10][-1]]
    perup += [tu[-1] for tu in self.ext_tag[1][11:18]]
    wall = [tu[-1] for tu in self.ext_tag[1][18:]]
    # create physical groups
    gmsh.model.geo.addPhysicalGroup(3, vol, tag=2000, name="fluid")
    logger.debug("3D BC: vol tag is 2000")
    gmsh.model.geo.addPhysicalGroup(2, top, tag=2001, name="periodic_span_r")
    logger.debug("3D BC: periodic_span_l tag is 100")
    logger.debug("3D BC: periodic_span_r tag is 2001")
    gmsh.model.geo.addPhysicalGroup(2, perlo, tag=2003, name="periodic_vert_l")
    logger.debug("3D BC: periodic_vert_l tag is 2003")
    gmsh.model.geo.addPhysicalGroup(2, perup, tag=2004, name="periodic_vert_r")
    logger.debug("3D BC: periodic_vert_r tag is 2004")
    gmsh.model.geo.addPhysicalGroup(2, inlet, tag=2005, name="inlet")
    logger.debug("3D BC: inlet tag is 2005")
    gmsh.model.geo.addPhysicalGroup(2, outlet, tag=2006, name="outlet")
    logger.debug("3D BC: outlet tag is 2006")
    gmsh.model.geo.addPhysicalGroup(2, wall, tag=2007, name="wall")
    logger.debug("3D BC: wall tag is 2007")
    # set 2 tags to none to prevent reformatting
    self.non_corner_tags = None
    self.bottom_tags = None
    self.top_tags = None

build_bl(blade_tag: list[int])

Builds the boundary layer around the blade.

Source code in aero_optim/mesh/cascade_mesh.py
100
101
102
103
104
105
106
107
108
109
110
111
def build_bl(self, blade_tag: list[int]):
    """
    **Builds** the boundary layer around the blade.
    """
    f_bl = gmsh.model.mesh.field.add('BoundaryLayer')
    gmsh.model.mesh.field.setNumbers(f_bl, 'CurvesList', blade_tag)
    gmsh.model.mesh.field.setNumber(f_bl, 'Size', self.bl_size)
    gmsh.model.mesh.field.setNumber(f_bl, 'Ratio', self.bl_ratio)
    gmsh.model.mesh.field.setNumber(f_bl, 'Quads', int(self.structured))
    gmsh.model.mesh.field.setNumber(f_bl, 'Thickness', self.bl_thickness)
    gmsh.model.mesh.field.setNumber(f_bl, 'SizeFar', self.bl_sizefar)
    gmsh.model.mesh.field.setAsBoundaryLayer(f_bl)

build_cylinder_field(radius: float, VIn: float, VOut: float, XAxis: float, XCenter: float, YAxis: float, YCenter: float, ZAxis: float = 0.0) -> int

Builds a cylinder field in the computational domain.

Source code in aero_optim/mesh/cascade_mesh.py
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
def build_cylinder_field(
        self, radius: float, VIn: float, VOut: float,
        XAxis: float, XCenter: float,
        YAxis: float, YCenter: float,
        ZAxis: float = 0.
) -> int:
    """
    **Builds** a cylinder field in the computational domain.
    """
    f_cyl = gmsh.model.mesh.field.add('Cylinder')
    gmsh.model.mesh.field.setNumber(f_cyl, 'Radius', radius)
    gmsh.model.mesh.field.setNumber(f_cyl, 'VIn', VIn)
    gmsh.model.mesh.field.setNumber(f_cyl, 'VOut', VOut)
    gmsh.model.mesh.field.setNumber(f_cyl, 'XAxis', XAxis)
    gmsh.model.mesh.field.setNumber(f_cyl, 'XCenter', XCenter)
    gmsh.model.mesh.field.setNumber(f_cyl, 'YAxis', YAxis)
    gmsh.model.mesh.field.setNumber(f_cyl, 'YCenter', YCenter)
    gmsh.model.mesh.field.setNumber(f_cyl, 'ZAxis', ZAxis)
    gmsh.model.mesh.field.setAsBackgroundMesh(f_cyl)
    return f_cyl

build_minaniso_field(tag: list[int])

Builds a MinAniso field in the computational domain.

Source code in aero_optim/mesh/cascade_mesh.py
134
135
136
137
138
139
140
def build_minaniso_field(self, tag: list[int]):
    """
    **Builds** a MinAniso field in the computational domain.
    """
    f_minaniso = gmsh.model.mesh.field.add('MinAniso')
    gmsh.model.mesh.field.setNumbers(f_minaniso, 'FieldsList', tag)
    gmsh.model.mesh.field.setAsBackgroundMesh(f_minaniso)

reorder_blade() -> list[list[float]]

Returns the blade profile after reordering.

Source code in aero_optim/mesh/cascade_mesh.py
89
90
91
92
93
94
95
96
97
98
def reorder_blade(self) -> list[list[float]]:
    """
    **Returns** the blade profile after reordering.
    """
    d = np.sqrt([abs(x**2 + y**2) for x, y, _ in self.pts])
    start = np.argmin(d)
    if self.pts[start + 1][1] > self.pts[start][1]:
        return [[p[0], p[1], p[2]] for p in self.pts[start:] + self.pts[:start]]
    else:
        return [[p[0], p[1], p[2]] for p in self.pts[:start] + self.pts[start:]]