Skip to content

feat: show what changed inside a NetCDF baseline - #913

Merged
lewisjared merged 6 commits into
mainfrom
feat/baseline-diff-netcdf
Sep 4, 2026
Merged

feat: show what changed inside a NetCDF baseline#913
lewisjared merged 6 commits into
mainfrom
feat/baseline-diff-netcdf

Conversation

@lewisjared

@lewisjared lewisjared commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

A changed .nc file in the ref test-cases diff report previously showed only a size delta and two download links, so a reviewer could not tell a renamed attribute from a numerical regression without downloading both blobs and opening them by hand.

Builds on #910

Each changed NetCDF file now gets two things: a collapsible diff of its ncdump-style header, and a table with one row per data variable.

  • Adds netcdf_diff to baseline_report/analyse.py, which opens both sides with xarray and returns the header diff plus one StatRow per data variable.
  • Each row carries min, max, mean and NaN count on both sides, the largest absolute and relative difference, and the number of cells that differ.
  • NaN counts as equal to NaN, so a masked cell staying masked is not a change.
  • Rows where something moved are shaded, so the eye lands on them.
  • Non-numeric variables get a row with shapes only, rather than an exception.
  • Splits the old combined "NetCDF and other" table into a NetCDF section and an "Other files" table.
  • Declares xarray, which the workspace previously only had through the root pyproject.toml and transitive dependencies.

Decoding is turned off when opening, so a non-standard calendar or unit cannot fail the report. A file over 100 MB is left unopened with a note, and any error opening one becomes a note on that file rather than an exception.

Also fixes a layout bug from #910.

Summary by CodeRabbit

  • New Features

    • NetCDF files in reference test-case reports now include collapsible header diffs and per-variable comparison statistics.
    • Reports show minimum, maximum, mean, NaN counts, absolute and relative differences, and differing-cell counts.
    • NetCDF and other binary files are presented in separate expandable sections.
  • Improvements

    • Moved rows are highlighted, with NaN values treated as equal.
    • Wide statistics tables and long filenames remain readable through improved scrolling and wrapping.
    • Reports support light and dark theme styling for moved changes.

A changed `.nc` file previously showed only a size delta and two download links, so a
reviewer could not tell a renamed attribute from a numerical regression without opening
both blobs by hand.

- Adds `netcdf_diff`, which opens both sides with xarray and returns a diff of the
  ncdump-style header plus one `StatRow` per data variable.
- Each row carries min, max, mean and NaN count on both sides, the largest absolute and
  relative difference, and the number of cells that differ.
- NaN counts as equal to NaN, so a masked cell staying masked is not a change.
- Rows where something moved are shaded.
- Splits the old combined table into a NetCDF section and an "Other files" table.
- Declares `xarray`, which the workspace previously only had by accident.
- Wraps both tables so a wide one scrolls inside its card, and lets a long file name wrap.

Decoding is turned off when opening, so a non-standard calendar or unit cannot fail the
report. Stats are whole-array. Per-time or per-level breakdowns are deliberately not here.
- Adds `_variable`, so each side of a data variable is looked up once instead of
  once for its shape and again for its values.
- Extracts `_fetch_pair`, which held the same four lines in `_diff_for` and
  `_netcdf_for` including the note precedence rule.
- Drops the text-flavoured defaults on `_fetch_side`, because `limit` and `oversize`
  are a pair and a caller that passes one and forgets the other gets a wrong note.
- Passes the base side's scale into `_compare` from the reduction `_summarise` already
  ran, so a large variable is not scanned a third time.
- Replaces the three overlapping all-NaN guards in `_compare` with one, and drops the
  warning filter in `_summarise` that its size check had already made unreachable.
- Narrows `_of_kind` to one kind, which is all any call site passes now.
- Gives `AnalysedFile.netcdf` the same standing as `text`, with no default.
- Adds a `dash` filter so the template no longer decides how an absent shape reads.

No behaviour change. The `num` filter is annotated to admit the counts it already formats.
@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Approval pending

CodeRabbit has no unresolved comments, but it could not review the latest commit because the review limit was reached. Follow the review guidance in this comment to continue.

📝 Walkthrough

Walkthrough

The report now analyses NetCDF files, computes header and variable-level differences, and renders them in separate collapsible sections. Other binary files remain in a separate table. Tests cover analysis, fetching, rendering, and layout behaviour.

Changes

NetCDF baseline report

Layer / File(s) Summary
NetCDF models and comparison
packages/climate-ref/src/climate_ref/baseline_report/analyse.py
Adds StatRow and NetcdfDiff. NetCDF headers and variables now produce shape, value, NaN, difference, and moved-row data.
NetCDF fetching and case wiring
packages/climate-ref/pyproject.toml, packages/climate-ref/src/climate_ref/baseline_report/analyse.py, packages/climate-ref/tests/unit/baseline_report/test_analyse.py
Adds the xarray dependency, NetCDF-specific fetch limits, separate netcdfs and others collections, and analysis tests for edge cases.
NetCDF report rendering
packages/climate-ref/src/climate_ref/baseline_report/render.py, packages/climate-ref/src/climate_ref/baseline_report/templates/*, packages/climate-ref/tests/unit/baseline_report/test_render.py, changelog/913.feature.md
Adds formatting filters, NetCDF and diff macros, separate report sections, moved-row styling, table scrolling, filename wrapping, rendering tests, and a changelog entry.

Sequence Diagram(s)

sequenceDiagram
  participant analyse
  participant _fetch_pair
  participant netcdf_diff
  participant netcdf_block
  analyse->>_fetch_pair: fetch NetCDF file sides
  _fetch_pair-->>analyse: return fetched paths or note
  analyse->>netcdf_diff: calculate header and variable differences
  netcdf_diff-->>analyse: return NetcdfDiff
  analyse->>netcdf_block: render NetcdfDiff
  netcdf_block-->>analyse: render report section
Loading

Merge Risk: 🟡 Moderate · up to f2c9f

Some NetCDF changes can be reported inaccurately, while highly compressed files can consume excessive memory. These issues should be fixed before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 56.60% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 4 files. (5 skipped: … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: detailed reporting of changes inside NetCDF baselines.
Description check ✅ Passed The description provides a detailed purpose, implementation summary, behaviour details, error handling, dependency change, and reference to the related issue. It omits the template headings and checkl…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Full details: Docstring Coverage

Explanation

Docstring coverage is 56.60% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 53 functions across 4 files. (5 skipped: 5 unsupported.)

✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/baseline-diff-netcdf

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov

codecov Bot commented Sep 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

Flag Coverage Δ
core 93.88% <100.00%> (+0.06%) ⬆️
providers 87.73% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...ate-ref/src/climate_ref/baseline_report/analyse.py 98.46% <100.00%> (+1.51%) ⬆️
...mate-ref/src/climate_ref/baseline_report/render.py 87.23% <100.00%> (+3.45%) ⬆️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The header was a unified diff, so a reader saw three lines of context around each hunk and
nothing else. That names what moved but does not say what the file holds, which is the thing
you want when reviewing a baseline you have not seen before. A file whose header did not move
showed no header at all.

- The header listing now keeps every line and tags the ones that moved, so it doubles as the
  file's description. The `---`, `+++` and `@@` markers are gone with it.
- Adds a Diff and Side by side toggle, so the two headers can be read one next to the other.
- `NetcdfDiff` carries `header_old` and `header_new` for that second view, and
  `header_changed` says whether anything moved.
- An unchanged header is marked as such on the summary rather than replaced by a note.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
packages/climate-ref/src/climate_ref/baseline_report/templates/macros.html.j2 (1)

48-48: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add a regression test for escaped NetCDF header values.

render.py configures Jinja with select_autoescape(["html", "j2"]), so line.text is escaped in this macro. Add a rendered-header test with a markup payload to protect this setting.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Team

Run ID: 29443150-915a-4267-a2f3-0a63e63b5b37

📥 Commits

Reviewing files that changed from the base of the PR and between 44a5c7a and f2c9f88.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (9)
  • changelog/913.feature.md
  • packages/climate-ref/pyproject.toml
  • packages/climate-ref/src/climate_ref/baseline_report/analyse.py
  • packages/climate-ref/src/climate_ref/baseline_report/render.py
  • packages/climate-ref/src/climate_ref/baseline_report/templates/case.html.j2
  • packages/climate-ref/src/climate_ref/baseline_report/templates/macros.html.j2
  • packages/climate-ref/src/climate_ref/baseline_report/templates/report.css
  • packages/climate-ref/tests/unit/baseline_report/test_analyse.py
  • packages/climate-ref/tests/unit/baseline_report/test_render.py

Included review availability: Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread packages/climate-ref/src/climate_ref/baseline_report/analyse.py Outdated
Comment thread packages/climate-ref/src/climate_ref/baseline_report/analyse.py
A shaded row said something in the variable changed but not what. Scanning a row of nine
old to new pairs to find the one that shifted is work the report can do for the reader.

- Adds `Pair`, which holds one statistic on each side and answers whether it moved.
- `StatRow` now carries `shape`, `minimum`, `maximum`, `mean` and `nan` as pairs, which
  drops it from fifteen fields to ten.
- The new half of a pair is bold when it differs, and the three diff columns are bold when
  any cell moved.
- A value can be bold while reading the same as its neighbour, because the display rounds
  to six significant figures and the underlying floats still differ.

Also carries an edit to the changelog fragment made outside these changes.
Three findings from the review, all in the statistics.

- Keeps the stored dtype when reading a variable. Casting to float first meant an `int64`
  past 2**53 lost its last digit, so two adjacent values compared equal and a changed cell
  was reported as unchanged. The mean still accumulates in float64, so a float32 variable
  reads the same as before.
- Reports no maximum difference when a cell moved between NaN and a number. The subtraction
  gives NaN there, so the finite maximum could read as 0 next to a non-zero cell count.
- Bounds what one side may decode to. The fetch cap covers the stored blob, and a compressed
  file under it can still expand past what the job can hold.
@lewisjared

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Sep 4, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@lewisjared
lewisjared merged commit 0e1d281 into main Sep 4, 2026
26 of 27 checks passed
@lewisjared
lewisjared deleted the feat/baseline-diff-netcdf branch September 4, 2026 03:17
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