The mesh and code main.py
are here: https://drive.google.com/file/d/1haXGCXzKZSS1QlqMUBpGiYLCJEFKqP7x/view?usp=sharing. As the whole mesh is complicated, so the only problematic part is truncated.
Run the code with DOLFINx v0.9.0, yielding 5 DOFs to the all-zero rows as
Found coordinates: dof = np.int32(32), coords[index] = array([ 5976.96870968, -1193.41938619, -3040. ])
Found coordinates: dof = np.int32(142), coords[index] = array([ 5967.45756383, -907.49851714, -3040. ])
Found coordinates: dof = np.int32(701), coords[index] = array([ 5999.11394495, -705.38 , -3000. ])
Found coordinates: dof = np.int32(3501), coords[index] = array([ 6318.38, -687.88, -3040. ])
Found coordinates: dof = np.int32(11919), coords[index] = array([ 6436.52080859, -1229.05201739, -3000. ])
4 of them are from the edges due to mesh truncation, which can be igonred. 1 of them causes the problem in the realistic application, which is marked by the green arrow:
Found coordinates: dof = np.int32(3501), coords[index] = array([ 6318.38, -687.88, -3040. ])
MWE (main.py
):
import os
os.environ['OMP_NUM_THREADS'] = '1'
import time
from mpi4py import MPI
comm = MPI.COMM_WORLD
rank = comm.rank
import basix, ufl
import numpy as np
from dolfinx import fem, io, mesh
from dolfinx.fem.petsc import LinearProblem
prefix = "./" # CHANGE here to the mesh
comm = MPI.COMM_WORLD
rank = comm.rank
mu = 1
tdim = 3
fdim = tdim - 1
v_order = 2
t0 = time.time()
_mesh = io.XDMFFile(comm, prefix + "truncated.xdmf", "r").read_mesh()
def inlet(x):
return x[0] < 6050
def outlet(x):
return x[0] > 6800
V_element = basix.ufl.element("Lagrange", _mesh.basix_cell(), v_order, shape=(tdim,))
Q_element = basix.ufl.element("Lagrange", _mesh.basix_cell(), 1)
W_element = basix.ufl.mixed_element([V_element, Q_element])
W = fem.functionspace(_mesh, W_element)
W0 = W.sub(0)
V = W0.collapse()[0]
W1 = W.sub(1)
Q = W1.collapse()[0]
_mesh.topology.create_connectivity(fdim, tdim)
boundary_facets = mesh.exterior_facet_indices(_mesh.topology)
Gamma_inlet = mesh.locate_entities_boundary(_mesh, fdim, inlet)
Gamma_outlet = mesh.locate_entities_boundary(_mesh, fdim, outlet)
inout = np.concatenate((Gamma_inlet, Gamma_outlet))
Gamma_wall = np.setdiff1d(boundary_facets, inout)
vq = ufl.TestFunction(W)
(v, q) = ufl.split(vq)
dup = ufl.TrialFunction(W)
(du, dp) = ufl.split(dup)
up = fem.Function(W)
(u, p) = ufl.split(up)
F_stokes = ( mu * ufl.inner((2.0*ufl.sym(ufl.nabla_grad(du))),
ufl.sym(ufl.nabla_grad(v))) * ufl.dx
- ufl.inner(dp, ufl.div(v)) * ufl.dx
+ ufl.inner(ufl.div(du), q) * ufl.dx
+ ufl.inner(u, v) * ufl.dx)
# Pseudo boundary condition
u_bc = fem.Function(V)
bc_wall = fem.dirichletbc(u_bc, fem.locate_dofs_topological((W0, V), fdim, Gamma_wall), W0)
bc_inlet = fem.dirichletbc(u_bc, fem.locate_dofs_topological((W0, V), fdim, Gamma_inlet), W0)
bcs = [bc_inlet, bc_wall]
a, L = ufl.system(F_stokes)
problem_stokes = LinearProblem(a, L, bcs=bcs, u=up, petsc_options={"ksp_type": "preonly",
"pc_type": "lu",
"pc_factor_mat_solver_type": "mumps"})
problem = problem_stokes
problem._A.zeroEntries()
fem.petsc.assemble_matrix(problem._A, problem._a, bcs=problem.bcs)
problem._A.assemble()
# Get diagonal of assembled A matrix
diagonal = problem._A.getDiagonal()
diagonal_values = diagonal.array
# Get zero rows of assembled A matrix.
zero_rows = problem._A.findZeroRows()
zero_rows_values_global = zero_rows.array
local_start = W.dofmap.index_map.local_range[0] * W.dofmap.index_map_bs
# Maps global numbering to local numbering
zero_rows_values_local = zero_rows_values_global - \
local_start
# Find the subspaces that contain zero rows
for i in range(W.num_sub_spaces):
v_sub, sub_to_parent = W.sub(i).collapse()
if len(zero_rows_values_local) > 0:
coords = v_sub.tabulate_dof_coordinates()
for dof in zero_rows_values_local:
if dof in sub_to_parent:
index = sub_to_parent.index(dof)
print(f"Found coordinates: {dof = }, {coords[index] = }", flush = True)