
Figure 1
Snapshots of the unit magnetisation. The plots were created with the code in the function plot_quiver of listing 1. (a) The initial unit magnetisation of the bar of Permalloy described in standard problem #4. (b) The magnetisation conguration at the moment of the reversal of the average magnetisation. (c) The magnetisation after a nanosecond of simulation time has elapsed.
Listing 1
Solution to the standard problem #4. The simulation defined in the function compute_initial_magnetisation returns the relaxed magnetisation configuration in absence of an applied magnetic field. In the function compute_dynamics that makes up the second simulation, the magnetic field that causes the magnetisation reversal is added on line 42. The snapshots in Figure 1 were made using the code in the function plot_quiver.
import os
import matplotlib.pyplot as plt
import numpy as np
from fidimag.micro import Sim, UniformExchange, Demag, Zeeman
from fidimag.common import CuboidMesh
from fidimag.common.constant import mu_0
mesh = CuboidMesh(nx=200, ny=50, nz=1, dx=2.5, dy=2.5, dz=3, unit_length=1e-9)
A = 1.3e-11
Ms = 8.0e5
alpha = 0.02
gamma = 2.211e5
mT = 0.001 / mu_0
def compute_initial_magnetisation():
sim = Sim(mesh, name=‘problem4_init’)
sim.driver.set_tols(rtol=1e-10, atol=1e-10)
sim.driver.alpha = 0.5
sim.driver.gamma = gamma
sim.Ms = Ms
sim.do_precession = False # saves time - not interested in dynamics here
sim.set_m((1, 0.25, 0.1))
sim.add(UniformExchange(A))
sim.add(Demag())
sim.relax(dt=1e-13, stopping_dmdt=0.01, max_steps=5000, save_m_steps=None, save_vtk_steps=None)
return sim.spin
def compute_dynamics(initial_magnetisation):
sim = Sim(mesh, name=‘problem4_dynamics’)
sim.set_m(initial_magnetisation)
sim.driver.set_tols(rtol=1e-10, atol=1e-10)
sim.driver.alpha = alpha
sim.driver.gamma = gamma
sim.Ms = Ms
sim.add(UniformExchange(A))
sim.add(Demag())
sim.add(Zeeman([-24.6 * mT, 4.3 * mT, 0], name=‘H’), save_field=True)
crossed_zero = False
ts = np.linspace(0, 1e-9, 201)
for t in ts:
sim.driver.run_until(t)
mx, my, mz = sim.compute_average()
print(“t = {:.3} ns\t mx = {:.3}”.format(t*1e9, mx))
if mx <= 0 and not crossed_zero:
print(“Crossed zero!”)
np.save(“problem4_m_when_mz_0.npy”, sim.spin)
crossed_zero = True
return sim.spin
def plot_quiver(m, filename):
m.shape = (50, 200, 3)
skip = 5
m = m[1::skip, 1::skip]
xyz = mesh.coordinates
xyz.shape = (50, 200, 3)
xyz = xyz[1::skip, 1::skip]
plt.figure(figsize=(12, 3))
plt.axes().set_aspect(‘equal’)
plt.quiver(xyz[:,:,0], xyz[:,:,1],
m[:,:,0], m[:,:,1], m[:,:,1],
pivot=‘mid’, scale=45, cmap=plt.get_cmap(‘jet’), edgecolors=‘None’)
c = plt.colorbar()
plt.xlabel(“x (nm)”)
plt.ylabel(“y (nm)”)
c.set_label(“$m_\mathrm{y}$ (1)”)
plt.clim([0, 1])
plt.xlim([0, 500])
plt.ylim([0, 125])
plt.savefig(filename)
if __name__ == ‘__main__’:
m0_file = “problem4_m0.npy”
if not os.path.exists(m0_file):
m0 = compute_initial_magnetisation()
np.save(m0_file, m0)
else:
m0 = np.load(m0_file)
mf_file = “problem4_mf.npy”
if not os.path.exists(mf_file):
mf = compute_dynamics(m0)
np.save(mf_file, mf)
else:
mf = np.load(mf_file)
plot_quiver(m0, “problem4_m0.pdf”)
plot_quiver(np.load(“problem4_m_when_mz_0.npy”), “problem4_m_when_mz_0.pdf”)
plot_quiver(mf, “problem4_mf.pdf”)
|

Figure 2
The components of the average magnetisation over time as computed with Fidimag and by OOMMF.
Listing 2
Solution to the standard problem #3. The code that computes the energy difference between the two possible magnetic configurations has been abstracted into the function energy_difference in the module cube_sim that is imported in line 6. The function energy_difference can then be handed off to a bisect method provided by SciPy to find the cube size for which both configurations have the same energy.
””” Solution to µmag standard problem #3 with fidimag. http://www.ctcms.nist.gov/~rdm/mumag.org.html ””” from .cube_sim import energy_difference from scipy.optimize import bisect single_domain_limit = bisect(energy_difference, 8, 8.5, xtol=0.1) print(“L = {:.2} nm.”.format(str(single_domain_limit)) |

Figure 3
Architecture of Fidimag as reflected by the directory structure and Python modules. Code useful to micromagnetic and atomistic simulations was extracted into a common namespace. The red arrows point to user-facing parts of the system.

Figure 4
Cuboid mesh and hexagonal lattice labelled according to the index of the sites.
Listing 3
Definition of a 2 nm wide sphere geometry using micromagnetics.
import fidimag import numpy as np mesh = fidimag.common.CuboidMesh(nx=6, ny=6, nz=6, dx=1, dy=1, dz=1, unit_length=1e-9) sim = fidimag.micro.Sim(mesh) Ms = 1e6 # A / m def sphere(r): x, y, z = r if x ** 2 + y ** 2 <= 2 ** 2: return Ms else: return 0 sim.set_Ms(sphere) |
Listing 4
A unit test in which a scalar field s is created on a hexagonal mesh, saved to a VTK file and compared to a reference file.
def test_save_scalar_field_hexagonal_mesh(tmpdir): mesh = HexagonalMesh(radius=1, nx=3, ny=3) s = scalar_field(mesh, lambda r: r[0] + r[1]) vtk = VTK(mesh, directory=str(tmpdir), filename=”scalar_hexagonal”) vtk.save_scalar(s, name=”s”) assert same_as_ref(vtk.write_file(), REF_DIR) |
