Cannot create 1d mesh

Hello, I would like to create a one-dimensional mesh, a segment, and tag its extremal points, but I cannot get this to work, here is a minimal working example:

import numpy
import meshio
import gmsh
import pygmsh

resolution = 0.1
L = 2.2

geometry = pygmsh.geo.Geometry()
model = geometry.__enter__()

my_points = [ model.add_point((0, 0, 0), mesh_size=resolution),
             model.add_point((L, 0, 0), mesh_size=resolution)]

lines = [model.add_line( my_points[i], my_points[i + 1] )
         for i in range(-1, len(my_points)-1)]

model.synchronize()
model.add_physical(lines[0], "Line")
model.add_physical([my_points[0]], "l")
model.add_physical([my_points[1]], "r")
geometry.generate_mesh(dim=1)
gmsh.write("membrane_mesh.msh")
gmsh.clear()
geometry.__exit__()

mesh_from_file = meshio.read("membrane_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)
    points = mesh.points[:, :2] if prune_z else mesh.points
    out_mesh = meshio.Mesh(points=points, cells={cell_type: cells}, cell_data={
                           "name_to_read": [cell_data]})
    return out_mesh

#triangle_mesh = create_mesh(mesh_from_file, "triangle", prune_z=True)
#meshio.write("triangle_mesh.xdmf", triangle_mesh)
line_mesh = create_mesh(mesh_from_file, "line", prune_z=True)
meshio.write("line_mesh.xdmf", line_mesh)

then I read the mesh from another example.py file where I solve a PDE, with

mesh=Mesh()
#with XDMFFile((args.input_directory) + "/triangle_mesh.xdmf") as infile:
#    infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, 2)
with XDMFFile((args.input_directory) + "/line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")

but I get

$python3 example.py
*** Warning: Mesh is empty, unable to create entities of dimension 1.
*** Warning: Mesh is empty, unable to create entities of dimension 1.
Floating point exception

Note that this works in a 2d case, where I define a square and uncomment the commented lines where I generate triangle_mesh, but I don’t know how to replace those lines in the 1d case.

Do you have any ideas?

Thank you

You haven’t read in your line mesh, so you cannot read the cell data.
The following runs nicely for me:

import numpy
import meshio
import gmsh
import pygmsh

resolution = 0.1
L = 2.2

geometry = pygmsh.geo.Geometry()
model = geometry.__enter__()

my_points = [ model.add_point((0, 0, 0), mesh_size=resolution),
             model.add_point((L, 0, 0), mesh_size=resolution)]

lines = [model.add_line( my_points[i], my_points[i + 1] )
         for i in range(-1, len(my_points)-1)]

model.synchronize()
model.add_physical(lines[0], "Line")
model.add_physical([my_points[0]], "l")
model.add_physical([my_points[1]], "r")
geometry.generate_mesh(dim=1)
gmsh.write("membrane_mesh.msh")
gmsh.clear()
geometry.__exit__()

mesh_from_file = meshio.read("membrane_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)
    points = mesh.points[:, :2] if prune_z else mesh.points
    out_mesh = meshio.Mesh(points=points, cells={cell_type: cells}, cell_data={
                           "name_to_read": [cell_data]})
    return out_mesh

#triangle_mesh = create_mesh(mesh_from_file, "triangle", prune_z=True)
#meshio.write("triangle_mesh.xdmf", triangle_mesh)
line_mesh = create_mesh(mesh_from_file, "line", prune_z=True)
meshio.write("line_mesh.xdmf", line_mesh)

from dolfin import *
mesh=Mesh()
with XDMFFile("line_mesh.xdmf") as infile:
   infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")

Thank you, I can now read the mesh with no issues.
However, I would like to make sure that the labelled boundaries ds are correct. To do this, I read the mesh and evaluate a test function at ds on the left and right edge of the line, here is the minimal working example ‘mwe.py’:

from __future__ import print_function
from fenics import *
from mshr import *
from fenics import *
from mshr import *

L = 1.0

mesh=Mesh()
with XDMFFile("line_mesh.xdmf") as infile:
   infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")

mf = dolfin.cpp.mesh.MeshFunctionSizet(mesh, mvc)

# Define function spaces
#finite elements for sigma .... omega
P_v = VectorElement( 'P', mesh.ufl_cell(), 2 )
P_z = FiniteElement( 'P', mesh.ufl_cell(), 1 )
element = MixedElement( [P_v, P_z] )
Q = FunctionSpace(mesh, element)

Q_v = Q.sub( 0 ).collapse()
Q_z= Q.sub( 1 ).collapse()

#analytical expression for a  scalar function used to test the ds
class TestFunction( UserExpression ):
    def eval(self, values, x):
        values[0] = x[0]**2
    def value_shape(self):
        return (1,)

# test for surface elements
ds_l = Measure( "ds", domain=mesh, subdomain_data=mf, subdomain_id=2 )
ds_r = Measure( "ds", domain=mesh, subdomain_data=mf, subdomain_id=3 )
f_test_ds = Function( Q_z )
f_test_ds.interpolate( TestFunction( element=Q_z.ufl_element() ) )

integral_l = assemble( f_test_ds * ds_l )
integral_r = assemble( f_test_ds * ds_r )

print( "Integral l = ", integral_l, " should be = 0" )
print( "Integral r = ", integral_r, " should be = 1" )

and I get

$python3 mwe.py 
Integral l =  0.0  should be = 0
Integral r =  0.0  should be = 1

Shouldn’t I get the value of the function at the boundaries in integral_l and integral_r ?
Is this because the integration domain ds has zero measure in 1d? If so, how may I test that ds_l and ds_r correspond to the two extremal points of the line ?

You are not storing facet data when generating the mesh. (You only store a line mesh, not a «point» mesh that correspond to the vertices).

As you have seen in the previous example one read the facet tags from a line mesh when the cells are triangular, and from triangle mesh if the cell type is tetrahedron

I don’t understand.

With a 2d mesh, whenever there is an intergration by parts one has
\int_\Omega dx \vec{\nabla} \cdot \vec{v} = \int_{\partial \Omega} dS \, \hat{n} \cdot \vec{v}
and this is written in Fenics as n[i] * v[i] * ds.

For a 1d mesh, this becomes
\int_0^L dx \partial_x v = v|^L_0, how may I write this in Fenics? Is it (v * ds_r) - (v * ds_l)?

Thank you

The point is:

  1. You haven’t stored your facet markers to file. See for instance: How to read a physical object from a xdmf file - #7 by dokken where I already showed you this.

The notation doesn’t change in DOLFIN for a 1D mesh. Just use the same notation (or alternatively what you have written as an option).

In that reply you show how to read the marked objects from .xdmf file (which is what I did here), not how to store facet markers to an .xdmf file. It is also unclear what a ‘facet’ would be for a 1d mesh.

I am looking for a way to make this test for ds_l and ds_r, which works in 2d, in one dimension. But this is still quite unclear …

Thank you

A facet is a vertex when you work on a 1D mesh.

There are many posts on the forum on this topic.
For instance by searching for “triangle_mesh”, you get: Error defining surface and measures for a 3D mesh imported from Gmsh - #4 by dokken
which explains how facet markers should be stored.

The link you posted, and the replies which come out when I look for ‘triangle mesh’ in this forum, refer to 2 or 3 dimensions.

As I said in my last post, I would like to make the test for ds_l and ds_r in one dimensions.

In addition to the test for ds_l and ds_r that I have been asking about, the following test for the full line ds also fails: I generate line_mesh.xdmf and line_mesh.h5 with your code and then integrate the f(x) = x^2 over the line

from __future__ import print_function
from fenics import *
from mshr import *
from fenics import *
from mshr import *

L = 1.0

mesh=Mesh()
with XDMFFile("line_mesh.xdmf") as infile:
   infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")

mf = dolfin.cpp.mesh.MeshFunctionSizet(mesh, mvc)

# Define function spaces
#finite elements for sigma .... omega
P_v = VectorElement( 'P', mesh.ufl_cell(), 2 )
P_z = FiniteElement( 'P', mesh.ufl_cell(), 1 )
element = MixedElement( [P_v, P_z] )
Q = FunctionSpace(mesh, element)

Q_v = Q.sub( 0 ).collapse()
Q_z= Q.sub( 1 ).collapse()

#analytical expression for a  scalar function used to test the ds
class TestFunction( UserExpression ):
    def eval(self, values, x):
        values[0] = x[0]**2
    def value_shape(self):
        return (1,)

# test for surface elements
ds = Measure( "ds", domain=mesh, subdomain_data=mf, subdomain_id=1 )
f_test_ds = Function( Q_z )
f_test_ds.interpolate( TestFunction( element=Q_z.ufl_element() ) )

integral = assemble( f_test_ds * ds )

print( "Integral = ", integral, " should be = 0.5" )

but I get the wrong result:

$ python3 example.py 
Integral =  1.0  should be = 0.5

Why ?

The whole point is that DOLFIN doesn’t care about if the facets are of dimension 1 or 2.
ds is an integration measure for entities of one degree lower dimension than the cells of the mesh (i.e. 1 for 2D meshes, 2 for 3D meshes).

As I have now stated a few times, please be incredibly careful when mixing integration measures and mesh functions.

  1. You have a mesh of lines (i.e. topological dimension 0).
  2. To read in data regarding facets (the information that should be in ds integrals, you need to read in data about the vertices, as done in:

Thank you, I tried to follow the example that you provided, from which I read the data regarding the lines and the data regarding the facets (vertices, or points).

I generate the mesh with generate_mesh.py

import numpy
import meshio
import gmsh
import pygmsh
import numpy as np

resolution = 0.01
L = 1.0

geometry = pygmsh.occ.Geometry()
model = geometry.__enter__()

#add a 1d object a set of lines
points = [model.add_point( (0, 0, 0), mesh_size=resolution ),
          model.add_point((np.pi/8.0, 0, 0), mesh_size=resolution),
          model.add_point((L, 0, 0), mesh_size=resolution)
          ]
my_lines = [model.add_line( points[0], points[1] ), model.add_line( points[1], points[2] )]

model.synchronize()

model.add_physical([my_lines[0]], "line_1")
model.add_physical([my_lines[1]], "line_2")
model.add_physical([points[0]], "point_1")
model.add_physical([points[1]], "point_2")
model.add_physical([points[2]], "point_3")

geometry.generate_mesh(dim=3)
gmsh.write("mesh.msh")
model.__exit__()

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

line_mesh = create_mesh(mesh_from_file, "line", True)
meshio.write("line_mesh.xdmf", line_mesh)

which runs ok and produces lines.xdmf. I then read lines.xdmf with read_mesh.py

from __future__ import print_function
from fenics import *
from mshr import *
from fenics import *
from mshr import *
import numpy as np
import ufl as ufl
from dolfin import *

L = 1

mesh = Mesh()
xdmf = XDMFFile(mesh.mpi_comm(), "line_mesh.xdmf")
xdmf.read(mesh)

#read the lines
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
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()-1)
with XDMFFile("line_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
sf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
xdmf.close()

#analytical expression for a function used to test the ds
class MyFunction( UserExpression ):
    def eval(self, values, x):
        values[0] = (np.cos(x[0]))**2
    def value_shape(self):
        return (1,)

dv_custom = Measure("dx", domain=mesh, subdomain_data=cf)    # Volume measure
ds_custom = Measure("ds", domain=mesh, subdomain_data=sf)    # Surface measure
ds_1 = Measure("ds", domain=mesh, subdomain_data=sf, subdomain_id=1)
ds_2 = Measure("ds", domain=mesh, subdomain_data=sf, subdomain_id=2)

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

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

print(f"Length of the line = {assemble(Constant(1.0)*dv_custom)}, should be 1.0")
print(f"Integral of f over the line =  {assemble( f_test_ds * dv_custom )}", " should be 0.727324")
print(f"Integral of f over line_1 =  {assemble( f_test_ds * dv_custom(1) )}", "should be 0.373126")
print(f"Integral of f over line_2 =  {assemble( f_test_ds * dv_custom(2) )}", "should be 0.354198")
#this computes \sum_{i \in vertices in ds_custom} f_test_ds (i-th vertex in ds_custom)
print("Sum_{i in vertices in ds_custom} f(i-th vertex in ds_custom)  =  ", assemble( f_test_ds * ds_custom ), " should be 1.29193")
print("f(1st vertex in ds_custom)  =  ", assemble( f_test_ds * ds_1 ), " should be 1")
print("f(2nd vertex in ds_custom)  =  ", assemble( f_test_ds * ds_2 ), " should be 2")

All the integrals on lines and points are correct, except the integrals restricted to points which I labelled (f(1st vertex in ds_custom) and f(2nd vertex in ds_custom) ):

$python3 read_mesh.py
Length of the line = 1.0, should be 1.0
Integral of f over the line =  0.727317007200262  should be 0.727324
Integral of f over line_1 =  0.3731205566952803 should be 0.373126
Integral of f over line_2 =  0.3541964505049818 should be 0.354198
Sum_{i in vertices in ds_custom} f(i-th vertex in ds_custom)  =   1.291926581726429  should be 1.29193
f(1st vertex in ds_custom)  =   1.291926581726429  should be 1
f(2nd vertex in ds_custom)  =   0.0  should be 2

Why? I added the vertices with model.add_physical([points[0]], "point_1") etc … so I expect assemble( f_test_ds * ds_1 ) to return the value of f at x=0, but this is not what I get …

Frankly, I am getting tired of repeating myself here.

Please look closely at the definition of sf.
This does not match what is done in:

where you observe that the surface/facet data is read from a different file that the mesh itself, which I have now stated multiple times before.

Sorry for bothering you, but if I keep asking that is because I don’t see the solution in your answers.

I see that the facets are read from a file, surface.xdmf, which is different form mesh.xdmf. Your example shows how to generate three different files for a 3d mesh: mesh.xdmf, surface.xdmf and line.xdmf.

I tried to do the same for a 1d mesh following your example, but I get an error (and this is why I read from the same file, I could not get to generate two different files):

import numpy
import meshio
import gmsh
import pygmsh
import numpy as np

resolution = 0.1
L = 1.0

geometry = pygmsh.occ.Geometry()
model = geometry.__enter__()

#add a 1d object a set of lines
points = [model.add_point( (0, 0, 0), mesh_size=resolution ),
          model.add_point((np.pi/8.0, 0, 0), mesh_size=resolution),
          model.add_point((L, 0, 0), mesh_size=resolution)
          ]
my_lines = [model.add_line( points[0], points[1] ), model.add_line( points[1], points[2] )]

model.synchronize()

model.add_physical([my_lines[0]], "line1")
model.add_physical([my_lines[1]], "line2")
model.add_physical([points[0]], "point_l")
model.add_physical([points[1]], "point_r")

geometry.generate_mesh(dim=3)
gmsh.write("mesh.msh")
model.__exit__()

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 mesh.xdmf (fail)
tetra_mesh = create_mesh(mesh_from_file, "tetra", True)
meshio.write("mesh.xdmf", tetra_mesh)

#create surface.xdmf (fail)
triangle_mesh = create_mesh(mesh_from_file, "triangle", True)
meshio.write("surface.xdmf", triangle_mesh)

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

Given that this is a one-dimensional mesh, there are no tetrahedra nor triangles, so it is no surprise that mesh.xdmf nor surface.xdmf can be created.
Of course, if I only generate line_mesh.xdmf then the code above works, but I need multiple files to read everything correctly, and I do not know, nor see, how to generate multiple ones.

I assumed that it was quite clear that you should create a mesh consisting of your points (a vertex mesh).
This can be achieved with the lines:

import numpy
import meshio
import gmsh
import pygmsh
import numpy as np

resolution = 0.1
L = 1.0

geometry = pygmsh.occ.Geometry()
model = geometry.__enter__()

#add a 1d object a set of lines
points = [model.add_point( (0, 0, 0), mesh_size=resolution ),
          model.add_point((np.pi/8.0, 0, 0), mesh_size=resolution),
          model.add_point((L, 0, 0), mesh_size=resolution)
          ]
my_lines = [model.add_line( points[0], points[1] ), model.add_line( points[1], points[2] )]

model.synchronize()

model.add_physical([my_lines[0]], "line1")
model.add_physical([my_lines[1]], "line2")
model.add_physical([points[0]], "point_l")
model.add_physical([points[1]], "point_r")

geometry.generate_mesh(dim=3)
gmsh.write("mesh.msh")
model.__exit__()

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 mesh.xdmf (fail)


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


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

Secondly, by looking at the vertex mesh in Paraview, you will see that the integer tag for the vertices are 3 and 4, not 1 and 2.

Thirdly, the vertex marked 4 is not on the boundary, it is an interior vertex, and should be accessed with dS, rather than ds.
This means that either you are tagging the wrong vertex, or the assumed value is wrong, as you get:

from fenics import *
import numpy as np

L = 1

mesh = Mesh()
xdmf = XDMFFile(mesh.mpi_comm(), "line_mesh.xdmf")
xdmf.read(mesh)

#read the lines
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
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()-1)
with XDMFFile("vertex_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
sf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
xdmf.close()

#analytical expression for a function used to test the ds
class MyFunction( UserExpression ):
    def eval(self, values, x):
        values[0] = (np.cos(x[0]))**2
    def value_shape(self):
        return (1,)

dv_custom = Measure("dx", domain=mesh, subdomain_data=cf)    # Volume measure
ds_custom = Measure("ds", domain=mesh, subdomain_data=sf)    # Surface measure
ds_3 = Measure("ds", domain=mesh, subdomain_data=sf, subdomain_id=3)
dS_4 = Measure("dS", domain=mesh, subdomain_data=sf, subdomain_id=4)

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

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

print(f"Length of the line = {assemble(Constant(1.0)*dv_custom)}, should be 1.0")
print(f"Integral of f over the line =  {assemble( f_test_ds * dv_custom )}", " should be 0.727324")
print(f"Integral of f over line_1 =  {assemble( f_test_ds * dv_custom(1) )}", "should be 0.373126")
print(f"Integral of f over line_2 =  {assemble( f_test_ds * dv_custom(2) )}", "should be 0.354198")
#this computes \sum_{i \in vertices in ds_custom} f_test_ds (i-th vertex in ds_custom)
print("Sum_{i in vertices in ds_custom} f(i-th vertex in ds_custom)  =  ", assemble( f_test_ds * ds_custom ), " should be 1.29193")
print("f(1st vertex in ds_custom)  =  ", assemble( f_test_ds * ds_3 ), " should be 1")
print("f(2nd vertex in ds_custom)  =  ", assemble( f_test_ds * dS_4 ), " should be 2")
Integral of f over the line =  0.7266291652063865  should be 0.727324
Integral of f over line_1 =  0.37255792945608923 should be 0.373126
Integral of f over line_2 =  0.35407123575029725 should be 0.354198
Sum_{i in vertices in ds_custom} f(i-th vertex in ds_custom)  =   1.291926581726429  should be 1.29193
f(1st vertex in ds_custom)  =   1.0  should be 1
f(2nd vertex in dS_custom)  =   0.8535533905932737  should be 2

Thank you, it appears to work now! The vertex tagging is also clear, I tagged the two lines first (ids 1 and 2), then the two vertices at the boundary (ids 3 and 4) and finally the vertex in the middle (id 5).

When I read them out, all the integrals check out:

import numpy
import meshio
import gmsh
import pygmsh
import argparse
import numpy as np

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

#mesh resolution
resolution = (float)(args.resolution)

#mesh parameters
#CHANGE PARAMETERS HERE
L = 1.0
h = L
r = 1.0
c_r = [0, 0, 0]
#CHANGE PARAMETERS HERE

print("L = ", L)
print("h = ", h)
print("r = ", r)
print("c_r = ", c_r)
print("resolution = ", resolution)


# Initialize empty geometry using the build in kernel in GMSH
geometry = pygmsh.occ.Geometry()
# Fetch model we would like to add data to
model = geometry.__enter__()

#add a 1d object a set of lines
points = [model.add_point( (0, 0, 0), mesh_size=resolution ),
          model.add_point((np.pi/8.0, 0, 0), mesh_size=resolution),
          model.add_point((L, 0, 0), mesh_size=resolution)
          ]
my_lines = [model.add_line( points[0], points[1] ), model.add_line( points[1], points[2] )]

#add a 2d object:  a plane surface starting from the 4 lines above


model.synchronize()

print("# of lines added = ", len(my_lines))

model.add_physical([my_lines[0]], "line1")
model.add_physical([my_lines[1]], "line2")
model.add_physical([points[0]], "point_l")
model.add_physical([points[2]], "point_r")
model.add_physical([points[1]], "point_in")

geometry.generate_mesh(dim=3)
gmsh.write("mesh.msh")
model.__exit__()

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 tetrahedron mesh
tetrahedron_mesh = create_mesh(msh, "tetra", True)
meshio.write("tetrahedron_mesh.xdmf", tetrahedron_mesh)

#create a triangle mesh
triangle_mesh = create_mesh(mesh_from_file, "triangle", prune_z=False)
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)

then I read the mesh with

from __future__ import print_function
from fenics import *
from mshr import *
from fenics import *
from mshr import *
import numpy as np
import ufl as ufl
import argparse
from dolfin import *


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

#CHANGE PARAMETERS HERE
L = 1
h = 1
r = 1
c_r = [0, 0]
#CHANGE PARAMETERS HERE

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

#read the lines
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
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()-1)
with XDMFFile("vertex_mesh.xdmf") as infile:
    infile.read(mvc, "name_to_read")
sf = 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):
        values[0] = (np.cos(3+x[0]))**2
    def value_shape(self):
        return (1,)

dv_custom = Measure("dx", domain=mesh, subdomain_data=cf)    # Line measure
ds_custom = Measure("ds", domain=mesh, subdomain_data=sf)    # Point measure for points at the edges of the mesh
dS_custom = Measure("dS", domain=mesh, subdomain_data=sf)    # Point measure for points in the mesh

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


# f_test_ds is a scalar function defined on the mesh, that will be used to test whether the boundary elements ds_circle, ds_inflow, ds_outflow, .. are defined correclty . This will be done by computing an integral of f_test_ds over these boundary terms and comparing with the exact result
f_test_ds = Function( Q )
f_test_ds.interpolate( FunctionTestIntegral( element=Q.ufl_element() ))


#print out the integrals on the surface elements and compare them with the exact values to double check that the elements are tagged correctly
print(f"Volume = {assemble(Constant(1.0)*dv_custom)}, should be 1.0")
print(f"Integral over the whole domain =  {assemble( f_test_ds * dv_custom )}", " should be 0.727324")
print(f"Integral over line #1 =  {assemble( f_test_ds * dv_custom(1) )}", "should be 0.373126")
print(f"Integral over line #2 =  {assemble( f_test_ds * dv_custom(2) )}", "should be 0.354198")

#this computes \sum_{i \in vertices in ds_custom} f_test_ds (i-th vertex in ds_custom)
print(f"Integral over point_l  =  {assemble( f_test_ds * ds_custom(3) )} should be 0.980085")
print(f"Integral over point_r =  {assemble( f_test_ds * ds_custom(4) )} should be 0.42725")
print(f"Integral over point_in =  {assemble( f_test_ds * dS_custom(5) )} should be 0.93826")

which gives

Volume = 1.0, should be 1.0
Integral over the whole domain =  0.8171933306687675  should be 0.817193
Integral over line #1 =  0.38654493390746114 should be 0.386545
Integral over line #2 =  0.43064839676130673 should be 0.430648
Integral over point_l  =  0.9800851433251829 should be 0.980085
Integral over point_r =  0.4272499830956933 should be 0.42725
Integral over point_in =  0.9382597571646916 should be 0.93826

I hope that this thread will be useful to other Fenics users :slight_smile: