PyOpenMagnetics 1.7.30__tar.gz → 1.7.32__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/CMakeLists.txt +1 -1
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/PKG-INFO +1 -1
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/pyproject.toml +1 -1
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/core.cpp +33 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/core.h +1 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/database.cpp +55 -3
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_core.py +20 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_magnetics_catalogue_load.py +35 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/.gitignore +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/AGENTS.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/LICENSE +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/PyOpenMagnetics.pyi +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/README.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/_packaging/__init__.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/_packaging/_build_info.py.in +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/api/MAS.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/api/validation.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/docs/errors.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/docs/performance.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/README.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/llms.txt +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/requirements.txt +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/advisers.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/advisers.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/cmc.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/cmc.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/common.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/converter.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/converter.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/crossref.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/crossref.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/database.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/logging.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/losses.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/module.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/plotting.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/settings.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/simulation.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/utils.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/winding.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/src/wire.h +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/test.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_datasheet_only_catalogue.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_losses_not_evaluable_catalogue.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_mas_schema_walk.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_material_validator.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_winding.py +0 -0
|
@@ -142,7 +142,7 @@ if(NOT LOCAL_MKF_MAS)
|
|
|
142
142
|
set(MKF_GIT_TAG "main"
|
|
143
143
|
CACHE STRING "MKF git ref for the vendored PyOM build")
|
|
144
144
|
# Force fresh clone by using a unique timestamp - update this when the pin changes
|
|
145
|
-
set(MKF_FORCE_REFRESH "2026-10-
|
|
145
|
+
set(MKF_FORCE_REFRESH "2026-10-03-mkf-5055470b")
|
|
146
146
|
# Tell MKF to disable matplotplusplus and use SVG-based Painter instead
|
|
147
147
|
set(INCLUDE_PYMKF ON CACHE BOOL "Build Python interface" FORCE)
|
|
148
148
|
# GIT_SUBMODULES_RECURSE pulls in CAS/PEAS (added 2026-04 alongside the
|
|
@@ -567,6 +567,21 @@ json calculate_complex_permeability(json materialJson, double frequency) {
|
|
|
567
567
|
return result;
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
+
json calculate_complex_permeability_frequency_range(json material) {
|
|
571
|
+
OpenMagnetics::ComplexPermeability complexPermeabilityObj;
|
|
572
|
+
std::pair<double, double> range;
|
|
573
|
+
if (material.is_string()) {
|
|
574
|
+
range = complexPermeabilityObj.get_frequency_range(material.get<std::string>());
|
|
575
|
+
}
|
|
576
|
+
else {
|
|
577
|
+
range = complexPermeabilityObj.get_frequency_range(CoreMaterial(material));
|
|
578
|
+
}
|
|
579
|
+
json result;
|
|
580
|
+
result["minimum"] = range.first;
|
|
581
|
+
result["maximum"] = range.second;
|
|
582
|
+
return result;
|
|
583
|
+
}
|
|
584
|
+
|
|
570
585
|
void register_core_bindings(py::module& m) {
|
|
571
586
|
// Core materials
|
|
572
587
|
m.def("get_core_materials", &get_core_materials,
|
|
@@ -1344,6 +1359,24 @@ void register_core_bindings(py::module& m) {
|
|
|
1344
1359
|
)pbdoc",
|
|
1345
1360
|
py::arg("material_json"), py::arg("frequency"),
|
|
1346
1361
|
py::call_guard<py::gil_scoped_release>());
|
|
1362
|
+
|
|
1363
|
+
m.def("calculate_complex_permeability_frequency_range", &calculate_complex_permeability_frequency_range,
|
|
1364
|
+
R"pbdoc(
|
|
1365
|
+
Frequency span in which a core material's complex permeability is defined.
|
|
1366
|
+
|
|
1367
|
+
calculate_complex_permeability (and every impedance sweep that uses it)
|
|
1368
|
+
throws outside this span: it is the span covered by both the material's
|
|
1369
|
+
tabulated mu'(f) and mu''(f), or by the table MKF derives from its
|
|
1370
|
+
frequency-dependent initial permeability.
|
|
1371
|
+
|
|
1372
|
+
Args:
|
|
1373
|
+
material: JSON CoreMaterial object, or the material name.
|
|
1374
|
+
|
|
1375
|
+
Returns:
|
|
1376
|
+
JSON object {"minimum": Hz, "maximum": Hz}.
|
|
1377
|
+
)pbdoc",
|
|
1378
|
+
py::arg("material"),
|
|
1379
|
+
py::call_guard<py::gil_scoped_release>());
|
|
1347
1380
|
}
|
|
1348
1381
|
|
|
1349
1382
|
} // namespace PyMKF
|
|
@@ -59,6 +59,7 @@ std::vector<std::string> get_available_core_filters();
|
|
|
59
59
|
std::vector<double> get_maximum_dimensions(json magneticJson);
|
|
60
60
|
json calculate_core_data_from_shape(json shapeJson);
|
|
61
61
|
json calculate_complex_permeability(json materialJson, double frequency);
|
|
62
|
+
json calculate_complex_permeability_frequency_range(json material);
|
|
62
63
|
|
|
63
64
|
void register_core_bindings(py::module& m);
|
|
64
65
|
|
|
@@ -234,12 +234,20 @@ struct RejectedRecord {
|
|
|
234
234
|
// Read the whole stream before touching the cache, so a strict load is all-or-nothing
|
|
235
235
|
// and a tolerant one is exact about what it dropped. Every failure is reported as
|
|
236
236
|
// "<source>:<line> (part 'X'): <what actually went wrong>".
|
|
237
|
+
//
|
|
238
|
+
// Two records under one manufacturerInfo.reference are an error too. The cache is keyed
|
|
239
|
+
// by the reference, so the second used to replace the first without a word: a catalogue
|
|
240
|
+
// of 2 came back as 1 (nine WE Midcom parts whose OEM P/N cell read "#N/A" collapsed to
|
|
241
|
+
// one). Nothing here can tell which of the two is that part's real record, so a strict
|
|
242
|
+
// load refuses the file and a tolerant one rejects every record sharing the reference.
|
|
237
243
|
std::vector<std::pair<std::string, OpenMagnetics::Magnetic>> stage_magnetics(std::istream& in,
|
|
238
244
|
const std::string& source,
|
|
239
245
|
bool expand,
|
|
240
246
|
bool skipInvalid,
|
|
241
247
|
std::vector<RejectedRecord>& rejected) {
|
|
242
248
|
std::vector<std::pair<std::string, OpenMagnetics::Magnetic>> staged;
|
|
249
|
+
std::vector<size_t> stagedLineNumbers;
|
|
250
|
+
std::map<std::string, size_t> firstLineOfReference;
|
|
243
251
|
std::string line;
|
|
244
252
|
size_t lineNumber = 0;
|
|
245
253
|
while (getline(in, line)) {
|
|
@@ -247,8 +255,9 @@ std::vector<std::pair<std::string, OpenMagnetics::Magnetic>> stage_magnetics(std
|
|
|
247
255
|
if (line.find_first_not_of(" \t\r\n") == std::string::npos) {
|
|
248
256
|
continue;
|
|
249
257
|
}
|
|
258
|
+
std::pair<std::string, OpenMagnetics::Magnetic> record;
|
|
250
259
|
try {
|
|
251
|
-
|
|
260
|
+
record = read_magnetic_line(line, expand);
|
|
252
261
|
}
|
|
253
262
|
catch (const std::exception& e) {
|
|
254
263
|
std::string reference;
|
|
@@ -262,7 +271,46 @@ std::vector<std::pair<std::string, OpenMagnetics::Magnetic>> stage_magnetics(std
|
|
|
262
271
|
throw std::runtime_error(locate(source, lineNumber, reference) + ": " + e.what());
|
|
263
272
|
}
|
|
264
273
|
rejected.push_back({lineNumber, reference, e.what()});
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
auto [first, isNew] = firstLineOfReference.emplace(record.first, lineNumber);
|
|
277
|
+
if (!isNew && !skipInvalid) {
|
|
278
|
+
throw std::runtime_error(locate(source, lineNumber, record.first) + ": manufacturerInfo.reference '" +
|
|
279
|
+
record.first + "' is also the reference of line " +
|
|
280
|
+
std::to_string(first->second) + ", and the magnetics cache is keyed by it: "
|
|
281
|
+
"one of the two records would silently replace the other");
|
|
282
|
+
}
|
|
283
|
+
staged.push_back(std::move(record));
|
|
284
|
+
stagedLineNumbers.push_back(lineNumber);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (skipInvalid) {
|
|
288
|
+
std::map<std::string, std::vector<size_t>> linesOfReference;
|
|
289
|
+
for (size_t index = 0; index < staged.size(); ++index) {
|
|
290
|
+
linesOfReference[staged[index].first].push_back(stagedLineNumbers[index]);
|
|
291
|
+
}
|
|
292
|
+
std::vector<std::pair<std::string, OpenMagnetics::Magnetic>> unique;
|
|
293
|
+
for (size_t index = 0; index < staged.size(); ++index) {
|
|
294
|
+
const auto& lines = linesOfReference[staged[index].first];
|
|
295
|
+
if (lines.size() == 1) {
|
|
296
|
+
unique.push_back(std::move(staged[index]));
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
std::string others;
|
|
300
|
+
for (auto otherLine : lines) {
|
|
301
|
+
if (otherLine != stagedLineNumbers[index]) {
|
|
302
|
+
others += (others.empty() ? "" : ", ") + std::to_string(otherLine);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
rejected.push_back({stagedLineNumbers[index], staged[index].first,
|
|
306
|
+
"manufacturerInfo.reference '" + staged[index].first +
|
|
307
|
+
"' is also the reference of line " + others +
|
|
308
|
+
", and the magnetics cache is keyed by it: every record under it is rejected,"
|
|
309
|
+
" since nothing tells which one is the part's real record"});
|
|
265
310
|
}
|
|
311
|
+
staged = std::move(unique);
|
|
312
|
+
std::stable_sort(rejected.begin(), rejected.end(),
|
|
313
|
+
[](const RejectedRecord& a, const RejectedRecord& b) { return a.lineNumber < b.lineNumber; });
|
|
266
314
|
}
|
|
267
315
|
return staged;
|
|
268
316
|
}
|
|
@@ -419,7 +467,9 @@ void register_database_bindings(py::module& m) {
|
|
|
419
467
|
(ABT #823). The first record that cannot be loaded raises EngineError naming
|
|
420
468
|
the file, the line number, the part reference and the underlying reason.
|
|
421
469
|
Blank lines are skipped. Records are keyed by manufacturerInfo.reference, so
|
|
422
|
-
a record without one is an error rather than an anonymous optional access
|
|
470
|
+
a record without one is an error rather than an anonymous optional access, and
|
|
471
|
+
two records sharing one raise EngineError naming the reference and both lines
|
|
472
|
+
instead of the second silently replacing the first.
|
|
423
473
|
|
|
424
474
|
Use load_magnetics_from_file_report() instead when a partial catalogue is
|
|
425
475
|
acceptable and you want the list of rejected records.
|
|
@@ -440,7 +490,9 @@ void register_database_bindings(py::module& m) {
|
|
|
440
490
|
The tolerant counterpart of load_magnetics_from_file (ABT #823): a bad record
|
|
441
491
|
no longer aborts the batch, and the caller is told exactly which records were
|
|
442
492
|
dropped and why instead of silently running on a truncated catalogue. A file
|
|
443
|
-
that cannot be opened is still an error.
|
|
493
|
+
that cannot be opened is still an error. Records sharing a
|
|
494
|
+
manufacturerInfo.reference are all rejected, each naming the other lines,
|
|
495
|
+
since nothing tells which of them is the part's real record.
|
|
444
496
|
|
|
445
497
|
Args:
|
|
446
498
|
path: Path to the NDJSON file.
|
|
@@ -312,3 +312,23 @@ class TestCoreProcessedDescription:
|
|
|
312
312
|
if "windingWindows" in processed:
|
|
313
313
|
assert isinstance(processed["windingWindows"], list)
|
|
314
314
|
assert len(processed["windingWindows"]) > 0
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class TestComplexPermeabilityFrequencyRange:
|
|
318
|
+
"""calculate_complex_permeability throws outside the material's tabulated span; the span is exposed so
|
|
319
|
+
callers (impedance sweeps) can stay inside it instead of catching the throw."""
|
|
320
|
+
|
|
321
|
+
def test_a07_span_ends_at_the_top_of_its_tabulated_mu_imaginary(self):
|
|
322
|
+
span = PyOpenMagnetics.calculate_complex_permeability_frequency_range("A07")
|
|
323
|
+
assert 0 < span["minimum"] < span["maximum"]
|
|
324
|
+
# A07's tabulated mu''(f) ends at ~17 MHz (mu' extended to it by Kramers-Kronig in MAS).
|
|
325
|
+
assert 1e7 < span["maximum"] < 1e8
|
|
326
|
+
|
|
327
|
+
def test_inside_the_span_answers_and_outside_it_throws(self):
|
|
328
|
+
material = PyOpenMagnetics.find_core_material_by_name("A07")
|
|
329
|
+
span = PyOpenMagnetics.calculate_complex_permeability_frequency_range(material)
|
|
330
|
+
assert span == PyOpenMagnetics.calculate_complex_permeability_frequency_range("A07")
|
|
331
|
+
inside = PyOpenMagnetics.calculate_complex_permeability(material, span["maximum"] * 0.99)
|
|
332
|
+
assert inside["imaginary"] > 0
|
|
333
|
+
with pytest.raises(Exception):
|
|
334
|
+
PyOpenMagnetics.calculate_complex_permeability(material, span["maximum"] * 1.01)
|
|
@@ -140,6 +140,27 @@ class TestStrictLoad:
|
|
|
140
140
|
assert "manufacturerInfo" in message, message
|
|
141
141
|
assert "anonymous.ndjson:1" in message, message
|
|
142
142
|
|
|
143
|
+
def test_two_records_sharing_a_reference_raise_and_name_it(self, tmp_path):
|
|
144
|
+
# The cache is keyed by manufacturerInfo.reference: the second record used to
|
|
145
|
+
# replace the first without a word (2 offered, "1" returned). Nine WE Midcom
|
|
146
|
+
# parts whose OEM P/N cell read "#N/A" collapsed to one that way.
|
|
147
|
+
first = magnetic("#N/A")
|
|
148
|
+
second = magnetic("#N/A")
|
|
149
|
+
second["coil"]["functionalDescription"][0]["numberTurns"] = 40
|
|
150
|
+
path = write_ndjson(tmp_path / "duplicates.ndjson", [magnetic("K1"), first, second])
|
|
151
|
+
|
|
152
|
+
with pytest.raises(PyOpenMagnetics.EngineError) as error:
|
|
153
|
+
PyOpenMagnetics.load_magnetics_from_file(path, True)
|
|
154
|
+
|
|
155
|
+
message = str(error.value)
|
|
156
|
+
assert "duplicates.ndjson:3" in message, message
|
|
157
|
+
assert "'#N/A'" in message, message
|
|
158
|
+
assert "line 2" in message, message
|
|
159
|
+
|
|
160
|
+
# All or nothing: none of the file reached the cache.
|
|
161
|
+
good = write_ndjson(tmp_path / "good.ndjson", [magnetic("K9")])
|
|
162
|
+
assert PyOpenMagnetics.load_magnetics_from_file(good, True) == "1"
|
|
163
|
+
|
|
143
164
|
|
|
144
165
|
class TestReportingLoad:
|
|
145
166
|
"""load_magnetics_from_file_report: keep the good records, name the dropped ones."""
|
|
@@ -167,6 +188,20 @@ class TestReportingLoad:
|
|
|
167
188
|
assert report["loaded"] == 2
|
|
168
189
|
assert report["rejected"] == []
|
|
169
190
|
|
|
191
|
+
def test_records_sharing_a_reference_are_all_rejected(self, tmp_path):
|
|
192
|
+
path = write_ndjson(
|
|
193
|
+
tmp_path / "duplicates.ndjson",
|
|
194
|
+
[magnetic("L1"), magnetic("#N/A"), magnetic("L3"), magnetic("#N/A")],
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
report = PyOpenMagnetics.load_magnetics_from_file_report(path, True)
|
|
198
|
+
|
|
199
|
+
assert report["loaded"] == 2
|
|
200
|
+
assert report["cacheSize"] == 2
|
|
201
|
+
assert [(r["line"], r["reference"]) for r in report["rejected"]] == [(2, "#N/A"), (4, "#N/A")]
|
|
202
|
+
assert "line 4" in report["rejected"][0]["reason"]
|
|
203
|
+
assert "line 2" in report["rejected"][1]["reason"]
|
|
204
|
+
|
|
170
205
|
def test_missing_file_still_raises(self, tmp_path):
|
|
171
206
|
with pytest.raises(PyOpenMagnetics.EngineError):
|
|
172
207
|
PyOpenMagnetics.load_magnetics_from_file_report(str(tmp_path / "nope.ndjson"), True)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyopenmagnetics-1.7.30 → pyopenmagnetics-1.7.32}/tests/test_losses_not_evaluable_catalogue.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|