Hi, Everyone!
Currently, I am decoupling and computing a 3D model:
E+ u\times B-(1/Rm)\curl B=g in \Omega E\times n=0 on \partial Omaga
Here, E represents the electric field, B represents the magnetic field, and u represents the velocity field. The discrete format of the equation is:
(E_h^n,F)+(u_h^n\times B_h^{n-1},F)-(1/Rm)*(B_h^n,\curl F)=(g,F)
I use the RT0 element to approximate B_h^n, the ND0(lowest-order edge element) to approximate E_h^n, and the vector P1 element to approximate u. And the B and u involved in the code are all calculated using the exact solution, but the convergence order I calculated was always incorrect. Can you help me see what’s wrong with the code?
from petsc4py import PETSc
from mpi4py import MPI
import numpy as np
import ufl
from dolfinx import mesh, fem
from dolfinx.fem.petsc import assemble_matrix, assemble_vector, apply_lifting, create_vector, set_bc
import time
from ufl import dx, grad, dot, lhs, rhs,inner,cross,curl,ds
from basix.ufl import mixed_element,element
from numpy import sin, cos, pi, exp
# 精确解
Rm=1
left =0.0
right = 1.0
bottom = 0.0
top = 1.0
near = 0.0
far = 1.0
def ExactU(x,t):
factor = exp(-t) + 1.0
temp1=factor * sin(pi * x[0]) *sin(pi * x[1]) *sin(pi * x[2])
temp2=np.zeros_like(x[0])
temp3=factor * (x[2]**2 - x[2]) *sin(pi * x[0]) * sin(pi * x[1])
return [temp1,temp2,temp3]
def ExactE(x,t):
sint = sin(pi*t)+1.5
temp1=sint*(x[1]**2-x[1])*sin(pi*x[2])
temp2=sint*(x[0]**2-x[0])*sin(pi*x[2])
temp3=sint*(x[0]**2-x[0])*sin(pi*x[1])
return [temp1,temp2,temp3]
def ExactB(x,t):
cost=(1/pi)*cos(pi*t)-1.5*t
temp1=cost*pi*(x[0]**2-x[0])*(cos(pi*x[1])-cos(pi*x[2]))
temp2=cost*(pi*(x[1]**2-x[1])*cos(pi*x[2])-(2*x[0]-1)*sin(pi*x[1]))
temp3=cost*(2*x[0]-2*x[1])*sin(pi*x[2])
return [temp1,temp2,temp3]
def rhs_expr(x, t):
factor = np.exp(-t) + 1.0
sint=sin(pi*t)+1.5
cost=1/pi*cos(pi*t)-1.5*t
E1=(x[1]**2-x[1])*sin(pi*x[2])
E2=(x[0]**2-x[0])*sin(pi*x[2])
E3=(x[0]**2-x[0])*sin(pi*x[1])
u1= sin(pi * x[0]) * sin(pi * x[1]) *sin(pi * x[2])
u2= np.zeros_like(x[0])
u3= (x[2]**2 - x[2]) * sin(pi * x[0]) * sin(pi * x[1])
B1=pi*(x[0]**2-x[0])*(cos(pi*x[1])-cos(pi*x[2]))
B2=(pi*(x[1]**2-x[1])*cos(pi*x[2])-(2*x[0]-1)*sin(pi*x[1]))
B3=(2*x[0]-2*x[1])*sin(pi*x[2])
curlB1=-2*sin(pi*x[2])+pi**2*(x[1]**2-x[1])*sin(pi*x[2])
curlB2=-2*sin(pi*x[2])+pi**2*(x[0]**2-x[0])*sin(pi*x[2])
curlB3=-2*sin(pi*x[1])+pi**2*(x[0]**2-x[0])*sin(pi*x[1])
temp1=sint*E1\
+factor*cost*(u2*B3-u3*B2)\
-(1/Rm)*cost*curlB1
temp2=sint*E2\
+factor*cost*(u3*B1-u1*B3)\
-(1/Rm)*cost*curlB2
#
temp3=sint*E3\
+factor*cost*(u1*B2-u2*B1)\
-(1/Rm)*cost*curlB3
return [temp1, temp2,temp3]
# 固定终止时间
T = 1 / 2
# 结果记录
hs = []
errors_L2 = []
errors_max = []
errors_H1 = []
errors_curl =[]
start_time = time.time()
for i in range(3):
n = 2 ** (i + 2) # 空间划分数 n = 2^{i+2}
h = 1.0 / n
num_steps = 2 ** (2 * i + 3) # 时间划分数 tau = 2^{2i+4}
dt = T / num_steps
t = 0.0
# 创建 3D 网格(单位立方体)
domain = mesh.create_unit_cube(MPI.COMM_WORLD, n, n, n, mesh.CellType.tetrahedron)
x = ufl.SpatialCoordinate(domain)
V = fem.functionspace(domain, ("Lagrange", 1)) # 线性 Lagrange 元
Vv=fem.functionspace(domain, ("Lagrange", 1, (3, )))
# 磁场空间与电场空间的定义
VB = fem.functionspace(domain, ("RT", 1))
VE = fem.functionspace(domain, ("N1curl", 1))
E_n=fem.Function(VE)
u_n = fem.Function(Vv)
B_old = fem.Function(VB)
B_old.interpolate(lambda x: ExactB(x, t))
B_n = fem.Function(VB)
E_D = fem.Function(VE)
E_D.interpolate(lambda x: ExactE(x, t))
f_fun = fem.Function(VE)
# 施加 Dirichlet 边界条件(3D 边界)
tdim = domain.topology.dim
fdim = tdim - 1
domain.topology.create_connectivity(fdim, tdim)
boundary_facets = mesh.exterior_facet_indices(domain.topology)
bc = fem.dirichletbc(E_D, fem.locate_dofs_topological(VE, fdim, boundary_facets))
ns = ufl.FacetNormal(domain)
# 变分形式(3D 形式相同,但 dx 现在是 3D 积分)
E, F = ufl.TrialFunction(VE), ufl.TestFunction(VE)
G = inner(E,F)*dx + inner(cross(u_n,B_old),F)*dx- inner(f_fun,F)*dx \
- (1/Rm)*inner(B_n,curl(F))*dx
a = fem.form(lhs(G))
L = fem.form(rhs(G))
# 组装矩阵和向量
A = assemble_matrix(a, bcs=[bc])
A.assemble()
b = create_vector(L)
E_h =fem.Function(VE)
# 定义求解器(直接法:LU 分解)
solver = PETSc.KSP().create(domain.comm)
solver.setOperators(A)
# solver.setType(PETSc.KSP.Type.PREONLY)
# solver.getPC().setType(PETSc.PC.Type.LU)
solver.setType(PETSc.KSP.Type.BCGS)
pc1 = solver.getPC()
pc1.setType(PETSc.PC.Type.HYPRE)
pc1.setHYPREType("boomeramg")
# 时间步进
for n_step in range(num_steps):
t += dt
u_n.interpolate(lambda x:ExactU(x,t))
B_n.interpolate(lambda x: ExactB(x, t))
E_D.interpolate(lambda x: ExactE(x, t))
bc = fem.dirichletbc(E_D, fem.locate_dofs_topological(VE, fdim, boundary_facets))
f_fun.interpolate(lambda x: rhs_expr(x, t))
# 更新矩阵
A.zeroEntries()
assemble_matrix(A, a, bcs=[bc])
# assemble_matrix(A, a, bcs=[bc])
A.assemble()
# 重新组装右端向量
with b.localForm() as loc_b:
loc_b.set(0)
assemble_vector(b, L)
# 施加边界条件
apply_lifting(b, [a], [[bc]])
b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
set_bc(b, [])
# 求解线性系统
solver.solve(b, E_h.x.petsc_vec)
E_h.x.scatter_forward()
B_old.interpolate(lambda x: ExactB(x, t))
# 计算误差(在更高阶空间)
V_dg = fem.functionspace(domain, ("Lagrange", 1, (3,)))
# V_dg = fem.functionspace(domain, ("N1curl", 2))
E_ex = fem.Function(V_dg)
E_ex.interpolate(lambda x: ExactE(x,t))
E_ex1 = fem.Function(VE)
E_ex1.interpolate(lambda x:ExactE(x,t))
# L2 误差
error_L2 = np.sqrt(domain.comm.allreduce(fem.assemble_scalar(fem.form((E_h - E_ex)**2 * ufl.dx)), op=MPI.SUM))
error_max= np.max(np.abs(E_ex1.x.array - E_h.x.array))
error_curl = np.sqrt(domain.comm.allreduce(fem.assemble_scalar(fem.form(curl(E_h - E_ex)**2*dx)), op=MPI.SUM))
if MPI.COMM_WORLD.rank == 0:
hs.append(h)
errors_L2.append(error_L2)
errors_max.append(error_max)
errors_curl.append(error_curl)
print(f"[i={i}] n={n}, num_steps={num_steps}, dt={dt:.3e}, h={h:.3e}")
print(f" → L2 error = {error_L2:.3e}, max error={error_max:.3e},curl error={error_curl:.3e}")
end_time = time.time() # 计时结束
elapsed = end_time - start_time
# 输出收敛阶
if MPI.COMM_WORLD.rank == 0:
print("\nEstimated convergence rates (L2):")
for j in range(1, len(hs)):
r1 = np.log(errors_L2[j-1]/errors_L2[j]) / np.log(hs[j-1]/hs[j])
r2 = np.log(errors_max[j-1]/errors_max[j]) / np.log(hs[j-1]/hs[j])
r3 = np.log(errors_curl[j-1]/errors_curl[j]) / np.log(hs[j-1]/hs[j])
print(f"h={hs[j-1]:.3e} → h={hs[j]:.3e}, rate ≈ {r1:.2f},rate ≈ {r2:.2f},rate ≈ {r3:.2f}")
print(f"\nTotal runtime: {elapsed:.2f} seconds")
Looking forward to your reply!