Skip to content

Fix leap-year Oct/Dec notch in the monthly FOD decay mask - #39

Merged
HughRunyan merged 1 commit into
mainfrom
country-exceedance-source-trace
Aug 4, 2026
Merged

Fix leap-year Oct/Dec notch in the monthly FOD decay mask#39
HughRunyan merged 1 commit into
mainfrom
country-exceedance-source-trace

Conversation

@HughRunyan

Copy link
Copy Markdown
Collaborator

Summary

Fixes a leap-year artifact in SWEET's monthly first-order-decay kernel (estimate_emissions_monthly) that produced a spurious ~1–2% dip in the 31-day peak months (October, December) of modeled sites, in leap years only. It surfaced as Climate TRACE QA "de-trended z-score > 3" flags on smoothly-growing modeled countries at Oct-2020 and Dec-2020 (e.g. Bangladesh, Cameroon), traced by the diagnostic in the paired RMI PR (see below).

Bug

  • Component: SWEET_python/model_v2.pySWEET.estimate_emissions_monthly (the modeled-site monthly path; reported/single-value sites don't use it).
  • Reproduction: any modeled site's monthly CH4 series shows Oct/Dec sitting below their own per-calendar-month trend in leap years (2016/2020/2024), with February correspondingly elevated. On a per-calendar-month linear fit, leap-year Oct/Dec are >3σ outliers.
  • Expected: the monthly profile should be leap-invariant apart from the genuine calendar effects (Feb's real 29th day; the ~0.27% shorter day-share of a 31-day month in a 366-day year).
  • Actual: an extra ~1–2% notch in Oct/Dec in leap years, unrelated to any input or physical effect.

Root cause

The kernel masks deposits younger than the ~half-year emission lag with a strict integral_adjusted > 0 (L270). The cutoff is integral_adjusted == 0 (deposit exactly 0.5 yr old). In a leap year the monthly grid lands exactly on it for the deposit six months before a 31-day emission month (Jun→Dec, Apr→Oct: 183/366 == 0.5); in a common year it lands just past (183/365 == 0.50137). Since integral_adjusted is a difference of two O(k) cumulative sums, at the cutoff it evaluates to 0 ± ~1e-15 (catastrophic cancellation) — so a bare > 0 (and, as tested, a bare >= 0) includes/drops that whole deposit-month unpredictably, notching Oct/Dec in leap years. September (184/366 = 0.5027) never grid-aligns → no notch (confirming null).

Fix

Mask with a small tolerance: integral_adjusted >= -1e-9. The tolerance is far below any real k-time step (genuine just-past-cutoff values are ~1e-4…1e-3) but above FP noise, so the exact-half-year deposit is retained consistently in leap and common years.

Impact — model-output-change

Within-year redistribution; common years are bit-for-bit unchanged. In leap years:

  • The monthly profile is smoothed — the Oct/Dec notch closes to the ~0.2% genuine 365-vs-366-day floor (which is physically correct and remains).
  • Leap-year ANNUAL totals rise by the physically-real extra-day mass: ~+0.25% (normal-k) to +0.36% (BGD-like hot/wet growing site). This corrects a pre-existing ~0.25% under-count in leap years (the notch was cancelling the extra-day mass). Submitted annual values for 2016/2020/2024 will move up by that amount; 2015/2017/2018/2019/2021/… are unchanged.

Only modeled sites are affected; reported/single-value city paths use a flat annual/12 split and are untouched.

Validation

Ran the real kernel (DB-free City.site_only_estimate_traceestimate_emissions_monthly), UNFIXED vs fixed, on constant-waste normal-k (k≈0.17) and fast-k (k≈0.69) sites and a BGD-like site built from asset 13169's actual parquet inputs (25.8 °C / 1955 mm, dumpsite, open 1990, growing waste reproduced to the ton):

site Oct/Dec-2020 notch UNFIXED fixed leap-2020 annual Δ common-year Δ
normal k≈0.17 −1.66% −0.21% +0.248% 0 (bit-identical)
fast k≈0.69 −0.20% (no mask-notch) −0.20% +0.000% 0
BGD-like k≈0.24 −2.36% −0.26% +0.364% 0

Whole-series diff (every month 2000–2050): the only cells that change are leap-year October and December, all additive; zero non-leap and zero other-calendar-month movement.

Acceptance Criteria

  • Leap-year Oct/Dec notch closes across k regimes (to the genuine days-in-year floor).
  • Common years bit-for-bit unchanged; only leap-year Oct/Dec cells move (all additive).
  • Leap-year annual change equals the physically-real extra-day mass (~+0.25%), documented as model-output-change.
  • No new artifacts (the tolerance is a systematic correction, not the FP one-off of a bare >= 0).

Definition of Done

  • Acceptance criteria met
  • Independently validated against the real kernel (numbers above)
  • Root cause documented in-code
  • Reviewed & merged (paired with the RMI diagnostic PR)

Cross-repo pairing: branch country-exceedance-source-trace, paired with RMI_Climate_TRACE_Waste_Methane PR #78 (the country_exceedance_trace.py diagnostic that surfaced and traced this). Please review/merge together.

🤖 Generated with Claude Code

estimate_emissions_monthly masked deposits younger than the ~half-year
emission lag with a strict `integral_adjusted > 0`. The cutoff is
integral_adjusted == 0 (deposit exactly 0.5 yr old). In a LEAP year the
monthly grid lands exactly on it for the deposit six months before a 31-day
emission month (Jun->Dec, Apr->Oct: 183/366 == 0.5); in a common year it
lands just past it (183/365 == 0.50137). Because integral_adjusted is a
difference of two O(k) cumulative sums, at the cutoff it is 0 +/- ~1e-15
(catastrophic cancellation), so a bare `> 0` dropped that whole deposit-month
unpredictably -> a spurious ~1-2% notch in the 31-day peak months (Oct, Dec)
in leap years only. This surfaced as Climate TRACE QA "de-trended z-score > 3"
flags on modeled countries at Oct-2020/Dec-2020 (e.g. BGD, CMR).

Fix: mask with a tolerance (`>= -1e-9`) — well below any real k-time step
(genuine just-past-cutoff values are ~1e-4..1e-3) but above FP noise — so the
exact-half-year deposit is retained consistently in leap and common years.

Impact (model-output-change): within-year redistribution only; the leap-year
monthly profile is smoothed (Oct/Dec notch closes to the ~0.2% genuine
365-vs-366-day floor), and leap-year ANNUAL totals rise by the physically-real
extra-day mass (~+0.25%; +0.36% on a BGD-like hot/wet growing site). Common
years are bit-for-bit unchanged; only leap-year Oct/Dec cells move, all
additive, no other-month leakage (validated across k regimes + BGD's real
inputs). Reported/single-value sites are unaffected (this path is modeled-only).

Paired with RMI_Climate_TRACE_Waste_Methane PR #78 (branch
country-exceedance-source-trace), whose diagnostic surfaced and traced this.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@HughRunyan HughRunyan added the model-output-change Changes model OUTPUT values (expected progress, not breaking); results differ from prior runs label Aug 4, 2026
@HughRunyan
HughRunyan requested a review from Copilot August 4, 2026 21:05

Copilot AI 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.

Pull request overview

Fixes a leap-year floating-point edge case in SWEET’s monthly first-order-decay kernel by making the half-year emission-lag mask tolerant to numerical noise at the exact cutoff, preventing spurious Oct/Dec dips in leap years for modeled sites.

Changes:

  • Replace a strict integral_adjusted > 0 mask with a tolerance-based cutoff (>= -1e-9) to avoid grid-aligned half-year artifacts in leap years.
  • Add detailed in-code rationale documenting the leap-year cutoff alignment and the FP cancellation behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@HughRunyan
HughRunyan merged commit 11d3879 into main Aug 4, 2026
1 check passed
@HughRunyan
HughRunyan deleted the country-exceedance-source-trace branch August 4, 2026 22:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

model-output-change Changes model OUTPUT values (expected progress, not breaking); results differ from prior runs

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants