Skip to content

fix: add biomass, biofuel and waste factors to carbon_intensity_per_source - #1446

Open
avalyset wants to merge 2 commits into
mlco2:masterfrom
avalyset:fix/1415-biomass-waste-factors
Open

avalyset wants to merge 2 commits into
mlco2:masterfrom
avalyset:fix/1415-biomass-waste-factors

Conversation

@avalyset

Copy link
Copy Markdown
Contributor

Description

Adds factors for biomass and waste to carbon_intensity_per_source.json, in the shape @davidberenstein1957 proposed in #1415: one value per key, with a _source entry next to each.

  • biomass: 230 gCO2eq/kWh, the median lifecycle value for "Biomass — dedicated" in IPCC AR5 WG3 Annex III, Table A.III.2 (min 130, max 420).
  • biofuel: the same 230. global_energy_mix.json names bioenergy generation biofuel_TWh, and the lookup strips _TWh, so a biomass key alone would never be reached from that file. This extra key is the smallest change that makes the factor reachable; the lookup itself is untouched.
  • waste: 230, one value for mixed renewable and non-renewable waste, with the assumption stated in waste_source.
  • docs/explanation/methodology.md: the factor table gets the three new rows.

Related Issue

Closes #1415

Motivation and Context

_global_energy_mix_to_emissions_rate looks up each *_TWh field in the factor table. A type with no factor is left out of the computed sum, the sanity check then fails, and the world average (475) is returned for the whole mix.

What this does not change. No value CodeCarbon ships today moves. 212 of the 213 entries in global_energy_mix.json carry carbon_intensity directly and never reach this path. The one that does (CAF) has no bioenergy and gives 26 before and after. nordic_emissions.json is not touched; its values were computed with the previous table.

What this does not fix on its own. A real country mix with coal generation still falls back to the world average on this path. coal_TWh and the fossil_TWh aggregate that already contains it both match a table key, so coal is counted twice and the sanity check fails (DNK: 36.30 TWh computed against 33.63 TWh total, the difference being its 2.67 TWh of coal). That is a separate question and is left out of this PR.

The waste value needs a decision. I have used 230, following Electricity Maps, which groups ENTSO-E B17 Waste with biomass (ProductionModes.BIOMASS: ["B01", "B17"]). It is a published figure on a lifecycle basis and keeps results in line with theirs. It is likely low for waste: the non-renewable share of municipal waste used for power is around half in Denmark, Norway and Finland (Eurostat 2023: 58.0 %, 48.0 % and 45.1 %), and that share is fossil carbon.

The alternative is Energinet's DeclarationProduction data. I need to correct what I wrote in the issue: 329.87 is a single hourly value (DK1, 15 January 2025, 00:00 local time, 125 % allocation), not an annual figure. Energinet's hourly "Final" values for 2025, weighted by its own production figures, give 329.67 for DK1, 369.95 for DK2 and 350.24 for both. That aggregation is mine. These are direct CO2 emissions at the stack, for Denmark, under one of the three CHP allocation methods the dataset offers. If you prefer that basis, it is a one-line change.

How Has This Been Tested?

Two new tests in tests/test_emissions.py cover a mix with biomass_TWh and waste_TWh, and a mix with biofuel_TWh as global_energy_mix.json names it. Both return the world average on master and the table-priced value on this branch. The key-count assertion in test_get_carbon_intensity_per_source_data goes from 21 to 27.

pytest --ignore=tests/test_viz_data.py -m 'not integ_test' tests/: 673 passed, 20 skipped (671 passed, 20 skipped on master).

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)

AI Usage Disclosure

  • 🟥 AI-vibecoded: You cannot explain the logic. Car analogy : the car drive by itself, you are outside it and just tell it where to go.
  • 🟠 AI-generated: Car analogy : the car drive by itself, you are inside and give instructions.
  • ⭐ AI-assisted. Car analogy : you drive the car, AI help you find your way.
  • ♻️ No AI used. Car analogy : you drive the car.

Claude (Anthropic) was used under my direction: Claude Opus 5.5 in Claude Code for the repository work, and Claude in the Claude app for analysis and fact-checking. The choice of values and sources is mine.

I can explain every line and have run the tests locally.

Checklist:

  • My code follows the code style of this project.
  • My change requires a change to the documentation.
  • I have updated the documentation accordingly.
  • I have read the docs/how-to/contributing.md document.
  • I have added tests to cover my changes.
  • All new and existing tests passed.

…_per_source.json

A mix carrying biomass or waste had no factor for them, so the two types
were left out of the computed sum, the sanity check failed and the world
average was returned. Add biomass (230, IPCC AR5 WG3 Annex III median),
biofuel (same factor, the name global_energy_mix.json uses) and waste
(one value, assumptions in waste_source).

Refs mlco2#1415
@avalyset
avalyset requested a review from a team as a code owner October 12, 2026 09:08

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Missing factor keys for waste and biomass in carbon_intensity_per_source.json

1 participant