Hysteresis simulations#
In this tutorial, we summarise some of the convenience methods offered by Ubermag that can be used for simulating hysteresis. Let us first define a simple system object. For details on how to define simulations, please refer to other tutorials.
[1]:
import oommfc as mc
import discretisedfield as df
import micromagneticmodel as mm
region = df.Region(p1=(-50e-9, -50e-9, -50e-9), p2=(50e-9, 50e-9, 50e-9))
mesh = df.Mesh(region=region, cell=(5e-9, 5e-9, 5e-9))
system = mm.System(name="hysteresis")
system.energy = (
mm.Exchange(A=1e-12)
+ mm.UniaxialAnisotropy(K=4e5, u=(0, 0, 1))
+ mm.DMI(D=1e-3, crystalclass="T")
) # + mm.Demag()
def Ms_fun(point):
x, y, z = point
if x**2 + y**2 + z**2 <= 50e-9**2:
return 1e6
else:
return 0
system.m = df.Field(mesh, nvdim=3, value=(0, 0, -1), norm=Ms_fun, valid="norm")
Now that we have system
object we can simulate hysteresis using HysteresisDriver
. Like all other drivers, HysteresisDriver
has drive
method. As input arguments it takes system
object (as usual) and:
Hmin
- the starting value of magnetic fieldHmin
- the end value of magnetic fieldn
- the number of points betweenHmin
andHmax
For instance, let us say we want to simulate hysteresis between \(-1\,\text{T}\) and \(1\,\text{T}\) applied along the \(z\)-direction in steps of \(0.2\,\text{T}\). Accordingly, Hmin
and Hmax
are:
[2]:
Hmin = (0, 0, -1 / mm.consts.mu0)
Hmax = (0, 0, 1 / mm.consts.mu0)
The number of steps n
should be 21, so that the values of magnetic field are: \(-1\,\text{T}\), \(-0.9\,\text{T}\), \(-0.8\,\text{T}\), …, \(0.9\,\text{T}\), \(1\,\text{T}\), \(0.9\,\text{T}\), \(0.8\,\text{T}\), …, \(-0.9\,\text{T}\), \(-1\,\text{T}\).
[3]:
n = 21
We can now create HysteresisDriver
and drive the system.
[4]:
hd = mc.HysteresisDriver()
hd.drive(system, Hmin=Hmin, Hmax=Hmax, n=n)
Running OOMMF (ExeOOMMFRunner)[2024/08/12 13:25]... (2.8 s)
After the simulation is complete, we can have a look at the last magnetisation step:
[5]:
system.m.sel("y").mpl()
Similarly, table can be viewed:
[6]:
system.table.data.head()
[6]:
max_mxHxm | E | delta_E | bracket_count | line_min_count | conjugate_cycle_count | cycle_count | cycle_sub_count | energy_calc_count | E_exchange | ... | B_hysteresis | Bx_hysteresis | By_hysteresis | Bz_hysteresis | iteration | stage_iteration | stage | mx | my | mz | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0.081239 | -5.296243e-16 | -2.958228e-31 | 9.0 | 9.0 | 1.0 | 8.0 | 7.0 | 19.0 | 1.178506e-19 | ... | 1000.0 | 0.0 | 0.0 | -1000.0 | 18.0 | 18.0 | 0.0 | -7.579123e-20 | -1.378022e-20 | -0.998400 |
1 | 0.079335 | -4.769135e-16 | -8.874685e-31 | 16.0 | 15.0 | 2.0 | 15.0 | 6.0 | 33.0 | 1.301416e-19 | ... | 900.0 | 0.0 | 0.0 | -900.0 | 32.0 | 13.0 | 1.0 | -5.512089e-20 | -3.445056e-20 | -0.998216 |
2 | 0.056329 | -4.242133e-16 | -9.860761e-32 | 23.0 | 22.0 | 3.0 | 22.0 | 6.0 | 48.0 | 1.445097e-19 | ... | 800.0 | 0.0 | 0.0 | -800.0 | 47.0 | 14.0 | 2.0 | -4.065166e-19 | 2.067033e-20 | -0.997999 |
3 | 0.086473 | -3.715256e-16 | -2.465190e-31 | 31.0 | 29.0 | 4.0 | 29.0 | 6.0 | 64.0 | 1.614662e-19 | ... | 700.0 | 0.0 | 0.0 | -700.0 | 62.0 | 14.0 | 3.0 | -6.270001e-19 | -8.612639e-20 | -0.997739 |
4 | 0.053097 | -3.188531e-16 | -3.944305e-31 | 40.0 | 36.0 | 5.0 | 37.0 | 7.0 | 81.0 | 1.816966e-19 | ... | 600.0 | 0.0 | 0.0 | -600.0 | 78.0 | 15.0 | 4.0 | 1.067967e-19 | 2.756045e-20 | -0.997422 |
5 rows × 26 columns
From the table, we can see at what external magnetic field the system was relaxed:
[7]:
system.table.data["B_hysteresis"]
[7]:
0 1000.0
1 900.0
2 800.0
3 700.0
4 600.0
5 500.0
6 400.0
7 300.0
8 200.0
9 100.0
10 0.0
11 100.0
12 200.0
13 300.0
14 400.0
15 500.0
16 600.0
17 700.0
18 800.0
19 900.0
20 1000.0
21 900.0
22 800.0
23 700.0
24 600.0
25 500.0
26 400.0
27 300.0
28 200.0
29 100.0
30 0.0
31 100.0
32 200.0
33 300.0
34 400.0
35 500.0
36 600.0
37 700.0
38 800.0
39 900.0
40 1000.0
Name: B_hysteresis, dtype: float64
The units of \(B\) are:
[8]:
system.table.units["B_hysteresis"]
[8]:
'mT'
Plotting hysteresis loop#
Using Table
object, we can plot different values as a function of external magnetic field. For that, as usual, we use mpl
method. We have to specify to that method what do we want to have on the \(y\)-axis.
[9]:
system.table.mpl(y=["mz"])
This does not look like a hysteresis loop as we expected. The reason is that on the horizontal axis we have the magnitude B
by default, which is always positive. We can change that by passing Bz
for x
:
[10]:
system.table.mpl(x="Bz_hysteresis", y=["mz"])
Table.mpl
is based on matplotlib.pyplot.plot
, so any keyword argument accepted by it can be passed:
[11]:
system.table.mpl(
x="Bz_hysteresis", y=["mz"], marker="o", linewidth=2, linestyle="dashed"
)
micromagneticdata
analysis#
We can also analyse magnetisation fields at different values of external magnetic field as well as building interactive plots using micromagneticdata
package.
[12]:
import micromagneticdata as md
We can create a data object by passing system’s name.
[13]:
data = md.Data(name=system.name)
Now, we can have a look at all drives we did so far:
[14]:
data.info
[14]:
drive_number | date | time | driver | adapter | Hsteps | n_threads | |
---|---|---|---|---|---|---|---|
0 | 0 | 2024-08-12 | 13:25:57 | HysteresisDriver | oommfc | [[[0, 0, -795774.7154594767], [0, 0, 795774.71... | None |
There is only one drive with index 0
. We can get if by indexing the data object:
[15]:
drive = data[0]
The number of steps saved is:
[16]:
drive.n
[16]:
41
We can get an individual step by passing a value between 0 and 40 as an index:
[17]:
drive[0].sel("x").mpl()
We can also interactively plot all individual steps by using drive.hv
. For more details on creating interactive plots, please refer to other tutorials.
[18]:
drive.hv(kdims=["x", "y"])
[18]:
Multi-step Hysteresis Drivers#
Ubermag also provides a flexible version of a hysteresis driver which enables a hysteresis loop to be broken down into multiple curves. A list of hysteresis loop segments can be provided as an argument Hsteps
. Each element of the list has the form (Hstart, Hend, n)
.
To see the power of this technique, we will first initialise the field and then perform a full hysteresis loop including the virgin curve.
[19]:
system.m = df.Field(
mesh,
nvdim=3,
value=lambda x: (0, 0, -1) if x[0] > 0 else (0, 0, 1),
norm=Ms_fun,
valid="norm",
)
[20]:
hd = mc.HysteresisDriver()
hd.drive(system, Hsteps=[[(0, 0, 0), Hmax, 10], [Hmax, Hmin, 10], [Hmin, Hmax, 20]])
Running OOMMF (ExeOOMMFRunner)[2024/08/12 13:26]... (2.6 s)
Finally we can plot the full curve.
[21]:
system.table.mpl(
x="Bz_hysteresis", y=["mz"], marker="o", linewidth=2, linestyle="dashed"
)