Hard magnet tutorial#
Introduction#
In this notebook we explore hard magnet properties such as Hc as function of temperature for Nd2Fe14B.
We query databases to get temperature-dependent inputs for micromagnetic simulations from DFT and spin dynamics simulations
We run hysteresis simulations and compute derived quantities.
Requirements:
Software:
mammos
,esys-escript
Basic understanding of mammos-units and mammos-entity
[1]:
%config InlineBackend.figure_format = "retina"
import math
import mammos_analysis
import mammos_dft
import mammos_entity as me
import mammos_mumag
import mammos_spindynamics
import mammos_units as u
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib import colormaps
[2]:
# Allow convenient conversions between A/m and T
u.set_enabled_equivalencies(u.magnetic_flux_field());
DFT data: magnetization and anisotropy at zero Kelvin#
The first step loads spontaneous magnetization Ms_0
and the uniaxial anisotropy constant K1_0
from a database of DFT calculations (at T=0K).
We can use the print_info
flag to trigger printing of crystallographic information.
[3]:
results_dft = mammos_dft.db.get_micromagnetic_properties("Nd2Fe14B", print_info=True)
Found material in database.
Chemical Formula: Nd2Fe14B Space group name: P42/mnm Space group number: 136 Cell length a: 8.78 Angstrom Cell length b: 8.78 Angstrom Cell length c: 12.12 Angstrom Cell angle alpha: 90.0 deg Cell angle beta: 90.0 deg Cell angle gamma: 90.0 deg Cell volume: 933.42 Angstrom3 ICSD_label: OQMD_label:
[4]:
results_dft
[4]:
MicromagneticProperties(Ms_0=SpontaneousMagnetization(value=1280000.0, unit=A / m), K1_0=UniaxialAnisotropyConstant(value=4300000.0, unit=J / m3))
[5]:
results_dft.Ms_0
[5]:
SpontaneousMagnetization(value=1280000.0, unit=A / m)
[6]:
results_dft.K1_0
[6]:
UniaxialAnisotropyConstant(value=4300000.0, unit=J / m3)
Temperature-dependent magnetization data from spindynamics database lookup#
In the second step we use a spin dynamics calculation database to load data for the temperature-dependent magnetization.
[7]:
results_spindynamics = mammos_spindynamics.db.get_spontaneous_magnetization("Nd2Fe14B")
We can visualize the pre-computed data using .plot
.
[8]:
results_spindynamics.plot();

We can access T
and Ms
and get mammos_entity.Entity
objects:
[9]:
results_spindynamics.T
[9]:
ThermodynamicTemperature(value=
[ 31.57894737 63.15789474 94.73684211 126.31578947 157.89473684
189.47368421 221.05263158 252.63157895 284.21052632 315.78947368
347.36842105 378.94736842 410.52631579 442.10526316 473.68421053
505.26315789 536.84210526 568.42105263 600. 630.
690. 720. 750. 780. ],
unit=K)
[10]:
results_spindynamics.Ms
[10]:
SpontaneousMagnetization(value=
[1456985.36046193 1449449.93657199 1437972.77253639 1422736.58364638
1403779.39648225 1381044.11152378 1354391.97626968 1323600.99407897
1288353.67899904 1248213.31769096 1202584.11677895 1150645.20320241
1091237.80552561 1022660.54963577 942263.95974459 845538.33920183
723617.755767 553555.21471405 171781.14820705 0.
0. 0. 0. 0. ],
unit=A / m)
We can get also the data in the form of a pandas.DataFrame
, which only contains the values in SI units:
[11]:
results_spindynamics.dataframe.head()
[11]:
T | Ms | |
---|---|---|
0 | 31.578947 | 1.456985e+06 |
1 | 63.157895 | 1.449450e+06 |
2 | 94.736842 | 1.437973e+06 |
3 | 126.315789 | 1.422737e+06 |
4 | 157.894737 | 1.403779e+06 |
Calculate micromagnetic intrinsic properties using Kuz’min formula#
We use Kuz’min equations to compute Ms(T), A(T), K1(T)
Kuz’min, M.D., Skokov, K.P., Diop, L.B. et al. Exchange stiffness of ferromagnets. Eur. Phys. J. Plus 135, 301 (2020). https://doi.org/10.1140/epjp/s13360-020-00294-y
Additional details about inputs and outputs are available in the API reference
[12]:
results_kuzmin = mammos_analysis.kuzmin_properties(
T=results_spindynamics.T,
Ms=results_spindynamics.Ms,
K1_0=results_dft.K1_0,
)
The plot
method of the returned object can be used to visualize temperature-dependence of all three quantities. The temperature range matches that of the fit data:
[13]:
results_kuzmin.plot();

[14]:
results_kuzmin
[14]:
KuzminResult(Ms=Ms(T), A=A(T), Tc=CurieTemperature(value=587.5414256215699, unit=K), s=<Quantity 0.94308558>, K1=K1(T))
The attributes
Ms
,A
andK1
provide fit results as function of temperature. They each have aplot
method.Tc
is the fitted Curie temperature.s
is a fit parameter in the Kuzmin equation.
To visually assess the accuracy of the fit, we can combine the plot
methods of results_kuzmin.Ms
and results_spindynamics
:
[15]:
ax = results_kuzmin.Ms.plot(label="Kuzmin fit")
results_spindynamics.plot(ax=ax, label="Spin dynamics");

To get inputs for the micromagnetic simulation at a specific temperature we call the three attributes Ms
, A
and K1
. We can pass a mammos_entity.Entity
, an astropy.units.Quantity
or a number:
[16]:
temperature = me.T(300)
temperature
[16]:
ThermodynamicTemperature(value=300.0, unit=K)
[17]:
results_kuzmin.Ms(temperature) # Evaluation with Entity
[17]:
SpontaneousMagnetization(value=1259051.2525345664, unit=A / m)
[18]:
results_kuzmin.A(300 * u.K) # Evaluation with Quantity
[18]:
ExchangeStiffnessConstant(value=4.233732726368305e-12, unit=J / m)
[19]:
results_kuzmin.K1(300) # Evaluation with number
[19]:
UniaxialAnisotropyConstant(value=2774809.160558035, unit=J / m3)
Run micromagnetic simulation to compute hysteresis loop#
We now compute a hysteresis loop (using a finite-element micromagnetic simulation) with the material parameters we have obtained.
We simulate a 20x20x20 nm cube for which a pre-defined mesh is available.
Additional documentation of this step is available this notebook.
[20]:
results_hysteresis = mammos_mumag.hysteresis.run(
mesh_filepath=mammos_mumag.mesh.CUBE_20_nm,
Ms=results_kuzmin.Ms(temperature),
A=results_kuzmin.A(temperature),
K1=results_kuzmin.K1(temperature),
hstart=(5 * u.T).to(u.A / u.m),
hfinal=(-5 * u.T).to(u.A / u.m),
hnsteps=30,
)
The returned results_hysteresis
object provides a plot
method to visualize the computed data. mammos_mumag.hysteresis
only computes half a hysteresis loop, going from hstart
to hfinal
. To show a full loop this function mirrors the computed data and plots it twice:
[21]:
results_hysteresis.plot(marker="."); # blue: simulation output, orange: mirrored data

The result object provides access to H
and M
:
[22]:
results_hysteresis.H
[22]:
ExternalMagneticField(value=
[ 3.97887358e+06 3.71361534e+06 3.44835710e+06 3.18309886e+06
2.91784062e+06 2.65258238e+06 2.38732415e+06 2.12206591e+06
1.85680767e+06 1.59154943e+06 1.32629119e+06 1.06103295e+06
7.95774715e+05 5.30516477e+05 2.65258238e+05 2.65046223e-10
-2.65258238e+05 -5.30516477e+05 -7.95774715e+05 -1.06103295e+06
-1.32629119e+06 -1.59154943e+06 -1.85680767e+06 -2.12206591e+06
-2.38732415e+06 -2.65258238e+06 -2.91784062e+06],
unit=A / m)
[23]:
results_hysteresis.M
[23]:
SpontaneousMagnetization(value=
[ 1258774.88418503 1258755.29173903 1258733.53294368 1258709.27568529
1258682.12113735 1258651.58741402 1258617.08777043 1258577.90196103
1258533.13789532 1258481.67979586 1258422.11708352 1258352.64525025
1258270.92537468 1258173.88115408 1258057.39937119 1257915.8788321
1257741.51752364 1257523.20080151 1257244.58817323 1256880.85890033
1256392.65400165 1255714.17388798 1254727.51057885 1253199.94548771
1250597.27231096 1245274.94300117 -1258682.12265352],
unit=A / m)
The dataframe
property generates a dataframe in the SI units.
[24]:
results_hysteresis.dataframe.head()
[24]:
configuration_type | H | M | energy_density | |
---|---|---|---|---|
0 | 1 | 3.978874e+06 | 1.258775e+06 | -8.760007e+06 |
1 | 1 | 3.713615e+06 | 1.258755e+06 | -8.340418e+06 |
2 | 1 | 3.448357e+06 | 1.258734e+06 | -7.920837e+06 |
3 | 1 | 3.183099e+06 | 1.258709e+06 | -7.501263e+06 |
4 | 1 | 2.917841e+06 | 1.258682e+06 | -7.081698e+06 |
We can generate a table in alternate units:
[25]:
df = pd.DataFrame(
{
"mu0_H": results_hysteresis.H.to(u.T),
"J": results_hysteresis.M.to(u.T),
},
)
df.head()
[25]:
mu0_H | J | |
---|---|---|
0 | 5.000000 | 1.581823 |
1 | 4.666667 | 1.581799 |
2 | 4.333333 | 1.581771 |
3 | 4.000000 | 1.581741 |
4 | 3.666667 | 1.581707 |
Plotting of magnetization configurations#
Simulation stores specific magnetization field configurations:
[26]:
results_hysteresis.plot(configuration_marks=True);

[27]:
results_hysteresis.configurations
[27]:
{1: PosixPath('/home/mlang/repos/mammos-devtools/packages/examples/hystloop/hystloop_0001.vtu'),
2: PosixPath('/home/mlang/repos/mammos-devtools/packages/examples/hystloop/hystloop_0002.vtu')}
[28]:
results_hysteresis.plot_configuration(1);
Analyze hysteresis loop#
We can extract extrinsic properties with the extrinsic_properties
function from the mammos_analysis
package:
[29]:
extrinsic_properties = mammos_analysis.hysteresis.extrinsic_properties(
results_hysteresis.H,
results_hysteresis.M,
demagnetization_coefficient=1 / 3,
)
[30]:
extrinsic_properties.Hc
[30]:
CoercivityHcExternal(value=2784501.3536631977, unit=A / m)
[31]:
extrinsic_properties.Mr
[31]:
Remanence(value=1257915.8788321046, unit=A / m)
[32]:
extrinsic_properties.BHmax
[32]:
MaximumEnergyProduct(value=205221.5492334361, unit=J / m3)
We can combine the results_hysteresis.plot
method with some custom code to show Hc and Mr in the hysteresis plot:
[33]:
ax = results_hysteresis.plot()
ax.scatter(0, extrinsic_properties.Mr.value, c="r", label="Mr")
ax.scatter(-extrinsic_properties.Hc.value, 0, c="g", label="Hc")
ax.axhline(0, c="k") # Horizontal line at M=0
ax.axvline(0, c="k") # Vertical line at H=0
ax.legend();

Compute Hc(T)#
We can leverage mammos to calculate Hc(T) for multiple values of T.
First, we run hysteresis simulations at 7 different temperatures and collect all simulation results:
[34]:
T = np.linspace(0, 1.1 * results_kuzmin.Tc, 7)
simulations = []
for temperature in T:
print(f"Running simulation for T={temperature:.0f}")
results_hysteresis = mammos_mumag.hysteresis.run(
mesh_filepath=mammos_mumag.mesh.CUBE_20_nm,
Ms=results_kuzmin.Ms(temperature),
A=results_kuzmin.A(temperature),
K1=results_kuzmin.K1(temperature),
hstart=(7 * u.T).to(u.A / u.m),
hfinal=(-7 * u.T).to(u.A / u.m),
hnsteps=30,
)
simulations.append(results_hysteresis)
Running simulation for T=0 K
Running simulation for T=108 K
Running simulation for T=215 K
Running simulation for T=323 K
Running simulation for T=431 K
Running simulation for T=539 K
Running simulation for T=646 K
We can now use mammos_analysis.hysteresis
as shown before to extract Hc for all simulations and visualize Hc(T):
[35]:
Hcs = []
for res in simulations:
cf = mammos_analysis.hysteresis.extract_coercive_field(H=res.H, M=res.M).value
if np.isnan(cf): # Above Tc
cf = 0
Hcs.append(cf)
[36]:
plt.plot(T, Hcs, linestyle="-", marker="o")
plt.xlabel(me.T().axis_label)
plt.ylabel(me.Hc().axis_label);

We can also show the hysteresis loops of all simulations:
[37]:
colors = colormaps["plasma"].colors[:: math.ceil(256 / len(T))]
fix, ax = plt.subplots()
for temperature, sim, color in zip(T, simulations, colors, strict=False):
if np.isnan(sim.M).all(): # no Ms above Tc
continue
sim.plot(ax=ax, label=f"{temperature:.0f}", color=color, duplicate_change_color=False)
ax.legend(loc="lower right");
