Skip to content

Fix monthly tasmax/tasmin being a copy of tas (#644) - #648

Merged
rhaegar325 merged 1 commit into
mainfrom
fix/644-monthly-tasmax-tasmin
Aug 25, 2026
Merged

Fix monthly tasmax/tasmin being a copy of tas (#644)#648
rhaegar325 merged 1 commit into
mainfrom
fix/644-monthly-tasmax-tasmin

Conversation

@rbeucher

Copy link
Copy Markdown
Member

Closes #644

The problem

Monthly tasmax and tasmin (CMIP7 tmaxavg/tminavg) were written with exactly the same values as tas. The daily variants were also wrong: they contained the daily mean temperature, not the daily max/min.

Why it happened

The mapping pointed all these variables at fld_s03i236, which is the mean temperature field. The real daily max/min live in separate fields (fld_s03i236_max / fld_s03i236_min) that only exist in the daily output stream — the monthly stream doesn't have them at all.

So for monthly targets, MOPPy read the monthly-mean field and asked for the "monthly maximum" of it. With one value per month, that operation returns the input unchanged — a silent copy of tas. For daily targets, it simply renamed the daily-mean field.

The fix

  1. Read the right fields: tasmax/tasmin now read fld_s03i236_max/_min from the daily files (ESM1-5 and ESM1-6 mappings), even for monthly output.
  2. Do the right reduction: monthly values are now the mean over days of the daily max/min (new calculate_monthly_mean operation), matching the CF definition "time: maximum within days time: mean over days". Daily output just renames the true extrema field.
  3. Fail loudly next time: the monthly reduction functions now raise an error if fed monthly data instead of silently copying it, and the resampling-method detection correctly handles chained cell_methods (the substring-ordering bug described in [BUG] CMIP7 tmaxavg/tminavg output is a copy of tavg, not a daily max/min aggregate #644, which also affects e.g. sfcWindmax).
  4. The auto-resampler now leaves the input alone when the variable's own formula does the time reduction, so the formula receives the raw daily data.

Verification

Against the ESM1.6 test archive (1951):

output before after expected
tmaxavg (mon) 277.70 (= tas) 279.57 279.57
tminavg (mon) 277.70 (= tas) 275.82 275.82
tmax (day) 277.71 (= daily-mean tas) 279.59 279.59 (raw daily max)
tmin (day) 277.71 (= daily-mean tas) 275.84 275.84 (raw daily min)

tasmax > tas > tasmin now holds everywhere. Full unit suite passes (1849 tests, 36 new/updated), and the CMIP7 integration tests for all four variants pass WCRP compliance.

Note for reviewers: the ESM1-5 mapping got the same change. Its stream layout and naming convention are identical (it already uses fld_s03i230_max for sfcWindmax), but I could only verify values against ESM1.6 data — worth confirming ESM1-5 archives carry fld_s03i236_max/_min in their pe daily files.

🤖 Generated with Claude Code

CMIP7 monthly tas_tmaxavg/tminavg (CMIP6 Amon.tasmax/tasmin) were written
bit-identical to tas: the mapping pointed all three variables at the
monthly-mean fld_s03i236, and calculate_monthly_maximum/minimum over
already-monthly bins is an identity. The daily variants renamed the daily
*mean* field, so they were wrong too.

- Point tasmax/tasmin (ESM1-5, ESM1-6) at the model's within-day extrema
  fld_s03i236_max/_min from the daily pe stream (per-variable file_pattern
  override), and reduce them to monthly with a new calculate_monthly_mean
  operation, matching the CF cell_methods
  "time: maximum within days time: mean over days". Daily targets keep the
  direct-rename bypass, which now renames the true extrema field.
- Skip the generic auto-resampling when the variable's formula performs the
  temporal reduction itself, so the formula receives the raw daily input.
- Guard the monthly reductions against monthly-or-coarser input: every bin
  would hold one sample, silently returning the input unchanged.
- determine_resampling_method: parse the last "time:" clause of cell_methods
  instead of substring priority, so chained reductions ("maximum within days
  ... mean over days") continue the outer reduction (mean) when coarsening.
- Integration test harness honours per-variable file_pattern overrides.

Verified against the ESM1.6 test archive: monthly tmaxavg/tminavg now equal
the monthly mean of the daily max/min (279.57/275.82 K vs tas 277.70 K),
daily tmax/tmin match the raw extrema exactly, and tasmax > tas > tasmin.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@rbeucher
rbeucher requested a review from rhaegar325 August 24, 2026 21:54
@codecov

codecov Bot commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 77.3%. Comparing base (b5d3b16) to head (eb74279).

Additional details and impacted files
@@          Coverage Diff          @@
##            main    #648   +/-   ##
=====================================
  Coverage   77.3%   77.3%           
=====================================
  Files         40      40           
  Lines       8524    8519    -5     
  Branches    1593    1587    -6     
=====================================
- Hits        6590    6589    -1     
+ Misses      1604    1602    -2     
+ Partials     330     328    -2     
Flag Coverage Δ
unit 77.3% <100.0%> (+<0.1%) ⬆️

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

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rhaegar325
rhaegar325 merged commit 5a49c25 into main Aug 25, 2026
4 checks passed
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.

[BUG] CMIP7 tmaxavg/tminavg output is a copy of tavg, not a daily max/min aggregate

2 participants