Repository navigation
Moving waste to a city's existing gas capture changes its emissions - #75
Merged
Merged
Conversation
The City DST's "move waste to existing gas capture" lever (move_gas and existing_gas_pct in implement_dst_changes_simple_v1_5) rewrote split_fractions in the generic branch but never passed the split to the landfills, whose waste comes from their own fraction_of_waste. Only the new-landfill-with-capture block did that, and WasteMAP switches that lever off for every city it offers this one to. So the slider returned the no-change scenario at every setting for all 7 cities in the June 2026 table where it is live. The arithmetic was wrong too: it multiplied the shares without capture by existing / original, scaling them up (Lahore's landfill without capture came out at 171% of its waste) and zeroing them for a city with no capture. _move_to_existing_gas_capture gives capture existing_gas_pct and the landfill and dumpsite without capture the rest, in their original proportions, and the scenario applies that split to the landfills. A request the lever can't carry out raises INVALID_PARAMETERS. The new-landfill sum check now builds CustomError with both arguments; it raised TypeError, a 500 at the API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced 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>
6 tasks done
The October changelog and its README line keep main's entries and add this PR's. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
7 of 8 tasks
HughRunyan
added a commit
that referenced
this pull request
Oct 17, 2026
* Bump the python-minor-and-patch group with 2 updates Bumps the python-minor-and-patch group with 2 updates: [fastapi](https://github.com/fastapi/fastapi) and [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy). Updates `fastapi` from 0.141.1 to 0.142.2 - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](fastapi/fastapi@0.141.1...0.142.2) Updates `sqlalchemy` from 2.1.1 to 2.1.3 - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: fastapi dependency-version: 0.142.2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-and-patch - dependency-name: sqlalchemy dependency-version: 2.1.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> * Indonesia sizes its waste from its own IPCC row msw_per_capita_country is IPCC 2019 Refinement Vol. 5 Ch. 2 Table 2A.1 (Updated), 2010 column. All 167 of its rows match the IPCC's, but Indonesia's (0.19 t/person/yr) was never typed in, so Indonesia fell back to the South-Eastern Asia default, 0.46: 2.4x its own row. Adds the row and cites the source tables in comments. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: Indonesia's per-capita waste row (#76) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * SWEET can name its next release, with tests for the counting scripts/next_release.py turns a list of git tags into the next free sweet/YYYY.MM.N for a month (N from 0, one past the highest used), checks a hand-typed release name, and picks the newest release in a list. Tags that are not release names -- other projects' wastemap/ and submission/ tags, a stray sweet/test -- are ignored. Standard library only, tags on stdin, so the release workflow can run it before installing anything and the tests need no git or network. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A one-click workflow cuts a dated SWEET release from main .github/workflows/release.yml (Actions > Release > Run workflow) tags a commit on main as sweet/YYYY.MM.N -- by default the tip of main and the next free number for the current UTC month -- and publishes a GitHub Release whose notes list the pull requests merged since the previous release, with a link to the month's changelog file. The job summary records the tag, the commit and the links. Before anything is tagged it refuses: a commit main does not contain; a name that already exists, or one that would sort before the newest release; a commit that is already a release; a commit that does not contain the newest release; and a commit whose own test suite fails (the same install and pytest command as tests.yml, run against that commit). The tag is annotated and pushed without force, so a release can never be moved. Only main's copy of the workflow may run, and one release runs at a time so two cannot take the same number. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * setup.py and the package docstring say releases are sweet/ tags Both said SWEET has no releases. They now say a release is a git tag sweet/YYYY.MM.N cut by the Release workflow, that setup.py's "0.1" is still decoration and never bumped, and that __version__ stays the commit: a release names one commit, the commit is all an installed copy can report without a repository, and it is the only name a branch install has. No code changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The README says how to cut, name and pin a SWEET release The README's release section described a shared vYYYY.MM.DD tag that was renamed to wastemap/YYYY.MM.DD in September and is not SWEET's own release anyway. It now explains sweet/YYYY.MM.N: what the name means, that a release is never moved or reused, how to cut one from the Actions tab, what the workflow refuses, and how an app pins one (a release name, or a SWEET branch only on a feature branch). Its Usage section pointed at sweet_tools_obj.py, model.py and load_from_database, none of which exist any more; it is now a short map of the modules and entry points the two apps actually call. changelog/README.md no longer says the project publishes no tags. It says releases are cut from main with generated notes listing the PRs since the last release, and that the monthly files keep the human-written entries. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The recovery command writes the same notes the Release step would have If the tag is pushed but the Release fails, the command in the job summary now includes the changelog link (and the first release's note), quoted for bash. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A copy built from git knows its commit, without the app having to say setup.py writes the commit of a clean checkout into the built package as SWEET_python/_build_sha.py, which is how pip builds a git install, and __version__ reads it first (source "build"), then SWEET_GIT_SHA, then .git. The stamp goes only into the build directory, never the source tree. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The README says how an installed copy knows its commit Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * SWEET's docs say its population table is the only copy Climate TRACE's pipeline and its offline country-data build now read SWEET_python/pops_yearly.csv (RMI_Climate_TRACE_Waste_Methane#208), so the "two copies, nothing keeps them in step" text in constants.py and population.py is out of date. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A site with no recorded type is typed by one rule, the same one Climate TRACE uses SWEET guessed a missing site type in seven places, from one of two region lists (one with Eastern Europe, one with Australia and New Zealand instead). Climate TRACE's pipeline used a fuller rule: income, region and named countries. So a Kazakh site with no type was a sanitary landfill in Climate TRACE and an open dump in WasteMAP's site tool. The rule now lives in SWEET_python/site_type_defaults.py, moved unchanged from Climate TRACE's landfill_table_ops.fill_landfill_types. A site is a sanitary landfill where its country is high income, or its region is one of landfill_default_regions plus Western Asia and Central Asia, or it is China, Russia, Mexico, Brazil or Costa Rica. Otherwise, and always for Mongolia and Yemen, it is an open dump. Income comes from SWEET_python/country_income.py, copied from the World Bank table Climate TRACE reads (What a Waste 2.0's country file, 217 countries). Every guess in city_params now calls it: the city split's last fallback (three branches), site_only_estimate, both trace paths, and the site tool's prefill by location and for a listed site with no type. dst_baseline_blank's fallback is left to #74, which rewrites it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: a site with no recorded type is guessed the same way everywhere Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Gas capture and oxidation follow a site's reports year by year when its rows carry their year The Climate TRACE pipeline's consolidated input gives a site one row per year and marks each with `row_year`. On those rows a reported gas collection efficiency or oxidation belongs to its year: one value is used in every model year, and several are carried back and forward -- the first back to the start, each forward to the next report, the latest to the end. Two consequences. A modelled site, which has no reported-emissions year, keeps its measured efficiency instead of falling to the site-type default. And if upstream starts resolving these two per year, the series follows the reports with no change here. Rows without `row_year` -- the legacy input and every other caller -- keep the old rule (site mean, overwritten in reported years). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The carried series reads nullable year and value columns Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: gas capture and oxidation follow a site's yearly reports when its rows carry their year Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Rows that carry their year never fall back to the emissions-year rule If every row_year on a site's rows was blank, both builders fell through to the legacy rule, which places a value by its reported-emissions year. For gas capture that brought back the fault this branch fixes: a modelled site, with no such year, lost its measured efficiency to the site-type default. Now an undated value on these rows is the site's value in every year, and a site with no value at all gets the gated default as before. Raised in review of #84. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The places SWEET applies a Climate TRACE modelling decision carry a comment naming it Seven decisions from RMI_Climate_TRACE_Waste_Methane's docs/MODEL_DECISIONS.md are tagged where SWEET applies them: MCF, site type, waste composition, gas capture, oxidation, flaring and population growth. That repo's test checks the comments once a SWEET release containing them is pinned. Comments only; no number moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Bump the python-minor-and-patch group with 5 updates Bumps the python-minor-and-patch group with 5 updates: | Package | From | To | | --- | --- | --- | | [asyncpg](https://github.com/MagicStack/asyncpg) | `0.31.0` | `0.32.0` | | [fastapi](https://github.com/fastapi/fastapi) | `0.142.2` | `0.143.0` | | [numpy](https://github.com/numpy/numpy) | `2.5.3` | `2.5.4` | | [pydantic](https://github.com/pydantic/pydantic) | `2.13.5` | `2.14.0` | | [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) | `2.1.3` | `2.1.4` | Updates `asyncpg` from 0.31.0 to 0.32.0 - [Release notes](https://github.com/MagicStack/asyncpg/releases) - [Commits](MagicStack/asyncpg@v0.31.0...v0.32.0) Updates `fastapi` from 0.142.2 to 0.143.0 - [Release notes](https://github.com/fastapi/fastapi/releases) - [Commits](fastapi/fastapi@0.142.2...0.143.0) Updates `numpy` from 2.5.3 to 2.5.4 - [Release notes](https://github.com/numpy/numpy/releases) - [Changelog](https://github.com/numpy/numpy/blob/main/doc/RELEASE_WALKTHROUGH.rst) - [Commits](numpy/numpy@v2.5.3...v2.5.4) Updates `pydantic` from 2.13.5 to 2.14.0 - [Release notes](https://github.com/pydantic/pydantic/releases) - [Changelog](https://github.com/pydantic/pydantic/blob/main/HISTORY.md) - [Commits](pydantic/pydantic@v2.13.5...v2.14.0) Updates `sqlalchemy` from 2.1.3 to 2.1.4 - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: asyncpg dependency-version: 0.32.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-and-patch - dependency-name: fastapi dependency-version: 0.143.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-and-patch - dependency-name: numpy dependency-version: 2.5.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor-and-patch - dependency-name: pydantic dependency-version: 2.14.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: python-minor-and-patch - dependency-name: sqlalchemy dependency-version: 2.1.4 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: python-minor-and-patch ... Signed-off-by: dependabot[bot] <support@github.com> * The README explains decision comments, and a pull-request template asks about them A "Decision comments" section says what a `# decision:` comment marks, that the list lives in the Climate TRACE pipeline repo, which four tagged decisions WasteMAP shares, and that changing a tagged line needs a paired Climate TRACE pull request updating its entry. A new pull-request template asks whether a change touches a tagged line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Seven more Climate TRACE modelling decisions are marked where SWEET applies them Comments naming entries in RMI_Climate_TRACE_Waste_Methane's docs/MODEL_DECISIONS.md: waste-before-1970-is-zero, intake-ends-before-close-year, intake-follows-population, decay-rate-from-climate, methane-potential-by-waste-type, monthly-from-decay-model and particulate-factors. Comments only; no number moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: seven decisions, not nine Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * City DST scenarios run for São Paulo and Brasília: load_csv_new normalizes a city's composition (#73) * load_csv_new normalizes a city's composition, so the city DST runs for São Paulo and Brasília WasteMAP's cities table publishes Brazil's precomputed rows with their source composition as it is: São Paulo's ten Waste Components sum to 99%, Brasília's to 87%, and Brazil's national default to 100.1%. load_csv_new was the one loader that took a composition as published. implement_dst_changes_simple_v1_5 therefore rejected São Paulo and Brasília outright with "Invalid waste fractions" (São Paulo by float rounding alone: 1 - 0.99 = 0.010000000000000009). Every other Brazilian city's typed masses missed or exceeded its waste, and its no-change scenario drifted from the baseline. Normalize proportionally, as load_andre_params and dst_baseline_blank do. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: load_csv_new normalizes a city's composition Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * A Custom Location buries or dumps its waste in every country (#74) * A Custom Location buries or dumps its waste in every country City.dst_baseline_blank looked a country's disposal split up with .get(iso3, .get(region, 0)). Southern Asia, South-Eastern Asia and Southern Africa have no row in fraction_open_dumped / fraction_landfilled (no label mismatch: the other regional tables carry them as 0.0 # np.nan), and Canada's, Germany's and Switzerland's country rows are all zeros, so 31 countries got a (0, 0, 0) split. Every landfill took 0% of the waste and the city modelled no landfill methane; the except-KeyError fallback never ran because .get never raises. defaults_2019.disposal_split_for gives the split load_andre_params gives a cities-table row with no landfill data: the country's row if it sends waste anywhere, else the region's, else all landfill in landfill_default_regions and all dumpsite elsewhere. load_andre_params is unchanged, so the cities table doesn't move; the other 218 countries' Custom Location output is bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * The changelog links the WasteMAP half Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A Custom Location's fallback split follows Climate TRACE's site-type rule main's #83 moved load_andre_params's no-data fallback to site_type_defaults.landfill_is_default. disposal_split_for still used landfill_default_regions, so the parity test failed for Brunei (high income): the cities table landfills it, a Custom Location dumped it. Calling the shared rule makes the two agree for all 249 countries; only Brunei moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * Moving waste to a city's existing gas capture changes its emissions (#75) * Moving waste to a city's existing gas capture changes its emissions The City DST's "move waste to existing gas capture" lever (move_gas and existing_gas_pct in implement_dst_changes_simple_v1_5) rewrote split_fractions in the generic branch but never passed the split to the landfills, whose waste comes from their own fraction_of_waste. Only the new-landfill-with-capture block did that, and WasteMAP switches that lever off for every city it offers this one to. So the slider returned the no-change scenario at every setting for all 7 cities in the June 2026 table where it is live. The arithmetic was wrong too: it multiplied the shares without capture by existing / original, scaling them up (Lahore's landfill without capture came out at 171% of its waste) and zeroing them for a city with no capture. _move_to_existing_gas_capture gives capture existing_gas_pct and the landfill and dumpsite without capture the rest, in their original proportions, and the scenario applies that split to the landfills. A request the lever can't carry out raises INVALID_PARAMETERS. The new-landfill sum check now builds CustomError with both arguments; it raised TypeError, a 500 at the API. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Link the changelog to the PRs it ships in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Drop the add_gas oxidation known limitation: #78 fixes it "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> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * The City DST's "add gas capture" applies the oxidation it sets (#78) * The City DST's "add gas capture" applies the oxidation it sets 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> * Link the changelog to the PRs it ships in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * A Custom Location in Kosovo builds, and the name "Kosovo" no longer means Serbia (#77) * A Custom Location in Kosovo builds: SWEET's own country table before pycountry pycountry has no Kosovo. _iso3_for asked it first, so XKX, the code SWEET's defaults and WasteMAP's country list use, raised LookupError, and dst_baseline_blank and cityparams_obj_for_blank_site raised "Country 'XKX' not found." (WasteMAP's city tool answered 500). By name, the fuzzy search resolved "Kosovo" to Serbia, so sdst_prepopulate prefilled a point in Kosovo as Serbia. _iso3_for now tries region_lookup_iso3 by code, then country_to_iso3 by name, both ignoring case, before pycountry. The other 249 codes WasteMAP offers resolve as before, so their output is bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog links the paired WasteMAP PR Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> * A blank or junk country is not found, and the site tool's prefill finds every country the geocoder names (#80) * A Custom Location in Kosovo builds: SWEET's own country table before pycountry pycountry has no Kosovo. _iso3_for asked it first, so XKX, the code SWEET's defaults and WasteMAP's country list use, raised LookupError, and dst_baseline_blank and cityparams_obj_for_blank_site raised "Country 'XKX' not found." (WasteMAP's city tool answered 500). By name, the fuzzy search resolved "Kosovo" to Serbia, so sdst_prepopulate prefilled a point in Kosovo as Serbia. _iso3_for now tries region_lookup_iso3 by code, then country_to_iso3 by name, both ignoring case, before pycountry. The other 249 codes WasteMAP offers resolve as before, so their output is bit-identical. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * A blank or junk country is not found; the prefill finds every country the geocoder names _iso3_for ended with pycountry's fuzzy search, which scores every country for an empty name and scores a country for any of its subdivisions that match. So "" and " " were the United Kingdom (the most subdivisions), "None" Italy (Frosinone), "nan" Thailand (Nan province), and None raised AttributeError rather than LookupError. _iso3_for now raises LookupError for anything that isn't a non-blank string, strips the name, and takes the fuzzy answer only when the name is whole words of that country's own name or its initials ("UK", "UAE"). The site tool's prefill (sdst_prepopulate) crashed with AttributeError for a point Nominatim can't place or gives no country, and failed for 11 English names Nominatim uses that neither SWEET nor pycountry spell its way (Turkey, Ivory Coast, Cape Verde, ...). _iso3_at tries the geocoder's name, then its ISO 3166-1 code, and the prefill says "No country at <lat>, <lon>." where there is none. Over 306 reverse-geocoded points (222 country names): 210 resolve as before, the 11 now resolve, Sint Maarten moves from NLD to SXM. Every code and name in SWEET's own table resolves as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Changelog: cite #80 and RMI/WasteMAP#859 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Bug
The City DST's "% of waste diverted to existing landfill with gas capture" slider (
move_gas,existing_gas_pctinCity.implement_dst_changes_simple_v1_5) did nothing. At every setting it returned the no-change scenario exactly.Environment: SWEET_python
mainat d5c592e, the generic branch ofimplement_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) andload_cityreplicated offline.Reproduction: load Lahore (34.4% of disposal to landfills with gas capture). Run the scenario twice with its own diversions and
implement_year=2026: once withmove_gas=0, once withmove_gas=1, existing_gas_pct=0.9.Expected: moving waste from the landfill without capture to the one with it lowers emissions after 2026.
Actual: both give 92,872 t CH₄ in 2040. The scenario's
split_fractionscame out as 0.9 / 1.714 / 0, and the landfills kept 0.344 / 0.656 / 0. Mexico City (2.3%) gave 0.9 / 38.48 / 0, also with no change. The same no-op held for all 7 cities in the June table where the slider is live (0 < share < 99%): Mexico City, Lahore, Santiago, Amman, Tegucigalpa, Cebu City, Tunis.Root cause
LandfillWasteMassDF.createreads each landfill'sfraction_of_waste. Only theif new_gas_pct > 0:block copiedsplit_fractionsthere. WasteMAP's frontend offers this slider only to a city that already captures gas, and disables "new landfill with gas capture" for exactly those cities (useChartData.ts), so the two never ran together.existing_gas_pct, then multiplied the shares without capture byexisting / original, scaling them up. With no capture to start from, it zeroed them. Combined withnew_gas_pct, the inflated split failed its sum check, and that check builtCustomErrorwith one argument, so it raisedTypeError(an API 500).Fix
_move_to_existing_gas_capture(split_fractions, existing_gas_pct)gives captureexisting_gas_pct. The landfill and dumpsite without capture take the rest,1 − existing_gas_pct, in the proportions they had.move_gasblock applies that split tolandfills[0..2].fraction_of_waste. As with every lever, years before the implementation year are still copied from the baseline.INVALID_PARAMETERS(400 at the API) instead of silently returning the no-change scenario:CustomError("INVALID_PARAMETERS", …).Model output (
model-output-change)2040 emissions against the no-change scenario, on the June table:
Before the fix every cell was 0%.
Unchanged, bit for bit: the no-change scenario and every request with
move_gas=0(all other levers), plus all 41 cities at 100% for everything the UI sends.Visible side effect in WasteMAP: an untouched slider sends the city's share rounded to a whole percent. So Lahore's default scenario now sits 0.35% above its baseline in 2040, Tegucigalpa's 0.25% below, and Santiago, Mexico City and Tunis move less than 0.2%. The diversion sliders already round the same way: Mexico City's untouched scenario was 0.72% below its baseline before this fix.
Fixed separately: "Add gas capture" loses its oxidation settings
In the same generic branch,
add_gassets oxidation to 0.22 for the landfill without capture and 0.1 for the converted dumpsite after the implementation year, and an override at the end of the method reset both to the no-capture defaults. That is fixed in #78 (with RMI/WasteMAP#856), so the Known limitations entry this PR added tochangelog/2026-10.mdhas been dropped (f69fdac).Tests
tests/test_move_to_existing_gas_capture.py: 16 tests. They build a city from country defaults (dst_baseline_blank) with a 30/50/20 split and run the realimplement_dst_changes_simple_v1_5, with no database.main, each on the defect: no emissions change, the landfills keep the baseline split, Lahore's split comes out 0.9 / 1.714 / 0,TypeErrorwithnew_gas_pct, and no error for impossible requests.mainis the guard that a city's own share reproduces the no-change scenario.The WasteMAP side, RMI/WasteMAP#854 (same branch name, so its CI installs this branch), adds an endpoint test on Lahore's real table row through
load_city. Two of its four tests fail againstmainhere. Merge this PR first. WasteMAP CI falls back to SWEET_pythonmainonce this branch is gone, so RMI/WasteMAP#854 is green onmainonly after this lands.Acceptance criteria
move_gas=1with a share different from the city's own changes emissions from the year after implementation, and never before it.existing_gas_pct. The landfill and dumpsite without capture share the rest in their original proportions, and the split sums to 1.move_gas=0, and the 41 cities at 100% give the same results as before.INVALID_PARAMETERSrather than a silent no-op or a 500.changelog/2026-10.md), cross-linked with WasteMAP's backend changelog.Definition of done
tests/fastagainst this branch: 580 passed, 1 skipped)🤖 Generated with Claude Code