Generate mesh with perfect radial symmetry

Hello,
I would like to generate a two-dimensional ring mesh with perfect radial symmetry. I manage to generate a ring mesh it with the following code

import argparse
import gmsh
import warnings

parser = argparse.ArgumentParser()
parser.add_argument("resolution")
args = parser.parse_args()

warnings.filterwarnings("ignore")
gmsh.initialize()

gmsh.model.add("my model")
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
resolution = (float)(args.resolution)

disk_r = gmsh.model.occ.addDisk( c_r[0], c_r[1], c_r[2], r, r )
disk_R = gmsh.model.occ.addDisk( c_R[0], c_R[1], c_R[2], R, R )
ring = gmsh.model.occ.cut( [(2, disk_R)], [(2, disk_r)] )

gmsh.model.occ.synchronize()



surfaces = gmsh.model.occ.getEntities(dim=2)
assert surfaces == ring[0]
disk_subdomain_id = 0
gmsh.model.addPhysicalGroup( surfaces[0][0], [surfaces[0][1]], disk_subdomain_id )
gmsh.model.setPhysicalName( surfaces[0][0], disk_subdomain_id, "disk" )

lines = gmsh.model.occ.getEntities(dim=1)
#these are the subdomain_ids with which the components will be read in read_2dmesh_ring.py
circle_r_subdomain_id = 1
circle_R_subdomain_id = 2
gmsh.model.addPhysicalGroup( lines[0][0], [lines[0][1]], circle_r_subdomain_id )
gmsh.model.setPhysicalName( lines[0][0], disk_subdomain_id, "circle_r" )
gmsh.model.addPhysicalGroup( lines[1][0], [lines[1][1]], circle_R_subdomain_id )
gmsh.model.setPhysicalName( lines[1][0], disk_subdomain_id, "circle_R" )




#set the resolution
distance = gmsh.model.mesh.field.add("Distance")
gmsh.model.mesh.field.setNumbers(distance, "FacesList", [surfaces[0][0]])

threshold = gmsh.model.mesh.field.add("Threshold")
gmsh.model.mesh.field.setNumber(threshold, "IField", distance)
gmsh.model.mesh.field.setNumber(threshold, "LcMin", resolution)
gmsh.model.mesh.field.setNumber(threshold, "LcMax", resolution)
gmsh.model.mesh.field.setNumber(threshold, "DistMin", 0.5 * r)
gmsh.model.mesh.field.setNumber(threshold, "DistMax", r)

circle_r_dist = gmsh.model.mesh.field.add("Distance")
circle_r_threshold = gmsh.model.mesh.field.add( "Threshold" )

gmsh.model.mesh.field.setNumber( circle_r_threshold, "IField", circle_r_dist )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "LcMin", resolution )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "LcMax", resolution )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "DistMin", 0.1 )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "DistMax", 0.5 )

minimum = gmsh.model.mesh.field.add("Min")
gmsh.model.mesh.field.setNumbers( minimum, "FieldsList", [threshold, circle_r_threshold] )
gmsh.model.mesh.field.setAsBackgroundMesh(minimum)


gmsh.model.occ.synchronize()
gmsh.model.mesh.generate(2)

gmsh.write("solution/mesh.msh")

which runs ok:

$ python3 generate_2dmesh_ring.py 0.1
Info    : Meshing 1D...                                                                                                                        
Info    : [  0%] Meshing curve 1 (Ellipse)
Info    : [ 60%] Meshing curve 2 (Ellipse)
Info    : Done meshing 1D (Wall 0.00238325s, CPU 0.002387s)
Info    : Meshing 2D...
Info    : Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0338327s, CPU 0.033824s)
Info    : 1229 nodes 2460 elements
Info    : Writing 'solution/mesh.msh'...
Info    : Done writing 'solution/mesh.msh'

However, as you can see from the image attached, the mesh is not radially symmetric: there are some ‘impurities,’ which break the radial symmetry (in red)

How may I modify the code above so as to obtain a mesh radially symmetric ?

FYI, no internet search provided an answer to this. AI tools suggested a method based on transfinite Surfaces which did not work.

I also tried to define a perfectly symmetric disk by approximating as a polygon: a hexagon is perfectly symmetric, but polygons with larger number of sides, even if multiples of three, are not.

Thank you

This is a pure meshing question,and is better asked to either the GMSH developers (Issues · gmsh / gmsh · GitLab) or another meshing software (for instance netgen: Netgen - Netgen/NGSolve).

1 Like

I managed to generate the mesh by adding multiple radial lines which, if close enough to each other, force the meshing algorithm to tessellate uniformly the ring, and gives a perfect radial symmetry:

import meshio
import argparse
import numpy as np
import gmsh
import warnings
from fenics import *
import sys

# add the path where to find the shared modules
module_path = '/home/fenics/shared/modules'
sys.path.append( module_path )

import input_output as io
import mesh as msh


parser = argparse.ArgumentParser()
parser.add_argument( "resolution" )
args = parser.parse_args()

msh_file_path = "solution/mesh.msh"

warnings.filterwarnings( "ignore" )
gmsh.initialize()

gmsh.model.add( "my model" )
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
N = 64
resolution = (float)( args.resolution )
print( f"Mesh resolution = {resolution}" )

delta_theta = 2 * np.pi / N

def Q(theta):
    return np.array( [[np.cos( theta ), -np.sin( theta )], [np.sin( theta ), np.cos( theta )]] )

x_r = np.array( [r+resolution, 0] )
x_R = np.array( [R-resolution, 0] )


disk_r = gmsh.model.occ.addDisk( c_r[0], c_r[1], c_r[2], r, r )
disk_R = gmsh.model.occ.addDisk( c_R[0], c_R[1], c_R[2], R, R )
# add this every time you add a component to the mesh and every time you make modifications to the mesh
gmsh.model.occ.synchronize()

ring = gmsh.model.occ.cut( [(2, disk_R)], [(2, disk_r)] )
gmsh.model.occ.synchronize()

print("Starting loop over circle ... ")
for i in range(N):

    Q_x_r = Q( i * delta_theta ).dot( x_r )
    Q_x_R = Q( i * delta_theta ).dot( x_R )

    p_r = gmsh.model.occ.addPoint( Q_x_r[0], Q_x_r[1], 0 )
    p_R = gmsh.model.occ.addPoint( Q_x_R[0], Q_x_R[1], 0 )
    gmsh.model.occ.synchronize()

    line_r_R = gmsh.model.occ.addLine( p_r, p_R )
    gmsh.model.occ.synchronize()

    gmsh.model.mesh.embed( 1, [line_r_R], 2, ring[0][0][0] )
    gmsh.model.occ.synchronize()
print("... done.")

# add 2-dimensional objects
surfaces = gmsh.model.occ.getEntities( dim=2 )
assert surfaces == ring[0]
disk_subdomain_id = 1

gmsh.model.addPhysicalGroup( surfaces[0][0], [surfaces[0][1]], disk_subdomain_id )
gmsh.model.setPhysicalName( surfaces[0][0], disk_subdomain_id, "disk" )

# add 1-dimensional objects
lines = gmsh.model.occ.getEntities( dim=1 )
circle_r_subdomain_id = 2
circle_R_subdomain_id = 3
# line_p_1_p_2_subdomain_id = 3

gmsh.model.addPhysicalGroup( lines[0][0], [lines[0][1]], circle_r_subdomain_id )
gmsh.model.setPhysicalName( lines[0][0], circle_r_subdomain_id, "circle_r" )
gmsh.model.addPhysicalGroup( lines[1][0], [lines[1][1]], circle_R_subdomain_id )
gmsh.model.setPhysicalName( lines[1][0], circle_R_subdomain_id, "circle_R" )

# add 0-dimensional objects
vertices = gmsh.model.occ.getEntities( dim=0 )

# set the resolution
distance = gmsh.model.mesh.field.add( "Distance" )
gmsh.model.mesh.field.setNumbers( distance, "FacesList", [surfaces[0][0]] )

threshold = gmsh.model.mesh.field.add( "Threshold" )
gmsh.model.mesh.field.setNumber( threshold, "IField", distance )
gmsh.model.mesh.field.setNumber( threshold, "LcMin", resolution )
gmsh.model.mesh.field.setNumber( threshold, "LcMax", resolution )
gmsh.model.mesh.field.setNumber( threshold, "DistMin", 0.5 * r )
gmsh.model.mesh.field.setNumber( threshold, "DistMax", r )

circle_r_dist = gmsh.model.mesh.field.add( "Distance" )
circle_r_threshold = gmsh.model.mesh.field.add( "Threshold" )

gmsh.model.mesh.field.setNumber( circle_r_threshold, "IField", circle_r_dist )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "LcMin", resolution )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "LcMax", resolution )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "DistMin", 0.1 )
gmsh.model.mesh.field.setNumber( circle_r_threshold, "DistMax", 0.5 )

minimum = gmsh.model.mesh.field.add( "Min" )
gmsh.model.mesh.field.setNumbers( minimum, "FieldsList", [threshold, circle_r_threshold] )
gmsh.model.mesh.field.setAsBackgroundMesh( minimum )

gmsh.model.occ.synchronize()
gmsh.model.mesh.generate( 2 )
gmsh.write( msh_file_path )





mesh_from_file = meshio.read( msh_file_path )

# create a triangle mesh in which the surfaces will be stored
triangle_mesh = msh.create_mesh( mesh_from_file, "triangle", prune_z=True )
meshio.write( "solution/triangle_mesh.xdmf", triangle_mesh )

# create a line mesh
line_mesh = msh.create_mesh( mesh_from_file, "line", True )
meshio.write( "solution/line_mesh.xdmf", line_mesh )

# create a vertex mesh
'''
vertex_mesh = msh.create_mesh( mesh_from_file, "vertex", True )
meshio.write( "solution/vertex_mesh.xdmf", vertex_mesh )
print( f"Check if all line vertices are triangle vertices : {np.isin( msh.line_vertices( msh_file_path ), msh.triangle_vertices( msh_file_path ) )}" )
'''

#print the mesh vertices to file
mesh = Mesh()
msh.read_mesh(mesh, "solution/triangle_mesh.xdmf")
io.print_vertices_to_csv_file(mesh, "solution/vertices.csv" )

which runs

$ rm -r solution; mkdir solution; python3 generate_mesh.py 0.1
Mesh resolution = 0.1
Starting loop over circle ...                                                                                                                  
... done.
Info    : Meshing 1D...
Info    : [  0%] Meshing curve 1 (Ellipse)
Info    : [ 10%] Meshing curve 2 (Ellipse)
Info    : [ 10%] Meshing curve 3 (Line)
Info    : [ 10%] Meshing curve 4 (Line)
Info    : [ 10%] Meshing curve 5 (Line)
Info    : [ 10%] Meshing curve 6 (Line)
Info    : [ 10%] Meshing curve 7 (Line)
Info    : [ 20%] Meshing curve 8 (Line)
Info    : [ 20%] Meshing curve 9 (Line)
Info    : [ 20%] Meshing curve 10 (Line)
Info    : [ 20%] Meshing curve 11 (Line)
Info    : [ 20%] Meshing curve 12 (Line)
Info    : [ 20%] Meshing curve 13 (Line)
Info    : [ 20%] Meshing curve 14 (Line)
Info    : [ 30%] Meshing curve 15 (Line)
Info    : [ 30%] Meshing curve 16 (Line)
Info    : [ 30%] Meshing curve 17 (Line)
Info    : [ 30%] Meshing curve 18 (Line)
Info    : [ 30%] Meshing curve 19 (Line)
Info    : [ 30%] Meshing curve 20 (Line)
Info    : [ 40%] Meshing curve 21 (Line)
Info    : [ 40%] Meshing curve 22 (Line)
Info    : [ 40%] Meshing curve 23 (Line)
Info    : [ 40%] Meshing curve 24 (Line)
Info    : [ 40%] Meshing curve 25 (Line)
Info    : [ 40%] Meshing curve 26 (Line)
Info    : [ 40%] Meshing curve 27 (Line)
Info    : [ 50%] Meshing curve 28 (Line)
Info    : [ 50%] Meshing curve 29 (Line)
Info    : [ 50%] Meshing curve 30 (Line)
Info    : [ 50%] Meshing curve 31 (Line)
Info    : [ 50%] Meshing curve 32 (Line)
Info    : [ 50%] Meshing curve 33 (Line)
Info    : [ 60%] Meshing curve 34 (Line)
Info    : [ 60%] Meshing curve 35 (Line)
Info    : [ 60%] Meshing curve 36 (Line)
Info    : [ 60%] Meshing curve 37 (Line)
Info    : [ 60%] Meshing curve 38 (Line)
Info    : [ 60%] Meshing curve 39 (Line)
Info    : [ 60%] Meshing curve 40 (Line)
Info    : [ 70%] Meshing curve 41 (Line)
Info    : [ 70%] Meshing curve 42 (Line)
Info    : [ 70%] Meshing curve 43 (Line)
Info    : [ 70%] Meshing curve 44 (Line)
Info    : [ 70%] Meshing curve 45 (Line)
Info    : [ 70%] Meshing curve 46 (Line)
Info    : [ 70%] Meshing curve 47 (Line)
Info    : [ 80%] Meshing curve 48 (Line)
Info    : [ 80%] Meshing curve 49 (Line)
Info    : [ 80%] Meshing curve 50 (Line)
Info    : [ 80%] Meshing curve 51 (Line)
Info    : [ 80%] Meshing curve 52 (Line)
Info    : [ 80%] Meshing curve 53 (Line)
Info    : [ 90%] Meshing curve 54 (Line)
Info    : [ 90%] Meshing curve 55 (Line)
Info    : [ 90%] Meshing curve 56 (Line)
Info    : [ 90%] Meshing curve 57 (Line)
Info    : [ 90%] Meshing curve 58 (Line)
Info    : [ 90%] Meshing curve 59 (Line)
Info    : [ 90%] Meshing curve 60 (Line)
Info    : [100%] Meshing curve 61 (Line)
Info    : [100%] Meshing curve 62 (Line)
Info    : [100%] Meshing curve 63 (Line)
Info    : [100%] Meshing curve 64 (Line)
Info    : [100%] Meshing curve 65 (Line)
Info    : [100%] Meshing curve 66 (Line)
Info    : Done meshing 1D (Wall 0.008609s, CPU 0.007712s)
Info    : Meshing 2D...
Info    : Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0775154s, CPU 0.077512s)
Info    : 1347 nodes 3336 elements
Info    : Writing 'solution/mesh.msh'...
Info    : Done writing 'solution/mesh.msh'

and yields a symmetric mesh, here is mesh.msh opened with Gmsh:

Hello Mekong,

I am encountering an issue with maintaining rotational invariance and your mesh generation seems the right way to do that. Do you happen to have a gmsh file that can generate this mesh and willing to share?

Many thanks in advance for the help!

Hello, sorry for the late reply. Sure, the code is freely available in IRENE’s repo. If you have any troubles running it, please let me know.

1 Like