A demo from MHD

Hello everyone.I am trying to solve an MHD problem:(Actually, it’s example 6.1 from
A fully divergence-free finite element method formagnetohydrodynamic equations
DOI: 10.1142/S0218202518500173)
The variational form is:


where

image
image

the python code is :
import numpy as np
import ufl
from dolfinx import cpp as _cpp
from dolfinx import fem
from dolfinx.fem import (Constant, Function, FunctionSpace, dirichletbc,
                         extract_function_spaces, form,
                         locate_dofs_geometrical, locate_dofs_topological)
from dolfinx.io import XDMFFile
from dolfinx.mesh import (CellType, GhostMode, create_rectangle,
                          locate_entities_boundary,create_unit_cube)
from ufl import div, dx, grad, inner,dot,outer,ds,dS,avg,jump,rot,cross

from mpi4py import MPI
from petsc4py import PETSc

# Create mesh
msh = create_unit_cube(MPI.COMM_WORLD,
                       8,8,8,
                       CellType.tetrahedron, GhostMode.none)

u_element=ufl.FiniteElement("RT", ufl.tetrahedron, 1)    # H(div)     Vh
A_element=ufl.FiniteElement("N1curl",ufl.tetrahedron,1)   # H(curl)      Ch
P_element=ufl.FiniteElement("DG",ufl.tetrahedron,0)       #                 Qh
#fai_element=ufl.FiniteElement("DG",ufl.tetrahedron,2)     #     SH

TH= ufl.MixedElement([u_element, A_element,P_element])

W =fem.FunctionSpace(msh,TH)

W0,_=W.sub(0).collapse()
W1,_=W.sub(1).collapse()
W2,_=W.sub(2).collapse()

(un,An,Pn)=ufl.TrialFunctions(W)   #
(v,fai,q)=ufl.TestFunctions(W)   #

#Vh=fem.FunctionSpace(msh,u_element)
un_1=fem.Function(W0)                         
un_2=fem.Function(W0)

def initial_u(x):
    values=np.zeros((3,x.shape[1]))
    values[0,:]=x[1]    #y
    values[1,:]=x[2]    #z
    values[2,:]=x[0]    #x
    return values

un_1.interpolate(initial_u)   

#Ch=fem.FunctionSpace(msh,A_element)
An_1=fem.Function(W1)                     
An_2=fem.Function(W1)

def initial_A(x):
    values=np.zeros((3,x.shape[1]))
    values[0,:]=x[2]      #z
    values[1,:]=x[0]*0    #0
    values[2,:]=x[1]      #y
    return values
An_1.interpolate(initial_A)

P=fem.Function(W2)
def initial_P(x):
    values=np.zeros((x.shape[1]))
    return values
P.interpolate(initial_P)

#------------------------------------------------------------------------------------------

x = ufl.SpatialCoordinate(msh)
h = ufl.CellDiameter(msh)
n = ufl.FacetNormal(msh)

unv=(un+un_1)*0.5
uns=1.5*un_1-0.5*un_2

Anv=(An+An_1)*0.5
Ans=1.5*An_1-0.5*An_2

lmbda = ufl.conditional(ufl.gt(ufl.dot(un_1, n), 0), 1, 0)    

u_uw = lmbda("+") * un_1("+") + lmbda("-") * un_1("-")        #  u ->   upwind flux

tao_1=1000
k=1
Rm=1
alph=10
b = 2*tao_1*inner(unv,v)*dx
b -= inner(unv,div(outer(uns,v)))*dx
b += inner((dot(un_1, n))("+") * u_uw, v("+")) * dS+inner((dot(un_1, n))("-") * u_uw, v("-")) * dS     # 
b += inner(dot(un_1, n) * lmbda * un, v) * ds
b += inner(grad(unv),grad(v))*dx +alph/h('+')*inner(jump(unv),jump(v))*dS 
b -= inner(avg(dot(grad(unv),n)),jump(v))*dS-inner(avg(dot(grad(v),n)),jump(unv))*dS 
b += 2*k*tao_1*inner(rot(Anv),rot(fai))*dx
b += k*inner(2*tao_1*Anv+cross(rot(Ans),unv),2*tao_1*fai+cross(rot(Ans),v))*dx 
b += inner(2*tao_1*un_1,v)*dx+k*inner(2*tao_1*An_1,2*tao_1*fai+cross(rot(Ans),v))*dx 
b += 2*tao_1*inner(div(unv),div(v))*dx+inner(div(unv),q)*dx
a=ufl.lhs(b)
L=ufl.rhs(b)

a=fem.form(a)
L=fem.form(L)

#------------------------------------------------------------------------------------
def local_A(x):               #mark  boundary of A 
    return np.isclose(x[1],0)        #mark y=1

def local_u(x):               #mark  boundary of u 
    return np.logical_and(np.isclose(x[2],0),np.isclose(x[0],0))    #mark z=0 && x=0

def u_D(x):
    values=np.zeros((3,x.shape[1]))
    values[0,:]=0           #y
    values[1,:]=0           #z
    values[2,:]=x[0]        #x
    return values

def A_D(x):
    values=np.zeros((3,x.shape[1]))
    values[0,:]=x[2]      #z
    values[1,:]=0         #z
    values[2,:]=0         #x
    return values

Vh=fem.FunctionSpace(msh,u_element)
u_boundary = Function(W0)
u_boundary.interpolate(u_D)
facets = locate_entities_boundary(msh,2, local_u)
dofs = locate_dofs_topological((W.sub(0), Vh), 2, facets)
bc0 = dirichletbc(u_boundary, dofs, W.sub(0))

Dh=fem.FunctionSpace(msh,A_element)
A_boundary = Function(W1)
A_boundary.interpolate(A_D)
facets = locate_entities_boundary(msh,2, local_A)
dofs = locate_dofs_topological((W.sub(1), Dh), 2, facets)
bc1 = dirichletbc(A_boundary, dofs, W.sub(1))
bcs=[bc0,bc1]
#----------------------------------------------------------------------------------------------
from dolfinx.fem.petsc import (apply_lifting, assemble_matrix, assemble_vector, 
                               create_vector, set_bc)

A=assemble_matrix(a,bcs=bcs)
A.assemble()
b=create_vector(L)

solver = PETSc.KSP().create(msh.comm)
solver.setOperators(A)
solver.setType(PETSc.KSP.Type.BCGS)
pc1 = solver.getPC()
pc1.setType(PETSc.PC.Type.HYPRE)
pc1.setHYPREType("boomeramg")
solver.setFromOptions()

def domain_average(msh, v):
    """Compute the average of a function over the domain"""
    vol = msh.comm.allreduce(fem.assemble_scalar(fem.form(fem.Constant(msh, 1.0) * dx)), op=MPI.SUM)
    return (1 / vol) * msh.comm.allreduce(fem.assemble_scalar(fem.form(v * dx)), op=MPI.SUM)

wn=Function(W)
t=0
for n in range(1):
    if n==0:
        t += 0.001
        un_2.x.array[::]=un_1.x.array[::]
        An_2.x.array[::]=An_1.x.array[::]
        fem.petsc.assemble_matrix(a,bcs=bcs)          # type: ignore
        assemble_vector(b, L)  
        apply_lifting(b, [a], [bcs])                  
        b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
        set_bc(b, bcs)
        solver.solve(b, wn.vector)
        u = wn.sub(0).collapse()
        A = wn.sub(1).collapse()
        p = wn.sub(2).collapse()
        un_2.x.array[::]=un_1.x.array[::]
        un_1.x.array[::]=u.x.array[::]
        An_2.x.array[::]=An_1.x.array[::]
        An_1.x.array[::]=A.x.array[::]
        un_2.x.scatter_forward()
        un_1.x.scatter_forward()
        An_2.x.scatter_forward()
        An_1.x.scatter_forward()
        p.x.array[:] -= domain_average(msh, p)
    else:
        t += 0.001
        fem.petsc.assemble_matrix(a,bcs=bcs)  # type: ignore
        with b.localForm() as b_loc:
            b_loc.set(0)
        assemble_vector(b, L)        
        apply_lifting(b, [a], [bcs])                 
        b.ghostUpdate(addv=PETSc.InsertMode.ADD_VALUES, mode=PETSc.ScatterMode.REVERSE)
        set_bc(b, bcs)
        solver.solve(b, wn.vector)
        u = wn.sub(0).collapse()
        A = wn.sub(1).collapse()
        p = wn.sub(2).collapse()
        un_2.x.array[::]=un_1.x.array[::]
        un_1.x.array[::]=u.x.array[::]
        An_2.x.array[::]=An_1.x.array[::]
        An_1.x.array[::]=A.x.array[::]
        un_2.x.scatter_forward()
        un_1.x.scatter_forward()
        An_2.x.scatter_forward()
        An_1.x.scatter_forward()
        p.x.array[:] -= domain_average(msh, p)
    print('t is',t)

u = wn.sub(0).collapse()
A = wn.sub(1).collapse()
p = wn.sub(2).collapse()

with XDMFFile(MPI.COMM_WORLD, "out_WH/velocity.xdmf", "w") as ufile_xdmf:
    u.x.scatter_forward()
    ufile_xdmf.write_mesh(msh)
    ufile_xdmf.write_function(u)

with XDMFFile(MPI.COMM_WORLD, "out_WH/A.xdmf", "w") as pfile_xdmf:
    A.x.scatter_forward()
    pfile_xdmf.write_mesh(msh)
    pfile_xdmf.write_function(A)

with XDMFFile(MPI.COMM_WORLD, "out_WH/p.xdmf", "w") as pfile_xdmf:
    p.x.scatter_forward()
    pfile_xdmf.write_mesh(msh)
    pfile_xdmf.write_function(p)

I want to know why does the solution I calculate not even meet the Dirichlet boundary condition?
The analytical solution is:
image
image