PyOpenMagnetics 1.7.28__tar.gz → 1.7.30__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 (95) hide show
  1. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/CMakeLists.txt +1 -1
  2. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/PKG-INFO +1 -1
  3. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/PyOpenMagnetics.pyi +3 -0
  4. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/pyproject.toml +1 -1
  5. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/advisers.cpp +20 -1
  6. pyopenmagnetics-1.7.30/tests/test_losses_not_evaluable_catalogue.py +62 -0
  7. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_magnetics_catalogue_load.py +1 -1
  8. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/.github/workflows/ci.yml +0 -0
  9. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/.github/workflows/publish.yml +0 -0
  10. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/.gitignore +0 -0
  11. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/AGENTS.md +0 -0
  12. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/LICENSE +0 -0
  13. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/README.md +0 -0
  14. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/_packaging/__init__.py +0 -0
  15. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/_packaging/_build_info.py.in +0 -0
  16. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/api/MAS.py +0 -0
  17. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/api/mas_db_reader.py +0 -0
  18. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/api/validation.py +0 -0
  19. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/clear_cibuildwheel_cache.sh +0 -0
  20. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/docs/compatibility.md +0 -0
  21. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/docs/errors.md +0 -0
  22. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/docs/performance.md +0 -0
  23. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/README.md +0 -0
  24. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/buck_inductor.py +0 -0
  25. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/complete_simulation_example.py +0 -0
  26. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/converter_design_example.py +0 -0
  27. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/debug_bobbin.py +0 -0
  28. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/debug_coil.py +0 -0
  29. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/debug_core.py +0 -0
  30. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/debug_plotting.py +0 -0
  31. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_220v_12v_1a.py +0 -0
  32. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_220v_12v_2a_complete.py +0 -0
  33. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_bh_curve.png +0 -0
  34. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_core.png +0 -0
  35. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_design.py +0 -0
  36. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_summary.png +0 -0
  37. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/flyback_waveforms.png +0 -0
  38. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/list_plot_funcs.py +0 -0
  39. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/plot_flyback_design.py +0 -0
  40. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/plot_flyback_pyom.py +0 -0
  41. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/test_field_calc.py +0 -0
  42. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/examples/test_field_plot.py +0 -0
  43. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/force_fresh_build.sh +0 -0
  44. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/llms.txt +0 -0
  45. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/notebooks/01_getting_started.ipynb +0 -0
  46. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/notebooks/02_buck_inductor.ipynb +0 -0
  47. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/notebooks/03_core_losses.ipynb +0 -0
  48. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/notebooks/README.md +0 -0
  49. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/requirements.txt +0 -0
  50. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/advisers.h +0 -0
  51. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/bobbin.cpp +0 -0
  52. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/bobbin.h +0 -0
  53. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/cmc.cpp +0 -0
  54. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/cmc.h +0 -0
  55. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/common.h +0 -0
  56. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/converter.cpp +0 -0
  57. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/converter.h +0 -0
  58. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/core.cpp +0 -0
  59. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/core.h +0 -0
  60. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/crossref.cpp +0 -0
  61. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/crossref.h +0 -0
  62. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/database.cpp +0 -0
  63. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/database.h +0 -0
  64. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/logging.cpp +0 -0
  65. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/logging.h +0 -0
  66. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/losses.cpp +0 -0
  67. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/losses.h +0 -0
  68. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/module.cpp +0 -0
  69. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/plotting.cpp +0 -0
  70. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/plotting.h +0 -0
  71. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/settings.cpp +0 -0
  72. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/settings.h +0 -0
  73. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/simulation.cpp +0 -0
  74. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/simulation.h +0 -0
  75. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/utils.cpp +0 -0
  76. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/utils.h +0 -0
  77. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/winding.cpp +0 -0
  78. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/winding.h +0 -0
  79. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/wire.cpp +0 -0
  80. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/src/wire.h +0 -0
  81. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/test.py +0 -0
  82. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/__init__.py +0 -0
  83. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/conftest.py +0 -0
  84. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_converter_endpoints.py +0 -0
  85. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_core.py +0 -0
  86. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_core_adviser.py +0 -0
  87. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_datasheet_only_catalogue.py +0 -0
  88. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_examples_integration.py +0 -0
  89. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_inputs.py +0 -0
  90. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_logging.py +0 -0
  91. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_magnetic_adviser.py +0 -0
  92. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_mas_schema_walk.py +0 -0
  93. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_material_validator.py +0 -0
  94. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/tests/test_plotting.py +0 -0
  95. {pyopenmagnetics-1.7.28 → pyopenmagnetics-1.7.30}/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-09-30-mkf-fd4751f1")
145
+ set(MKF_FORCE_REFRESH "2026-10-02-mkf-7b80390f")
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyOpenMagnetics
3
- Version: 1.7.28
3
+ Version: 1.7.30
4
4
  Summary: Python wrapper for OpenMagnetics
5
5
  Author-Email: Alfonso Martinez <Alfonso_VII@hotmail.com>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -676,6 +676,9 @@ def calculate_advised_magnetics_from_catalog(
676
676
  - "mas": Mas object with magnetic data
677
677
  - "scoring": Overall float score
678
678
  - "scoringPerFilter": Object with individual scores per filter
679
+ - "lossesNotEvaluable" (only on such parts): why the part's core losses could not be
680
+ evaluated (its core material has no core-loss model); it is ranked without any
681
+ loss-based filter and returned without simulated outputs.
679
682
  """
680
683
  ...
681
684
 
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "PyOpenMagnetics"
7
- version = "1.7.28"
7
+ version = "1.7.30"
8
8
  requires-python = ">=3.8"
9
9
  authors = [
10
10
  { name="Alfonso Martinez", email="Alfonso_VII@hotmail.com" },
@@ -195,6 +195,7 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
195
195
  auto masMagnetics = magneticAdviser.get_advised_magnetic(inputs, catalog, maximumNumberResults);
196
196
 
197
197
  auto scoringsPerFilter = magneticAdviser.get_scorings();
198
+ const auto& lossesNotEvaluable = magneticAdviser.get_losses_not_evaluable();
198
199
 
199
200
  json results = json();
200
201
  results["data"] = json::array();
@@ -212,6 +213,11 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
212
213
  }
213
214
  result["scoringPerFilter"] = filterScorings;
214
215
  }
216
+ // A part whose core material has no core-loss model is ranked without the loss filters
217
+ // and returned without simulated outputs; this field says so, and why.
218
+ if (name && lossesNotEvaluable.count(*name)) {
219
+ result["lossesNotEvaluable"] = lossesNotEvaluable.at(*name);
220
+ }
215
221
  results["data"].push_back(result);
216
222
  }
217
223
 
@@ -247,6 +253,7 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
247
253
  : magneticAdviser.get_advised_magnetic(inputs, OpenMagnetics::magneticsCache.get(), filterFlow, maximumNumberResults);
248
254
 
249
255
  auto scoringsPerFilter = magneticAdviser.get_scorings();
256
+ const auto& lossesNotEvaluable = magneticAdviser.get_losses_not_evaluable();
250
257
 
251
258
  json results = json();
252
259
  results["data"] = json::array();
@@ -264,6 +271,11 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
264
271
  }
265
272
  result["scoringPerFilter"] = filterScorings;
266
273
  }
274
+ // A part whose core material has no core-loss model is ranked without the loss filters
275
+ // and returned without simulated outputs; this field says so, and why.
276
+ if (name && lossesNotEvaluable.count(*name)) {
277
+ result["lossesNotEvaluable"] = lossesNotEvaluable.at(*name);
278
+ }
267
279
  results["data"].push_back(result);
268
280
  }
269
281
 
@@ -491,7 +503,10 @@ void register_adviser_bindings(py::module& m) {
491
503
  - "mas": Mas object with magnetic data
492
504
  - "scoring": Overall float score
493
505
  - "scoringPerFilter": Object with individual scores per filter
494
-
506
+ - "lossesNotEvaluable" (only on such parts): why this part's core losses could
507
+ not be evaluated -- its core material carries no core-loss model. The part is
508
+ ranked without any loss-based filter and returned without simulated outputs.
509
+
495
510
  Example:
496
511
  >>> inputs = PyMKF.process_inputs(raw_inputs)
497
512
  >>> catalog = [magnetic1, magnetic2, magnetic3]
@@ -531,6 +546,10 @@ void register_adviser_bindings(py::module& m) {
531
546
  - "mas": Mas object with magnetic data
532
547
  - "scoring": Overall float score
533
548
  - "scoringPerFilter": Object with individual scores per filter
549
+ - "lossesNotEvaluable" (only on such parts): why this part's core losses could
550
+ not be evaluated -- its core material carries no core-loss model. The part is
551
+ ranked on the filters that apply to it, with no loss-based filter scored, and
552
+ is returned without simulated outputs.
534
553
 
535
554
  Note:
536
555
  Cache must be populated before calling this function.
@@ -0,0 +1,62 @@
1
+ """
2
+ A catalogue part whose core material carries no core-loss data any model can run
3
+ (`volumetricLosses: {}`, no `massLosses`) must neither abort the catalogue adviser nor vanish
4
+ from its results. Like a datasheet-only part, the loss-based filters do not apply to it (no score,
5
+ never a stand-in loss), it is ranked on the filters that do, and the result says why its losses
6
+ were not judged, in "lossesNotEvaluable".
7
+ """
8
+ import json
9
+
10
+ import pytest
11
+
12
+ import PyOpenMagnetics
13
+
14
+ from .test_datasheet_only_catalogue import buck_inputs, modelled_part, references_of
15
+
16
+ LOSSLESS_MATERIAL = "Test Ferrite Without Loss Data"
17
+
18
+ FLOW = [
19
+ {"filter": "Losses No Proximity", "invert": True, "log": False, "strictlyRequired": False, "weight": 1.0},
20
+ {"filter": "Volume", "invert": True, "log": False, "strictlyRequired": False, "weight": 1.0},
21
+ ]
22
+
23
+
24
+ def lossless_part(reference):
25
+ """The modelled part, on N87 with its core-loss data removed, as MAS stores a material nobody
26
+ has measured losses for."""
27
+ material = PyOpenMagnetics.find_core_material_by_name("N87")
28
+ material["name"] = LOSSLESS_MATERIAL
29
+ material["volumetricLosses"] = {}
30
+ material.pop("massLosses", None)
31
+ part = modelled_part(reference)
32
+ part["core"]["functionalDescription"]["material"] = material
33
+ return part
34
+
35
+
36
+ @pytest.fixture
37
+ def catalogue(tmp_path):
38
+ PyOpenMagnetics.clear_magnetic_cache()
39
+ path = tmp_path / "catalogue.ndjson"
40
+ parts = [modelled_part("NORMAL-A"), lossless_part("LOSSLESS"), modelled_part("NORMAL-B")]
41
+ path.write_text("\n".join(json.dumps(part) for part in parts) + "\n", encoding="utf-8")
42
+ PyOpenMagnetics.load_magnetics_from_file(str(path), True)
43
+ yield
44
+ PyOpenMagnetics.clear_magnetic_cache()
45
+
46
+
47
+ def test_a_part_without_a_core_loss_model_is_ranked_and_flagged(catalogue):
48
+ result = PyOpenMagnetics.calculate_advised_magnetics_from_cache(buck_inputs(), FLOW, 10)
49
+
50
+ assert set(references_of(result)) == {"NORMAL-A", "LOSSLESS", "NORMAL-B"}
51
+ by_reference = {item["mas"]["magnetic"]["manufacturerInfo"]["reference"]: item for item in result["data"]}
52
+
53
+ flagged = by_reference["LOSSLESS"]
54
+ assert LOSSLESS_MATERIAL in flagged["lossesNotEvaluable"]
55
+ assert "no core-loss data" in flagged["lossesNotEvaluable"]
56
+ # Ranked only on what applies to it: no loss score, never a stand-in.
57
+ assert "LOSSES_NO_PROXIMITY" not in flagged["scoringPerFilter"]
58
+ assert "VOLUME" in flagged["scoringPerFilter"]
59
+
60
+ for reference in ("NORMAL-A", "NORMAL-B"):
61
+ assert "lossesNotEvaluable" not in by_reference[reference]
62
+ assert "LOSSES_NO_PROXIMITY" in by_reference[reference]["scoringPerFilter"]
@@ -223,7 +223,7 @@ INLINE_WIRE_MAGNETIC = {
223
223
  },
224
224
  "gapping": [],
225
225
  "numberStacks": 1,
226
- "coating": {"type": "epoxy", "thickness": 0.0006, "material": "epoxy"},
226
+ "coating": {"type": "epoxy", "thickness": {"nominal": 0.0006}, "material": "epoxy"},
227
227
  }
228
228
  },
229
229
  "coil": {