Skip to content

#560: re-land pec_csv two-stage port (lost in #605 stacked-merge)#607

Merged
jepegit merged 1 commit into
masterfrom
issue-560-parse-pec-relanding
Jul 21, 2026
Merged

#560: re-land pec_csv two-stage port (lost in #605 stacked-merge)#607
jepegit merged 1 commit into
masterfrom
issue-560-parse-pec-relanding

Conversation

@jepegit

@jepegit jepegit commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Re-lands the pec_csv two-stage port. The original #605 was marked merged but its changes never reached master — it was a stacked PR whose base was #602's branch, and when #602 squash-merged, #605's diff merged into the intermediate branch rather than master. git merge-base --is-ancestor confirms #605's merge commit is not on master, and pec_csv.py on master has no parse().

This is the same commit, cherry-picked cleanly onto current master (which now has #602, #603, #604, #606). Opened against master so CI actually runs this time.


pec_csv gets parse() and declarations() beside its existing loader(), which is untouched. It was the one loader that couldn't be handled mechanically: a BaseLoader (no config_params), columns identified by alias set rather than fixed strings, and a different unit per column embedded in the header. Design decisions in the plan (§2.6a).

  • parse() reads → matches alias sets → renames matched columns to canonical vendor-neutral names → scales each to its canonical unit → shifts 0-based cycles. The unit scaling is not optional: harmonize() has no per-column unit scaling, so an unscaled mV column would be tagged as V. The parity oracle catches exactly that.
  • declarations() reuses derive_column_maps (inverting _COLUMN_KEY_TO_CELLPY_HEADER, composing with cellpy-core's legacy→native map) rather than hand-transcribing — so pec inherits the provenance rule and energy mapping from the same code the config loaders use. Only the passthrough set is per file.
  • Oracle fix: pec's sparse all-null columns made the numeric diff None, which was read as a mismatch (and would have hidden a real values-vs-null diff). The comparison now checks null masks first, then non-null values. Mutation-checked both ways.

pec reaches value parity on every mapped and passthrough column (only exclusion: the shared date_time gap). Five parity cases; tests/test_pec_two_stage.py pins the decisions. Full suite under PYTHONUTF8=1: 1243 passed, 0 failed.

🤖 Generated with Claude Code

pec_csv is not configuration-driven - it identifies columns by alias set and
carries a different unit per column in the header - so its parse()/declarations()
are hand-written rather than derived. loader() is untouched; this adds the two
stages beside it.

parse() reads the file, matches the alias sets, renames matched columns to
canonical vendor-neutral names (voltage, current, ...), and scales each column
to its canonical unit. That last step is not optional: harmonize() has no
per-column unit scaling, so leaving a mV column unscaled would tag it as V -
silent corruption. The parity oracle catches exactly that (a mutation removing
the scaling shows a 3795 mV diff on potential).

declarations() does not hand-transcribe the canonical->native map. It reuses
derive_column_maps by inverting _COLUMN_KEY_TO_CELLPY_HEADER and composing with
cellpy-core's legacy->native map, so pec gets the provenance rule (vendor `test`
dropped rather than mapped onto the framework test_id) and the 0.2.3 energy
mapping for free - the same code the config loaders use. Only the passthrough
set is per file: the export's other columns (temperatures, OCV, peak power) are
kept under their sanitised names, matching loader().

Building it surfaced a false negative in the parity oracle, now fixed: pec
carries sparse per-measurement columns that are all-null in the fixture, and the
numeric diff of two all-null columns is None, which the oracle counted as a
mismatch. That rule would also have hidden a real values-vs-null difference. The
comparison now checks null masks first, then non-null values. Mutation-checked
both directions (the neware PER_STEP mutant still fails; an all-null column
agrees).

pec reaches value parity on every mapped and passthrough column; the only
exclusion is the shared date_time representation gap.

tests/test_pec_two_stage.py pins the decisions (units normalised in parse, static
canonical->native map, passthroughs preserved, provenance dropped).
Full suite under PYTHONUTF8=1: 1205 passed, 0 failed.

Loader plan §2.6a updated with the execution record.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@jepegit
jepegit merged commit 83707e7 into master Jul 21, 2026
5 checks passed
@jepegit
jepegit deleted the issue-560-parse-pec-relanding branch July 21, 2026 08:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant