PyOpenMagnetics 1.3.9__tar.gz → 1.3.10__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.9 → pyopenmagnetics-1.3.10}/CMakeLists.txt +50 -3
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/PKG-INFO +1 -1
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/pyproject.toml +1 -1
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/converter.cpp +235 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/.gitignore +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/AGENTS.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/LICENSE +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/README.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/api/MAS.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/api/validation.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/docs/errors.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/docs/performance.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/README.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/llms.txt +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/requirements.txt +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/advisers.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/common.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/converter.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/core.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/core.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/database.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/database.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/logging.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/losses.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/module.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/plotting.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/settings.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/simulation.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/utils.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/winding.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/src/wire.h +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/test.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.3.9 → pyopenmagnetics-1.3.10}/tests/test_winding.py +0 -0
|
@@ -120,14 +120,25 @@ include_directories("${CMAKE_BINARY_DIR}/_deps/eigen-src")
|
|
|
120
120
|
if(NOT LOCAL_MKF_MAS)
|
|
121
121
|
message(STATUS "Fetching MKF")
|
|
122
122
|
# Force fresh clone by using a unique timestamp - update this when MKF changes
|
|
123
|
-
set(MKF_FORCE_REFRESH "
|
|
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/EAS (added 2026-04 alongside the
|
|
127
|
+
# ExtraComponentsMode API). MAS is also a submodule of MKF but PyMKF
|
|
128
|
+
# fetches it independently below for fast-update reasons; CAS/EAS only
|
|
129
|
+
# exist as MKF submodules, so we need them populated under MKF/.
|
|
130
|
+
# NOTE: we cannot use GIT_SHALLOW with GIT_SUBMODULES — shallow clone
|
|
131
|
+
# skips submodule init. Drop the shallow flag for MKF so CAS/EAS/
|
|
132
|
+
# cci_coords are actually checked out. cci_coords MUST stay in this
|
|
133
|
+
# list — the CCI generator reads from MKF/cci_coords/coordinates/.
|
|
134
|
+
# MAS is fetched separately below (not a sub-fetch of MKF here) so
|
|
135
|
+
# leave it out, otherwise FetchContent doubles the clone.
|
|
126
136
|
FetchContent_Declare(MKF
|
|
127
137
|
GIT_REPOSITORY https://github.com/OpenMagnetics/MKF.git
|
|
128
138
|
GIT_TAG main
|
|
129
139
|
GIT_PROGRESS TRUE
|
|
130
|
-
|
|
140
|
+
GIT_SUBMODULES "CAS" "EAS" "cci_coords"
|
|
141
|
+
GIT_SUBMODULES_RECURSE TRUE)
|
|
131
142
|
|
|
132
143
|
message(STATUS "Fetching mas")
|
|
133
144
|
# Skip Git LFS to avoid bandwidth quota issues - data files are optional for build
|
|
@@ -237,6 +248,42 @@ add_custom_target(PyMASGeneration
|
|
|
237
248
|
/bin/echo "RUNNING PyMASGeneration"
|
|
238
249
|
DEPENDS "${MAS_DIRECTORY}/MAS.hpp")
|
|
239
250
|
|
|
251
|
+
# ──────────────────────────────────────────────────────────────────
|
|
252
|
+
# CAS.hpp generation (mirrors MKF/CMakeLists.txt:419-451)
|
|
253
|
+
# CAS = Capacitor Adviser Schema. EAS = Element Adviser Schema.
|
|
254
|
+
# Both are submodules of MKF (added 2026-04). MKF/Topology.h #includes
|
|
255
|
+
# <CAS.hpp> for the get_extra_components_inputs API; we must generate
|
|
256
|
+
# the header here so PyOpenMagnetics's TU sees it.
|
|
257
|
+
# ──────────────────────────────────────────────────────────────────
|
|
258
|
+
set(CAS_DIRECTORY "${CMAKE_BINARY_DIR}/CAS/")
|
|
259
|
+
set(CAS_DIR "${MKF_DIR}/CAS")
|
|
260
|
+
set(EAS_DIR "${MKF_DIR}/EAS")
|
|
261
|
+
file(MAKE_DIRECTORY "${CAS_DIRECTORY}")
|
|
262
|
+
|
|
263
|
+
add_custom_command(
|
|
264
|
+
OUTPUT "${CAS_DIRECTORY}/CAS.hpp"
|
|
265
|
+
COMMAND ${CMAKE_COMMAND} -E remove -f "${CAS_DIRECTORY}/CAS.hpp"
|
|
266
|
+
COMMAND quicktype -l c++ -s schema ${CAS_DIR}/schemas/inputs.json
|
|
267
|
+
-S ${CAS_DIR}/schemas/inputs/designRequirements.json
|
|
268
|
+
-S ${EAS_DIR}/schemas/utils.json
|
|
269
|
+
-S ${EAS_DIR}/schemas/inputs/twoTerminalOperatingPoint.json
|
|
270
|
+
-o ${CAS_DIRECTORY}/CAS.hpp --namespace CAS --source-style single-source
|
|
271
|
+
--type-style pascal-case --member-style underscore-case
|
|
272
|
+
--enumerator-style upper-underscore-case --no-boost
|
|
273
|
+
--top-level Inputs
|
|
274
|
+
DEPENDS
|
|
275
|
+
"${CAS_DIR}/schemas/inputs.json"
|
|
276
|
+
"${CAS_DIR}/schemas/inputs/designRequirements.json"
|
|
277
|
+
"${EAS_DIR}/schemas/utils.json"
|
|
278
|
+
"${EAS_DIR}/schemas/inputs/twoTerminalOperatingPoint.json"
|
|
279
|
+
USES_TERMINAL)
|
|
280
|
+
|
|
281
|
+
add_custom_target(PyCASGeneration
|
|
282
|
+
/bin/echo "RUNNING PyCASGeneration"
|
|
283
|
+
DEPENDS "${CAS_DIRECTORY}/CAS.hpp")
|
|
284
|
+
add_dependencies(PyCASGeneration PyMASGeneration)
|
|
285
|
+
include_directories("${CAS_DIRECTORY}")
|
|
286
|
+
|
|
240
287
|
message(STATUS "Compiling PyOpenMagnetics with modular structure")
|
|
241
288
|
file(GLOB SOURCES src/*.cpp
|
|
242
289
|
${MKF_DIR}/src/*.cpp
|
|
@@ -266,7 +313,7 @@ message(STATUS SOURCES)
|
|
|
266
313
|
message(STATUS ${SOURCES})
|
|
267
314
|
pybind11_add_module(PyOpenMagnetics ${SOURCES})
|
|
268
315
|
|
|
269
|
-
add_dependencies(PyOpenMagnetics PyMASGeneration cci_data_gen)
|
|
316
|
+
add_dependencies(PyOpenMagnetics PyMASGeneration PyCASGeneration cci_data_gen)
|
|
270
317
|
|
|
271
318
|
target_link_libraries(PyOpenMagnetics PUBLIC nlohmann_json::nlohmann_json levmar rapidfuzz::rapidfuzz)
|
|
272
319
|
|
|
@@ -629,6 +629,220 @@ json process_current_transformer(json ctJson, double turnsRatio, double secondar
|
|
|
629
629
|
return process_converter("current_transformer", ctJson, true);
|
|
630
630
|
}
|
|
631
631
|
|
|
632
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
633
|
+
// get_extra_components_inputs (MKF 2026-04-29 API)
|
|
634
|
+
//
|
|
635
|
+
// Returns a JSON list describing the design requirements / operating
|
|
636
|
+
// points of any extra components a topology brings along besides its
|
|
637
|
+
// main magnetic — resonant tank caps for LLC, snubber nets, auxiliary
|
|
638
|
+
// inductors, etc. The downstream caller can pipe each entry into a
|
|
639
|
+
// component-search (TAS) or a fresh PyOM design call.
|
|
640
|
+
//
|
|
641
|
+
// Each list entry is one of:
|
|
642
|
+
// { "kind": "magnetic", "inputs": { ...MAS::Inputs JSON... } }
|
|
643
|
+
// { "kind": "capacitor", "inputs": { ...CAS::Inputs JSON... } }
|
|
644
|
+
//
|
|
645
|
+
// Arguments:
|
|
646
|
+
// topology_name — same key set as dispatch_converter (e.g. "llc",
|
|
647
|
+
// "psfb", "active_clamp_forward").
|
|
648
|
+
// converter_json — the converter spec used to construct the simple
|
|
649
|
+
// topology class (NOT the AdvancedXxx version).
|
|
650
|
+
// mode — "IDEAL" or "REAL". IDEAL returns archetypal
|
|
651
|
+
// requirements without parasitics; REAL refines
|
|
652
|
+
// with the main magnetic's actual leakage,
|
|
653
|
+
// winding resistance, etc.
|
|
654
|
+
// magnetic_json — optional MAS::Magnetic JSON, required by some
|
|
655
|
+
// topologies in REAL mode. Pass null in IDEAL.
|
|
656
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
657
|
+
|
|
658
|
+
namespace {
|
|
659
|
+
|
|
660
|
+
// Construct the simple topology subclass (NOT AdvancedXxx), run the
|
|
661
|
+
// design pipeline (process() / process_design_requirements()) so the
|
|
662
|
+
// internal resonant-tank / extra-component values are populated, and
|
|
663
|
+
// then invoke get_extra_components_inputs on it. The simple classes
|
|
664
|
+
// own the design physics, so they're what the new API was designed
|
|
665
|
+
// against. Some topologies require process() to have run first; we
|
|
666
|
+
// invoke it via the SFINAE-detected member that exists on each.
|
|
667
|
+
template <typename TopologyT>
|
|
668
|
+
std::vector<std::variant<OpenMagnetics::Inputs, CAS::Inputs>>
|
|
669
|
+
collect_extra_components(const json& converterJson,
|
|
670
|
+
OpenMagnetics::ExtraComponentsMode mode,
|
|
671
|
+
std::optional<OpenMagnetics::Magnetic> magnetic) {
|
|
672
|
+
TopologyT topology(converterJson);
|
|
673
|
+
topology._assertErrors = true;
|
|
674
|
+
// Run process() — the Topology base class entry point that wires
|
|
675
|
+
// process_design_requirements() + process_operating_points() and
|
|
676
|
+
// populates the internal Lr / Cr / inductance ratio that
|
|
677
|
+
// get_extra_components_inputs() reads. The Inputs return value is
|
|
678
|
+
// discarded; we only care about the side effects on `topology`.
|
|
679
|
+
topology.process();
|
|
680
|
+
return topology.get_extra_components_inputs(mode, magnetic);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
std::vector<std::variant<OpenMagnetics::Inputs, CAS::Inputs>>
|
|
684
|
+
dispatch_extra_components(const std::string& topologyName,
|
|
685
|
+
const json& converterJson,
|
|
686
|
+
OpenMagnetics::ExtraComponentsMode mode,
|
|
687
|
+
std::optional<OpenMagnetics::Magnetic> magnetic) {
|
|
688
|
+
if (topologyName == "flyback")
|
|
689
|
+
return collect_extra_components<OpenMagnetics::Flyback>(converterJson, mode, magnetic);
|
|
690
|
+
if (topologyName == "buck")
|
|
691
|
+
return collect_extra_components<OpenMagnetics::Buck>(converterJson, mode, magnetic);
|
|
692
|
+
if (topologyName == "boost")
|
|
693
|
+
return collect_extra_components<OpenMagnetics::Boost>(converterJson, mode, magnetic);
|
|
694
|
+
if (topologyName == "single_switch_forward")
|
|
695
|
+
return collect_extra_components<OpenMagnetics::SingleSwitchForward>(converterJson, mode, magnetic);
|
|
696
|
+
if (topologyName == "two_switch_forward")
|
|
697
|
+
return collect_extra_components<OpenMagnetics::TwoSwitchForward>(converterJson, mode, magnetic);
|
|
698
|
+
if (topologyName == "active_clamp_forward")
|
|
699
|
+
return collect_extra_components<OpenMagnetics::ActiveClampForward>(converterJson, mode, magnetic);
|
|
700
|
+
if (topologyName == "push_pull")
|
|
701
|
+
return collect_extra_components<OpenMagnetics::PushPull>(converterJson, mode, magnetic);
|
|
702
|
+
if (topologyName == "llc")
|
|
703
|
+
return collect_extra_components<OpenMagnetics::Llc>(converterJson, mode, magnetic);
|
|
704
|
+
if (topologyName == "dab")
|
|
705
|
+
return collect_extra_components<OpenMagnetics::Dab>(converterJson, mode, magnetic);
|
|
706
|
+
if (topologyName == "phase_shifted_full_bridge" || topologyName == "psfb")
|
|
707
|
+
return collect_extra_components<OpenMagnetics::Psfb>(converterJson, mode, magnetic);
|
|
708
|
+
if (topologyName == "phase_shifted_half_bridge" || topologyName == "pshb")
|
|
709
|
+
return collect_extra_components<OpenMagnetics::Pshb>(converterJson, mode, magnetic);
|
|
710
|
+
if (topologyName == "cllc")
|
|
711
|
+
return collect_extra_components<OpenMagnetics::CllcConverter>(converterJson, mode, magnetic);
|
|
712
|
+
if (topologyName == "isolated_buck")
|
|
713
|
+
return collect_extra_components<OpenMagnetics::IsolatedBuck>(converterJson, mode, magnetic);
|
|
714
|
+
if (topologyName == "isolated_buck_boost")
|
|
715
|
+
return collect_extra_components<OpenMagnetics::IsolatedBuckBoost>(converterJson, mode, magnetic);
|
|
716
|
+
throw std::invalid_argument(
|
|
717
|
+
"get_extra_components_inputs: topology '" + topologyName +
|
|
718
|
+
"' has no extra-components dispatch (or hasn't been wired in PyMKF)."
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
|
|
722
|
+
} // namespace
|
|
723
|
+
|
|
724
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
725
|
+
// generate_ngspice_circuit (MKF native SPICE generator)
|
|
726
|
+
//
|
|
727
|
+
// Returns the canonical ngspice deck for a converter at a given operating
|
|
728
|
+
// point, sized from the magnetic that was designed in Phase 3. This
|
|
729
|
+
// replaces hand-rolled netlist templates on the Proteus side: MKF owns
|
|
730
|
+
// the topology wiring (rectifier polarity, gate-drive timing, dead-time,
|
|
731
|
+
// snubbers, K-coupling) so the simulation always validates the actual
|
|
732
|
+
// designed magnetic.
|
|
733
|
+
//
|
|
734
|
+
// Args mirror the C++ method: turns_ratios (Np:Ns vector), magnetizing
|
|
735
|
+
// inductance, optional vin / operating-point indices. The simple
|
|
736
|
+
// topology class is constructed and process()'d first so internal state
|
|
737
|
+
// (Lr / Cr for LLC, etc.) is populated before deck generation.
|
|
738
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
739
|
+
namespace {
|
|
740
|
+
// Isolated topologies (transformer): pass turns ratios + magnetizing inductance.
|
|
741
|
+
template <typename TopologyT>
|
|
742
|
+
std::string generate_spice_isolated(const json& converterJson,
|
|
743
|
+
const std::vector<double>& turnsRatios,
|
|
744
|
+
double magnetizingInductance,
|
|
745
|
+
size_t vinIdx, size_t opIdx) {
|
|
746
|
+
TopologyT topology(converterJson);
|
|
747
|
+
topology._assertErrors = true;
|
|
748
|
+
topology.process();
|
|
749
|
+
return topology.generate_ngspice_circuit(turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
// Non-isolated topologies (single inductor): just pass the inductance.
|
|
753
|
+
template <typename TopologyT>
|
|
754
|
+
std::string generate_spice_inductor(const json& converterJson,
|
|
755
|
+
double inductance,
|
|
756
|
+
size_t vinIdx, size_t opIdx) {
|
|
757
|
+
TopologyT topology(converterJson);
|
|
758
|
+
topology._assertErrors = true;
|
|
759
|
+
topology.process();
|
|
760
|
+
return topology.generate_ngspice_circuit(inductance, vinIdx, opIdx);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
} // namespace
|
|
764
|
+
|
|
765
|
+
json generate_ngspice_circuit(const std::string& topologyName,
|
|
766
|
+
json converterJson,
|
|
767
|
+
std::vector<double> turnsRatios,
|
|
768
|
+
double magnetizingInductance,
|
|
769
|
+
size_t vinIdx,
|
|
770
|
+
size_t opIdx) {
|
|
771
|
+
try {
|
|
772
|
+
std::string spice;
|
|
773
|
+
// Non-isolated single-inductor topologies — magnetizingInductance
|
|
774
|
+
// arg is interpreted as the main inductor value.
|
|
775
|
+
if (topologyName == "buck")
|
|
776
|
+
spice = generate_spice_inductor<OpenMagnetics::Buck>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
777
|
+
else if (topologyName == "boost")
|
|
778
|
+
spice = generate_spice_inductor<OpenMagnetics::Boost>(converterJson, magnetizingInductance, vinIdx, opIdx);
|
|
779
|
+
// Isolated topologies — turnsRatios + magnetizing inductance.
|
|
780
|
+
else if (topologyName == "flyback") spice = generate_spice_isolated<OpenMagnetics::Flyback>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
781
|
+
else if (topologyName == "single_switch_forward") spice = generate_spice_isolated<OpenMagnetics::SingleSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
782
|
+
else if (topologyName == "two_switch_forward") spice = generate_spice_isolated<OpenMagnetics::TwoSwitchForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
783
|
+
else if (topologyName == "active_clamp_forward") spice = generate_spice_isolated<OpenMagnetics::ActiveClampForward>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
784
|
+
else if (topologyName == "push_pull") spice = generate_spice_isolated<OpenMagnetics::PushPull>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
785
|
+
else if (topologyName == "llc") spice = generate_spice_isolated<OpenMagnetics::Llc>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
786
|
+
// CLLC is intentionally not wired here yet — its
|
|
787
|
+
// generate_ngspice_circuit signature takes (double turnsRatio,
|
|
788
|
+
// CllcResonantParameters&, ...), which doesn't fit the uniform
|
|
789
|
+
// (vector<double> turnsRatios, double Lm, ...) shape used by every
|
|
790
|
+
// other topology. Add a separate dispatch path when we need it.
|
|
791
|
+
else if (topologyName == "dab") spice = generate_spice_isolated<OpenMagnetics::Dab>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
792
|
+
else if (topologyName == "phase_shifted_full_bridge" || topologyName == "psfb") spice = generate_spice_isolated<OpenMagnetics::Psfb>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
793
|
+
else if (topologyName == "phase_shifted_half_bridge" || topologyName == "pshb") spice = generate_spice_isolated<OpenMagnetics::Pshb>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
794
|
+
else if (topologyName == "isolated_buck") spice = generate_spice_isolated<OpenMagnetics::IsolatedBuck>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
795
|
+
else if (topologyName == "isolated_buck_boost") spice = generate_spice_isolated<OpenMagnetics::IsolatedBuckBoost>(converterJson, turnsRatios, magnetizingInductance, vinIdx, opIdx);
|
|
796
|
+
else return json{{"error", "generate_ngspice_circuit: unknown topology '" + topologyName + "'"}};
|
|
797
|
+
return json{{"netlist", spice}};
|
|
798
|
+
} catch (const std::exception& exc) {
|
|
799
|
+
return json{{"error", std::string("generate_ngspice_circuit: ") + exc.what()}};
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
json get_extra_components_inputs(const std::string& topologyName,
|
|
804
|
+
json converterJson,
|
|
805
|
+
const std::string& modeStr,
|
|
806
|
+
json magneticJson) {
|
|
807
|
+
try {
|
|
808
|
+
OpenMagnetics::ExtraComponentsMode mode;
|
|
809
|
+
if (modeStr == "IDEAL" || modeStr == "ideal")
|
|
810
|
+
mode = OpenMagnetics::ExtraComponentsMode::IDEAL;
|
|
811
|
+
else if (modeStr == "REAL" || modeStr == "real")
|
|
812
|
+
mode = OpenMagnetics::ExtraComponentsMode::REAL;
|
|
813
|
+
else
|
|
814
|
+
return json{{"error", "mode must be 'IDEAL' or 'REAL', got: " + modeStr}};
|
|
815
|
+
|
|
816
|
+
std::optional<OpenMagnetics::Magnetic> magnetic;
|
|
817
|
+
if (!magneticJson.is_null()) {
|
|
818
|
+
magnetic = OpenMagnetics::Magnetic(magneticJson);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
auto components = dispatch_extra_components(topologyName, converterJson, mode, magnetic);
|
|
822
|
+
|
|
823
|
+
json out = json::array();
|
|
824
|
+
for (const auto& comp : components) {
|
|
825
|
+
json entry;
|
|
826
|
+
std::visit([&entry](const auto& inputs) {
|
|
827
|
+
using T = std::decay_t<decltype(inputs)>;
|
|
828
|
+
json inputsJson;
|
|
829
|
+
to_json(inputsJson, inputs);
|
|
830
|
+
if constexpr (std::is_same_v<T, OpenMagnetics::Inputs>) {
|
|
831
|
+
entry["kind"] = "magnetic";
|
|
832
|
+
} else {
|
|
833
|
+
// CAS::Inputs
|
|
834
|
+
entry["kind"] = "capacitor";
|
|
835
|
+
}
|
|
836
|
+
entry["inputs"] = inputsJson;
|
|
837
|
+
}, comp);
|
|
838
|
+
out.push_back(entry);
|
|
839
|
+
}
|
|
840
|
+
return out;
|
|
841
|
+
} catch (const std::exception& exc) {
|
|
842
|
+
return json{{"error", std::string("get_extra_components_inputs: ") + exc.what()}};
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
632
846
|
void register_converter_bindings(py::module& m) {
|
|
633
847
|
m.def("process_converter", &process_converter,
|
|
634
848
|
"Process a converter topology specification to Inputs.",
|
|
@@ -651,6 +865,27 @@ void register_converter_bindings(py::module& m) {
|
|
|
651
865
|
m.def("process_isolated_buck_boost", &process_isolated_buck_boost, "Process Isolated Buck-Boost.", py::arg("isolated_buck_boost"));
|
|
652
866
|
m.def("process_current_transformer", &process_current_transformer, "Process Current Transformer.",
|
|
653
867
|
py::arg("ct"), py::arg("turns_ratio"), py::arg("secondary_resistance") = 0.0);
|
|
868
|
+
|
|
869
|
+
m.def("generate_ngspice_circuit", &generate_ngspice_circuit,
|
|
870
|
+
"Return the canonical ngspice SPICE deck for the topology at a "
|
|
871
|
+
"given operating point, sized from the magnetic design (turns "
|
|
872
|
+
"ratios + magnetizing inductance). Wrapper around each Topology "
|
|
873
|
+
"subclass's generate_ngspice_circuit method; processes the "
|
|
874
|
+
"topology first so internal state (Lr/Cr for LLC, etc.) is "
|
|
875
|
+
"populated. Returns {'netlist': '<spice>'} or {'error': '...'}.",
|
|
876
|
+
py::arg("topology_name"), py::arg("converter_json"),
|
|
877
|
+
py::arg("turns_ratios"), py::arg("magnetizing_inductance"),
|
|
878
|
+
py::arg("vin_index") = 0, py::arg("op_index") = 0);
|
|
879
|
+
|
|
880
|
+
m.def("get_extra_components_inputs", &get_extra_components_inputs,
|
|
881
|
+
"Return the design requirements for extra components a topology brings "
|
|
882
|
+
"alongside its main magnetic — resonant tank Lr/Cr for LLC, snubber "
|
|
883
|
+
"components for PSFB, etc. Each entry is {kind: 'magnetic'|'capacitor', "
|
|
884
|
+
"inputs: <MAS or CAS Inputs JSON>}. Mode 'IDEAL' returns archetypal "
|
|
885
|
+
"requirements; 'REAL' refines using the supplied main magnetic's "
|
|
886
|
+
"leakage / DCR / etc.",
|
|
887
|
+
py::arg("topology_name"), py::arg("converter_json"),
|
|
888
|
+
py::arg("mode") = "IDEAL", py::arg("magnetic_json") = nullptr);
|
|
654
889
|
}
|
|
655
890
|
|
|
656
891
|
} // 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
|