Skip to content

The City DST's "add gas capture" applies the oxidation it sets - #78

Open
HughRunyan wants to merge 2 commits into
mainfrom
add-gas-capture-keeps-its-oxidation
Open

HughRunyan wants to merge 2 commits into
mainfrom
add-gas-capture-keeps-its-oxidation

Conversation

@HughRunyan

@HughRunyan HughRunyan commented Oct 9, 2026 •

Copy link
Copy Markdown
Collaborator

Bug

The City DST's "Add gas capture" lever (add_gas in City.implement_dst_changes_simple_v1_5) applied the gas capture it adds but not the oxidation it sets.

Environment: SWEET_python main at d5c592e, the generic branch of implement_dst_changes_simple_v1_5 (if not scenario_parameters.sites_method:). WasteMAP /v1/city_emissions/implement_dst_simple_v1_5, with the June 2026 cities table (cities_for_map_0626.csv) and load_city replicated offline.

Reproduction: load Lima (all disposal to a landfill without gas capture, no diversion). Run the scenario with implement_year=2026, once with add_gas=0 and once with add_gas=1.

Expected: after 2026 the landfill captures 60% of its methane and oxidises 22% of the rest instead of 10%. Emissions are the no-change scenario's × 0.4 × 0.78 / 0.9 = 0.347, i.e. 65.3% lower.

Actual: × 0.4, i.e. 60.0% lower: 30,393 t CH₄ in 2040 instead of 26,341. After the run, the landfill's oxidation_factor is the scalar 0.1 and the dumpsite's is 0, in every year.

Root cause

After the implementation year, add_gas gives the landfill without capture 60% gas capture and an oxidation factor of 0.22. It converts the dumpsite to a controlled dumpsite with 30% capture and 0.1. Then it sets skip_ox = True so that Landfill.estimate_emissions keeps those factors. The emissions loop at the end of the method overwrote that:

skip_ox = scenario_parameters.sites_method   # False on the generic branch
for landfill in scenario_parameters.landfills:
    landfill.estimate_emissions(skip_ox=skip_ox)

With skip_ox=False, estimate_emissions resets every non-advanced landfill's oxidation factor from its gas_capture flag (ox_cap / ox_nocap). add_gas never sets that flag, so the converted sites got the no-capture defaults: 0.1 for the landfill and 0 for the dumpsite. Both lines date from the repository's first commit (2c5db07).

Are 0.22 and 0.1 the right values?

Yes. I found no reason they shouldn't apply.

  • They are the model's own values for these sites with gas collection. estimate_emissions' ox_cap table gives a landfill 0.22 and a controlled_dumpsite 0.1, and so does defaults_2019.oxidation_factor["with_lfg"]. The real-sites branch of this method also gives a sanitary landfill 0.22 when it adds capture.
  • Up to the implementation year they equal the baseline's. add_gas keeps the no-capture defaults (0.1 and 0) through the implementation year. So nothing moves before it, and the converted sites are a capped landfill and a controlled dump only from the year their capture starts.
  • No double counting. The engine computes (generated − captured) × (1 − oxidation), so oxidation acts only on the gas the capture misses. Since the August fix for RMI/WasteMAP#719 it is applied by emission year, as capture is, so it covers waste deposited before the implementation year just as the capture does.

Fix

Each branch sets skip_ox itself, and the emissions loop uses it:

  • the generic branch: False, or True when add_gas has set its oxidation factors (as it already did)
  • the real-sites branch: True (what it got before, from sites_method)

The overwrite is gone. Real sites are advanced and keep the oxidation they carry whatever skip_ox says. skip_ox matters on that branch only for the generic landfills that WasteMAP's Brazilian rows bring there; see RMI/WasteMAP#851.

Model output (model-output-change)

Measured over WasteMAP's June 2026 cities table (5,689 city names) and a Custom Location in each of 249 countries (2 million people, 500 mm, 25 °C). Each scenario uses the city's own diversions with gas capture added in 2026, compared before and after this change.

2040 emissions against the no-change scenario:

Split (landfill without capture / dumpsite) Before After
Lima, Riyadh, Jakarta 100% / 0% −60.0% −65.3%
Abidjan 76% / 24% −54.7% −60.3%
Cairo 18% / 82% −37.3% −43.9%
Lagos, Karachi, Dhaka, Delhi (API only) 0% / 100% −30.0% −37.0%
  • The 168 cities the UI offers the lever to (no gas capture, some landfill) all move. Their add_gas result falls another 10.0–13.3% in 2040 (median 13.3%). That is 13.3% for a landfill (0.78 / 0.9) and 10.0% for a dumpsite (0.9 / 1).
  • 187 cities that only dump move by 10.0%. The UI hides the lever for them (no_landfills_present), but the API runs it.
  • 218 of 249 Custom Locations move by 10.0–13.3%. The other 31 have the empty disposal split A Custom Location buries or dumps its waste in every country #74 fixes.
  • Brazil's 4,694 flagged rows don't move. WasteMAP's load_city still sends them down the real-sites branch, where add_gas changes nothing for generic sites. With RMI/WasteMAP#851's load_city, 4,622 of the 4,627 that run move by 10.0–13.3%; the other 5 model zero emissions.
  • Unchanged, bit for bit:
    • the no-change scenario, for all 5,871 cities and Custom Locations that run
    • every add_gas result up to and including the implementation year
    • every request without add_gas, whose skip_ox is what it was
  • Same failures: the 67 cities that fail fail the same way before and after (65 over the recycling cap, plus São Paulo and Brasília until City DST scenarios run for São Paulo and Brasília: load_csv_new normalizes a city's composition #73).
  • Cities that don't move are explained: 597 Brazilian rows publish a waste generation rate of 0 t/yr, and 41 cities send everything to capture.

Climate TRACE's pipeline doesn't call this method, so its outputs do not move.

Tests

tests/test_add_gas_capture_oxidation.py: 12 tests. They build a city from country defaults (dst_baseline_blank) with a 0/50/50 split, as in tests/test_move_to_existing_gas_capture.py (#75), and run the real implement_dst_changes_simple_v1_5, with no database.

  • 8 of 12 fail on main, each on the defect:
    • the landfill's and dumpsite's oxidation factors are reset (also with a new landfill with gas capture alongside, which the UI allows)
    • emissions after the implementation year are × 0.4 and × 0.7 instead of × 0.347 and × 0.63
    • a city that landfills everything falls 60.0%, not 65.3%
    • the generic branch passes skip_ox=False with add_gas
  • The 4 that pass on main are guards: the no-change scenario keeps the default oxidation and reproduces the baseline's landfill emissions, and each branch passes the skip_ox it should (the generic branch without add_gas, and the sites branch with and without it).
  • Full suite: 284 passed.

The WasteMAP side, RMI/WasteMAP#856 (same branch name, so its CI installs this branch), adds an endpoint test on Lima's table row through load_city. It fails against main here and passes against this branch. Merge this PR first.

changelog/2026-10.md doesn't exist on main yet. #73, #74, #75 and this PR each create it, so whichever merges later resolves a changelog conflict; the code merges cleanly with all three. #75 documented this bug as a known limitation; I've removed that entry from #75, so no merge order leaves it behind.

Acceptance criteria

  • With add_gas, the landfill without capture oxidises at 0.22 and the converted dumpsite at 0.1 from the year after implementation, and at 0.1 and 0 up to and including it.
  • Emissions after the implementation year are the no-change scenario's × (1 − capture) × (1 − new oxidation) / (1 − old oxidation) for each converted site.
  • The no-change scenario, every request without add_gas, and every year up to the implementation year are bit-identical to before.
  • The real-sites branch still passes skip_ox=True.
  • Changelog entry (changelog/2026-10.md), cross-linked with WasteMAP's backend changelog; the known-limitation entries in Moving waste to a city's existing gas capture changes its emissions #75 and RMI/WasteMAP#854 are removed.

Definition of done

  • Acceptance criteria met
  • Tests and checks pass (SWEET_python: 284 passed; WasteMAP tests/fast against this branch: 577 passed, 1 skipped)
  • Docs updated (both changelogs)
  • Reviewed and merged (before RMI/WasteMAP#856)

🤖 Generated with Claude Code

In the generic branch of implement_dst_changes_simple_v1_5, add_gas gives the
landfill without capture an oxidation factor of 0.22 after the implementation
year and the dumpsite it converts to a controlled dumpsite 0.1, and sets
skip_ox = True to keep them. The emissions loop at the end replaced that with
skip_ox = scenario_parameters.sites_method, which is false on this branch, so
Landfill.estimate_emissions reset both from gas_capture (still False) to 0.1
and 0: the gas capture applied, the oxidation never did. Each branch now sets
skip_ox itself and the loop uses it; the real-sites branch keeps True.

Every add_gas result falls another 10.0-13.3% after the implementation year; a
city that landfills everything goes from -60.0% to -65.3% against no change in
2040. Across WasteMAP's June 2026 table and a Custom Location in every country,
the no-change scenario, the other levers and every year up to the
implementation year are bit-identical.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@HughRunyan HughRunyan added bug Something isn't working model-output-change Changes model OUTPUT values (expected progress, not breaking); results differ from prior runs test Adds or modifies tests labels Oct 9, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
HughRunyan added a commit that referenced this pull request Oct 9, 2026
"Add gas capture" losing the oxidation it sets is fixed in #78 (with
RMI/WasteMAP#856), so this changelog no longer lists it as a known limitation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working model-output-change Changes model OUTPUT values (expected progress, not breaking); results differ from prior runs test Adds or modifies tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant