12.7. Elastodynamics#

Idea: Discretize velocity by push-forward from deformation

12.7.1. Scalar wave equation:#

\[ \ddot u = \Delta u \]

reduction to first order in time:

\[\begin{eqnarray*} \dot u & = & v \\ \dot v & = & -\Delta u \end{eqnarray*}\]

Solution space (Bochner spaces):

\[ u \in L_2([0,T], H^1(\Omega)), \qquad v \in L_2([0,T], L_2(\Omega)) \]

Do our discretizations respect these spaces?

12.7.2. Elastic wave equation:#

\[ u \in L_2([0,T], H^1(\Omega)^d), \qquad v \in L_2([0,T], H(\operatorname{curl})) \]

See Michi Neunteufel’s master thesis, Chapter 6.4

from ngsolve import *
from netgen.occ import *
from ngsolve.webgui import Draw
geo = WorkPlane().RectangleC(10,1).Face()
mesh = geo.GenerateMesh(maxh=0.4, dim=2)

tend = 10
taumax = 0.2
tau = Parameter(taumax)

order = 1
dirichlet = "xxx"

Vu = VectorH1(mesh, order=order, dirichlet=dirichlet)
Vv = Discontinuous(HCurl(mesh, order=order, dirichlet=dirichlet)) 
Vp = Discontinuous(HCurl(mesh, order=order, dirichlet=dirichlet))
V = Vu*Vv*Vp

u, v, p = V.TrialFunction()
ut, vt, pt = V.TestFunction()

p = p.Operator("dual")
pt = pt.Operator("dual")

gfu = GridFunction(V)
gfuold = GridFunction(V)

uold = gfuold.components[0]
vold = gfuold.components[1]
pold = gfuold.components[2].Operator("dual")

um = 0.5*(u+uold)
vm = 0.5*(v+vold)
# vm = v
pm = 0.5*(p+pold)

# Id = CoefficientFunction( (1,0,0,1), dims=(2,2) )
F = Grad(u) + Id(2)

Fold = Grad(uold) + Id(2)
Ft = grad(ut)
# Fm = 0.5 * (F+Fold)
Fm = 2*Inv (Inv(F)+Inv(Fold))
# Cm = 0.5 * (F.trans*F + Fold.trans*Fold)
C = F.trans*F
Cold = Fold.trans*Fold
Cm = 0.5*(C+Cold)
def Rot(u): return CF( ( u[1], -u[0]) )
E = 0.5 * (F.trans * F - Id(2))
def Pow(a, b): return a**b 
truecompile = True
nonlinear_material = True

E, nu = 2e2, 0.2
mu  = E / 2 / (1+nu)
lam = E * nu / ((1+nu)*(1-2*nu))

def NeoHook (C):
    if nonlinear_material:
        return 0.5 * mu * (Trace(C-Id(2)) + 2*mu/lam * (Pow(Det(C), -lam/2/mu) - 1))
    else:
        return 0.5 * mu * InnerProduct(C-Id(2),C-Id(2))

def Stress (C):
    if nonlinear_material:    
        return 0.5 * mu * (Id(2) - Pow(Det(C), -lam/2/mu) * Inv(C))
    else:
        return mu * (C-Id(2))

n = specialcf.normal(2)
def Ptau (v):
    return v - (v*n)*n
    

a = BilinearForm(V, condense=True)
a += (1/tau*Fm.trans*(u-uold)-vm) * pt * dx(element_boundary=False)
a += (1/tau*Fm.trans*(u-uold)-vm) * pt * dx(element_boundary=True)

a += (p-pold)*vt * dx(element_boundary=False)
a += (p-pold)*vt * dx(element_boundary=True)

a += Inv(Cm)*(-v+vold)*vt*dx
a += -0.5 * (Inv(Cm)*(Cold-C)*Inv(Cm)*vm)*vt *dx 
a +=  (((tau*0.5/Det(Cm)*(0.5*curl(v)+0.5*curl(vold)))*Rot(vm))) * vt*dx 
    
a +=  (1/tau * (Fm*(p-pold)) * ut)*dx(element_boundary=True)
a +=  (1/tau * (Fm*(p-pold)) * ut)*dx(element_boundary=False)

a += 2*InnerProduct(Stress(Cm), Fm.trans*Ft)*dx
# a += SymbolicBFI ( 5 * ut[1] )



akin = BilinearForm(V)
akin += Variation ((0.5*(Inv(F.trans)*v) * (Inv(F.trans)*v)) * dx)

apot = BilinearForm(V)
apot += Variation ( NeoHook(C)*dx )
# apot += Variation ( 5*u[1]*dx)
u = gfu.components[0]
F = grad(gfu.components[0]).trans + Id(2)

print ("Velocity y-component:")
scene_velhat = Draw (gfu.components[1][1], mesh, "vel-hat", deformation=u)
Velocity y-component:
vold.Set( CF( (-y,x) ))

res = gfu.vec.CreateVector()
w = gfu.vec.CreateVector()

t = 0
gfu.vec.data = gfuold.vec

errE = []
from time import sleep
with TaskManager():
  while t < tend+1e-8:
    # print (t)

    newton_conv = True
    lasterr = 1
    for step in range(15):
        a.AssembleLinearization(gfu.vec)
        a.Apply (gfu.vec, res)
        inv = a.mat.Inverse(V.FreeDofs(True), inverse="umfpack")

        res.data += a.harmonic_extension_trans * res
        w.data = inv * res
        w.data += a.harmonic_extension * w
        w.data += a.inner_solve * res

        err = Norm(w)
        # print ("|w| = ", err)
        if lasterr < 1e-8: break
        lasterr = err
        
        gfu.vec.data -= w
    else:
        newton_conv = False
        print ("Newton did not converge")

    # print ("step =", step, "tau =", tau.Get())
    if newton_conv:
        t = t + tau.Get()
        gfuold.vec.data = gfu.vec
    else:
        tau.Set(0.5*tau.Get())
        gfu.vec.data = gfuold.vec

    if step <= 8:
        tau.Set(1.1*tau.Get())        
    if step >= 10:
        tau.Set(0.8*tau.Get())
    if tau.Get()  > taumax:
        tau.Set(taumax)

    tau.Set(taumax)
    Redraw(blocking=True)
    scene_velhat.Redraw()

    epot = apot.Energy (gfu.vec)
    ekin = akin.Energy (gfu.vec)
    # print ("pot =", epot, ", ekin =", ekin, ", sum = ", epot+ekin)
    errE.append (epot+ekin)

    sleep(0.1)
from matplotlib.pyplot import plot
plot (errE)
[<matplotlib.lines.Line2D at 0x114259400>]
../_images/9b8f03d5f1c0f7ab0ef57e125993d05c7c8ed6faac3a29854c78bd534140a7c7.png