Skip to content

Commit

Permalink
Add interactive GUI and loss refinement (#13)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-kirienko authored Feb 4, 2025
1 parent 8e868a8 commit e333381
Show file tree
Hide file tree
Showing 17 changed files with 846 additions and 114 deletions.
3 changes: 2 additions & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:

jobs:
nox:
timeout-minutes: 180
timeout-minutes: 240
runs-on: ubuntu-latest
if: >
github.event_name == 'pull_request' ||
Expand All @@ -28,4 +28,5 @@ jobs:
with:
name: outputs
path: .
include-hidden-files: true
compression-level: 9
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ __pycache__/
*$py.class
*.so
*.tmp
*.wrk
*.work
*.tsv
*.tab
*.out
Expand Down
4 changes: 4 additions & 0 deletions .idea/dictionaries/pavel.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

96 changes: 74 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ Supports several JA model definitions.
```shell
git clone https://github.com/Zubax/jafit
cd jafit
pip install .
pip install ".[interactive]"
```

You may omit `[interactive]` if you are not planning on using the GUI.

The tool works on GNU/Linux and Windows. Probably also on macOS, but YMMV.

### Solve the JA equation
Expand All @@ -39,16 +41,19 @@ the resulting hysteresis loop will be a minor loop.

### Find JA coefficients for a given reference B(H) curve

The fitting problem may take multiple hours to solve, depending on the curve shape and the performance of your computer.
The fitting problem may take multiple hours or days to solve,
depending on the curve shape and the performance of your computer.
Intermediate results and logs will be stored in the current working directory,
so it may be a good idea to create a dedicated directory for this purpose.

Basic examples:

```shell
# Fit the example curve from Altair Flux:
jafit model=venk ref="data/B(H).Altair_Flux.Example.csv" interpolate=300
jafit model=venk ref="data/B(H).Altair_Flux.Example.csv" interpolate=300 H_amp_max=0

# Find coefficients for isotropic AlNiCo 5:
jafit model=venk ref="data/B(H).Jesenik.AlNiCo.tab"
jafit model=venk ref="data/B(H).Jesenik.AlNiCo.tab" preg=100
```

Output symbol legend per function evaluation:
Expand All @@ -57,11 +62,37 @@ Use `quiet=1` to reduce the verbosity.

The input reference curve file must contain two columns: H \[A/m\] and B \[T\], either tab- or comma-separated.
The first row may or may not be the header row.
The reference curve may be either the entire hysteresis loop, whether major or minor,
or only a part of the descending branch.

By default, the tool will attempt to determine the suitable range of the H amplitude values using heuristics.
It is always a good idea to specify this manually instead by setting `H_amp_min` and/or `H_amp_max`,
where `H_amp_min` specifies the minimum H magnitude that must be reached before switching the H sweep direction,
and `H_amp_max` is the maximum H magnitude that the solver is allowed to use; the solver will flip the H sweep
direction somewhere between these two values as soon as the material reaches saturation ($\chi^\prime$ becomes small).

`H_amp_max` is clamped to be at least as large as `H_amp_min`; therefore, setting `H_amp_max=0`
will effectively force the tool to use a fixed H amplitude, irrespective of the saturation detection.
If the provided loop is a minor loop, the tool needs to be instructed to limit the H amplitude to what is seen
in the reference dataset; to do that, simply pass `H_amp_max=0`.

By default, the tool will assume that the reference loop may not push the material into saturation,
and thus it will attempt to determine $M_s$ as part of the optimization problem.
By default, the range for the $M_s$ search is determined automatically using heuristics.
The heuristics can be overridden using the optional `M_s_min` and/or `M_s_max` parameters.
If `M_s_max<=M_s_min`, the tool will assume that $M_s$ is known exactly, $M_s$=`M_s_min`=`M_s_max`,
and remove the corresponding dimension from the optimization problem.
This is often useful because manufacturers often provide the saturation magnetization (or polarization) directly.

Optionally, you can provide the initial guess for (some of) the coefficients: `c_r`, `M_s`, `a`, `k_p`, `alpha`.
It is required that `M_s_min <= M_s <= M_s_max`; if both min and max are provided, `M_s` is not needed.
It is usually not necessary to set `k_p` because it defaults to $H_c$, which is a reasonable guess.

The reference curve may be either the entire hysteresis loop, or any part of it;
e.g., only a part of the descending branch.
If a full loop is provided, then that loop doesn't need to be the major loop;
the tool will simply use the H amplitude seen in the reference loop for solving the JA equation.
The priority region error gain -- `preg` --
can be set to a value greater than one to make the optimizer assign proportionally higher importance
to the part of the descending branch where $M>0$, and the part of the ascending branch where $M<0$.
Example: `preg=100`.
This option only has effect if the full reference loop is provided.

Option `interpolate=N`, where N is a positive integer, can be used to interpolate the reference curve
with N equidistant sample points distributed along its length. Note that this is not the same as sampling the curve
Expand All @@ -73,20 +104,6 @@ If interpolation is not used (it is not by default), then the optimizer will nat
to the regions of the curve with higher density of sample points. This may be leveraged to great advantage
if the reference curve is pre-processed to leave out the regions that are less important for the fitting.

If the reference curve is only a part of the hysteresis loop,
then the tool will use simple heuristics to guess the reasonable H amplitude for solving the JA equation,
assuming that the loop is the major loop (i.e., it reaches saturation).
In this case, it is recommended to specify `H_amp_min` and/or `H_amp_max` explicitly instead of relying on heuristics.

`H_amp_min` specifies the minimum H magnitude that must be reached before switching the H sweep direction,
and `H_amp_max` is the maximum H magnitude that the solver is allowed to use; the solver will flip the H sweep
direction somewhere between these two values as soon as the material reaches saturation ($\chi^\prime$ becomes small).

If the loop is known to be the major loop, then it is occasionally useful to manually extend `H_amp_max` a little
to ensure that the material reaches deep saturation, so that the optimizer converges to a good $M_s$ value faster.

Optionally, you can provide the initial guess for (some of) the coefficients: `c_r`, `M_s`, `a`, `k_p`, `alpha`.

The optimization is done in multiple stages, with global search preceding local refinement.
The tool can be instructed to skip N first stages by setting `stage=N`. See the code for details.

Expand All @@ -96,6 +113,40 @@ unrealistically high magnetic susceptibility, being able to solve such cases is
of the optimization landscape, which in turn helps the optimizer converge faster and reduces the chances of
getting stuck in local minima.

For a more advanced usage example, consider the following datasheet from the manufacturer:

<img src="data/B(H),J(H).VegaTechnik.LNG60.png" width="600" alt="">

The following data is immediately available:

* The third quadrant of the $B(H)$ curve; the data is extracted here as `data/B(H).VegaTechnik.LNG60.tab`.

* The third quadrant polarization $J(H)$ curve is also available but not needed --- the tool will convert
$B(H) \Rightarrow J(H) \Rightarrow M(H)$.

* Saturation magnetization $M_s=1174563$ A/m, which is expressed as saturation polarization $J_s$ in the datasheet.

* The excitation field intensity $H_m=713014$ A/m.

Sadly, the datasheet is not using the SI system, so manual conversion is needed.
The corresponding optimization command is as follows:

```shell
jafit ref='data/B(H).VegaTechnik.LNG60.tab' model=venkataraman M_s_min=1174563 M_s_max=1174563 H_amp_min=713014 H_amp_max=713014
```

### Adjust the parameters interactively

Use `interactive=1` to launch an interactive tool with web GUI.
This mode requires that the interactive GUI option is enabled when installing the package;
refer to the installation section for details.

```
jafit interactive=1 model=venk ref='data/B(H).Campbell.AlNiCo_5.tab' c_r=0.00083284 M_s=1251180 a=20838 k_p=69771 alpha=0.08
```

<img src="interactive.png" width="800" alt="">

### Helpful tips

For fetching the (approximate) data points from a third-party plot,
Expand All @@ -106,6 +157,7 @@ For the benefit of all mankind, please only use SI units. To convert data from a

- $1 \ \text{oersted} \approx 79.57747 \frac{\text{A}}{\text{m}}$
- $1 \ \frac{\text{emu}}{\text{cm}^3} = 10^3 \frac{\text{A}}{\text{m}}$
- $1 \ \text{gauss} = 10^{-4} \ \text{T}$

For more, refer to `papers/magnetic_units.pdf`.

Expand Down
Binary file added data/B(H),J(H).VegaTechnik.LNG37.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added data/B(H),J(H).VegaTechnik.LNG60.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
67 changes: 67 additions & 0 deletions data/B(H).VegaTechnik.LNG37.tab
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
H B
0 1.2363
-1693.177349 1.232302
-3315.876504 1.228305
-5079.582068 1.226306
-6984.444101 1.222308
-8818.777919 1.219310
-10653.061717 1.215311
-12205.182638 1.212313
-13616.147089 1.208316
-15097.739794 1.206317
-16861.445358 1.203319
-18413.566278 1.200320
-19824.580749 1.194324
-21165.016986 1.190326
-22717.137906 1.186329
-24339.787041 1.178332
-26032.964390 1.171337
-27302.872412 1.161342
-28854.993332 1.157345
-29842.688456 1.149350
-31183.124692 1.142354
-32523.610949 1.134357
-33722.940736 1.124363
-34851.742309 1.113370
-36121.650331 1.104374
-37250.451904 1.089383
-38590.888140 1.079389
-39719.689713 1.061399
-40707.384837 1.042410
-41977.292859 1.013426
-42753.353319 0.989439
-43247.200881 0.968452
-44023.261341 0.937469
-44446.530668 0.909485
-44940.428250 0.874505
-45363.697577 0.836526
-45857.545139 0.792551
-46139.758037 0.759570
-46421.970936 0.733585
-46563.077385 0.703602
-46774.712048 0.674618
-47127.453161 0.645634
-47198.031396 0.617651
-47480.194274 0.590666
-47550.772509 0.560683
-47832.935387 0.535696
-48044.620070 0.502715
-48256.254734 0.476730
-48326.832969 0.445748
-48467.889398 0.422760
-48679.574081 0.398775
-48750.102296 0.368791
-48891.208745 0.347803
-48891.208745 0.325816
-48961.736960 0.298831
-49032.315194 0.275843
-49102.843409 0.245861
-49243.949858 0.218876
-49455.634541 0.193890
-49455.634541 0.170904
-49455.634541 0.141920
-49667.269205 0.112936
-49737.797420 0.087950
-49808.375654 0.057968
-49878.903869 0.029983
-50020.010318 0
65 changes: 65 additions & 0 deletions data/B(H).VegaTechnik.LNG60.tab
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
H B
0 1.3281
-1888.399972 1.324105
-3706.850408 1.321110
-5665.199917 1.318114
-7553.599889 1.314120
-9442.059241 1.312123
-11610.197980 1.308128
-13638.497026 1.302137
-15876.585302 1.300140
-18184.682495 1.295147
-20492.720307 1.292151
-22590.968890 1.290155
-24899.006702 1.283164
-27276.994052 1.276174
-29654.981401 1.272180
-31123.743533 1.265191
-33221.992115 1.259198
-35250.291161 1.251210
-37068.741597 1.246217
-38537.503728 1.237230
-40425.903701 1.233235
-42244.354136 1.222252
-44412.552255 1.206275
-46370.901764 1.191295
-47979.503589 1.173322
-49238.476491 1.153350
-50217.651245 1.135375
-50986.977390 1.109412
-51546.514303 1.088443
-51966.152144 1.061481
-52595.638595 1.037516
-52945.326898 1.010554
-53364.964739 0.977601
-53784.602579 0.946646
-54134.350263 0.920683
-54414.089030 0.889727
-54693.887177 0.858772
-54973.625944 0.820826
-55253.364711 0.792865
-55323.314248 0.765905
-55533.162858 0.740940
-55812.901625 0.710982
-55952.800699 0.684022
-56232.539466 0.652067
-56302.489002 0.614121
-56582.287149 0.583166
-56582.287149 0.555206
-56792.076380 0.531240
-57001.924990 0.487302
-57141.824063 0.454350
-57281.663757 0.413408
-57631.411441 0.380455
-57701.301597 0.351498
-57911.150208 0.317546
-58051.049281 0.290584
-58051.049281 0.257631
-58190.888975 0.228672
-58400.737585 0.204707
-58610.586195 0.171754
-58750.425889 0.138801
-58820.375425 0.110842
-59030.224036 0.074893
-59170.063729 0.039943
-59379.912339 0
Binary file added interactive.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 9 additions & 2 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,16 @@ def run(args: str) -> None:
run("model=or c_r=0.885 M_s=1080000 a=1107718.3824 k_p=702271.17275 alpha=3.168")
run("model=po c_r=0.956886 M_s=2956870.912 a=025069.875361 k_p=019498.2 alpha=0.18122")

run(f"model=ve effort=20 plot_failed=1 fast=1 quiet=1 ref='{ROOT}/data/B(H).Jesenik.AlNiCo.tab'")
run(f"model=ve effort=20 plot_failed=1 fast=1 quiet=1 preg=100 ref='{ROOT}/data/B(H).Jesenik.AlNiCo.tab'")
run(f"model=ve effort=20 plot_failed=1 fast=1 interpolate=100 ref='{ROOT}/data/B(H).Ansys.LNG37.tab'")
run(f"model=ve effort=20 plot_failed=1 fast=1 interpolate=300 ref='{ROOT}/data/B(H).Altair_Flux.Example.csv'")
run(
f"model=ve effort=20 plot_failed=1 fast=1 interpolate=300 H_amp_max=0 ref='{ROOT}/data/B(H).Altair_Flux.Example.csv'"
)
for preg in [1, 100]:
run(
f"model=ve ref='{ROOT}/data/B(H).Jesenik.AlNiCo.tab' effort=20 quiet=1 stage=2 preg={preg} "
f" c_r=0.1 M_s=1460000 a=20000 k_p=66000 alpha=0.063"
)

# Run pytest with coverage
session.install("pytest ~= 8.3")
Expand Down
7 changes: 6 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ dependencies = [
"numba", # For JIT compilation, not functionally significant
]

[project.optional-dependencies]
interactive = [
"dash ~= 2.18",
]

[project.scripts]
jafit = "jafit.jafit:main"

Expand Down Expand Up @@ -60,7 +65,7 @@ show_error_context = true
mypy_path = []

[[tool.mypy.overrides]]
module = ["scipy", "scipy.*", "matplotlib", "numba", "traitlets.*"]
module = ["scipy", "scipy.*", "matplotlib", "numba", "traitlets.*", "dash", "dash.*", "plotly.*"]
ignore_missing_imports = true

# -------------------------------------------------- BLACK --------------------------------------------------
Expand Down
Loading

0 comments on commit e333381

Please sign in to comment.