Dear Community,
For coupled problems (e.g. FSI), it can be beneficial to have a routine that removes Dirichlet BCs that are both set on a fluid and a solid mesh node (e.g. when two surfaces with DBCs meet at an edge), since the Lagrange multiplier field at these nodes does not have a proper solution.
Hence, my logic in doing so would be to go from the DBC dof indices - in my understanding local with respect to the submesh dofmap - to the global indices, intersect the arrays, remove common entries in one of them, and then go back to the local indices (w.r.t. the submesh).
However, I do not understand the logic of dof index oderings on the submeshes. The following MWE on the one hand selects the common dofs between two submeshes, and then tries to find these common dofs via the DBC dof indices of DBCs applied to the center facets of the main mesh - but at the submesh boundaries. (MWE with dolfinx nightly container from 6 Aug 2025)
#!/usr/bin/env python3
from dolfinx import mesh, fem
from mpi4py import MPI
import numpy as np
n = 2
# Demo assumes faces lie along x[0] == 0.5, so check n is even
assert n % 2 == 0
msh = mesh.create_unit_square(MPI.COMM_WORLD, n, n)
tdim = msh.topology.dim
fdim = tdim - 1
left_cells = mesh.locate_entities(msh, tdim, lambda x: x[0] <= 0.5)
left_sm, left_em, left_vm, left_gm = mesh.create_submesh(msh, tdim, left_cells)
right_cells = mesh.locate_entities(msh, tdim, lambda x: x[0] >= 0.5)
right_sm, right_em, right_vm, right_gm = mesh.create_submesh(msh, tdim, right_cells)
# function spaces and functions on the submeshes
V_l = fem.functionspace(left_sm, ("Lagrange", 1, (left_sm.geometry.dim,)))
f_l = fem.Function(V_l)
V_r = fem.functionspace(right_sm, ("Lagrange", 1, (right_sm.geometry.dim,)))
f_r = fem.Function(V_r)
centre_dofs_left = fem.locate_dofs_geometrical(V_l, lambda x: np.isclose(x[0], 0.5))
centre_dofs_right = fem.locate_dofs_geometrical(V_r, lambda x: np.isclose(x[0], 0.5))
dbcs_l, dbcs_r = [], []
dbcs_l.append( fem.dirichletbc(f_l, centre_dofs_left) )
dbcs_r.append( fem.dirichletbc(f_r, centre_dofs_right) )
# get dofs numbering with respect to main mesh - left side
num_vertices_l = V_l.dofmap.index_map.size_local + V_l.dofmap.index_map.num_ghosts
bs = V_l.dofmap.bs
dofs_left_ = np.empty(bs*num_vertices_l, dtype=np.int32)
for i, vert in enumerate(left_gm):
for j in range(bs):
dofs_left_[bs*i+j] = bs*vert+j
# get dofs numbering with respect to main mesh - right side
num_vertices_r = V_r.dofmap.index_map.size_local + V_r.dofmap.index_map.num_ghosts
bs = V_r.dofmap.bs
dofs_right_ = np.empty(bs*num_vertices_r, dtype=np.int32)
for i, vert in enumerate(right_gm):
for j in range(bs):
dofs_right_[bs*i+j] = bs*vert+j
common_dofs = np.intersect1d(dofs_left_,dofs_right_)
print("common_dofs btw. submeshes (=interface): ",common_dofs)
# get dofs of DBCs w.r.t. main mesh
dbc_dofs_l = dofs_left_[dbcs_l[0].dof_indices()[0]]
dbc_dofs_r = dofs_right_[dbcs_r[0].dof_indices()[0]]
print(dbc_dofs_l)
print(dbc_dofs_r)
common_dofs_dbcs = np.intersect1d(dbc_dofs_l,dbc_dofs_r)
# should yield the same set of dofs like common_dofs
print("common_dofs of DBCs: ",common_dofs_dbcs)
Here, I would expect that common_dofs_dbcs yields the same set of indices as common_dofs, right? But it does not. Would be great if someone has a hint on how dof orderings happen on submeshes.
Best,
Marc