PyOpenMagnetics 1.3.12__tar.gz → 1.3.13__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.3.12 → pyopenmagnetics-1.3.13}/.github/workflows/publish.yml +16 -11
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/CMakeLists.txt +14 -10
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/PKG-INFO +1 -1
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/pyproject.toml +1 -1
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/converter.cpp +109 -32
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/settings.cpp +6 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/.gitignore +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/AGENTS.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/LICENSE +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/README.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/api/MAS.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/api/validation.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/docs/errors.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/docs/performance.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/README.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/llms.txt +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/requirements.txt +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/advisers.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/common.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/converter.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/core.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/core.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/database.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/database.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/logging.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/losses.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/module.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/plotting.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/settings.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/simulation.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/utils.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/winding.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/src/wire.h +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/test.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.3.12 → pyopenmagnetics-1.3.13}/tests/test_winding.py +0 -0
|
@@ -10,6 +10,11 @@ on:
|
|
|
10
10
|
required: false
|
|
11
11
|
default: false
|
|
12
12
|
type: boolean
|
|
13
|
+
publish_to_pypi:
|
|
14
|
+
description: 'Publish to real PyPI instead of TestPyPI (use to rebuild a failed release without recreating the GitHub release/tag)'
|
|
15
|
+
required: false
|
|
16
|
+
default: false
|
|
17
|
+
type: boolean
|
|
13
18
|
|
|
14
19
|
env:
|
|
15
20
|
GIT_LFS_SKIP_SMUDGE: 1 # Skip LFS to avoid bandwidth quota issues
|
|
@@ -109,18 +114,18 @@ jobs:
|
|
|
109
114
|
if [ $i -lt 3 ]; then sleep 60; fi
|
|
110
115
|
done
|
|
111
116
|
CIBW_ENVIRONMENT_LINUX: PATH=/opt/rh/gcc-toolset-13/root/usr/bin:$PATH LD_LIBRARY_PATH=/opt/rh/gcc-toolset-13/root/usr/lib64:$LD_LIBRARY_PATH
|
|
112
|
-
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core cmake ninja pybind11
|
|
117
|
+
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core "cmake<4" ninja pybind11
|
|
113
118
|
CIBW_TEST_COMMAND: python -c "import PyOpenMagnetics; print(f'PyOpenMagnetics loaded successfully with {len(dir(PyOpenMagnetics))} functions')"
|
|
114
119
|
|
|
115
120
|
- name: Publish Linux wheels to PyPI
|
|
116
|
-
if: github.event_name == 'release'
|
|
121
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi)
|
|
117
122
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
118
123
|
with:
|
|
119
124
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
120
125
|
packages-dir: wheelhouse/
|
|
121
126
|
|
|
122
127
|
- name: Publish Linux wheels to TestPyPI
|
|
123
|
-
if: github.event_name == 'workflow_dispatch'
|
|
128
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.publish_to_pypi
|
|
124
129
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
125
130
|
with:
|
|
126
131
|
repository-url: https://test.pypi.org/legacy/
|
|
@@ -181,21 +186,21 @@ jobs:
|
|
|
181
186
|
CIBW_BUILD: cp310-* cp311-* cp312-* cp313-*
|
|
182
187
|
CIBW_SKIP: "*-win32 *-manylinux_i686 *musllinux* pp*"
|
|
183
188
|
CIBW_BEFORE_ALL_WINDOWS: npm install -g quicktype
|
|
184
|
-
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core cmake ninja pybind11
|
|
189
|
+
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core "cmake<4" ninja pybind11
|
|
185
190
|
CIBW_TEST_COMMAND: python -c "import PyOpenMagnetics; print(f'PyOpenMagnetics loaded successfully with {len(dir(PyOpenMagnetics))} functions')"
|
|
186
191
|
|
|
187
192
|
- name: Install twine
|
|
188
193
|
run: python -m pip install twine
|
|
189
194
|
|
|
190
195
|
- name: Publish Windows wheels to PyPI
|
|
191
|
-
if: github.event_name == 'release'
|
|
196
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi)
|
|
192
197
|
run: python -m twine upload wheelhouse/*.whl
|
|
193
198
|
env:
|
|
194
199
|
TWINE_USERNAME: __token__
|
|
195
200
|
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
196
201
|
|
|
197
202
|
- name: Publish Windows wheels to TestPyPI
|
|
198
|
-
if: github.event_name == 'workflow_dispatch'
|
|
203
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.publish_to_pypi
|
|
199
204
|
run: python -m twine upload --repository-url https://test.pypi.org/legacy/ wheelhouse/*.whl
|
|
200
205
|
env:
|
|
201
206
|
TWINE_USERNAME: __token__
|
|
@@ -266,21 +271,21 @@ jobs:
|
|
|
266
271
|
if [ $i -lt 5 ]; then sleep 60; fi
|
|
267
272
|
done
|
|
268
273
|
CIBW_ENVIRONMENT_MACOS: MACOSX_DEPLOYMENT_TARGET=15.0
|
|
269
|
-
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core cmake ninja pybind11
|
|
274
|
+
CIBW_BEFORE_BUILD: pip install --default-timeout=60 --retries 5 scikit-build-core "cmake<4" ninja pybind11
|
|
270
275
|
CIBW_TEST_COMMAND: python -c "import PyOpenMagnetics; print(f'PyOpenMagnetics loaded successfully with {len(dir(PyOpenMagnetics))} functions')"
|
|
271
276
|
|
|
272
277
|
- name: Install twine
|
|
273
278
|
run: python -m pip install twine
|
|
274
279
|
|
|
275
280
|
- name: Publish macOS wheels to PyPI
|
|
276
|
-
if: github.event_name == 'release'
|
|
281
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi)
|
|
277
282
|
run: python -m twine upload wheelhouse/*.whl
|
|
278
283
|
env:
|
|
279
284
|
TWINE_USERNAME: __token__
|
|
280
285
|
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
|
|
281
286
|
|
|
282
287
|
- name: Publish macOS wheels to TestPyPI
|
|
283
|
-
if: github.event_name == 'workflow_dispatch'
|
|
288
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.publish_to_pypi
|
|
284
289
|
run: python -m twine upload --repository-url https://test.pypi.org/legacy/ wheelhouse/*.whl
|
|
285
290
|
env:
|
|
286
291
|
TWINE_USERNAME: __token__
|
|
@@ -338,14 +343,14 @@ jobs:
|
|
|
338
343
|
run: python -m build --sdist
|
|
339
344
|
|
|
340
345
|
- name: Publish sdist to PyPI
|
|
341
|
-
if: github.event_name == 'release'
|
|
346
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.publish_to_pypi)
|
|
342
347
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
343
348
|
with:
|
|
344
349
|
password: ${{ secrets.PYPI_API_TOKEN }}
|
|
345
350
|
packages-dir: dist/
|
|
346
351
|
|
|
347
352
|
- name: Publish sdist to TestPyPI
|
|
348
|
-
if: github.event_name == 'workflow_dispatch'
|
|
353
|
+
if: github.event_name == 'workflow_dispatch' && !inputs.publish_to_pypi
|
|
349
354
|
uses: pypa/gh-action-pypi-publish@release/v1
|
|
350
355
|
with:
|
|
351
356
|
repository-url: https://test.pypi.org/legacy/
|
|
@@ -123,12 +123,12 @@ if(NOT LOCAL_MKF_MAS)
|
|
|
123
123
|
set(MKF_FORCE_REFRESH "2026-04-29-extra-components")
|
|
124
124
|
# Tell MKF to disable matplotplusplus and use SVG-based Painter instead
|
|
125
125
|
set(INCLUDE_PYMKF ON CACHE BOOL "Build Python interface" FORCE)
|
|
126
|
-
# GIT_SUBMODULES_RECURSE pulls in CAS/
|
|
126
|
+
# GIT_SUBMODULES_RECURSE pulls in CAS/PEAS (added 2026-04 alongside the
|
|
127
127
|
# ExtraComponentsMode API). MAS is also a submodule of MKF but PyMKF
|
|
128
|
-
# fetches it independently below for fast-update reasons; CAS/
|
|
128
|
+
# fetches it independently below for fast-update reasons; CAS/PEAS only
|
|
129
129
|
# exist as MKF submodules, so we need them populated under MKF/.
|
|
130
130
|
# NOTE: we cannot use GIT_SHALLOW with GIT_SUBMODULES — shallow clone
|
|
131
|
-
# skips submodule init. Drop the shallow flag for MKF so CAS/
|
|
131
|
+
# skips submodule init. Drop the shallow flag for MKF so CAS/PEAS/
|
|
132
132
|
# cci_coords are actually checked out. cci_coords MUST stay in this
|
|
133
133
|
# list — the CCI generator reads from MKF/cci_coords/coordinates/.
|
|
134
134
|
# MAS is fetched separately below (not a sub-fetch of MKF here) so
|
|
@@ -137,7 +137,7 @@ if(NOT LOCAL_MKF_MAS)
|
|
|
137
137
|
GIT_REPOSITORY https://github.com/OpenMagnetics/MKF.git
|
|
138
138
|
GIT_TAG main
|
|
139
139
|
GIT_PROGRESS TRUE
|
|
140
|
-
GIT_SUBMODULES "CAS" "
|
|
140
|
+
GIT_SUBMODULES "CAS" "PEAS" "cci_coords"
|
|
141
141
|
GIT_SUBMODULES_RECURSE TRUE)
|
|
142
142
|
|
|
143
143
|
message(STATUS "Fetching mas")
|
|
@@ -262,14 +262,14 @@ add_custom_target(PyMASGeneration
|
|
|
262
262
|
|
|
263
263
|
# ──────────────────────────────────────────────────────────────────
|
|
264
264
|
# CAS.hpp generation (mirrors MKF/CMakeLists.txt:419-451)
|
|
265
|
-
# CAS = Capacitor Adviser Schema.
|
|
265
|
+
# CAS = Capacitor Adviser Schema. PEAS = Power Element Adviser Schema.
|
|
266
266
|
# Both are submodules of MKF (added 2026-04). MKF/Topology.h #includes
|
|
267
267
|
# <CAS.hpp> for the get_extra_components_inputs API; we must generate
|
|
268
268
|
# the header here so PyOpenMagnetics's TU sees it.
|
|
269
269
|
# ──────────────────────────────────────────────────────────────────
|
|
270
270
|
set(CAS_DIRECTORY "${CMAKE_BINARY_DIR}/CAS/")
|
|
271
271
|
set(CAS_DIR "${MKF_DIR}/CAS")
|
|
272
|
-
set(
|
|
272
|
+
set(PEAS_DIR "${MKF_DIR}/PEAS")
|
|
273
273
|
file(MAKE_DIRECTORY "${CAS_DIRECTORY}")
|
|
274
274
|
|
|
275
275
|
add_custom_command(
|
|
@@ -277,8 +277,10 @@ add_custom_command(
|
|
|
277
277
|
COMMAND ${CMAKE_COMMAND} -E remove -f "${CAS_DIRECTORY}/CAS.hpp"
|
|
278
278
|
COMMAND quicktype -l c++ -s schema ${CAS_DIR}/schemas/inputs.json
|
|
279
279
|
-S ${CAS_DIR}/schemas/inputs/designRequirements.json
|
|
280
|
-
-S ${
|
|
281
|
-
-S ${
|
|
280
|
+
-S ${PEAS_DIR}/schemas/utils.json
|
|
281
|
+
-S ${PEAS_DIR}/schemas/inputs/twoTerminalOperatingPoint.json
|
|
282
|
+
-S ${PEAS_DIR}/schemas/inputs/operatingConditions.json
|
|
283
|
+
-S ${PEAS_DIR}/schemas/inputs/operatingPointExcitation.json
|
|
282
284
|
-o ${CAS_DIRECTORY}/CAS.hpp --namespace CAS --source-style single-source
|
|
283
285
|
--type-style pascal-case --member-style underscore-case
|
|
284
286
|
--enumerator-style upper-underscore-case --no-boost
|
|
@@ -286,8 +288,10 @@ add_custom_command(
|
|
|
286
288
|
DEPENDS
|
|
287
289
|
"${CAS_DIR}/schemas/inputs.json"
|
|
288
290
|
"${CAS_DIR}/schemas/inputs/designRequirements.json"
|
|
289
|
-
"${
|
|
290
|
-
"${
|
|
291
|
+
"${PEAS_DIR}/schemas/utils.json"
|
|
292
|
+
"${PEAS_DIR}/schemas/inputs/twoTerminalOperatingPoint.json"
|
|
293
|
+
"${PEAS_DIR}/schemas/inputs/operatingConditions.json"
|
|
294
|
+
"${PEAS_DIR}/schemas/inputs/operatingPointExcitation.json"
|
|
291
295
|
USES_TERMINAL)
|
|
292
296
|
|
|
293
297
|
add_custom_target(PyCASGeneration
|
|
@@ -1180,14 +1180,48 @@ dispatch_extra_components(const std::string& topologyName,
|
|
|
1180
1180
|
// (Lr / Cr for LLC, etc.) is populated before deck generation.
|
|
1181
1181
|
// ─────────────────────────────────────────────────────────────────────
|
|
1182
1182
|
namespace {
|
|
1183
|
+
// Apply optional bridge simulation mode override on a topology instance.
|
|
1184
|
+
// Must be called BEFORE topology.process() because process() consults the
|
|
1185
|
+
// mode when computing dead time and other timing parameters.
|
|
1186
|
+
//
|
|
1187
|
+
// modeStr accepts:
|
|
1188
|
+
// "" | "default" | "pulse" | "behavioral_pulse" → no-op (BEHAVIORAL_PULSE
|
|
1189
|
+
// stays as default; safe for non-bridge topologies because
|
|
1190
|
+
// set_bridge_simulation_mode throws if you try to force PULSE on a
|
|
1191
|
+
// non-bridge topology).
|
|
1192
|
+
// "switch" | "voltage_controlled_switch" → set VOLTAGE_CONTROLLED_SWITCH,
|
|
1193
|
+
// producing a real SW1/body-diode/snubber bridge instead of a single
|
|
1194
|
+
// dependent V-source. Required when downstream tooling needs to size
|
|
1195
|
+
// the bridge MOSFETs (e.g. Heaviside's TAS decomposer).
|
|
1196
|
+
// anything else → returns false so the caller can raise a clear error.
|
|
1197
|
+
template <typename TopologyT>
|
|
1198
|
+
bool apply_bridge_simulation_mode(TopologyT& topology, const std::string& modeStr) {
|
|
1199
|
+
if (modeStr.empty() || modeStr == "default" ||
|
|
1200
|
+
modeStr == "pulse" || modeStr == "behavioral_pulse") {
|
|
1201
|
+
return true;
|
|
1202
|
+
}
|
|
1203
|
+
if (modeStr == "switch" || modeStr == "voltage_controlled_switch") {
|
|
1204
|
+
topology.set_bridge_simulation_mode(
|
|
1205
|
+
OpenMagnetics::BridgeSimulationMode::VOLTAGE_CONTROLLED_SWITCH);
|
|
1206
|
+
return true;
|
|
1207
|
+
}
|
|
1208
|
+
return false;
|
|
1209
|
+
}
|
|
1210
|
+
|
|
1183
1211
|
// Isolated topologies (transformer): pass turns ratios + magnetizing inductance.
|
|
1184
1212
|
template <typename TopologyT>
|
|
1185
1213
|
std::string generate_spice_isolated(const json& converterJson,
|
|
1186
1214
|
const std::vector<double>& turnsRatios,
|
|
1187
1215
|
double magnetizingInductance,
|
|
1188
|
-
size_t vinIdx, size_t opIdx
|
|
1216
|
+
size_t vinIdx, size_t opIdx,
|
|
1217
|
+
const std::string& bridgeMode) {
|
|
1189
1218
|
TopologyT topology(converterJson);
|
|
1190
1219
|
topology._assertErrors = true;
|
|
1220
|
+
if (!apply_bridge_simulation_mode(topology, bridgeMode)) {
|
|
1221
|
+
throw std::runtime_error(
|
|
1222
|
+
"generate_ngspice_circuit: unknown bridge_simulation_mode '" +
|
|
1223
|
+
bridgeMode + "' — expected '', 'pulse', or 'switch'");
|
|
1224
|
+
}
|
|
1191
1225
|
topology.process();
|
|
1192
1226
|
return topology.generate_ngspice_circuit(turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1193
1227
|
}
|
|
@@ -1196,9 +1230,15 @@ std::string generate_spice_isolated(const json& converterJson,
|
|
|
1196
1230
|
template <typename TopologyT>
|
|
1197
1231
|
std::string generate_spice_inductor(const json& converterJson,
|
|
1198
1232
|
double inductance,
|
|
1199
|
-
size_t vinIdx, size_t opIdx
|
|
1233
|
+
size_t vinIdx, size_t opIdx,
|
|
1234
|
+
const std::string& bridgeMode) {
|
|
1200
1235
|
TopologyT topology(converterJson);
|
|
1201
1236
|
topology._assertErrors = true;
|
|
1237
|
+
if (!apply_bridge_simulation_mode(topology, bridgeMode)) {
|
|
1238
|
+
throw std::runtime_error(
|
|
1239
|
+
"generate_ngspice_circuit: unknown bridge_simulation_mode '" +
|
|
1240
|
+
bridgeMode + "' — expected '', 'pulse', or 'switch'");
|
|
1241
|
+
}
|
|
1202
1242
|
topology.process();
|
|
1203
1243
|
return topology.generate_ngspice_circuit(inductance, vinIdx, opIdx);
|
|
1204
1244
|
}
|
|
@@ -1210,9 +1250,15 @@ template <typename TopologyT>
|
|
|
1210
1250
|
std::string generate_spice_isolated_scalar(const json& converterJson,
|
|
1211
1251
|
const std::vector<double>& turnsRatios,
|
|
1212
1252
|
double magnetizingInductance,
|
|
1213
|
-
size_t vinIdx, size_t opIdx
|
|
1253
|
+
size_t vinIdx, size_t opIdx,
|
|
1254
|
+
const std::string& bridgeMode) {
|
|
1214
1255
|
TopologyT topology(converterJson);
|
|
1215
1256
|
topology._assertErrors = true;
|
|
1257
|
+
if (!apply_bridge_simulation_mode(topology, bridgeMode)) {
|
|
1258
|
+
throw std::runtime_error(
|
|
1259
|
+
"generate_ngspice_circuit: unknown bridge_simulation_mode '" +
|
|
1260
|
+
bridgeMode + "' — expected '', 'pulse', or 'switch'");
|
|
1261
|
+
}
|
|
1216
1262
|
topology.process();
|
|
1217
1263
|
double turnsRatio = turnsRatios.empty() ? 1.0 : turnsRatios[0];
|
|
1218
1264
|
return topology.generate_ngspice_circuit(turnsRatio, magnetizingInductance, vinIdx, opIdx);
|
|
@@ -1225,53 +1271,83 @@ json generate_ngspice_circuit(const std::string& topologyName,
|
|
|
1225
1271
|
std::vector<double> turnsRatios,
|
|
1226
1272
|
double magnetizingInductance,
|
|
1227
1273
|
size_t vinIdx,
|
|
1228
|
-
size_t opIdx
|
|
1274
|
+
size_t opIdx,
|
|
1275
|
+
std::string bridgeMode) {
|
|
1229
1276
|
try {
|
|
1230
1277
|
std::string spice;
|
|
1231
1278
|
// Non-isolated single-inductor topologies — magnetizingInductance
|
|
1232
1279
|
// arg is interpreted as the main inductor value.
|
|
1233
1280
|
if (topologyName == "buck")
|
|
1234
|
-
spice = generate_spice_inductor<OpenMagnetics::Buck>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1281
|
+
spice = generate_spice_inductor<OpenMagnetics::Buck>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1235
1282
|
else if (topologyName == "boost")
|
|
1236
|
-
spice = generate_spice_inductor<OpenMagnetics::Boost>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1283
|
+
spice = generate_spice_inductor<OpenMagnetics::Boost>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1237
1284
|
// Isolated topologies — turnsRatios + magnetizing inductance.
|
|
1238
|
-
else if (topologyName == "flyback") spice = generate_spice_isolated<OpenMagnetics::Flyback>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1239
|
-
else if (topologyName == "single_switch_forward") spice = generate_spice_isolated<OpenMagnetics::SingleSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1240
|
-
else if (topologyName == "two_switch_forward") spice = generate_spice_isolated<OpenMagnetics::TwoSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1241
|
-
else if (topologyName == "active_clamp_forward") spice = generate_spice_isolated<OpenMagnetics::ActiveClampForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1242
|
-
else if (topologyName == "push_pull") spice = generate_spice_isolated<OpenMagnetics::PushPull>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1243
|
-
else if (topologyName == "llc") spice = generate_spice_isolated<OpenMagnetics::Llc>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1244
|
-
// CLLC
|
|
1245
|
-
//
|
|
1246
|
-
//
|
|
1247
|
-
//
|
|
1248
|
-
//
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
else if (topologyName == "
|
|
1252
|
-
|
|
1253
|
-
|
|
1285
|
+
else if (topologyName == "flyback") spice = generate_spice_isolated<OpenMagnetics::Flyback>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1286
|
+
else if (topologyName == "single_switch_forward") spice = generate_spice_isolated<OpenMagnetics::SingleSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1287
|
+
else if (topologyName == "two_switch_forward") spice = generate_spice_isolated<OpenMagnetics::TwoSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1288
|
+
else if (topologyName == "active_clamp_forward") spice = generate_spice_isolated<OpenMagnetics::ActiveClampForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1289
|
+
else if (topologyName == "push_pull") spice = generate_spice_isolated<OpenMagnetics::PushPull>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1290
|
+
else if (topologyName == "llc") spice = generate_spice_isolated<OpenMagnetics::Llc>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1291
|
+
// CLLC has a distinct signature: generate_ngspice_circuit takes
|
|
1292
|
+
// (double turnsRatio, const CllcResonantParameters&, ...) instead
|
|
1293
|
+
// of the uniform (vector<double> turnsRatios, double Lm, ...)
|
|
1294
|
+
// shape, because CLLC needs the full resonant-parameter set
|
|
1295
|
+
// (Lr1, Cr1, Lr2, Cr2, Lm) computed by
|
|
1296
|
+
// CllcConverter::calculate_resonant_parameters() — a single
|
|
1297
|
+
// magnetizing inductance scalar would not determine the tank.
|
|
1298
|
+
else if (topologyName == "cllc" || topologyName == "advanced_cllc") {
|
|
1299
|
+
OpenMagnetics::AdvancedCllcConverter topology(converterJson);
|
|
1300
|
+
topology._assertErrors = true;
|
|
1301
|
+
if (!apply_bridge_simulation_mode(topology, bridgeMode)) {
|
|
1302
|
+
throw std::runtime_error(
|
|
1303
|
+
"generate_ngspice_circuit: unknown bridge_simulation_mode '" +
|
|
1304
|
+
bridgeMode + "' — expected '', 'pulse', or 'switch'");
|
|
1305
|
+
}
|
|
1306
|
+
topology.process();
|
|
1307
|
+
auto params = topology.calculate_resonant_parameters();
|
|
1308
|
+
double turnsRatio = turnsRatios.empty() ? 1.0 : turnsRatios[0];
|
|
1309
|
+
spice = topology.generate_ngspice_circuit(turnsRatio, params, vinIdx, opIdx);
|
|
1310
|
+
}
|
|
1311
|
+
else if (topologyName == "dab") spice = generate_spice_isolated<OpenMagnetics::Dab>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1312
|
+
else if (topologyName == "phase_shifted_full_bridge" || topologyName == "psfb") spice = generate_spice_isolated<OpenMagnetics::Psfb>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1313
|
+
else if (topologyName == "phase_shifted_half_bridge" || topologyName == "pshb") spice = generate_spice_isolated<OpenMagnetics::Pshb>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1314
|
+
else if (topologyName == "isolated_buck") spice = generate_spice_isolated<OpenMagnetics::IsolatedBuck>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1315
|
+
else if (topologyName == "isolated_buck_boost") spice = generate_spice_isolated<OpenMagnetics::IsolatedBuckBoost>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1254
1316
|
// Single-inductor non-isolated (2026-05): Cuk, Sepic, Zeta, FSBB.
|
|
1255
1317
|
else if (topologyName == "cuk" || topologyName == "advanced_cuk")
|
|
1256
|
-
spice = generate_spice_inductor<OpenMagnetics::Cuk>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1318
|
+
spice = generate_spice_inductor<OpenMagnetics::Cuk>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1257
1319
|
else if (topologyName == "sepic" || topologyName == "advanced_sepic")
|
|
1258
|
-
spice = generate_spice_inductor<OpenMagnetics::Sepic>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1320
|
+
spice = generate_spice_inductor<OpenMagnetics::Sepic>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1259
1321
|
else if (topologyName == "zeta" || topologyName == "advanced_zeta")
|
|
1260
|
-
spice = generate_spice_inductor<OpenMagnetics::Zeta>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1322
|
+
spice = generate_spice_inductor<OpenMagnetics::Zeta>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1261
1323
|
else if (topologyName == "four_switch_buck_boost" || topologyName == "advanced_four_switch_buck_boost")
|
|
1262
|
-
spice = generate_spice_inductor<OpenMagnetics::FourSwitchBuckBoost>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
1324
|
+
spice = generate_spice_inductor<OpenMagnetics::FourSwitchBuckBoost>(converterJson, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1263
1325
|
// Isolated with vector turns ratios (2026-05): AsymHB, Clllc, Src, Vienna.
|
|
1264
1326
|
else if (topologyName == "asymmetric_half_bridge" || topologyName == "advanced_asymmetric_half_bridge")
|
|
1265
|
-
spice = generate_spice_isolated<OpenMagnetics::AsymmetricHalfBridge>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1327
|
+
spice = generate_spice_isolated<OpenMagnetics::AsymmetricHalfBridge>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1266
1328
|
else if (topologyName == "clllc" || topologyName == "advanced_clllc")
|
|
1267
|
-
spice = generate_spice_isolated<OpenMagnetics::Clllc>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1329
|
+
spice = generate_spice_isolated<OpenMagnetics::Clllc>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1268
1330
|
else if (topologyName == "src" || topologyName == "advanced_src")
|
|
1269
|
-
spice = generate_spice_isolated<OpenMagnetics::Src>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1331
|
+
spice = generate_spice_isolated<OpenMagnetics::Src>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1270
1332
|
else if (topologyName == "vienna" || topologyName == "advanced_vienna")
|
|
1271
|
-
spice = generate_spice_isolated<OpenMagnetics::Vienna>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1333
|
+
spice = generate_spice_isolated<OpenMagnetics::Vienna>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1272
1334
|
// Isolated with scalar turns ratio (single secondary): Weinberg.
|
|
1273
1335
|
else if (topologyName == "weinberg" || topologyName == "advanced_weinberg")
|
|
1274
|
-
spice = generate_spice_isolated_scalar<OpenMagnetics::Weinberg>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
1336
|
+
spice = generate_spice_isolated_scalar<OpenMagnetics::Weinberg>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx, bridgeMode);
|
|
1337
|
+
// PFC has a distinct method signature — (inductance, dcResistance,
|
|
1338
|
+
// simulationTime, timeStep) — because its deck simulates over a
|
|
1339
|
+
// line cycle rather than a switching cycle. Use the method's
|
|
1340
|
+
// defaults for dcResistance/simulationTime/timeStep; vinIdx/opIdx
|
|
1341
|
+
// are not meaningful for PFC (line voltage drives the operating
|
|
1342
|
+
// point).
|
|
1343
|
+
else if (topologyName == "power_factor_correction"
|
|
1344
|
+
|| topologyName == "advanced_power_factor_correction"
|
|
1345
|
+
|| topologyName == "pfc") {
|
|
1346
|
+
OpenMagnetics::PowerFactorCorrection topology(converterJson);
|
|
1347
|
+
topology._assertErrors = true;
|
|
1348
|
+
topology.process();
|
|
1349
|
+
spice = topology.generate_ngspice_circuit(magnetizingInductance);
|
|
1350
|
+
}
|
|
1275
1351
|
else return json{{"error", "generate_ngspice_circuit: unknown topology '" + topologyName + "'"}};
|
|
1276
1352
|
return json{{"netlist", spice}};
|
|
1277
1353
|
} catch (const std::exception& exc) {
|
|
@@ -1367,7 +1443,8 @@ void register_converter_bindings(py::module& m) {
|
|
|
1367
1443
|
"populated. Returns {'netlist': '<spice>'} or {'error': '...'}.",
|
|
1368
1444
|
py::arg("topology_name"), py::arg("converter_json"),
|
|
1369
1445
|
py::arg("turns_ratios"), py::arg("magnetizing_inductance"),
|
|
1370
|
-
py::arg("vin_index") = 0, py::arg("op_index") = 0
|
|
1446
|
+
py::arg("vin_index") = 0, py::arg("op_index") = 0,
|
|
1447
|
+
py::arg("bridge_simulation_mode") = std::string(""));
|
|
1371
1448
|
|
|
1372
1449
|
m.def("get_extra_components_inputs", &get_extra_components_inputs,
|
|
1373
1450
|
"Return the design requirements for extra components a topology brings "
|
|
@@ -161,6 +161,9 @@ json get_settings() {
|
|
|
161
161
|
settingsJson["coreAdviserIncludeMargin"] = OpenMagnetics::settings.get_core_adviser_include_margin();
|
|
162
162
|
settingsJson["coreAdviserEnableIntermediatePruning"] = OpenMagnetics::settings.get_core_adviser_enable_intermediate_pruning();
|
|
163
163
|
settingsJson["coreAdviserMaximumMagneticsAfterFiltering"] = OpenMagnetics::settings.get_core_adviser_maximum_magnetics_after_filtering();
|
|
164
|
+
settingsJson["coreAdviserEnableTemperatureFilter"] = OpenMagnetics::settings.get_core_adviser_enable_temperature_filter();
|
|
165
|
+
settingsJson["coreAdviserMaximumTemperature"] = OpenMagnetics::settings.get_core_adviser_maximum_temperature();
|
|
166
|
+
settingsJson["coreAdviserSaturationMargin"] = OpenMagnetics::settings.get_core_adviser_saturation_margin();
|
|
164
167
|
{
|
|
165
168
|
json aux;
|
|
166
169
|
to_json(aux, OpenMagnetics::settings.get_gapping_strategy());
|
|
@@ -347,6 +350,9 @@ void set_settings(json settingsJson) {
|
|
|
347
350
|
if (settingsJson.contains("coreAdviserIncludeMargin")) OpenMagnetics::settings.set_core_adviser_include_margin(settingsJson["coreAdviserIncludeMargin"]);
|
|
348
351
|
if (settingsJson.contains("coreAdviserEnableIntermediatePruning")) OpenMagnetics::settings.set_core_adviser_enable_intermediate_pruning(settingsJson["coreAdviserEnableIntermediatePruning"]);
|
|
349
352
|
if (settingsJson.contains("coreAdviserMaximumMagneticsAfterFiltering")) OpenMagnetics::settings.set_core_adviser_maximum_magnetics_after_filtering(settingsJson["coreAdviserMaximumMagneticsAfterFiltering"]);
|
|
353
|
+
if (settingsJson.contains("coreAdviserEnableTemperatureFilter")) OpenMagnetics::settings.set_core_adviser_enable_temperature_filter(settingsJson["coreAdviserEnableTemperatureFilter"]);
|
|
354
|
+
if (settingsJson.contains("coreAdviserMaximumTemperature")) OpenMagnetics::settings.set_core_adviser_maximum_temperature(settingsJson["coreAdviserMaximumTemperature"]);
|
|
355
|
+
if (settingsJson.contains("coreAdviserSaturationMargin")) OpenMagnetics::settings.set_core_adviser_saturation_margin(settingsJson["coreAdviserSaturationMargin"]);
|
|
350
356
|
if (settingsJson.contains("gappingStrategy")) {
|
|
351
357
|
GappingOptimizationStrategy strategy;
|
|
352
358
|
from_json(settingsJson["gappingStrategy"], strategy);
|
|
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
|