Library introduction¶
This notebook is a walkthorugh the basic concepts in the PyMTJ library and covers the basics of using the C++ bindings in Python.
Voltage Spin Diode & IEC¶
In the first section, we will run the basic voltage spin diode experiment using the IEC (Interlayer Exchange Coupling) excitation.
STT¶
In the second section, we are going to construct the Spin Torque Oscillator (STO) using the STT (Slonczewski Spin Torque) addition to our LLG equations. STO is able to sustain the magnetisation precession for a prolonged period of time, since the spin-polarised current flowing through the junction compensates the energy losses from the Gilbert's damping contribution.
from cmtj import Junction, Layer, CVector, Axis, ScalarDriver, AxialDriver, NullDriver
import numpy as np
import pandas as pd
import time
We start with the definition of our Junction, which in turn is composed from two layers. Here are some key parameters:
- mag - magnetisation vector
- anis - the vector (axis) of the magnetic anisotoropy
- K - magnetic anisotropy $[J/m^3]$
- Ms - magnetisation saturation $[A/m]$
- thickness - thickness of the layer $[m]$
- J - the IEC coupling with the other layers $[J/m^2]$
N = [CVector(0, 0, 0), CVector(0, 0, 0), CVector(0, 0, 1)]
damping = 0.01
cl1 = Layer(id="free",
mag=CVector(0, 0, 1),
anis=CVector(0, -0.0871557, 0.996195),
Ms=1.07,
thickness=1e-9,
cellSurface=0,
temperature=0,
dipoleTensor=[CVector(0, 0, 0) for i in range(3)],
demagTensor=N,
damping=damping)
cl2 = Layer(id="bottom",
mag=CVector(0, 0, 1),
anis=CVector(0.34071865, -0.08715574, 0.936116),
Ms=1.07,
thickness=1e-9,
cellSurface=0,
temperature=0,
dipoleTensor=[CVector(0, 0, 0) for i in range(3)],
demagTensor=N,
damping=damping)
Voltage Spin Diode effect¶
As we have defined the Junction, we now proceed to running the Voltage Spin Diode experiment.
To perform the VSD the external magnetic field is swept in some range, in this case we picked 30 points in range from 0 to 600 mT. For each field point we solve the LLG under the specific excitation and frequency.
In this example we will be exciting the system with its resonant frequency which is given a priori but you may find it with the FFT analysis included in the PyMTJ package as well. We skipped that part for clarity. For each of the points we will calculate the constant voltage (Vmix, the diode voltage) arising from the interaction between the sinusoidal current flowing throught the MTJ (of 0 phase and frequency equal to that of the resonant frequency) and also oscillating magnetoresistance, which in turn is invoked by the oscillating magnetisation in response to the effective field contributions.
Other parameters were fixed so that we get a nice result, but feel free to tinker with the options and see what happens.
from collections import defaultdict
data = defaultdict(list)
start = time.time()
TtoAm = 795774.715459 #
def compute_vsd(simulation_log, frequency, offset=0, tstart=1e-9):
stime = np.asarray(simulation_log['time'])
indx = np.argwhere(stime >= tstart).ravel()
Rx = np.asarray(simulation_log['Rx'])[indx]
avg_res = np.mean(Rx)
current = np.sqrt(
10e-6 / avg_res) * np.sin(2 * np.pi * frequency * stime[indx] + offset)
return np.mean(current * Rx)
start = time.time()
junction = Junction(layers=[cl1, cl2],
Rx0=[100, 100],
Ry0=[0, 0],
AMR_X=[10, 10],
AMR_Y=[30 / 2, 30 / 2],
SMR_X=[0, 0],
SMR_Y=[0, 0],
AHE=[0, 0],
filename="")
junction.setLayerAnisotropyDriver("free",
ScalarDriver.getConstantDriver((305e3)))
junction.setLayerAnisotropyDriver("bottom",
ScalarDriver.getConstantDriver((728e3)))
junction.setIECDriver("free", "bottom",
ScalarDriver.getConstantDriver(4e-5))
HoeAmpl = 5e2 # A/m
Hspace = np.linspace(-800e3, 800e3, 100)
frequencies = [1e9 * i for i in range(48)]
mag_free = CVector(1, 1, 0)
mag_bottom = CVector(1, 1, 0)
VSD = []
for frequency in frequencies:
H_sweep = []
for H in Hspace:
junction.clearLog()
junction.setLayerMagnetisation("free", mag_free)
junction.setLayerMagnetisation("bottom", mag_bottom)
HDriver = AxialDriver(
ScalarDriver.getConstantDriver(H * np.sqrt(2) / 2),
ScalarDriver.getConstantDriver(H * np.sqrt(2) / 2),
NullDriver())
HoeDriver = AxialDriver(
NullDriver(),
ScalarDriver.getSineDriver(0, HoeAmpl, frequency, 0),
NullDriver())
junction.setLayerExternalFieldDriver("all", HDriver)
junction.setLayerOerstedFieldDriver("all", HoeDriver)
junction.runSimulation(4e-9, 4e-12, 4e-12)
mag_free = junction.getLayerMagnetisation("free")
mag_bottom = junction.getLayerMagnetisation("bottom")
vmix = compute_vsd(junction.getLog(), frequency=frequency)
H_sweep.append(vmix)
VSD.append(H_sweep)
end = time.time()
print(f"Simulation time: {end-start:.2f}")
Simulation time: 5.55
Now, we may plot our Voltage Spin Diode curve, which is a ferromagnetic resonance curve -- the FMR (fitting the antisymmetric Lorentz curve), being a Vmix function of the field applied to the junction.
from mpl_toolkits.axes_grid1 import make_axes_locatable
import matplotlib.pyplot as plt
plt.style.use('default')
fsize= 32
VSD = np.asarray(VSD, dtype=np.float32)
plt.rcParams['figure.figsize'] = [10, 6]
plt.rc('font', family='serif', serif='Times', size=18)
plt.rc('text', usetex=False)
plt.rc('xtick', labelsize=fsize)
plt.rc('ytick', labelsize=fsize)
plt.rc('axes', labelsize=fsize)
plt.rc('mathtext',**{'default':'regular'})
with plt.style.context(['science', 'no-latex']):
fig, ax = plt.subplots(figsize=(10, 10))
im = ax.imshow(VSD, origin='lower')
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
fig.colorbar(im, cax=cax, orientation='vertical')
ax.set_xlabel("Field [A/m]")
ax.set_ylabel("Vmix [mV]")
ax.set_title("FMR curve for the Voltage Spin Diode Effect")
plt.show()
STT¶
We may now transition to simulating Spin Torque Oscillators. As mentioned before, these are called the oscillators because they can sustain the oscillating magnetisation.
This experiment is much simpler than the previous one. Like before, we will set up our layers, but this time, we will enable the STT contributions and we will invoke the critical current densities.
Note, we turn the STT in the free layer, which has now the magnetisation in the -z direction. Additionally, we lower the free layer anisotropy and we turn off the coupling (for simplicity).
We also slightly tilt the bottom layer's magnetisation in order to invoke the torque on the spin torque.
from mpl_toolkits.mplot3d import Axes3D
def plot_trajectories(df, title):
with plt.style.context(['science', 'no-latex']):
fig = plt.figure(figsize=(14, 6))
ax = fig.add_subplot(1, 2, 1, projection='3d')
ax.set_title("Free layer magnetisation trajectory")
ax.plot3D(df[f'free_mx'],
df[f'free_my'],
df[f'free_mz'])
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax2 = fig.add_subplot(1, 2, 2)
ax2.plot(df['time'], df['R_free_bottom'])
ax2.set_xlabel("Time [s]")
ax2.set_ylabel("Magnetoresistance [Ohm]")
fig.suptitle(title)
fig.tight_layout()
demagTensor = [
CVector(0., 0., 0.),
CVector(0., 0., 0.),
CVector(0., 0., 1.0)
]
dipoleTensor = [
CVector(5.57049776248663e-4, 0., 0.),
CVector(0., 0.00125355500286346, 0.),
CVector(0., 0.0, -0.00181060482770131)
]
damping = 0.03
currentDensity = 1e10
beta = 1
spinPolarisation = 1.0
l1 = Layer.createSTTLayer(id="free",
mag=CVector(0., 0., -1.),
anis=CVector(0, 0., 1.),
Ms=1.2,
thickness=1.4e-9,
cellSurface=7e-10 * 7e-10,
demagTensor=demagTensor,
dipoleTensor=dipoleTensor,
damping=damping,
SlonczewskiSpacerLayerParameter=1.0,
spinPolarisation=spinPolarisation,
beta=beta)
l2 = Layer(id="bottom",
mag=CVector(0., 1., 1.),
anis=CVector(0, 1., 1.),
Ms=1.2,
thickness=3e-9,
cellSurface=7e-10 * 7e-10,
demagTensor=demagTensor,
dipoleTensor=dipoleTensor)
l1.setReferenceLayer(CVector(0, 1., 1.))
junction = Junction([l1, l2], "STT.csv", 100, 200)
junction.setLayerAnisotropyDriver("free", ScalarDriver.getConstantDriver(800e3))
junction.setLayerAnisotropyDriver("bottom", ScalarDriver.getConstantDriver(1500e3))
junction.setIECDriver("free", "bottom", ScalarDriver.getConstantDriver(-2.5e-6))
# current driver
junction.setLayerCurrentDriver("free", ScalarDriver.getConstantDriver(currentDensity))
junction.runSimulation(2e-9, 1e-12, 1e-12, True, False)
df = pd.read_csv('STT.csv', sep=';')
plot_trajectories(df, title="STO with STT on")
We will also run the same simulation but with the STT turned off, to see if the spin polarised current flow actually helps to sustain the oscillations.
l1 = Layer(id="free",
mag=CVector(0., 0., -1.),
anis=CVector(0, 0., 1.),
Ms=1.2,
thickness=1.4e-9,
cellSurface=7e-10 * 7e-10,
demagTensor=demagTensor,
dipoleTensor=dipoleTensor)
l2 = Layer(id="bottom",
mag=CVector(0., 1., 1.),
anis=CVector(0, 1., 1.),
Ms=1.2,
thickness=3e-9,
cellSurface=7e-10 * 7e-10,
demagTensor=demagTensor,
dipoleTensor=dipoleTensor)
junction = Junction([l1, l2], "noSTT.csv", 100, 200)
junction.setLayerAnisotropyDriver("free", ScalarDriver.getConstantDriver(800e3))
junction.setLayerAnisotropyDriver("bottom", ScalarDriver.getConstantDriver(1500e3))
junction.runSimulation(1e-9, 1e-12, 1e-12, True, False)
df = pd.read_csv('noSTT.csv', sep=';')
plot_trajectories(df, title="STT turned off")