Thin film dispersion exact vs perturbation (Kalinikos & Slavin)#
Here, we calculate the dispersion of a 50 nm thick Permalloy film in the Damon-Eshbach geometry and compare it to the zeroth-order perturbation theory of Kalinikos and Slavin in Ref 1 and to the finite-difference dynamic-matrix code SWIIM from Ref. 2. This example is based on Ref. 3.
We begin by setting up the sample with an applied transversal field of 20 mT.
[1]:
import tetrax as tx
T = 50
sample = tx.Sample(tx.geometries.layer.monolayer(thickness=T,cell_size=1))
sample.material["Msat"] = 800e3
sample.material["Aex"] = 11e-12
sample.material["gamma"] = 1.822123739e11
sample.mag = (1,0,0)
Bext = 20e-3
sample.external_field = (Bext,0,0)
Dispersion calculation with TetraX#
We calculate the dispersion using the eigenmodes function.
[2]:
spectrum_tx = tx.experiments.eigenmodes(sample, num_modes=3, num_cpus=-1, num_k=201)
100%|█████████████████| 101/101 [00:02<00:00, 34.87it/s]
We can plot the dispersion in the usual way. The different modes correspond to different perpendicular-standing waves, while the lowest-mode (the surface mode) is hybridized with the first-order mode.
[3]:
spectrum_tx.plot(fscale="G", kscale="u", renderer="notebook") # remove the renderer argument when running on your computer
Comparison with Kalinikos-Slavin theory and finite-difference method#
Let us know compare these results with the well-known perturbation theory of Kalinikos and Slavin presented in Ref. 1. The following function omega computes the dispersion for both backward volume (BVM) and Daemon-Eshbach (SW) geometry by giving the proper keyword as an input to the function at call.
[4]:
import numpy as np
def omega(k, gamma, M_s, A, T, B_0=0, kind="BVW", n=0, mu_0=4*np.pi*1e-7):
omega_M = gamma * mu_0 * M_s
omega_0 = gamma * B_0
Lambda = np.sqrt(2*A / (mu_0 *M_s**2))
k_n = np.sqrt(k**2 + (n*np.pi/T)**2)
rat = k**2 / k_n**2
kron_n = (n == 0)
P_nn = rat*(1 - (2/(1+kron_n))*rat*(1-(-1)**n *np.exp(-k*T))/(k*T))
if kind == "BVW":
return np.sqrt(
(omega_0 + omega_M* Lambda**2 * k_n**2) *\
(omega_0 + omega_M* Lambda**2 *k_n**2 + omega_M*(1-P_nn))
)
if kind == "SW":
return np.sqrt(
(omega_0 + omega_M * Lambda**2 *k_n**2 + omega_M*P_nn) *\
(omega_0 + omega_M* Lambda**2 *k_n**2 + omega_M*(1-P_nn))
)
else:
print("No valid spin wave kind specified in dispersion. "
"Please use BVW or SW.")
return k*0
We will see below that this zeroth-order approach does not work well for a 50 nm permalloy film. Therefore, we will also compare the TetraX results with another exact code, the SWIIM program, presented in Ref. 2. The raw data is found in Ref. 4.
[5]:
data_SWIIM = tx.fetch_reference_data("example_dispersion_SWIIM_DE_50nm_Py_monolayer_20mT")
To plot theory and numerics together, we use the Plotly package.
[6]:
from plotly import graph_objects as go
fig = go.Figure()
k_ = np.linspace(-40e6, 40e6, 200)
colors = ["#000E86","#C81C89", "#FFB63F"]
for i, c in zip(range(3), colors):
fig.add_trace(
go.Scatter(
x=k_*1e-6,
y=omega(
np.abs(k_),
sample.material["gamma"].average,
sample.material["Msat"].average,
sample.material["Aex"].average,
T*1e-9,
Bext,
kind="SW",
n=i, mu_0=4*np.pi*1e-7
)/(2*np.pi*1e9),
mode="lines",
name=f"f{i}",
line = dict(color=c, width=1, dash='dash'),
legendgroup="0",
legendgrouptitle_text="Theory Ref. 1",
)
)
fig.add_trace(
go.Scatter(
x=spectrum_tx.k*1e-6,
y=spectrum_tx.frequencies(n=i)*1e-9,
mode="lines",
name=f"f{i}",
legendgroup="1",
legendgrouptitle_text="TetraX",
line = dict(color=c),
)
)
fig.add_trace(
go.Scatter(
x=data_SWIIM["k (rad/m)"]*1e-6,
y=data_SWIIM[f"f{i} (Hz)"]*1e-9,
mode="markers",
name=f"f{i}",
legendgroup="2",
legendgrouptitle_text="SWIIM",
line = dict(color=c),
marker = dict(symbol="x",size=4),
)
)
fig.update_xaxes(title_text="k (rad/µm)", exponentformat="power")
fig.update_yaxes(title_text="f (GHz)", exponentformat="power")
fig.update_layout(
template="simple_white",
height=600,
width=600,
hoverlabel={"bordercolor": "rgba(255,255,255,1)"},
)
fig.show(renderer="notebook") # remove the renderer argument when running on your computer