PyOpenMagnetics 1.7.5__tar.gz → 1.7.7__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.5 → pyopenmagnetics-1.7.7}/.github/workflows/ci.yml +5 -1
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/.github/workflows/publish.yml +19 -8
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/CMakeLists.txt +1 -1
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/PKG-INFO +1 -1
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/pyproject.toml +1 -1
- pyopenmagnetics-1.7.7/src/common.h +114 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/core.cpp +12 -18
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/losses.cpp +2 -4
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/plotting.cpp +29 -3
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/simulation.cpp +4 -12
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/winding.cpp +14 -2
- pyopenmagnetics-1.7.5/src/common.h +0 -58
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/.gitignore +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/AGENTS.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/LICENSE +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/README.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/_packaging/__init__.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/_packaging/_build_info.py.in +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/api/MAS.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/api/validation.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/docs/errors.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/docs/performance.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/README.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/llms.txt +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/requirements.txt +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/advisers.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/cmc.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/cmc.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/converter.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/converter.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/core.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/crossref.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/crossref.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/database.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/database.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/logging.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/losses.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/module.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/plotting.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/settings.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/simulation.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/utils.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/winding.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/src/wire.h +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/test.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_magnetics_catalogue_load.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.7.5 → pyopenmagnetics-1.7.7}/tests/test_winding.py +0 -0
|
@@ -17,7 +17,11 @@ jobs:
|
|
|
17
17
|
fail-fast: false
|
|
18
18
|
matrix:
|
|
19
19
|
os: [ubuntu-22.04, windows-2022, macos-14]
|
|
20
|
-
|
|
20
|
+
# Track every CPython that publish.yml ships a wheel for (cp310-cp314),
|
|
21
|
+
# so a version we distribute is never untested. 3.14 was added after a
|
|
22
|
+
# Windows user on 3.14 hit the sdist fallback: no cp314 wheel existed,
|
|
23
|
+
# pip tried to compile the engine with nmake and failed.
|
|
24
|
+
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
21
25
|
|
|
22
26
|
steps:
|
|
23
27
|
- uses: actions/checkout@v4
|
|
@@ -26,15 +26,15 @@ jobs:
|
|
|
26
26
|
build_and_publish_linux:
|
|
27
27
|
name: Build and Publish Linux wheels (${{ matrix.python }})
|
|
28
28
|
runs-on: ubuntu-22.04
|
|
29
|
-
# Each matrix job builds a single CPython version. Building all
|
|
30
|
-
# (cp310-
|
|
29
|
+
# Each matrix job builds a single CPython version. Building them all
|
|
30
|
+
# (cp310-cp314) sequentially in one job exceeded the runner's window and
|
|
31
31
|
# was killed mid-compile (exit 143). One version per parallel job keeps
|
|
32
32
|
# each job short and well under any timeout.
|
|
33
33
|
timeout-minutes: 120
|
|
34
34
|
strategy:
|
|
35
35
|
fail-fast: false
|
|
36
36
|
matrix:
|
|
37
|
-
python: [cp310, cp311, cp312, cp313]
|
|
37
|
+
python: [cp310, cp311, cp312, cp313, cp314]
|
|
38
38
|
environment:
|
|
39
39
|
name: pypi
|
|
40
40
|
url: https://pypi.org/p/pyopenmagnetics
|
|
@@ -85,7 +85,7 @@ jobs:
|
|
|
85
85
|
done
|
|
86
86
|
|
|
87
87
|
- name: Install cibuildwheel
|
|
88
|
-
run: python -m pip install cibuildwheel==2.
|
|
88
|
+
run: python -m pip install cibuildwheel==4.2.0
|
|
89
89
|
|
|
90
90
|
- name: Pre-download virtualenv
|
|
91
91
|
shell: bash
|
|
@@ -154,7 +154,7 @@ jobs:
|
|
|
154
154
|
strategy:
|
|
155
155
|
fail-fast: false
|
|
156
156
|
matrix:
|
|
157
|
-
python: [cp310, cp311, cp312, cp313]
|
|
157
|
+
python: [cp310, cp311, cp312, cp313, cp314]
|
|
158
158
|
environment:
|
|
159
159
|
name: pypi
|
|
160
160
|
url: https://pypi.org/p/pyopenmagnetics
|
|
@@ -197,7 +197,7 @@ jobs:
|
|
|
197
197
|
run: npm install -g quicktype@23.2.6
|
|
198
198
|
|
|
199
199
|
- name: Install cibuildwheel
|
|
200
|
-
run: python -m pip install cibuildwheel==2.
|
|
200
|
+
run: python -m pip install cibuildwheel==4.2.0
|
|
201
201
|
|
|
202
202
|
- name: Build wheels
|
|
203
203
|
run: python -m cibuildwheel --output-dir wheelhouse
|
|
@@ -206,6 +206,17 @@ jobs:
|
|
|
206
206
|
CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
|
|
207
207
|
CIBW_BEFORE_ALL_WINDOWS: git config --global url."https://github.com/".insteadOf "git@github.com:" && npm install -g quicktype@23.2.6
|
|
208
208
|
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core "cmake<4" ninja pybind11
|
|
209
|
+
# cibuildwheel 3.0 started running `delvewheel repair` on Windows by
|
|
210
|
+
# default; 2.21.3 did no Windows repair at all. delvewheel resolves an
|
|
211
|
+
# extension's dependent DLLs via PATH, and kirchhoffapi.dll is built by
|
|
212
|
+
# the Kirchhoff ExternalProject and shipped INSIDE the package rather
|
|
213
|
+
# than installed system-wide -- so the repair aborts with "Unable to
|
|
214
|
+
# find library: kirchhoffapi.dll" and every Windows wheel fails,
|
|
215
|
+
# cp310 included. Empty = keep the behaviour that shipped every
|
|
216
|
+
# Windows wheel up to 1.7.5; scikit-build-core already places the DLL
|
|
217
|
+
# next to the extension. Bundling it via delvewheel --add-path is the
|
|
218
|
+
# follow-up, not a prerequisite for 3.14 support.
|
|
219
|
+
CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: ""
|
|
209
220
|
CIBW_TEST_COMMAND: python -c "import PyOpenMagnetics; print(f'PyOpenMagnetics loaded successfully with {len(dir(PyOpenMagnetics))} functions')"
|
|
210
221
|
|
|
211
222
|
- name: Install twine
|
|
@@ -234,7 +245,7 @@ jobs:
|
|
|
234
245
|
strategy:
|
|
235
246
|
fail-fast: false
|
|
236
247
|
matrix:
|
|
237
|
-
python: [cp310, cp311, cp312, cp313]
|
|
248
|
+
python: [cp310, cp311, cp312, cp313, cp314]
|
|
238
249
|
environment:
|
|
239
250
|
name: pypi
|
|
240
251
|
url: https://pypi.org/p/pyopenmagnetics
|
|
@@ -282,7 +293,7 @@ jobs:
|
|
|
282
293
|
done
|
|
283
294
|
|
|
284
295
|
- name: Install cibuildwheel
|
|
285
|
-
run: python -m pip install cibuildwheel==2.
|
|
296
|
+
run: python -m pip install cibuildwheel==4.2.0
|
|
286
297
|
|
|
287
298
|
- name: Build wheels
|
|
288
299
|
run: python -m cibuildwheel --output-dir wheelhouse
|
|
@@ -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-
|
|
145
|
+
set(MKF_FORCE_REFRESH "2026-08-26-mkf-10abe8d7")
|
|
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
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <pybind11/pybind11.h>
|
|
4
|
+
#include <pybind11/stl.h>
|
|
5
|
+
#include "pybind11_json/pybind11_json.hpp"
|
|
6
|
+
#include <magic_enum.hpp>
|
|
7
|
+
#include <algorithm>
|
|
8
|
+
#include <stdexcept>
|
|
9
|
+
#include "json.hpp"
|
|
10
|
+
|
|
11
|
+
#include <MAS.hpp>
|
|
12
|
+
#include "advisers/MagneticAdviser.h"
|
|
13
|
+
#include "constructive_models/Bobbin.h"
|
|
14
|
+
#include "constructive_models/Coil.h"
|
|
15
|
+
#include "constructive_models/Core.h"
|
|
16
|
+
#include "constructive_models/Insulation.h"
|
|
17
|
+
#include "constructive_models/Magnetic.h"
|
|
18
|
+
#include "constructive_models/Mas.h"
|
|
19
|
+
#include "constructive_models/NumberTurns.h"
|
|
20
|
+
#include "constructive_models/Wire.h"
|
|
21
|
+
#include "physical_models/InitialPermeability.h"
|
|
22
|
+
#include "physical_models/MagneticEnergy.h"
|
|
23
|
+
#include "physical_models/Reluctance.h"
|
|
24
|
+
#include "physical_models/Temperature.h"
|
|
25
|
+
#include "physical_models/ThermalResistance.h"
|
|
26
|
+
#include "physical_models/MagnetizingInductance.h"
|
|
27
|
+
#include "physical_models/CoreLosses.h"
|
|
28
|
+
#include "physical_models/Resistivity.h"
|
|
29
|
+
#include "physical_models/CoreTemperature.h"
|
|
30
|
+
#include "physical_models/LeakageInductance.h"
|
|
31
|
+
#include "physical_models/Inductance.h"
|
|
32
|
+
#include "physical_models/StrayCapacitance.h"
|
|
33
|
+
#include "physical_models/WindingOhmicLosses.h"
|
|
34
|
+
#include "physical_models/WindingSkinEffectLosses.h"
|
|
35
|
+
#include "physical_models/WindingLosses.h"
|
|
36
|
+
#include "processors/Inputs.h"
|
|
37
|
+
#include "processors/MagneticSimulator.h"
|
|
38
|
+
#include "processors/CircuitSimulatorInterface.h"
|
|
39
|
+
#include "processors/Sweeper.h"
|
|
40
|
+
#include "support/Painter.h"
|
|
41
|
+
#include "support/Utils.h"
|
|
42
|
+
|
|
43
|
+
using namespace MAS;
|
|
44
|
+
using json = nlohmann::json;
|
|
45
|
+
using ordered_json = nlohmann::ordered_json;
|
|
46
|
+
|
|
47
|
+
namespace py = pybind11;
|
|
48
|
+
|
|
49
|
+
#define STRINGIFY(x) #x
|
|
50
|
+
#define MACRO_STRINGIFY(x) STRINGIFY(x)
|
|
51
|
+
|
|
52
|
+
namespace PyMKF {
|
|
53
|
+
|
|
54
|
+
// THE MODELS DICT NAMES THE GAP-RELUCTANCE MODEL UNDER TWO SPELLINGS, AND BOTH ARE REAL.
|
|
55
|
+
// Every Python entry point here has always read "reluctance", settings.cpp publishes that key as
|
|
56
|
+
// the default, and it stays the documented name. MKF's own C++ spells the same choice
|
|
57
|
+
// "gapReluctance" -- CoreCrossReferencer, MagneticAdviser, MagneticFilter, MagneticFilterLosses
|
|
58
|
+
// and CoreAdviserPipeline all use that -- so anyone moving between the two layers reaches for it
|
|
59
|
+
// naturally. It used to be accepted and silently dropped: the key was never looked at, the call
|
|
60
|
+
// fell back to defaults.reluctanceModelDefault, and a sweep over all nine models returned nine
|
|
61
|
+
// identical numbers, which reads as "the model does not matter for this core" rather than "the
|
|
62
|
+
// argument was ignored" (heimdall ABT #952 -- it cost a full pass of a real investigation).
|
|
63
|
+
//
|
|
64
|
+
// Accept both, "reluctance" first so the documented key wins if a caller somehow sets both.
|
|
65
|
+
// Unknown KEYS are still ignored on purpose: one dict legitimately carries several subsystems'
|
|
66
|
+
// choices ("coreLosses" alongside this one), so a strict reader would break valid callers.
|
|
67
|
+
// An unknown model NAME does throw, and now says so -- magic_enum's .value() on an empty
|
|
68
|
+
// optional threw a bare "bad optional access" that named neither the key nor the offending value.
|
|
69
|
+
inline bool find_reluctance_model(const std::map<std::string, std::string>& models,
|
|
70
|
+
OpenMagnetics::ReluctanceModels& reluctanceModelName) {
|
|
71
|
+
for (const char* key : {"reluctance", "gapReluctance"}) {
|
|
72
|
+
auto entry = models.find(key);
|
|
73
|
+
if (entry == models.end()) {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
std::string modelNameJsonUpper = entry->second;
|
|
77
|
+
std::transform(modelNameJsonUpper.begin(), modelNameJsonUpper.end(),
|
|
78
|
+
modelNameJsonUpper.begin(), ::toupper);
|
|
79
|
+
auto parsed = magic_enum::enum_cast<OpenMagnetics::ReluctanceModels>(modelNameJsonUpper);
|
|
80
|
+
if (!parsed.has_value()) {
|
|
81
|
+
std::string known;
|
|
82
|
+
for (auto name : magic_enum::enum_names<OpenMagnetics::ReluctanceModels>()) {
|
|
83
|
+
known += (known.empty() ? "" : ", ") + std::string(name);
|
|
84
|
+
}
|
|
85
|
+
throw std::runtime_error("Unknown reluctance model '" + entry->second + "' given as '"
|
|
86
|
+
+ std::string(key) + "'. Known models: " + known);
|
|
87
|
+
}
|
|
88
|
+
reluctanceModelName = parsed.value();
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
inline bool find_reluctance_model(const json& modelsData,
|
|
95
|
+
OpenMagnetics::ReluctanceModels& reluctanceModelName) {
|
|
96
|
+
if (modelsData.is_null() || !modelsData.is_object()) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
std::map<std::string, std::string> models;
|
|
100
|
+
for (auto& [key, value] : modelsData.items()) {
|
|
101
|
+
if (value.is_string()) {
|
|
102
|
+
models[key] = value.get<std::string>();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return find_reluctance_model(models, reluctanceModelName);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Global database declaration - defined in module.cpp
|
|
109
|
+
extern std::map<std::string, OpenMagnetics::Mas> masDatabase;
|
|
110
|
+
|
|
111
|
+
// Get the directory containing the PyOpenMagnetics module
|
|
112
|
+
std::string get_module_path();
|
|
113
|
+
|
|
114
|
+
} // namespace PyMKF
|
|
@@ -290,11 +290,7 @@ double calculate_inductance_from_number_turns_and_gapping(json coreData, json co
|
|
|
290
290
|
std::map<std::string, std::string> models = modelsData.get<std::map<std::string, std::string>>();
|
|
291
291
|
|
|
292
292
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
293
|
-
|
|
294
|
-
std::string modelNameJsonUpper = models["reluctance"];
|
|
295
|
-
std::transform(modelNameJsonUpper.begin(), modelNameJsonUpper.end(), modelNameJsonUpper.begin(), ::toupper);
|
|
296
|
-
reluctanceModelName = magic_enum::enum_cast<OpenMagnetics::ReluctanceModels>(modelNameJsonUpper).value();
|
|
297
|
-
}
|
|
293
|
+
find_reluctance_model(models, reluctanceModelName);
|
|
298
294
|
|
|
299
295
|
OpenMagnetics::MagnetizingInductance magnetizingInductanceObj(reluctanceModelName);
|
|
300
296
|
double magnetizingInductance = magnetizingInductanceObj.calculate_inductance_from_number_turns_and_gapping(core, coil, &operatingPoint).get_magnetizing_inductance().get_nominal().value();
|
|
@@ -310,9 +306,7 @@ double calculate_number_turns_from_gapping_and_inductance(json coreData, json co
|
|
|
310
306
|
std::map<std::string, std::string> models = modelsData.get<std::map<std::string, std::string>>();
|
|
311
307
|
|
|
312
308
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
313
|
-
|
|
314
|
-
OpenMagnetics::from_json(models["reluctance"], reluctanceModelName);
|
|
315
|
-
}
|
|
309
|
+
find_reluctance_model(models, reluctanceModelName);
|
|
316
310
|
|
|
317
311
|
OpenMagnetics::MagnetizingInductance magnetizingInductanceObj(reluctanceModelName);
|
|
318
312
|
double numberTurns = magnetizingInductanceObj.calculate_number_turns_from_gapping_and_inductance(core, coil, &inputs);
|
|
@@ -331,9 +325,7 @@ double calculate_number_turns_from_gapping_and_inductance_legacy(json coreData,
|
|
|
331
325
|
std::map<std::string, std::string> models = modelsData.get<std::map<std::string, std::string>>();
|
|
332
326
|
|
|
333
327
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
334
|
-
|
|
335
|
-
OpenMagnetics::from_json(models["reluctance"], reluctanceModelName);
|
|
336
|
-
}
|
|
328
|
+
find_reluctance_model(models, reluctanceModelName);
|
|
337
329
|
|
|
338
330
|
OpenMagnetics::MagnetizingInductance magnetizingInductanceObj(reluctanceModelName);
|
|
339
331
|
double numberTurns = magnetizingInductanceObj.calculate_number_turns_from_gapping_and_inductance(core, &inputs);
|
|
@@ -351,9 +343,7 @@ json calculate_gapping_from_number_turns_and_inductance(json coreData, json coil
|
|
|
351
343
|
OpenMagnetics::GappingType gappingType = magic_enum::enum_cast<OpenMagnetics::GappingType>(gappingTypeJson).value();
|
|
352
344
|
|
|
353
345
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
354
|
-
|
|
355
|
-
OpenMagnetics::from_json(models["reluctance"], reluctanceModelName);
|
|
356
|
-
}
|
|
346
|
+
find_reluctance_model(models, reluctanceModelName);
|
|
357
347
|
|
|
358
348
|
OpenMagnetics::MagnetizingInductance magnetizingInductanceObj(reluctanceModelName);
|
|
359
349
|
std::vector<CoreGap> gapping = magnetizingInductanceObj.calculate_gapping_from_number_turns_and_inductance(core, coil, &inputs, gappingType, decimals);
|
|
@@ -1003,7 +993,8 @@ void register_core_bindings(py::module& m) {
|
|
|
1003
993
|
core_data: JSON object with core specification.
|
|
1004
994
|
coil_data: JSON object with coil specification (for turns).
|
|
1005
995
|
operating_point_data: JSON operating point for DC bias consideration.
|
|
1006
|
-
models_data: JSON dict with "reluctance"
|
|
996
|
+
models_data: JSON dict with "reluctance" (or MKF's own spelling,
|
|
997
|
+
"gapReluctance" -- both accepted) model selection.
|
|
1007
998
|
|
|
1008
999
|
Returns:
|
|
1009
1000
|
Magnetizing inductance in Henries.
|
|
@@ -1021,7 +1012,8 @@ void register_core_bindings(py::module& m) {
|
|
|
1021
1012
|
core_data: JSON object with core specification.
|
|
1022
1013
|
coil_data: JSON object with coil/winding specification.
|
|
1023
1014
|
inputs_data: JSON Inputs with magnetizingInductance requirement.
|
|
1024
|
-
models_data: JSON dict with "reluctance"
|
|
1015
|
+
models_data: JSON dict with "reluctance" (or MKF's own spelling,
|
|
1016
|
+
"gapReluctance" -- both accepted) model selection.
|
|
1025
1017
|
|
|
1026
1018
|
Returns:
|
|
1027
1019
|
Required number of turns (may be non-integer).
|
|
@@ -1043,7 +1035,8 @@ void register_core_bindings(py::module& m) {
|
|
|
1043
1035
|
Args:
|
|
1044
1036
|
core_data: JSON object with core specification.
|
|
1045
1037
|
inputs_data: JSON Inputs with magnetizingInductance requirement.
|
|
1046
|
-
models_data: JSON dict with "reluctance"
|
|
1038
|
+
models_data: JSON dict with "reluctance" (or MKF's own spelling,
|
|
1039
|
+
"gapReluctance" -- both accepted) model selection.
|
|
1047
1040
|
|
|
1048
1041
|
Returns:
|
|
1049
1042
|
Required number of turns (may be non-integer).
|
|
@@ -1063,7 +1056,8 @@ void register_core_bindings(py::module& m) {
|
|
|
1063
1056
|
inputs_data: JSON Inputs with magnetizingInductance requirement.
|
|
1064
1057
|
gapping_type_json: Gap type ("SUBTRACTIVE", "ADDITIVE", "DISTRIBUTED").
|
|
1065
1058
|
decimals: Precision in decimal places for gap length.
|
|
1066
|
-
models_data: JSON dict with "reluctance"
|
|
1059
|
+
models_data: JSON dict with "reluctance" (or MKF's own spelling,
|
|
1060
|
+
"gapReluctance" -- both accepted) model selection.
|
|
1067
1061
|
|
|
1068
1062
|
Returns:
|
|
1069
1063
|
JSON Core object with updated gapping configuration.
|
|
@@ -18,9 +18,7 @@ json calculate_core_losses(json coreData, json coilData, json inputsData, json m
|
|
|
18
18
|
std::map<std::string, std::string> models = modelsData.get<std::map<std::string, std::string>>();
|
|
19
19
|
|
|
20
20
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
21
|
-
|
|
22
|
-
OpenMagnetics::from_json(models["reluctance"], reluctanceModelName);
|
|
23
|
-
}
|
|
21
|
+
find_reluctance_model(models, reluctanceModelName);
|
|
24
22
|
auto coreLossesModelName = OpenMagnetics::defaults.coreLossesModelDefault;
|
|
25
23
|
if (models.find("coreLosses") != models.end()) {
|
|
26
24
|
OpenMagnetics::from_json(models["coreLosses"], coreLossesModelName);
|
|
@@ -315,7 +313,7 @@ void register_losses_bindings(py::module& m) {
|
|
|
315
313
|
inputs_data: JSON object with operating points (frequency, flux density).
|
|
316
314
|
models_data: JSON dict specifying models to use:
|
|
317
315
|
- "coreLosses": "STEINMETZ", "IGSE", "MSE", "BARG", "ROSHEN", "PROPRIETARY"
|
|
318
|
-
- "reluctance": "ZHANG", "MUEHLETHALER", "PARTRIDGE", "STENGLEIN"
|
|
316
|
+
- "reluctance" (alias "gapReluctance"): "ZHANG", "MUEHLETHALER", "PARTRIDGE", "STENGLEIN"
|
|
319
317
|
|
|
320
318
|
Returns:
|
|
321
319
|
JSON object containing:
|
|
@@ -320,14 +320,40 @@ json plot_temperature_field(json magneticJson, json operatingPointJson, std::str
|
|
|
320
320
|
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
321
321
|
OperatingPoint operatingPoint(operatingPointJson);
|
|
322
322
|
|
|
323
|
-
|
|
323
|
+
// For toroidal cores, ensure the coil is wound (turn nodes need positions)
|
|
324
|
+
if (magnetic.get_core().get_shape_family() == CoreShapeFamily::T) {
|
|
325
|
+
auto coil = magnetic.get_mutable_coil();
|
|
326
|
+
if (!coil.get_turns_description() || coil.get_turns_description()->empty()) {
|
|
327
|
+
coil.wind();
|
|
328
|
+
magnetic.set_coil(coil);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
double ambientTemp = operatingPoint.get_conditions().get_ambient_temperature();
|
|
333
|
+
|
|
334
|
+
// Simulate the operating point to get the losses that power the thermal
|
|
335
|
+
// network — a default-constructed Temperature(magnetic) solves an UNPOWERED
|
|
336
|
+
// network at the default 25 C ambient and paints everything stone cold
|
|
337
|
+
// (ABT #906). This mirrors the WASM wrapper in WebLibMKF.
|
|
338
|
+
OpenMagnetics::MagneticSimulator magneticSimulator;
|
|
339
|
+
OpenMagnetics::Mas mas;
|
|
340
|
+
mas.set_magnetic(magnetic);
|
|
341
|
+
mas.get_mutable_inputs().set_operating_points({operatingPoint});
|
|
342
|
+
auto simulatedMas = magneticSimulator.simulate(mas);
|
|
343
|
+
if (simulatedMas.get_outputs().empty()) {
|
|
344
|
+
throw std::runtime_error("plot_temperature_field: simulation produced no outputs");
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Shared config builder (ABT #906): the same configuration MagneticSimulator uses
|
|
348
|
+
// for outputs[].temperature, so this plot and the exported MAS can never disagree.
|
|
349
|
+
auto config = OpenMagnetics::TemperatureConfig::fromSimulatedOutput(operatingPoint, simulatedMas.get_outputs()[0]);
|
|
350
|
+
|
|
351
|
+
OpenMagnetics::Temperature temperatureModel(magnetic, config);
|
|
324
352
|
auto thermalResult = temperatureModel.calculateTemperatures();
|
|
325
353
|
|
|
326
354
|
std::filesystem::path filePath = outputPath.empty()
|
|
327
355
|
? std::filesystem::temp_directory_path() / "pyom_plot_temperature_field.svg"
|
|
328
356
|
: std::filesystem::path(outputPath);
|
|
329
|
-
|
|
330
|
-
double ambientTemp = operatingPoint.get_conditions().get_ambient_temperature();
|
|
331
357
|
OpenMagnetics::Painter painter(filePath);
|
|
332
358
|
painter.paint_temperature_field(magnetic, thermalResult.nodeTemperatures, true,
|
|
333
359
|
OpenMagnetics::ColorPalette::BLUE_TO_RED, ambientTemp, textColor, bgColor);
|
|
@@ -7,9 +7,7 @@ json simulate(json inputsJson, json magneticJson, json modelsData) {
|
|
|
7
7
|
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
8
8
|
|
|
9
9
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
10
|
-
|
|
11
|
-
OpenMagnetics::from_json(modelsData["reluctance"], reluctanceModelName);
|
|
12
|
-
}
|
|
10
|
+
find_reluctance_model(modelsData, reluctanceModelName);
|
|
13
11
|
auto coreLossesModelName = OpenMagnetics::defaults.coreLossesModelDefault;
|
|
14
12
|
if (!modelsData.is_null() && modelsData.find("coreLosses") != modelsData.end()) {
|
|
15
13
|
OpenMagnetics::from_json(modelsData["coreLosses"], coreLossesModelName);
|
|
@@ -145,9 +143,7 @@ json calculate_inductance_matrix(json magneticJson, double frequency, json model
|
|
|
145
143
|
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
146
144
|
|
|
147
145
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
148
|
-
|
|
149
|
-
OpenMagnetics::from_json(modelsData["reluctance"], reluctanceModelName);
|
|
150
|
-
}
|
|
146
|
+
find_reluctance_model(modelsData, reluctanceModelName);
|
|
151
147
|
|
|
152
148
|
OpenMagnetics::Inductance inductance(reluctanceModelName);
|
|
153
149
|
auto inductanceMatrix = inductance.calculate_inductance_matrix(magnetic, frequency);
|
|
@@ -313,9 +309,7 @@ json calculate_coupling_coefficient_matrix(json magneticJson, double frequency,
|
|
|
313
309
|
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
314
310
|
|
|
315
311
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
316
|
-
|
|
317
|
-
OpenMagnetics::from_json(modelsData["reluctance"], reluctanceModelName);
|
|
318
|
-
}
|
|
312
|
+
find_reluctance_model(modelsData, reluctanceModelName);
|
|
319
313
|
|
|
320
314
|
OpenMagnetics::Inductance inductance(reluctanceModelName);
|
|
321
315
|
|
|
@@ -352,9 +346,7 @@ json calculate_leakage_inductance_matrix(json magneticJson, double frequency, js
|
|
|
352
346
|
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
353
347
|
|
|
354
348
|
auto reluctanceModelName = OpenMagnetics::defaults.reluctanceModelDefault;
|
|
355
|
-
|
|
356
|
-
OpenMagnetics::from_json(modelsData["reluctance"], reluctanceModelName);
|
|
357
|
-
}
|
|
349
|
+
find_reluctance_model(modelsData, reluctanceModelName);
|
|
358
350
|
|
|
359
351
|
OpenMagnetics::Inductance inductance(reluctanceModelName);
|
|
360
352
|
auto leakageInductanceMatrix = inductance.calculate_leakage_inductance_matrix(magnetic, frequency);
|
|
@@ -117,7 +117,14 @@ json wind(json coilJson, size_t repetitions, json proportionPerWindingJson, json
|
|
|
117
117
|
}
|
|
118
118
|
|
|
119
119
|
if (!coil.get_turns_description()) {
|
|
120
|
-
|
|
120
|
+
// ABT #930: "Turns not created" on its own cannot be acted on — it reads as a broken
|
|
121
|
+
// winder even when MKF is correctly refusing an impossible geometry (a 0.36 mm conductor
|
|
122
|
+
// asked into a 0.10 mm annulus). MKF now records why; pass it on.
|
|
123
|
+
const auto& reason = coil.get_last_fit_failure();
|
|
124
|
+
throw std::runtime_error(reason.empty()
|
|
125
|
+
? std::string("Turns not created: the winding does not fit its window, and the reason "
|
|
126
|
+
"could not be narrowed further.")
|
|
127
|
+
: "Turns not created. " + reason);
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
json result;
|
|
@@ -136,7 +143,12 @@ json wind_planar(json coilJson, json stackUpJson, double borderToWireDistance, j
|
|
|
136
143
|
coil.wind_planar(stackUp, borderToWireDistance, wireToWireDistance, insulationThickness, coreToLayerDistance);
|
|
137
144
|
|
|
138
145
|
if (!coil.get_turns_description()) {
|
|
139
|
-
|
|
146
|
+
// ABT #930: same as wind() above — say why, not just that.
|
|
147
|
+
const auto& reason = coil.get_last_fit_failure();
|
|
148
|
+
throw std::runtime_error(reason.empty()
|
|
149
|
+
? std::string("Turns not created: the planar winding does not fit its window, and the "
|
|
150
|
+
"reason could not be narrowed further.")
|
|
151
|
+
: "Turns not created. " + reason);
|
|
140
152
|
}
|
|
141
153
|
|
|
142
154
|
return coil;
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
#pragma once
|
|
2
|
-
|
|
3
|
-
#include <pybind11/pybind11.h>
|
|
4
|
-
#include <pybind11/stl.h>
|
|
5
|
-
#include "pybind11_json/pybind11_json.hpp"
|
|
6
|
-
#include <magic_enum.hpp>
|
|
7
|
-
#include "json.hpp"
|
|
8
|
-
|
|
9
|
-
#include <MAS.hpp>
|
|
10
|
-
#include "advisers/MagneticAdviser.h"
|
|
11
|
-
#include "constructive_models/Bobbin.h"
|
|
12
|
-
#include "constructive_models/Coil.h"
|
|
13
|
-
#include "constructive_models/Core.h"
|
|
14
|
-
#include "constructive_models/Insulation.h"
|
|
15
|
-
#include "constructive_models/Magnetic.h"
|
|
16
|
-
#include "constructive_models/Mas.h"
|
|
17
|
-
#include "constructive_models/NumberTurns.h"
|
|
18
|
-
#include "constructive_models/Wire.h"
|
|
19
|
-
#include "physical_models/InitialPermeability.h"
|
|
20
|
-
#include "physical_models/MagneticEnergy.h"
|
|
21
|
-
#include "physical_models/Reluctance.h"
|
|
22
|
-
#include "physical_models/Temperature.h"
|
|
23
|
-
#include "physical_models/ThermalResistance.h"
|
|
24
|
-
#include "physical_models/MagnetizingInductance.h"
|
|
25
|
-
#include "physical_models/CoreLosses.h"
|
|
26
|
-
#include "physical_models/Resistivity.h"
|
|
27
|
-
#include "physical_models/CoreTemperature.h"
|
|
28
|
-
#include "physical_models/LeakageInductance.h"
|
|
29
|
-
#include "physical_models/Inductance.h"
|
|
30
|
-
#include "physical_models/StrayCapacitance.h"
|
|
31
|
-
#include "physical_models/WindingOhmicLosses.h"
|
|
32
|
-
#include "physical_models/WindingSkinEffectLosses.h"
|
|
33
|
-
#include "physical_models/WindingLosses.h"
|
|
34
|
-
#include "processors/Inputs.h"
|
|
35
|
-
#include "processors/MagneticSimulator.h"
|
|
36
|
-
#include "processors/CircuitSimulatorInterface.h"
|
|
37
|
-
#include "processors/Sweeper.h"
|
|
38
|
-
#include "support/Painter.h"
|
|
39
|
-
#include "support/Utils.h"
|
|
40
|
-
|
|
41
|
-
using namespace MAS;
|
|
42
|
-
using json = nlohmann::json;
|
|
43
|
-
using ordered_json = nlohmann::ordered_json;
|
|
44
|
-
|
|
45
|
-
namespace py = pybind11;
|
|
46
|
-
|
|
47
|
-
#define STRINGIFY(x) #x
|
|
48
|
-
#define MACRO_STRINGIFY(x) STRINGIFY(x)
|
|
49
|
-
|
|
50
|
-
namespace PyMKF {
|
|
51
|
-
|
|
52
|
-
// Global database declaration - defined in module.cpp
|
|
53
|
-
extern std::map<std::string, OpenMagnetics::Mas> masDatabase;
|
|
54
|
-
|
|
55
|
-
// Get the directory containing the PyOpenMagnetics module
|
|
56
|
-
std::string get_module_path();
|
|
57
|
-
|
|
58
|
-
} // namespace PyMKF
|
|
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
|