def generate_mesh(fractures_data, max_el_size, file_name, verbose=0): r""" Create mesh and write it to a file. Parameters ---------- fractures_data : list of FractureData Array of objects defining fractures. max_el_size : double Maximal size of mesh element. file_name : str File name to write mesh into. verbose : {0, 1} If set to 1, messages during mesh generation will be printed. """ model = gmsh.model factory = model.occ gmsh.initialize() gmsh.option.setNumber("General.Terminal", verbose) model.add("test_api") # set options gmsh.option.setNumber("Geometry.Tolerance", 0) gmsh.option.setNumber("Geometry.ToleranceBoolean", 0) gmsh.option.setNumber("Geometry.MatchMeshTolerance", 0) gmsh.option.setNumber("Mesh.ToleranceInitialDelaunay", 1e-12) # Compute mesh element sizes from values given at geometry points, default value: 1 gmsh.option.setNumber("Mesh.CharacteristicLengthFromPoints", 1) # Automatically compute mesh element sizes from curvature, respects surface curvature, default value: 0 gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 0) gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 0) # Element size constraint options # gmsh.option.setNumber("Mesh.CharacteristicLengthMin", max_el_size*0.01) # gmsh.option.setNumber("Mesh.CharacteristicLengthMax", max_el_size) # gmsh.option.setNumber("Mesh.MinimumCurvePoints", 5) # create fractures (currently we ignore r2 and create circular disks) disks = [] tags = [] for f in fractures_data: pc = factory.addPoint(f.centre[0], f.centre[1], f.centre[2], f.r1) axis, angle = f.get_rotation_axis_angle() p0 = factory.addPoint(f.centre[0] + f.r1, f.centre[1], f.centre[2], f.r1) p1 = factory.addPoint(f.centre[0] + f.r1 * cos(pi * 2 / 3), f.centre[1] + f.r1 * sin(pi * 2 / 3), f.centre[2], f.r1) p2 = factory.addPoint(f.centre[0] + f.r1 * cos(pi * 4 / 3), f.centre[1] + f.r1 * sin(pi * 4 / 3), f.centre[2], f.r1) factory.rotate([(0, p0), (0, p1), (0, p2)], f.centre[0], f.centre[1], f.centre[2], axis[0], axis[1], axis[2], angle) c1 = factory.addCircleArc(p0, pc, p1) c2 = factory.addCircleArc(p1, pc, p2) c3 = factory.addCircleArc(p2, pc, p0) cl = factory.addCurveLoop([c1, c2, c3]) d = factory.addPlaneSurface([cl]) disks.append((2, d)) tags.append(f.tag) # fragment fractures fractures, fractures_map = factory.fragment(disks, []) print('disks = ', disks) # List of fracture tuples (dim, tag) print('fractures = ', fractures) # List of lists of tuples, each sublist contains fragments (dim, tag) from particular fracture print('fractures_map = ', fractures_map) assert len(fractures_map) == len(disks) # set physical id and name of fractures fractures_phys_id = model.addPhysicalGroup(2, [id for dim, id in fractures]) model.setPhysicalName(2, fractures_phys_id, "fractures") # define 3d volume and embed fractures into it box = factory.addBox(0, 0, 0, 1, 1, 1) factory.synchronize() # Embed the model entities of dimension 'dim', and tags 'tag' in the other model entity which is given by # dimension (3) and tag (box) model.mesh.embed(2, [tag for dim, tag in fractures], 3, box) # define physical id of volume and its boundaries box_phys_id = model.addPhysicalGroup(3, [box], -1) model.setPhysicalName(3, box_phys_id, "box") # box_bdry = model.getBoundary([(3,box)], True) # box_bdry_left_phys_id = model.addPhysicalGroup(2, [box_bdry[0][1]], -1) # box_bdry_right_phys_id = model.addPhysicalGroup(2, [box_bdry[1][1]], -1) # model.setPhysicalName(2, box_bdry_left_phys_id, ".left") # model.setPhysicalName(2, box_bdry_right_phys_id, ".right") # Define fields for mesh refinement # Compute the distance from curves, each curve is replaced by NNodesByEdge equidistant nodes # and the distance from those nodes is computed. model.mesh.field.add("Distance", 1) model.mesh.field.setNumber(1, "NNodesByEdge", 100) frac_bdry = model.getBoundary(fractures, False, False, False) # Set the numerical list option "EdgesList" to list of "frac_bdry" tags for field tag 1. model.mesh.field.setNumbers(1, "EdgesList", [tag for dm, tag in frac_bdry if dm == 1]) # Threshold # F = LCMin if Field[IField] <= DistMin, # F = LCMax if Field[IField] >= DistMax, # F = interpolation between LcMin and LcMax if DistMin < Field[IField] < DistMax model.mesh.field.add("Threshold", 2) # Index of the field to evaluate (in this case 1 is "Distance") model.mesh.field.setNumber(2, "IField", 1) # Element size inside DistMin model.mesh.field.setNumber(2, "LcMin", max_el_size * 0.03) # Element size outside DistMax model.mesh.field.setNumber(2, "LcMax", max_el_size) # Distance from entity up to which element size will be LcMin, it should depend on particular model model.mesh.field.setNumber(2, "DistMin", 0.001) # Distance from entity after which element size will be LcMax, it should depend on particular model model.mesh.field.setNumber(2, "DistMax", 0.5) # Set threshold as the background mesh size field. model.mesh.field.setAsBackgroundMesh(2) # generate mesh, write to file and output number of entities that produced error factory.synchronize() gmsh.write(file_name + '.brep') model.mesh.generate(3) bad_entities = model.mesh.getLastEntityError() gmsh.write(file_name) gmsh.finalize() return len(bad_entities)
def test_extrude_polygon(): """ Test extrusion of an polygon. BUG: Currently failing: - in this setting (set mesh step directly to nodes of polygon) it generates elements but writes empty mesh - when setting mesh step by getting boundary inside make_mesh, it fails """ gmsh.initialize() file_name = "extrude_polygon" gmsh.model.add(file_name) # plane directional vectors vector u = np.array([1, 1, 0]) u = u / np.linalg.norm(u) v = np.array([0, 1, 1]) v = v / np.linalg.norm(v) #normal n = np.cross(u, v) n = n / np.linalg.norm(n) # add some points in the plane points = [] points.append(u) points.append(2 * u + 1 * v) points.append(5 * u + -2 * v) points.append(5 * u + 3 * v) points.append(4 * u + 5 * v) points.append(-2 * u + 3 * v) points.append(v) point_tags = [gmsh.model.occ.addPoint(*p) for p in points] # point_tags = [gmsh.model.occ.addPoint(*p, meshSize=0.2) for p in points] lines = [ gmsh.model.occ.addLine(point_tags[i - 1], point_tags[i]) for i in range(len(points)) ] cl = gmsh.model.occ.addCurveLoop(lines) pol = gmsh.model.occ.addPlaneSurface([cl]) ext_dimtags = gmsh.model.occ.extrude([(2, pol)], *(3 * n)) tube_dimtags = [ext_dimtags[1]] gmsh.model.occ.synchronize() nodes = gmsh.model.getBoundary(tube_dimtags, combined=False, oriented=False, recursive=True) print(nodes) p_dimtags = [(0, tag) for tag in point_tags] gmsh.model.occ.mesh.setSize(p_dimtags, size=0.2) gmsh.model.occ.synchronize() # generate mesh, write to file and output number of entities that produced error # gmsh.option.setNumber("Mesh.CharacteristicLengthFromPoints", 0.2) # gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 0) # gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 1) # gmsh.option.setNumber("Mesh.CharacteristicLengthMin", 0.1) # gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 1.0) gmsh.model.mesh.generate(3) gmsh.model.mesh.removeDuplicateNodes() bad_entities = gmsh.model.mesh.getLastEntityError() print(bad_entities) # gmsh.fltk.run() gmsh.write(sandbox_fname(file_name, "msh2")) gmsh.finalize()
def generate_mesh(): gmsh.initialize() file_name = "box_wells" gmsh.model.add(file_name) box = gmsh.model.occ.addBox(-1000, -1000, -1000, 2000, 2000, 2000) rec1 = gmsh.model.occ.addRectangle(-800, -800, 0, 1600, 1600) rec2 = gmsh.model.occ.addRectangle(-1200, -1200, 0, 2400, 2400) rec1_dt = (2, rec1) rec2_dt = (2, rec2) gmsh.model.occ.rotate([rec2_dt], 0, 0, 0, 0, 1, 0, np.pi / 2) rectangle, map = gmsh.model.occ.fragment([rec1_dt, rec2_dt], []) box = [(3, box)] box_copy = gmsh.model.occ.copy(box) dim_tags, map = gmsh.model.occ.intersect(rectangle, box_copy) gmsh.model.occ.synchronize() dim_tags_copy = gmsh.model.occ.copy(dim_tags) box_copy = gmsh.model.occ.copy(box) box_cut, map = gmsh.model.occ.fragment(box_copy, dim_tags_copy) gmsh.model.occ.removeAllDuplicates() gmsh.model.occ.synchronize() b = gmsh.model.addPhysicalGroup(3, [tag for dim, tag in box_cut]) gmsh.model.setPhysicalName(3, b, "box") rect = gmsh.model.addPhysicalGroup(2, [tag for dim, tag in dim_tags]) gmsh.model.setPhysicalName(2, rect, "rectangle") bc_tags = gmsh.model.getBoundary(dim_tags, combined=False, oriented=False) bc_nodes = gmsh.model.getBoundary(dim_tags, combined=False, oriented=False, recursive=True) bc_box_nodes = gmsh.model.getBoundary(box_cut, combined=False, oriented=False, recursive=True) bc_rect = gmsh.model.addPhysicalGroup(1, [tag for dim, tag in bc_tags]) gmsh.model.setPhysicalName(1, bc_rect, ".rectangle") gmsh.model.occ.setMeshSize(bc_nodes, 50) #gmsh.model.mesh.embed(2, [tag for dim, tag in dim_tags], 3, box_copy[0][1]) #factory.synchronize() model = gmsh.model # generate mesh, write to file and output number of entities that produced error gmsh.option.setNumber("Mesh.CharacteristicLengthFromPoints", 1) gmsh.option.setNumber("Mesh.CharacteristicLengthFromCurvature", 0) gmsh.option.setNumber("Mesh.CharacteristicLengthExtendFromBoundary", 1) #gmsh.option.setNumber("Mesh.CharacteristicLengthMin", 100) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 300) gmsh.write(file_name + '.brep') gmsh.write(file_name + '.geo') model.mesh.generate(3) gmsh.model.mesh.removeDuplicateNodes() bad_entities = model.mesh.getLastEntityError() print(bad_entities) gmsh.write(file_name + ".msh") gmsh.fltk.run() gmsh.finalize() return len(bad_entities)
def draw_GMSH( output, sym, is_antiper=False, is_remove_vent=False, is_remove_slotS=False, is_remove_slotR=False, is_lam_only_S=False, is_lam_only_R=False, kgeo_fineness=1, kmesh_fineness=1, user_mesh_dict={}, path_save="GMSH_model.msh", is_sliding_band=True, transform_list=[], ): """Draws a machine mesh in GMSH format Parameters ---------- output : Output Output object is_remove_vent : bool True to remove the ventilation ducts (Default value = False) is_remove_slotS : bool True to solve without slot effect on the Stator (Default value = False) is_remove_slotR : bool True to solve without slot effect on the Rotor (Default value = False) kgeo_fineness : float global coefficient to adjust geometry fineness kmesh_fineness : float global coefficient to adjust mesh fineness sym : int the symmetry applied on the stator and the rotor (take into account antiperiodicity) is_antiper: bool To apply antiperiodicity boundary conditions is_lam_only_S: bool Draw only stator lamination is_lam_only_R: bool Draw only rotor lamination Returns ------- GMSH_dict : dict Dictionnary containing the main parameters of GMSH File """ # check some input parameter if is_lam_only_S and is_lam_only_R: raise InputError( "Only 'is_lam_only_S' or 'is_lam_only_R' can be True at the same time" ) # get machine machine = output.simu.machine mesh_dict = {} tol = 1e-6 # For readibility model = gmsh.model factory = model.geo # Start a new model gmsh.initialize(sys.argv) gmsh.option.setNumber("General.Terminal", int(True)) gmsh.option.setNumber("Geometry.CopyMeshingMethod", 1) gmsh.option.setNumber("Geometry.PointNumbers", 0) gmsh.option.setNumber("Geometry.LineNumbers", 0) model.add("Pyleecan") rotor_list = list() rotor_list.extend(machine.shaft.build_geometry(sym=sym)) rotor_list.extend(machine.rotor.build_geometry(sym=sym)) stator_list = list() stator_list.extend(machine.stator.build_geometry(sym=sym)) oo = factory.addPoint(0, 0, 0, 0, tag=-1) gmsh_dict = { 0: { "tag": 0, "label": "origin", "with_holes": False, 1: { "tag": 0, "n_elements": 1, "bc_name": None, "begin": { "tag": oo, "coord": complex(0.0, 0.0) }, "end": { "tag": None, "coord": None }, "cent": { "tag": None, "coord": None }, "arc_angle": None, "line_angle": None, }, } } # Default rotor mesh element size mesh_size = machine.rotor.Rext / 25.0 nsurf = 0 # number of surfaces if not is_lam_only_S: for surf in rotor_list: nsurf += 1 gmsh_dict.update({nsurf: {"tag": None, "label": surf.label}}) if surf.label.find("Lamination_Rotor") != -1: gmsh_dict[nsurf]["with_holes"] = True lam_rotor_surf_id = nsurf else: gmsh_dict[nsurf]["with_holes"] = False if user_mesh_dict is not None: mesh_dict = surf.comp_mesh_dict(element_size=mesh_size) mesh_dict.update(user_mesh_dict) for line in surf.get_lines(): # When symmetry is 1 the shaft surface is substrtacted from Rotor Lam instead if sym == 1 and line.label == "Lamination_Rotor_Yoke_Radius_Int": continue n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_line_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) elif isinstance(line, Arc) and (abs( line.get_angle() * 180.0 / cmath.pi) <= tol): # Don't draw anything, this is a circle and usually is repeated ? pass else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) lam_and_holes = list() ext_lam_loop = None for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lvalues in s_data.values(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) # search for the holes to substract from rotor lam if s_data["label"].find("Lamination_Rotor") != -1: ext_lam_loop = cloop else: # MachineSIPSM does not have holes in rotor lam # only shaft is taken out if symmetry is one if isinstance(machine, MachineSIPMSM): if sym == 1 and s_data["label"] == "Shaft": lam_and_holes.extend([cloop]) else: if sym == 1: lam_and_holes.extend([cloop]) elif s_data["label"] != "Shaft": lam_and_holes.extend([cloop]) else: pass # Shaft, magnets and magnet pocket surfaces are created if not is_lam_only_R: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # Finally rotor lamination is built if ext_lam_loop is not None: lam_and_holes.insert(0, ext_lam_loop) gmsh_dict[lam_rotor_surf_id]["tag"] = factory.addPlaneSurface( lam_and_holes, tag=-1) pg = model.addPhysicalGroup(2, [gmsh_dict[lam_rotor_surf_id]["tag"]]) model.setPhysicalName(2, pg, gmsh_dict[lam_rotor_surf_id]["label"]) # rotor_cloops = lam_and_holes # store rotor dict rotor_dict = gmsh_dict.copy() # init new dict for stator gmsh_dict = { 0: { "tag": 0, "label": "origin", "with_holes": False, 1: { "tag": 0, "n_elements": 1, "bc_name": None, "begin": { "tag": oo, "coord": complex(0.0, 0.0) }, "end": { "tag": None, "coord": None }, "cent": { "tag": None, "coord": None }, }, } } # Default stator mesh element size mesh_size = machine.stator.Rext / 100.0 # nsurf = 0 if not is_lam_only_R: stator_cloops = [] for surf in stator_list: nsurf += 1 gmsh_dict.update({nsurf: {"tag": None, "label": surf.label}}) if surf.label.find("Lamination_Stator") != -1: gmsh_dict[nsurf]["with_holes"] = True else: gmsh_dict[nsurf]["with_holes"] = False if user_mesh_dict is not None: mesh_dict = surf.comp_mesh_dict(element_size=mesh_size) mesh_dict.update(user_mesh_dict) for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_line_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lvalues in s_data.values(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) stator_cloops.append(cloop) # Winding surfaces are created if (s_data["label"].find("Lamination_Stator") != -1) or (not is_lam_only_S): s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # stator_dict = gmsh_dict.copy() gmsh_dict.update(rotor_dict) if is_sliding_band and (not is_lam_only_R) and (not is_lam_only_S): sb_list = get_sliding_band(sym=sym, machine=machine) else: sb_list = [] # Default sliding mesh element size mesh_size = 2.0 * cmath.pi * machine.rotor.Rext / 360.0 # nsurf = 0 for surf in sb_list: nsurf += 1 gmsh_dict.update({nsurf: {"tag": None, "label": surf.label}}) for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_agline_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) else: _add_agline_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lvalues in s_data.values(): if (s_data["label"].find("Airgap") != -1 or s_data["label"].find("SlidingBand") != -1): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) else: continue if lloop: cloop = factory.addCurveLoop(lloop) s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # Set boundary conditions in gmsh lines bc_master_stator_id = [] bc_slave_stator_id = [] bc_master_rotor_id = [] bc_slave_rotor_id = [] for propname in boundary_list: # propname = boundary_prop[bound_label] bc_id = [] for s_id, s_data in gmsh_dict.items(): for lid, lvalues in s_data.items(): if type(lvalues) is not dict: continue if lvalues["bc_name"] == propname: if propname == "MASTER_SLAVE_STATOR_BOUNDARY": line_angle = lvalues.get("line_angle", None) print(propname, lvalues["tag"], line_angle) # Assumes Master coincides with x-Axis if line_angle is not None and abs(line_angle) < tol: bc_master_stator_id.extend([abs(lvalues["tag"])]) else: bc_slave_stator_id.extend([abs(lvalues["tag"])]) elif propname == "MASTER_SLAVE_ROTOR_BOUNDARY": line_angle = lvalues.get("line_angle", None) print(propname, lvalues["tag"], line_angle) # Assumes Master coincides with x-Axis if line_angle is not None and abs(line_angle) < tol: bc_master_rotor_id.extend([abs(lvalues["tag"])]) else: bc_slave_rotor_id.extend([abs(lvalues["tag"])]) else: bc_id.extend([abs(lvalues["tag"])]) if bc_id: pg = model.addPhysicalGroup(1, bc_id) model.setPhysicalName(1, pg, propname) if bc_master_stator_id: pg = model.addPhysicalGroup(1, bc_master_stator_id) model.setPhysicalName(1, pg, "MASTER_SATOR_BOUNDARY") print("MASTER_STATOR_BOUNDARY", bc_master_stator_id) if bc_slave_stator_id: pg = model.addPhysicalGroup(1, bc_slave_stator_id) model.setPhysicalName(1, pg, "SLAVE_SATOR_BOUNDARY") print("SLAVE_STATOR_BOUNDARY", bc_slave_stator_id) if bc_master_rotor_id: pg = model.addPhysicalGroup(1, bc_master_rotor_id) model.setPhysicalName(1, pg, "MASTER_ROTOR_BOUNDARY") print("MASTER_ROTOR_BOUNDARY", bc_master_rotor_id) if bc_slave_rotor_id: pg = model.addPhysicalGroup(1, bc_slave_rotor_id) model.setPhysicalName(1, pg, "SLAVE_ROTOR_BOUNDARY") print("SLAVE_ROTOR_BOUNDARY", bc_slave_rotor_id) factory.synchronize() gmsh.model.mesh.generate(2) # Save and close gmsh.write(path_save) # gmsh.fltk.run() # Uncomment to launch Gmsh GUI gmsh.finalize() return gmsh_dict
def _finalize_gmsh(): gmsh.finalize()
# Origin, width, inside, outside sizes fid = 1 field = model.mesh.field # Refine close to left field.add('Box', fid) field.setNumber(fid, 'XMin', 0) field.setNumber(fid, 'XMax', 0.1 * geometry_parameters['X']) field.setNumber(fid, 'YMin', 0) field.setNumber(fid, 'YMax', geometry_parameters['Y']) field.setNumber(fid, 'VIn', 0.05) field.setNumber(fid, 'VOut', 0.2) field.setAsBackgroundMesh(fid) model.occ.synchronize() # gmsh.fltk.initialize() # gmsh.fltk.run() h5_filename = './test/square_domain.h5' mesh_model2d(model, tags, h5_filename) mesh, markers, lookup = load_mesh2d(h5_filename) cell_f, facet_f = markers gmsh.finalize() df.File('./test/square_cells.pvd') << cell_f df.File('./test/square_facets.pvd') << facet_f
def draw_GMSH( output, sym, is_antiper=False, is_remove_vent=False, is_remove_slotS=False, is_remove_slotR=False, is_lam_only_S=False, is_lam_only_R=False, kgeo_fineness=1, kmesh_fineness=1, user_mesh_dict={}, path_save="GMSH_model.msh", is_sliding_band=True, transform_list=[], ): """Draws a machine mesh in GMSH format Parameters ---------- output : Output Output object is_remove_vent : bool True to remove the ventilation ducts (Default value = False) is_remove_slotS : bool True to solve without slot effect on the Stator (Default value = False) is_remove_slotR : bool True to solve without slot effect on the Rotor (Default value = False) kgeo_fineness : float global coefficient to adjust geometry fineness kmesh_fineness : float global coefficient to adjust mesh fineness sym : int the symmetry applied on the stator and the rotor (take into account antiperiodicity) is_antiper: bool To apply antiperiodicity boundary conditions is_lam_only_S: bool Draw only stator lamination is_lam_only_R: bool Draw only rotor lamination Returns ------- GMSH_dict : dict Dictionnary containing the main parameters of GMSH File """ machine = output.simu.machine mesh_dict = {} tol = 1e-6 # For readibility model = gmsh.model factory = model.geo # Start a new model gmsh.initialize(sys.argv) gmsh.option.setNumber("General.Terminal", int(True)) gmsh.option.setNumber("Geometry.CopyMeshingMethod", 1) gmsh.option.setNumber("Geometry.PointNumbers", 0) gmsh.option.setNumber("Geometry.LineNumbers", 0) model.add("Pyleecan") rotor_list = list() rotor_list.extend(machine.shaft.build_geometry(sym=sym)) rotor_list.extend(machine.rotor.build_geometry(sym=sym)) stator_list = list() stator_list.extend(machine.stator.build_geometry(sym=sym)) oo = factory.addPoint(0, 0, 0, 0, tag=-1) gmsh_dict = { 0: { "tag": 0, "label": "origin", "with_holes": False, 1: { "tag": 0, "n_elements": 1, "begin": { "tag": oo, "coord": complex(0.0, 0.0) }, "end": { "tag": None, "coord": None }, "cent": { "tag": None, "coord": None }, }, } } # Default rotor mesh element size mesh_size = machine.rotor.Rext / 25.0 nsurf = 0 for surf in rotor_list: nsurf += 1 gmsh_dict.update({nsurf: {"tag": None, "label": surf.label}}) if surf.label.find("Lamination_Rotor") != -1: gmsh_dict[nsurf]["with_holes"] = True lam_rotor_surf_id = nsurf else: gmsh_dict[nsurf]["with_holes"] = False if user_mesh_dict is not None: mesh_dict = surf.comp_mesh_dict(element_size=mesh_size) mesh_dict.update(user_mesh_dict) for line in surf.get_lines(): # When symmetry is 1 the shaft surface is substrtacted from Rotor Lam instead if sym == 1 and line.label == "Lamination_Rotor_Yoke_Radius_Int": continue n_elem = mesh_dict.get(line.label) if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): if line.is_trigo_direction == True: arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=cmath.pi / 2.0, label=line.label, ) else: arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=-cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=-cmath.pi / 2.0, label=line.label, ) if n_elem is not None: _add_line_to_dict( geo=factory, line=arc1, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) _add_line_to_dict( geo=factory, line=arc2, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) else: _add_line_to_dict( geo=factory, line=arc1, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) _add_line_to_dict( geo=factory, line=arc2, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) elif isinstance( line, Arc) and (abs(line.get_angle() * 180.0 / cmath.pi) <= tol): # Don't draw anything, this is a circle and usually is repeated ? pass else: if n_elem is not None: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) lam_and_holes = list() ext_lam_loop = None for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lid, lvalues in s_data.items(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) # search for the holes to substract from rotor lam if s_data["label"].find("Lamination_Rotor") != -1: ext_lam_loop = cloop else: # MachineSIPSM does not have holes in rotor lam # only shaft is taken out if symmetry is one if isinstance(machine, MachineSIPMSM): if sym == 1 and s_data["label"] == "Shaft": lam_and_holes.extend([cloop]) else: if sym == 1: lam_and_holes.extend([cloop]) elif s_data["label"] != "Shaft": lam_and_holes.extend([cloop]) else: pass # Shaft, magnets and magnet pocket surfaces are created if not is_lam_only_R: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # Finally rotor lamination is built if ext_lam_loop is not None: lam_and_holes.insert(0, ext_lam_loop) gmsh_dict[lam_rotor_surf_id]["tag"] = factory.addPlaneSurface( lam_and_holes, tag=-1) pg = model.addPhysicalGroup(2, [gmsh_dict[lam_rotor_surf_id]["tag"]]) model.setPhysicalName(2, pg, gmsh_dict[lam_rotor_surf_id]["label"]) # Default rotor mesh element size mesh_size = machine.stator.Rext / 100.0 nsurf = 0 for surf in stator_list: nsurf += 1 gmsh_dict.update({nsurf: {"tag": None, "label": surf.label}}) if surf.label.find("Lamination_Stator") != -1: gmsh_dict[nsurf]["with_holes"] = True else: gmsh_dict[nsurf]["with_holes"] = False if user_mesh_dict is not None: mesh_dict = surf.comp_mesh_dict(element_size=mesh_size) mesh_dict.update(user_mesh_dict) for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) == 180.0): if line.is_trigo_direction == True: arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=cmath.pi / 2.0, label=line.label, ) else: arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=-cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=-cmath.pi / 2.0, label=line.label, ) if n_elem is not None: _add_line_to_dict( geo=factory, line=arc1, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) _add_line_to_dict( geo=factory, line=arc2, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) else: _add_line_to_dict( geo=factory, line=arc1, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) _add_line_to_dict( geo=factory, line=arc2, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) else: if n_elem is not None: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, n_elements=n_elem, ) else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, ) for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lid, lvalues in s_data.items(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) # Winding surfaces are created if s_data["label"].find("Lamination_Stator") != -1: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) else: # Stator lamination is built if not is_lam_only_S: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) factory.synchronize() gmsh.model.mesh.generate(2) # Save and close gmsh.write(path_save) # gmsh.fltk.run() # Uncomment to launch Gmsh GUI gmsh.finalize() return gmsh_dict
def create_mesh(r, w, l1, l2, n): gmsh.initialize() gmsh.option.setNumber("General.Terminal", 0) gmsh.model.add("fork") lc = 0 gmsh.model.occ.addPoint(0, 0, 0, lc, 1) gmsh.model.occ.addPoint(-r, 0, 0, lc, 2) gmsh.model.occ.addPoint(-(r + w), 0, 0, lc, 3) gmsh.model.occ.addPoint(+r, 0, 0, lc, 4) gmsh.model.occ.addPoint(+(r + w), 0, 0, lc, 5) gmsh.model.occ.addPoint(-(r + w), +l1, 0, lc, 6) gmsh.model.occ.addPoint(+(r + w), +l1, 0, lc, 7) gmsh.model.occ.addPoint(-r, +l1, 0, lc, 8) gmsh.model.occ.addPoint(+r, +l1, 0, lc, 9) x1 = w / 2 y1 = math.sqrt((r + w) * (r + w) - (w / 2) * (w / 2)) a = x1 / y1 y2 = math.sqrt(r * r / (1 + a * a)) x2 = a * y2 gmsh.model.occ.addPoint(-x1, -y1, lc, 10) gmsh.model.occ.addPoint(-x2, -y2, lc, 11) gmsh.model.occ.addPoint(+x1, -y1, lc, 12) gmsh.model.occ.addPoint(+x2, -y2, lc, 13) gmsh.model.occ.addPoint(-w / 2, -l2, lc, 14) gmsh.model.occ.addPoint(+w / 2, -l2, lc, 15) gmsh.model.occ.addLine(14, 10, 1) gmsh.model.occ.addLine(10, 11, 2) gmsh.model.occ.addLine(14, 15, 3) gmsh.model.occ.addLine(15, 12, 4) gmsh.model.occ.addLine(12, 13, 5) gmsh.model.occ.addLine(3, 6, 6) gmsh.model.occ.addLine(6, 8, 7) gmsh.model.occ.addLine(8, 2, 8) gmsh.model.occ.addLine(2, 3, 9) gmsh.model.occ.addLine(4, 9, 10) gmsh.model.occ.addLine(9, 7, 11) gmsh.model.occ.addLine(7, 5, 12) gmsh.model.occ.addLine(5, 4, 13) gmsh.model.occ.addCircleArc(2, 1, 11, 14) gmsh.model.occ.addCircleArc(11, 1, 13, 15) gmsh.model.occ.addCircleArc(13, 1, 4, 16) gmsh.model.occ.addCircleArc(3, 1, 10, 17) gmsh.model.occ.addCircleArc(10, 1, 12, 18) gmsh.model.occ.addCircleArc(12, 1, 5, 19) gmsh.model.occ.addCurveLoop([3, 4, -18, -1], 1) gmsh.model.occ.addCurveLoop([18, 5, -15, -2], 2) gmsh.model.occ.addCurveLoop([19, 13, -16, -5], 3) gmsh.model.occ.addCurveLoop([14, -2, -17, -9], 4) gmsh.model.occ.addCurveLoop([13, 10, 11, 12], 5) gmsh.model.occ.addCurveLoop([8, 9, 6, 7], 6) gmsh.model.occ.addPlaneSurface([1], 1) gmsh.model.occ.addPlaneSurface([2], 2) gmsh.model.occ.addPlaneSurface([3], 3) gmsh.model.occ.addPlaneSurface([4], 4) gmsh.model.occ.addPlaneSurface([5], 5) gmsh.model.occ.addPlaneSurface([6], 6) gmsh.model.occ.synchronize() gmsh.model.mesh.setTransfiniteCurve(3, n + 1) gmsh.model.mesh.setTransfiniteCurve(18, n + 1) gmsh.model.mesh.setTransfiniteCurve(15, n + 1) gmsh.model.mesh.setTransfiniteCurve(5, n + 1) gmsh.model.mesh.setTransfiniteCurve(2, n + 1) gmsh.model.mesh.setTransfiniteCurve(9, n + 1) gmsh.model.mesh.setTransfiniteCurve(13, n + 1) gmsh.model.mesh.setTransfiniteCurve(11, n + 1) gmsh.model.mesh.setTransfiniteCurve(7, n + 1) gmsh.model.mesh.setTransfiniteCurve(6, 12 * n + 1) gmsh.model.mesh.setTransfiniteCurve(8, 12 * n + 1) gmsh.model.mesh.setTransfiniteCurve(10, 12 * n + 1) gmsh.model.mesh.setTransfiniteCurve(12, 12 * n + 1) gmsh.model.mesh.setTransfiniteCurve(14, 3 * n + 1) gmsh.model.mesh.setTransfiniteCurve(17, 3 * n + 1) gmsh.model.mesh.setTransfiniteCurve(16, 3 * n + 1) gmsh.model.mesh.setTransfiniteCurve(19, 3 * n + 1) gmsh.model.mesh.setTransfiniteCurve(1, 8 * n + 1) gmsh.model.mesh.setTransfiniteCurve(4, 8 * n + 1) gmsh.model.mesh.setTransfiniteSurface(1) gmsh.model.mesh.setTransfiniteSurface(2) gmsh.model.mesh.setTransfiniteSurface(3) gmsh.model.mesh.setTransfiniteSurface(4) gmsh.model.mesh.setTransfiniteSurface(5) gmsh.model.mesh.setTransfiniteSurface(6) gmsh.model.occ.extrude([(2, 1), (2, 2), (2, 3), (2, 4), (2, 5), (2, 6)], 0, 0, w, [int(n)], [], True) gmsh.model.occ.synchronize() gmsh.model.addPhysicalGroup(3, [1, 2, 3, 4, 5, 6], 1) gmsh.model.setPhysicalName(3, 1, "bulk") gmsh.model.addPhysicalGroup(2, [7], 2) gmsh.model.setPhysicalName(2, 2, "base") gmsh.model.occ.synchronize() gmsh.option.setNumber("Mesh.RecombineAll", 1) gmsh.option.setNumber("Mesh.ElementOrder", 2) gmsh.model.mesh.generate(3) nodes, coord, parametric_coord = gmsh.model.mesh.getNodes() gmsh.write("fork.msh") gmsh.model.remove() #gmsh.fltk.run() gmsh.finalize() return len(nodes)
def optimize_mesh(in_file, out_file=None, method="", force=False, dim_tags=[], dim=3): """ Optimize a mesh using an optimizer See: https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/api/gmsh.py#L1444 Parameters ---------- in_file : Path or str path to .geo file to be optimized Note: You are unable to optimize .msh-files. Therefore, only .geo files can be passed. out_file : Path or str output file. By default, in_file+"optimized" method : str name of optimizer. Default: '' (gmsh default tetrahedral optimizer) Other options: 'Netgen': Netgen optimizer 'HighOrder': direct high-order mesh optimizer 'HighOrderElastic': high-order elastic smoother 'HighOrderFastCurving': fast curving algorithm 'Laplace2D': Laplace smoothing 'Relocate2D': Node relocation, 2d 'Relocate3D': Node relocation, 3d force : bool If set, apply the optimization also to discrete entities dim_tags : List If supplied, only apply the optimizer to the given entities dim : int Which dimension to mesh. Defaults to 3D. """ # Check in- and out-file paths in_file = Path(in_file) out_file = Path(out_file) assert in_file.is_file() assert in_file.suffix == ".geo" out_file.parent.mkdir(exist_ok=True, parents=True) out_file.with_suffix(".msh") print(out_file) # Optimize the mesh. import gmsh gmsh.initialize() # Mesh Statistics gmsh.option.setNumber("General.Terminal", 1) # gmsh.option.setNumber("Print.PostGamma", 1) # gmsh.option.setNumber("Print.PostEta", 1) # gmsh.option.setNumber("Print.PostSICN", 1) # gmsh.option.setNumber("Print.PostSIGE", 1) gmsh.open(str(in_file)) gmsh.model.mesh.generate(dim=dim) gmsh.model.mesh.optimize(method=method, force=force, dimTags=dim_tags) # Write to .msh and close gmsh gmsh.write(str(out_file)) gmsh.finalize()
def _finalize_gmsh(self): gmsh.finalize()
def __exit__(self, *a): # TODO remove once gmsh 4.7.0 is out # <https://gitlab.onelab.info/gmsh/gmsh/-/issues/1001> gmsh.option.setNumber("Mesh.CharacteristicLengthMin", 0.0) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", 1.0e22) gmsh.finalize()
def __exit__(self, *a): gmsh.finalize()
def PVSbrain_simulation(args): """ Test case for simple diffusion from PVS to the brain. Outputs : - a logfile with information about the simulation - .pvd files at specified args.toutput time period with the u, p and c fields in stokes domain and u, p , q, phi and c fields in Biot domain - .csv files of u, p, c 1D array of the u, p, c fields on the middle line of the PVS - .csv files of the total mass in the domain, mass flux from the brain to sas, brain to PVS, PVS to SAS """ # output folder name outputfolder = args.output_folder + '/' + args.job_name + '/' if not os.path.exists(outputfolder): os.makedirs(outputfolder) if not os.path.exists(outputfolder + '/fields'): os.makedirs(outputfolder + '/fields') # Create output files #pvd files c_out = File(outputfolder + 'fields' + '/c.pvd') facets_out_fluid = File(outputfolder + 'fields' + '/facets_fluid.pvd') facets_out_solid = File(outputfolder + 'fields' + '/facets_solid.pvd') # Create logger logger = logging.getLogger() logger.setLevel(logging.INFO) # log to a file now = datetime.now().strftime("%Y%m%d_%H%M%S") filename = os.path.join(outputfolder + '/', 'PVSBrain_info.log') file_handler = logging.FileHandler(filename, mode='w') file_handler.setLevel(logging.INFO) #formatter = logging.Formatter("%(asctime)s %(filename)s, %(lineno)d, %(funcName)s: %(message)s") #file_handler.setFormatter(formatter) logger.addHandler(file_handler) # log to the console console_handler = logging.StreamHandler() level = logging.INFO console_handler.setLevel(level) logger.addHandler(console_handler) # initialise logging logging.info( title1( "Test case of simple diffusion from PVS to the brain using the diffusion-advection solver" )) logging.info("Date and time:" + datetime.now().strftime("%m/%d/%Y, %H:%M:%S")) logging.info('Job name : ' + args.job_name) logging.debug('logging initialized') # Set parameters logging.info(title1("Parameters")) # Geometry params logging.info('\n * Geometry') Rv = args.radius_vessel # centimeters Rpvs = args.radius_pvs # centimeters Rbrain = args.radius_brain # centimeters L = args.length # centimeters logging.info('Vessel radius : %e cm' % Rv) logging.info('PVS radius : %e cm' % Rpvs) logging.info('Brain radius : %e cm' % Rbrain) logging.info('length : %e cm' % L) #Mesh logging.info('\n * Mesh') #number of cells in the radial direction fluid domain Nr = args.N_radial_fluid #number of cells in the radial direction biot domain Nr_biot = args.N_radial_biot s_biot = args.biot_progression DR = (Rpvs - Rv) / Nr #number of cells in the axial direction if args.N_axial: Nl = args.N_axial else: Nl = round(L / DR) DY = L / Nl logging.info('N axial: %i' % Nl) logging.info('N radial PVS: %e' % Nr) logging.info('N radial Biot: %e' % Nr_biot) logging.info('progression parameter in biot: %e' % s_biot) #time parameters logging.info('\n * Time') toutput = args.toutput tfinal = args.tend dt = args.time_step logging.info('final time: %e s' % tfinal) logging.info('output period : %e s' % toutput) logging.info('time step : %e s' % dt) dt_advdiff = dt logging.info('\n* Tracer properties') D = args.diffusion_coef sigma_gauss = args.sigma logging.info('Free diffusion coef: %e cm2/s' % D) logging.info('STD of initial gaussian profile: %e ' % sigma_gauss) xi_gauss = args.initial_pos logging.info('Initial position: %e cm2' % xi_gauss) logging.info('\n * Porous medium properties') porosity_0 = args.biot_porosity tortuosity = args.biot_tortuosity dt_solid = dt logging.info('initial porosity: %e ' % porosity_0) logging.info('tortuosity: %e ' % tortuosity) ## The tracer is solver in the full domain, so it has two subdomains # 1 for solid # 0 for fluid tracer_parameters = { 'kappa_0': D, 'kappa_1': D * tortuosity, 'dt': dt_advdiff, 'nsteps': 1 } # Mesh logging.info(title1('Meshing')) meshing = args.mesh_method ##### Meshing method : regular if meshing == 'regular': # Create a mesh using Rectangle mesh : all the cells are regulars but this means a lot of cells logging.info('cell size : %e cm' % (np.sqrt(DR**2 + DY**2))) # Create a rectangle mesh with Nr + Nsolid cells in the radias direction and Nl cells in the axial direction # the geometrical progression for the solid mesh is s #Creation of the uniform mesh Rext = 1 + Nr_biot / Nr mesh = RectangleMesh(Point(0, 0), Point(L, Rext), Nl, Nr + Nr_biot) x = mesh.coordinates()[:, 0] y = mesh.coordinates()[:, 1] #Deformation of the mesh def deform_mesh(x, y): transform_fluid = Rv + (Rpvs - Rv) * y transform_solid = Rpvs + (Rbrain - Rpvs) * ((y - 1) / (Rext - 1))**s_biot yp = np.where(y <= 1, transform_fluid, transform_solid) return [x, yp] x_bar, y_bar = deform_mesh(x, y) xy_bar_coor = np.array([x_bar, y_bar]).transpose() mesh.coordinates()[:] = xy_bar_coor mesh.bounding_box_tree().build(mesh) else: ##### Meshing method : gmsh with box for refinement from sleep.mesh import mesh_model2d, load_mesh2d, set_mesh_size import sys gmsh.initialize(['', '-format', 'msh2']) model = gmsh.model import math Apvs0 = math.pi * Rpvs**2 Av0 = math.pi * Rv**2 A0 = Apvs0 - Av0 # progressive mesh factory = model.occ a = factory.addPoint(0, Rv, 0) b = factory.addPoint(L, Rv, 0) c = factory.addPoint(L, Rpvs, 0) d = factory.addPoint(0, Rpvs, 0) e = factory.addPoint(L, Rbrain, 0) f = factory.addPoint(0, Rbrain, 0) fluid_lines = [ factory.addLine(*p) for p in ((a, b), (b, c), (c, d), (d, a)) ] named_lines = dict( zip(('bottom', 'pvs_right', 'interface', 'pvs_left'), fluid_lines)) fluid_loop = factory.addCurveLoop(fluid_lines) fluid = factory.addPlaneSurface([fluid_loop]) solid_lines = [ factory.addLine(*p) for p in ((d, c), (c, e), (e, f), (f, d)) ] named_lines.update( dict( zip(('interface', 'brain_right', 'brain_top', 'brain_left'), solid_lines))) solid_loop = factory.addCurveLoop(solid_lines) solid = factory.addPlaneSurface([solid_loop]) factory.synchronize() tags = {'cell': {'F': 1, 'S': 2}, 'facet': {}} model.addPhysicalGroup(2, [fluid], 1) model.addPhysicalGroup(2, [solid], 2) for name in named_lines: tag = named_lines[name] model.addPhysicalGroup(1, [tag], tag) # boxes for mesh refinement cell_size = DR * (Rpvs - Rv) / (Rpvs - Rv) boxes = [] # add box on the PVS for mesh field = model.mesh.field fid = 1 field.add('Box', fid) field.setNumber(fid, 'XMin', 0) field.setNumber(fid, 'XMax', L) field.setNumber(fid, 'YMin', Rv) field.setNumber(fid, 'YMax', Rpvs) field.setNumber(fid, 'VIn', cell_size * 2) field.setNumber(fid, 'VOut', DR * 50) field.setNumber(fid, 'Thickness', (Rpvs - Rv) / 4) boxes.append(fid) # Combine field.add('Min', fid + 1) field.setNumbers(fid + 1, 'FieldsList', boxes) field.setAsBackgroundMesh(fid + 1) model.occ.synchronize() h5_filename = outputfolder + '/mesh.h5' mesh_model2d(model, tags, h5_filename) mesh, markers, lookup = load_mesh2d(h5_filename) from IPython import embed embed() gmsh.finalize() ## Define subdomains x = mesh.coordinates()[:, 0] y = mesh.coordinates()[:, 1] tol = 1e-7 class Omega_0(SubDomain): def inside(self, x, on_boundary): return x[1] < Rpvs + tol class Omega_1(SubDomain): def inside(self, x, on_boundary): return x[1] > Rpvs - tol subdomains = MeshFunction("size_t", mesh, mesh.topology().dim(), 0) subdomain_0 = Omega_0() subdomain_1 = Omega_1() subdomain_0.mark(subdomains, 0) subdomain_1.mark(subdomains, 1) mesh_f = EmbeddedMesh(subdomains, 0) mesh_s = EmbeddedMesh(subdomains, 1) ## Define boundaries solid_bdries = MeshFunction("size_t", mesh_s, mesh_s.topology().dim() - 1, 0) fluid_bdries = MeshFunction("size_t", mesh_f, mesh_f.topology().dim() - 1, 0) full_bdries = MeshFunction("size_t", mesh, mesh.topology().dim() - 1, 0) # Label facets class Boundary_left_fluid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], 0, tol) and (x[1] <= Rpvs + tol ) #left fluid class Boundary_right_fluid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], L, tol) and (x[1] <= Rpvs + tol ) # right fluid class Boundary_left_solid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], 0, tol) and (x[1] >= Rpvs - tol ) #left solid class Boundary_right_solid(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[0], L, tol) and (x[1] >= Rpvs - tol ) # right solid class Boundary_bottom(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rv, tol) #bottom class Boundary_top(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rbrain, tol) #top class Boundary_interface(SubDomain): def inside(self, x, on_boundary): return on_boundary and near(x[1], Rpvs, tol) #interface #todo : keep separate F and S for right and left in the full bdries btop = Boundary_top() bbottom = Boundary_bottom() bleft_fluid = Boundary_left_fluid() bright_fluid = Boundary_right_fluid() bleft_solid = Boundary_left_solid() bright_solid = Boundary_right_solid() binterface = Boundary_interface() bbottom.mark(fluid_bdries, 2) binterface.mark(fluid_bdries, 4) bleft_fluid.mark(fluid_bdries, 1) bright_fluid.mark(fluid_bdries, 3) binterface.mark(solid_bdries, 4) bright_solid.mark(solid_bdries, 5) btop.mark(solid_bdries, 6) bleft_solid.mark(solid_bdries, 7) bleft_fluid.mark(full_bdries, 1) bleft_solid.mark(full_bdries, 7) bbottom.mark(full_bdries, 2) bright_fluid.mark(full_bdries, 3) bright_solid.mark(full_bdries, 5) btop.mark(full_bdries, 6) facet_lookup = { 'F_left': 1, 'F_bottom': 2, 'F_right': 3, 'Interface': 4, 'S_left': 7, 'S_top': 6, 'S_right': 5 } facets_out_fluid << fluid_bdries facets_out_solid << solid_bdries # define the domain specific parameters for the tracer # NOTE: Here we do P0 projection dx = Measure('dx', domain=mesh, subdomain_data=subdomains) CoefSpace = FunctionSpace(mesh, 'DG', 0) q = TestFunction(CoefSpace) # Remove coef = 'kappa' fluid_coef = tracer_parameters.pop('%s_0' % coef) solid_coef = tracer_parameters.pop('%s_1' % coef) form = ((1 / CellVolume(mesh)) * fluid_coef * q * dx(0) + (1 / CellVolume(mesh)) * solid_coef * q * dx(1)) tracer_parameters[coef] = Function(CoefSpace, assemble(form)) #FEM space logging.info(title1("Set FEM spaces")) logging.info('\n * Tracer') #### Todo : I would like to be able to have discontinuous concentration when we will have the membrane #### Beter to solve in two domains or one domain with discontinuous lagrange element ? Ct_elm = FiniteElement('Lagrange', triangle, 1) Ct = FunctionSpace(mesh, Ct_elm) logging.info('Concentration : "Lagrange", triangle, 1') #Advection velocity FS_advvel = VectorFunctionSpace(mesh, 'CG', 2) # Setup of boundary conditions logging.info(title1("Boundary conditions")) ## to do : allow zero concentration if fluid BC is free on the right logging.info('\n * Tracer concentration') bcs_tracer = { 'concentration': [(facet_lookup['S_top'], Constant(0))], 'flux': [ (facet_lookup['S_left'], Constant(0)), (facet_lookup['S_right'], Constant(0)), (facet_lookup['F_right'], Constant(0)), (facet_lookup['F_left'], Constant(0)), (facet_lookup['F_bottom'], Constant(0)), ] } # Initialisation : # 1 in the PVS cf_0 = Expression('x[1]<= Rpvs ? 1 : 0 ', degree=2, a=1 / 2 / sigma_gauss**2, b=xi_gauss, Rv=Rv, Rpvs=Rpvs) c_n = project(cf_0, Ct) # File('initial_reg.pvd') << c_n exit() #Initial deformation of the fluid domain # We start at a time shift tshift = 0 # c_n.rename("c", "tmp") c_out << (c_n, 0) ############# RUN ############3 logging.info(title1("Run")) # Time loop time = tshift timestep = 0 # Here I dont know if there will be several dt for advdiff and fluid solver while time < tfinal + tshift: time += dt timestep += 1 print('time', time - tshift) # Solve tracer problem tracer_parameters["T0"] = time advection_velocity = project(Constant((0, 0)), FS_advvel) c_, T0 = solve_adv_diff(Ct, velocity=advection_velocity, phi=Constant(0.2), f=Constant(0), c_0=c_n, phi_0=Constant(0.2), bdries=full_bdries, bcs=bcs_tracer, parameters=tracer_parameters) # Update current solution c_n.assign(c_) # Save output if (timestep % int(toutput / dt) == 0): logging.info("\n*** save output time %e s" % (time - tshift)) logging.info("number of time steps %i" % timestep) # may report Courant number or other important values that indicate how is doing the run c_n.rename("c", "tmp") c_out << (c_n, time - tshift) advection_velocity.rename("adv_vel", "tmp") File(outputfolder + 'fields' + '/adv_vel.pvd') << (advection_velocity, time - tshift)
def draw_GMSH( output, sym, boundary_prop, boundary_list, surface_label, is_antiper=False, is_remove_vent=False, is_remove_slotS=False, is_remove_slotR=False, is_lam_only_S=False, is_lam_only_R=False, kgeo_fineness=1, kmesh_fineness=1, user_mesh_dict={}, path_save="GMSH_model.msh", is_sliding_band=False, is_airbox=False, transform_list=[], is_set_labels=False, ): """Draws a machine mesh in GMSH format Parameters ---------- output : Output Output object sym : int the symmetry applied on the stator and the rotor (take into account antiperiodicity) boundary_prop : dict dictionary to match FEA boundary conditions (dict values) with line labels (dict keys) that are set in the build_geometry methods boundary_list : list list of boundary condition names surface_label : dict dict for the translation of the actual surface labels into FEA software compatible labels is_remove_vent : bool True to remove the ventilation ducts (Default value = False) is_remove_slotS : bool True to solve without slot effect on the Stator (Default value = False) is_remove_slotR : bool True to solve without slot effect on the Rotor (Default value = False) kgeo_fineness : float global coefficient to adjust geometry fineness kmesh_fineness : float global coefficient to adjust mesh fineness is_antiper: bool To apply antiperiodicity boundary conditions is_lam_only_S: bool Draw only stator lamination is_lam_only_R: bool Draw only rotor lamination Returns ------- GMSH_dict : dict dictionary containing the main parameters of GMSH File """ # check some input parameter if is_lam_only_S and is_lam_only_R: raise InputError( "Only 'is_lam_only_S' or 'is_lam_only_R' can be True at the same time" ) # get machine machine = output.simu.machine mesh_dict = {} tol = 1e-6 # Default stator mesh element size mesh_size_S = machine.stator.Rext / 100.0 mesh_size_R = machine.rotor.Rext / 25.0 # For readibility model = gmsh.model factory = model.geo # Start a new model gmsh.initialize() gmsh.option.setNumber("General.Terminal", int(False)) gmsh.option.setNumber("Geometry.CopyMeshingMethod", 1) gmsh.option.setNumber("Geometry.PointNumbers", 0) gmsh.option.setNumber("Geometry.LineNumbers", 0) gmsh.option.setNumber("Mesh.CharacteristicLengthMin", min(mesh_size_S, mesh_size_R)) gmsh.option.setNumber("Mesh.CharacteristicLengthMax", max(mesh_size_S, mesh_size_R)) model.add("Pyleecan") # build geometry alpha = 0 rotor_list = list() rotor_list.extend(machine.shaft.build_geometry(sym=sym, alpha=alpha)) rotor_list.extend(machine.rotor.build_geometry(sym=sym, alpha=alpha)) stator_list = list() stator_list.extend(machine.stator.build_geometry(sym=sym, alpha=alpha)) # set origin oo = factory.addPoint(0, 0, 0, 0, tag=-1) gmsh_dict = { 0: { "tag": 0, "label": "origin", "with_holes": False, 1: { "tag": 0, "n_elements": 1, "bc_name": None, "begin": { "tag": oo, "coord": complex(0.0, 0.0) }, "end": { "tag": None, "coord": None }, "cent": { "tag": None, "coord": None }, "arc_angle": None, "line_angle": None, }, } } nsurf = 0 # number of surfaces if not is_lam_only_S: for surf in rotor_list: nsurf += 1 # print(surf.label) gmsh_dict.update({ nsurf: { "tag": None, "label": surface_label.get(surf.label, "UNKNOWN"), } }) if "Lamination_Rotor" in surf.label: gmsh_dict[nsurf]["with_holes"] = True lam_rotor_surf_id = nsurf else: gmsh_dict[nsurf]["with_holes"] = False # comp. number of elements on the lines & override by user values in case mesh_dict = surf.comp_mesh_dict(element_size=mesh_size_R) if user_mesh_dict: # check if dict is not None nor empty mesh_dict.update(user_mesh_dict) # add all lines of the current surface to the gmsh_dict for line in surf.get_lines(): # When symmetry is 1 the shaft surface is substrtacted from Rotor Lam instead if sym == 1 and line.label == "Lamination_Rotor_Yoke_Radius_Int": continue n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line, boundary_prop) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_line_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size_R, n_elements=n_elem, bc=bc_name, ) elif isinstance(line, Arc) and (abs( line.get_angle() * 180.0 / cmath.pi) <= tol): # Don't draw anything, this is a circle and usually is repeated ? pass else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size_R, n_elements=n_elem, bc=bc_name, ) lam_and_holes = list() ext_lam_loop = None rotor_cloops = list() # loop though all (surface) entries of the rotor lamination for s_data in gmsh_dict.values(): lloop = [] # skip this surface dataset if it is the origin if s_data["label"] == "origin": continue # build a lineloop of the surfaces lines for lvalues in s_data.values(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) if "MAGNET" in s_data["label"]: rotor_cloops.extend([cloop]) # search for the holes to substract from rotor lam if "ROTOR_LAM" in s_data["label"]: ext_lam_loop = cloop else: # MachineSIPSM does not have holes in rotor lam # only shaft is taken out if symmetry is one if isinstance(machine, MachineSIPMSM): if sym == 1 and s_data["label"] == "SHAFT": lam_and_holes.extend([cloop]) else: if sym == 1: lam_and_holes.extend([cloop]) elif s_data["label"] != "SHAFT": lam_and_holes.extend([cloop]) else: pass # Shaft, magnets and magnet pocket surfaces are created if not is_lam_only_R: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # Finally rotor lamination is built if ext_lam_loop is not None: lam_and_holes.insert(0, ext_lam_loop) if len(lam_and_holes) > 0: gmsh_dict[lam_rotor_surf_id]["tag"] = factory.addPlaneSurface( lam_and_holes, tag=-1) pg = model.addPhysicalGroup(2, [gmsh_dict[lam_rotor_surf_id]["tag"]]) model.setPhysicalName(2, pg, gmsh_dict[lam_rotor_surf_id]["label"]) # rotor_cloops = lam_and_holes # store rotor dict rotor_dict = gmsh_dict.copy() # init new dict for stator gmsh_dict = { 0: { "tag": 0, "label": "origin", "with_holes": False, 1: { "tag": 0, "n_elements": 1, "bc_name": None, "begin": { "tag": oo, "coord": complex(0.0, 0.0) }, "end": { "tag": None, "coord": None }, "cent": { "tag": None, "coord": None }, }, } } # nsurf = 0 if not is_lam_only_R: stator_cloops = [] for surf in stator_list: nsurf += 1 gmsh_dict.update({ nsurf: { "tag": None, "label": surface_label.get(surf.label, "UNKNOWN"), } }) if surf.label.find("Lamination_Stator") != -1: gmsh_dict[nsurf]["with_holes"] = True else: gmsh_dict[nsurf]["with_holes"] = False # comp. number of elements on the lines & override by user values in case mesh_dict = surf.comp_mesh_dict(element_size=mesh_size_S) if user_mesh_dict: # check if dict is not None nor empty mesh_dict.update(user_mesh_dict) # add all lines of the current surface to the gmsh_dict for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line, boundary_prop) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_line_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size_S, n_elements=n_elem, bc=bc_name, ) else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size_S, n_elements=n_elem, bc=bc_name, ) for s_data in gmsh_dict.values(): lloop = [] # skip this surface dataset if it is the origin if s_data["label"] == "origin": continue # build a lineloop of the surfaces lines for lvalues in s_data.values(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) cloop = factory.addCurveLoop(lloop) stator_cloops.append(cloop) # Winding surfaces are created if (s_data["label"].find("STATOR_LAM") != -1) or (not is_lam_only_S): s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # stator_dict = gmsh_dict.copy() gmsh_dict.update(rotor_dict) if is_sliding_band and (not is_lam_only_R) and (not is_lam_only_S): sb_list = get_sliding_band(sym=sym, machine=machine) else: sb_list = [] # Default sliding mesh element size mesh_size = 2.0 * cmath.pi * machine.rotor.Rext / 360.0 # nsurf = 0 for surf in sb_list: nsurf += 1 gmsh_dict.update({ nsurf: { "tag": None, "label": surface_label.get(surf.label, "UNKNOWN"), } }) for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line, boundary_prop) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.is_trigo_direction == True else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_agline_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) else: _add_agline_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) for s_data in gmsh_dict.values(): lloop = [] # skip this surface dataset if it is the origin if s_data["label"] == "origin" or not ("AG_" in s_data["label"] or "SB_" in s_data["label"]): continue # build a lineloop of the surfaces lines for lvalues in s_data.values(): if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) if lloop: cloop = factory.addCurveLoop(lloop) if "AG_INT" in s_data["label"] and isinstance( machine, MachineSIPMSM): s_data["tag"] = factory.addPlaneSurface([cloop] + rotor_cloops, tag=-1) else: s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) if is_airbox and (not is_lam_only_R) and (not is_lam_only_S): ab_list = get_air_box(sym=sym, machine=machine) else: ab_list = [] # Default airbox mesh element size mesh_size = machine.stator.Rext / 50.0 for surf in ab_list: nsurf += 1 gmsh_dict.update({ nsurf: { "tag": None, "label": surface_label.get(surf.label, "UNKNOWN"), } }) for line in surf.get_lines(): n_elem = mesh_dict.get(line.label) n_elem = n_elem if n_elem is not None else 0 bc_name = get_boundary_condition(line, boundary_prop) # Gmsh built-in engine does not allow arcs larger than 180deg # so arcs are split into two if (isinstance(line, Arc) and abs(line.get_angle() * 180.0 / cmath.pi) >= 180.0): rot_dir = 1 if line.get_angle() > 0 else -1 arc1 = Arc2( begin=line.get_begin(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) arc2 = Arc2( begin=arc1.get_end(), center=line.get_center(), angle=rot_dir * cmath.pi / 2.0, label=line.label, ) for arc in [arc1, arc2]: _add_line_to_dict( geo=factory, line=arc, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) else: _add_line_to_dict( geo=factory, line=line, d=gmsh_dict, idx=nsurf, mesh_size=mesh_size, n_elements=n_elem, bc=bc_name, ) for s_id, s_data in gmsh_dict.items(): lloop = [] if s_id == 0: continue for lvalues in s_data.values(): if s_data["label"].find("AIRBOX") != -1: if type(lvalues) is not dict: continue lloop.extend([lvalues["tag"]]) else: continue if lloop: cloop = factory.addCurveLoop(lloop) s_data["tag"] = factory.addPlaneSurface([cloop], tag=-1) pg = model.addPhysicalGroup(2, [s_data["tag"]]) model.setPhysicalName(2, pg, s_data["label"]) # Set boundary conditions in gmsh lines for propname in boundary_list: bc_id = [] for s_data in gmsh_dict.values(): for lvalues in s_data.values(): if type(lvalues) is not dict: continue if lvalues["bc_name"] == propname: bc_id.extend([abs(lvalues["tag"])]) if bc_id: pg = model.addPhysicalGroup(1, bc_id) model.setPhysicalName(1, pg, propname) # Set all line labels as physical groups if is_set_labels: groups = {} for s_data in gmsh_dict.values(): for lvalues in s_data.values(): if (type(lvalues) is not dict or "label" not in lvalues or not lvalues["label"]): continue if lvalues["label"] not in groups.keys(): groups[lvalues["label"]] = [] groups[lvalues["label"]].append(abs(lvalues["tag"])) for label, tags in groups.items(): pg = model.addPhysicalGroup(1, tags) model.setPhysicalName(1, pg, label) factory.synchronize() # save mesh or geo file depending on file extension filename, file_extension = splitext(path_save) if file_extension == ".geo": gmsh.write(filename + ".geo_unrolled") replace(filename + ".geo_unrolled", filename + file_extension) else: gmsh.model.mesh.generate(2) gmsh.write(path_save) # gmsh.fltk.run() # Uncomment to launch Gmsh GUI gmsh.finalize() return gmsh_dict
def chain(self, maxElementSize=None): """Get a polygonal chain representing the speaker membrane.""" # check if cached polygonal chain is still good if not (maxElementSize is None or maxElementSize == self.maxElementSize): self.maxElementSize = maxElementSize self.aVertex = None self.aSegment = None self.nOpenElements = -1 if self.aVertex is None or self.aSegment is None or self.nOpenElements == -1: gmsh.initialize() gmsh.option.setNumber("General.Terminal", 1) gmsh.model.add("Cone Speaker") gmsh.model.geo.addPoint(0.0, 0.0, 0.0, self.maxElementSize, 1) gmsh.model.geo.addPoint(*self.coneEdge(), self.maxElementSize, 2) gmsh.model.geo.addPoint(*self.capEdge(), self.maxElementSize, 3) gmsh.model.geo.addPoint(*self.capSphereCenter(), self.maxElementSize, 4) gmsh.model.geo.addPoint(*self.capCenter(), self.maxElementSize, 5) gmsh.model.geo.addLine(1, 2, 1) gmsh.model.addPhysicalGroup(1, [1], 1) gmsh.model.setPhysicalName(1, 1, "Interface") gmsh.model.geo.addLine(2, 3, 2) gmsh.model.geo.addCircleArc(3, 4, 5, 3) gmsh.model.addPhysicalGroup(1, [2, 3], 2) gmsh.model.setPhysicalName(1, 2, "Speaker Cone") gmsh.model.geo.synchronize() gmsh.model.mesh.generate(1) nodeTags, coord, parametricCoord = gmsh.model.mesh.getNodes( 1, -1, True) self.aVertex = np.asarray(coord, dtype=np.float32).reshape( len(nodeTags), 3) # build reordering dictionary nodeTagToIdx = dict() for i, t in enumerate(nodeTags): nodeTagToIdx[t] = i aNodeTags = [] # extract "open elements" or "Interface" first elementTypes, elementTags, nodeTags = gmsh.model.mesh.getElements( 1, 1) assert len(elementTypes ) == 1 and elementTypes[0] == 1 # only line segments assert len(elementTags) == 1 assert len(nodeTags) == 1 and len( nodeTags[0]) == len(elementTags[0]) * 2 aNodeTags.extend(nodeTags[0]) # extract line segment node tags self.nOpenElements = len(elementTags[0]) # extract speaker elements elementTypes, elementTags, nodeTags = gmsh.model.mesh.getElements( 1, 2) assert len(elementTypes ) == 1 and elementTypes[0] == 1 # only line segments assert len(elementTags) == 1 assert len(nodeTags) == 1 and len( nodeTags[0]) == len(elementTags[0]) * 2 aNodeTags.extend(nodeTags[0]) # extract line segment node tags elementTypes, elementTags, nodeTags = gmsh.model.mesh.getElements( 1, 3) assert len(elementTypes ) == 1 and elementTypes[0] == 1 # only line segments assert len(elementTags) == 1 assert len(nodeTags) == 1 and len( nodeTags[0]) == len(elementTags[0]) * 2 aNodeTags.extend(nodeTags[0]) # extract line segment node tags # relabel node tags with index into vertex array aTempNodeTags = np.empty(len(aNodeTags), dtype=np.int32) for i, t in enumerate(aNodeTags): aTempNodeTags[i] = nodeTagToIdx[t] self.aSegment = aTempNodeTags.reshape(len(aNodeTags) // 2, 2) gmsh.finalize() self.oGeometry = Chain(self.aVertex.shape[0], self.aSegment.shape[0]) self.oGeometry.aVertex = self.aVertex[:, 1:3] self.oGeometry.aEdge = self.aSegment self.oGeometry.namedPartition['interface'] = (0, self.nOpenElements) self.oGeometry.namedPartition['cavity'] = (self.nOpenElements, self.aSegment.shape[0]) return self.oGeometry
def create_plate_mesh(plate, geom_repr="solid", max_size=0.1, order=1, algo=8, tol=1e-3, fem=None, interactive=False, gmsh_session=None): """ :param plate: :param gmsh_session: :param geom_repr: :param max_size: :param order: :param algo: Mesh algorithm :param tol: Maximum geometry tolerance :param fem: :param interactive: :type plate: ada.Plate :type fem: ada.fem.FEM :type gmsh_session: gmsh """ temp_dir = _Settings.temp_dir if gmsh_session is None: gmsh_session = _init_gmsh_session() option = gmsh_session.option model = gmsh_session.model option.setNumber("Mesh.Algorithm", algo) option.setNumber("Mesh.MeshSizeFromCurvature", True) option.setNumber("Mesh.MinimumElementsPerTwoPi", 12) option.setNumber("Mesh.MeshSizeMax", max_size) option.setNumber("Mesh.ElementOrder", order) option.setNumber("Mesh.SecondOrderIncomplete", 1) option.setNumber("Mesh.Smoothing", 3) option.setNumber("Geometry.Tolerance", tol) option.setNumber("Geometry.OCCImportLabels", 1) # import colors from STEP if geom_repr == "solid": option.setNumber("Geometry.OCCMakeSolids", 1) name = f"temp_{create_guid()}" plate.to_stp(temp_dir / name, geom_repr=geom_repr) gmsh_session.merge(str(temp_dir / f"{name}.stp")) factory = model.geo factory.synchronize() model.mesh.setRecombine(2, 1) model.mesh.generate(3) model.mesh.removeDuplicateNodes() if interactive: gmsh_session.fltk.run() if fem is None: gmsh_session.write(str(temp_dir / f"{name}.msh")) m = meshio.read(str(temp_dir / f"{name}.msh")) m.write(temp_dir / f"{name}.xdmf") gmsh.finalize() return None fem_set_name = f"{plate.name}_all" get_nodes_and_elements(gmsh_session, fem, fem_set_name=fem_set_name) femset = fem.elsets[fem_set_name] if geom_repr == "solid": fem_sec = FemSection( f"{plate.name}_sec", "solid", femset, plate.material, ) else: # geom_repr == "shell": fem_sec = FemSection(f"{plate.name}_sec", "shell", femset, plate.material, local_z=plate.n, thickness=plate.t, int_points=5) fem.add_section(fem_sec)
def discretize(self, g, data): # Get the dictionaries for storage of data and discretization matrices parameter_dictionary = data[pp.PARAMETERS][self.keyword] matrix_dictionary = data[pp.DISCRETIZATION_MATRICES][self.keyword] # The the local mesh arguments local_mesh_args = data.get("local_mesh_args", None) micro_network = parameter_dictionary[self.network_keyword] num_processes = parameter_dictionary.get("num_processes", 1) discr_ig = partial( self._discretize_interaction_region, g, micro_network, parameter_dictionary, local_mesh_args, ) if num_processes == 1: # run the code in serial, useful also for debug out = [] for reg in self._interaction_regions(g): out.append(discr_ig(reg)) else: # run the code in parallel with mp.Pool(processes=num_processes) as p: out = p.map(discr_ig, list(self._interaction_regions(g))) # Finalize gmsh now that we're done with the discretization. gmsh.finalize() # Also inform the Gmsh writer we have finalized gmsh. pp.fracs.gmsh_interface.GmshWriter.gmsh_initialized = False # Data structures to build up the flux discretization as a sparse coo-matrix rows_flux, cols_flux, data_flux = [], [], [] # Data structures for the discretization of boundary conditions rows_bound, cols_bound, data_bound = [], [], [] # data structures for discretization of pressure trace operator on macro boundaries rows_cell_trace, cols_cell_trace, data_cell_trace = [], [], [] rows_face_trace, cols_face_trace, data_face_trace = [], [], [] # unpack all the values for reg_values in out: cell, bound, cell_trace, face_trace = reg_values cols_flux += cell[0] rows_flux += cell[1] data_flux += cell[2] cols_bound += bound[0] rows_bound += bound[1] data_bound += bound[2] cols_cell_trace += cell_trace[0] rows_cell_trace += cell_trace[1] data_cell_trace += cell_trace[2] cols_face_trace += face_trace[0] rows_face_trace += face_trace[1] data_face_trace += face_trace[2] # Construct the global matrix flux = sps.coo_matrix((data_flux, (rows_flux, cols_flux)), shape=(g.num_faces, g.num_cells)).tocsr() # Construct the global flux matrix bound_flux = sps.coo_matrix((data_bound, (rows_bound, cols_bound)), shape=(g.num_faces, g.num_faces)).tocsr() bound_pressure_cell = sps.coo_matrix( (data_cell_trace, (rows_cell_trace, cols_cell_trace)), shape=(g.num_faces, g.num_cells), ).tocsr() bound_pressure_face = sps.coo_matrix( (data_face_trace, (rows_face_trace, cols_face_trace)), shape=(g.num_faces, g.num_faces), ).tocsr() # For Neumann boundaries, we should not use the flux discretization (the flux # is known). The boundary discretization is modified to simply reuse the flux. bc = parameter_dictionary["bc"] neumann_faces = np.where(bc.is_neu)[0] pp.fvutils.zero_out_sparse_rows(flux, neumann_faces) # Set sign of Neumann faces according to the divergence operator. This will # counteract minus signs in the divergence during assembly. sgn, _ = g.signs_and_cells_of_boundary_faces(neumann_faces) # Here we also zero out non-diagonal terms, but these should be zero anyhow # (by construction of the coarse problems), but better safe than sorry bound_flux = pp.fvutils.zero_out_sparse_rows(bound_flux, neumann_faces, diag=sgn) matrix_dictionary[self.flux_matrix_key] = flux matrix_dictionary[self.bound_flux_matrix_key] = bound_flux matrix_dictionary[ self.bound_pressure_cell_matrix_key] = bound_pressure_cell matrix_dictionary[ self.bound_pressure_face_matrix_key] = bound_pressure_face # Empty discretization of vector sources - we will not provide this for the # foreseeable future. matrix_dictionary[self.vector_source_matrix_key] = sps.csc_matrix( (g.num_faces, g.num_cells * g.dim)) matrix_dictionary[ self.bound_pressure_vector_source_matrix_key] = sps.csc_matrix( (g.num_faces, g.num_cells * g.dim))
def generalized_mesher(elements, fem, geom_repr="solid", max_size=0.1, order=1, algo=8, tol=1e-3, interactive=False, gmsh_session=None): """ :param elements: :param gmsh_session: :param geom_repr: :param max_size: :param order: :param algo: Mesh algorithm :param tol: Maximum geometry tolerance :param interactive: :type elements: list :type fem: ada.fem.FEM :type gmsh_session: gmsh """ from ada import Beam if gmsh_session is None: gmsh_session = _init_gmsh_session() temp_dir = _Settings.temp_dir name = fem.parent.name.replace("/", "") + f"_{create_guid()}" option = gmsh_session.option model = gmsh_session.model option.setNumber("Mesh.Algorithm", algo) option.setNumber("Mesh.MeshSizeFromCurvature", True) option.setNumber("Mesh.MinimumElementsPerTwoPi", 12) option.setNumber("Mesh.MeshSizeMax", max_size) option.setNumber("Mesh.ElementOrder", order) option.setNumber("Mesh.SecondOrderIncomplete", 1) option.setNumber("Mesh.Smoothing", 3) option.setNumber("Geometry.Tolerance", tol) option.setNumber("Geometry.OCCImportLabels", 1) # import colors from STEP if geom_repr == "solid": option.setNumber("Geometry.OCCMakeSolids", 1) model.add(name) p = Part("DummyPart") / elements if geom_repr in ["shell", "solid"]: p.to_stp(temp_dir / name, geom_repr=geom_repr) gmsh_session.open(str(temp_dir / f"{name}.stp")) else: # beam for el in elements: if type(el) is Beam: p1, p2 = el.n1.p, el.n2.p s = get_point(p1, gmsh_session) e = get_point(p2, gmsh_session) if len(s) == 0: s = [(0, model.geo.addPoint(*p1.tolist(), max_size))] if len(e) == 0: e = [(0, model.geo.addPoint(*p2.tolist(), max_size))] # line = model.geo.addLine(s[0][1], e[0][1]) model.geo.synchronize() model.mesh.setRecombine(3, 1) model.mesh.generate(3) model.mesh.removeDuplicateNodes() if interactive: gmsh_session.fltk.run() get_nodes_and_elements(gmsh_session, fem, name) if fem is None: gmsh_session.write(str(temp_dir / f"{name}.msh")) m = meshio.read(str(temp_dir / f"{name}.msh")) m.write(temp_dir / f"{name}.xdmf") gmsh.finalize() return None # TODO: Identify which part of cross section the entities belong to and add physical group to assign section props # Alternatively it might be more simple to just build the geometries from scratch. # p1, p2 = beam.n1.p, beam.n2.p if geom_repr == "solid": NotImplementedError("") elif geom_repr == "shell": # Get element section properties ents = gmsh.model.occ.getEntities(2) for dim, ent in ents: r = model.occ.getCenterOfMass(2, ent) for el in elements: if type(el) is Beam: elname = el.name.replace("/", "") t, n, c = eval_thick_normal_from_cog_of_beam_plate(el, r) name = model.getEntityName(dim, ent) tags, coord, param = model.mesh.getNodes(2, ent, True) # get surface normal on all nodes, i.e. including on the geometrical # singularities (edges/points) # normals = gmsh.model.getNormal(ent, param) # curv = gmsh.model.getCurvature(2, ent, param) # print(ent, r) elemTypes, elemTags, elemNodeTags = model.mesh.getElements( 2, ent) femset = FemSet(f"{elname}_ent{ent}", [ fem.elements.from_id(x) for x in chain.from_iterable(elemTags) ], "elset") fem.add_set(femset) fem_sec = FemSection( f"{elname}_{c}_{ent}", "shell", femset, el.material, local_z=n, thickness=t, int_points=5, ) fem.add_section(fem_sec) else: # geom_repr == "beam": raise NotImplementedError() gmsh.finalize()
for i in range(1, 11): factory.addPoint(i, math.sin(i/9.*2.*math.pi), 0, 0.1, i) factory.addSpline(range(1, 11), 1) factory.addBSpline(range(1, 11), 2) factory.addBezier(range(1, 11), 3) factory.addPoint(0.2,-1.6,0,0.1, 101) factory.addPoint(1.2,-1.6,0,0.1, 102) factory.addPoint(1.2,-1.1,0,0.1, 103) factory.addPoint(0.3,-1.1,0,0.1, 104) factory.addPoint(0.7,-1,0,0.1, 105) # periodic bspline through the control points factory.addSpline([103,102,101,104,105,103], 100) # periodic bspline from given control points and default parameters - will # create a new vertex factory.addBSpline([103,102,101,104,105,103], 101) # general bspline with explicit degree, knots and multiplicities factory.addPoint(0,-2,0,0.1,201) factory.addPoint(1,-2,0,0.1,202) factory.addPoint(1,-3,0,0.1,203) factory.addPoint(0,-3,0,0.1,204) factory.addBSpline([201,202,203,204], 200, 2, [], [0,0.5,1], [3,1,3]) factory.synchronize() gmsh.fltk.run() gmsh.finalize()
def mesh_w_tube(mesh_params, mesh_name): filename_stem = __file__.split(".")[0] case_stem = f"{filename_stem}_{mesh_name}" gmsh.initialize() model.add(case_stem) inner_radius = physical_params.r - physical_params.thickness outer_radius = physical_params.r # 1 origin = -physical_params.R, 0.0, 0.0 axis_dir = 0.0, 1.0, 0.0 center_of_rot = 0.0, 0.0, 0.0 rot_normal = 0.0, 0.0, -1.0 make_elbow(origin, axis_dir, inner_radius, outer_radius, center_of_rot, rot_normal, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_axial) # 2 origin = physical_params.R, -physical_params.l1, 0.0 axis_dir = 0.0, 1.0, 0.0 height = physical_params.l1 make_tube(origin, axis_dir, inner_radius, outer_radius, height, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_layers_2_3) # 3 origin = -physical_params.R, -physical_params.l1, 0.0 axis_dir = 0.0, 1.0, 0.0 height = physical_params.l1 make_tube(origin, axis_dir, inner_radius, outer_radius, height, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_layers_2_3) # 4 origin = -3.0 * physical_params.R, -physical_params.l1, 0.0 axis_dir = 0.0, 1.0, 0.0 center_of_rot = -2.0 * physical_params.R, -physical_params.l1, 0.0 rot_normal = 0.0, 0.0, 1.0 make_elbow(origin, axis_dir, inner_radius, outer_radius, center_of_rot, rot_normal, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_axial) # 5 origin = 3.0 * physical_params.R, -physical_params.l1, 0.0 axis_dir = 0.0, 1.0, 0.0 center_of_rot = 2.0 * physical_params.R, -physical_params.l1, 0.0 rot_normal = 0.0, 0.0, -1.0 make_elbow(origin, axis_dir, inner_radius, outer_radius, center_of_rot, rot_normal, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_axial) # 6 origin = 3.0 * physical_params.R, physical_params.l2, 0.0 axis_dir = 0.0, -1.0, 0.0 height = physical_params.l1 + physical_params.l2 make_tube(origin, axis_dir, inner_radius, outer_radius, height, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_layers_6_7) # 7 origin = -3.0 * physical_params.R, physical_params.l2, 0.0 axis_dir = 0.0, -1.0, 0.0 height = physical_params.l1 + physical_params.l2 make_tube(origin, axis_dir, inner_radius, outer_radius, height, mesh_params.n_rings, mesh_params.n_sectors, mesh_params.n_layers_6_7) model.geo.synchronize() model.mesh.generate(3) gmsh.write(f"{case_stem}.msh") gmsh.finalize()