|
| 1 | +--- |
| 2 | +jupytext: |
| 3 | + formats: md:myst,ipynb |
| 4 | + text_representation: |
| 5 | + extension: .md |
| 6 | + format_name: myst |
| 7 | + format_version: 0.13 |
| 8 | + jupytext_version: 1.14.5 |
| 9 | +kernelspec: |
| 10 | + display_name: Python 3 (ipykernel) |
| 11 | + language: python |
| 12 | + name: python3 |
| 13 | +--- |
| 14 | + |
| 15 | ++++ {"user_expressions": []} |
| 16 | + |
| 17 | +# Soft Magnets |
| 18 | + |
| 19 | ++++ |
| 20 | + |
| 21 | +This code demonstrates demagnetization calculations for a hard and a soft cuboid magnet using the Magpylib library. |
| 22 | +Demagnetization is applied using varying numbers of cells for the mesh and compared to the computed magnetic fields from Magpylib withoug demagnetization and with FEM analysis data obtained from an external dataset. |
| 23 | + |
| 24 | ++++ {"user_expressions": []} |
| 25 | + |
| 26 | +## Define magnetic sources with their susceptibilities |
| 27 | + |
| 28 | +```{code-cell} ipython3 |
| 29 | +import json |
| 30 | +
|
| 31 | +import magpylib as magpy |
| 32 | +import numpy as np |
| 33 | +import pandas as pd |
| 34 | +import plotly.express as px |
| 35 | +from loguru import logger |
| 36 | +from magpylib_material_response.demag import apply_demag |
| 37 | +from magpylib_material_response.meshing import mesh_all |
| 38 | +
|
| 39 | +magpy.defaults.display.backend = "plotly" |
| 40 | +
|
| 41 | +# hard magnet |
| 42 | +cube1 = magpy.magnet.Cuboid(magnetization=(0, 0, 1000), dimension=(1, 1, 2)) |
| 43 | +cube1.move((0, 0, 0.5)) |
| 44 | +cube1.xi = 0.5 # µr=1.5 |
| 45 | +cube1.style.label = f"Hard cuboid magnet, xi={cube1.xi}" |
| 46 | +
|
| 47 | +# soft magnet |
| 48 | +cube2 = magpy.magnet.Cuboid(magnetization=(0, 0, 0), dimension=(1, 1, 1)) |
| 49 | +cube2.rotate_from_angax(angle=45, axis="y").move((1.5, 0, 0)) |
| 50 | +cube2.xi = 3999 # µr=4000 |
| 51 | +cube2.style.label = f"Soft cuboid magnet, xi={cube2.xi}" |
| 52 | +
|
| 53 | +# collection of all cells |
| 54 | +coll = magpy.Collection(cube1, cube2, style_label="No demag") |
| 55 | +
|
| 56 | +# add sensors |
| 57 | +sensors = [ |
| 58 | + magpy.Sensor( |
| 59 | + position=np.linspace((-4, 0, z), (6, 0, z), 1001), |
| 60 | + style_label=f"Sensor, z={z}mm", |
| 61 | + ) |
| 62 | + for z in (-1, -3, -5) |
| 63 | +] |
| 64 | +
|
| 65 | +magpy.show(*coll, *sensors) |
| 66 | +``` |
| 67 | + |
| 68 | +```{code-cell} ipython3 |
| 69 | +# example of meshed Collection |
| 70 | +coll_meshed = mesh_all( |
| 71 | + coll, target_elems=50, per_child_elems=False, style_label="No demag - meshed" |
| 72 | +) |
| 73 | +magpy.show(*coll_meshed) |
| 74 | +``` |
| 75 | + |
| 76 | ++++ {"user_expressions": []} |
| 77 | + |
| 78 | +## Compute material response - demagnetization |
| 79 | + |
| 80 | +```{code-cell} ipython3 |
| 81 | +# apply demagnetization with varying number of cells |
| 82 | +colls = [coll] |
| 83 | +for target_elems in [1, 2, 8, 16, 32, 64, 128, 256]: |
| 84 | + with logger.contextualize(target_elems=target_elems): |
| 85 | + coll_meshed = mesh_all( |
| 86 | + coll, target_elems=target_elems, per_child_elems=True, min_elems=1 |
| 87 | + ) |
| 88 | + coll_demag = apply_demag( |
| 89 | + coll_meshed, |
| 90 | + style={"label": f"Coll_demag ({len(coll_meshed.sources_all):3d} cells)"}, |
| 91 | + ) |
| 92 | + colls.append(coll_demag) |
| 93 | +``` |
| 94 | + |
| 95 | ++++ {"user_expressions": []} |
| 96 | + |
| 97 | +## Compare with FEM analysis |
| 98 | + |
| 99 | +```{code-cell} ipython3 |
| 100 | +# compute field before demag |
| 101 | +B_no_demag_df = magpy.getB(coll_meshed, sensors, output="dataframe") |
| 102 | +
|
| 103 | +B_cols = ["Bx", "Bz"] |
| 104 | +
|
| 105 | +
|
| 106 | +def get_FEM_dataframe(sim): |
| 107 | + res = sim["results"][0] |
| 108 | + df = B_no_demag_df.copy() |
| 109 | + for Bk in B_cols: |
| 110 | + df[Bk] = res["value"].get(Bk, np.nan) |
| 111 | + df["computation"] = res["computation"] |
| 112 | + return df |
| 113 | +
|
| 114 | +
|
| 115 | +def get_magpylib_dataframe(collection, sensors): |
| 116 | + df = magpy.getB(collection, sensors, output="dataframe") |
| 117 | + df["computation"] = collection.style.label |
| 118 | + return df |
| 119 | +
|
| 120 | +
|
| 121 | +from magpylib_material_response.data import get_dataset |
| 122 | +
|
| 123 | +sim_ANSYS = get_dataset("FEMdata_test_softmag") # FEM dataset has only Bx and Bz |
| 124 | +
|
| 125 | +df = pd.concat( |
| 126 | + [ |
| 127 | + get_FEM_dataframe(sim_ANSYS), |
| 128 | + *[get_magpylib_dataframe(c, sensors) for c in colls], |
| 129 | + ] |
| 130 | +).sort_values(["computation", "path"]) |
| 131 | +
|
| 132 | +
|
| 133 | +df["Distance [mm]"] = sensors[0].position[df["path"]][:, 0] |
| 134 | +df["Distance [mm]"] -= df["Distance [mm]"].min() |
| 135 | +``` |
| 136 | + |
| 137 | +```{code-cell} ipython3 |
| 138 | +px_kwargs = dict( |
| 139 | + x="path", |
| 140 | + y=B_cols, |
| 141 | + facet_row="variable", |
| 142 | + facet_col="sensor", |
| 143 | + color="computation", |
| 144 | + line_dash="computation", |
| 145 | + height=600, |
| 146 | + facet_col_spacing=0.05, |
| 147 | + labels={Bk: f"{Bk} [mT]" for Bk in B_cols}, |
| 148 | +) |
| 149 | +fig1 = px.line( |
| 150 | + df, |
| 151 | + title="Methods comparison", |
| 152 | + **px_kwargs, |
| 153 | +) |
| 154 | +fig1.update_yaxes(matches=None, showticklabels=True) |
| 155 | +
|
| 156 | +df_diff = df.copy() |
| 157 | +ref = sim_ANSYS["results"][0]["computation"] |
| 158 | +
|
| 159 | +for st in df_diff["computation"].unique(): |
| 160 | + df_diff.loc[df_diff["computation"] == st, B_cols] -= df.loc[ |
| 161 | + df["computation"] == ref, B_cols |
| 162 | + ].values |
| 163 | +
|
| 164 | +fig2 = px.line( |
| 165 | + df_diff, |
| 166 | + title=f"Methods comparison - diff vs {ref}", |
| 167 | + **px_kwargs, |
| 168 | +) |
| 169 | +fig2.update_yaxes(matches=None, showticklabels=True) |
| 170 | +display(fig1, fig2) |
| 171 | +``` |
| 172 | + |
| 173 | ++++ {"user_expressions": []} |
| 174 | + |
| 175 | +As shown above, the demagnetized collection outputs are approaching the reference FEM values. |
0 commit comments