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 field

  • Hmin - the end value of magnetic field

  • n - the number of points between Hmin and Hmax

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)[2023/10/23 16:08]... (1.6 s)

After the simulation is complete, we can have a look at the last magnetisation step:

[5]:
system.m.sel('y').mpl()
../../_images/examples_notebooks_hysteresis_9_0.png

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.081187 -5.296243e-16 -3.944305e-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 -6.890111e-21 -4.478572e-20 -0.998400
1 0.079349 -4.769135e-16 -7.888609e-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 -4.375221e-19 -2.067033e-20 -0.998216
2 0.056383 -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 -1.584726e-19 -5.167584e-20 -0.997999
3 0.086409 -3.715256e-16 -1.972152e-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 -2.652693e-19 -3.100550e-20 -0.997739
4 0.053104 -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 6.166650e-19 -3.100550e-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'])
../../_images/examples_notebooks_hysteresis_18_0.png

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'])
../../_images/examples_notebooks_hysteresis_20_0.png

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')
../../_images/examples_notebooks_hysteresis_22_0.png

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 Hmin Hmax n n_threads
0 0 2022-10-20 15:13:27 HysteresisDriver [0, 0, -795774.7154594767] [0, 0, 795774.7154594767] 21 NaN
1 1 2022-10-20 15:27:16 HysteresisDriver [0, 0, -795774.7154594767] [0, 0, 795774.7154594767] 21 NaN
2 2 2023-10-23 16:08:27 HysteresisDriver [0, 0, -795774.7154594767] [0, 0, 795774.7154594767] 21 NaN

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()
../../_images/examples_notebooks_hysteresis_34_0.png

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]: