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:

[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();
../../_images/examples_workflows_hard-magnet-tutorial_11_0.png

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();
../../_images/examples_workflows_hard-magnet-tutorial_20_0.png
[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 and K1 provide fit results as function of temperature. They each have a plot 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");
../../_images/examples_workflows_hard-magnet-tutorial_23_0.png

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
../../_images/examples_workflows_hard-magnet-tutorial_32_0.png

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);
../../_images/examples_workflows_hard-magnet-tutorial_41_0.png
[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();
../../_images/examples_workflows_hard-magnet-tutorial_50_0.png

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);
../../_images/examples_workflows_hard-magnet-tutorial_55_0.png

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");
../../_images/examples_workflows_hard-magnet-tutorial_57_0.png