Hello,everyone
I’m trying to solve the stokes problem on the unite square \Omega={]0,1[}^2 (with only Dirichlet conditions) :
-\Delta u+ \nabla p=f~~~~ in ~\Omega
\nabla.u=0~~~~ in ~\Omega
u=0~~~ in \partial \Omega
such that:
f(x,y)= -\Delta u_{ex}+\nabla p_{ex}
u_{ex}=(-256y(y − 1)(2y − 1)x^2(x − 1)^2,256x(x − 1)(2x − 1)y^2(y − 1)^2)
p_{ex}= (x-0.5)(y-0.5)
The code doesn’t return any errors, but from visualizing the solutions (velocity and pressure), i noticed that :
- the pressure doesn’t seem to converge to the exact solution.
- the Dirichlet conditions are not reconized, since the velocity is not nul in the boundaries of the domain (as shown in the last figure).
Could you help me please to fixe these problems. Thank you in advance.
Here is my code :
from dolfinx import plot
from mpi4py import MPI
from petsc4py import PETSc
import numpy as np
import ufl
from basix.ufl import element, mixed_element
from dolfinx import mesh, fem, plot, io
from dolfinx.fem import (Function, dirichletbc, form, functionspace,
locate_dofs_topological)
from dolfinx.fem.petsc import assemble_matrix_block, assemble_vector_block
from dolfinx.mesh import CellType, create_rectangle, locate_entities_boundary
from ufl import div, dx, grad, inner
import matplotlib.pyplot as plt
import pyvista
from dolfinx.plot import vtk_mesh
import warnings
warnings.filterwarnings("ignore")
# Define boundary conditions
def noslip_boundary(x):
return np.logical_or(np.logical_or(np.isclose(x[0], 0.0), np.isclose(x[0], 1.0)),np.logical_or(np.isclose(x[1], 0.0), np.isclose(x[1], 1.0)))
def u_expression(x):
return np.stack((np.zeros(x.shape[1]), np.zeros(x.shape[1])))
def mixed_direct(TH,msh):
# Define function spaces
W = functionspace(msh, TH)
W0, _ = W.sub(0).collapse()
W1,_ = W.sub(1).collapse()
u_d=Function(W0)
# Dirichlet Conditions
u_d.interpolate(u_expression)
facets = locate_entities_boundary(msh, 1, noslip_boundary)
dofs = locate_dofs_topological((W.sub(0), W0), 1, facets)
bc0 = dirichletbc(u_d, dofs, W.sub(0))
bcs = [bc0]
# Define variational problem
(u, p) = ufl.TrialFunctions(W)
(v, q) = ufl.TestFunctions(W)
u_exact = Function(W0)
p_exact=Function(W1)
p_exact.interpolate(lambda x: (x[0]-0.5)*(x[1]-0.5))
u_exact.interpolate(lambda x: (-256*x[1]*(x[1]-1)*(2*x[1]-1)*(x[0]**2)*(x[0]-1)**2, 256*x[0]*(x[0]-1)*
(2*x[0]-1)*(x[1]**2)*(x[1]-1)**2))
f=-div(grad(u_exact))+grad(p_exact)
a = form((inner(grad(u), grad(v)) + inner(p, div(v)) + inner(div(u), q)) * dx)
L = form(inner(f, v) * dx)
# Assemble LHS matrix and RHS vector
A = fem.petsc.assemble_matrix(a, bcs=bcs)
A.assemble()
b = fem.petsc.assemble_vector(L)
fem.petsc.apply_lifting(b, [a], bcs=[bcs])
b.ghostUpdate(addv=PETSc.InsertMode.ADD, mode=PETSc.ScatterMode.REVERSE)
# Set Dirichlet boundary condition values in the RHS
fem.petsc.set_bc(b, bcs)
# Create and configure solver
ksp = PETSc.KSP().create(msh.comm)
ksp.setOperators(A)
ksp.setType("preonly")
# Configure MUMPS to handle pressure nullspace
pc = ksp.getPC()
pc.setType("lu")
pc.setFactorSolverType("mumps")
pc.setFactorSetUpSolverType()
pc.getFactorMatrix().setMumpsIcntl(icntl=24, ival=1)
pc.getFactorMatrix().setMumpsIcntl(icntl=25, ival=0)
# Compute the solution
U = Function(W)
try:
ksp.solve(b, U.vector)
except PETSc.Error as e:
if e.ierr == 92:
print("The required PETSc solver/preconditioner is not available. Exiting.")
print(e)
exit(0)
else:
raise e
ksp.solve(b, U.vector)
# Split the mixed solution and collapse
u, p = U.sub(0).collapse(), U.sub(1).collapse()
u.x.scatter_forward()
p.x.scatter_forward()
return u,p
def compute_errors(u,p,msh,TH):
W = functionspace(msh, TH)
W0, _ = W.sub(0).collapse()
W1,_ = W.sub(1).collapse()
# Define velocity exact solution
u_exact = Function(W0)
u_exact.interpolate(lambda x: (-256*x[1]*(x[1]-1)*(2*x[1]-1)*(x[0]**2)*(x[0]-1)**2, 256*x[0]*(x[0]-1)*
(2*x[0]-1)*(x[1]**2)*(x[1]-1)**2))
#Define pressure exact solution
p_exact=Function(W1)
p_exact.interpolate(lambda x: (x[0]-0.5)*(x[1]-0.5))
# L2 error for Velocity
L2_error = fem.form(ufl.inner(u - u_exact, u- u_exact) * ufl.dx)
error_local = fem.assemble_scalar(L2_error)
error_L2 = np.sqrt(msh.comm.allreduce(error_local, op=MPI.SUM))
# H1 error for Velocity
H1_u_1 = inner(u - u_exact, u- u_exact) * ufl.dx
H1_u_2 = inner(grad(u - u_exact), grad(u- u_exact)) * ufl.dx
H1_u=fem.form(H1_u_1+H1_u_2)
H1_error=fem.assemble_scalar(H1_u)
error_H1= np.sqrt(msh.comm.allreduce(H1_error, op=MPI.SUM))
# L2 error for presure
L2_error_2 = fem.form(ufl.inner(-p - p_exact, -p- p_exact) * ufl.dx)
error_local_2 = fem.assemble_scalar(L2_error_2)
error_L2_2 = np.sqrt(msh.comm.allreduce(error_local_2, op=MPI.SUM))
return error_L2,error_H1,error_L2_2
#Convergence curve for velocity
errors_L2_u = [ ]
errors_L2_p=[ ]
errors_H1_u=[ ]
h=[]
num_elements_list = [4,8,16, 32,64]
for i in num_elements_list:
mesh = create_rectangle(MPI.COMM_WORLD, ((0.0, 0.0), (1.0, 1.0)), (i,i), CellType.triangle)
P2 = element("Lagrange", mesh.basix_cell(), 2, shape=(mesh.geometry.dim,))
P1 = element("Lagrange", mesh.basix_cell(), 1)
TH = mixed_element([P2, P1])
u,p=mixed_direct(TH,mesh)
error_u,error_H1,error_p=compute_errors(u,p,mesh,TH)
errors_L2_u.append(error_u)
errors_L2_p.append(error_p)
errors_H1_u.append(error_H1)
h.append(1./i)
V = fem.FunctionSpace(mesh, P2)
num_dofs_local = (V.dofmap.index_map.size_local) * V.dofmap.index_map_bs
num_dofs_global = V.dofmap.index_map.size_global * V.dofmap.index_map_bs
fig = plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
legend=[]
plt.plot(h, errors_L2_u, "bo-")
plt.plot(h, errors_H1_u, "ro-")
legend += [r"$L^2(\mathbf{u_h}-\mathbf{u}_{ex})$",r"$H^1(\mathbf{u_h}-\mathbf{u}_{ex})$"]
plt.legend(legend)
plt.xlim(plt.xlim()[::-1])
plt.xlabel("h")
plt.ylabel("error")
plt.title("convergence curve for velocity")
plt.subplot(1, 2, 2)
legend=[]
plt.plot(h, errors_L2_u, "bo-")
plt.plot(h, errors_H1_u, "ro-")
legend += [r"$L^2(\mathbf{u_h}-\mathbf{u}_{ex})$",r"$H^1(\mathbf{u_h}-\mathbf{u}_{ex})$"]
plt.legend(legend)
plt.xscale("log")
plt.yscale("log")
plt.xlabel("log(h)")
plt.ylabel("log(error)")
plt.axis("equal")
plt.title("conergence curve for velocity")
plt.show()
# Convergence curve for pressure
fig = plt.figure(figsize=(14, 6))
plt.subplot(1, 2, 1)
legend=[]
plt.plot(h, errors_L2_p, "bo-")
legend += [r"$L^2(\mathbf{p_h}-\mathbf{p}_{ex})$"]
plt.legend(legend)
plt.xlim(plt.xlim()[::-1])
plt.xlabel("h")
plt.ylabel("error")
plt.title("convergence curve for pressure")
plt.subplot(1, 2, 2)
legend=[]
plt.plot(h, errors_L2_p, "ro-")
legend += [r"$L^2(\mathbf{p_h}-\mathbf{p}_{ex})$"]
plt.legend(legend)
plt.xscale("log")
plt.yscale("log")
plt.xlabel("log(h)")
plt.ylabel("log(error)")
plt.axis("equal")
plt.title("convergence curve for pressure")
plt.show()
#Visualizing the numeric solution
mesh = create_rectangle(MPI.COMM_WORLD, ((0.0, 0.0), (1.0, 1.0)), (8,8), CellType.triangle)
P2 = element("Lagrange", mesh.basix_cell(), 2, shape=(mesh.geometry.dim,))
P1 = element("Lagrange", mesh.basix_cell(), 1)
V = fem.FunctionSpace(mesh, P2)
TH = mixed_element([P2, P1])
u_h,p_h=mixed_direct(TH,mesh)
pyvista.start_xvfb()
topology, cell_types, geometry = vtk_mesh(V)
values = np.zeros((geometry.shape[0], 3), dtype=np.float64)
values[:, :len(u_h)] = u_h.x.array.real.reshape((geometry.shape[0], len(u_h)))
# Create a point cloud of glyphs
function_grid = pyvista.UnstructuredGrid(topology, cell_types, geometry)
function_grid["u_h"] = values
glyphs = function_grid.glyph(orient="u_h", factor=0.2)
# Create a pyvista-grid for the mesh
grid = pyvista.UnstructuredGrid(*vtk_mesh(mesh, mesh.topology.dim))
# Create plotter
plotter = pyvista.Plotter()
plotter.add_mesh(grid, style="wireframe", color="k")
plotter.add_mesh(glyphs)
plotter.view_xy()
if not pyvista.OFF_SCREEN:
plotter.show()
else:
fig_as_array = plotter.screenshot("glyphs.png")
The convergence_curve for velocity was good ( i got a line, the convergence order is close to 2)
as you can see the velocity is not nul in the bounadries.