Hello Fenics Community,
I am solving stokes equation using SUPG stabilization. I am setting this problem for lid-driven cavity flow problem. I am using mixed finite element spaces. I am facing issues in the Nonlinear solver. The solver is not converging. I have tried changing solver options, but failed. Can someone help me with it? Am I missing anything in the formulation or solver setup?
I am using FeNiCSx 0.9.0. The MWE is as follows:
import numpy as np
from dolfinx import fem, mesh, nls, io
from mpi4py import MPI
import ufl
from basix.ufl import element, mixed_element
from petsc4py import PETSc
from dolfinx.nls.petsc import NewtonSolver
# Create mesh (Unit square domain)
domain = mesh.create_unit_square(MPI.COMM_WORLD, 50, 50)
def noslip_boundary(x):
return np.isclose(x[1], 0.0) | np.isclose(x[0], 0.0) | np.isclose(x[0], 1.0)
def lid_boundary(x):
return np.isclose(x[1], 1.0)
noslip_facets = mesh.locate_entities_boundary(domain, domain.topology.dim - 1, noslip_boundary)
lid_facets = mesh.locate_entities_boundary(domain, domain.topology.dim - 1, lid_boundary)
# Define equal-order elements (P1-P1 discretization)
P1v = element("Lagrange", domain.basix_cell(), 2, shape=(domain.geometry.dim,) ) # Velocity (P1)
P1p = element("Lagrange", domain.basix_cell(), 1) # Pressure (P1)
TH = mixed_element([P1v, P1p])
VQ= fem.functionspace(domain, TH)
(v, q) =ufl.TestFunctions(VQ)
# Define solution function
up = fem.Function(VQ) # Nonlinear unknown (velocity-pressure)
u, p = ufl.split(up)
# Fluid properties
nu_val = 0.01 # Kinematic viscosity
f = fem.Constant(domain, PETSc.ScalarType((0.0, 0.0))) # Body force
# SUPG stabilization parameters
h = ufl.CellDiameter(domain)
vel_norm = ufl.sqrt(ufl.inner(u, u) + 1e-6)
Pe = vel_norm * h / (2 * nu_val)
Pe = ufl.max_value(Pe, 1e-6) # Avoid zero Pe
tau_SUPG = h / (2 * vel_norm) * (ufl.exp(Pe) - ufl.exp(-Pe)) / (ufl.exp(Pe) + ufl.exp(-Pe)) - 1/Pe
# Weak form of Stokes equations
F = (nu_val * ufl.inner(ufl.grad(u), ufl.grad(v)) * ufl.dx
- p * ufl.div(v) * ufl.dx
- q * ufl.div(u) * ufl.dx
- ufl.inner(f, v) * ufl.dx)
# SUPG stabilization term
R_m = -nu_val * ufl.div(ufl.grad(u)) + ufl.grad(p) - f
F_SUPG = tau_SUPG * ufl.inner(R_m, ufl.dot(u, ufl.grad(v))) * ufl.dx
F += F_SUPG
# Get subspace of V
V0 = VQ.sub(0)
V, _ = V0.collapse()
# Get subspace of Q
Q0 = VQ.sub(1)
Q, _ = Q0.collapse()
# Define boundary conditions
zero_velocity = fem.Function(V)
zero_velocity.interpolate(lambda x: np.zeros((2, x.shape[1])))
lid_velocity = fem.Function(V)
lid_velocity.interpolate(lambda x: np.stack((np.ones_like(x[0]), np.zeros_like(x[1]))))
noslip_dofs = fem.locate_dofs_topological(V, domain.topology.dim - 1, noslip_facets)
lid_dofs = fem.locate_dofs_topological(V, domain.topology.dim - 1, lid_facets)
bc_noslip = fem.dirichletbc(zero_velocity, noslip_dofs)
bc_lid = fem.dirichletbc(lid_velocity, lid_dofs)
bcs = [bc_noslip, bc_lid]
if len(bcs) == 0:
raise ValueError("No boundary conditions found. Check domain boundaries.")
# Define nonlinear problem
uh = fem.Function(VQ)
duh = ufl.TrialFunction(VQ)
dF = ufl.derivative(F, uh, duh)
problem = fem.petsc.NonlinearProblem(F, uh, bcs=bcs, J=dF)
solver = NewtonSolver(MPI.COMM_WORLD, problem)
#nls.petsc.NewtonSolver(MPI.COMM_WORLD, problem)
solver.convergence_criterion = "incremental"
solver.rtol = 1e-3 # Looser relative tolerance
solver.atol = 1e-3 # Looser absolute tolerance
solver.max_it = 200 # Allow more iterations
solver.report = True # Print iteration details
ksp = solver.krylov_solver
opts = PETSc.Options()
option_prefix = ksp.getOptionsPrefix()
opts[f"{option_prefix}ksp_type"] = "preonly" # Use a direct solver (no Krylov iterations)
opts[f"{option_prefix}pc_type"] = "lu" # LU factorization (direct solve)
opts[f"{option_prefix}pc_factor_mat_solver_type"] = "mumps" # Use MUMPS as the direct solver
ksp.setFromOptions()
# Compute the solution
solver.solve(uh)
# Extract velocity and pressure solutions
uh_velocity = fem.Function(V)
uh_pressure = fem.Function(Q)
uh_velocity.interpolate(uh.sub(0))
uh_pressure.interpolate(uh.sub(1))
# Save results
with io.XDMFFile(MPI.COMM_WORLD, "velocity.xdmf", "w") as file:
file.write_mesh(domain)
file.write_function(uh_velocity)
with io.XDMFFile(MPI.COMM_WORLD, "pressure.xdmf", "w") as file:
file.write_function(uh_pressure)