Example #1
0
pattern_ll = geo.remove_duplicates(pattern_ll)
logger.info("Removing of the line-loops duplicates : Done")
logger.info(f"pattern_ll length : {len(pattern_ll)}")
# * 13 no-redundant LineLoops to define the pantograph microstructure geometry in 1 cell.

for ll in pattern_ll:
    ll.round_corner_incircle(r)
logger.info("Rounding all corners of pattern line-loops : Done")

macro_vtcs = [O_pt, gen_vect[0], gen_vect[0] + gen_vect[1], gen_vect[1]]
macro_ll = geo.LineLoop([geo.Point(c) for c in macro_vtcs])
macro_s = geo.PlaneSurface(macro_ll)

logger.info("Start boolean operations on surfaces")
pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll]
rve_s = geo.surface_bool_cut(macro_s, pattern_s)
rve_s = rve_s[0]
logger.info("Boolean operations on surfaces : Done")
rve_s_phy = geo.PhysicalGroup([rve_s], 2, "partition_plein")
factory.synchronize()

rve_s_phy.add_gmsh()
factory.synchronize()
data = model.getPhysicalGroups()
logger.info(f"All physical groups in the model : \n {data}")
names = [model.getPhysicalName(*dimtag) for dimtag in data]
logger.info(f"Physical group names: \n {names}")

logger.info("Generate geometry model : Done")
gmsh.write(str(geo_file))
run(f"gmsh {str(geo_file)} &", shell=True, check=True)
Example #2
0
def test_get_domains_gmsh(plots=False):
    """ Get subdomains and partition of the boundary from a .msh file. """
    name = "test_domains"
    local_dir = Path(__file__).parent
    mesh_file = local_dir.joinpath(name + ".msh")
    gmsh.model.add(name)
    L_x, L_y = 2.0, 2.0
    H = 1.0
    vertices = [(0.0, 0.0), (0.0, L_y), (L_x, L_y), (L_x, 0.0)]
    contour = geo.LineLoop([geo.Point(np.array(c)) for c in vertices], False)
    surface = geo.PlaneSurface(contour)
    inclusion_vertices = list()
    for coord in [
        (H / 2, -H / 2, 0.0),
        (H / 2, H / 2, 0.0),
        (-H / 2, H / 2, 0.0),
        (-H / 2, -H / 2, 0.0),
    ]:
        vertex = geo.translation(geo.Point((L_x / 2, L_y / 2)), coord)
        inclusion_vertices.append(vertex)
    inclusion = geo.PlaneSurface(geo.LineLoop(inclusion_vertices, False))
    for s in [surface, inclusion]:
        s.add_gmsh()
    factory.synchronize()
    (stiff_s, ) = geo.surface_bool_cut(surface, inclusion)
    factory.synchronize()
    (soft_s, ) = geo.surface_bool_cut(surface, stiff_s)
    factory.synchronize()
    domains = {
        "stiff": geo.PhysicalGroup(stiff_s, 2),
        "soft": geo.PhysicalGroup(soft_s, 2),
    }
    boundaries = {
        "S": geo.PhysicalGroup(surface.ext_contour.sides[0], 1),
        "W": geo.PhysicalGroup(surface.ext_contour.sides[1], 1),
        "N": geo.PhysicalGroup(surface.ext_contour.sides[2], 1),
        "E": geo.PhysicalGroup(surface.ext_contour.sides[3], 1),
    }
    for group in domains.values():
        group.add_gmsh()
    for group in boundaries.values():
        group.add_gmsh()
    charact_field = mesh_tools.MathEvalField("0.05")
    mesh_tools.set_background_mesh(charact_field)
    geo.set_gmsh_option("Mesh.SaveAll", 0)
    model.mesh.generate(1)
    model.mesh.generate(2)
    gmsh.model.mesh.removeDuplicateNodes()
    gmsh.write(str(mesh_file))
    E_1, E_2, nu = 1, 3, 0.3
    materials = {
        domains["soft"].tag: mat.Material(E_1, nu, "cp"),
        domains["stiff"].tag: mat.Material(E_1, nu, "cp"),
    }
    test_part = part.FenicsPart.part_from_file(mesh_file,
                                               materials,
                                               subdomains_import=True)
    assert test_part.mat_area == approx(L_x * L_y)
    elem_type = "CG"
    degree = 2
    V = fe.VectorFunctionSpace(test_part.mesh, elem_type, degree)
    W = fe.FunctionSpace(
        test_part.mesh,
        fe.VectorElement(elem_type, test_part.mesh.ufl_cell(), degree, dim=3),
    )
    boundary_conditions = {
        boundaries["N"].tag: fe.Expression(("x[0]-1", "1"), degree=1),
        boundaries["S"].tag: fe.Expression(("x[0]-1", "-1"), degree=1),
        boundaries["E"].tag: fe.Expression(("1", "x[1]-1"), degree=1),
        boundaries["W"].tag: fe.Expression(("-1", "x[1]-1"), degree=1),
    }
    bcs = list()
    for tag, val in boundary_conditions.items():
        bcs.append(fe.DirichletBC(V, val, test_part.facet_regions, tag))
    ds = fe.Measure("ds",
                    domain=test_part.mesh,
                    subdomain_data=test_part.facet_regions)
    v = fe.TestFunctions(V)
    u = fe.TrialFunctions(V)
    F = (fe.inner(mat.sigma(test_part.elasticity_tensor, mat.epsilon(u)),
                  mat.epsilon(v)) * fe.dx)
    a, L = fe.lhs(F), fe.rhs(F)
    u_sol = fe.Function(V)
    fe.solve(a == L, u_sol, bcs)
    strain = fe.project(mat.epsilon(u_sol), W)
    if plots:
        import matplotlib.pyplot as plt

        plt.figure()
        plot = fe.plot(u_sol)
        plt.colorbar(plot)
        plt.figure()
        plot = fe.plot(strain[0])
        plt.colorbar(plot)
        plt.figure()
        plot = fe.plot(strain[1])
        plt.colorbar(plot)
        plt.figure()
        plot = fe.plot(strain[2])
        plt.colorbar(plot)
        plt.show()
    error = fe.errornorm(
        strain,
        fe.Expression(("1", "1", "0"), degree=0),
        degree_rise=3,
        mesh=test_part.mesh,
    )
    assert error == approx(0, abs=1e-12)

    materials = {
        domains["soft"].tag: mat.Material(E_1, nu, "cp"),
        domains["stiff"].tag: mat.Material(E_2, nu, "cp"),
    }
    test_part = part.FenicsPart.part_from_file(mesh_file,
                                               materials,
                                               subdomains_import=True)
    V = fe.VectorFunctionSpace(test_part.mesh, elem_type, degree)
    W = fe.FunctionSpace(
        test_part.mesh,
        fe.VectorElement(elem_type, test_part.mesh.ufl_cell(), degree, dim=3),
    )
    bcs = list()
    for tag, val in boundary_conditions.items():
        bcs.append(fe.DirichletBC(V, val, test_part.facet_regions, tag))
    v = fe.TestFunctions(V)
    u = fe.TrialFunctions(V)
    F = (fe.inner(mat.sigma(test_part.elasticity_tensor, mat.epsilon(u)),
                  mat.epsilon(v)) * fe.dx)
    a, L = fe.lhs(F), fe.rhs(F)
    u_sol = fe.Function(V)
    fe.solve(a == L, u_sol, bcs)
    strain = mat.epsilon(u_sol)
    stress = mat.sigma(test_part.elasticity_tensor, strain)
    energy = 0.5 * fe.assemble(
        fe.inner(stress, strain) * fe.dx(test_part.mesh))
    energy_abaqus = 12.8788939
    assert energy == approx(energy_abaqus, rel=1e-3)
    geo.reset()
Example #3
0
def test_mat_area():
    """FenicsPart method global_aera, for 2D parts created from a gmsh mesh and from a FEniCS mesh"""
    L_x, L_y = 4.0, 5.0
    H = 1.0
    size = 0.5
    rectangle = mshr.Rectangle(fe.Point(0.0, 0), fe.Point(L_x, L_y))
    hole = mshr.Rectangle(
        fe.Point(L_x / 2 - H / 2, L_y / 2 - H / 2),
        fe.Point(L_x / 2 + H / 2, L_y / 2 + H / 2),
    )
    domain = rectangle - hole
    domain.set_subdomain(1, rectangle)
    mesh = mshr.generate_mesh(domain, size)
    dimensions = np.array(((L_x, 0.0), (0.0, L_y)))
    material = {"0": mat.Material(1.0, 0.3, "cp")}
    rect_part = part.FenicsPart(
        mesh,
        materials=material,
        subdomains=None,
        global_dimensions=dimensions,
        facet_regions=None,
    )
    assert rect_part.mat_area == (L_x * L_y - H**2)

    name = "test_mat_area"
    local_dir = Path(__file__).parent
    mesh_file = local_dir.joinpath(name + ".msh")
    gmsh.model.add(name)
    vertices = [(0.0, 0.0), (0.0, L_y), (L_x, L_y), (L_x, 0.0)]
    contour = geo.LineLoop([geo.Point(np.array(c)) for c in vertices], False)
    surface = geo.PlaneSurface(contour)
    cut_vertices = list()
    for local_coord in [(H, 0.0, 0.0), (0.0, H, 0.0), (-H, 0.0, 0.0),
                        (0.0, -H, 0.0)]:
        vertex = geo.translation(contour.vertices[2], np.array(local_coord))
        cut_vertices.append(vertex)
    cut_surface = geo.PlaneSurface(geo.LineLoop(cut_vertices, False))
    for s in [surface, cut_surface]:
        s.add_gmsh()
    factory.synchronize()
    (surface, ) = geo.surface_bool_cut(surface, cut_surface)
    factory.synchronize()
    for dim_tag in model.getEntities(2):
        if not dim_tag[1] == surface.tag:
            model.removeEntities([dim_tag], True)
    charact_field = mesh_tools.MathEvalField("0.1")
    mesh_tools.set_background_mesh(charact_field)
    geo.set_gmsh_option("Mesh.SaveAll", 1)
    model.mesh.generate(2)
    gmsh.write(str(mesh_file))
    cmd = f"dolfin-convert {mesh_file} {mesh_file.with_suffix('.xml')}"
    run(cmd, shell=True, check=True)
    mesh = fe.Mesh(str(mesh_file.with_suffix(".xml")))
    dimensions = np.array(((L_x, 0.0), (0.0, L_y)))
    material = {"0": mat.Material(1.0, 0.3, "cp")}
    rect_part = part.FenicsPart(
        mesh,
        materials=material,
        subdomains=None,
        global_dimensions=dimensions,
        facet_regions=None,
    )
    assert rect_part.mat_area == approx(L_x * L_y - H * H / 2)
    geo.reset()
Example #4
0
    def __init__(self, pattern_ll, cell_vect, nb_cells, offset, attractors,
                 soft_mat, name):
        """
        Contrat : Créer un maillage pour des RVE 2D, plans, comportant au plus 2 matériaux constitutifs et pouvant contenir plusieurs cellules.

        cell_vect : Vecteurs de périodicité en colonne, définit également le parallélogramme qui contient la cellule.
        #! La cellule doit être un parallélogramme.

        Parameters
        ----------
        pattern_ll : list
            Instances of LineLoop that define the contours of the microstructure.
        cell_vect : 2D array
            dimensions of the unit cell and directions of periodicity.
            (given in a 2D cartesian coordinate system)
        nb_cells : 1D array
            Numbers of cells in each direction of repetition/periodicity.
        offset : 1D array
            Relative position inside a cell of the point that will coincide with the origin of the global domain.
        attractors : list
            Instances of Point.
            Can also be = None or empty.
            It represent the points that will be used as attractors in the definition of the element characteristic length fields.
            Attractors are geometrical elements of the cell around which mesh refinement constraints will be set.
        name : string or Path
        """

        self.name = name.stem if isinstance(name, PurePath) else name
        model.add(self.name)
        model.setCurrent(self.name)

        if offset.any():
            nb_pattern = [
                math.ceil(val + 1) if offset[i] != 0 else math.ceil(val)
                for i, val in enumerate(nb_cells)
            ]
            nb_pattern = np.array(nb_pattern, dtype=np.int8)
            pattern_ll = offset_pattern(pattern_ll, offset, cell_vect)
        else:
            nb_pattern = np.int8(np.ceil(nb_cells))

        if not np.equal(nb_pattern, 1).all():
            duplicate_pattern(pattern_ll, nb_pattern, cell_vect)

        rve_vect = cell_vect * np.asarray(nb_cells)
        pt_o = np.zeros((3, ))
        macro_vtcs = [
            pt_o,
            rve_vect[:, 0],
            rve_vect[:, 0] + rve_vect[:, 1],
            rve_vect[:, 1],
        ]
        macro_ll = geo.LineLoop([geo.Point(c) for c in macro_vtcs])
        macro_s = geo.PlaneSurface(macro_ll)

        if attractors:
            if not all(isinstance(e, geo.Point) for e in attractors):
                _err_msg = """Use of curves as attractors for the refinement of the mesh
                is not yet fully supported in our python library for gmsh."""
                raise TypeError(_err_msg)
                # TODO : toujours d'actualité ?
            if offset.any():
                attractors = offset_pattern(attractors, offset, cell_vect)
            if not np.equal(nb_pattern, 1).all():
                duplicate_pattern(attractors, nb_pattern, cell_vect)

        logger.info("Start boolean operations on surfaces")
        _msg_ok = "The main material domain of the RVE is connected (topological property)."  # noqa
        _msg_no_surface = "The boolean operation for creating the main material domain of the RVE return 0 surfaces."
        _msg_not_connected = "The main material domain of the RVE obtained by a boolean operation is disconnected (topological property)."

        phy_surf = list()
        pattern_s = [geo.PlaneSurface(ll) for ll in pattern_ll]
        rve_s = geo.surface_bool_cut(macro_s, pattern_s)
        if len(rve_s) == 1:
            logger.info(_msg_ok)
        elif len(rve_s) == 0:
            logger.warning(_msg_no_surface)
        else:
            logger.warning(_msg_not_connected)

        rve_s_phy = geo.PhysicalGroup(rve_s, 2, "microstruct_domain")
        phy_surf.append(rve_s_phy)
        if soft_mat:
            soft_s = geo.surface_bool_cut(macro_s, rve_s)
            soft_s_phy = geo.PhysicalGroup(soft_s, 2, "soft_domain")
            phy_surf.append(soft_s_phy)
        logger.info("Done boolean operations on surfaces")

        if attractors:
            need_sync = False
            for entity in attractors:
                if not entity.tag:
                    entity.add_gmsh()
                    need_sync = True
            if need_sync:
                factory.synchronize()  # ? Pourrait être enlevé ?

        for gp in phy_surf:
            gp.add_gmsh()
        factory.synchronize()

        data = model.getPhysicalGroups()
        details = [
            f"Physical group id : {dimtag[1]}, " +
            f"dimension : {dimtag[0]}, " +
            f"name : {model.getPhysicalName(*dimtag)}, " +
            f"nb of entitities {len(model.getEntitiesForPhysicalGroup(*dimtag))} \n"
            for dimtag in data
        ]
        logger.debug(f"All physical groups in the model : {data}")
        logger.debug(f"Physical groups details : \n {details}")
        logger.info("Done generating the gmsh geometrical model")
        if isinstance(name, PurePath):
            gmsh.write(str(name.with_suffix(".brep")))
            logger.info(f"Saving brep at {str(name.with_suffix('.brep'))}")
        else:
            gmsh.write(f"{name}.brep")
            logger.info(f"Saving brep at {name}.brep")
        macro_bndry = macro_ll.sides
        if soft_mat:
            boundary = geo.AbstractSurface.get_surfs_boundary(rve_s + soft_s)
        else:
            try:
                s = one(rve_s)
                boundary = geo.AbstractSurface.get_surfs_boundary(
                    s, recursive=False)
            except ValueError:
                boundary = geo.AbstractSurface.get_surfs_boundary(
                    rve_s, recursive=False)
        for b in boundary:
            b.get_boundary(get_coords=True)
        # factory.synchronize()
        micro_bndry = list()
        for macro_line in macro_bndry:
            fragments = geo.macro_line_fragments(boundary, macro_line)
            for c in fragments:
                c.gmsh_type = model.getType(1, c.tag)
            lines_only = [c for c in fragments if c.gmsh_type == "Line"]
            micro_bndry.append(lines_only)
        macro_dir = list()
        for i in range(len(macro_bndry) // 2):
            macro_line = macro_bndry[i]
            direction = macro_line.def_pts[-1].coord - macro_line.def_pts[
                0].coord
            macro_dir.append(direction)
        for i, crvs in enumerate(micro_bndry):
            msh.order_curves(crvs, macro_dir[i % 2], orientation=True)
        msh.set_periodicity_pairs(micro_bndry[0], micro_bndry[2])
        msh.set_periodicity_pairs(micro_bndry[1], micro_bndry[3])
        logger.info("Done defining a mesh periodicity constraint")

        tags = [
            f"per_pair_{k}" for k in ("1_slave", "2_slave", "1_mast", "2_mast")
        ]
        per_pair_phy = list()
        for crvs, tag in zip(micro_bndry, tags):
            per_pair_phy.append(geo.PhysicalGroup(crvs, 1, tag))
        for gp in per_pair_phy:
            gp.add_gmsh()

        self.gen_vect = rve_vect
        self.nb_cells = nb_cells
        self.attractors = attractors if attractors else []
        self.phy_surf = phy_surf
        self.mesh_fields = []
        self.mesh_abs_path = ""
Example #5
0
def test_gather_line():
    """
    Test of the macro_line_fragments function.
    colors on plots : OK

    """
    name = "test_gather_line"
    gmsh.model.add(name)
    rect_vtcs = [(-4, 2), (-4, -2), (4, -2), (4, 2)]
    rect_vtcs = [geo.Point(np.array(c), 0.2) for c in rect_vtcs]
    line_N = geo.Line(geo.Point(np.array((-4, 2))), geo.Point(np.array(
        (4, 2))))
    line_W = geo.Line(geo.Point(np.array((-4, -2))),
                      geo.Point(np.array((-4, 2))))
    line_S = geo.Line(geo.Point(np.array((-4, -2))),
                      geo.Point(np.array((4, -2))))
    line_E = geo.Line(geo.Point(np.array((4, -2))), geo.Point(np.array(
        (4, 2))))
    holes = list()
    hole_shape = [
        np.array(c) for c in [(-0.2, 0), (0, -0.4), (0.2, 0), (0, 0.4)]
    ]
    translations = [
        (-3, 2.1),
        (-1, 2.1),
        (1, 2.1),
        (3, 2.1),
        (-3, -2.1),
        (-1, -2.1),
        (1, -2.1),
        (3, -2.1),
        (4.1, -1),
        (4.1, 1),
        (-4.1, -1),
        (-4.1, 1),
    ]
    translations = [np.array(t) for t in translations]
    for t in translations:
        holes.append([geo.Point(c + t, 0.05) for c in hole_shape])

    rect_ll = geo.LineLoop(rect_vtcs, explicit=False)
    hole_ll = [geo.LineLoop(h, explicit=False) for h in holes]
    rect_s = geo.PlaneSurface(rect_ll)
    hole_s = [geo.PlaneSurface(ll) for ll in hole_ll]
    final_s = geo.surface_bool_cut(rect_s, hole_s)
    factory.synchronize()
    print("length of final_s : ", len(final_s))
    final_s = final_s[0]
    final_s.get_boundary(recursive=True)

    plt.figure()
    plt.axis("equal")
    for crv in final_s.boundary:
        crv.plot2D("blue")
    boundary_N = geo.macro_line_fragments(final_s.boundary, line_N)
    boundary_W = geo.macro_line_fragments(final_s.boundary, line_W)
    boundary_S = geo.macro_line_fragments(final_s.boundary, line_S)
    boundary_E = geo.macro_line_fragments(final_s.boundary, line_E)
    factory.synchronize()
    plt.figure()
    plt.axis("equal")
    line_lists = [boundary_N, boundary_W, boundary_S, boundary_E]
    colors = ["red", "green", "orange", "blue"]
    names = ["N", "W", "S", "E"]
    gps = list()
    for l_list, col, n in zip(line_lists, colors, names):
        gp_l = geo.PhysicalGroup(l_list, 1, n)
        gp_l.add_gmsh()
        gps.append(gp_l)
        for ln in l_list:
            ln.plot2D(col)
    factory.synchronize()

    gmsh.option.setNumber("Mesh.SaveAll", 1)
    gmsh.model.mesh.generate(2)
    gmsh.write(f"{name}.brep")
    gmsh.write(f"{name}.msh")
    os.system(f"gmsh {name}.brep &")
    os.system(f"gmsh {name}.msh &")
Example #6
0
def test_bool_ops():
    """
    Test of boolean operations performed on PlaneSurfaces instances.
    cut : OK
    intersection : geometry : OK
    intersection : mesh size : No. Very coarse mesh if the characteristic length is set from the values at Points
    #? What the second output of the booleans operations functions is?

    """
    name = "test_PlaneSurface_bool_ops"
    gmsh.model.add(name)

    coords = [
        (0.0, 0.05),
        (0.05, 0.0),
        (1.8, 0.0),
        (2.0, 0.2),
        (2.0, 1.95),
        (1.95, 2.0),
        (0.2, 2.0),
        (0.0, 1.8),
    ]
    pts = [geo.Point(np.array(c), 0.03) for c in coords]
    lines = [
        geo.Line(pts[2 * i - 1], pts[2 * i]) for i in range(len(pts) // 2)
    ]
    centers = [
        geo.Point(np.array(c), 0.03)
        for c in [(0.05, 0.05), (1.8, 0.2), (1.95, 1.95), (0.2, 1.8)]
    ]
    arcs = [
        geo.Arc(pts[2 * i], centers[i], pts[2 * i + 1])
        for i in range(len(pts) // 2)
    ]
    elmts_1D = [item for pair in zip(lines, arcs) for item in pair]
    ll_1 = geo.LineLoop(elmts_1D, explicit=True)
    coords = [(1.0, 1.0), (3.0, 1.0), (3.0, 3.0), (1.0, 3.0)]
    vertc = [geo.Point(np.array(c), 0.1) for c in coords]
    ll_2 = geo.LineLoop(vertc, explicit=False)
    rect_vtcs = [
        geo.Point(np.array(c), 0.05) for c in [(4, 2), (4, 4), (6, 4), (6, 2)]
    ]
    hole_vtcs_1 = [
        geo.Point(np.array(c), 0.02)
        for c in [(5 - 0.1, 3), (5, 3 - 0.5), (5 + 0.1, 3), (5, 3 + 0.5)]
    ]
    hole_vtcs_2 = [
        geo.Point(np.array(c), 0.02)
        for c in [(5, 3 - 0.1), (5 - 0.5, 3), (5, 3 + 0.1), (5 + 0.5, 3)]
    ]
    rect_ll = geo.LineLoop(rect_vtcs, explicit=False)
    hole_ll_1 = geo.LineLoop(hole_vtcs_1, explicit=False)
    hole_ll_2 = geo.LineLoop(hole_vtcs_2, explicit=False)

    surf_1 = geo.PlaneSurface(ll_1)
    surf_2 = geo.PlaneSurface(ll_2)
    surf_rect = geo.PlaneSurface(rect_ll)
    surf_hole_1 = geo.PlaneSurface(hole_ll_1)
    surf_hole_2 = geo.PlaneSurface(hole_ll_2)
    for s in [surf_1, surf_2, surf_rect, surf_hole_1, surf_hole_2]:
        s.add_gmsh()
    print(f"""Tags before boolean operations :
            surf_1 : {surf_1.tag}; surf_2 : {surf_2.tag}; surf_rect : {surf_rect.tag};
            surf_hole_1 : {surf_hole_1.tag}; surf_hole_2 : {surf_hole_2.tag}"""
          )
    surf_with_hole = geo.surface_bool_cut(surf_rect,
                                          [surf_hole_1, surf_hole_2])
    print(surf_with_hole)
    surf_with_hole = surf_with_hole[0]
    surf_inter = geo.surface_bool_intersect(surf_1, surf_2)
    print(surf_inter)
    surf_inter = surf_inter[0]
    factory.synchronize()

    # * Intersection, when a surface is inside another one.
    coords3 = [
        geo.Point(np.array(c), 0.05)
        for c in [(10, 10), (10, 14), (14, 14), (14, 10)]
    ]
    coords4 = [
        geo.Point(np.array(c), 0.05)
        for c in [(11, 11), (11, 13), (13, 13), (13, 11)]
    ]
    surf3 = geo.PlaneSurface(geo.LineLoop(coords3, explicit=False))
    surf3.add_gmsh()
    surf4 = geo.PlaneSurface(geo.LineLoop(coords4, explicit=False))
    surf4.add_gmsh()
    factory.synchronize()
    surf_inter34 = geo.surface_bool_intersect(surf3, [surf4])
    factory.synchronize()
    data = gmsh.model.getEntities()
    print(f"model name : {name}")
    print(data)
    print(f"""Tags after boolean operations :
            surf_1 : {surf_1.tag}; surf_2 : {surf_2.tag}; surf_rect : {surf_rect.tag};
            surf_hole_1 : {surf_hole_1.tag}; surf_hole_2 : {surf_hole_2.tag};
            surf_with_hole : {surf_with_hole.tag}; surf_inter: {surf_inter.tag}"""
          )
    gmsh.model.mesh.generate(2)
    gmsh.write(f"{name}.brep")
    gmsh.write(f"{name}.msh")
    os.system(f"gmsh {name}.brep &")
    os.system(f"gmsh {name}.msh &")