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

Dear all,
I am quite confused by following this page: https://fenicsproject.org/pub/tutorial/sphinx1/._ftut1005.html.
I am trying to solve the heat equation in a region that consists of several mshr boxes put one on top of each other, so that there are surfaces, the interfaces between each boxes, where I do not want to apply any boundary conditions. And there are surfaces (the exposed ones) where I want to apply either Dirichlet, Robin or Neumann boundary conditions.
I already have the weak form and it would work if I could assign ds(n) to the nth surface of interest. I haven’t been able to do that, so far.

Here’s my current code

from dolfin import *
import mshr
block_length = 4
block_height = 3
block_sink_length = block_length
block_sink_height = 1
paralle_1 = mshr.Box(Point(0.0, 0.0, 0.0), Point(block_length, block_length, block_height))
paralle_2 = mshr.Box(Point(0.0, 0.0, block_height), Point(block_sink_length, block_sink_length, block_height + block_sink_height)) 

domain = paralle_1 + paralle_2
mesh = mshr.generate_mesh(domain, 15)
V = FunctionSpace(mesh, 'CG', 1)

t_f = 10  
num_steps = 10    
dt = t_f / num_steps 
T_hot = 273.15+20 # in K.
T_room = 273.15 # in K.

tol = 1E-14
class Bottom(SubDomain):
    def inside(self, x , on_boundary):
       return on_boundary and near(x[2], 0.0, tol)

class Omega_0(SubDomain):
    def inside(self, x, on_boundary):
        return x[2] >= 0.0 + tol and x[2] <= block_height - tol


class Omega_1(SubDomain):
    def inside(self, x, on_boundary):
        return x[2] >= block_height + tol and x[2] <= block_sink_height + 0.01 - tol

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

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

boundary_markers = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
boundary_markers.set_all(0) # mark all surface elements 0 
ds = Measure('ds', domain=mesh, subdomain_data=boundary_markers)
subdomain_1 = top_block()
subdomain_1.mark(boundary_markers, 1)
subdomain_2 = top_blokus()
subdomain_2.mark(boundary_markers, 2)
materials = MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
subdomain_10 = Omega_0()
subdomain_11 = Omega_1()
subdomain_10.mark(materials, 10)
subdomain_11.mark(materials, 11)
k_0 = 30
k_1 = 22
class K(UserExpression):
    def __init__(self, materials, k_0, k_1, **kwargs):
        super().__init__(**kwargs)
        self.materials = materials
        self.k_0 = k_0
        self.k_1 = k_1
    def eval_cell(self, values, x, cell):
        if self.materials[cell.index] == 0:
            values[0] = self.k_0
        else:
            values[0] = self.k_1
    def value_shape(self):
        return ()
kappa = K(materials, k_0, k_1)#, k_2, k_3, k_4)#, degree=1)
u_D0 = Constant(T_hot)
bc0 = DirichletBC(V, u_D0, Bottom())
u_0 = Expression('T_room', T_room = T_room, degree = 1)
u_n = interpolate(u_0, V)
u = TrialFunction(V)
v = TestFunction(V)
F = dt * dot(kappa*grad(u), grad(v)) * dx + v *(u-u_n)*dx
a, L = lhs(F), rhs(F)
u = Function(V)
t = 0

for n in range(num_steps):
    u_D0.t = t
    t += dt
    solve(a == L, u, bcs = bc0)           
    u_n.assign(u)

So, as you can see I have defined several classes. Some are supposed to be volumes, and others are supposed to be surfaces (only the surfaces I care about and where I wish to apply ds(n) with them). However the top_block surface doesn’t seem to work well because when I apply Dirichlet boundary conditions on it by replacing the bc0 line by “bc0 = [DirichletBC(V, u_D0, Bottom()), DirichletBC(V, u_D0, top_block())]”, I get a warning that “*** Warning: Found no facets matching domain for boundary condition.”. So I am unable to do it properly… any idea what’s wrong? I am getting quite confused!

You can debug this by writing your mesh function (boundary_markers) to file after applying the markings (for instance to pvd and visualize it in paraview), to check that you are marking the correct facets.

Unfortunately ParaView 5.8.0 has a bug which makes it unable to visualize any solution. This has been fixed, from what I gathered on this discourse website, for the 5.8.1 version. I tried to downgrade this morning (spent about 1 hour and a half), to no avail. I would have to downgrade cmake and several other packages (too many to do manually), leaving my system unstable. So I will wait until the next Paraview version hits the repository of my linux distribution.
I haven’t found a quick way to visualize the mesh.

It is very easy to have multiple versions of paraview on one system (at least Linux) as They supply standalone folders with all dependencies, that you so not have to install on your system

You can simply go to: https://www.paraview.org/download/ get the .tar.gz file for the 5.7 version, unzip it whereever, and go to ~/pathtodownload/ParaView-5.7.0-MPI-Linux-Python3.7-64bit/bin/ and run ./paraview

Thanks a lot, I wasn’t aware of this. I just tried but unfortunately it core dumps with many lines such as “Fontconfig warning: “/etc/fonts/fonts.conf”, line 5: unknown element “its:rules””

When trying to run paraview or open the pvd file?

When trying to run Paraview. A quick duckduckgo search yields this can of worms: https://gitlab.kitware.com/paraview/paraview/issues/19346.

Well, then try paraview 5.4 as it predates these issues.

According to the link above, it also suffers from it.
I notice that the nightly version is not available for linux, unfortunately.

5.4.0 is not listed.

I just tested the 5.3 and it core dumps with the same error(s).
Edit: The 5.4 is listed: “All versions of ParaView are broken with this updated fontconfig. @nicolas.vuaille tested from 5.4 to 5.7.”

You could try to use VTKPlotter as it has quite a lot of dolfin compatible functions.

I see, I would have to learn how to use it. Does it run with docker without too much hassle though? I use docker in order to run fenics.

As it is a python package, I expect it to be usable through docker. I guess @marcomusy can tell you more about VTKPlotter than I can.

Alright, I’ll wait for an eventual reply from him.
Back to my problem, when I change “bc0 = [DirichletBC(V, u_D0, Bottom()), DirichletBC(V, u_D0, top_block())]” to “bc0 = [DirichletBC(V, u_D0, Bottom()), DirichletBC(V, u_D0, top_blockus())]” the code seems to work. The problem though is that I really want to compute the average temperature over the surface top_block. Apparently it isn’t meshed like the other surfaces because it’s the interface between the 2 blocks, so it isn’t exposed like the other surfaces.
How would I compute the average of the function I am solving for, on such a surface? Usually I would assemble() over the ds() surface I want, but here I don’t know how to even mesh the surface to then mark it and make ds(n) work. Any idea?

The measure dS is used for internal facets of a mesh.
You can mesh such an interface using for instance gmsh and the BooleanFragments operator. I’ve made several other posts on the forum about this Transitioning from mesh.xml to mesh.xdmf, from dolfin-convert to meshio.
Then you can mark it with gmsh operations and load it into FEniCS.

I see… thanks a lot.
I really wanted to avoid gmsh at all cost for that “simple” geometry but if that’s the only way to go, I’ll go for it. Not only I have a hard time to create what I want (I edit the .geo file manually), but any little change like the addition of a single point will change the numbering of all surfaces, and from my experience it isn’t just a +1 numbering, I have to rename a lot of things for a tiny modification.
I am sure I am not using gmsh in the normal way because I do it in an extremely inefficient way.

If you are doing 2D geometries, using pygmsh can probably speed up your efficiency. Pygmsh Also does 3D, But then you have to use boolean fragments

vtkplotter should work with docker and it supports MeshFunction but need to change
MeshFunction("size_t", mesh, mesh.topology().dim() - 1)
to
MeshFunction("size_t", mesh, 0)
(to be perfectly honest I don’t remember why…)

At the end of your script:

from vtkplotter.dolfin import plot, interactive

plt = plot(materials, style='paraview', viewup='z', axes=1)

# can also cut your mesh:
# vmesh = plt.actors[0]
# vmesh.cutWithPlane(origin=(2.5,0,0), normal=(1,1,1))
# plot(vmesh)

for n in range(num_steps):
    u_D0.t = t
    t += dt
    solve(a == L, u, bcs = bc0)           
    plot(u, interactive=False, viewup='z')
    u_n.assign(u)    

interactive()