Marking surfaces of a 3D mesh (mshr) to apply b.c

I see, but isn’t it what I do with my code:

Surface Loop(10000) = {1001,2001,5001,5101,5201,5301};
Volume(10001) = {10000};
Surface Loop(10002)={3001,6001,6101,6201,6301,2001};
Volume(10003)={10002};
v() = BooleanFragments { Volume{10001}; Delete; }{ Volume{10003}; Delete;};

?
The common surface is the 2001 one. The code doesn’t work well, as before the top block has no element and the bottom block contains all the volume elements of the whole structure (I do not know why.)

Edit: Same problem even if I remove one of the Delete;.

Edit2: I’ve re-verified that each volume is formed with the 6 surrounding surfaces. All seems to be OK logically to me. I see no reason whatsoever why the volume of the first block should be the whole structure, and the volume of the second block contains no element. I am missing something but I do not see it.

As I have said before, you are making your geometry in an overcomplicated way.
Here is a version using openCASCADE and the built in convenience functions.

//+
SetFactory("OpenCASCADE");
//+
Box(1) = {0, 0, 0, 1, 1, 1};
//+
Box(2) = {1, 0, 0, 0.2, 1, 1};

v() = BooleanFragments { Volume{1}; Delete; }{ Volume{2}; Delete;};
Physical Volume("bott_block") = {#v()};
Physical Volume("top_block") = {#v()-1};

I have added the lines

Mesh.RandomFactor=1.0e-4;
Mesh.Algorithm = 7;
Mesh.Algorithm3D= 7;

to your example and I got exactly the same problem as in my case, namely that one of the volume contains no element. So I simply commented out the lines in my original code, and now it seems to work. Who would have thought such a thing could be a problem…

Edit: Unfortunately there are still problems. Now dolfinx isn’t able to run on the code with meshio, I get an error for the line

tetra_mesh = meshio.Mesh(points=msh.points, cells={"tetra": tetra_cells},

which is NameError: name 'tetra_cells' is not defined

I’ve no idea why now the code doesn’t work well…

With your code, after generating the mesh with Gmsh and trying to run my code with dolfinx and meshio, I get:

cells=[("triangle", triangle_cells)],
NameError: name 'triangle_cells' is not defined

That is because i have not marked any surfaces in the mesh. I have spent quite a lot of time guiding you in this case, and I think with all the posts that i have made and linked you to, you should be able to do this on your own. The marking of surfaces can Even be done in the Gmsh GUI, and then be saved to your GEO file for reproducebility.

You’re right, I’ve just realized this. And in my code with my own mesh I forgot to modify it back to what it was (and similar to your example).
Edit: Damn, that doesn’t fix it. When I examine the file.geo, I see no volume now (I used to see the v() volumes before, when I still had those 3 preamble Mesh. lines). I see no volume despite having Physical Volume() defined.

I am still completely stuck, even with the much simpler Gmsh (and dolfin) codes.
First, the very simple gmsh code:

SetFactory("OpenCASCADE");
Box(1) = {0, 0, 0, 4, 4, 3};
Box(2) = {0, 0, 3, 4, 4, 1};
v() = BooleanFragments { Volume{1}; Delete; }{ Volume{2}; Delete;};
Physical Volume("top_block") = {#v()};
Physical Volume("bottom_block") = {#v()-1};
Physical Surface("bottom") = {5};
Physical Surface("top") = {11};
Physical Surface("intermediate") = {6};

where I have used to GUI to mark the surfaces of interest as Physical Surface(). So far so good, gmsh file.geo -3 returns no error and the generated mesh looks fine to me.

Here’s the Python code:

from dolfin import *
import meshio
msh = meshio.read("simple_gmsh_mesh.msh")
for key in msh.cell_data_dict["gmsh:physical"].keys():
    if key == "triangle":
        triangle_data = msh.cell_data_dict["gmsh:physical"][key]
    elif key == "tetra":
        tetra_data = msh.cell_data_dict["gmsh:physical"][key]
for cell in msh.cells:
    if cell.type == "tetra":
        tetra_cells = cell.data
    elif cell.type == "triangle":
        triangle_cells = cell.data
tetra_mesh = meshio.Mesh(points=msh.points, cells={"tetra": tetra_cells},
                         cell_data={"name_to_read":[tetra_data]})
triangle_mesh =meshio.Mesh(points=msh.points,
                           cells=[("triangle", triangle_cells)],
                           cell_data={"name_to_read":[triangle_data]})
meshio.write("file2.xdmf", tetra_mesh)
meshio.write("mf2.xdmf", triangle_mesh)
mesh = Mesh()
with XDMFFile("file2.xdmf") as infile:
    infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, 2)
with XDMFFile("mf2.xdmf") as infile:
    infile.read(mvc, "name_to_read")
mf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
mvc2 = MeshValueCollection("size_t", mesh, 3)
with XDMFFile("file2.xdmf") as infile:
    infile.read(mvc2, "name_to_read")
cf = cpp.mesh.MeshFunctionSizet(mesh, mvc2)
dx = Measure("dx", domain=mesh,subdomain_data=cf)
ds = Measure("ds", domain=mesh, subdomain_data=mf)
subdomains = cf
boundary_parts = mf
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=0)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=1)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=2)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=3)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=4)))
print('test volume: ',assemble(Constant(1)*dx(domain=mesh, subdomain_data=cf, subdomain_id=5)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=1)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=2)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=3)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=4)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=5)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=6)))
print('test surface: ',assemble(Constant(1)*dS(domain=mesh, subdomain_data=mf, subdomain_id=7)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=1)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=2)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=3)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=4)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=5)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=6)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=7)))
print('test surface: ',assemble(Constant(1)*ds(domain=mesh, subdomain_data=mf, subdomain_id=11)))

The output of running this file is extremely strange to me:

test volume:  16.00000000000001
test volume:  0.0
test volume:  0.0
test volume:  16.00000000000001
test volume:  0.0
test volume:  0.0
test volume:  0.0
test surface:  48.00000000000001
test surface:  156.12965142809307
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  48.00000000000001
test surface:  0.0
test surface:  0.0
test surface:  15.999999999999993
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0
test surface:  0.0

Which indicates that dx is indeed all the volume as it should, but dx(0) through dx(5) is no volume element, except for dx(2) which is also all the volume. I would have expected dx(1) to be the top block volume element and dx(2) to be the bottom block volume element, as is shown with Gmsh. What is going on? I have no idea.

Then external surfaces. ds() yields 48 which is 3 x 16, or, I assume, the 3 Physical Surface() I have defined. This means that the interface (internal surface where I want to spatially average u later on) is considered an external surface. I have no idea why. In particular ds(3) = 16 so it is one of the surfaces I have defined as Physical Surface(), but which one, I am not sure. It should be the bottom surface, but then again, if that was true then ds(4) and ds(5) should have returned 16 instead of 0. Thus, I don’t understand what’s going on.

Then dS() is worth 156, I assume it consists of many internal surfaces that I haven’t defined as Physical Surface(). I have no idea how to select the dS() corresponding to the interface I am interested in.

There are still many things that you are not carefully considering.
First of all, the physical markers for your problem are the following:

Top block has marker 1
bottom block has marker 2
bottom has marker 3
top has marker 4
surface has marker 5

You can verify this by opening gmsh->tools->visibility and look at physical entities.

Secondly, you are not writing your full mesh to xdmf. As I have told you earlier on in this thread , meshio create cell blocks, and these have to be merged. Frankly, I am getting quite tired of having to refer back to my old posts in this thread.

I am sorry for any discomfort (I too hope this nightmare finishes!).
I was already looking at the Visibility section in Gmsh. And this is consistent with my comments:

I would have expected dx(1) to be the top block volume element and dx(2) to be the bottom block volume element, as is shown with Gmsh.

and

but then again, if that was true then ds(4) and ds(5) should have returned 16 instead of 0.

So yes, I know (or at least, I think I know) what I should expect with the dx(subdomain_id=N), ds() and dS(). Or am I still missing something?

Regarding your other comment, I used your own code from March 5th of Transitioning from mesh.xml to mesh.xdmf, from dolfin-convert to meshio - #93 by Ali. I thought the code would work for a 3D mesh. I will look better then.

Here is a snapshot from my gmsh. As you give your physical markers “str” names instead of integers, it gives them a separate numbering.

You should rather follow the one from 9th of march, which handles multiple mesh blocks, Transitioning from mesh.xml to mesh.xdmf, from dolfin-convert to meshio
Everything is read in wrong since you are not reading in all mesh-blocks.

Yes, this is also what I see in Gmsh.

Ok about the meshio code.
Unfortunately the following code:

msh = meshio.read("ultimate_gmsh_MWE.msh")
cells = np.vstack(np.array([cells.data for cells in mesh.cells
                            if cells.type == "triangle"]))
triangle_mesh = meshio.Mesh(points=mesh.points,
                            cells=[("triangle", cells)])
facet_cells = np.vstack(np.array([cells.data for cells in mesh.cells
                              if cells.type == "line"]))
facet_data = mesh.cell_data_dict["gmsh:physical"]["line"]
facet_mesh = meshio.Mesh(points=mesh.points,
                         cells=[("line", facet_cells)],
                         cell_data={"name_to_read": [facet_data]})
# Write mesh
meshio.xdmf.write("mesh2.xdmf", triangle_mesh)
meshio.xdmf.write("facet_mesh2.xdmf", facet_mesh)

yields

cells = np.vstack(np.array([cells.data for cells in mesh.cells
AttributeError: module 'dolfinx.mesh' has no attribute 'cells'

And the only way for me to use meshio is inside the dolfinx container. I cannot import dolfin.

I guess I’ll have to fall back to use mshr and use a nasty hack to compute the solution u at the interface (or near it).

Well, you do not need to import dolfinx to use meshio and Gmsh. So you should not Get any dolfinx issues when running the write mesh to xdmf script.
In your code you are using mesh.cells when you want to call msh.cells.

Thanks again.
Then I got the error

facet_cells = np.vstack(np.array([cells.data for cells in msh.cells
  File "<__array_function__ internals>", line 5, in vstack
  File "/usr/lib/python3/dist-packages/numpy/core/shape_base.py", line 282, in vstack
return _nx.concatenate(arrs, 0)
  File "<__array_function__ internals>", line 5, in concatenate
ValueError: need at least one array to concatenate

which I bypassed by removing the np.vstack() part.
However now it returns the error:

Traceback (most recent call last):
  File "with_dolfinx.py", line 25, in <module>
    facet_data = msh.cell_data_dict["gmsh:physical"]["line"]
KeyError: 'line'

Here I’m not sure what to do.

Please spend some time thinking about the error message before you Ask questions here. It is quite clear that you are not marking line-segments as facets, But triangles as facets, and that The “line” keyword has to be changed. I will not reply to more questions like these now, as it should be quite easy to debug by applying some effort.

Ok no worry :slight_smile:
Thanks a lot for all.

It’s now too hard for me to debug without Paraview.
My code to write the mesh elements is now

cells = np.vstack(np.array([cells.data for cells in msh.cells
                            if cells.type == "tetra"]))
tetra_mesh = meshio.Mesh(points=msh.points,
                            cells=[("tetra", cells)])
facet_cells = np.array([cells.data for cells in msh.cells if cells.type == "triangle"])

facet_data = msh.cell_data_dict["gmsh:physical"]["triangle"]
facet_mesh = meshio.Mesh(points=msh.points,
                         cells=[("triangle", facet_cells)],
                         cell_data={"name_to_read": [facet_data]})
# Write mesh
meshio.xdmf.write("mesh2.xdmf", tetra_mesh)
meshio.xdmf.write("facet_mesh2.xdmf", facet_mesh)

So I have changed “triangle” to “tetra” and “line” to “triangle”. The code produces no error, but then reading it with

with XDMFFile("mesh2.xdmf") as infile:
    infile.read(mesh)
mvc = MeshValueCollection("size_t", mesh, 2)
with XDMFFile("facet_mesh2.xdmf") as infile:
    infile.read(mvc, "name_to_read")
mf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
mvc2 = MeshValueCollection("size_t", mesh, 3)
with XDMFFile("mesh2.xdmf") as infile:
    infile.read(mvc2, "name_to_read")

but this yields

*** Error: Unable to reading data from XDMF file.
*** Reason: This combination of array shapes in XDMF and HDF5 not supported.
*** Where: This error was encountered inside XDMFFile.cpp.
*** Process: 0


*** DOLFIN version: 2018.1.0
*** Git changeset: 948dc42cc4e06ed9227d0201ad50f94ac94cbf9f

for the line infile.read(mvc, "name_to_read").

I’ll wait until a fixed version of Paraview comes into the Linux repository to see if I can debug what’s wrong.

For now I’ll just move on with mshr.

Again, you are really sloppy when changing your code.
It should be:

facet_cells = np.vstack([cells.data for cells in msh.cells if cells.type == "triangle"])

You are also not adding cell_data to the tetrahedron mesh, which would make the mvc_2 code to fail.
By adding:

tetra_data = msh.cell_data_dict["gmsh:physical"]["tetra"]

tetra_mesh = meshio.Mesh(points=msh.points,
                            cells=[("tetra", cells)],
                         cell_data={"name_to_read": [tetra_data]})

The data reads into dolfin nicely.

1 Like

Thank you very much for everything dokken, I think the code works as expected now.
It’s a bit hard for me to pick which one of the so many posts solved the problem. In short a solution that worked was to use Gmsh to produce the mesh (mshr being too limited), then meshio to write it and read it so that it is ready to be used with FEniCS and then make sure that the volumes and areas match what they should, using dolfin. Then we can use dS(N) where N is the number/tag of the internal surface.

Actually there is one little last step I am unsure about. I am trying to apply a Dirichlet boundary condition on the internal surface. I still get the message *** Warning: Found no facets matching domain for boundary condition. when I do bc0 = DirichletBC(V, Constant(300.0), top_block()) where

class top_block(SubDomain):
    def inside(self, x , on_boundary):
        return on_boundary and near(x[2], block_height, tol)

Well, the MWE could be the one in my first post except that the mesh is now created with Gmsh and dealt with with meshio. I still get the same warning. I guess I am missing a one liner that specifies that the internal surface is meshed (I have checked with Gmsh and that surface is well meshed and dolfin is able to compute correctly its area so I assume it’s fine).

Edit: Got it! bc0 = DirichletBC(V, Constant(300.0), mf, 5)