How to define BCs on boundaries of submeshes of a parent mesh?

Hi there! I am experiencing problem defining BCs on submeshes extracted from a parent mesh.
My geometry consists of two concentric circles. I am solving one PDE for the smaller disk and another for the annulus. I successfully recovered the submeshes but having trouble defining the boundaries to my submeshes. Below is the gmsh script followed by the Python script.

gmsh script:

//+
SetFactory("OpenCASCADE");
//+
Circle(1) = {0, 0, 0, 5, 0, 2*Pi};
//+
Circle(2) = {0, 0, 0, 3, 0, 2*Pi};
//+
Curve Loop(1) = {2};
//+
Plane Surface(1) = {1};
//+
Curve Loop(2) = {1};
//+
Curve Loop(3) = {2};
//+
Plane Surface(2) = {2, 3};
//+
Physical Curve(1) = {2};
//+
Physical Curve(2) = {1};
//+
Physical Surface(5) = {1};
//+
Physical Surface(4) = {2};

Python script:

from __future__ import print_function
from fenics import *
import matplotlib.pyplot as plt


from dolfin import *
import meshio
from dolfin import Mesh, XDMFFile, File, MeshValueCollection, cpp



# Optimization options for the form compiler
parameters["form_compiler"]["cpp_optimize"] = True
ffc_options = {"optimize": True, \
               "eliminate_zeros": True, \
               "precompute_basis_const": True, \
               "precompute_ip_const": True}


import numpy
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]})
    if prune_z:
        out_mesh.prune_z_0()
    return out_mesh

import meshio
msh = meshio.read("example.msh")
triangle_mesh = create_mesh(msh, "triangle", True)
line_mesh = create_mesh(msh, "line", True)
meshio.write("mesh.xdmf", triangle_mesh)
meshio.write("mf.xdmf", line_mesh) 
from dolfin import *
mesh = Mesh()
mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim())
with XDMFFile("mesh.xdmf") as infile:
   infile.read(mesh)
   infile.read(mvc, "name_to_read")
cf = cpp.mesh.MeshFunctionSizet(mesh, mvc)

mvc = MeshValueCollection("size_t", mesh, mesh.topology().dim()-1)
with XDMFFile("mf.xdmf") as infile:
    infile.read(mvc, "name_to_read")
mf = cpp.mesh.MeshFunctionSizet(mesh, mvc)
ds_custom = Measure("ds", domain=mesh, subdomain_data=mf)


s5 = MeshView.create(cf, 5) # Extracting subspace 5
s4 = MeshView.create(cf, 4) # Extracting subspace 4

W1 = FunctionSpace(s5,"Lagrange",1) # Defining function space on s5
W2 = FunctionSpace(s4,"Lagrange",1) # Defining function space on s4

def bs5(x):  # Defining boundary for subspace s5, curve 1
  return mf == 1 
  
def b2s4(x): # Defining boundary for subspace s4, curve 2
  return mf == 2
  
# PROBLEM FOR DOMAIN 5 WITH BOUNDARY 1 
  
# Define boundary condition
G , mu = 1, 0.1
u_D=Constant(0.0)

bc = DirichletBC(W1, u_D, bs5)

# Define variational problem
u = TrialFunction(W1)
v = TestFunction(W1)
f = Constant(-G/mu) # f=-G/mu
a = dot(grad(u), grad(v))*dx
L = -f*v*dx

# Compute solution
us = Function(W1)
solve(a == L, us, bc)

# Save solution to file in PVD format for Paraview
file = File("examplelumenvelocity.pvd");
file << us;

# PROBLEM FOR SURFACE 4 WITH BOUNDARIES 1 & 2

D, k, t = 0.7, 0.3, 3
f0, f1, beta = 5, 2, 0.3
gamma1 = (-1/D)*Constant(f0 + f1*(1-exp(-beta*(t**2)))) # Neumann condition on inner curve -dot(grad(c),n)=gamma1
gamma2 = Constant(0.0)    # Dirichlet condition on outer circle 

bc2 = DirichletBC(W2, gamma2, b2s4)

bcs=[bc2]

# Define variational problem
w = TestFunction(W2)
c = TrialFunction(W2)
a = inner(grad(c), grad(w))*dx + (((k/D)*c)*w)*dx
a -= - inner(gamma1, w)*ds_custom(bs5)
 

# Compute solution
cs= Function(W2)
solve(a == 0, cs, bcs)


# Save solution to file in PVD format for Paraview
file = File("example.pvd");
file << cs;

Error message:

*** Warning: Found no facets matching domain for boundary condition.
Invalid subdomain_id <function bs5 at 0x7fc435e3f1f0>.

How to fix this? I am using Dolfin version 2019.2.0.dev0.

I modified the BCs as

bc = DirichletBC(W1, u_D, bs5, method="pointwise") and similarly, the other one as
bc2 = DirichletBC(W2, gamma2, b2s4, method="pointwise")

Now, the error reads

ufl.log.UFLException: Invalid subdomain_id <function bs5 at 0x7f91ac0111f0>.

Does importing the parent mesh from gmsh and then extracting the subdomains mess up the physical tags or is there something wrong with my code? Any suggestion would be extremely helpful.

I think I had a similar issue. To my knowledge there is no direct way unfortunately to convert a MeshFunction defined on the parent mesh to the submesh. At least I could not find an elegant solution.
Below a code, that does it manually: not the fastest but it works.
It basically iterates over every cell/facet to copy the names of the subdomains and facets defined on a MeshFunction on your parent mesh to a new MeshFunction which is defined on your new mesh of the subdomain defined via MeshView.create(…).

def extract_SubMesh_FacetDomain(mesh, mesh_Subdomain, mf_2d, mf_1d, indices):
    """
        Extracts the submesh with the indices 'indices' from mesh 
        similar to https://fenicsproject.org/qa/9486/submesh-meshfunction-dirichletbc-meshview-etc/
        Input:
            - mesh: mesh of the system
            - mesh_Subdomain: MeshView object of the subdomain ( created with extract_SubMesh(mesh, mf_2d, indices) )
            - mf_2d: MeshFunction labeling the subdomains of the mesh
            - mf_1d: MeshFunction labeling the facets of the mesh
            - indices: infices of the subdomains stored in mf_2d that should be extracted

        Output:
            - submesh_boundaries: facets of the submesh extracted from mf_1d
            - submesh_domains:  subdomains of the submesh extracted from mf_2d
    """
    # Step 1: Create a submesh with SubMesh(). This is needed because I need to access the 'parent_vertex_indices' and 'parent_cell_indices', which apparently is not possible via MeshView.create (to my knowledge)
    subdomains = MeshFunction("size_t", mesh, mesh.topology().dim(), 0) 
    for k in indices:
        subdomains.array()[mf_2d.array()==k] = 1

    temp_SubMesh = SubMesh(mesh,  subdomains, 1)
   
    # Step 2: Iterate over every cell and copy the indices from mf_2d/mf_2d in their new equivalent for the subdomain
    submesh_domains = MeshFunction('size_t', mesh_Subdomain, mesh_Subdomain.topology().dim(), 0)
    submesh_domains.set_all(0)
    submesh_boundaries = MeshFunction('size_t', mesh_Subdomain, mesh_Subdomain.topology().dim()-1, 0)
    submesh_boundaries.set_all(0)
    
    vmap = temp_SubMesh.data().array('parent_vertex_indices', 0)
    cmap = temp_SubMesh.data().array('parent_cell_indices', 2)
    for submesh_cell in cells(mesh_Subdomain): # loop for copying the names of the subdomains
        parent_cell = cmap[submesh_cell.index()]
        submesh_domains.array()[submesh_cell.index()] = mf_2d.array()[parent_cell]
    for submesh_facet in facets(mesh_Subdomain): # loop for copying the names of the facets (boundaries)
        submesh_facet_vertices = vmap[submesh_facet.entities(0)]
        for facet in facets(mesh):
            if set(facet.entities(0)) == set(submesh_facet_vertices):
                submesh_boundaries.array()[submesh_facet.index()] = mf_1d.array()[facet.index()]
                break

    return submesh_boundaries, submesh_domains
1 Like

Hi Emanuel Gebauer,

Many thanks for sharing the code. I have the same issue with boundary labels and I wish to know what are the “indices” and “mesh_Subdomain” and how can I get them for input?

For example:

mvc = MeshValueCollection(“size_t”, mesh, 3)
mvc_b = MeshValueCollection(“size_t”, mesh, 2)

with XDMFFile(“md.xdmf”) as infile:
infile.read(mvc, “name_to_read”)
md = cpp.mesh.MeshFunctionSizet(mesh, mvc)

with XDMFFile(“mf.xdmf”) as infile:
infile.read(mvc_b, “name_to_read”)
mf = cpp.mesh.MeshFunctionSizet(mesh, mvc_b)

I suppose the md here is for the “mf_2d” and mf is for the “mf_1d”.

Thank you!
Best

Hi,

sorry, i should have been more clear in my code, i just copied it out of my project.

  • mesh_Subdomain is a MeshView object created via MeshView.create(subdomain, idx) as here: Assigning one VectorFunctionSpace to multiple submeshes - #2 by Emanuel_Gebauer

  • indices is a list of numbers to identify the subdomain for which you want to get your factet/cell meshfunction.
    So let’s say you want have a meshFunction (let’s call it mf_2d_subdomain) for your subdomain, which is marked in your original mesh/MeshFunction (called md) with a 1.
    In order to get the mesh from your subdomain you’d write:

mesh_subdomain = Meshview.create(md, 1)

This creates a submesh for your subdomain. The code in Assigning one VectorFunctionSpace to multiple submeshes - #2 by Emanuel_Gebauer only extens this so that you can define your subdomain by a list of numbers.
Then you would want to get your meshfunction for your subdomain. For this you would use the above code and write:

submesh_mf, submesh_md = extract_SubMesh_FacetDomain(mesh, mesh_subdomain, md, mf, [1])

Here I replaced in my code with mf_2d with md and mf_1d with mf.

Hope this helps for you :slight_smile:

1 Like

Hi,

Thank you for the reply! I am running a 3D mesh so is it correct to change:

cmap = temp_SubMesh.data().array('parent_cell_indices', 2)

to

cmap = temp_SubMesh.data().array('parent_cell_indices', 3)

?

The mesh has around 1million tetra and I run it for around 6h but no sign of finishing. I am not sure if there is anything wrong with the way I changed it or it is the problem with my mesh?

Thank you again.

Best,
CX

I think it is the ‘’ for submesh_facet in facets(mesh_Subdomain): ‘’
iteration that takes a long time since the previous steps were done in seconds.