PyOpenMagnetics 1.7.22__tar.gz → 1.7.24__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.
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/CMakeLists.txt +1 -1
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/PKG-INFO +1 -1
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/PyOpenMagnetics.pyi +12 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/pyproject.toml +1 -1
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/losses.cpp +66 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/losses.h +4 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/settings.cpp +6 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/simulation.cpp +46 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/simulation.h +1 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_datasheet_only_catalogue.py +15 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_examples_integration.py +16 -0
- pyopenmagnetics-1.7.24/tests/test_material_validator.py +40 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/.gitignore +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/AGENTS.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/LICENSE +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/README.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/_packaging/__init__.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/_packaging/_build_info.py.in +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/api/MAS.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/api/validation.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/docs/errors.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/docs/performance.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/README.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/llms.txt +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/requirements.txt +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/advisers.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/cmc.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/cmc.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/common.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/converter.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/converter.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/core.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/core.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/crossref.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/crossref.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/database.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/database.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/logging.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/module.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/plotting.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/settings.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/utils.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/winding.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/src/wire.h +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/test.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_magnetics_catalogue_load.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_mas_schema_walk.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.7.22 → pyopenmagnetics-1.7.24}/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-
|
|
145
|
+
set(MKF_FORCE_REFRESH "2026-09-28-mkf-780d21de")
|
|
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
|
|
@@ -512,6 +512,18 @@ def get_core_losses_model_information(material: CoreMaterial) -> JsonDict:
|
|
|
512
512
|
"""Get available loss models and data for material."""
|
|
513
513
|
...
|
|
514
514
|
|
|
515
|
+
def validate_material(material: CoreMaterial, extra_loss_points: List[JsonDict] = ...) -> JsonDict:
|
|
516
|
+
"""Magnetic Blade Runner: physics check of one MAS core-material record.
|
|
517
|
+
|
|
518
|
+
Returns {reference, valid, materialClass, findings, skipped}; valid is False iff a finding
|
|
519
|
+
is IMPOSSIBLE.
|
|
520
|
+
"""
|
|
521
|
+
...
|
|
522
|
+
|
|
523
|
+
def validate_all_materials(core_materials_path: str = "", advanced_core_materials_path: str = "") -> JsonDict:
|
|
524
|
+
"""Magnetic Blade Runner over a core-material catalogue (empty path = MKF's embedded one)."""
|
|
525
|
+
...
|
|
526
|
+
|
|
515
527
|
# =============================================================================
|
|
516
528
|
# WINDING ENGINE
|
|
517
529
|
# =============================================================================
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include "losses.h"
|
|
2
|
+
#include "support/MaterialValidator.h"
|
|
2
3
|
|
|
3
4
|
namespace PyMKF {
|
|
4
5
|
|
|
@@ -59,6 +60,35 @@ json get_core_losses_model_information(json material) {
|
|
|
59
60
|
return info;
|
|
60
61
|
}
|
|
61
62
|
|
|
63
|
+
json validate_material(json material, json extraLossPoints) {
|
|
64
|
+
std::vector<OpenMagnetics::MaterialLossPoint> points;
|
|
65
|
+
for (const auto& p : extraLossPoints) {
|
|
66
|
+
points.push_back(OpenMagnetics::MaterialLossPoint{p.at("frequency").get<double>(), p.at("magneticFluxDensityPeak").get<double>(),
|
|
67
|
+
p.at("temperature").get<double>(), p.at("volumetricLosses").get<double>(),
|
|
68
|
+
p.value("origin", std::string())});
|
|
69
|
+
}
|
|
70
|
+
json out = OpenMagnetics::MaterialValidator().validate(material, points);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
json validate_all_materials(std::string coreMaterialsPath, std::string advancedCoreMaterialsPath) {
|
|
75
|
+
OpenMagnetics::MaterialValidator validator;
|
|
76
|
+
std::vector<OpenMagnetics::MaterialVerdict> verdicts;
|
|
77
|
+
if (coreMaterialsPath.empty()) {
|
|
78
|
+
if (!advancedCoreMaterialsPath.empty()) {
|
|
79
|
+
throw std::invalid_argument("advanced_core_materials_path needs core_materials_path");
|
|
80
|
+
}
|
|
81
|
+
verdicts = validator.validate_embedded_catalogue();
|
|
82
|
+
}
|
|
83
|
+
else {
|
|
84
|
+
verdicts = validator.validate_catalogue(coreMaterialsPath, advancedCoreMaterialsPath.empty() ? std::nullopt : std::optional<std::string>(advancedCoreMaterialsPath));
|
|
85
|
+
}
|
|
86
|
+
json out = OpenMagnetics::summarize_material_verdicts(verdicts);
|
|
87
|
+
out["verdicts"] = verdicts;
|
|
88
|
+
out["classTable"] = OpenMagnetics::MaterialValidator::class_table_json();
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
62
92
|
json calculate_steinmetz_coefficients(json dataJson, json rangesJson) {
|
|
63
93
|
std::vector<std::pair<double, double>> ranges;
|
|
64
94
|
for (auto rangeJson : rangesJson) {
|
|
@@ -334,6 +364,42 @@ void register_losses_bindings(py::module& m) {
|
|
|
334
364
|
py::arg("core_data"), py::arg("coil_data"), py::arg("inputs_data"), py::arg("models_data"),
|
|
335
365
|
py::call_guard<py::gil_scoped_release>());
|
|
336
366
|
|
|
367
|
+
m.def("validate_material", &validate_material,
|
|
368
|
+
R"pbdoc(
|
|
369
|
+
Magnetic Blade Runner: check one MAS core-material record against physics.
|
|
370
|
+
|
|
371
|
+
Evaluates the record with MKF's own loss/permeability/saturation models and judges the
|
|
372
|
+
results against published class envelopes (each with its datasheet source).
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
material: one MAS core-material record (a line of core_materials.ndjson).
|
|
376
|
+
extra_loss_points: optional measured points kept outside the record, each
|
|
377
|
+
{frequency, magneticFluxDensityPeak, temperature, volumetricLosses, origin}.
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
{reference, valid, materialClass, findings: [{code, severity (WARNING/SUSPICIOUS/
|
|
381
|
+
IMPOSSIBLE), component, reference, message, value, threshold}], skipped: ["RULE: reason"]}.
|
|
382
|
+
valid is False iff a finding is IMPOSSIBLE.
|
|
383
|
+
)pbdoc",
|
|
384
|
+
py::arg("material"), py::arg("extra_loss_points") = json::array(),
|
|
385
|
+
py::call_guard<py::gil_scoped_release>());
|
|
386
|
+
|
|
387
|
+
m.def("validate_all_materials", &validate_all_materials,
|
|
388
|
+
R"pbdoc(
|
|
389
|
+
Magnetic Blade Runner over a whole core-material catalogue.
|
|
390
|
+
|
|
391
|
+
Args:
|
|
392
|
+
core_materials_path: a core_materials.ndjson; empty = the catalogue built into MKF.
|
|
393
|
+
advanced_core_materials_path: optional advanced_core_materials.ndjson (real file,
|
|
394
|
+
not a git-LFS pointer) whose sinusoidal loss points are checked too.
|
|
395
|
+
|
|
396
|
+
Returns:
|
|
397
|
+
Summary {records, invalid, recordsWithSuspicious, unclassified, findingsByCode,
|
|
398
|
+
skippedByCode, provenance, impossible, suspicious} plus verdicts and classTable.
|
|
399
|
+
)pbdoc",
|
|
400
|
+
py::arg("core_materials_path") = "", py::arg("advanced_core_materials_path") = "",
|
|
401
|
+
py::call_guard<py::gil_scoped_release>());
|
|
402
|
+
|
|
337
403
|
m.def("get_core_losses_model_information", &get_core_losses_model_information,
|
|
338
404
|
R"pbdoc(
|
|
339
405
|
Get documentation and metadata for available core loss models.
|
|
@@ -7,6 +7,10 @@ namespace PyMKF {
|
|
|
7
7
|
// Core losses
|
|
8
8
|
json calculate_core_losses(json coreData, json coilData, json inputsData, json modelsData);
|
|
9
9
|
json get_core_losses_model_information(json material);
|
|
10
|
+
|
|
11
|
+
// Magnetic Blade Runner: physics validation of MAS core-material records
|
|
12
|
+
json validate_material(json material, json extraLossPoints);
|
|
13
|
+
json validate_all_materials(std::string coreMaterialsPath, std::string advancedCoreMaterialsPath);
|
|
10
14
|
json calculate_steinmetz_coefficients(json dataJson, json rangesJson);
|
|
11
15
|
json calculate_steinmetz_coefficients_with_error(json dataJson, json rangesJson);
|
|
12
16
|
|
|
@@ -193,6 +193,9 @@ json get_settings() {
|
|
|
193
193
|
// Memory kept for the turn-field sums of recently evaluated meshes (0 keeps nothing).
|
|
194
194
|
settingsJson["magneticFieldTurnSumsCacheBytes"] = OpenMagnetics::settings.get_magnetic_field_turn_sums_cache_bytes();
|
|
195
195
|
|
|
196
|
+
// Thermal network: a turn overlapping its enclosure throws (true) or loses that conduction path with an ERROR logged (false).
|
|
197
|
+
settingsJson["thermalNetworkStrictGeometry"] = OpenMagnetics::settings.get_thermal_network_strict_geometry();
|
|
198
|
+
|
|
196
199
|
// Models
|
|
197
200
|
{
|
|
198
201
|
json aux;
|
|
@@ -379,6 +382,9 @@ void set_settings(json settingsJson) {
|
|
|
379
382
|
if (settingsJson.contains("harmonicAmplitudeThreshold")) OpenMagnetics::settings.set_harmonic_amplitude_threshold(settingsJson["harmonicAmplitudeThreshold"]);
|
|
380
383
|
if (settingsJson.contains("magneticFieldTurnSumsCacheBytes")) OpenMagnetics::settings.set_magnetic_field_turn_sums_cache_bytes(settingsJson["magneticFieldTurnSumsCacheBytes"].get<size_t>());
|
|
381
384
|
|
|
385
|
+
// Thermal network
|
|
386
|
+
if (settingsJson.contains("thermalNetworkStrictGeometry")) OpenMagnetics::settings.set_thermal_network_strict_geometry(settingsJson["thermalNetworkStrictGeometry"]);
|
|
387
|
+
|
|
382
388
|
// Models
|
|
383
389
|
if (settingsJson.contains("magneticFieldStrengthModel")) {
|
|
384
390
|
OpenMagnetics::MagneticFieldStrengthModels model;
|
|
@@ -23,6 +23,31 @@ json simulate(json inputsJson, json magneticJson, json modelsData) {
|
|
|
23
23
|
return result;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
json build_datasheet(json inputsJson, json magneticJson, json modelsData) {
|
|
27
|
+
// Simulate the magnetic at the given operating points (the same models as simulate), then let
|
|
28
|
+
// MagneticSimulator::build_datasheet read the outputs into a manufacturer-style datasheet. Returns
|
|
29
|
+
// the magnetic's manufacturerInfo with datasheetInfo filled in; its other fields are kept.
|
|
30
|
+
OpenMagnetics::Inputs inputs(inputsJson);
|
|
31
|
+
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
32
|
+
|
|
33
|
+
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
34
|
+
find_reluctance_model(modelsData, reluctanceModelName);
|
|
35
|
+
auto coreLossesModelName = OpenMagnetics::defaults.coreLossesModelDefault;
|
|
36
|
+
if (!modelsData.is_null() && modelsData.find("coreLosses") != modelsData.end()) {
|
|
37
|
+
OpenMagnetics::from_json(modelsData["coreLosses"], coreLossesModelName);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
OpenMagnetics::MagneticSimulator magneticSimulator;
|
|
41
|
+
magneticSimulator.set_core_losses_model_name(coreLossesModelName);
|
|
42
|
+
magneticSimulator.set_reluctance_model_name(reluctanceModelName);
|
|
43
|
+
auto mas = magneticSimulator.simulate(inputs, magnetic);
|
|
44
|
+
auto manufacturerInfo = magneticSimulator.build_datasheet(mas);
|
|
45
|
+
|
|
46
|
+
json result;
|
|
47
|
+
to_json(result, manufacturerInfo);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
|
|
26
51
|
std::string export_magnetic_as_subcircuit(json magneticJson) {
|
|
27
52
|
// Returns the raw SPICE subcircuit netlist as a plain string. We must NOT
|
|
28
53
|
// return nlohmann::ordered_json here: pybind11_json registers a caster for
|
|
@@ -418,6 +443,27 @@ void register_simulation_bindings(py::module& m) {
|
|
|
418
443
|
)pbdoc",
|
|
419
444
|
py::call_guard<py::gil_scoped_release>());
|
|
420
445
|
|
|
446
|
+
m.def("build_datasheet", &build_datasheet,
|
|
447
|
+
R"pbdoc(
|
|
448
|
+
Build a manufacturer-style datasheet for a magnetic from MKF's own simulation.
|
|
449
|
+
|
|
450
|
+
Simulates the magnetic at the given operating points, then reads the results into
|
|
451
|
+
manufacturerInfo.datasheetInfo (MagneticSimulator::build_datasheet): inductance at the
|
|
452
|
+
first operating point, DC resistance, the heat-limited rated current (40 K rise), the
|
|
453
|
+
peak saturation current at the hottest operating point, self-resonant frequency and an
|
|
454
|
+
impedance sweep, thermal and mechanical data.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
inputs_json: MAS Inputs: the conditions the datasheet is stated at.
|
|
458
|
+
magnetic_json: the magnetic (core and coil).
|
|
459
|
+
models_json: models to simulate with, as for simulate().
|
|
460
|
+
|
|
461
|
+
Returns:
|
|
462
|
+
The magnetic's manufacturerInfo with datasheetInfo filled in.
|
|
463
|
+
)pbdoc",
|
|
464
|
+
py::arg("inputs_json"), py::arg("magnetic_json"), py::arg("models_json"),
|
|
465
|
+
py::call_guard<py::gil_scoped_release>());
|
|
466
|
+
|
|
421
467
|
m.def("export_magnetic_as_subcircuit", &export_magnetic_as_subcircuit,
|
|
422
468
|
R"pbdoc(
|
|
423
469
|
Export a magnetic component as a SPICE-compatible subcircuit.
|
|
@@ -142,3 +142,18 @@ def test_results_can_come_back_ranked_but_unsimulated(mixed_catalogue):
|
|
|
142
142
|
built = references_of(simulated).index("BUILT")
|
|
143
143
|
assert simulated["data"][built]["mas"].get("outputs"), "the default still simulates a modelled part"
|
|
144
144
|
assert not ranked["data"][built]["mas"].get("outputs")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_build_datasheet_states_what_mkf_simulates_for_a_modelled_part():
|
|
148
|
+
"""A modelled part with no datasheet gets one from MKF: the values a catalogue datasheet
|
|
149
|
+
states, computed at the given conditions, with the part's own manufacturerInfo kept."""
|
|
150
|
+
part = modelled_part("BUILT")
|
|
151
|
+
del part["manufacturerInfo"]["datasheetInfo"]
|
|
152
|
+
info = PyOpenMagnetics.build_datasheet(buck_inputs(), PyOpenMagnetics.magnetic_autocomplete(part, {}), {})
|
|
153
|
+
|
|
154
|
+
assert info["reference"] == "BUILT"
|
|
155
|
+
electrical = info["datasheetInfo"]["electrical"][0]
|
|
156
|
+
assert electrical["subtype"] == "inductor"
|
|
157
|
+
for key in ("inductance", "dcResistance", "ratedCurrents", "saturationCurrentPeak", "selfResonantFrequency"):
|
|
158
|
+
assert electrical.get(key) is not None, key
|
|
159
|
+
assert electrical["ratedCurrents"][0] > 0 and electrical["saturationCurrentPeak"] > 0
|
|
@@ -171,6 +171,22 @@ class TestSettingsManagement:
|
|
|
171
171
|
assert after_reset is not None
|
|
172
172
|
assert isinstance(after_reset, dict)
|
|
173
173
|
|
|
174
|
+
def test_thermal_network_strict_geometry_round_trip(self):
|
|
175
|
+
"""thermalNetworkStrictGeometry defaults to strict and round-trips through set_settings."""
|
|
176
|
+
PyOpenMagnetics.reset_settings()
|
|
177
|
+
try:
|
|
178
|
+
assert PyOpenMagnetics.get_settings()["thermalNetworkStrictGeometry"] is True
|
|
179
|
+
|
|
180
|
+
settings = PyOpenMagnetics.get_settings()
|
|
181
|
+
settings["thermalNetworkStrictGeometry"] = False
|
|
182
|
+
PyOpenMagnetics.set_settings(settings)
|
|
183
|
+
assert PyOpenMagnetics.get_settings()["thermalNetworkStrictGeometry"] is False
|
|
184
|
+
|
|
185
|
+
PyOpenMagnetics.reset_settings()
|
|
186
|
+
assert PyOpenMagnetics.get_settings()["thermalNetworkStrictGeometry"] is True
|
|
187
|
+
finally:
|
|
188
|
+
PyOpenMagnetics.reset_settings()
|
|
189
|
+
|
|
174
190
|
|
|
175
191
|
class TestBobbinCreation:
|
|
176
192
|
"""Test bobbin creation for cores."""
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"""Magnetic Blade Runner (MKF MaterialValidator) through the Python binding."""
|
|
2
|
+
import copy
|
|
3
|
+
|
|
4
|
+
import pytest
|
|
5
|
+
import PyOpenMagnetics
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _material(name):
|
|
9
|
+
return PyOpenMagnetics.get_material_data(name)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def test_real_power_ferrite_is_clean():
|
|
13
|
+
verdict = PyOpenMagnetics.validate_material(_material("N87"))
|
|
14
|
+
assert verdict["valid"] is True
|
|
15
|
+
assert verdict["findings"] == []
|
|
16
|
+
assert verdict["materialClass"] == "MnZn power ferrite"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_saturation_above_iron_is_impossible():
|
|
20
|
+
record = copy.deepcopy(_material("N87"))
|
|
21
|
+
record["name"] = "N87 Bs 3 T"
|
|
22
|
+
record["saturation"][0]["magneticFluxDensity"] = 3.0
|
|
23
|
+
verdict = PyOpenMagnetics.validate_material(record)
|
|
24
|
+
assert verdict["valid"] is False
|
|
25
|
+
assert any(f["code"] == "MAT_BSAT_CEILING" and f["severity"] == "IMPOSSIBLE" for f in verdict["findings"])
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_extra_points_hysteresis_bound():
|
|
29
|
+
points = [{"frequency": 1e3, "magneticFluxDensityPeak": 0.05, "temperature": 25,
|
|
30
|
+
"volumetricLosses": 80e3, "origin": "manufacturer"}]
|
|
31
|
+
verdict = PyOpenMagnetics.validate_material(_material("P63"), points)
|
|
32
|
+
assert any(f["code"] == "MAT_LOSS_HYSTERESIS_BOUND" for f in verdict["findings"])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def test_embedded_catalogue_sweep():
|
|
36
|
+
summary = PyOpenMagnetics.validate_all_materials()
|
|
37
|
+
assert summary["records"] > 1000
|
|
38
|
+
assert len(summary["verdicts"]) == summary["records"]
|
|
39
|
+
assert "findingsByCode" in summary
|
|
40
|
+
assert len(summary["classTable"]) > 0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|