PyOpenMagnetics 1.4.6__tar.gz → 1.5.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.4.6 → pyopenmagnetics-1.5.1}/CMakeLists.txt +9 -1
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/PKG-INFO +1 -1
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/complete_simulation_example.py +1 -2
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_220v_12v_1a.py +1 -2
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_220v_12v_2a_complete.py +1 -2
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_design.py +1 -2
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/pyproject.toml +1 -1
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/converter.cpp +248 -88
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/converter.h +7 -6
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/core.cpp +98 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/core.h +1 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/losses.cpp +0 -23
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/losses.h +0 -1
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/simulation.cpp +1 -6
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_core.py +84 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/.gitignore +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/AGENTS.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/LICENSE +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/README.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/__init__.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/api/MAS.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/api/validation.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/docs/errors.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/docs/performance.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/README.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/llms.txt +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/requirements.txt +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/advisers.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/common.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/database.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/database.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/logging.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/module.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/plotting.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/settings.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/simulation.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/utils.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/winding.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/src/wire.h +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/test.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.4.6 → pyopenmagnetics-1.5.1}/tests/test_winding.py +0 -0
|
@@ -218,7 +218,15 @@ add_custom_command(
|
|
|
218
218
|
OUTPUT "${MAS_DIRECTORY}/MAS.hpp"
|
|
219
219
|
# Force regeneration by removing output first
|
|
220
220
|
COMMAND ${CMAKE_COMMAND} -E remove -f ${MAS_DIRECTORY}/MAS.hpp
|
|
221
|
-
COMMAND quicktype -l c++ -s schema ${MAS_DIR}/schemas/MAS.json
|
|
221
|
+
COMMAND quicktype -l c++ -s schema ${MAS_DIR}/schemas/MAS.json
|
|
222
|
+
# PEAS schemas: MAS main ($id https://psma.com/mas/...) now $refs shared
|
|
223
|
+
# PEAS primitives (https://psma.com/peas/...). quicktype resolves those
|
|
224
|
+
# external $refs by $id from sources passed with -S, so the PEAS schemas
|
|
225
|
+
# must be fed in here. PEAS is an MKF submodule at ${MKF_DIR}/PEAS.
|
|
226
|
+
-S ${MKF_DIR}/PEAS/schemas/utils.json
|
|
227
|
+
-S ${MKF_DIR}/PEAS/schemas/inputs/operatingPointExcitation.json
|
|
228
|
+
-S ${MKF_DIR}/PEAS/schemas/outputs/insulationCoordination.json
|
|
229
|
+
-S ${MKF_DIR}/PEAS/schemas/outputs/lossElementPerHarmonic.json
|
|
222
230
|
-S ${MAS_DIR}/schemas/magnetic.json
|
|
223
231
|
-S ${MAS_DIR}/schemas/magnetic/core.json
|
|
224
232
|
-S ${MAS_DIR}/schemas/magnetic/coil.json
|
|
@@ -149,7 +149,75 @@ static std::string normalize_topology_name(const std::string& s) {
|
|
|
149
149
|
return s;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
153
|
+
// Base-spec fast path (ABT #11)
|
|
154
|
+
//
|
|
155
|
+
// The fast path normally constructs an Advanced* converter whose from_json
|
|
156
|
+
// hard-requires `desiredInductance` (e.g. AdvancedBuck, MKF Buck.h:219). A
|
|
157
|
+
// caller passing a BASE spec (operating point + currentRippleRatio, no
|
|
158
|
+
// desiredInductance) would otherwise get
|
|
159
|
+
// `[json.exception.out_of_range.403] key 'desiredInductance' not found`.
|
|
160
|
+
//
|
|
161
|
+
// When `desiredInductance` is absent we instead construct the BASE converter
|
|
162
|
+
// class, whose virtual process_design_requirements() DERIVES the inductance
|
|
163
|
+
// from the operating points + currentRippleRatio (or maximumSwitchCurrent) —
|
|
164
|
+
// the same MKF math the slow path uses. No L-derivation is duplicated here;
|
|
165
|
+
// it all stays in MKF (house rule). If neither desiredInductance nor a ripple
|
|
166
|
+
// driver is present, the base class throws a loud, specific exception rather
|
|
167
|
+
// than substituting a default.
|
|
168
|
+
//
|
|
169
|
+
// Two helpers cover the two simulate signatures: single-inductor topologies
|
|
170
|
+
// (Buck/Boost/Cuk/…) take a scalar inductance; transformer topologies
|
|
171
|
+
// (Flyback/Forward family/IsolatedBuck/…) take (turnsRatios, inductance).
|
|
172
|
+
// ─────────────────────────────────────────────────────────────────────
|
|
173
|
+
template <typename BaseConverter>
|
|
174
|
+
OpenMagnetics::Inputs process_base_spec_scalar_L(const json& converterJson, bool useNgspice, MAS::Topologies topology) {
|
|
175
|
+
BaseConverter converter(converterJson);
|
|
176
|
+
converter._assertErrors = true;
|
|
177
|
+
if (useNgspice) {
|
|
178
|
+
auto designReqs = converter.process_design_requirements();
|
|
179
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
180
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(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(topology);
|
|
185
|
+
return result;
|
|
186
|
+
} else {
|
|
187
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
188
|
+
result.get_mutable_design_requirements().set_topology(topology);
|
|
189
|
+
return result;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
template <typename BaseConverter>
|
|
194
|
+
OpenMagnetics::Inputs process_base_spec_vector_L(const json& converterJson, bool useNgspice, MAS::Topologies topology) {
|
|
195
|
+
BaseConverter converter(converterJson);
|
|
196
|
+
converter._assertErrors = true;
|
|
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(topology);
|
|
209
|
+
return result;
|
|
210
|
+
} else {
|
|
211
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
212
|
+
result.get_mutable_design_requirements().set_topology(topology);
|
|
213
|
+
return result;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
152
217
|
OpenMagnetics::Inputs process_flyback_internal(const json& converterJson, bool useNgspice) {
|
|
218
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
219
|
+
return process_base_spec_vector_L<OpenMagnetics::Flyback>(converterJson, useNgspice, MAS::Topologies::FLYBACK_CONVERTER);
|
|
220
|
+
}
|
|
153
221
|
OpenMagnetics::AdvancedFlyback converter(converterJson);
|
|
154
222
|
converter._assertErrors = true;
|
|
155
223
|
|
|
@@ -184,6 +252,9 @@ OpenMagnetics::Inputs process_flyback_internal(const json& converterJson, bool u
|
|
|
184
252
|
}
|
|
185
253
|
|
|
186
254
|
OpenMagnetics::Inputs process_buck_internal(const json& converterJson, bool useNgspice) {
|
|
255
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
256
|
+
return process_base_spec_scalar_L<OpenMagnetics::Buck>(converterJson, useNgspice, MAS::Topologies::BUCK_CONVERTER);
|
|
257
|
+
}
|
|
187
258
|
OpenMagnetics::AdvancedBuck converter(converterJson);
|
|
188
259
|
converter._assertErrors = true;
|
|
189
260
|
|
|
@@ -204,6 +275,9 @@ OpenMagnetics::Inputs process_buck_internal(const json& converterJson, bool useN
|
|
|
204
275
|
}
|
|
205
276
|
|
|
206
277
|
OpenMagnetics::Inputs process_boost_internal(const json& converterJson, bool useNgspice) {
|
|
278
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
279
|
+
return process_base_spec_scalar_L<OpenMagnetics::Boost>(converterJson, useNgspice, MAS::Topologies::BOOST_CONVERTER);
|
|
280
|
+
}
|
|
207
281
|
OpenMagnetics::AdvancedBoost converter(converterJson);
|
|
208
282
|
converter._assertErrors = true;
|
|
209
283
|
|
|
@@ -224,6 +298,9 @@ OpenMagnetics::Inputs process_boost_internal(const json& converterJson, bool use
|
|
|
224
298
|
}
|
|
225
299
|
|
|
226
300
|
OpenMagnetics::Inputs process_single_switch_forward_internal(const json& converterJson, bool useNgspice) {
|
|
301
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
302
|
+
return process_base_spec_vector_L<OpenMagnetics::SingleSwitchForward>(converterJson, useNgspice, MAS::Topologies::SINGLE_SWITCH_FORWARD_CONVERTER);
|
|
303
|
+
}
|
|
227
304
|
OpenMagnetics::AdvancedSingleSwitchForward converter(converterJson);
|
|
228
305
|
converter._assertErrors = true;
|
|
229
306
|
|
|
@@ -248,6 +325,9 @@ OpenMagnetics::Inputs process_single_switch_forward_internal(const json& convert
|
|
|
248
325
|
}
|
|
249
326
|
|
|
250
327
|
OpenMagnetics::Inputs process_two_switch_forward_internal(const json& converterJson, bool useNgspice) {
|
|
328
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
329
|
+
return process_base_spec_vector_L<OpenMagnetics::TwoSwitchForward>(converterJson, useNgspice, MAS::Topologies::TWO_SWITCH_FORWARD_CONVERTER);
|
|
330
|
+
}
|
|
251
331
|
OpenMagnetics::AdvancedTwoSwitchForward converter(converterJson);
|
|
252
332
|
converter._assertErrors = true;
|
|
253
333
|
|
|
@@ -272,6 +352,9 @@ OpenMagnetics::Inputs process_two_switch_forward_internal(const json& converterJ
|
|
|
272
352
|
}
|
|
273
353
|
|
|
274
354
|
OpenMagnetics::Inputs process_active_clamp_forward_internal(const json& converterJson, bool useNgspice) {
|
|
355
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
356
|
+
return process_base_spec_vector_L<OpenMagnetics::ActiveClampForward>(converterJson, useNgspice, MAS::Topologies::ACTIVE_CLAMP_FORWARD_CONVERTER);
|
|
357
|
+
}
|
|
275
358
|
OpenMagnetics::AdvancedActiveClampForward converter(converterJson);
|
|
276
359
|
converter._assertErrors = true;
|
|
277
360
|
|
|
@@ -296,6 +379,9 @@ OpenMagnetics::Inputs process_active_clamp_forward_internal(const json& converte
|
|
|
296
379
|
}
|
|
297
380
|
|
|
298
381
|
OpenMagnetics::Inputs process_push_pull_internal(const json& converterJson, bool useNgspice) {
|
|
382
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
383
|
+
return process_base_spec_vector_L<OpenMagnetics::PushPull>(converterJson, useNgspice, MAS::Topologies::PUSH_PULL_CONVERTER);
|
|
384
|
+
}
|
|
299
385
|
OpenMagnetics::AdvancedPushPull converter(converterJson);
|
|
300
386
|
converter._assertErrors = true;
|
|
301
387
|
|
|
@@ -428,6 +514,9 @@ OpenMagnetics::Inputs process_pshb_internal(const json& converterJson, bool useN
|
|
|
428
514
|
}
|
|
429
515
|
|
|
430
516
|
OpenMagnetics::Inputs process_isolated_buck_internal(const json& converterJson, bool useNgspice) {
|
|
517
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
518
|
+
return process_base_spec_vector_L<OpenMagnetics::IsolatedBuck>(converterJson, useNgspice, MAS::Topologies::ISOLATED_BUCK_CONVERTER);
|
|
519
|
+
}
|
|
431
520
|
OpenMagnetics::AdvancedIsolatedBuck converter(converterJson);
|
|
432
521
|
converter._assertErrors = true;
|
|
433
522
|
|
|
@@ -452,6 +541,9 @@ OpenMagnetics::Inputs process_isolated_buck_internal(const json& converterJson,
|
|
|
452
541
|
}
|
|
453
542
|
|
|
454
543
|
OpenMagnetics::Inputs process_isolated_buck_boost_internal(const json& converterJson, bool useNgspice) {
|
|
544
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
545
|
+
return process_base_spec_vector_L<OpenMagnetics::IsolatedBuckBoost>(converterJson, useNgspice, MAS::Topologies::ISOLATED_BUCK_BOOST_CONVERTER);
|
|
546
|
+
}
|
|
455
547
|
OpenMagnetics::AdvancedIsolatedBuckBoost converter(converterJson);
|
|
456
548
|
converter._assertErrors = true;
|
|
457
549
|
|
|
@@ -511,6 +603,9 @@ OpenMagnetics::Inputs process_pfc_internal(const json& converterJson) {
|
|
|
511
603
|
// ─────────────────────────────────────────────────────────────────────
|
|
512
604
|
|
|
513
605
|
OpenMagnetics::Inputs process_cuk_internal(const json& converterJson, bool useNgspice) {
|
|
606
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
607
|
+
return process_base_spec_scalar_L<OpenMagnetics::Cuk>(converterJson, useNgspice, MAS::Topologies::CUK_CONVERTER);
|
|
608
|
+
}
|
|
514
609
|
OpenMagnetics::AdvancedCuk converter(converterJson);
|
|
515
610
|
converter._assertErrors = true;
|
|
516
611
|
if (useNgspice) {
|
|
@@ -530,6 +625,9 @@ OpenMagnetics::Inputs process_cuk_internal(const json& converterJson, bool useNg
|
|
|
530
625
|
}
|
|
531
626
|
|
|
532
627
|
OpenMagnetics::Inputs process_sepic_internal(const json& converterJson, bool useNgspice) {
|
|
628
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
629
|
+
return process_base_spec_scalar_L<OpenMagnetics::Sepic>(converterJson, useNgspice, MAS::Topologies::SEPIC_CONVERTER);
|
|
630
|
+
}
|
|
533
631
|
OpenMagnetics::AdvancedSepic converter(converterJson);
|
|
534
632
|
converter._assertErrors = true;
|
|
535
633
|
if (useNgspice) {
|
|
@@ -549,6 +647,9 @@ OpenMagnetics::Inputs process_sepic_internal(const json& converterJson, bool use
|
|
|
549
647
|
}
|
|
550
648
|
|
|
551
649
|
OpenMagnetics::Inputs process_zeta_internal(const json& converterJson, bool useNgspice) {
|
|
650
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
651
|
+
return process_base_spec_scalar_L<OpenMagnetics::Zeta>(converterJson, useNgspice, MAS::Topologies::ZETA_CONVERTER);
|
|
652
|
+
}
|
|
552
653
|
OpenMagnetics::AdvancedZeta converter(converterJson);
|
|
553
654
|
converter._assertErrors = true;
|
|
554
655
|
if (useNgspice) {
|
|
@@ -568,6 +669,9 @@ OpenMagnetics::Inputs process_zeta_internal(const json& converterJson, bool useN
|
|
|
568
669
|
}
|
|
569
670
|
|
|
570
671
|
OpenMagnetics::Inputs process_four_switch_buck_boost_internal(const json& converterJson, bool useNgspice) {
|
|
672
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
673
|
+
return process_base_spec_scalar_L<OpenMagnetics::FourSwitchBuckBoost>(converterJson, useNgspice, MAS::Topologies::FOUR_SWITCH_BUCK_BOOST_CONVERTER);
|
|
674
|
+
}
|
|
571
675
|
OpenMagnetics::AdvancedFourSwitchBuckBoost converter(converterJson);
|
|
572
676
|
converter._assertErrors = true;
|
|
573
677
|
if (useNgspice) {
|
|
@@ -610,6 +714,35 @@ OpenMagnetics::Inputs process_asymmetric_half_bridge_internal(const json& conver
|
|
|
610
714
|
}
|
|
611
715
|
|
|
612
716
|
OpenMagnetics::Inputs process_weinberg_internal(const json& converterJson, bool useNgspice) {
|
|
717
|
+
// Base-spec fast path (ABT #11): derive L from the base Weinberg when no
|
|
718
|
+
// desiredInductance is supplied. Inlined (not via the shared helper)
|
|
719
|
+
// because Weinberg's simulate signature takes a SCALAR turns ratio
|
|
720
|
+
// (single-secondary topology), not the vector the other transformers use.
|
|
721
|
+
if (!converterJson.contains("desiredInductance")) {
|
|
722
|
+
OpenMagnetics::Weinberg converter(converterJson);
|
|
723
|
+
converter._assertErrors = true;
|
|
724
|
+
if (useNgspice) {
|
|
725
|
+
auto designReqs = converter.process_design_requirements();
|
|
726
|
+
std::vector<double> turnsRatios;
|
|
727
|
+
for (const auto& tr : designReqs.get_turns_ratios()) {
|
|
728
|
+
turnsRatios.push_back(OpenMagnetics::resolve_dimensional_values(tr));
|
|
729
|
+
}
|
|
730
|
+
if (turnsRatios.empty()) {
|
|
731
|
+
throw std::runtime_error("Weinberg converter requires at least one turns ratio");
|
|
732
|
+
}
|
|
733
|
+
double inductance = OpenMagnetics::resolve_dimensional_values(designReqs.get_magnetizing_inductance());
|
|
734
|
+
auto operatingPoints = converter.simulate_and_extract_operating_points(turnsRatios[0], inductance);
|
|
735
|
+
OpenMagnetics::Inputs result;
|
|
736
|
+
result.set_design_requirements(designReqs);
|
|
737
|
+
result.set_operating_points(operatingPoints);
|
|
738
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::WEINBERG_CONVERTER);
|
|
739
|
+
return result;
|
|
740
|
+
} else {
|
|
741
|
+
OpenMagnetics::Inputs result = converter.process();
|
|
742
|
+
result.get_mutable_design_requirements().set_topology(MAS::Topologies::WEINBERG_CONVERTER);
|
|
743
|
+
return result;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
613
746
|
OpenMagnetics::AdvancedWeinberg converter(converterJson);
|
|
614
747
|
converter._assertErrors = true;
|
|
615
748
|
if (useNgspice) {
|
|
@@ -788,16 +921,50 @@ json process_converter(const std::string& topologyName, json converterJson, bool
|
|
|
788
921
|
return process_converter_internal(normalized, converterJson, useNgspice);
|
|
789
922
|
}
|
|
790
923
|
|
|
924
|
+
// Build candidate magnetics from a converter object via the MKF template
|
|
925
|
+
// method, threading the `fast` flag (fast core-only adviser vs full
|
|
926
|
+
// winding+simulation adviser) and optional per-filter weights.
|
|
927
|
+
template <typename ConverterType>
|
|
928
|
+
static std::vector<std::pair<OpenMagnetics::Mas, double>> advise_from_converter_spec(
|
|
929
|
+
const json& converterJson, bool fast,
|
|
930
|
+
const std::map<OpenMagnetics::MagneticFilters, double>& weights,
|
|
931
|
+
int maxResults, OpenMagnetics::MagneticAdviser& adviser) {
|
|
932
|
+
ConverterType converter(converterJson);
|
|
933
|
+
converter._assertErrors = true;
|
|
934
|
+
return weights.empty()
|
|
935
|
+
? adviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
936
|
+
: adviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// Single-inductor, non-isolated family (Buck/Boost/Cuk/Sepic/Zeta/
|
|
940
|
+
// FourSwitchBuckBoost). Their Advanced* class requires ONLY desiredInductance
|
|
941
|
+
// on top of a plain converter spec, so we pick the model purely on the
|
|
942
|
+
// presence of that key: Advanced* when the user pins L (honoured as the
|
|
943
|
+
// nominal target), Base when absent (L derived from the ripple budget in
|
|
944
|
+
// MKF). Transformer/resonant families do NOT get this duality — their
|
|
945
|
+
// Advanced* from_json also requires desiredTurnsRatios (Flyback also
|
|
946
|
+
// desiredDutyCycle), i.e. design *outputs* a converter spec does not carry —
|
|
947
|
+
// so they always use the Base model, which derives turns + L itself.
|
|
948
|
+
template <typename Base, typename Advanced>
|
|
949
|
+
static std::vector<std::pair<OpenMagnetics::Mas, double>> advise_inductor_family(
|
|
950
|
+
const json& converterJson, bool fast,
|
|
951
|
+
const std::map<OpenMagnetics::MagneticFilters, double>& weights,
|
|
952
|
+
int maxResults, OpenMagnetics::MagneticAdviser& adviser) {
|
|
953
|
+
return converterJson.contains("desiredInductance")
|
|
954
|
+
? advise_from_converter_spec<Advanced>(converterJson, fast, weights, maxResults, adviser)
|
|
955
|
+
: advise_from_converter_spec<Base>(converterJson, fast, weights, maxResults, adviser);
|
|
956
|
+
}
|
|
957
|
+
|
|
791
958
|
// Simplified design_magnetics_from_converter using MKF template method
|
|
792
959
|
json design_magnetics_from_converter(
|
|
793
960
|
const std::string& topologyNameRaw,
|
|
794
|
-
json converterJson,
|
|
795
|
-
int maxResults,
|
|
796
|
-
json coreModeJson,
|
|
797
|
-
bool useNgspice,
|
|
798
|
-
json weightsJson
|
|
799
|
-
|
|
800
|
-
|
|
961
|
+
json converterJson,
|
|
962
|
+
int maxResults,
|
|
963
|
+
json coreModeJson,
|
|
964
|
+
bool useNgspice,
|
|
965
|
+
json weightsJson,
|
|
966
|
+
bool fast) {
|
|
967
|
+
|
|
801
968
|
|
|
802
969
|
// Accept MAS 1.0 camelCase, pre-1.0 Title Case, or internal short form.
|
|
803
970
|
const std::string topologyName = normalize_topology_name(topologyNameRaw);
|
|
@@ -821,125 +988,109 @@ json design_magnetics_from_converter(
|
|
|
821
988
|
|
|
822
989
|
std::vector<std::pair<OpenMagnetics::Mas, double>> masMagnetics;
|
|
823
990
|
|
|
824
|
-
//
|
|
825
|
-
//
|
|
826
|
-
// only `magnetizingInductance.minimum` (a ripple-floor), and
|
|
827
|
-
// the CoreAdviser picks the smallest core satisfying it
|
|
828
|
-
// regardless of the L the user asked for. Dispatch to the
|
|
829
|
-
// Advanced* subclass so spec.desiredInductance becomes the
|
|
830
|
-
// nominal target (AdvancedBuck/Boost::process_design_requirements
|
|
831
|
-
// override does this, MKF commit 8acd72c7).
|
|
991
|
+
// Build candidate magnetics from the converter spec via the MKF
|
|
992
|
+
// template method. Two independent axes (4 behaviours total):
|
|
832
993
|
//
|
|
833
|
-
//
|
|
834
|
-
//
|
|
835
|
-
//
|
|
836
|
-
//
|
|
837
|
-
//
|
|
838
|
-
//
|
|
994
|
+
// * Base vs Advanced* model — chosen PER TOPOLOGY:
|
|
995
|
+
// - Single-inductor family (Buck/Boost/Cuk/Sepic/Zeta/
|
|
996
|
+
// FourSwitchBuckBoost): advise_inductor_family() picks
|
|
997
|
+
// Advanced* when desiredInductance is present (honoured as the
|
|
998
|
+
// nominal L target), Base otherwise (L derived from the ripple
|
|
999
|
+
// budget). This is ABT #11 — the slow path used to force
|
|
1000
|
+
// Advanced for Buck/Boost and throw on a base spec.
|
|
1001
|
+
// - Transformer/resonant families: always Base. Their Advanced*
|
|
1002
|
+
// from_json requires desiredTurnsRatios (Flyback also
|
|
1003
|
+
// desiredDutyCycle) — design outputs a converter spec lacks —
|
|
1004
|
+
// so only Base is constructible; it derives turns + L itself.
|
|
1005
|
+
// * fast flag — fast core-only adviser vs full winding+sim adviser,
|
|
1006
|
+
// threaded into get_advised_magnetic_from_converter for every
|
|
1007
|
+
// topology.
|
|
839
1008
|
if (topologyName == "flyback" || topologyName == "advanced_flyback") {
|
|
840
1009
|
OpenMagnetics::Flyback converter(converterJson);
|
|
841
1010
|
converter._assertErrors = true;
|
|
842
1011
|
masMagnetics = weights.empty()
|
|
843
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
844
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1012
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1013
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
845
1014
|
}
|
|
846
1015
|
else if (topologyName == "buck" || topologyName == "advanced_buck") {
|
|
847
|
-
OpenMagnetics::AdvancedBuck
|
|
848
|
-
|
|
849
|
-
masMagnetics = weights.empty()
|
|
850
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
851
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1016
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::Buck, OpenMagnetics::AdvancedBuck>(
|
|
1017
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
852
1018
|
}
|
|
853
1019
|
else if (topologyName == "boost" || topologyName == "advanced_boost") {
|
|
854
|
-
OpenMagnetics::AdvancedBoost
|
|
855
|
-
|
|
856
|
-
masMagnetics = weights.empty()
|
|
857
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
858
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1020
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::Boost, OpenMagnetics::AdvancedBoost>(
|
|
1021
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
859
1022
|
}
|
|
860
1023
|
else if (topologyName == "single_switch_forward") {
|
|
861
1024
|
OpenMagnetics::SingleSwitchForward converter(converterJson);
|
|
862
1025
|
converter._assertErrors = true;
|
|
863
1026
|
masMagnetics = weights.empty()
|
|
864
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
865
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1027
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1028
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
866
1029
|
}
|
|
867
1030
|
else if (topologyName == "two_switch_forward") {
|
|
868
1031
|
OpenMagnetics::TwoSwitchForward converter(converterJson);
|
|
869
1032
|
converter._assertErrors = true;
|
|
870
1033
|
masMagnetics = weights.empty()
|
|
871
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
872
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1034
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1035
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
873
1036
|
}
|
|
874
1037
|
else if (topologyName == "active_clamp_forward") {
|
|
875
1038
|
OpenMagnetics::ActiveClampForward converter(converterJson);
|
|
876
1039
|
converter._assertErrors = true;
|
|
877
1040
|
masMagnetics = weights.empty()
|
|
878
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
879
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1041
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1042
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
880
1043
|
}
|
|
881
1044
|
else if (topologyName == "push_pull") {
|
|
882
1045
|
OpenMagnetics::PushPull converter(converterJson);
|
|
883
1046
|
converter._assertErrors = true;
|
|
884
1047
|
masMagnetics = weights.empty()
|
|
885
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
886
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1048
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1049
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
887
1050
|
}
|
|
888
1051
|
else if (topologyName == "isolated_buck") {
|
|
889
1052
|
OpenMagnetics::IsolatedBuck converter(converterJson);
|
|
890
1053
|
converter._assertErrors = true;
|
|
891
1054
|
masMagnetics = weights.empty()
|
|
892
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
893
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1055
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1056
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
894
1057
|
}
|
|
895
1058
|
else if (topologyName == "isolated_buck_boost") {
|
|
896
1059
|
OpenMagnetics::IsolatedBuckBoost converter(converterJson);
|
|
897
1060
|
converter._assertErrors = true;
|
|
898
1061
|
masMagnetics = weights.empty()
|
|
899
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
900
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1062
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1063
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
901
1064
|
}
|
|
902
1065
|
else if (topologyName == "llc" || topologyName == "advanced_llc") {
|
|
903
1066
|
OpenMagnetics::Llc converter(converterJson);
|
|
904
1067
|
converter._assertErrors = true;
|
|
905
1068
|
masMagnetics = weights.empty()
|
|
906
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
907
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1069
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1070
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
908
1071
|
}
|
|
909
1072
|
else if (topologyName == "cuk" || topologyName == "advanced_cuk") {
|
|
910
|
-
OpenMagnetics::Cuk
|
|
911
|
-
|
|
912
|
-
masMagnetics = weights.empty()
|
|
913
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
914
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1073
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::Cuk, OpenMagnetics::AdvancedCuk>(
|
|
1074
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
915
1075
|
}
|
|
916
1076
|
else if (topologyName == "sepic" || topologyName == "advanced_sepic") {
|
|
917
|
-
OpenMagnetics::Sepic
|
|
918
|
-
|
|
919
|
-
masMagnetics = weights.empty()
|
|
920
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
921
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1077
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::Sepic, OpenMagnetics::AdvancedSepic>(
|
|
1078
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
922
1079
|
}
|
|
923
1080
|
else if (topologyName == "zeta" || topologyName == "advanced_zeta") {
|
|
924
|
-
OpenMagnetics::Zeta
|
|
925
|
-
|
|
926
|
-
masMagnetics = weights.empty()
|
|
927
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
928
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1081
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::Zeta, OpenMagnetics::AdvancedZeta>(
|
|
1082
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
929
1083
|
}
|
|
930
1084
|
else if (topologyName == "four_switch_buck_boost" || topologyName == "advanced_four_switch_buck_boost") {
|
|
931
|
-
OpenMagnetics::FourSwitchBuckBoost
|
|
932
|
-
|
|
933
|
-
masMagnetics = weights.empty()
|
|
934
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
935
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1085
|
+
masMagnetics = advise_inductor_family<OpenMagnetics::FourSwitchBuckBoost, OpenMagnetics::AdvancedFourSwitchBuckBoost>(
|
|
1086
|
+
converterJson, fast, weights, maxResults, magneticAdviser);
|
|
936
1087
|
}
|
|
937
1088
|
else if (topologyName == "asymmetric_half_bridge" || topologyName == "advanced_asymmetric_half_bridge") {
|
|
938
1089
|
OpenMagnetics::AsymmetricHalfBridge converter(converterJson);
|
|
939
1090
|
converter._assertErrors = true;
|
|
940
1091
|
masMagnetics = weights.empty()
|
|
941
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
942
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1092
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1093
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
943
1094
|
}
|
|
944
1095
|
else if (topologyName == "phase_shifted_full_bridge" || topologyName == "psfb" ||
|
|
945
1096
|
topologyName == "advanced_phase_shifted_full_bridge" || topologyName == "advanced_psfb") {
|
|
@@ -952,36 +1103,36 @@ json design_magnetics_from_converter(
|
|
|
952
1103
|
OpenMagnetics::Psfb converter(converterJson);
|
|
953
1104
|
converter._assertErrors = true;
|
|
954
1105
|
masMagnetics = weights.empty()
|
|
955
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
956
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1106
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1107
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
957
1108
|
}
|
|
958
1109
|
else if (topologyName == "weinberg" || topologyName == "advanced_weinberg") {
|
|
959
1110
|
OpenMagnetics::Weinberg converter(converterJson);
|
|
960
1111
|
converter._assertErrors = true;
|
|
961
1112
|
masMagnetics = weights.empty()
|
|
962
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
963
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1113
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1114
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
964
1115
|
}
|
|
965
1116
|
else if (topologyName == "vienna" || topologyName == "advanced_vienna") {
|
|
966
1117
|
OpenMagnetics::Vienna converter(converterJson);
|
|
967
1118
|
converter._assertErrors = true;
|
|
968
1119
|
masMagnetics = weights.empty()
|
|
969
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
970
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1120
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1121
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
971
1122
|
}
|
|
972
1123
|
else if (topologyName == "clllc" || topologyName == "advanced_clllc") {
|
|
973
1124
|
OpenMagnetics::Clllc converter(converterJson);
|
|
974
1125
|
converter._assertErrors = true;
|
|
975
1126
|
masMagnetics = weights.empty()
|
|
976
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
977
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1127
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1128
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
978
1129
|
}
|
|
979
1130
|
else if (topologyName == "src" || topologyName == "advanced_src") {
|
|
980
1131
|
OpenMagnetics::Src converter(converterJson);
|
|
981
1132
|
converter._assertErrors = true;
|
|
982
1133
|
masMagnetics = weights.empty()
|
|
983
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
984
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1134
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1135
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
985
1136
|
}
|
|
986
1137
|
else if (topologyName == "dab" || topologyName == "advanced_dab" ||
|
|
987
1138
|
topologyName == "dual_active_bridge") {
|
|
@@ -992,18 +1143,26 @@ json design_magnetics_from_converter(
|
|
|
992
1143
|
OpenMagnetics::Dab converter(converterJson);
|
|
993
1144
|
converter._assertErrors = true;
|
|
994
1145
|
masMagnetics = weights.empty()
|
|
995
|
-
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults)
|
|
996
|
-
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults);
|
|
1146
|
+
? magneticAdviser.get_advised_magnetic_from_converter(converter, maxResults, fast)
|
|
1147
|
+
: magneticAdviser.get_advised_magnetic_from_converter(converter, weights, maxResults, fast);
|
|
997
1148
|
}
|
|
998
1149
|
else {
|
|
999
|
-
// Fall back
|
|
1150
|
+
// Fall back for topologies without a converter-model adviser path
|
|
1151
|
+
// (e.g. PFC, current transformer). dispatch_converter / the
|
|
1152
|
+
// process_*_internal it calls already pick Base vs Advanced on the
|
|
1153
|
+
// presence of desiredInductance (ABT #11), so the Base/Advanced
|
|
1154
|
+
// behaviour is preserved here too; only the adviser stage differs.
|
|
1000
1155
|
json inputsJson = process_converter_internal(topologyName, converterJson, useNgspice);
|
|
1001
1156
|
if (inputsJson.contains("error")) return inputsJson;
|
|
1002
|
-
|
|
1157
|
+
|
|
1003
1158
|
OpenMagnetics::Inputs inputs(inputsJson);
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1159
|
+
if (fast) {
|
|
1160
|
+
masMagnetics = magneticAdviser.get_advised_magnetic_fast(inputs, maxResults);
|
|
1161
|
+
} else if (weights.empty()) {
|
|
1162
|
+
masMagnetics = magneticAdviser.get_advised_magnetic(inputs, maxResults);
|
|
1163
|
+
} else {
|
|
1164
|
+
masMagnetics = magneticAdviser.get_advised_magnetic(inputs, weights, maxResults);
|
|
1165
|
+
}
|
|
1007
1166
|
}
|
|
1008
1167
|
|
|
1009
1168
|
// Build results (same as before)
|
|
@@ -3944,9 +4103,10 @@ void register_converter_bindings(py::module& m) {
|
|
|
3944
4103
|
|
|
3945
4104
|
m.def("design_magnetics_from_converter", &design_magnetics_from_converter,
|
|
3946
4105
|
"Design magnetic components from a converter specification.",
|
|
3947
|
-
py::arg("topology_name"), py::arg("converter_json"),
|
|
4106
|
+
py::arg("topology_name"), py::arg("converter_json"),
|
|
3948
4107
|
py::arg("max_results") = 1, py::arg("core_mode_json") = "available cores",
|
|
3949
|
-
py::arg("use_ngspice") = true, py::arg("weights_json") = nullptr
|
|
4108
|
+
py::arg("use_ngspice") = true, py::arg("weights_json") = nullptr,
|
|
4109
|
+
py::arg("fast") = false);
|
|
3950
4110
|
|
|
3951
4111
|
m.def("process_flyback", &process_flyback, "Process Flyback converter.", py::arg("flyback"));
|
|
3952
4112
|
m.def("process_buck", &process_buck, "Process Buck converter.", py::arg("buck"));
|
|
@@ -9,12 +9,13 @@ json process_converter(const std::string& topologyName, json converterJson, bool
|
|
|
9
9
|
|
|
10
10
|
// Combined endpoint: converter -> magnetic designs
|
|
11
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
|
|
12
|
+
const std::string& topologyName,
|
|
13
|
+
json converterJson,
|
|
14
|
+
int maxResults,
|
|
15
|
+
json coreModeJson,
|
|
16
|
+
bool useNgspice = true,
|
|
17
|
+
json weightsJson = nullptr,
|
|
18
|
+
bool fast = false);
|
|
18
19
|
|
|
19
20
|
// Per-topology thin wrappers (from .pyi stubs)
|
|
20
21
|
json process_flyback(json flybackJson);
|
|
@@ -430,6 +430,70 @@ double calculate_saturation_current_at_operating_point(
|
|
|
430
430
|
return magnetic.calculate_saturation_current(op, temperature);
|
|
431
431
|
}
|
|
432
432
|
|
|
433
|
+
double calculate_peak_winding_current(json magneticJson, json operatingPointJson, int windingIndex) {
|
|
434
|
+
OpenMagnetics::Magnetic magnetic(magneticJson);
|
|
435
|
+
MAS::OperatingPoint op;
|
|
436
|
+
from_json(operatingPointJson, op);
|
|
437
|
+
|
|
438
|
+
// Operating-point magnetizing inductance of the as-designed magnetic
|
|
439
|
+
// (gapped circuit + DC-bias rolloff). Same L the saturation-current
|
|
440
|
+
// accessor uses, so I_peak and I_sat stay on a consistent footing.
|
|
441
|
+
OpenMagnetics::MagnetizingInductance magnetizingInductanceCalc;
|
|
442
|
+
auto inductanceOutput =
|
|
443
|
+
magnetizingInductanceCalc.calculate_inductance_from_number_turns_and_gapping(
|
|
444
|
+
magnetic.get_mutable_core(), magnetic.get_mutable_coil(), &op);
|
|
445
|
+
auto inductanceNominal = inductanceOutput.get_magnetizing_inductance().get_nominal();
|
|
446
|
+
if (!inductanceNominal.has_value() || inductanceNominal.value() <= 0) {
|
|
447
|
+
throw std::runtime_error(
|
|
448
|
+
"calculate_peak_winding_current: operating-point magnetizing "
|
|
449
|
+
"inductance is missing or non-positive — cannot derive the "
|
|
450
|
+
"magnetizing current.");
|
|
451
|
+
}
|
|
452
|
+
double magnetizingInductance = inductanceNominal.value();
|
|
453
|
+
|
|
454
|
+
auto excitations = op.get_mutable_excitations_per_winding();
|
|
455
|
+
if (excitations.empty()) {
|
|
456
|
+
throw std::runtime_error(
|
|
457
|
+
"calculate_peak_winding_current: operating point has no winding "
|
|
458
|
+
"excitations.");
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// The magnetizing (flux-driving) current is referred to the primary
|
|
462
|
+
// (winding 0): I_mag = (1/L) ∫ v_primary dt, derived from the primary
|
|
463
|
+
// excitation's voltage. addOffset=true folds the operating DC bias of
|
|
464
|
+
// the primary current back in, so the returned peak is the saturation-
|
|
465
|
+
// driving peak (DC + ripple), not just the AC ripple amplitude.
|
|
466
|
+
auto primaryExcitation = excitations[0];
|
|
467
|
+
auto magnetizingCurrent = OpenMagnetics::Inputs::calculate_magnetizing_current(
|
|
468
|
+
primaryExcitation, magnetizingInductance, true, true);
|
|
469
|
+
auto processed = magnetizingCurrent.get_processed();
|
|
470
|
+
if (!processed.has_value() || !processed->get_peak().has_value()) {
|
|
471
|
+
throw std::runtime_error(
|
|
472
|
+
"calculate_peak_winding_current: computed magnetizing current has "
|
|
473
|
+
"no processed peak.");
|
|
474
|
+
}
|
|
475
|
+
double primaryPeak = processed->get_peak().value();
|
|
476
|
+
|
|
477
|
+
if (windingIndex == 0) {
|
|
478
|
+
return primaryPeak;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Saturation is a single-flux core property; the magnetizing current
|
|
482
|
+
// referred to winding i is the primary-referred value scaled by the
|
|
483
|
+
// turns ratio: I_mag_i = I_mag_0 · N_0 / N_i.
|
|
484
|
+
double numberTurnsPrimary =
|
|
485
|
+
static_cast<double>(magnetic.get_mutable_coil().get_number_turns(0));
|
|
486
|
+
double numberTurnsWinding =
|
|
487
|
+
static_cast<double>(magnetic.get_mutable_coil().get_number_turns(windingIndex));
|
|
488
|
+
if (numberTurnsWinding <= 0) {
|
|
489
|
+
throw std::runtime_error(
|
|
490
|
+
"calculate_peak_winding_current: winding " +
|
|
491
|
+
std::to_string(windingIndex) + " has no turns — cannot refer the "
|
|
492
|
+
"magnetizing current to it.");
|
|
493
|
+
}
|
|
494
|
+
return primaryPeak * numberTurnsPrimary / numberTurnsWinding;
|
|
495
|
+
}
|
|
496
|
+
|
|
433
497
|
double calculate_temperature_from_core_thermal_resistance(json coreJson, double totalLosses) {
|
|
434
498
|
OpenMagnetics::Core core(coreJson);
|
|
435
499
|
|
|
@@ -1073,6 +1137,40 @@ void register_core_bindings(py::module& m) {
|
|
|
1073
1137
|
py::arg("magnetic_json"), py::arg("operating_point_json"),
|
|
1074
1138
|
py::arg("temperature"));
|
|
1075
1139
|
|
|
1140
|
+
m.def("calculate_peak_winding_current",
|
|
1141
|
+
&calculate_peak_winding_current,
|
|
1142
|
+
R"pbdoc(
|
|
1143
|
+
Peak magnetizing (flux-driving) current at an operating point.
|
|
1144
|
+
|
|
1145
|
+
Returns the peak of the magnetizing current — the quantity that
|
|
1146
|
+
actually drives core flux and therefore saturation — for the
|
|
1147
|
+
as-designed magnetic at the supplied operating point. This is the
|
|
1148
|
+
consistent partner to ``calculate_saturation_current_at_operating_point``:
|
|
1149
|
+
both use the same operating-point magnetizing inductance, so the
|
|
1150
|
+
caller can compare I_peak against I_sat on the same footing.
|
|
1151
|
+
|
|
1152
|
+
The magnetizing current is computed by MKF from the primary voltage
|
|
1153
|
+
waveform (``I_mag = (1/L) ∫ v dt``) with the operating DC bias folded
|
|
1154
|
+
in, NOT read back from the input current. For a transformer this is
|
|
1155
|
+
the genuine flux-driving current, which differs from the primary
|
|
1156
|
+
winding current (the latter includes reflected load current that
|
|
1157
|
+
produces no net flux).
|
|
1158
|
+
|
|
1159
|
+
Args:
|
|
1160
|
+
magnetic_json: JSON Magnetic object (core + coil).
|
|
1161
|
+
operating_point_json: JSON OperatingPoint object with at least the
|
|
1162
|
+
primary winding voltage excitation.
|
|
1163
|
+
winding_index: Winding the result is referred to (default 0,
|
|
1164
|
+
primary). For other windings the primary-referred peak is
|
|
1165
|
+
scaled by the turns ratio N_0 / N_i — saturation is a single
|
|
1166
|
+
core-flux property, so this is largely a unit choice.
|
|
1167
|
+
|
|
1168
|
+
Returns:
|
|
1169
|
+
Peak magnetizing current in Amperes.
|
|
1170
|
+
)pbdoc",
|
|
1171
|
+
py::arg("magnetic_json"), py::arg("operating_point_json"),
|
|
1172
|
+
py::arg("winding_index") = 0);
|
|
1173
|
+
|
|
1076
1174
|
m.def("calculate_saturation_current", &calculate_saturation_current,
|
|
1077
1175
|
R"pbdoc(
|
|
1078
1176
|
Calculate saturation current for a complete magnetic.
|
|
@@ -37,6 +37,7 @@ json load_core_data(json coresJson);
|
|
|
37
37
|
json get_core_temperature_dependant_parameters(json coreData, double temperature);
|
|
38
38
|
double calculate_core_maximum_magnetic_energy(json coreDataJson, json operatingPointJson);
|
|
39
39
|
double calculate_saturation_current(json magneticJson, double temperature);
|
|
40
|
+
double calculate_peak_winding_current(json magneticJson, json operatingPointJson, int windingIndex);
|
|
40
41
|
double calculate_temperature_from_core_thermal_resistance(json coreJson, double totalLosses);
|
|
41
42
|
|
|
42
43
|
// Gap and reluctance
|
|
@@ -25,10 +25,6 @@ json calculate_core_losses(json coreData, json coilData, json inputsData, json m
|
|
|
25
25
|
if (models.find("coreLosses") != models.end()) {
|
|
26
26
|
OpenMagnetics::from_json(models["coreLosses"], coreLossesModelName);
|
|
27
27
|
}
|
|
28
|
-
auto coreTemperatureModelName = OpenMagnetics::defaults.coreTemperatureModelDefault;
|
|
29
|
-
if (models.find("coreTemperature") != models.end()) {
|
|
30
|
-
OpenMagnetics::from_json(models["coreTemperature"], coreTemperatureModelName);
|
|
31
|
-
}
|
|
32
28
|
|
|
33
29
|
OpenMagnetics::Magnetic magnetic;
|
|
34
30
|
magnetic.set_core(core);
|
|
@@ -36,7 +32,6 @@ json calculate_core_losses(json coreData, json coilData, json inputsData, json m
|
|
|
36
32
|
|
|
37
33
|
OpenMagnetics::MagneticSimulator magneticSimulator;
|
|
38
34
|
magneticSimulator.set_core_losses_model_name(coreLossesModelName);
|
|
39
|
-
magneticSimulator.set_core_temperature_model_name(coreTemperatureModelName);
|
|
40
35
|
magneticSimulator.set_reluctance_model_name(reluctanceModelName);
|
|
41
36
|
auto coreLossesOutput = magneticSimulator.calculate_core_losses(operatingPoint, magnetic);
|
|
42
37
|
json result;
|
|
@@ -66,15 +61,6 @@ json get_core_losses_model_information(json material) {
|
|
|
66
61
|
return info;
|
|
67
62
|
}
|
|
68
63
|
|
|
69
|
-
json get_core_temperature_model_information() {
|
|
70
|
-
json info;
|
|
71
|
-
info["information"] = OpenMagnetics::CoreTemperatureModel::get_models_information();
|
|
72
|
-
info["errors"] = OpenMagnetics::CoreTemperatureModel::get_models_errors();
|
|
73
|
-
info["internal_links"] = OpenMagnetics::CoreTemperatureModel::get_models_internal_links();
|
|
74
|
-
info["external_links"] = OpenMagnetics::CoreTemperatureModel::get_models_external_links();
|
|
75
|
-
return info;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
64
|
json calculate_steinmetz_coefficients(json dataJson, json rangesJson) {
|
|
79
65
|
try {
|
|
80
66
|
std::vector<std::pair<double, double>> ranges;
|
|
@@ -383,7 +369,6 @@ void register_losses_bindings(py::module& m) {
|
|
|
383
369
|
models_data: JSON dict specifying models to use:
|
|
384
370
|
- "coreLosses": "STEINMETZ", "IGSE", "MSE", "BARG", "ROSHEN", "PROPRIETARY"
|
|
385
371
|
- "reluctance": "ZHANG", "MUEHLETHALER", "PARTRIDGE", "STENGLEIN"
|
|
386
|
-
- "coreTemperature": Model for temperature estimation
|
|
387
372
|
|
|
388
373
|
Returns:
|
|
389
374
|
JSON object containing:
|
|
@@ -423,14 +408,6 @@ void register_losses_bindings(py::module& m) {
|
|
|
423
408
|
)pbdoc",
|
|
424
409
|
py::arg("material"));
|
|
425
410
|
|
|
426
|
-
m.def("get_core_temperature_model_information", &get_core_temperature_model_information,
|
|
427
|
-
R"pbdoc(
|
|
428
|
-
Get documentation for available core temperature models.
|
|
429
|
-
|
|
430
|
-
Returns:
|
|
431
|
-
JSON object with model information, errors, and reference links.
|
|
432
|
-
)pbdoc");
|
|
433
|
-
|
|
434
411
|
m.def("calculate_steinmetz_coefficients", &calculate_steinmetz_coefficients,
|
|
435
412
|
R"pbdoc(
|
|
436
413
|
Fit Steinmetz equation coefficients from measured loss data.
|
|
@@ -7,7 +7,6 @@ namespace PyMKF {
|
|
|
7
7
|
// Core losses
|
|
8
8
|
json calculate_core_losses(json coreData, json coilData, json inputsData, json modelsData);
|
|
9
9
|
json get_core_losses_model_information(json material);
|
|
10
|
-
json get_core_temperature_model_information();
|
|
11
10
|
json calculate_steinmetz_coefficients(json dataJson, json rangesJson);
|
|
12
11
|
json calculate_steinmetz_coefficients_with_error(json dataJson, json rangesJson);
|
|
13
12
|
|
|
@@ -15,14 +15,9 @@ json simulate(json inputsJson, json magneticJson, json modelsData) {
|
|
|
15
15
|
if (!modelsData.is_null() && modelsData.find("coreLosses") != modelsData.end()) {
|
|
16
16
|
OpenMagnetics::from_json(modelsData["coreLosses"], coreLossesModelName);
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
if (!modelsData.is_null() && modelsData.find("coreTemperature") != modelsData.end()) {
|
|
20
|
-
OpenMagnetics::from_json(modelsData["coreTemperature"], coreTemperatureModelName);
|
|
21
|
-
}
|
|
22
|
-
|
|
18
|
+
|
|
23
19
|
OpenMagnetics::MagneticSimulator magneticSimulator;
|
|
24
20
|
magneticSimulator.set_core_losses_model_name(coreLossesModelName);
|
|
25
|
-
magneticSimulator.set_core_temperature_model_name(coreTemperatureModelName);
|
|
26
21
|
magneticSimulator.set_reluctance_model_name(reluctanceModelName);
|
|
27
22
|
auto mas = magneticSimulator.simulate(inputs, magnetic);
|
|
28
23
|
|
|
@@ -181,6 +181,90 @@ class TestCoreCalculations:
|
|
|
181
181
|
assert isinstance(gapping, list)
|
|
182
182
|
|
|
183
183
|
|
|
184
|
+
class TestPeakWindingCurrent:
|
|
185
|
+
"""
|
|
186
|
+
Tests for calculate_peak_winding_current (ABT #16).
|
|
187
|
+
|
|
188
|
+
Verifies the binding that exposes the MKF-computed peak magnetizing
|
|
189
|
+
(flux-driving) current at an operating point, so Heaviside no longer
|
|
190
|
+
needs analytical I_peak fallbacks.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
def _advise_magnetic(self, inputs):
|
|
194
|
+
proc = PyOpenMagnetics.process_inputs(inputs)
|
|
195
|
+
res = PyOpenMagnetics.calculate_advised_magnetics(proc, 1, "available cores")
|
|
196
|
+
data = json.loads(res) if isinstance(res, str) else res
|
|
197
|
+
data = data["data"] if isinstance(data, dict) and "data" in data else data
|
|
198
|
+
assert len(data) > 0, "adviser returned no magnetic"
|
|
199
|
+
mas = data[0].get("mas", data[0])
|
|
200
|
+
return mas["magnetic"], mas["inputs"]["operatingPoints"][0]
|
|
201
|
+
|
|
202
|
+
def test_inductor_peak_includes_dc_bias(self):
|
|
203
|
+
"""
|
|
204
|
+
Peak magnetizing current must fold in the operating DC bias, not
|
|
205
|
+
return only the AC ripple amplitude. Input current here is a 5 A DC
|
|
206
|
+
bias with a 4 A peak-to-peak ripple.
|
|
207
|
+
"""
|
|
208
|
+
op = {
|
|
209
|
+
"name": "Nominal",
|
|
210
|
+
"conditions": {"ambientTemperature": 25},
|
|
211
|
+
"excitationsPerWinding": [{
|
|
212
|
+
"frequency": 100000,
|
|
213
|
+
"current": {"waveform": {"data": [3, 7, 3],
|
|
214
|
+
"time": [0, 0.0000025, 0.00001]}},
|
|
215
|
+
"voltage": {"waveform": {"data": [-10, 30, 30, -10, -10],
|
|
216
|
+
"time": [0, 0, 0.0000025, 0.0000025, 0.00001]}},
|
|
217
|
+
}],
|
|
218
|
+
}
|
|
219
|
+
inputs = {
|
|
220
|
+
"designRequirements": {"magnetizingInductance": {"nominal": 100e-6},
|
|
221
|
+
"turnsRatios": []},
|
|
222
|
+
"operatingPoints": [op],
|
|
223
|
+
}
|
|
224
|
+
magnetic, op_used = self._advise_magnetic(inputs)
|
|
225
|
+
|
|
226
|
+
ipeak = PyOpenMagnetics.calculate_peak_winding_current(magnetic, op_used, 0)
|
|
227
|
+
# Default winding_index argument must match explicit 0.
|
|
228
|
+
assert ipeak == PyOpenMagnetics.calculate_peak_winding_current(magnetic, op_used)
|
|
229
|
+
assert ipeak > 0
|
|
230
|
+
# Must include the ~5 A DC bias, so well above the bare ripple amplitude.
|
|
231
|
+
assert ipeak > 4.0
|
|
232
|
+
|
|
233
|
+
# Consistent footing with I_sat at the same operating point.
|
|
234
|
+
isat = PyOpenMagnetics.calculate_saturation_current_at_operating_point(
|
|
235
|
+
magnetic, op_used, 25.0)
|
|
236
|
+
assert isat > ipeak # an advised design should not be saturating
|
|
237
|
+
|
|
238
|
+
def test_transformer_winding_index_scales_by_turns_ratio(self):
|
|
239
|
+
"""
|
|
240
|
+
For a transformer the per-winding magnetizing peak is the primary
|
|
241
|
+
value scaled by the turns ratio N_0 / N_i.
|
|
242
|
+
"""
|
|
243
|
+
op = {
|
|
244
|
+
"name": "Nominal",
|
|
245
|
+
"conditions": {"ambientTemperature": 25},
|
|
246
|
+
"excitationsPerWinding": [{
|
|
247
|
+
"frequency": 100000,
|
|
248
|
+
"current": {"waveform": {"data": [-5, 5, -5],
|
|
249
|
+
"time": [0, 0.0000025, 0.00001]}},
|
|
250
|
+
"voltage": {"waveform": {"data": [-10, 30, 30, -10, -10],
|
|
251
|
+
"time": [0, 0, 0.0000025, 0.0000025, 0.00001]}},
|
|
252
|
+
}],
|
|
253
|
+
}
|
|
254
|
+
inputs = {
|
|
255
|
+
"designRequirements": {"magnetizingInductance": {"nominal": 100e-6},
|
|
256
|
+
"turnsRatios": [{"nominal": 2.0}]},
|
|
257
|
+
"operatingPoints": [op],
|
|
258
|
+
}
|
|
259
|
+
magnetic, op_used = self._advise_magnetic(inputs)
|
|
260
|
+
turns = [w["numberTurns"]
|
|
261
|
+
for w in magnetic["coil"]["functionalDescription"]]
|
|
262
|
+
|
|
263
|
+
p0 = PyOpenMagnetics.calculate_peak_winding_current(magnetic, op_used, 0)
|
|
264
|
+
p1 = PyOpenMagnetics.calculate_peak_winding_current(magnetic, op_used, 1)
|
|
265
|
+
assert p1 == pytest.approx(p0 * turns[0] / turns[1], rel=1e-9)
|
|
266
|
+
|
|
267
|
+
|
|
184
268
|
class TestCoreProcessedDescription:
|
|
185
269
|
"""Test suite for processed core descriptions."""
|
|
186
270
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|