The main goal of this tutorial is to have a quick look at how a simple ubermag simulation inside Jupyter looks like and to make ourselves comfortable with Jupyter. We are going to try to guess what the meaning of Python commands in code cells is, and eventually try to identify the skeleton of ubermag simulation. There are probably going to be many parts you do not understand, but please do not worry - we are going to go into all the details in the next sessions.
Before we specify and run the simulation, we have to import Ubermag modules we intend to use. For defining micromagnetic models, we need to import micromagneticmodel
and for defining finite-diference fields, we are going to import discretisedfield
.
import micromagneticmodel as mm # mm is just a shorter name we want to use later
import discretisedfield as df # df is just a shorter name we want to use later
The main object in ubermag is mm.System
. In order to define the micromagnetic system we want to simulate, we have to specify:
system = mm.System(name="first_ubermag_simulation")
The energy equation for the first Ubermag simulaton is very simple and contains only the following energy terms:
A = 1e-12 # exchange energy constant (J/m)
H = (5e6, 0, 0) # external magnetic field in the x-direction (A/m)
system.energy = mm.Exchange(A=A) + mm.Demag() + mm.Zeeman(H=H)
We choose to simulate a cube with $50 \,\,\text{nm}$ edge length and discretise it into $10$ cells in each direction ($1000$ in total). We initialise the system in positive $y$-direction, i.e. $\mathbf{m} = (0, 1, 0)$, which is different from the equlibrium state we expect for the external magnetic field applied in $x$-direction. For its norm (saturation magnetisation), we choose $M_\text{s} = 8\times 10^{6} \,\text{A}/\text{m}$.
L = 50e-9 # cubic sample edge length (m)
region = df.Region(p1=(0, 0, 0), p2=(L, L, L))
mesh = df.Mesh(region=region, n=(10, 10, 10))
Ms = 8e6 # saturation magnetisation (A/m)
system.m = df.Field(mesh, nvdim=3, value=(0, 1, 0), norm=Ms)
We have defined the system object and now we can do some inspection to make sure we did not make a mistake. First, we are going to check the energy equation:
system.energy
This gives us a human-readable equation, which is actually the sum of terms we defined earlier.
Inspecting magnetisation is sligtly more complicated because there are many things we can ask the magnetisation for. Let us have a look at a few basic ones and the rest of them, we are going to explore in later sessions.
The region we defined is:
system.m.mesh.region.mpl()
Representation of the mesh:
system.m.mesh.mpl()
A 2d plot of the magnetisation in the z-slice:
system.m.sel("z").mpl()
After the system object is created, we can minimise its energy (relax it) using the Minimisation Driver (MinDriver
). At this point, we choose the calculator we want to use. During this workshop, we are going to use OOMMF. Therefore, we import OOMMF calculator - oommfc
.
import oommfc as mc
md = mc.MinDriver()
md.drive(system)
Running OOMMF (ExeOOMMFRunner)[2023/10/23 15:52]... (0.4 s)
The system is now relaxed and its previous magnetisation is now replaced with the new one.
Please note: system.m
is updated after using a Driver
. This means if you use a second Driver
it will start from the system.m
that was obtained after the first Driver
. If you want to start from the initial system.m
you will have to reinitialise it with a df.Field
in the same way it was done originally (Cell 5).
We can plot its slice and compute its average magnetisation.
system.m.sel("z").resample((10, 10)).mpl()
system.m.mean()
array([ 7.86015824e+06, -2.25800930e-03, 2.14204192e-11])
system.m.orientation.mean()
array([ 9.82519780e-01, -2.82251136e-10, 2.05391260e-18])
We can see that the magnetisation is aligned along the $x$-direction, as expected having in mind we applied the external magnetic field in that direction.