This page was generated from unit-2.12-periodicity/dispersion.ipynb.

Eigenvalue problem for phase shift

[WIP: Master’s thesis A. Schöfl]

Let us solve the eigenvalue problem of finding \((k,u)\) satisfying

\[\int_\Omega \frac{1}{\mu} \nabla \left( e^{i k x} u(x,y)\right) \cdot \nabla \left( e^{-i k x} v(x,y)\right) = \omega^2 \int_\Omega \epsilon \, u v\]

leads to quadratic eigenvalue problem for \(ik\):

\[\underbrace{\int_\Omega \frac{1}{\mu} \nabla u \nabla v -\omega^2 \int_\Omega \epsilon \, u v }_A + i k \underbrace{\int_\Omega \frac{1}{\mu}\left( u \partial_x v- v\partial_x u\right) }_B + (ik)^2 \underbrace{\int_\Omega \frac{-1}{\mu} u v }_C =0\]
[1]:
from ngsolve import *
from ngsolve.webgui import Draw
import math
import scipy.linalg
import numpy as np
from ngsolve.eigenvalues import SOAR
[2]:
nr_eigs = 200
omega=Parameter(5)

Periodic unit-cell

[3]:
from netgen.occ import *
from netgen.webgui import Draw as DrawGeo

a = 1
r = 0.38

rect = WorkPlane().RectangleC(a,a).Face()
circ = WorkPlane().Circle(0,0, r).Face()
# r2 = WorkPlane().Rotate(30).RectangleC(a, a/10).Face()
# circ += r2

outer = rect-circ
inner = rect*circ

outer.faces.name = "outer"
outer.faces.col=(1,1,0)

inner.faces.col=(1,0,0)
inner.faces.name="inner"
shape = Glue([outer, inner])

shape.edges.Max(X).name = "right"
shape.edges.Max(-X).name = "left"
shape.edges.Max(Y).name = "top"
shape.edges.Max(-Y).name = "bot"

shape.edges.Max(Y).Identify(shape.edges.Min(Y), "bt")
shape.edges.Max(X).Identify(shape.edges.Min(X), "lr")
[4]:
DrawGeo(shape);
[5]:
mesh = Mesh(OCCGeometry(shape, dim=2).GenerateMesh(maxh=0.1))
mesh.Curve(5)
Draw (mesh);

Setting up finite element system

[6]:
fes = Compress(Periodic(H1(mesh, complex=True, order=5)))
# fes = Periodic(H1(mesh, complex=True, order=4))

print (fes.ndof)
u,v = fes.TnT()

cf_mu = 1
cf_eps = mesh.MaterialCF({"inner":9}, default=1)

a = BilinearForm( 1/cf_mu*grad(u)*grad(v)*dx-cf_eps*omega**2*u*v*dx )
b = BilinearForm( 1/cf_mu*(u*grad(v)[0]-grad(u)[0]*v)*dx )
c = BilinearForm( -1/cf_mu*u*v*dx )
a1 = BilinearForm( 1/cf_mu*grad(u)*grad(v)*dx )
a2 = BilinearForm( cf_eps*u*v*dx )
# a = a1 - omega**2 * a2


a.Assemble()
b.Assemble()
c.Assemble()
a1.Assemble()
a2.Assemble();
2525
[7]:
inv = a.mat.Inverse(freedofs=fes.FreeDofs(), inverse="sparsecholesky")
M1 = -inv@b.mat
M2 = -inv@c.mat
Q = SOAR(M1,M2, nr_eigs)
[8]:
len(Q)
[8]:
200

Small QEP

\[(A_m + \lambda B_m + \lambda^2 C_m) x = 0\]
[9]:
def SolveProjectedSmall (Am, Bm, Cm):
    half = Am.h
    Cmi = Cm.I
    K = Matrix(2*half, 2*half, complex = fes.is_complex)

    K[:half, :half] = -Cmi*Bm
    K[:half, half:] = -Cmi*Am
    K[half:, :half] = Matrix(half, complex=True).Identity()
    K[half:, half:] = 0

    Kr = Matrix(2*half)
    Kr.A = K.A.real
    lam, eig = scipy.linalg.eig(Kr)
    vecs = Matrix(eig)[0:len(Q),:]
    lam = Vector(lam)

    return lam, vecs

def SolveProjected(mata, matb, matc, Q):
    Am = InnerProduct(Q, mata*Q, conjugate = True)
    Bm = InnerProduct(Q, matb*Q, conjugate = True)
    Cm = InnerProduct(Q, matc*Q, conjugate = True)
    return SolveProjectedSmall (Am, Bm, Cm)
[10]:
lams, vecs = SolveProjected(a.mat, b.mat, c.mat, Q)
Z = (Q * vecs).Evaluate()
for vec in Z: vec /= Norm(vec)
[11]:
gfu = GridFunction(fes)
ind = np.absolute(np.asarray(lams)-6j).argmin()
gfu.vec.data = Z[ind]
Draw (gfu, animate_complex=True, deformation=True, scale=2);
[12]:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.set_xlim(-20,20)
ax.set_ylim(-20,20)
plt.plot([l.real for l in lams],[l.imag for l in lams],'x')
plt.show()
../../_images/i-tutorials_unit-2.12-periodicity_dispersion_15_0.png

Experiments with reduced basis:

[13]:
# help (Q)
Qred = MultiVector(Q[0], 0)

for fs in np.linspace(0,0.8,5):
    omega.Set(2*math.pi*fs)
    print ("fs =", fs)
    a.Assemble()

    inv = a.mat.Inverse(freedofs=fes.FreeDofs(), inverse="sparsecholesky")
    M1 = -inv@b.mat
    M2 = -inv@c.mat
    Q = SOAR(M1,M2, nr_eigs)
    lams, vecs = SolveProjected(a.mat, b.mat, c.mat, Q)
    Z = (Q * vecs).Evaluate()
    for vec in Z: vec /= Norm(vec)

    for lam, vec in zip(lams, Z):
      if abs(lam) < 6:
        if lam.imag > 1e-8:
            hvr = vec.CreateVector()
            hvi = vec.CreateVector()
            for i in range(len(vec)):
                hvr[i] = vec[i].real
                hvi[i] = vec[i].imag
            Qred.Append (hvr)
            Qred.Append (hvi)
        if abs(lam.imag) < 1e-8:
            hvr = vec.CreateVector()
            for i in range(len(vec)):
                hvr[i] = vec[i].real
            Qred.Append (hvr)

Qred.Orthogonalize()
lamsred, vecsred = SolveProjected(a.mat, b.mat, c.mat, Qred)

print ("dim Qred=", len(Qred))
fs = 0.0
fs = 0.2
fs = 0.4
fs = 0.6000000000000001
fs = 0.8
dim Qred= 40
[14]:
fs = []
ks =[]
ksi =[]

A1m = InnerProduct(Qred, a1.mat*Qred, conjugate = True)
A2m = InnerProduct(Qred, a2.mat*Qred, conjugate = True)
Bm = InnerProduct(Qred, b.mat*Qred, conjugate = True)
Cm = InnerProduct(Qred, c.mat*Qred, conjugate = True)

for fi in np.linspace(0, 0.7, 1000):
    # print ("fi =", fi)
    omega.Set(2*math.pi*fi)
    Am = A1m - omega.Get()**2 * A2m
    lamsred, vecsred = SolveProjectedSmall(Am, Bm, Cm)

    for lamred in lamsred:
        if abs(lamred.real) < 2 and lamred.imag >= 0 and lamred.imag < 6.29:
            fs.append(fi)
            ks.append(lamred.imag)
            ksi.append(lamred.real)

Agrees very well with Scheiber et al, path \(\Gamma-X\):

[15]:
fig, ax = plt.subplots()
ax.set_xlim(0,6.28)
ax.set_ylim(0,0.7)
plt.plot(ks, fs, "*", ms=2)
plt.show()
../../_images/i-tutorials_unit-2.12-periodicity_dispersion_20_0.png
[16]:
fig, ax = plt.subplots()
ax.set_xlim(0,2)
ax.set_ylim(0,0.7)
plt.plot(ksi, fs, "*", ms=2)
plt.show()
../../_images/i-tutorials_unit-2.12-periodicity_dispersion_21_0.png
[17]:
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection='3d')
ax.set_zlim(0,0.7)
plt.plot(ks, ksi, fs, "*", ms=1)
plt.show()
../../_images/i-tutorials_unit-2.12-periodicity_dispersion_23_0.png
[ ]:

[ ]: