PyOpenMagnetics 1.2.0__tar.gz → 1.2.1__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.2.0 → pyopenmagnetics-1.2.1}/PKG-INFO +1 -1
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/PyOpenMagnetics.pyi +54 -5
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/pyproject.toml +10 -10
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/common.h +1 -0
- pyopenmagnetics-1.2.1/src/converter.cpp +583 -0
- pyopenmagnetics-1.2.1/src/converter.h +33 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/module.cpp +2 -0
- pyopenmagnetics-1.2.1/tests/test_converter_endpoints.py +235 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/.gitignore +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/CMakeLists.txt +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/LICENSE +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/README.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/api/MAS.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/api/validation.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/docs/errors.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/docs/performance.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/examples/README.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/llms.txt +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/requirements.txt +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/advisers.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/core.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/core.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/database.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/database.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/logging.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/losses.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/plotting.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/settings.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/simulation.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/utils.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/winding.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/src/wire.h +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/test.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.2.0 → pyopenmagnetics-1.2.1}/tests/test_winding.py +0 -0
|
@@ -685,11 +685,60 @@ def get_default_models() -> JsonDict:
|
|
|
685
685
|
"""Get default model selections."""
|
|
686
686
|
...
|
|
687
687
|
|
|
688
|
-
# =============================================================================
|
|
689
|
-
# CONVERTER TOPOLOGY PROCESSORS
|
|
690
|
-
# =============================================================================
|
|
691
|
-
|
|
692
|
-
def
|
|
688
|
+
# =============================================================================
|
|
689
|
+
# CONVERTER TOPOLOGY PROCESSORS
|
|
690
|
+
# =============================================================================
|
|
691
|
+
|
|
692
|
+
def process_converter(topology: str, converter: JsonDict, use_ngspice: bool = True) -> JsonDict:
|
|
693
|
+
"""Process a converter topology specification to Inputs.
|
|
694
|
+
|
|
695
|
+
Generic endpoint that dispatches to the appropriate topology processor.
|
|
696
|
+
|
|
697
|
+
Args:
|
|
698
|
+
topology: Topology name ("flyback", "buck", "boost", "single_switch_forward",
|
|
699
|
+
"two_switch_forward", "active_clamp_forward", "push_pull", "llc",
|
|
700
|
+
"cllc", "dab", "phase_shifted_full_bridge", "phase_shifted_half_bridge",
|
|
701
|
+
"isolated_buck", "isolated_buck_boost", "current_transformer")
|
|
702
|
+
converter: JSON object with converter specification per MAS schema
|
|
703
|
+
use_ngspice: If True, uses ngspice simulation for waveform extraction
|
|
704
|
+
|
|
705
|
+
Returns:
|
|
706
|
+
JSON object with "designRequirements" and "operatingPoints"
|
|
707
|
+
On error, returns {"error": "..."}
|
|
708
|
+
"""
|
|
709
|
+
...
|
|
710
|
+
|
|
711
|
+
def design_magnetics_from_converter(
|
|
712
|
+
topology: str,
|
|
713
|
+
converter: JsonDict,
|
|
714
|
+
max_results: int = 1,
|
|
715
|
+
core_mode: str = "AVAILABLE_CORES",
|
|
716
|
+
use_ngspice: bool = True,
|
|
717
|
+
weights: Optional[Dict[str, float]] = None
|
|
718
|
+
) -> JsonDict:
|
|
719
|
+
"""Design magnetic components from a converter specification.
|
|
720
|
+
|
|
721
|
+
High-level endpoint that combines converter processing with magnetic adviser
|
|
722
|
+
to go from converter specification directly to ranked magnetic designs.
|
|
723
|
+
|
|
724
|
+
Args:
|
|
725
|
+
topology: Topology name (see process_converter for list)
|
|
726
|
+
converter: JSON object with converter specification per MAS schema
|
|
727
|
+
max_results: Maximum number of magnetic designs to return
|
|
728
|
+
core_mode: Core selection mode - "AVAILABLE_CORES" or "STANDARD_CORES"
|
|
729
|
+
use_ngspice: If True, uses ngspice simulation for waveform extraction
|
|
730
|
+
weights: Optional filter weights (e.g., {"COST": 1.0, "LOSSES": 2.0})
|
|
731
|
+
|
|
732
|
+
Returns:
|
|
733
|
+
JSON object with "data" array containing ranked results.
|
|
734
|
+
Each result has:
|
|
735
|
+
- "mas": Mas object with magnetic design
|
|
736
|
+
- "scoring": Overall float score
|
|
737
|
+
- "scoringPerFilter": Object with individual filter scores
|
|
738
|
+
"""
|
|
739
|
+
...
|
|
740
|
+
|
|
741
|
+
def process_flyback(flyback: JsonDict) -> Inputs:
|
|
693
742
|
"""Process Flyback converter specification to Inputs."""
|
|
694
743
|
...
|
|
695
744
|
|
|
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "PyOpenMagnetics"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.1"
|
|
8
8
|
requires-python = ">=3.8"
|
|
9
9
|
authors = [
|
|
10
10
|
{ name="Alfonso Martinez", email="Alfonso_VII@hotmail.com" },
|
|
@@ -48,15 +48,15 @@ manylinux-pypy_aarch64-image = "manylinux_2_28"
|
|
|
48
48
|
environment = { GIT_LFS_SKIP_SMUDGE=1 }
|
|
49
49
|
build-verbosity = 1
|
|
50
50
|
|
|
51
|
-
[tool.cibuildwheel.linux]
|
|
52
|
-
before-all = [
|
|
53
|
-
"rm -rf /project/build /root/.cmake",
|
|
54
|
-
"dnf -y module reset nodejs",
|
|
55
|
-
"dnf -y module enable nodejs:20",
|
|
56
|
-
"dnf -y install nodejs npm",
|
|
57
|
-
"npm --version",
|
|
58
|
-
"npm install -g quicktype@23.0.170"
|
|
59
|
-
]
|
|
51
|
+
[tool.cibuildwheel.linux]
|
|
52
|
+
before-all = [
|
|
53
|
+
"rm -rf /project/build /root/.cmake",
|
|
54
|
+
"dnf -y module reset nodejs",
|
|
55
|
+
"dnf -y module enable nodejs:20",
|
|
56
|
+
"dnf -y install nodejs npm",
|
|
57
|
+
"npm --version",
|
|
58
|
+
"npm install -g quicktype@23.0.170"
|
|
59
|
+
]
|
|
60
60
|
environment = { GIT_LFS_SKIP_SMUDGE=1, CMAKE_BUILD_PARALLEL_LEVEL="3" }
|
|
61
61
|
|
|
62
62
|
[tool.cibuildwheel.macos]
|
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
#include "converter.h"
|
|
2
|
+
|
|
3
|
+
// Include all converter model headers
|
|
4
|
+
#include "converter_models/Flyback.h"
|
|
5
|
+
#include "converter_models/Buck.h"
|
|
6
|
+
#include "converter_models/Boost.h"
|
|
7
|
+
#include "converter_models/SingleSwitchForward.h"
|
|
8
|
+
#include "converter_models/TwoSwitchForward.h"
|
|
9
|
+
#include "converter_models/ActiveClampForward.h"
|
|
10
|
+
#include "converter_models/PushPull.h"
|
|
11
|
+
#include "converter_models/Llc.h"
|
|
12
|
+
#include "converter_models/Cllc.h"
|
|
13
|
+
#include "converter_models/Dab.h"
|
|
14
|
+
#include "converter_models/PhaseShiftedFullBridge.h"
|
|
15
|
+
#include "converter_models/PhaseShiftedHalfBridge.h"
|
|
16
|
+
#include "converter_models/IsolatedBuck.h"
|
|
17
|
+
#include "converter_models/IsolatedBuckBoost.h"
|
|
18
|
+
#include "converter_models/CurrentTransformer.h"
|
|
19
|
+
#include "converter_models/PowerFactorCorrection.h"
|
|
20
|
+
|
|
21
|
+
namespace PyMKF {
|
|
22
|
+
|
|
23
|
+
OpenMagnetics::Inputs process_flyback_internal(const json& converterJson, bool useNgspice) {
|
|
24
|
+
OpenMagnetics::AdvancedFlyback converter(converterJson);
|
|
25
|
+
converter._assertErrors = true;
|
|
26
|
+
|
|
27
|
+
if (useNgspice) {
|
|
28
|
+
// Get design parameters directly from AdvancedFlyback
|
|
29
|
+
std::vector<double> turnsRatios = converter.get_desired_turns_ratios();
|
|
30
|
+
double inductance = converter.get_desired_inductance();
|
|
31
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
32
|
+
|
|
33
|
+
// Build DesignRequirements manually like AdvancedFlyback::process() does
|
|
34
|
+
OpenMagnetics::DesignRequirements designReqs;
|
|
35
|
+
designReqs.get_mutable_turns_ratios().clear();
|
|
36
|
+
for (auto turnsRatio : turnsRatios) {
|
|
37
|
+
OpenMagnetics::DimensionWithTolerance turnsRatioWithTolerance;
|
|
38
|
+
turnsRatioWithTolerance.set_nominal(turnsRatio);
|
|
39
|
+
designReqs.get_mutable_turns_ratios().push_back(turnsRatioWithTolerance);
|
|
40
|
+
}
|
|
41
|
+
OpenMagnetics::DimensionWithTolerance inductanceWithTolerance;
|
|
42
|
+
inductanceWithTolerance.set_nominal(inductance);
|
|
43
|
+
designReqs.set_magnetizing_inductance(inductanceWithTolerance);
|
|
44
|
+
designReqs.set_topology(MAS::Topologies::FLYBACK_CONVERTER);
|
|
45
|
+
|
|
46
|
+
OpenMagnetics::Inputs result;
|
|
47
|
+
result.set_design_requirements(designReqs);
|
|
48
|
+
result.set_operating_points(operatingPoints);
|
|
49
|
+
return result;
|
|
50
|
+
} else {
|
|
51
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
52
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::FLYBACK_CONVERTER);
|
|
53
|
+
return result;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
OpenMagnetics::Inputs process_buck_internal(const json& converterJson, bool useNgspice) {
|
|
58
|
+
OpenMagnetics::AdvancedBuck converter(converterJson);
|
|
59
|
+
converter._assertErrors = true;
|
|
60
|
+
|
|
61
|
+
if (useNgspice) {
|
|
62
|
+
auto designReqs = converter.process_design_requirements();
|
|
63
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
64
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(inductance);
|
|
65
|
+
OpenMagnetics::Inputs result;
|
|
66
|
+
result.set_design_requirements(designReqs);
|
|
67
|
+
result.set_operating_points(operatingPoints);
|
|
68
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::BUCK_CONVERTER);
|
|
69
|
+
return result;
|
|
70
|
+
} else {
|
|
71
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
72
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::BUCK_CONVERTER);
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
OpenMagnetics::Inputs process_boost_internal(const json& converterJson, bool useNgspice) {
|
|
78
|
+
OpenMagnetics::AdvancedBoost converter(converterJson);
|
|
79
|
+
converter._assertErrors = true;
|
|
80
|
+
|
|
81
|
+
if (useNgspice) {
|
|
82
|
+
auto designReqs = converter.process_design_requirements();
|
|
83
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
84
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(inductance);
|
|
85
|
+
OpenMagnetics::Inputs result;
|
|
86
|
+
result.set_design_requirements(designReqs);
|
|
87
|
+
result.set_operating_points(operatingPoints);
|
|
88
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::BOOST_CONVERTER);
|
|
89
|
+
return result;
|
|
90
|
+
} else {
|
|
91
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
92
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::BOOST_CONVERTER);
|
|
93
|
+
return result;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
OpenMagnetics::Inputs process_single_switch_forward_internal(const json& converterJson, bool useNgspice) {
|
|
98
|
+
OpenMagnetics::AdvancedSingleSwitchForward converter(converterJson);
|
|
99
|
+
converter._assertErrors = true;
|
|
100
|
+
|
|
101
|
+
if (useNgspice) {
|
|
102
|
+
auto designReqs = converter.process_design_requirements();
|
|
103
|
+
std::vector<double> turnsRatios;
|
|
104
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
105
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
106
|
+
}
|
|
107
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
108
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
109
|
+
OpenMagnetics::Inputs result;
|
|
110
|
+
result.set_design_requirements(designReqs);
|
|
111
|
+
result.set_operating_points(operatingPoints);
|
|
112
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::SINGLE_SWITCH_FORWARD_CONVERTER);
|
|
113
|
+
return result;
|
|
114
|
+
} else {
|
|
115
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
116
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::SINGLE_SWITCH_FORWARD_CONVERTER);
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
OpenMagnetics::Inputs process_two_switch_forward_internal(const json& converterJson, bool useNgspice) {
|
|
122
|
+
OpenMagnetics::AdvancedTwoSwitchForward converter(converterJson);
|
|
123
|
+
converter._assertErrors = true;
|
|
124
|
+
|
|
125
|
+
if (useNgspice) {
|
|
126
|
+
auto designReqs = converter.process_design_requirements();
|
|
127
|
+
std::vector<double> turnsRatios;
|
|
128
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
129
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
130
|
+
}
|
|
131
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
132
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
133
|
+
OpenMagnetics::Inputs result;
|
|
134
|
+
result.set_design_requirements(designReqs);
|
|
135
|
+
result.set_operating_points(operatingPoints);
|
|
136
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::TWO_SWITCH_FORWARD_CONVERTER);
|
|
137
|
+
return result;
|
|
138
|
+
} else {
|
|
139
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
140
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::TWO_SWITCH_FORWARD_CONVERTER);
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
OpenMagnetics::Inputs process_active_clamp_forward_internal(const json& converterJson, bool useNgspice) {
|
|
146
|
+
OpenMagnetics::AdvancedActiveClampForward converter(converterJson);
|
|
147
|
+
converter._assertErrors = true;
|
|
148
|
+
|
|
149
|
+
if (useNgspice) {
|
|
150
|
+
auto designReqs = converter.process_design_requirements();
|
|
151
|
+
std::vector<double> turnsRatios;
|
|
152
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
153
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
154
|
+
}
|
|
155
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
156
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
157
|
+
OpenMagnetics::Inputs result;
|
|
158
|
+
result.set_design_requirements(designReqs);
|
|
159
|
+
result.set_operating_points(operatingPoints);
|
|
160
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ACTIVE_CLAMP_FORWARD_CONVERTER);
|
|
161
|
+
return result;
|
|
162
|
+
} else {
|
|
163
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
164
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ACTIVE_CLAMP_FORWARD_CONVERTER);
|
|
165
|
+
return result;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
OpenMagnetics::Inputs process_push_pull_internal(const json& converterJson, bool useNgspice) {
|
|
170
|
+
OpenMagnetics::AdvancedPushPull converter(converterJson);
|
|
171
|
+
converter._assertErrors = true;
|
|
172
|
+
|
|
173
|
+
if (useNgspice) {
|
|
174
|
+
auto designReqs = converter.process_design_requirements();
|
|
175
|
+
std::vector<double> turnsRatios;
|
|
176
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
177
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
178
|
+
}
|
|
179
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
180
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
181
|
+
OpenMagnetics::Inputs result;
|
|
182
|
+
result.set_design_requirements(designReqs);
|
|
183
|
+
result.set_operating_points(operatingPoints);
|
|
184
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::PUSH_PULL_CONVERTER);
|
|
185
|
+
return result;
|
|
186
|
+
} else {
|
|
187
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
188
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::PUSH_PULL_CONVERTER);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
OpenMagnetics::Inputs process_llc_internal(const json& converterJson, bool useNgspice) {
|
|
194
|
+
OpenMagnetics::AdvancedLlc converter(converterJson);
|
|
195
|
+
converter._assertErrors = true;
|
|
196
|
+
|
|
197
|
+
if (useNgspice) {
|
|
198
|
+
auto designReqs = converter.process_design_requirements();
|
|
199
|
+
std::vector<double> turnsRatios;
|
|
200
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
201
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
202
|
+
}
|
|
203
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
204
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
205
|
+
OpenMagnetics::Inputs result;
|
|
206
|
+
result.set_design_requirements(designReqs);
|
|
207
|
+
result.set_operating_points(operatingPoints);
|
|
208
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::LLC_RESONANT_CONVERTER);
|
|
209
|
+
return result;
|
|
210
|
+
} else {
|
|
211
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
212
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::LLC_RESONANT_CONVERTER);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
OpenMagnetics::Inputs process_cllc_internal(const json& converterJson, bool useNgspice) {
|
|
218
|
+
OpenMagnetics::AdvancedCllcConverter converter(converterJson);
|
|
219
|
+
converter._assertErrors = true;
|
|
220
|
+
|
|
221
|
+
if (useNgspice) {
|
|
222
|
+
auto designReqs = converter.process_design_requirements();
|
|
223
|
+
std::vector<double> turnsRatios;
|
|
224
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
225
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
226
|
+
}
|
|
227
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
228
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
229
|
+
OpenMagnetics::Inputs result;
|
|
230
|
+
result.set_design_requirements(designReqs);
|
|
231
|
+
result.set_operating_points(operatingPoints);
|
|
232
|
+
return result;
|
|
233
|
+
} else {
|
|
234
|
+
return converter.process();
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
OpenMagnetics::Inputs process_dab_internal(const json& converterJson, bool useNgspice) {
|
|
239
|
+
OpenMagnetics::AdvancedDab converter(converterJson);
|
|
240
|
+
converter._assertErrors = true;
|
|
241
|
+
|
|
242
|
+
if (useNgspice) {
|
|
243
|
+
auto designReqs = converter.process_design_requirements();
|
|
244
|
+
std::vector<double> turnsRatios;
|
|
245
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
246
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
247
|
+
}
|
|
248
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
249
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
250
|
+
OpenMagnetics::Inputs result;
|
|
251
|
+
result.set_design_requirements(designReqs);
|
|
252
|
+
result.set_operating_points(operatingPoints);
|
|
253
|
+
return result;
|
|
254
|
+
} else {
|
|
255
|
+
return converter.process();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
OpenMagnetics::Inputs process_psfb_internal(const json& converterJson, bool useNgspice) {
|
|
260
|
+
OpenMagnetics::AdvancedPsfb converter(converterJson);
|
|
261
|
+
converter._assertErrors = true;
|
|
262
|
+
|
|
263
|
+
if (useNgspice) {
|
|
264
|
+
auto designReqs = converter.process_design_requirements();
|
|
265
|
+
std::vector<double> turnsRatios;
|
|
266
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
267
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
268
|
+
}
|
|
269
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
270
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
271
|
+
OpenMagnetics::Inputs result;
|
|
272
|
+
result.set_design_requirements(designReqs);
|
|
273
|
+
result.set_operating_points(operatingPoints);
|
|
274
|
+
return result;
|
|
275
|
+
} else {
|
|
276
|
+
return converter.process();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
OpenMagnetics::Inputs process_pshb_internal(const json& converterJson, bool useNgspice) {
|
|
281
|
+
OpenMagnetics::AdvancedPshb converter(converterJson);
|
|
282
|
+
converter._assertErrors = true;
|
|
283
|
+
|
|
284
|
+
if (useNgspice) {
|
|
285
|
+
auto designReqs = converter.process_design_requirements();
|
|
286
|
+
std::vector<double> turnsRatios;
|
|
287
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
288
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
289
|
+
}
|
|
290
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
291
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
292
|
+
OpenMagnetics::Inputs result;
|
|
293
|
+
result.set_design_requirements(designReqs);
|
|
294
|
+
result.set_operating_points(operatingPoints);
|
|
295
|
+
return result;
|
|
296
|
+
} else {
|
|
297
|
+
return converter.process();
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
OpenMagnetics::Inputs process_isolated_buck_internal(const json& converterJson, bool useNgspice) {
|
|
302
|
+
OpenMagnetics::AdvancedIsolatedBuck converter(converterJson);
|
|
303
|
+
converter._assertErrors = true;
|
|
304
|
+
|
|
305
|
+
if (useNgspice) {
|
|
306
|
+
auto designReqs = converter.process_design_requirements();
|
|
307
|
+
std::vector<double> turnsRatios;
|
|
308
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
309
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
310
|
+
}
|
|
311
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
312
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
313
|
+
OpenMagnetics::Inputs result;
|
|
314
|
+
result.set_design_requirements(designReqs);
|
|
315
|
+
result.set_operating_points(operatingPoints);
|
|
316
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ISOLATED_BUCK_CONVERTER);
|
|
317
|
+
return result;
|
|
318
|
+
} else {
|
|
319
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
320
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ISOLATED_BUCK_CONVERTER);
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
OpenMagnetics::Inputs process_isolated_buck_boost_internal(const json& converterJson, bool useNgspice) {
|
|
326
|
+
OpenMagnetics::AdvancedIsolatedBuckBoost converter(converterJson);
|
|
327
|
+
converter._assertErrors = true;
|
|
328
|
+
|
|
329
|
+
if (useNgspice) {
|
|
330
|
+
auto designReqs = converter.process_design_requirements();
|
|
331
|
+
std::vector<double> turnsRatios;
|
|
332
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
333
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
334
|
+
}
|
|
335
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
336
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios, inductance);
|
|
337
|
+
OpenMagnetics::Inputs result;
|
|
338
|
+
result.set_design_requirements(designReqs);
|
|
339
|
+
result.set_operating_points(operatingPoints);
|
|
340
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ISOLATED_BUCK_BOOST_CONVERTER);
|
|
341
|
+
return result;
|
|
342
|
+
} else {
|
|
343
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
344
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::ISOLATED_BUCK_BOOST_CONVERTER);
|
|
345
|
+
return result;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
OpenMagnetics::Inputs process_current_transformer_internal(const json& converterJson) {
|
|
350
|
+
OpenMagnetics::CurrentTransformer converter(converterJson);
|
|
351
|
+
converter._assertErrors = true;
|
|
352
|
+
|
|
353
|
+
double turnsRatio = converterJson.contains("turnsRatio") ? converterJson["turnsRatio"].get<double>() : 100.0;
|
|
354
|
+
double secondaryResistance = converterJson.contains("secondaryResistance") ? converterJson["secondaryResistance"].get<double>() : 0.0;
|
|
355
|
+
OpenMagnetics::Inputs result = converter.process(turnsRatio, secondaryResistance);
|
|
356
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::CURRENT_TRANSFORMER);
|
|
357
|
+
return result;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
OpenMagnetics::Inputs process_pfc_internal(const json& converterJson) {
|
|
361
|
+
OpenMagnetics::PowerFactorCorrection converter(converterJson);
|
|
362
|
+
converter._assertErrors = true;
|
|
363
|
+
return converter.process();
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
OpenMagnetics::Inputs dispatch_converter(const std::string& topologyName, const json& converterJson, bool useNgspice) {
|
|
367
|
+
if (topologyName == "flyback" || topologyName == "advanced_flyback") {
|
|
368
|
+
return process_flyback_internal(converterJson, useNgspice);
|
|
369
|
+
}
|
|
370
|
+
else if (topologyName == "buck" || topologyName == "advanced_buck") {
|
|
371
|
+
return process_buck_internal(converterJson, useNgspice);
|
|
372
|
+
}
|
|
373
|
+
else if (topologyName == "boost" || topologyName == "advanced_boost") {
|
|
374
|
+
return process_boost_internal(converterJson, useNgspice);
|
|
375
|
+
}
|
|
376
|
+
else if (topologyName == "single_switch_forward") {
|
|
377
|
+
return process_single_switch_forward_internal(converterJson, useNgspice);
|
|
378
|
+
}
|
|
379
|
+
else if (topologyName == "two_switch_forward") {
|
|
380
|
+
return process_two_switch_forward_internal(converterJson, useNgspice);
|
|
381
|
+
}
|
|
382
|
+
else if (topologyName == "active_clamp_forward") {
|
|
383
|
+
return process_active_clamp_forward_internal(converterJson, useNgspice);
|
|
384
|
+
}
|
|
385
|
+
else if (topologyName == "push_pull") {
|
|
386
|
+
return process_push_pull_internal(converterJson, useNgspice);
|
|
387
|
+
}
|
|
388
|
+
else if (topologyName == "llc" || topologyName == "advanced_llc") {
|
|
389
|
+
return process_llc_internal(converterJson, useNgspice);
|
|
390
|
+
}
|
|
391
|
+
else if (topologyName == "cllc" || topologyName == "advanced_cllc") {
|
|
392
|
+
return process_cllc_internal(converterJson, useNgspice);
|
|
393
|
+
}
|
|
394
|
+
else if (topologyName == "dab" || topologyName == "advanced_dab") {
|
|
395
|
+
return process_dab_internal(converterJson, useNgspice);
|
|
396
|
+
}
|
|
397
|
+
else if (topologyName == "phase_shifted_full_bridge" || topologyName == "psfb") {
|
|
398
|
+
return process_psfb_internal(converterJson, useNgspice);
|
|
399
|
+
}
|
|
400
|
+
else if (topologyName == "phase_shifted_half_bridge" || topologyName == "pshb") {
|
|
401
|
+
return process_pshb_internal(converterJson, useNgspice);
|
|
402
|
+
}
|
|
403
|
+
else if (topologyName == "isolated_buck") {
|
|
404
|
+
return process_isolated_buck_internal(converterJson, useNgspice);
|
|
405
|
+
}
|
|
406
|
+
else if (topologyName == "isolated_buck_boost") {
|
|
407
|
+
return process_isolated_buck_boost_internal(converterJson, useNgspice);
|
|
408
|
+
}
|
|
409
|
+
else if (topologyName == "current_transformer") {
|
|
410
|
+
return process_current_transformer_internal(converterJson);
|
|
411
|
+
}
|
|
412
|
+
else if (topologyName == "power_factor_correction" || topologyName == "pfc") {
|
|
413
|
+
return process_pfc_internal(converterJson);
|
|
414
|
+
}
|
|
415
|
+
else {
|
|
416
|
+
throw std::invalid_argument("Unknown topology: " + topologyName);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
json process_converter_internal(const std::string& topologyName, const json& converterJson, bool useNgspice) {
|
|
421
|
+
try {
|
|
422
|
+
OpenMagnetics::Inputs inputs = dispatch_converter(topologyName, converterJson, useNgspice);
|
|
423
|
+
json result;
|
|
424
|
+
to_json(result, inputs);
|
|
425
|
+
return result;
|
|
426
|
+
}
|
|
427
|
+
catch (const std::exception& e) {
|
|
428
|
+
json error;
|
|
429
|
+
error["error"] = "Exception: " + std::string(e.what());
|
|
430
|
+
return error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
json process_converter(const std::string& topologyName, json converterJson, bool useNgspice) {
|
|
435
|
+
return process_converter_internal(topologyName, converterJson, useNgspice);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
json design_magnetics_from_converter(
|
|
439
|
+
const std::string& topologyName,
|
|
440
|
+
json converterJson,
|
|
441
|
+
int maxResults,
|
|
442
|
+
json coreModeJson,
|
|
443
|
+
bool useNgspice,
|
|
444
|
+
json weightsJson) {
|
|
445
|
+
|
|
446
|
+
try {
|
|
447
|
+
json inputsJson = process_converter_internal(topologyName, converterJson, useNgspice);
|
|
448
|
+
|
|
449
|
+
if (inputsJson.contains("error")) {
|
|
450
|
+
return inputsJson;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
OpenMagnetics::Inputs inputs(inputsJson);
|
|
454
|
+
|
|
455
|
+
OpenMagnetics::CoreAdviser::CoreAdviserModes coreMode;
|
|
456
|
+
from_json(coreModeJson, coreMode);
|
|
457
|
+
|
|
458
|
+
std::map<OpenMagnetics::MagneticFilters, double> weights;
|
|
459
|
+
if (!weightsJson.is_null()) {
|
|
460
|
+
for (auto& [key, value] : weightsJson.items()) {
|
|
461
|
+
OpenMagnetics::MagneticFilters filter;
|
|
462
|
+
OpenMagnetics::from_json(key, filter);
|
|
463
|
+
weights[filter] = value;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
OpenMagnetics::MagneticAdviser magneticAdviser;
|
|
468
|
+
magneticAdviser.set_core_mode(coreMode);
|
|
469
|
+
|
|
470
|
+
std::vector<std::pair<OpenMagnetics::Mas, double>> masMagnetics;
|
|
471
|
+
if (weights.empty()) {
|
|
472
|
+
masMagnetics = magneticAdviser.get_advised_magnetic(inputs, maxResults);
|
|
473
|
+
} else {
|
|
474
|
+
masMagnetics = magneticAdviser.get_advised_magnetic(inputs, weights, maxResults);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
auto scoringsPerFilter = magneticAdviser.get_scorings();
|
|
478
|
+
|
|
479
|
+
json results = json();
|
|
480
|
+
results["data"] = json::array();
|
|
481
|
+
for (size_t i = 0; i < masMagnetics.size(); ++i) {
|
|
482
|
+
auto& masMagnetic = masMagnetics[i].first;
|
|
483
|
+
double scoring = masMagnetics[i].second;
|
|
484
|
+
std::string name = masMagnetic.get_magnetic().get_manufacturer_info().value().get_reference().value();
|
|
485
|
+
json result;
|
|
486
|
+
json masJson;
|
|
487
|
+
to_json(masJson, masMagnetic);
|
|
488
|
+
result["mas"] = masJson;
|
|
489
|
+
result["scoring"] = scoring;
|
|
490
|
+
if (scoringsPerFilter.count(name)) {
|
|
491
|
+
json filterScorings;
|
|
492
|
+
for (auto& filterPair : scoringsPerFilter[name]) {
|
|
493
|
+
auto filter = filterPair.first;
|
|
494
|
+
double filterScore = filterPair.second;
|
|
495
|
+
filterScorings[std::string(magic_enum::enum_name(filter))] = filterScore;
|
|
496
|
+
}
|
|
497
|
+
result["scoringPerFilter"] = filterScorings;
|
|
498
|
+
}
|
|
499
|
+
results["data"].push_back(result);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
sort(results["data"].begin(), results["data"].end(), [](json& b1, json& b2) {
|
|
503
|
+
return b1["scoring"] > b2["scoring"];
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
OpenMagnetics::settings.reset();
|
|
507
|
+
|
|
508
|
+
return results;
|
|
509
|
+
}
|
|
510
|
+
catch (const std::exception& e) {
|
|
511
|
+
json error;
|
|
512
|
+
error["error"] = "Exception: " + std::string(e.what());
|
|
513
|
+
return error;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
json process_flyback(json flybackJson) {
|
|
518
|
+
return process_converter("flyback", flybackJson, true);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
json process_buck(json buckJson) {
|
|
522
|
+
return process_converter("buck", buckJson, true);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
json process_boost(json boostJson) {
|
|
526
|
+
return process_converter("boost", boostJson, true);
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
json process_single_switch_forward(json forwardJson) {
|
|
530
|
+
return process_converter("single_switch_forward", forwardJson, true);
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
json process_two_switch_forward(json forwardJson) {
|
|
534
|
+
return process_converter("two_switch_forward", forwardJson, true);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
json process_active_clamp_forward(json forwardJson) {
|
|
538
|
+
return process_converter("active_clamp_forward", forwardJson, true);
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
json process_push_pull(json pushPullJson) {
|
|
542
|
+
return process_converter("push_pull", pushPullJson, true);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
json process_isolated_buck(json isolatedBuckJson) {
|
|
546
|
+
return process_converter("isolated_buck", isolatedBuckJson, true);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
json process_isolated_buck_boost(json isolatedBuckBoostJson) {
|
|
550
|
+
return process_converter("isolated_buck_boost", isolatedBuckBoostJson, true);
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
json process_current_transformer(json ctJson, double turnsRatio, double secondaryResistance) {
|
|
554
|
+
ctJson["turnsRatio"] = turnsRatio;
|
|
555
|
+
ctJson["secondaryResistance"] = secondaryResistance;
|
|
556
|
+
return process_converter("current_transformer", ctJson, true);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
void register_converter_bindings(py::module& m) {
|
|
560
|
+
m.def("process_converter", &process_converter,
|
|
561
|
+
"Process a converter topology specification to Inputs.",
|
|
562
|
+
py::arg("topology_name"), py::arg("converter_json"), py::arg("use_ngspice") = true);
|
|
563
|
+
|
|
564
|
+
m.def("design_magnetics_from_converter", &design_magnetics_from_converter,
|
|
565
|
+
"Design magnetic components from a converter specification.",
|
|
566
|
+
py::arg("topology_name"), py::arg("converter_json"),
|
|
567
|
+
py::arg("max_results") = 1, py::arg("core_mode_json") = "AVAILABLE_CORES",
|
|
568
|
+
py::arg("use_ngspice") = true, py::arg("weights_json") = nullptr);
|
|
569
|
+
|
|
570
|
+
m.def("process_flyback", &process_flyback, "Process Flyback converter.", py::arg("flyback"));
|
|
571
|
+
m.def("process_buck", &process_buck, "Process Buck converter.", py::arg("buck"));
|
|
572
|
+
m.def("process_boost", &process_boost, "Process Boost converter.", py::arg("boost"));
|
|
573
|
+
m.def("process_single_switch_forward", &process_single_switch_forward, "Process Single-Switch Forward.", py::arg("forward"));
|
|
574
|
+
m.def("process_two_switch_forward", &process_two_switch_forward, "Process Two-Switch Forward.", py::arg("forward"));
|
|
575
|
+
m.def("process_active_clamp_forward", &process_active_clamp_forward, "Process Active Clamp Forward.", py::arg("forward"));
|
|
576
|
+
m.def("process_push_pull", &process_push_pull, "Process Push-Pull converter.", py::arg("push_pull"));
|
|
577
|
+
m.def("process_isolated_buck", &process_isolated_buck, "Process Isolated Buck.", py::arg("isolated_buck"));
|
|
578
|
+
m.def("process_isolated_buck_boost", &process_isolated_buck_boost, "Process Isolated Buck-Boost.", py::arg("isolated_buck_boost"));
|
|
579
|
+
m.def("process_current_transformer", &process_current_transformer, "Process Current Transformer.",
|
|
580
|
+
py::arg("ct"), py::arg("turns_ratio"), py::arg("secondary_resistance") = 0.0);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
} // namespace PyMKF
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include "common.h"
|
|
4
|
+
|
|
5
|
+
namespace PyMKF {
|
|
6
|
+
|
|
7
|
+
// Main generic converter processor
|
|
8
|
+
json process_converter(const std::string& topologyName, json converterJson, bool useNgspice = true);
|
|
9
|
+
|
|
10
|
+
// Combined endpoint: converter -> magnetic designs
|
|
11
|
+
json design_magnetics_from_converter(
|
|
12
|
+
const std::string& topologyName,
|
|
13
|
+
json converterJson,
|
|
14
|
+
int maxResults,
|
|
15
|
+
json coreModeJson,
|
|
16
|
+
bool useNgspice = true,
|
|
17
|
+
json weightsJson = nullptr);
|
|
18
|
+
|
|
19
|
+
// Per-topology thin wrappers (from .pyi stubs)
|
|
20
|
+
json process_flyback(json flybackJson);
|
|
21
|
+
json process_buck(json buckJson);
|
|
22
|
+
json process_boost(json boostJson);
|
|
23
|
+
json process_single_switch_forward(json forwardJson);
|
|
24
|
+
json process_two_switch_forward(json forwardJson);
|
|
25
|
+
json process_active_clamp_forward(json forwardJson);
|
|
26
|
+
json process_push_pull(json pushPullJson);
|
|
27
|
+
json process_isolated_buck(json isolatedBuckJson);
|
|
28
|
+
json process_isolated_buck_boost(json isolatedBuckBoostJson);
|
|
29
|
+
json process_current_transformer(json ctJson, double turnsRatio, double secondaryResistance = 0.0);
|
|
30
|
+
|
|
31
|
+
void register_converter_bindings(py::module& m);
|
|
32
|
+
|
|
33
|
+
} // namespace PyMKF
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
#include "bobbin.h"
|
|
6
6
|
#include "winding.h"
|
|
7
7
|
#include "advisers.h"
|
|
8
|
+
#include "converter.h"
|
|
8
9
|
#include "losses.h"
|
|
9
10
|
#include "simulation.h"
|
|
10
11
|
#include "plotting.h"
|
|
@@ -22,6 +23,7 @@ PYBIND11_MODULE(PyOpenMagnetics, m) {
|
|
|
22
23
|
PyMKF::register_bobbin_bindings(m);
|
|
23
24
|
PyMKF::register_winding_bindings(m);
|
|
24
25
|
PyMKF::register_adviser_bindings(m);
|
|
26
|
+
PyMKF::register_converter_bindings(m);
|
|
25
27
|
PyMKF::register_losses_bindings(m);
|
|
26
28
|
PyMKF::register_simulation_bindings(m);
|
|
27
29
|
PyMKF::register_plotting_bindings(m);
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
"""Tests for converter topology endpoints."""
|
|
2
|
+
import json
|
|
3
|
+
import PyOpenMagnetics as PyMKF
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def test_process_flyback():
|
|
7
|
+
"""Test Flyback converter processing."""
|
|
8
|
+
flyback = {
|
|
9
|
+
"inputVoltage": {"minimum": 150, "maximum": 150},
|
|
10
|
+
"desiredInductance": 400e-6,
|
|
11
|
+
"desiredTurnsRatios": [6.14],
|
|
12
|
+
"desiredDutyCycle": [[0.45, 0.45]],
|
|
13
|
+
"maximumDutyCycle": 0.5,
|
|
14
|
+
"efficiency": 0.9,
|
|
15
|
+
"diodeVoltageDrop": 0.7,
|
|
16
|
+
"operatingPoints": [{
|
|
17
|
+
"outputVoltages": [20.0],
|
|
18
|
+
"outputCurrents": [1.5],
|
|
19
|
+
"switchingFrequency": 100000,
|
|
20
|
+
"ambientTemperature": 25
|
|
21
|
+
}]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
# Test with ngspice (default)
|
|
25
|
+
result = PyMKF.process_converter("flyback", flyback, use_ngspice=True)
|
|
26
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
27
|
+
assert "designRequirements" in result
|
|
28
|
+
assert "operatingPoints" in result
|
|
29
|
+
print("✓ Flyback converter processed successfully")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def test_process_buck():
|
|
33
|
+
"""Test Buck converter processing."""
|
|
34
|
+
buck = {
|
|
35
|
+
"inputVoltage": {"minimum": 12, "maximum": 12},
|
|
36
|
+
"desiredInductance": 10e-6,
|
|
37
|
+
"diodeVoltageDrop": 0.7,
|
|
38
|
+
"operatingPoints": [{
|
|
39
|
+
"outputVoltage": 5.0,
|
|
40
|
+
"outputCurrent": 2.0,
|
|
41
|
+
"switchingFrequency": 100000,
|
|
42
|
+
"ambientTemperature": 25
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
result = PyMKF.process_converter("buck", buck, use_ngspice=False) # Use analytical for speed
|
|
47
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
48
|
+
assert "designRequirements" in result
|
|
49
|
+
assert "operatingPoints" in result
|
|
50
|
+
print("✓ Buck converter processed successfully")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def test_process_boost():
|
|
54
|
+
"""Test Boost converter processing."""
|
|
55
|
+
boost = {
|
|
56
|
+
"inputVoltage": {"minimum": 5, "maximum": 5},
|
|
57
|
+
"desiredInductance": 10e-6,
|
|
58
|
+
"diodeVoltageDrop": 0.7,
|
|
59
|
+
"operatingPoints": [{
|
|
60
|
+
"outputVoltage": 12.0,
|
|
61
|
+
"outputCurrent": 1.0,
|
|
62
|
+
"switchingFrequency": 100000,
|
|
63
|
+
"ambientTemperature": 25
|
|
64
|
+
}]
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
result = PyMKF.process_converter("boost", boost, use_ngspice=False)
|
|
68
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
69
|
+
assert "designRequirements" in result
|
|
70
|
+
assert "operatingPoints" in result
|
|
71
|
+
print("✓ Boost converter processed successfully")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_design_magnetics_from_flyback():
|
|
75
|
+
"""Test full magnetic design from Flyback converter."""
|
|
76
|
+
flyback = {
|
|
77
|
+
"inputVoltage": {"minimum": 150, "maximum": 150},
|
|
78
|
+
"desiredInductance": 400e-6,
|
|
79
|
+
"desiredTurnsRatios": [6.14],
|
|
80
|
+
"desiredDutyCycle": [[0.45, 0.45]],
|
|
81
|
+
"maximumDutyCycle": 0.5,
|
|
82
|
+
"efficiency": 0.9,
|
|
83
|
+
"diodeVoltageDrop": 0.7,
|
|
84
|
+
"operatingPoints": [{
|
|
85
|
+
"outputVoltages": [20.0],
|
|
86
|
+
"outputCurrents": [1.5],
|
|
87
|
+
"switchingFrequency": 100000,
|
|
88
|
+
"ambientTemperature": 25
|
|
89
|
+
}]
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
# Design with analytical waveforms (faster)
|
|
93
|
+
# Note: This test is skipped in CI as magnetic adviser can take a long time
|
|
94
|
+
print("✓ Magnetic design test skipped (too slow for unit test)")
|
|
95
|
+
print(" Use design_magnetics_from_converter() for full magnetic design")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def test_per_topology_wrappers():
|
|
99
|
+
"""Test per-topology wrapper functions."""
|
|
100
|
+
|
|
101
|
+
# Test process_flyback wrapper
|
|
102
|
+
flyback = {
|
|
103
|
+
"inputVoltage": {"minimum": 100, "maximum": 100},
|
|
104
|
+
"desiredInductance": 1e-3,
|
|
105
|
+
"desiredTurnsRatios": [10.0],
|
|
106
|
+
"desiredDutyCycle": [[0.45]],
|
|
107
|
+
"maximumDutyCycle": 0.5,
|
|
108
|
+
"diodeVoltageDrop": 0.7,
|
|
109
|
+
"efficiency": 0.9,
|
|
110
|
+
"operatingPoints": [{
|
|
111
|
+
"outputVoltages": [10.0],
|
|
112
|
+
"outputCurrents": [1.0],
|
|
113
|
+
"switchingFrequency": 100000,
|
|
114
|
+
"ambientTemperature": 25
|
|
115
|
+
}]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
result = PyMKF.process_flyback(flyback)
|
|
119
|
+
assert "error" not in result
|
|
120
|
+
print("✓ process_flyback wrapper works")
|
|
121
|
+
|
|
122
|
+
# Test process_buck wrapper
|
|
123
|
+
buck = {
|
|
124
|
+
"inputVoltage": {"minimum": 12, "maximum": 12},
|
|
125
|
+
"desiredInductance": 10e-6,
|
|
126
|
+
"diodeVoltageDrop": 0.7,
|
|
127
|
+
"currentRippleRatio": 0.3,
|
|
128
|
+
"operatingPoints": [{
|
|
129
|
+
"outputVoltage": 5.0,
|
|
130
|
+
"outputCurrent": 2.0,
|
|
131
|
+
"switchingFrequency": 100000,
|
|
132
|
+
"ambientTemperature": 25
|
|
133
|
+
}]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
result = PyMKF.process_buck(buck)
|
|
137
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
138
|
+
print("✓ process_buck wrapper works")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def test_invalid_topology():
|
|
142
|
+
"""Test error handling for invalid topology."""
|
|
143
|
+
converter = {"some": "data"}
|
|
144
|
+
|
|
145
|
+
result = PyMKF.process_converter("invalid_topology", converter)
|
|
146
|
+
assert "error" in result
|
|
147
|
+
assert "Unknown topology" in result["error"]
|
|
148
|
+
print("✓ Invalid topology error handling works")
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def test_llc_converter():
|
|
152
|
+
"""Test LLC resonant converter."""
|
|
153
|
+
llc = {
|
|
154
|
+
"inputVoltage": {"minimum": 400, "maximum": 400},
|
|
155
|
+
"desiredInductance": 100e-6,
|
|
156
|
+
"desiredTurnsRatios": [1.0],
|
|
157
|
+
"minSwitchingFrequency": 80000,
|
|
158
|
+
"maxSwitchingFrequency": 120000,
|
|
159
|
+
"operatingPoints": [{
|
|
160
|
+
"outputVoltages": [48.0],
|
|
161
|
+
"outputCurrents": [5.0],
|
|
162
|
+
"switchingFrequency": 100000,
|
|
163
|
+
"ambientTemperature": 25
|
|
164
|
+
}]
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
result = PyMKF.process_converter("llc", llc, use_ngspice=False)
|
|
168
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
169
|
+
assert "designRequirements" in result
|
|
170
|
+
print("✓ LLC converter processed successfully")
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_forward_converters():
|
|
174
|
+
"""Test Forward converter variants."""
|
|
175
|
+
# Single switch forward: turns ratios must have one more position than outputs for demagnetization winding
|
|
176
|
+
single_switch_forward = {
|
|
177
|
+
"inputVoltage": {"minimum": 48, "maximum": 48},
|
|
178
|
+
"desiredInductance": 1e-3,
|
|
179
|
+
"desiredTurnsRatios": [1.0, 2.0], # [demagnetization, output]
|
|
180
|
+
"diodeVoltageDrop": 0.7,
|
|
181
|
+
"currentRippleRatio": 0.2,
|
|
182
|
+
"operatingPoints": [{
|
|
183
|
+
"outputVoltages": [12.0],
|
|
184
|
+
"outputCurrents": [5.0],
|
|
185
|
+
"switchingFrequency": 100000,
|
|
186
|
+
"ambientTemperature": 25
|
|
187
|
+
}]
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# Single switch forward
|
|
191
|
+
result = PyMKF.process_converter("single_switch_forward", single_switch_forward, use_ngspice=False)
|
|
192
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
193
|
+
print("✓ Single-switch forward processed successfully")
|
|
194
|
+
|
|
195
|
+
# Two switch forward and Active clamp forward: turns ratios must have same positions as outputs
|
|
196
|
+
other_forward = {
|
|
197
|
+
"inputVoltage": {"minimum": 48, "maximum": 48},
|
|
198
|
+
"desiredInductance": 1e-3,
|
|
199
|
+
"desiredTurnsRatios": [2.0], # Same as number of outputs
|
|
200
|
+
"diodeVoltageDrop": 0.7,
|
|
201
|
+
"currentRippleRatio": 0.2,
|
|
202
|
+
"operatingPoints": [{
|
|
203
|
+
"outputVoltages": [12.0],
|
|
204
|
+
"outputCurrents": [5.0],
|
|
205
|
+
"switchingFrequency": 100000,
|
|
206
|
+
"ambientTemperature": 25
|
|
207
|
+
}]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
# Two switch forward
|
|
211
|
+
result = PyMKF.process_converter("two_switch_forward", other_forward, use_ngspice=False)
|
|
212
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
213
|
+
print("✓ Two-switch forward processed successfully")
|
|
214
|
+
|
|
215
|
+
# Active clamp forward
|
|
216
|
+
result = PyMKF.process_converter("active_clamp_forward", other_forward, use_ngspice=False)
|
|
217
|
+
assert "error" not in result, f"Error: {result.get('error')}"
|
|
218
|
+
print("✓ Active clamp forward processed successfully")
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
if __name__ == "__main__":
|
|
222
|
+
print("Running converter topology tests...\n")
|
|
223
|
+
|
|
224
|
+
test_process_flyback()
|
|
225
|
+
test_process_buck()
|
|
226
|
+
test_process_boost()
|
|
227
|
+
test_llc_converter()
|
|
228
|
+
test_forward_converters()
|
|
229
|
+
test_per_topology_wrappers()
|
|
230
|
+
test_invalid_topology()
|
|
231
|
+
|
|
232
|
+
print("\n--- Full Design Flow Test ---")
|
|
233
|
+
test_design_magnetics_from_flyback()
|
|
234
|
+
|
|
235
|
+
print("\n✓ All tests passed!")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|