Tutorial 03: Uniaxial anisotropy energy term#

Uniaxial anisotropy energy density is computed as

\[w_\text{ua} = -K (\mathbf{m}\cdot\mathbf{u})^{2}\]

where \(\mathbf{m}\) is the normalised (\(|\mathbf{m}|=1\)) magnetisation, \(K\) is the uniaxial anisotropy constant, and \(\mathbf{u}\) is the anisotropy axis. Uniaxial anisotropy energy term tends to align all magnetic moments parallel or antiparallel to the anisotropy axis.

In oommfc, \(\mathbf{m}\) is a part of the magnetisation field system.m. Therefore, only uniaxial anisotropy constant \(K\) and axis \(\mathbf{u}\) should be provided as input parameters to uniquely define the energy term. Both \(K\) and \(\mathbf{u}\) can be constant in space or spatially varying.

Spatially constant \(K\) and \(\mathbf{u}\)#

Let us start by assembling a simple simple simulation where neither \(K\) nor \(\mathbf{u}\) vary in space. The sample is a “one-dimensional” chain of magnetic moments.

[1]:
import discretisedfield as df
import micromagneticmodel as mm

import oommfc as oc

p1 = (-10e-9, 0, 0)
p2 = (10e-9, 1e-9, 1e-9)
cell = (1e-9, 1e-9, 1e-9)
region = df.Region(p1=p1, p2=p2)
mesh = df.Mesh(region=region, cell=cell)

The mesh is

[2]:
mesh.k3d()

The system has a Hamiltonian, which consists of only uniaxial anisotropy energy term.

[3]:
K = 1e5  # uniaxial anisotropy constant (J/m**3)
u = (0, 0, 1)  # uniaxial anisotropy axis
system = mm.System(name="uniaxialanisotropy_constant_K_u")
system.energy = mm.UniaxialAnisotropy(K=K, u=u)

We are going to minimise the system’s energy using oommfc.MinDriver later. Therefore, we do not have to define the system’s dynamics equation. Finally, we need to define the system’s magnetisation (system.m). We are going to make it random with \(M_\text{s}=8\times10^{5} \,\text{Am}^{-1}\)

[4]:
import random

import discretisedfield as df

Ms = 8e5  # saturation magnetisation (A/m)


def m_fun(pos):
    return [2 * random.random() - 1 for i in range(3)]


system.m = df.Field(mesh, nvdim=3, value=m_fun, norm=Ms)

The magnetisation, we set is

[5]:
system.m.k3d.vector(color_field=system.m.z)

Now, we can minimise the system’s energy by using oommfc.MinDriver.

[6]:
md = oc.MinDriver()
md.drive(system)
Running OOMMF (ExeOOMMFRunner)[2023/10/18 12:40]... (0.4 s)

We expect that now all magnetic moments are aligned parallel or antiparallel to the anisotropy axis (in the \(z\)-direction).

[7]:
system.m.k3d.vector(color_field=system.m.z)

Spatially varying \(\mathbf{K}\)#

There are two different ways how a parameter can be made spatially varying, by using: 1. Dictionary 2. discretisedfield.Field

Dictionary#

In order to define a parameter using a dictionary, regions must be defined in the mesh. Regions are defined as a dictionary, whose keys are the strings and values are discretisedfield.Region objects, which take two corner points of the region as input parameters.

[8]:
p1 = (-10e-9, 0, 0)
p2 = (10e-9, 1e-9, 1e-9)
cell = (1e-9, 1e-9, 1e-9)
subregions = {
    "region1": df.Region(p1=(-10e-9, 0, 0), p2=(0, 1e-9, 1e-9)),
    "region2": df.Region(p1=(0, 0, 0), p2=(10e-9, 1e-9, 1e-9)),
}
region = df.Region(p1=p1, p2=p2)
mesh = df.Mesh(region=region, cell=cell, subregions=subregions)
[9]:
mesh.k3d.subregions()

Let us say that there is no uniaxial anisotropy (\(K=0\)) in region 1, whereas in region 2 it is \(K=10^{5} \,\text{Jm}^{-3}\). \(\mathbf{u}\) is in the \(z\)-direction. K is now defined as a dictionary:

[10]:
K = {"region1": 0, "region2": 1e5}

The system object is

[11]:
system = mm.System(name="uniaxialanisotropy_dict_K")
system.energy = mm.UniaxialAnisotropy(K=K, u=u)
system.m = df.Field(mesh, nvdim=3, value=m_fun, norm=Ms)

Its magnetisation is

[12]:
system.m.k3d.vector(color_field=system.m.z)

After we minimise the energy

[13]:
md.drive(system)
Running OOMMF (ExeOOMMFRunner)[2023/10/18 12:40]... (0.2 s)

The magnetisation is as we expected.

[14]:
system.m.k3d.vector(color_field=system.m.z)

discretisedfield.Field#

Let us define the spatailly varying uniaxial anisotropy, so that

$:nbsphinx-math:mathbf{u}`(x, y, z) = :nbsphinx-math:left`{

\right. $

The value of u for the spatially varying anisotropy is set using a Python function.

[15]:
def u_fun(pos):
    x, y, z = pos
    if x <= 0:
        return (0, 0, 1)
    else:
        return (1, 0, 0)

The uniaxial anisotropy parameters are

[16]:
K = 1e5
u = df.Field(mesh, nvdim=3, value=u_fun)

The system is

[17]:
system = mm.System(name="uniaxialanisotropy_field_u")
system.energy = mm.UniaxialAnisotropy(K=K, u=u)
system.m = df.Field(mesh, nvdim=3, value=m_fun, norm=Ms)

and its magnetisation is

[18]:
system.m.k3d.vector(color_field=system.m.z)

After the energy minimisation, the magnetisation is:

[19]:
md.drive(system)
system.m.k3d.vector(color_field=system.m.z)
Running OOMMF (ExeOOMMFRunner)[2023/10/18 12:40]... (0.2 s)

Higher-order uniaxial anisotropy#

In order to define higher-order anisotropy term:

\[w_\text{ua} = -K_{1} (\mathbf{m}\cdot\mathbf{u})^{2} - K_{2} (\mathbf{m}\cdot\mathbf{u})^{4}\]

anisotropy constants K1 and K2 must be passed. Similar to previous example, all parameters (K1, K2, and u) can be spatially varying (dictionary or field).

[20]:
K1 = 1e5
K2 = 1e3
u = df.Field(mesh, nvdim=3, value=u_fun)

system = mm.System(name="uniaxialanisotropy_higher_order")
system.energy = mm.UniaxialAnisotropy(K1=K1, K2=K2, u=u)
system.m = df.Field(mesh, nvdim=3, value=m_fun, norm=Ms)

md.drive(system)
system.m.k3d.vector(color_field=system.m.z)
Running OOMMF (ExeOOMMFRunner)[2023/10/18 12:40]... (0.2 s)