PyOpenMagnetics 1.7.33__tar.gz → 1.7.35__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.
Files changed (95) hide show
  1. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/CMakeLists.txt +1 -1
  2. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/PKG-INFO +1 -1
  3. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/PyOpenMagnetics.pyi +11 -1
  4. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/pyproject.toml +1 -1
  5. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/advisers.cpp +88 -4
  6. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/.github/workflows/ci.yml +0 -0
  7. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/.github/workflows/publish.yml +0 -0
  8. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/.gitignore +0 -0
  9. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/AGENTS.md +0 -0
  10. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/LICENSE +0 -0
  11. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/README.md +0 -0
  12. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/_packaging/__init__.py +0 -0
  13. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/_packaging/_build_info.py.in +0 -0
  14. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/api/MAS.py +0 -0
  15. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/api/mas_db_reader.py +0 -0
  16. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/api/validation.py +0 -0
  17. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/clear_cibuildwheel_cache.sh +0 -0
  18. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/docs/compatibility.md +0 -0
  19. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/docs/errors.md +0 -0
  20. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/docs/performance.md +0 -0
  21. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/README.md +0 -0
  22. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/buck_inductor.py +0 -0
  23. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/complete_simulation_example.py +0 -0
  24. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/converter_design_example.py +0 -0
  25. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/debug_bobbin.py +0 -0
  26. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/debug_coil.py +0 -0
  27. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/debug_core.py +0 -0
  28. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/debug_plotting.py +0 -0
  29. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_220v_12v_1a.py +0 -0
  30. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_220v_12v_2a_complete.py +0 -0
  31. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_bh_curve.png +0 -0
  32. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_core.png +0 -0
  33. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_design.py +0 -0
  34. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_summary.png +0 -0
  35. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/flyback_waveforms.png +0 -0
  36. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/list_plot_funcs.py +0 -0
  37. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/plot_flyback_design.py +0 -0
  38. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/plot_flyback_pyom.py +0 -0
  39. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/test_field_calc.py +0 -0
  40. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/examples/test_field_plot.py +0 -0
  41. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/force_fresh_build.sh +0 -0
  42. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/llms.txt +0 -0
  43. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/notebooks/01_getting_started.ipynb +0 -0
  44. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/notebooks/02_buck_inductor.ipynb +0 -0
  45. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/notebooks/03_core_losses.ipynb +0 -0
  46. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/notebooks/README.md +0 -0
  47. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/requirements.txt +0 -0
  48. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/advisers.h +0 -0
  49. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/bobbin.cpp +0 -0
  50. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/bobbin.h +0 -0
  51. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/cmc.cpp +0 -0
  52. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/cmc.h +0 -0
  53. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/common.h +0 -0
  54. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/converter.cpp +0 -0
  55. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/converter.h +0 -0
  56. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/core.cpp +0 -0
  57. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/core.h +0 -0
  58. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/crossref.cpp +0 -0
  59. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/crossref.h +0 -0
  60. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/database.cpp +0 -0
  61. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/database.h +0 -0
  62. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/logging.cpp +0 -0
  63. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/logging.h +0 -0
  64. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/losses.cpp +0 -0
  65. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/losses.h +0 -0
  66. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/module.cpp +0 -0
  67. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/plotting.cpp +0 -0
  68. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/plotting.h +0 -0
  69. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/settings.cpp +0 -0
  70. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/settings.h +0 -0
  71. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/simulation.cpp +0 -0
  72. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/simulation.h +0 -0
  73. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/utils.cpp +0 -0
  74. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/utils.h +0 -0
  75. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/winding.cpp +0 -0
  76. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/winding.h +0 -0
  77. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/wire.cpp +0 -0
  78. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/src/wire.h +0 -0
  79. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/test.py +0 -0
  80. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/__init__.py +0 -0
  81. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/conftest.py +0 -0
  82. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_converter_endpoints.py +0 -0
  83. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_core.py +0 -0
  84. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_core_adviser.py +0 -0
  85. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_datasheet_only_catalogue.py +0 -0
  86. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_examples_integration.py +0 -0
  87. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_inputs.py +0 -0
  88. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_logging.py +0 -0
  89. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_losses_not_evaluable_catalogue.py +0 -0
  90. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_magnetic_adviser.py +0 -0
  91. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_magnetics_catalogue_load.py +0 -0
  92. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_mas_schema_walk.py +0 -0
  93. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_material_validator.py +0 -0
  94. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/tests/test_plotting.py +0 -0
  95. {pyopenmagnetics-1.7.33 → pyopenmagnetics-1.7.35}/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-04-mkf-508edb8b")
145
+ set(MKF_FORCE_REFRESH "2026-10-04-mkf-39b59172")
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
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: PyOpenMagnetics
3
- Version: 1.7.33
3
+ Version: 1.7.35
4
4
  Summary: Python wrapper for OpenMagnetics
5
5
  Author-Email: Alfonso Martinez <Alfonso_VII@hotmail.com>
6
6
  Classifier: Development Status :: 4 - Beta
@@ -679,8 +679,18 @@ 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
+ - "judgedFrequencies" (only with a frequency-wise requirement): requirement frequencies
683
+ in Hz each such filter judged the part on, keyed by filter enum name, e.g.
684
+ {"IMPEDANCE": [150000.0, 1000000.0, 30000000.0]}. IMPEDANCE judges a minimumImpedance
685
+ point inside the core material's tabulated mu(f) range on the model, and outside it on
686
+ the part's own measured common-mode |Z| (zero-bias datasheet impedancePoints, log-log
687
+ interpolated, never extrapolated); unlisted points were not judged.
688
+ - "measuredFrequencies" (only with a frequency-wise requirement): the subset of
689
+ "judgedFrequencies" judged on measured data, e.g. {"IMPEDANCE": [30000000.0]}; an
690
+ empty list means every judged point used the model.
682
691
  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.
692
+ the search excluded (a filter or the final simulation raised, or the
693
+ LOSS_MODEL_FREQUENCY_SPAN gate excluded it while losses are computed); it is not ranked.
684
694
  """
685
695
  ...
686
696
 
@@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build"
4
4
 
5
5
  [project]
6
6
  name = "PyOpenMagnetics"
7
- version = "1.7.33"
7
+ version = "1.7.35"
8
8
  requires-python = ">=3.8"
9
9
  authors = [
10
10
  { name="Alfonso Martinez", email="Alfonso_VII@hotmail.com" },
@@ -207,6 +207,8 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
207
207
 
208
208
  auto scoringsPerFilter = magneticAdviser.get_scorings();
209
209
  const auto& lossesNotEvaluable = magneticAdviser.get_losses_not_evaluable();
210
+ const auto& judgedFrequencies = magneticAdviser.get_judged_frequencies();
211
+ const auto& measuredFrequencies = magneticAdviser.get_measured_frequencies();
210
212
 
211
213
  json results = json();
212
214
  results["data"] = json::array();
@@ -229,6 +231,28 @@ json calculate_advised_magnetics_from_catalog(json inputsJson, json catalogJson,
229
231
  if (name && lossesNotEvaluable.count(*name)) {
230
232
  result["lossesNotEvaluable"] = lossesNotEvaluable.at(*name);
231
233
  }
234
+ // The requirement frequencies each frequency-wise filter judged this part on. IMPEDANCE
235
+ // judges a minimumImpedance point inside the core material's tabulated mu(f) range on the
236
+ // model, and outside it on the part's measured common-mode |Z| when the datasheet has one;
237
+ // a point not listed here was not judged (neither passed nor failed).
238
+ if (name && judgedFrequencies.count(*name)) {
239
+ json judged;
240
+ for (auto& [filter, frequencies] : judgedFrequencies.at(*name)) {
241
+ judged[std::string(magic_enum::enum_name(filter))] = frequencies;
242
+ }
243
+ result["judgedFrequencies"] = judged;
244
+ }
245
+ // The subset of judgedFrequencies judged on the part's own measured data instead of the
246
+ // model: IMPEDANCE judges a point outside the material's mu(f) range on the datasheet's
247
+ // measured common-mode |Z| (zero-bias impedancePoints, log-log interpolated, never
248
+ // extrapolated). An empty list: every judged point used the model.
249
+ if (name && measuredFrequencies.count(*name)) {
250
+ json measured;
251
+ for (auto& [filter, frequencies] : measuredFrequencies.at(*name)) {
252
+ measured[std::string(magic_enum::enum_name(filter))] = frequencies;
253
+ }
254
+ result["measuredFrequencies"] = measured;
255
+ }
232
256
  results["data"].push_back(result);
233
257
  }
234
258
 
@@ -266,6 +290,8 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
266
290
 
267
291
  auto scoringsPerFilter = magneticAdviser.get_scorings();
268
292
  const auto& lossesNotEvaluable = magneticAdviser.get_losses_not_evaluable();
293
+ const auto& judgedFrequencies = magneticAdviser.get_judged_frequencies();
294
+ const auto& measuredFrequencies = magneticAdviser.get_measured_frequencies();
269
295
 
270
296
  json results = json();
271
297
  results["data"] = json::array();
@@ -288,6 +314,28 @@ json calculate_advised_magnetics_from_cache(json inputsJson, json filterFlowJson
288
314
  if (name && lossesNotEvaluable.count(*name)) {
289
315
  result["lossesNotEvaluable"] = lossesNotEvaluable.at(*name);
290
316
  }
317
+ // The requirement frequencies each frequency-wise filter judged this part on. IMPEDANCE
318
+ // judges a minimumImpedance point inside the core material's tabulated mu(f) range on the
319
+ // model, and outside it on the part's measured common-mode |Z| when the datasheet has one;
320
+ // a point not listed here was not judged (neither passed nor failed).
321
+ if (name && judgedFrequencies.count(*name)) {
322
+ json judged;
323
+ for (auto& [filter, frequencies] : judgedFrequencies.at(*name)) {
324
+ judged[std::string(magic_enum::enum_name(filter))] = frequencies;
325
+ }
326
+ result["judgedFrequencies"] = judged;
327
+ }
328
+ // The subset of judgedFrequencies judged on the part's own measured data instead of the
329
+ // model: IMPEDANCE judges a point outside the material's mu(f) range on the datasheet's
330
+ // measured common-mode |Z| (zero-bias impedancePoints, log-log interpolated, never
331
+ // extrapolated). An empty list: every judged point used the model.
332
+ if (name && measuredFrequencies.count(*name)) {
333
+ json measured;
334
+ for (auto& [filter, frequencies] : measuredFrequencies.at(*name)) {
335
+ measured[std::string(magic_enum::enum_name(filter))] = frequencies;
336
+ }
337
+ result["measuredFrequencies"] = measured;
338
+ }
291
339
  results["data"].push_back(result);
292
340
  }
293
341
 
@@ -519,9 +567,25 @@ void register_adviser_bindings(py::module& m) {
519
567
  - "lossesNotEvaluable" (only on such parts): why this part's core losses could
520
568
  not be evaluated -- its core material carries no core-loss model. The part is
521
569
  ranked without any loss-based filter and returned without simulated outputs.
570
+ - "judgedFrequencies" (only with a frequency-wise requirement): the requirement
571
+ frequencies, in Hz, each such filter judged the part on, keyed by filter enum
572
+ name, e.g. {"IMPEDANCE": [150000.0, 1000000.0, 30000000.0]}. IMPEDANCE judges a
573
+ minimumImpedance point inside the core material's tabulated mu(f) range on the
574
+ model, and outside it on the part's own measured common-mode |Z| (datasheet
575
+ impedancePoints with no DC bias, log|Z| vs log f interpolated between the
576
+ bracketing measured points, never extrapolated); a point covered by neither is
577
+ not listed and was not judged (neither passed nor failed), and the IMPEDANCE
578
+ score is the mean over the listed points.
579
+ - "measuredFrequencies" (only with a frequency-wise requirement): the subset of
580
+ "judgedFrequencies" judged on the measured data, keyed the same way, e.g.
581
+ {"IMPEDANCE": [30000000.0]}; an empty list means every judged point used the model.
522
582
  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.
583
+ candidate the search excluded: a filter or the final simulation raised (e.g.
584
+ IMPEDANCE when no minimumImpedance point lies inside the material's mu(f) range),
585
+ or the LOSS_MODEL_FREQUENCY_SPAN gate excluded it because an operating frequency
586
+ lies outside its material's fitted core-loss span while the flow or the final
587
+ simulation computes core losses. Such a candidate is not ranked; this names it
588
+ and the reason instead of dropping it silently.
525
589
 
526
590
  Example:
527
591
  >>> inputs = PyMKF.process_inputs(raw_inputs)
@@ -554,6 +618,10 @@ void register_adviser_bindings(py::module& m) {
554
618
  outputs (losses, temperature). False returns the ranking and the
555
619
  per-filter scores only -- much faster over many results -- and a part
556
620
  whose simulation would fail is then returned rather than dropped.
621
+ The simulation computes core losses at every operating point, so with
622
+ True a part whose material's core-loss fit does not cover an operating
623
+ frequency (e.g. a 50 Hz mains point) is excluded and listed in
624
+ failedCandidates; with False and no loss-based filter it is kept.
557
625
 
558
626
  Returns:
559
627
  JSON object with "data" array containing ranked results.
@@ -566,9 +634,25 @@ void register_adviser_bindings(py::module& m) {
566
634
  not be evaluated -- its core material carries no core-loss model. The part is
567
635
  ranked on the filters that apply to it, with no loss-based filter scored, and
568
636
  is returned without simulated outputs.
637
+ - "judgedFrequencies" (only with a frequency-wise requirement): the requirement
638
+ frequencies, in Hz, each such filter judged the part on, keyed by filter enum
639
+ name, e.g. {"IMPEDANCE": [150000.0, 1000000.0, 30000000.0]}. IMPEDANCE judges a
640
+ minimumImpedance point inside the core material's tabulated mu(f) range on the
641
+ model, and outside it on the part's own measured common-mode |Z| (datasheet
642
+ impedancePoints with no DC bias, log|Z| vs log f interpolated between the
643
+ bracketing measured points, never extrapolated); a point covered by neither is
644
+ not listed and was not judged (neither passed nor failed), and the IMPEDANCE
645
+ score is the mean over the listed points.
646
+ - "measuredFrequencies" (only with a frequency-wise requirement): the subset of
647
+ "judgedFrequencies" judged on the measured data, keyed the same way, e.g.
648
+ {"IMPEDANCE": [30000000.0]}; an empty list means every judged point used the model.
569
649
  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.
650
+ candidate the search excluded: a filter or the final simulation raised (e.g.
651
+ IMPEDANCE when no minimumImpedance point lies inside the material's mu(f) range),
652
+ or the LOSS_MODEL_FREQUENCY_SPAN gate excluded it because an operating frequency
653
+ lies outside its material's fitted core-loss span while the flow or the final
654
+ simulation computes core losses. Such a candidate is not ranked; this names it
655
+ and the reason instead of dropping it silently.
572
656
 
573
657
  Note:
574
658
  Cache must be populated before calling this function.