PyOpenMagnetics 1.7.31__tar.gz → 1.7.33__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.31 → pyopenmagnetics-1.7.33}/CMakeLists.txt +1 -1
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/PKG-INFO +1 -1
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/PyOpenMagnetics.pyi +2 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/pyproject.toml +1 -1
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/advisers.cpp +19 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/database.cpp +55 -3
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_magnetics_catalogue_load.py +35 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/.github/workflows/ci.yml +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/.github/workflows/publish.yml +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/.gitignore +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/AGENTS.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/LICENSE +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/README.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/_packaging/__init__.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/_packaging/_build_info.py.in +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/api/MAS.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/api/mas_db_reader.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/api/validation.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/clear_cibuildwheel_cache.sh +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/docs/compatibility.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/docs/errors.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/docs/performance.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/README.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/buck_inductor.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/complete_simulation_example.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/converter_design_example.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/debug_bobbin.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/debug_coil.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/debug_core.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/debug_plotting.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_220v_12v_1a.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_220v_12v_2a_complete.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_bh_curve.png +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_core.png +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_design.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_summary.png +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/flyback_waveforms.png +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/list_plot_funcs.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/plot_flyback_design.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/plot_flyback_pyom.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/test_field_calc.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/examples/test_field_plot.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/force_fresh_build.sh +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/llms.txt +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/notebooks/01_getting_started.ipynb +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/notebooks/02_buck_inductor.ipynb +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/notebooks/03_core_losses.ipynb +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/notebooks/README.md +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/requirements.txt +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/advisers.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/bobbin.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/bobbin.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/cmc.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/cmc.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/common.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/converter.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/converter.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/core.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/core.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/crossref.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/crossref.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/database.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/logging.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/logging.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/losses.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/losses.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/module.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/plotting.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/plotting.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/settings.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/settings.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/simulation.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/simulation.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/utils.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/utils.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/winding.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/winding.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/wire.cpp +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/src/wire.h +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/test.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/__init__.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/conftest.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_converter_endpoints.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_core.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_core_adviser.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_datasheet_only_catalogue.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_examples_integration.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_inputs.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_logging.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_losses_not_evaluable_catalogue.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_magnetic_adviser.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_mas_schema_walk.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_material_validator.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/tests/test_plotting.py +0 -0
- {pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/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-04-mkf-508edb8b")
|
|
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
|
|
@@ -679,6 +679,8 @@ def calculate_advised_magnetics_from_catalog(
|
|
|
679
679
|
- "lossesNotEvaluable" (only on such parts): why the part's core losses could not be
|
|
680
680
|
evaluated (its core material has no core-loss model); it is ranked without any
|
|
681
681
|
loss-based filter and returned without simulated outputs.
|
|
682
|
+
And a "failedCandidates" array next to "data": one {"reference", "error"} per candidate
|
|
683
|
+
whose evaluation raised (a filter or the final simulation threw); it is not ranked.
|
|
682
684
|
"""
|
|
683
685
|
...
|
|
684
686
|
|
|
@@ -179,6 +179,17 @@ json calculate_advised_magnetics_fast(json inputsJson, int maximumNumberResults,
|
|
|
179
179
|
return results;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
// The candidates the adviser excluded because evaluating them raised (a filter or the final
|
|
183
|
+
// simulation threw), each with the error. They are not ranked; this says which and why, instead of
|
|
184
|
+
// the result silently holding fewer parts.
|
|
185
|
+
static json failed_candidates_to_json(const OpenMagnetics::MagneticAdviser& magneticAdviser) {
|
|
186
|
+
json failedCandidates = json::array();
|
|
187
|
+
for (const auto& [reference, error] : magneticAdviser.get_failed_candidates()) {
|
|
188
|
+
failedCandidates.push_back({{"reference", reference}, {"error", error}});
|
|
189
|
+
}
|
|
190
|
+
return failedCandidates;
|
|
191
|
+
}
|
|
192
|
+
|
|
182
193
|
json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson, int maximumNumberResults) {
|
|
183
194
|
OpenMagnetics::settings.set_coil_delimit_and_compact(true);
|
|
184
195
|
OpenMagnetics::Inputs inputs(inputsJson);
|
|
@@ -224,6 +235,7 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
|
|
|
224
235
|
sort(results["data"].begin(), results["data"].end(), [](json& b1, json& b2) {
|
|
225
236
|
return b1["scoring"] > b2["scoring"];
|
|
226
237
|
});
|
|
238
|
+
results["failedCandidates"] = failed_candidates_to_json(magneticAdviser);
|
|
227
239
|
|
|
228
240
|
return results;
|
|
229
241
|
}
|
|
@@ -282,6 +294,7 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
|
|
|
282
294
|
sort(results["data"].begin(), results["data"].end(), [](json& b1, json& b2) {
|
|
283
295
|
return b1["scoring"] > b2["scoring"];
|
|
284
296
|
});
|
|
297
|
+
results["failedCandidates"] = failed_candidates_to_json(magneticAdviser);
|
|
285
298
|
|
|
286
299
|
return results;
|
|
287
300
|
}
|
|
@@ -506,6 +519,9 @@ void register_adviser_bindings(py::module& m) {
|
|
|
506
519
|
- "lossesNotEvaluable" (only on such parts): why this part's core losses could
|
|
507
520
|
not be evaluated -- its core material carries no core-loss model. The part is
|
|
508
521
|
ranked without any loss-based filter and returned without simulated outputs.
|
|
522
|
+
And a "failedCandidates" array next to "data": one {"reference", "error"} per
|
|
523
|
+
candidate whose evaluation raised (a filter or the final simulation threw). Such a
|
|
524
|
+
candidate is not ranked; this names it and the error instead of dropping it silently.
|
|
509
525
|
|
|
510
526
|
Example:
|
|
511
527
|
>>> inputs = PyMKF.process_inputs(raw_inputs)
|
|
@@ -550,6 +566,9 @@ void register_adviser_bindings(py::module& m) {
|
|
|
550
566
|
not be evaluated -- its core material carries no core-loss model. The part is
|
|
551
567
|
ranked on the filters that apply to it, with no loss-based filter scored, and
|
|
552
568
|
is returned without simulated outputs.
|
|
569
|
+
And a "failedCandidates" array next to "data": one {"reference", "error"} per
|
|
570
|
+
candidate whose evaluation raised (a filter or the final simulation threw). Such a
|
|
571
|
+
candidate is not ranked; this names it and the error instead of dropping it silently.
|
|
553
572
|
|
|
554
573
|
Note:
|
|
555
574
|
Cache must be populated before calling this function.
|
|
@@ -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.
|
|
@@ -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
|
|
File without changes
|
{pyopenmagnetics-1.7.31 → pyopenmagnetics-1.7.33}/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
|