Soft Magnets#

This code demonstrates demagnetization calculations for a hard and a soft cuboid magnet using the Magpylib library. Demagnetization is applied using varying numbers of cells for the mesh and compared to the computed magnetic fields from Magpylib without demagnetization and with FEM analysis data obtained from an external dataset.

Define magnetic sources with their susceptibilities#

import json

import magpylib as magpy
import numpy as np
import pandas as pd
import plotly.express as px
from magpylib_material_response.demag import apply_demag
from magpylib_material_response.meshing import mesh_all

# Uncomment to enable logging output from the package (silent by default).
# from magpylib_material_response import configure_logging
# configure_logging(min_log_time=5)  # log steps taking longer than 5 s

magpy.defaults.display.backend = "plotly"

# hard magnet
cube1 = magpy.magnet.Cuboid(polarization=(0, 0, 1), dimension=(0.001, 0.001, 0.002))
cube1.move((0, 0, 0.0005))
cube1.susceptibility = 0.5  # µr=1.5
cube1.style.label = f"Hard cuboid magnet, susceptibility={cube1.susceptibility}"

# soft magnet
cube2 = magpy.magnet.Cuboid(polarization=(0, 0, 0), dimension=(0.001, 0.001, 0.001))
cube2.rotate_from_angax(angle=45, axis="y").move((0.0015, 0, 0))
cube2.susceptibility = 3999  # µr=4000
cube2.style.label = f"Soft cuboid magnet, susceptibility={cube2.susceptibility}"

# collection of all cells
coll = magpy.Collection(cube1, cube2, style_label="No demag")

# add sensors
sensors = [
    magpy.Sensor(
        position=np.linspace((-0.004, 0, z), (0.006, 0, z), 1001),
        style_label=f"Sensor, z={z}m",
    )
    for z in (-0.001, -0.003, -0.005)
]

magpy.show(*coll, *sensors)
# example of meshed Collection
coll_meshed = mesh_all(
    coll, target_elems=50, per_child_elems=False, style_label="No demag - meshed"
)
magpy.show(*coll_meshed)

Compute material response - demagnetization#

# apply demagnetization with varying number of cells
colls = [coll]
for target_elems in [1, 2, 8, 16, 32, 64, 128, 256]:
    print(f"Processing demagnetization with {target_elems} target elements")

    coll_meshed = mesh_all(
        coll, target_elems=target_elems, per_child_elems=True, min_elems=1
    )

    coll_demag = apply_demag(
        coll_meshed,
        style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"},
    )
    colls.append(coll_demag)

    print(f"Completed demagnetization: {len(coll_meshed.sources_all)} cells created")
Processing demagnetization with 1 target elements
Completed demagnetization: 2 cells created
Processing demagnetization with 2 target elements
Completed demagnetization: 4 cells created
Processing demagnetization with 8 target elements
Completed demagnetization: 16 cells created
Processing demagnetization with 16 target elements
Completed demagnetization: 34 cells created
Processing demagnetization with 32 target elements
Completed demagnetization: 66 cells created
Processing demagnetization with 64 target elements
Completed demagnetization: 127 cells created
Processing demagnetization with 128 target elements
Completed demagnetization: 253 cells created
Processing demagnetization with 256 target elements
Completed demagnetization: 502 cells created

Compare with FEM analysis#

# compute field before demag
B_no_demag_df = magpy.getB(coll_meshed, sensors, output="dataframe")

B_cols = ["Bx", "Bz"]


def get_FEM_dataframe(sim):
    res = sim["results"][0]
    df = B_no_demag_df.copy()
    for Bk in B_cols:
        df[Bk] = res["value"].get(Bk, np.nan)
    df["computation"] = res["computation"]
    return df


def get_magpylib_dataframe(collection, sensors):
    df = magpy.getB(collection, sensors, output="dataframe")
    df["computation"] = collection.style.label
    return df


from magpylib_material_response import get_dataset

sim_ANSYS = get_dataset("FEMdata_test_softmag")  # FEM dataset has only Bx and Bz

df = pd.concat(
    [
        get_FEM_dataframe(sim_ANSYS),
        *[get_magpylib_dataframe(c, sensors) for c in colls],
    ]
).sort_values(["computation", "path"])


df["Distance [m]"] = sensors[0].position[df["path"]][:, 0]
df["Distance [m]"] -= df["Distance [m]"].min()
px_kwargs = dict(
    x="path",
    y=B_cols,
    facet_row="variable",
    facet_col="sensor",
    color="computation",
    line_dash="computation",
    height=600,
    facet_col_spacing=0.05,
    labels={**{Bk: f"{Bk} [T]" for Bk in B_cols}, "value": "value [T]"},
)
fig1 = px.line(
    df,
    title="Methods comparison",
    **px_kwargs,
)
fig1.update_yaxes(matches=None, showticklabels=True)

df_diff = df.copy()
ref = sim_ANSYS["results"][0]["computation"]

for st in df_diff["computation"].unique():
    df_diff.loc[df_diff["computation"] == st, B_cols] -= df.loc[
        df["computation"] == ref, B_cols
    ].values

fig2 = px.line(
    df_diff,
    title=f"Methods comparison - diff vs {ref}",
    **px_kwargs,
)
fig2.update_yaxes(matches=None, showticklabels=True)
display(fig1, fig2)

As shown above, the demagnetized collection outputs are approaching the reference FEM values while refining the mesh.