PyOpenMagnetics 1.7.0__tar.gz → 1.7.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/CMakeLists.txt +2 -2
  2. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/PKG-INFO +1 -1
  3. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/pyproject.toml +1 -1
  4. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/advisers.cpp +51 -25
  5. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/bobbin.cpp +36 -11
  6. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/cmc.cpp +4 -2
  7. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/converter.cpp +138 -82
  8. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/core.cpp +91 -46
  9. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/crossref.cpp +4 -2
  10. pyopenmagnetics-1.7.2/src/database.cpp +515 -0
  11. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/database.h +6 -0
  12. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/losses.cpp +81 -26
  13. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/losses.h +1 -0
  14. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/plotting.cpp +22 -11
  15. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/simulation.cpp +60 -30
  16. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/utils.cpp +38 -19
  17. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/winding.cpp +46 -23
  18. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/wire.cpp +68 -34
  19. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/conftest.py +95 -25
  20. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_converter_endpoints.py +43 -3
  21. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_core.py +0 -1
  22. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_core_adviser.py +0 -2
  23. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_examples_integration.py +26 -6
  24. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_inputs.py +0 -2
  25. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_magnetic_adviser.py +5 -4
  26. pyopenmagnetics-1.7.2/tests/test_magnetics_catalogue_load.py +256 -0
  27. pyopenmagnetics-1.7.0/src/database.cpp +0 -263
  28. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/.github/workflows/ci.yml +0 -0
  29. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/.github/workflows/publish.yml +0 -0
  30. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/.gitignore +0 -0
  31. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/AGENTS.md +0 -0
  32. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/LICENSE +0 -0
  33. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/PyOpenMagnetics.pyi +0 -0
  34. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/README.md +0 -0
  35. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/_packaging/__init__.py +0 -0
  36. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/_packaging/_build_info.py.in +0 -0
  37. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/api/MAS.py +0 -0
  38. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/api/mas_db_reader.py +0 -0
  39. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/api/validation.py +0 -0
  40. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/clear_cibuildwheel_cache.sh +0 -0
  41. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/docs/compatibility.md +0 -0
  42. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/docs/errors.md +0 -0
  43. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/docs/performance.md +0 -0
  44. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/README.md +0 -0
  45. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/buck_inductor.py +0 -0
  46. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/complete_simulation_example.py +0 -0
  47. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/converter_design_example.py +0 -0
  48. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/debug_bobbin.py +0 -0
  49. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/debug_coil.py +0 -0
  50. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/debug_core.py +0 -0
  51. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/debug_plotting.py +0 -0
  52. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_220v_12v_1a.py +0 -0
  53. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_220v_12v_2a_complete.py +0 -0
  54. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_bh_curve.png +0 -0
  55. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_core.png +0 -0
  56. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_design.py +0 -0
  57. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_summary.png +0 -0
  58. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/flyback_waveforms.png +0 -0
  59. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/list_plot_funcs.py +0 -0
  60. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/plot_flyback_design.py +0 -0
  61. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/plot_flyback_pyom.py +0 -0
  62. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/test_field_calc.py +0 -0
  63. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/examples/test_field_plot.py +0 -0
  64. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/force_fresh_build.sh +0 -0
  65. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/llms.txt +0 -0
  66. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/notebooks/01_getting_started.ipynb +0 -0
  67. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/notebooks/02_buck_inductor.ipynb +0 -0
  68. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/notebooks/03_core_losses.ipynb +0 -0
  69. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/notebooks/README.md +0 -0
  70. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/requirements.txt +0 -0
  71. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/advisers.h +0 -0
  72. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/bobbin.h +0 -0
  73. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/cmc.h +0 -0
  74. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/common.h +0 -0
  75. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/converter.h +0 -0
  76. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/core.h +0 -0
  77. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/crossref.h +0 -0
  78. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/logging.cpp +0 -0
  79. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/logging.h +0 -0
  80. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/module.cpp +0 -0
  81. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/plotting.h +0 -0
  82. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/settings.cpp +0 -0
  83. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/settings.h +0 -0
  84. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/simulation.h +0 -0
  85. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/utils.h +0 -0
  86. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/winding.h +0 -0
  87. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/src/wire.h +0 -0
  88. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/test.py +0 -0
  89. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/__init__.py +0 -0
  90. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_logging.py +0 -0
  91. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_plotting.py +0 -0
  92. {pyopenmagnetics-1.7.0 → pyopenmagnetics-1.7.2}/tests/test_winding.py +0 -0
@@ -142,7 +142,7 @@ if(NOT LOCAL_MKF_MAS)
142
142
  set(MKF_GIT_TAG "main"
143
143
  CACHE STRING "MKF git ref for the vendored PyOM build")
144
144
  # Force fresh clone by using a unique timestamp - update this when the pin changes
145
- set(MKF_FORCE_REFRESH "2026-08-10-v1.7.0-mkf-d9293885")
145
+ set(MKF_FORCE_REFRESH "2026-08-21-v1.7.2-mkf-75afbb6f")
146
146
  # Tell MKF to disable matplotplusplus and use SVG-based Painter instead
147
147
  set(INCLUDE_PYMKF ON CACHE BOOL "Build Python interface" FORCE)
148
148
  # GIT_SUBMODULES_RECURSE pulls in CAS/PEAS (added 2026-04 alongside the
@@ -166,7 +166,7 @@ if(NOT LOCAL_MKF_MAS)
166
166
  # Skip Git LFS to avoid bandwidth quota issues - data files are optional for build
167
167
  set(ENV{GIT_LFS_SKIP_SMUDGE} "1")
168
168
  # Force fresh clone by using a unique timestamp - update this when MAS changes
169
- set(MAS_FORCE_REFRESH "2026-08-10-v1.7.0-mas-f736d41")
169
+ set(MAS_FORCE_REFRESH "2026-08-14-v1.7.0-mas-934d1392")
170
170
  FetchContent_Declare(
171
171
  mas
172
172
  GIT_REPOSITORY https://github.com/OpenMagnetics/MAS.git
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyOpenMagnetics
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Python wrapper for OpenMagnetics
5
5
  Author-Email: Alfonso Martinez <Alfonso_VII@hotmail.com>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "PyOpenMagnetics"
7
- version = "1.7.0"
7
+ version = "1.7.2"
8
8
  requires-python = ">=3.8"
9
9
  authors = [
10
10
  { name="Alfonso Martinez", email="Alfonso_VII@hotmail.com" },
@@ -2,6 +2,24 @@
2
2
 
3
3
  namespace PyMKF {
4
4
 
5
+ namespace {
6
+
7
+ // A result's reference is only the lookup key for the per-filter scoring breakdown, and BOTH
8
+ // manufacturerInfo and its reference are optional in MAS. Six result loops dereferenced both
9
+ // unconditionally, so a catalogue entry carrying no reference killed the whole call with an
10
+ // anonymous "bad optional access" — at the very end, after every candidate had already been
11
+ // advised and scored. (One of the six did not even use the value it dereferenced; that line is
12
+ // gone.) An absent reference is not an error, it just means there is no per-filter breakdown to
13
+ // attach to that result.
14
+ std::optional<std::string> magnetic_reference(const OpenMagnetics::Magnetic& magnetic) {
15
+ if (!magnetic.get_manufacturer_info()) {
16
+ return std::nullopt;
17
+ }
18
+ return magnetic.get_manufacturer_info()->get_reference();
19
+ }
20
+
21
+ } // namespace
22
+
5
23
  json calculate_advised_cores(json inputsJson, json weightsJson, int maximumNumberResults, json coreModeJson) {
6
24
  OpenMagnetics::Inputs inputs(inputsJson);
7
25
  OpenMagnetics::CoreAdviser::CoreAdviserModes coreMode;
@@ -28,15 +46,15 @@ json calculate_advised_cores(json inputsJson, json weightsJson, int maximumNumbe
28
46
  json results = json();
29
47
  results["data"] = json::array();
30
48
  for (auto& [masMagnetic, scoring] : masMagnetics) {
31
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
49
+ auto name = magnetic_reference(masMagnetic.get_magnetic());
32
50
  json result;
33
51
  json masJson;
34
52
  to_json(masJson, masMagnetic);
35
53
  result["mas"] = masJson;
36
54
  result["scoring"] = scoring;
37
- if (scoringsPerFilter.count(name)) {
55
+ if (name && scoringsPerFilter.count(*name)) {
38
56
  json filterScorings;
39
- for (auto& [filter, filterScore] : scoringsPerFilter[name]) {
57
+ for (auto& [filter, filterScore] : scoringsPerFilter[*name]) {
40
58
  filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
41
59
  }
42
60
  result["scoringPerFilter"] = filterScorings;
@@ -67,15 +85,15 @@ json calculate_advised_magnetics(json inputsJson, int maximumNumberResults, json
67
85
  json results = json();
68
86
  results["data"] = json::array();
69
87
  for (auto& [masMagnetic, scoring] : masMagnetics) {
70
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
88
+ auto name = magnetic_reference(masMagnetic.get_magnetic());
71
89
  json result;
72
90
  json masJson;
73
91
  to_json(masJson, masMagnetic);
74
92
  result["mas"] = masJson;
75
93
  result["scoring"] = scoring;
76
- if (scoringsPerFilter.count(name)) {
94
+ if (name && scoringsPerFilter.count(*name)) {
77
95
  json filterScorings;
78
- for (auto& [filter, filterScore] : scoringsPerFilter[name]) {
96
+ for (auto& [filter, filterScore] : scoringsPerFilter[*name]) {
79
97
  filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
80
98
  }
81
99
  result["scoringPerFilter"] = filterScorings;
@@ -115,15 +133,15 @@ json calculate_advised_magnetics_with_filters(json inputsJson, json filterFlowJs
115
133
  json results = json();
116
134
  results["data"] = json::array();
117
135
  for (auto& [masMagnetic, scoring] : masMagnetics) {
118
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
136
+ auto name = magnetic_reference(masMagnetic.get_magnetic());
119
137
  json result;
120
138
  json masJson;
121
139
  to_json(masJson, masMagnetic);
122
140
  result["mas"] = masJson;
123
141
  result["scoring"] = scoring;
124
- if (scoringsPerFilter.count(name)) {
142
+ if (name && scoringsPerFilter.count(*name)) {
125
143
  json filterScorings;
126
- for (auto& [filter, filterScore] : scoringsPerFilter[name]) {
144
+ for (auto& [filter, filterScore] : scoringsPerFilter[*name]) {
127
145
  filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
128
146
  }
129
147
  result["scoringPerFilter"] = filterScorings;
@@ -150,7 +168,6 @@ json calculate_advised_magnetics_fast(json inputsJson, int maximumNumberResults,
150
168
  json results = json();
151
169
  results["data"] = json::array();
152
170
  for (auto& [masMagnetic, scoring] : masMagnetics) {
153
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
154
171
  json result;
155
172
  json masJson;
156
173
  to_json(masJson, masMagnetic);
@@ -182,15 +199,15 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
182
199
  json results = json();
183
200
  results["data"] = json::array();
184
201
  for (auto& [masMagnetic, scoring] : masMagnetics) {
185
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
202
+ auto name = magnetic_reference(masMagnetic.get_magnetic());
186
203
  json result;
187
204
  json masJson;
188
205
  to_json(masJson, masMagnetic);
189
206
  result["mas"] = masJson;
190
207
  result["scoring"] = scoring;
191
- if (scoringsPerFilter.count(name)) {
208
+ if (name && scoringsPerFilter.count(*name)) {
192
209
  json filterScorings;
193
- for (auto& [filter, filterScore] : scoringsPerFilter[name]) {
210
+ for (auto& [filter, filterScore] : scoringsPerFilter[*name]) {
194
211
  filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
195
212
  }
196
213
  result["scoringPerFilter"] = filterScorings;
@@ -227,15 +244,15 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
227
244
  json results = json();
228
245
  results["data"] = json::array();
229
246
  for (auto& [masMagnetic, scoring] : masMagnetics) {
230
- std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
247
+ auto name = magnetic_reference(masMagnetic.get_magnetic());
231
248
  json result;
232
249
  json masJson;
233
250
  to_json(masJson, masMagnetic);
234
251
  result["mas"] = masJson;
235
252
  result["scoring"] = scoring;
236
- if (scoringsPerFilter.count(name)) {
253
+ if (name && scoringsPerFilter.count(*name)) {
237
254
  json filterScorings;
238
- for (auto& [filter, filterScore] : scoringsPerFilter[name]) {
255
+ for (auto& [filter, filterScore] : scoringsPerFilter[*name]) {
239
256
  filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
240
257
  }
241
258
  result["scoringPerFilter"] = filterScorings;
@@ -359,7 +376,8 @@ void register_adviser_bindings(py::module& m) {
359
376
  ... print(f"Score: {item['scoring']}, Per filter: {item['scoringPerFilter']}")
360
377
  )pbdoc",
361
378
  py::arg("inputs_json"), py::arg("weights_json"),
362
- py::arg("max_results"), py::arg("core_mode_json"));
379
+ py::arg("max_results"), py::arg("core_mode_json"),
380
+ py::call_guard<py::gil_scoped_release>());
363
381
 
364
382
  m.def("calculate_advised_magnetics", &calculate_advised_magnetics,
365
383
  R"pbdoc(
@@ -389,7 +407,8 @@ void register_adviser_bindings(py::module& m) {
389
407
  >>> for item in result["data"]:
390
408
  ... print(f"Score: {item['scoring']}, Per filter: {item['scoringPerFilter']}")
391
409
  )pbdoc",
392
- py::arg("inputs_json"), py::arg("max_results"), py::arg("core_mode_json"));
410
+ py::arg("inputs_json"), py::arg("max_results"), py::arg("core_mode_json"),
411
+ py::call_guard<py::gil_scoped_release>());
393
412
 
394
413
  m.def("calculate_advised_magnetics_with_filters", &calculate_advised_magnetics_with_filters,
395
414
  R"pbdoc(
@@ -402,7 +421,8 @@ void register_adviser_bindings(py::module& m) {
402
421
  {"filter": <TitleCaseName>, "invert": bool, "log": bool,
403
422
  "strictlyRequired": bool, "weight": float}.
404
423
  )pbdoc",
405
- py::arg("inputs_json"), py::arg("filter_flow_json"), py::arg("max_results"), py::arg("core_mode_json"));
424
+ py::arg("inputs_json"), py::arg("filter_flow_json"), py::arg("max_results"), py::arg("core_mode_json"),
425
+ py::call_guard<py::gil_scoped_release>());
406
426
 
407
427
  m.def("calculate_advised_magnetics_fast", &calculate_advised_magnetics_fast,
408
428
  R"pbdoc(
@@ -434,7 +454,8 @@ void register_adviser_bindings(py::module& m) {
434
454
  >>> for item in result["data"]:
435
455
  ... print(f"Total losses: {item['scoring']} W")
436
456
  )pbdoc",
437
- py::arg("inputs_json"), py::arg("max_results"), py::arg("core_mode_json"));
457
+ py::arg("inputs_json"), py::arg("max_results"), py::arg("core_mode_json"),
458
+ py::call_guard<py::gil_scoped_release>());
438
459
 
439
460
  m.def("calculate_advised_magnetics_from_catalog", &calculate_advised_magnetics_from_catalog,
440
461
  R"pbdoc(
@@ -462,7 +483,8 @@ void register_adviser_bindings(py::module& m) {
462
483
  >>> for item in result["data"]:
463
484
  ... print(f"Score: {item['scoring']}, Per filter: {item['scoringPerFilter']}")
464
485
  )pbdoc",
465
- py::arg("inputs_json"), py::arg("catalog_json"), py::arg("max_results"));
486
+ py::arg("inputs_json"), py::arg("catalog_json"), py::arg("max_results"),
487
+ py::call_guard<py::gil_scoped_release>());
466
488
 
467
489
  m.def("calculate_advised_magnetics_from_cache", &calculate_advised_magnetics_from_cache,
468
490
  R"pbdoc(
@@ -489,21 +511,25 @@ void register_adviser_bindings(py::module& m) {
489
511
  Cache must be populated before calling this function.
490
512
  Raises PyOpenMagnetics.EngineError if the cache is empty.
491
513
  )pbdoc",
492
- py::arg("inputs_json"), py::arg("filter_flow_json"), py::arg("max_results"));
514
+ py::arg("inputs_json"), py::arg("filter_flow_json"), py::arg("max_results"),
515
+ py::call_guard<py::gil_scoped_release>());
493
516
 
494
517
  m.def("calculate_advised_sections", &calculate_advised_sections,
495
518
  "Get advised coil sections.",
496
- py::arg("mas"), py::arg("pattern"), py::arg("repetitions"));
519
+ py::arg("mas"), py::arg("pattern"), py::arg("repetitions"),
520
+ py::call_guard<py::gil_scoped_release>());
497
521
 
498
522
  m.def("calculate_advised_coil", &calculate_advised_coil,
499
523
  "Get full coil design advice.",
500
- py::arg("mas"));
524
+ py::arg("mas"),
525
+ py::call_guard<py::gil_scoped_release>());
501
526
 
502
527
  m.def("calculate_advised_wires", &calculate_advised_wires,
503
528
  "Get wire selection advice.",
504
529
  py::arg("winding"), py::arg("section"), py::arg("current"),
505
530
  py::arg("solid_insulation_requirements"), py::arg("temperature"), py::arg("number_sections"),
506
- py::arg("max_results"), py::arg("use_planar_wires") = false);
531
+ py::arg("max_results"), py::arg("use_planar_wires") = false,
532
+ py::call_guard<py::gil_scoped_release>());
507
533
  }
508
534
 
509
535
  } // namespace PyMKF
@@ -111,14 +111,36 @@ json create_simple_bobbin_from_core_with_custom_thicknesses(json coreJson, doubl
111
111
  }
112
112
 
113
113
  void register_bobbin_bindings(py::module& m) {
114
- m.def("get_bobbins", &get_bobbins, "Retrieve all available bobbins as JSON objects");
115
- m.def("get_bobbin_names", &get_bobbin_names, "Retrieve list of all bobbin names");
116
- m.def("find_bobbin_by_name", &find_bobbin_by_name, "Find bobbin data by name");
117
- m.def("create_basic_bobbin", &create_basic_bobbin, "Create a basic bobbin from core data");
118
- m.def("create_basic_bobbin_by_thickness", &create_basic_bobbin_by_thickness, "Create a basic bobbin with specified thickness");
119
- m.def("calculate_bobbin_data", &calculate_bobbin_data, "Calculate bobbin specifications");
120
- m.def("process_bobbin", &process_bobbin, "Process bobbin geometry");
121
- m.def("check_if_fits", &check_if_fits, "Check if winding fits in available space");
114
+ m.def("get_bobbins", &get_bobbins, "Retrieve all available bobbins as JSON objects",
115
+ py::call_guard<py::gil_scoped_release>());
116
+ m.def("get_bobbin_names", &get_bobbin_names, "Retrieve list of all bobbin names",
117
+ py::call_guard<py::gil_scoped_release>());
118
+ m.def("find_bobbin_by_name", &find_bobbin_by_name, "Find bobbin data by name",
119
+ py::call_guard<py::gil_scoped_release>());
120
+ m.def("create_basic_bobbin", &create_basic_bobbin, "Create a basic bobbin from core data",
121
+ py::call_guard<py::gil_scoped_release>());
122
+ m.def("create_basic_bobbin_by_thickness", &create_basic_bobbin_by_thickness, "Create a basic bobbin with specified thickness",
123
+ py::call_guard<py::gil_scoped_release>());
124
+ m.def("calculate_bobbin_data", &calculate_bobbin_data, "Calculate bobbin specifications",
125
+ py::call_guard<py::gil_scoped_release>());
126
+ m.def("process_bobbin", &process_bobbin,
127
+ R"pbdoc(
128
+ Process a bobbin's functionalDescription into its processedDescription
129
+ (winding window, column/wall thickness, coordinates).
130
+
131
+ Args:
132
+ bobbin_json: JSON Bobbin OBJECT — a Python dict, e.g. a row of MAS's
133
+ data/bobbins.ndjson. It must NOT be pre-serialised: passing
134
+ json.dumps(row) hands the engine a json *string*, which carries no
135
+ functionalDescription and is refused (ABT #763).
136
+
137
+ Returns:
138
+ JSON Bobbin object with processedDescription filled in.
139
+ )pbdoc",
140
+ py::arg("bobbin_json"),
141
+ py::call_guard<py::gil_scoped_release>());
142
+ m.def("check_if_fits", &check_if_fits, "Check if winding fits in available space",
143
+ py::call_guard<py::gil_scoped_release>());
122
144
 
123
145
  m.def("create_simple_bobbin_from_core", &create_simple_bobbin_from_core,
124
146
  R"pbdoc(
@@ -130,7 +152,8 @@ void register_bobbin_bindings(py::module& m) {
130
152
  Returns:
131
153
  JSON Bobbin object.
132
154
  )pbdoc",
133
- py::arg("core_json"));
155
+ py::arg("core_json"),
156
+ py::call_guard<py::gil_scoped_release>());
134
157
 
135
158
  m.def("create_simple_bobbin_from_core_with_custom_thickness", &create_simple_bobbin_from_core_with_custom_thickness,
136
159
  R"pbdoc(
@@ -143,7 +166,8 @@ void register_bobbin_bindings(py::module& m) {
143
166
  Returns:
144
167
  JSON Bobbin object.
145
168
  )pbdoc",
146
- py::arg("core_json"), py::arg("thickness"));
169
+ py::arg("core_json"), py::arg("thickness"),
170
+ py::call_guard<py::gil_scoped_release>());
147
171
 
148
172
  m.def("create_simple_bobbin_from_core_with_custom_thicknesses", &create_simple_bobbin_from_core_with_custom_thicknesses,
149
173
  R"pbdoc(
@@ -157,7 +181,8 @@ void register_bobbin_bindings(py::module& m) {
157
181
  Returns:
158
182
  JSON Bobbin object.
159
183
  )pbdoc",
160
- py::arg("core_json"), py::arg("wall_thickness"), py::arg("column_thickness"));
184
+ py::arg("core_json"), py::arg("wall_thickness"), py::arg("column_thickness"),
185
+ py::call_guard<py::gil_scoped_release>());
161
186
  }
162
187
 
163
188
  } // namespace PyMKF
@@ -58,12 +58,14 @@ void register_cmc_bindings(py::module& m) {
58
58
  "numberOfWindings, and an impedance/insertion-loss/noise spec). Mirrors "
59
59
  "the WASM `calculate_cmc_inputs` used by el-choker's CMC wizard. "
60
60
  "Delegates to Kirchhoff api::design_cmc.",
61
- py::arg("cmc_inputs"));
61
+ py::arg("cmc_inputs"),
62
+ py::call_guard<py::gil_scoped_release>());
62
63
  m.def("calculate_advanced_cmc_inputs", &calculate_advanced_cmc_inputs,
63
64
  "Advanced-mode CMC inputs builder (user supplies `desiredInductance` + "
64
65
  "`designFrequency` directly rather than a spec). Delegates to Kirchhoff "
65
66
  "api::design_cmc.",
66
- py::arg("cmc_inputs"));
67
+ py::arg("cmc_inputs"),
68
+ py::call_guard<py::gil_scoped_release>());
67
69
  }
68
70
 
69
71
  } // namespace PyMKF