Thick YIG film - spin-wave hybridization#
notebook used for the results published in C.Riedel, et al., Hybridization‐Induced Spin‐Wave Transmission Stop Band within a 1D Diffraction Grating, Advanced Physics Research, https://onlinelibrary.wiley.com/doi/10.1002/apxr.202200104
in this notebook we would like to show you the possibility how to obtain the resonance fields for a well-defined frequency in function of the wave vectors
[1]:
import tetrax as tx
import numpy as np
%matplotlib notebook
import matplotlib.pyplot as plt
Analytics: Kalinikos - Slavin equation#
the 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
[2]:
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
Field versus wave vector derived from the analytical Kalinikos-Slavin equation#
[3]:
def Bext_KS(k, gamma, M_s, A, T, omega, n=0, mu_0=4*np.pi*1e-7):
omega_M = gamma * mu_0 * M_s
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))
b = omega_M * (2 * Lambda**2 * k_n**2 + 1)
delta = np.sqrt(omega_M**2 + 4*omega**2 - 4*omega_M**2*P_nn*(1-P_nn))
return np.abs((-b+delta)/(2*gamma))
Numerical dispersion calculation#
we create a YIG film with 200 nm thickness and a line trace mesh along the thickness with 5 nm resolution
NOTE: the thickness direction is the y direction, propagation direction is the z direction.
[4]:
T = 200
Msat = 139.6e3
Aex = 4.0e-12
sample = tx.create_sample(geometry="layer",name="Level_crossing")
msh = tx.geometries.monolayer_line_trace(T,5)
sample.set_geom(msh)
sample.Msat = Msat
sample.Aex = Aex
Setting geometry and calculating discretized differential operators on mesh.
Done.
To have a feeling about the dispersion we compute it first for a given applied field#
[5]:
Bext = 35e-3
sample.mag = [1,0,0]
exp = tx.create_experimental_setup(sample,name="DE-geometry")
exp.Bext = [Bext,0,0]
disp = exp.eigenmodes(kmin=0e6,kmax=5e6,Nk=201,num_modes=3,num_cpus=-1,save_modes=False, fname="disp_35mT.csv")
100%|███████████████████████████| 201/201 [00:25<00:00, 7.80it/s]
and we plot it together with the analytical dispersion#
[6]:
kdp = disp["k (rad/m)"]
plt.rcParams["figure.figsize"] = (8,6.5)
plt.figure()
for i in range(2):
plt.plot(kdp*1e-6,disp[f"f{i} (GHz)"],ls="-",label=f"TetraX - mode {i}")
plt.plot(kdp*1e-6,omega(np.abs(kdp), sample.gamma, Msat, Aex, T*1e-9, Bext, kind="SW", n=i, mu_0=4*np.pi*1e-7)/(2*np.pi*1e9),ls="--",marker="",alpha=0.5,c="b",label="Analytics")
plt.xlabel("wave vector (rad/µm)")
plt.ylabel("frequency (GHz)")
plt.legend()
plt.show()
Here we define the external field values as an array and for every single field value we calculate the dispersion#
the field loop is devided in three parts, the middle region has a higher resolution for better resolving the “Level crossing” of the different spin-wave branches
PLEASE be patient, this will take a while… many dispersions to be calculated. Even TetraX can be slow… time to time. :-D
[7]:
Bext_ = 1e-3*np.concatenate((np.arange(26,30,0.5),np.arange(30,34,0.05),np.arange(34,39,0.5))) # fields in Tesla
angle = 0
f0 = []
f1 = []
f2 = []
for Bext in Bext_:
# print(Bext)
exp.Bext = [Bext*np.cos(angle*np.pi/180),0,Bext*np.sin(angle*np.pi/180)]
dispersion = exp.eigenmodes(kmin=0.8e6,kmax=2.2e6,Nk=81,num_modes=3,num_cpus=-1,save_modes=False,verbose=False)
f0.append(dispersion["f0 (GHz)"].values)
f1.append(dispersion["f1 (GHz)"].values)
f2.append(dispersion["f2 (GHz)"].values)
Interpolation/extrapolation is used to couple the frequencies with fields#
For every single field value we need to find the \(k_0\) and \(k_1\) so that \(f_0(k_0) = f_{RF} = 2.8\ \mathrm{GHz}\) and \(f_1(k_1) = f_{RF} = 2.8\ \mathrm{GHz}\), respectively. NOTE: an extrapolation will be used!
[8]:
from scipy.interpolate import interp1d
k_ = dispersion["k (rad/m)"]
f0 = np.array(f0)
f1 = np.array(f1)
f2 = np.array(f2)
k0 = []
k1 = []
k2 = []
f_RF = 2.8 # GHz
for i, Bext in enumerate(Bext_):
f0_B = f0[i]
f1_B = f1[i]
f2_B = f2[i]
k0.append(interp1d(f0_B, k_,fill_value="extrapolate")(f_RF))
k1.append(interp1d(f1_B, k_,fill_value="extrapolate")(f_RF))
k2.append(interp1d(f2_B, k_,fill_value="extrapolate")(f_RF))
k0 = np.array(k0)
k1 = np.array(k1)
k2 = np.array(k2)
Let’s plot the results together with the analytical values from the Kalinikos-Slavin derivation#
[9]:
plt.rcParams["figure.figsize"] = (8,6.5)
plt.figure()
# numerical results from the extrapolation
plt.plot(1e-6*k0,Bext_*1e3,ls="--", marker=".",c="k", label = "TetraX")
plt.plot(1e-6*k1,Bext_*1e3,ls="--",marker=".", c="k")
#plt.plot(1e-6*k2,Bext_*1e3,ls="--",marker=".", c="r")
# analytical values using the Bext_KS function defined above
plt.plot(k0*1e-6,Bext_KS(k0, sample.gamma, Msat, Aex, T*1e-9, 2*np.pi*2.8e9, 0, 4*np.pi*1e-7)*1000,ls=":",color="grey",label="KS")
plt.plot(k1*1e-6,Bext_KS(k1, sample.gamma, Msat, Aex, T*1e-9, 2*np.pi*2.8e9, 1, 4*np.pi*1e-7)*1000,ls=":",color="grey")
plt.xlim([0.8,2.2])
plt.ylim([24,38])
plt.xlabel("Wave vector (rad/µm)")
plt.ylabel("External field (mT)")
plt.legend()
[9]:
<matplotlib.legend.Legend at 0x7fc3707375e0>
One can save the obtained data as pandas dataframe#
[10]:
import pandas as pd
d = {"k0 [rad/m]": k0, "k1 [rad/m]": k1, "Bext [T]": Bext_}
B_vs_k_200nm = pd.DataFrame(data=d)
B_vs_k_200nm.to_csv("Level_crossing/B_vs_k_200nm.csv")