7. JKM mixed finite elements#
J. Gopalakrishnan, J. Guzman, J.J. Lee: “The Johnson-Krizek-Mercier elasticity element in any dimensions”
A stress finite element on Alfeld split. Stress space is symmetric and row-wise in \(H(\operatorname{div})\)
NGSolve implementation:
JKM space is \(nn\)-continuous
\(nt\) continuity by variational formulation
from ngsolve import *
from ngsolve.webgui import Draw
mesh = Mesh(unit_square.GenerateMesh(maxh=0.1))
Draw (mesh);
7.1. JKM - TDNNS#
Find \(\sigma \in JKM\), \(v \in {\mathcal N} \subset H(\operatorname{curl})\)
\[\begin{split}
\begin{array}{ccccll}
(\sigma, \tau) & + & \left< \operatorname{div} \tau, u \right> & = & 0 & \forall \, \tau \\
\left< \operatorname{div} \sigma, v \right> & & & = & f(v) & \forall \, v
\end{array}
\end{split}\]
fesu = HCurl(mesh, order=2, orderinner=3, type1=True, dirichlet=".*")
fesS = JKM_FESpace(mesh, order=1)
fes = fesu*fesS
(u,sigma), (v, tau) = fes.TnT()
n = specialcf.normal(2)
def tang(v): return v - (n*v)*n
irs = fesS.GetIntegrationRules(bonus_intorder=4)
def Div(sigma,v):
return div(sigma)*v*dx(intrules=irs) - tang(v)*(sigma*n)*dx(element_boundary=True)
def Strain(stress): return Deviator(stress) + 1e-4/2 * Trace(stress) * Id(2)
def Stress(strain): return Deviator(strain) + 1e4/2 * Trace(strain) * Id(2)
WARNING: kwarg 'orderinner' is an undocumented flags option for class <class 'ngsolve.comp.HCurl'>, maybe there is a typo?
rhs = CF( (y-1/2,0) )
term = InnerProduct(Strain(sigma),tau)*dx(intrules=irs) + Div(tau,u) + Div(sigma,v)
a = BilinearForm(term).Assemble()
f = LinearForm(-v*rhs*dx).Assemble()
gfu = GridFunction(fes)
gfu.vec.data = a.mat.Inverse(freedofs=fes.FreeDofs()) * f.vec
Draw (gfu.components[0], mesh)
Draw (gfu.components[1][0,0], mesh)
gfsigma1 = gfu.components[1]
7.2. The original method#
fesu = VectorL2(mesh, order=1)
fesuf = TangentialFacetFESpace(mesh, order=1, dirichlet=".*")
fesS = JKM_FESpace(mesh, order=1)
fes = fesu*fesuf*fesS
(u,uf, sigma), (v, vf, tau) = fes.TnT()
n = specialcf.normal(2)
irs = fesS.GetIntegrationRules()
def Div(sigma,v,vf):
return div(sigma)*v*dx(intrules=irs) - tang(vf)*(sigma*n)*dx(element_boundary=True)
term = InnerProduct(Strain(sigma),tau)*dx(intrules=irs) + Div(tau,u,uf) + Div(sigma,v,vf)
a = BilinearForm(term).Assemble()
f = LinearForm(-v*rhs*dx).Assemble()
gfu = GridFunction(fes)
gfu.vec.data = a.mat.Inverse(freedofs=fes.FreeDofs()) * f.vec
Draw (gfu.components[0], mesh)
Draw (gfu.components[2][0,0], mesh)
gfsigma2 = gfu.components[2]
diff = gfsigma1-gfsigma2
Draw (diff[0,0], mesh)
WebGLScene
7.3. TDNNS#
fesu = HCurl(mesh, order=2, type1=True, dirichlet=".*")
fesS = HDivDiv(mesh, order=1, plus=True)
fes = fesu*fesS
(u,sigma), (v, tau) = fes.TnT()
n = specialcf.normal(2)
def tang(v): return v - (n*v)*n
def Div(sigma,v):
return div(sigma)*v*dx - tang(v)*(sigma*n)*dx(element_boundary=True)
term = InnerProduct(Strain(sigma),tau)*dx(intrules=irs) + Div(tau,u) + Div(sigma,v)
a = BilinearForm(term).Assemble()
f = LinearForm(-v*rhs*dx).Assemble()
gfu = GridFunction(fes)
gfu.vec.data = a.mat.Inverse(freedofs=fes.FreeDofs()) * f.vec
Draw (gfu.components[0], mesh)
Draw (gfu.components[1][0,0], mesh)
gfsigma3 = gfu.components[1]
7.4. The primal method:#
fesprimal = VectorH1(mesh, order=8, dirichlet=".*")
u,v = fesprimal.TnT()
aprimal = BilinearForm(InnerProduct(Stress(Sym(Grad(u))), Sym(Grad(v)))*dx).Assemble()
fprimal = LinearForm(v*rhs*dx).Assemble()
gfprimal = GridFunction(fesprimal)
gfprimal.vec.data = aprimal.mat.Inverse(freedofs=fesprimal.FreeDofs()) * fprimal.vec
Draw (gfprimal, mesh)
Draw (Stress(Sym(Grad(gfprimal)))[0,0], mesh)
WebGLScene
print ("err JKM-tdnns = ", Integrate ( ( Norm(gfsigma1-Stress(Sym(Grad(gfprimal))))**2), mesh))
print ("err mixed = ", Integrate ( ( Norm(gfsigma2-Stress(Sym(Grad(gfprimal))))**2), mesh))
print ("err tdnns = ", Integrate ( ( Norm(gfsigma3-Stress(Sym(Grad(gfprimal))))**2), mesh))
err JKM-tdnns = 2.2243517407269708e-06
err mixed = 2.3585620579637012e-06
err tdnns = 1.1725698768853879e-06