Fail to read tagged facets from mesh generated with GMSH

,

Hello,
I would like to generate a 2d mesh by using gmsh.model.occ but I am having some issues. I generate the mesh with

import meshio
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 )
print( f"Mesh resolution = {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)] )

p_1 = gmsh.model.occ.addPoint( (r + R) / 2.0, 0, 0 )
p_2 = gmsh.model.occ.addPoint( (r + R) / 2.0, r / 10.0, 0 )
line_p_1_p_2 = gmsh.model.occ.addLine( p_1, p_2 )

gmsh.model.occ.synchronize()

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

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 = 1
circle_R_subdomain_id = 2
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" )

gmsh.model.addPhysicalGroup( lines[2][0], [lines[2][1]], line_p_1_p_2_subdomain_id )
gmsh.model.setPhysicalName( lines[2][0], line_p_1_p_2_subdomain_id, "line_p_1_p_2" )

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

gmsh.model.addPhysicalGroup( vertices[0][0], [vertices[0][1]], p_1_subdomain_id )
gmsh.model.setPhysicalName( vertices[0][0], p_1_subdomain_id, "p_1" )

gmsh.model.addPhysicalGroup( vertices[1][0], [vertices[1][1]], p_2_subdomain_id )
gmsh.model.setPhysicalName( vertices[1][0], p_2_subdomain_id, "p_2" )

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

gmsh.write( "mesh.msh" )


def create_mesh(mesh, cell_type, prune_z=False):
    cells = mesh.get_cells_type( cell_type )
    cell_data = mesh.get_cell_data( "gmsh:physical", cell_type )
    out_mesh = meshio.Mesh( points=mesh.points, cells={
        cell_type: cells}, cell_data={"name_to_read": [cell_data]} )
    return out_mesh


mesh_from_file = meshio.read( "mesh.msh" )

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

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

# create a vertex mesh
vertex_mesh = create_mesh( mesh_from_file, "vertex", True )
meshio.write( "vertex_mesh.xdmf", vertex_mesh )

which creates the mesh and stores its triangles, lines and vertices in the respective xdmf files:

$ python3 generate_2dmesh_ring.py 0.1
Mesh resolution = 0.1
Info    : Meshing 1D...                                                                                                                        
Info    : [  0%] Meshing curve 1 (Ellipse)
Info    : [ 40%] Meshing curve 2 (Ellipse)
Info    : [ 70%] Meshing curve 3 (Line)
Info    : Done meshing 1D (Wall 0.00192213s, CPU 0.001924s)
Info    : Meshing 2D...
Info    : Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0109537s, CPU 0.010954s)
Info    : 61 nodes 123 elements
Info    : Writing 'mesh.msh'...
Info    : Done writing 'mesh.msh'

However, when I try to read it back and tag dx and the dss with the following code

from fenics import *
from mshr import *
from dolfin import *
from dolfin import *
import numpy as np

#CHANGE PARAMETERS HERE
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
#CHANGE PARAMETERS HERE

# read the mesh
mesh = Mesh()
xdmf = XDMFFile( mesh.mpi_comm(), "triangle_mesh.xdmf" )
xdmf.read( mesh )

print(f"Mesh dimension = {mesh.topology().dim()}")

# read the triangles
mvc = MeshValueCollection( "size_t", mesh, mesh.topology().dim() )
with XDMFFile( "triangle_mesh.xdmf" ) as infile:
    infile.read( mvc, "name_to_read" )
vf = cpp.mesh.MeshFunctionSizet( mesh, mvc )
xdmf.close()

#read the lines
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim()-1)
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
cf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
xdmf.close()

#read the vertices
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim()-2)
with XDMFFile("vertex_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
pf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
xdmf.close()


#analytical expression for a  scalar function used to test the ds
class FunctionTestIntegral(UserExpression):
    def eval(self, values, x):
        c_test = [0.3, 0.76, 0]
        r_test = 0.345

        values[0] = np.cos(geo.my_norm(np.subtract(x, c_test)) - r_test)**2.0
    def value_shape(self):
        return (1,)



dx = Measure( "dx", domain=mesh, subdomain_data=vf, subdomain_id=6   )
ds_r = Measure( "ds", domain=mesh, subdomain_data=cf, subdomain_id=1 )
ds_R = Measure( "ds", domain=mesh, subdomain_data=cf, subdomain_id=2 )
ds_custom = Measure("ds", domain=mesh, subdomain_data=pf)    # Point measure for points at the edges of the mesh
dS_custom = Measure("dS", domain=mesh, subdomain_data=pf)    # Point measure for points in the mesh



Q = FunctionSpace( mesh, 'P', 1 )

f_test_ds = Function( Q )
f_test_ds.interpolate( FunctionTestIntegral( element=Q.ufl_element() ))

print(f"int f dx = {assemble(f_test_ds * dx)}")
print(f"int f ds_r = {assemble(f_test_ds * ds_r)}")
print(f"int f ds_p_1 = {assemble(f_test_ds * ds_custom)}")

To clarify, in the last line with I would like assemble(f_test_ds * ds_custom) return the value of f at the point p_1 previously defined.

When I run the latter code, I get the following error

Mesh dimension = 2
Traceback (most recent call last):
  File "read_2dmesh_ring.py", line 31, in <module>
    infile.read(mvc, "name_to_read")
RuntimeError: 

*** -------------------------------------------------------------------------
*** DOLFIN encountered an error. If you are not able to resolve this issue
*** using the information listed below, you can ask for help at
***
***     fenics-support@googlegroups.com
***
*** Remember to include the error message listed below and, if possible,
*** include a *minimal* running example to reproduce the error.
***
*** -------------------------------------------------------------------------
*** Error:   Unable to find entity in map.
*** Reason:  Error reading MeshValueCollection.
*** Where:   This error was encountered inside HDF5File.cpp.
*** Process: 0
*** 
*** DOLFIN version: 2019.1.0
*** Git changeset:  74d7efe1e84d65e9433fd96c50f1d278fa3e3f3f
*** -------------------------------------------------------------------------

Do you know what I am doing wrong ? I tried searching online and asking ChatGPT before posting, nothing works.

You are here trying to integrate over the vertices of your mesh?
Then you are not integrating over a facet (“ds”) but a vertex (“dp”).

However, lets take 10 steps backwards, and consider:

import meshio
import gmsh

gmsh.initialize()

gmsh.model.add("my model")
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
resolution = 0.1
print(f"Mesh resolution = {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)])

p_1 = gmsh.model.occ.addPoint((r + R) / 2.0, 0, 0)
p_2 = gmsh.model.occ.addPoint((r + R) / 2.0, r / 10.0, 0)
line_p_1_p_2 = gmsh.model.occ.addLine(p_1, p_2)

gmsh.model.occ.synchronize()

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

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 = 1
circle_R_subdomain_id = 2
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")

gmsh.model.addPhysicalGroup(lines[2][0], [lines[2][1]], line_p_1_p_2_subdomain_id)
gmsh.model.setPhysicalName(lines[2][0], line_p_1_p_2_subdomain_id, "line_p_1_p_2")

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

gmsh.model.addPhysicalGroup(vertices[0][0], [vertices[0][1]], p_1_subdomain_id)
gmsh.model.setPhysicalName(vertices[0][0], p_1_subdomain_id, "p_1")

gmsh.model.addPhysicalGroup(vertices[1][0], [vertices[1][1]], p_2_subdomain_id)
gmsh.model.setPhysicalName(vertices[1][0], p_2_subdomain_id, "p_2")

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

gmsh.write("mesh.msh")


def create_mesh(mesh, cell_type, prune_z=False):
    cells = mesh.get_cells_type(cell_type)
    cell_data = mesh.get_cell_data("gmsh:physical", cell_type)
    out_mesh = meshio.Mesh(
        points=mesh.points,
        cells={cell_type: cells},
        cell_data={"name_to_read": [cell_data]},
    )
    return out_mesh


mesh_from_file = meshio.read("mesh.msh")

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

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

# create a vertex mesh
import numpy as np

line_vertices = np.unique(line_mesh.cells_dict["line"].flatten())
triangle_vertices = np.unique(triangle_mesh.cells_dict["triangle"].flatten())
print(line_vertices)
print(triangle_vertices)
print(np.isin(line_vertices, triangle_vertices))

This yields a list of what vertices are in your cells, and what vertices are in your marked facets. This is:

[ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29 30 31 32 33 34 35 36] 
[ 0  1  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
 50 51 52 53 54 55 56 57 58 59 60]
[ True  True False False  True  True  True  True  True  True  True  True
  True  True  True  True  True  True  True  True  True  True  True  True
  True  True  True  True  True  True  True  True  True  True  True  True
  True]

Note that the last line indicates that there are some vertices in your tagged facets that are not in the cells. (2, 3).
This is because you have not correctly embedded then in your mesh with gmsh.
By simple trial and failure, we can identify the lines:

gmsh.model.addPhysicalGroup(lines[2][0], [lines[2][1]], line_p_1_p_2_subdomain_id)
gmsh.model.setPhysicalName(lines[2][0], line_p_1_p_2_subdomain_id, "line_p_1_p_2")

as the problematic ones, which goes back to:

p_1 = gmsh.model.occ.addPoint((r + R) / 2.0, 0, 0)
p_2 = gmsh.model.occ.addPoint((r + R) / 2.0, r / 10.0, 0)
line_p_1_p_2 = gmsh.model.occ.addLine(p_1, p_2)

which is not part of your mesh.
This can thus be fixed by adding:


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

p_1 = gmsh.model.occ.addPoint((r + R) / 2.0, 0, 0)
p_2 = gmsh.model.occ.addPoint((r + R) / 2.0, r / 10.0, 0)
line_p_1_p_2 = gmsh.model.occ.addLine(p_1, p_2)
gmsh.model.occ.synchronize()
gmsh.model.mesh.embed(1, [line_p_1_p_2], 2, ring[0][0][0])
gmsh.model.occ.synchronize()

Complete mesh generating script is:

import meshio
import gmsh

gmsh.initialize()

gmsh.model.add("my model")
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
resolution = 0.1
print(f"Mesh resolution = {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)

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

p_1 = gmsh.model.occ.addPoint((r + R) / 2.0, 0, 0)
p_2 = gmsh.model.occ.addPoint((r + R) / 2.0, r / 10.0, 0)
line_p_1_p_2 = gmsh.model.occ.addLine(p_1, p_2)
gmsh.model.occ.synchronize()
gmsh.model.mesh.embed(1, [line_p_1_p_2], 2, ring[0][0][0])
gmsh.model.occ.synchronize()

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

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 = 1
circle_R_subdomain_id = 2
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")

gmsh.model.addPhysicalGroup(lines[2][0], [lines[2][1]], line_p_1_p_2_subdomain_id)
gmsh.model.setPhysicalName(lines[2][0], line_p_1_p_2_subdomain_id, "line_p_1_p_2")

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

gmsh.model.addPhysicalGroup(vertices[0][0], [vertices[0][1]], p_1_subdomain_id)
gmsh.model.setPhysicalName(vertices[0][0], p_1_subdomain_id, "p_1")

gmsh.model.addPhysicalGroup(vertices[1][0], [vertices[1][1]], p_2_subdomain_id)
gmsh.model.setPhysicalName(vertices[1][0], p_2_subdomain_id, "p_2")

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

gmsh.write("mesh.msh")


def create_mesh(mesh, cell_type, prune_z=False):
    cells = mesh.get_cells_type(cell_type)
    cell_data = mesh.get_cell_data("gmsh:physical", cell_type)
    out_mesh = meshio.Mesh(
        points=mesh.points,
        cells={cell_type: cells},
        cell_data={"name_to_read": [cell_data]},
    )
    return out_mesh


mesh_from_file = meshio.read("mesh.msh")

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

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

# create a vertex mesh
import numpy as np

line_vertices = np.unique(line_mesh.cells_dict["line"].flatten())
triangle_vertices = np.unique(triangle_mesh.cells_dict["triangle"].flatten())

For future posts, please note the following:

  1. Your error happens way before the end of your code. This indicates that you shouldn’t post the rest of the code.
  2. You should visually inspect your markers in paraview to see if they make sense. For the problem at hand, it was quite clear that the facet that you had marked is not in the mesh (use coarse resolution).

Thank you for your reply.

Yes.

This works, and I can now generate the mesh with the following code

import meshio
import numpy as np
import gmsh
import warnings

msh_file_path = "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

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()

p_1 = gmsh.model.occ.addPoint( 1.0 / 4.0 * r + 3.0 / 4.0 * R, 0, 0 )
p_2 = gmsh.model.occ.addPoint( 0, 1.0 / 4.0 * r + 3.0 / 4.0 * R, 0 )
gmsh.model.occ.synchronize()

line_p_1_p_2 = gmsh.model.occ.addLine( p_1, p_2 )
gmsh.model.occ.synchronize()

gmsh.model.mesh.embed( 1, [line_p_1_p_2], 2, ring[0][0][0] )
gmsh.model.occ.synchronize()

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

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 = 1
circle_R_subdomain_id = 2
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" )

gmsh.model.addPhysicalGroup( 1, [line_p_1_p_2], line_p_1_p_2_subdomain_id )
gmsh.model.setPhysicalName( 1, line_p_1_p_2_subdomain_id, "line_p_1_p_2" )

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

gmsh.model.addPhysicalGroup( vertices[0][0], [vertices[0][1]], p_1_subdomain_id )
gmsh.model.setPhysicalName( vertices[0][0], p_1_subdomain_id, "p_1" )

gmsh.model.addPhysicalGroup( vertices[1][0], [vertices[1][1]], p_2_subdomain_id )
gmsh.model.setPhysicalName( vertices[1][0], p_2_subdomain_id, "p_2" )

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


def create_mesh(mesh, cell_type, prune_z=False):
    cells = mesh.get_cells_type( cell_type )
    cell_data = mesh.get_cell_data( "gmsh:physical", cell_type )
    out_mesh = meshio.Mesh( points=mesh.points, cells={
        cell_type: cells}, cell_data={"name_to_read": [cell_data]} )
    return out_mesh


mesh_from_file = meshio.read( msh_file_path )

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

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

# create a vertex mesh
vertex_mesh = create_mesh( mesh_from_file, "vertex", True )
meshio.write( "vertex_mesh.xdmf", vertex_mesh )

#print a list of the vertices in the .msh file in 'filename' which belong to lines in the mesh
def line_vertices(filename):
    mesh_from_file = meshio.read(filename)
    line_mesh = create_mesh( mesh_from_file, "line", False )
    line_vertices = np.unique( line_mesh.cells_dict["line"].flatten() )
    print(f"line vertices: {line_vertices}")

    return line_vertices


def triangle_vertices(filename):
    mesh_from_file = meshio.read(filename)
    triangle_mesh = create_mesh(mesh_from_file, "triangle", prune_z=False)
    triangle_vertices = np.unique( triangle_mesh.cells_dict["triangle"].flatten() )
    print(f"triangle vertices: {triangle_vertices}")

    return triangle_vertices

print( f"Check if all line vertices are triangle vertices : {np.isin( line_vertices( msh_file_path ), triangle_vertices( msh_file_path ) )}" )

which gives

$ python3 generate_2dmesh_ring.py 
Info    : Meshing 1D...                                                                                                                        
Info    : [  0%] Meshing curve 1 (Ellipse)
Info    : [ 40%] Meshing curve 2 (Ellipse)
Info    : [ 70%] Meshing curve 3 (Line)
Info    : Done meshing 1D (Wall 0.00189438s, CPU 0.001897s)
Info    : Meshing 2D...
Info    : Meshing surface 2 (Plane, Frontal-Delaunay)
Info    : Done meshing 2D (Wall 0.0126082s, CPU 0.012589s)
Info    : 71 nodes 151 elements
Info    : Writing 'mesh.msh'...
Info    : Done writing 'mesh.msh'
line vertices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40]
triangle vertices: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70]
Check if all line vertices are triangle vertices : [ True  True  True  True  True  True  True  True  True  True  True  True
  True  True  True  True  True  True  True  True  True  True  True  True
  True  True  True  True  True  True  True  True  True  True  True  True
  True  True  True  True  True]

So all line vertices are triangle vertices, and I checked with Paraview that the line vertices are included in the mesh. The line parts of the mesh are also tagged correctly after inspection with Paraview, here is line_mesh.xdmf:

I then read back the mesh with the code

from fenics import *
from mshr import *
from dolfin import *
from dolfin import *
import argparse
import numpy as np


#CHANGE PARAMETERS HERE
c_r = [0, 0, 0]
c_R = [0, 0, 0]
r = 1
R = 2
#CHANGE PARAMETERS HERE

# read the mesh
mesh = Mesh()
xdmf = XDMFFile( mesh.mpi_comm(),"triangle_mesh.xdmf" )
xdmf.read( mesh )
xdmf.close()

print(f"Mesh dimension = {mesh.topology().dim()}")

# read the triangles
mvc = MeshValueCollection( "size_t", mesh, mesh.topology().dim() )
with XDMFFile("triangle_mesh.xdmf" ) as infile:
    infile.read( mvc, "name_to_read" )
vf = cpp.mesh.MeshFunctionSizet( mesh, mvc )

#read the lines
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim()-1)
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
sf = cpp.mesh.MeshFunctionSizet(mesh, mvc)


#read the vertices
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim()-2)
with XDMFFile("vertex_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
pf = cpp.mesh.MeshFunctionSizet(mesh, mvc)


def my_norm(x):
    return (sqrt( np.dot( x, x ) ))

#analytical expression for a  scalar function used to test the ds
class FunctionTestIntegral(UserExpression):
    def eval(self, values, x):
        c_test = [0.3, 0.76, 0]
        r_test = 0.345

        values[0] = np.cos(my_norm(np.subtract(x, c_test)) - r_test)**2.0
    def value_shape(self):
        return (1,)



dx = Measure( "dx", domain=mesh, subdomain_data=vf, subdomain_id=6   )
ds_r = Measure( "ds", domain=mesh, subdomain_data=sf, subdomain_id=1 )
ds_R = Measure( "ds", domain=mesh, subdomain_data=sf, subdomain_id=2 )
ds_line = Measure( "ds", domain=mesh, subdomain_data=sf, subdomain_id=3 )
# dp_1 = Measure( "dp", domain=mesh, subdomain_data=sf, subdomain_id=4 )




Q = FunctionSpace( mesh, 'P', 1 )

f_test_ds = Function( Q )
f_test_ds.interpolate( FunctionTestIntegral( element=Q.ufl_element() ))

#compare the numerical value of the integral of a test function over a ds, dx, .... with the exact one and output the relative difference
def test_mesh_integral(exact_value, f_test, measure, label):
    numerical_value = assemble( f_test * measure )
    print( f"{label} = {numerical_value:.{4}}, should be {exact_value:.{4}}, relative error =  {abs( (numerical_value - exact_value) / exact_value )}" )



#print out the integrals on the surface elements and compare them with the exact values to double check that the elements are tagged correctly
test_mesh_integral(2.9021223108952894, f_test_ds, dx, '\int f dx')
test_mesh_integral(2.7759459256115657, f_test_ds, ds_r, 'int f ds_r')
test_mesh_integral(3.6717505977470717, f_test_ds, ds_R, 'int f ds_R')

test_mesh_integral(1.774455046337995, f_test_ds, ds_line, 'int f ds_line')
# test_mesh_integral(0.0756808, f_test_ds, dp_1, 'f on p_1')

which gives


Mesh dimension = 2
\int f dx = 3.046, should be 2.902, relative error =  0.04957548981372177
int f ds_r = 2.745, should be 2.776, relative error =  0.011117845025071448
int f ds_R = 3.66, should be 3.672, relative error =  0.0031066167358116113
int f ds_line = 0.0, should be 1.774, relative error =  1.0

The integration over ds_r and ds_R is correct, but not the one on ds_line, which gives the wrong value (0.0). Do you know why ?

Also, I tried to add the measure on points p_1 and p_2 according to your suggestion to use ‘dp’, but then if I comment out line dp_1 = Measure( "dp", domain=mesh, subdomain_data=sf, subdomain_id=4 ) I get the error message

Mesh dimension = 2
Invalid integral_type.
Traceback (most recent call last):
  File "read_2dmesh_ring.py", line 63, in <module>
    dp_1 = Measure( "dp", domain=mesh, subdomain_data=sf, subdomain_id=4 )
  File "/usr/local/lib/python3.6/dist-packages/ufl/measure.py", line 155, in __init__
    self._integral_type = as_integral_type(integral_type)
  File "/usr/local/lib/python3.6/dist-packages/ufl/measure.py", line 99, in as_integral_type
    error("Invalid integral_type.")
  File "/usr/local/lib/python3.6/dist-packages/ufl/log.py", line 172, in error
    raise self._exception_type(self._format_raw(*message))
ufl.log.UFLException: Invalid integral_type.

Thank you for your help!