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