Read a power summary
Decide, from summary.json alone, whether a power capture is a
measurement and which number to publish. You will learn the order to read
the power block in, what each health signal can mean, and how to script
the same check.
Hardware and wiring
Section titled “Hardware and wiring”None. This page reads a bundle a power run already wrote; producing one is Power capture with a Joulescope.
Software prerequisites
Section titled “Software prerequisites”hpx (pip install helia-profiler) for the typed loader, and jq for the
shell lines.
Config file
Section titled “Config file”None. The bundle’s own configuration is recorded in run_metadata.json
under config, and the power stages that produced the block are listed in
summary.json under power.power_plan and power.target_lifecycle.
jq '.power | {measurement_scope, gated_window_count, gated_window_duration_ratio, gated_window_duration_suspect, integrity, power_firmware}' results/summary.jsonjq '.power | {energy_per_inference_j, inferences_per_joule, avg_current_a, avg_power_w}' results/summary.jsonjq '[.issues[] | select(.code | startswith("power."))]' results/summary.jsonfrom pathlib import Pathfrom helia_profiler.results.run_summary import load_run_summary
summary = load_run_summary(Path("results/summary.json"))power = summary.powerif power is None: raise SystemExit("no power block: the run captured no power")print(power.measurement_scope, power.gated_window_count, power.gated_window_duration_ratio)if power.gate_duration_unarbitrated_failure or power.gated_window_duration_suspect: print("do not quote energy per inference; see the power.* issue codes")else: print(power.energy_per_inference_j, "J per inference,", power.avg_current_a, "A average")Expected output
Section titled “Expected output”Not yet captured: no bundle with a power block ships with the
documentation, so no figures appear here. The keys are the contract in
PowerSection (results/run_summary.py), written by report/summary.py;
the capture-side metadata keys it carries through are pinned by
tests/contracts/snapshots/power_metadata_census.json. A healthy Joulescope capture answers the
first jq line with measurement_scope: "gpio_gated_clean_window",
gated_window_count: 1, a gated_window_duration_ratio near 1.0,
gated_window_duration_suspect null in the jq output (the writer never
emits false), integrity: "valid" and
power_firmware: "dedicated"; the Python block then prints the energy per
inference and the average current.
How to read it
Section titled “How to read it”Read the block in this order; each step can stop the next.
- Scope.
measurement_scopesays what the numbers were integrated over.gpio_gated_clean_window(Joulescope,power.mode: external) andon_device_gated_inference(INA228,power.mode: internal) are per-inference measurements;free_form_capturemeans no qualifying gate was found and the numbers cover the whole capture, with the per-inference fields withheld (Windows and lock-step). - Health.
gated_window_duration_suspectset, orgate_duration_unarbitrated_failuretrue on the loadedPowerSection, means the gate did not line up with the inference loop or the firmware reported failed work;gated_window_duration_ratiofar from 1.0 withoutgated_window_reference_driftsays the same. Theissues[]entries whose code starts withpower.name the reason; each is on the issue codes page and its fix on Verifying a capture. - Provenance.
power_firmwaremust bededicatedfor a clean number (sharedcarries the transport’s own draw);clean_workloadandfirmware_code_fingerprintrecord what ran inside the window and are whathpx compareuses to refuse a comparison. - The numbers.
energy_per_inference_jis the scope’s integrated energy divided by the inferences it counted;inferences_per_jouleis its inverse;avg_current_a,median_current_a,p95_current_aandp99_current_aare in amperes inside the scope (the percentiles exist for the Joulescope only);avg_power_win watts;capture_duration_sis the scope’s duration, not the host’s. Units are the SI base units the field names carry. - The terminal. The run printed the same figures as a
Powertable in milliamps, milliwatts and microjoules; the JSON is the record.
An INA228 run answers step 1 with on_device_gated_inference and carries
on_device_summary under power instead of the Joulescope percentiles;
its fields are on INA228.
Variations
Section titled “Variations”detailed/power_summary.csv(withoutput.detailed: true) hasscope,metric,valuerows for each gated window pluswhole_capture_windowreference rows when the whole-capture summary was recorded, which is where to look whengated_vs_whole_current_okisfalse.- A
busy_loopwindow (profiling.clean_window_probe: busy_loop, for bring-up) carries the same scope and health fields but measures a calibrated spin, not an inference: the per-inference figures are withheld andpower.per_inference_metrics_omittednames the probe; thepower_clean_window_probedimension lives inresult_manifest.json. - Two captures: Compare two power runs.
Validated on
Section titled “Validated on”Not yet validated. No captured power bundle ships with the documentation,
so the page carries no figures; the field names and the health rules are
quoted from results/run_summary.py, power/metadata.py and the
Output files contract at the source tree this site
was built from. The nightly hardware validation runs with power: off.