Skip to content

data(nordic): per-zone carbon intensity for Swedish bidding zones SE1–SE4 - #1262

Open
avalyset wants to merge 4 commits into
mlco2:masterfrom
avalyset:khepri-se-per-zone-ci
Open

data(nordic): per-zone carbon intensity for Swedish bidding zones SE1–SE4#1262
avalyset wants to merge 4 commits into
mlco2:masterfrom
avalyset:khepri-se-per-zone-ci

Conversation

@avalyset

@avalyset avalyset commented Jun 29, 2026

Copy link
Copy Markdown

Hi! This proposes replacing the uniform 18.0 gCO2eq/kWh placeholder currently shared by all four Swedish bidding zones in codecarbon/data/private_infra/nordic_emissions.json with per-zone values derived on CodeCarbon's own factor table. Data-only change apart from one test constant.

Companion to #1260 (Norwegian zones). The two are independent and can be merged in either order.

This revision follows @benoit-cty's review of 2026-07-19. Both requested changes are made, and one claim in the previous version of this PR was wrong and has been removed — details below.

What

Zone before after (gCO2eq/kWh) coverage
SE1 (Northern Sweden) 18.0 25.9 99.59 %
SE2 (North-central Sweden) 18.0 25.7 98.92 %
SE3 (South-central Sweden, incl. Stockholm) 18.0 27.0 94.58 %
SE4 (Southern Sweden) 18.0 24.7 85.05 %

NO1–NO5 and FI are unchanged (byte-for-byte; only SE blocks + file metadata touched).

Factor basis: your table, not AR5

The previous version of this PR derived these on IPCC AR5 Annex III lifecycle medians. You asked us to either re-derive on carbon_intensity_per_source.json or first establish a project-wide basis change. We re-derived on your table. The shipped values are now internally consistent with the rest of CodeCarbon.

Correction to the previous PR text. It said the main table difference was gas, and that gas is a negligible share of the Swedish mix so the choice barely mattered. That was wrong, and you were right to flag it. The differences that matter for Sweden are nuclear and wind:

Source carbon_intensity_per_source.json IPCC AR5 Annex III
nuclear 29 12
wind 26 11
hydroelectricity 26 24
natural gas 743 490

SE3 is 63.1 % nuclear and SE1/SE2/SE4 are hydro-and-wind, so the basis change moves SE3 by 1.86× and the others by 1.26–1.42×.

What this costs the original argument — stated plainly

On your factor table the case for per-zone SE values is weaker than the previous version of this PR claimed, and we would rather say so than let the numbers imply otherwise:

  • The old "wrong in both directions" argument is dead. On AR5 the placeholder overvalued SE3 and undervalued SE1/SE2. On your table 18.0 is too low for all four zones, by 37–50 %.
  • Per-zone spread across SE1–SE4 is only 2.3 gCO2eq/kWh (24.7–27.0), because hydro and wind both carry 26 in your table. The per-zone signal that motivated this PR largely disappears on this basis.

What survives is that 18.0 is uniformly and substantially too low for Sweden on CodeCarbon's own factors. If you consider that insufficient reason to take per-zone values for SE, closing this in favour of a single corrected Swedish value — or of @SaboniAmine's Electricity Maps work — is a reasonable call, and we would not argue against it. #1260 (Norway) is a stronger case and stands on its own.

Coverage — the values are complete factors now

You noted that CodeCarbon applies the stored number to all consumed energy, so a partial-coverage estimate is presented as more complete than it is. Fixed:

Production types with no entry in carbon_intensity_per_source.json — ENTSO-E Other (B20), Other renewable, Waste, Biomass — are now kept in the denominator with factor 0, contributing nothing to the numerator. The stored value is therefore a complete gCO2eq/kWh factor over all zone generation, which is how CodeCarbon uses it. No factor was invented for those types.

Each zone block carries an explicit coverage field naming the uncovered share. SE4 is the weak one: 14.95 % of its 2025 generation is Other. Carried at 0, SE4's value is a lower bound — any positive factor for Other raises it. That is a real limitation of the data, disclosed in the file rather than in this thread.

The term "true CI" is gone from the PR text and the shipped metadata; these are production-based values.

Source & method

  • Source: ENTSO-E Transparency Actual Generation per Production Type (A75), per bidding zone, full year 2025.
  • Method: production-based generation-mix — energy-weighted (duration-correct) average of each production type's share × its factor from carbon_intensity_per_source.json. The derivation is described in arXiv:2608.29717.
  • This updates the static Nordic fallback only; it does not touch electricitymaps_api.py or any other data path.

Provenance

The upstream derivation is archived at 10.5281/zenodo.21097430 (Khepri v1.2, versioned record), cited in the file metadata as you asked.

That record publishes these zones on the AR5 basis (SE3 14.5, SE4 17.4), which also excludes factor-less types from both numerator and denominator instead of carrying them at 0. Those are not the numbers this PR ships. The metadata says so explicitly, so a future reader who follows the DOI does not find a different value with no explanation.

Tests

test_get_emissions_PRIVATE_INFRA_NORDIC_REGION asserts SE2 directly; updated 0.02010.0257. The remaining Nordic tests read the JSON dynamically and pass unchanged.

Thanks for the careful review — it caught a claim that did not hold.

Replace the uniform 18.0 gCO2eq/kWh placeholder for SE1-SE4 with per-zone
production-based values (SE1 20.6, SE2 20.1, SE3 14.5, SE4 17.4) derived
from ENTSO-E generation-per-type x IPCC AR5 lifecycle factors (2025 annual
means). Adds per-zone method/source/drift notes + updated file metadata.
NO1-NO5 and FI unchanged.

Update test_get_emissions_PRIVATE_INFRA_NORDIC_REGION to reflect SE2's
new derived value (20.1 gCO2eq/kWh = 0.0201 kg/kWh). Test previously
hardcoded the placeholder 0.018; now uses the derived 2025 figure. All
9 nordic tests pass; 8 package integrity tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@avalyset
avalyset requested a review from a team as a code owner June 29, 2026 21:09
@benoit-cty

Copy link
Copy Markdown
Contributor

Thanks for your contribution.
Can you give us the exact link to review the source of the value ?

@codecov

codecov Bot commented Jun 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.68%. Comparing base (3161c53) to head (20de124).

Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1262      +/-   ##
==========================================
+ Coverage   89.64%   89.68%   +0.04%     
==========================================
  Files          48       48              
  Lines        4771     4771              
==========================================
+ Hits         4277     4279       +2     
+ Misses        494      492       -2     

☔ 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.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@avalyset

Copy link
Copy Markdown
Author

Hi Benoît, thanks for taking a look.

The values are derived rather than taken from a single source, so the "source" is a short chain:

Generation data: ENTSO-E Transparency, Actual Generation per Production Type (A75), per bidding zone, full year 2025. The raw series isn't committed (fetched reproducibly from the ENTSO-E API), but the exact EIC codes per zone are in ADR-0006.

Emission factors: IPCC AR5 Annex III, Table A.III.2 lifecycle medians (hydro 24, wind 11, nuclear 12, solar 48, gas 490 gCO2eq/kWh).

Method: production-based generation-mix weighted average — each source weighted by its annual share, multiplied by its factor.

The four SE values, the drift analysis, and the nine-zone table are in the README, and the whole thing is frozen and citable at https://doi.org/10.5281/zenodo.21042581.

So there's no single CSV that says "20.6" — the value is traceable through method + the two sources above. Glad to add a worked example for one zone (e.g. SE3) showing the mix × factors arithmetic explicitly if that helps.

@avalyset

avalyset commented Jul 1, 2026

Copy link
Copy Markdown
Author

Quick follow-up, @benoit-cty — I've since published a corrected version (same concept DOI, now resolves to v1.2):

https://doi.org/10.5281/zenodo.21042581

Two things this fixes for your review: the SE per-zone drift results are now included in full (docs/se-drift-results-2022-2025.md), and one correction to my earlier note — the factor source is IPCC WG III AR5 Annex III lifecycle medians (I referenced "Table A.III.2" above; the manuscript and repo now cite "Annex III" without that sub-ID). Factor values are unchanged (hydro 24, wind 11, nuclear 12, solar 48, gas 490 gCO2eq/kWh). Method in ADR-0006 (SE CI) and ADR-0007 (SE drift).

@avalyset

Copy link
Copy Markdown
Author

Hi @benoit-cty — following up on your June 30 request for the exact way to review
the source of the per-zone values. You asked for something you could check rather
than take on trust, which is fair, and I owe you the explicit SE3 arithmetic we
offered earlier.

First, the honest reason "check it in docs/" wasn't enough. The headline CI is
energy/duration-weighted over clean intervals, per ADR-0001 §4:

  • CI per interval: Σ(MW_type × factor_type) / Σ(MW_type)
  • Period average (energy-weighted): Σ(MW × factor × duration) / Σ(MW × duration)

i.e. total emissions / total energy over clean intervals.

The mix table in docs/se-drift-results-2022-2025.md shows each type's energy-weighted
share of total generation (denominator = all occurring types), so the listed material
types sum to ~94.5%. It omits the ~5.4% "Other" category (no verified factor, excluded
from CI) and the 0.0168% Fossil Gas trace (classified negligible rather than material by
src/khepri/ci.py, but included in CI at factor 490). Multiplying the shares as printed gives 13.64. The headline CI
applies the same energy weighting but normalises over the factor-carrying types, giving
14.53. Same weighting — the difference is the denominator. That's a documentation gap on
our side, not a change in the value.

SE3 (2025) — energy/duration-weighted decomposition

  • EIC: 10Y1001A1001A46L
  • Interval coverage: 100.00% (10923 / 10923 intervals)
  • Factors: IPCC AR5 Annex III lifecycle medians (per src/khepri/factors.py)
Production type Energy share AR5 factor (gCO2eq/kWh) Contribution
Nuclear 66.7463 % 12 8.0096
Wind Onshore 15.6437 % 11 1.7208
Hydro Water Reservoir 15.5638 % 24 3.7353
Solar 2.0284 % 48 0.9736
Fossil Gas 0.0178 % 490 0.0872
Total 14.5265 → 14.53

"Energy share" = each type's Σ(MW × duration) over clean intervals, normalised over
the factor-carrying (included) types. Shares are given to 4 decimal places so that
Σ(share × factor) reproduces the total; note Fossil Gas is a genuine 0.0178 % of the
factor-carrying types here (0.0168 % of total generation) — rounding it to 0.02 % would
give 14.54, not 14.5265.

Happy to provide the same decomposition for SE1, SE2, and SE4 on request; the method
is identical (SE3 is the only Swedish zone with nuclear).

@benoit-cty benoit-cty left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for providing the worked SE3 decomposition and the archived derivation. The code path, JSON loading, affected tests, and merge against current master all check out, but I think the data basis needs two changes before these factors can be shipped.

  1. The lifecycle-factor basis is materially inconsistent with CodeCarbon, not just for gas. The PR says the main table difference is gas, but carbon_intensity_per_source.json currently uses nuclear=29, wind=26, hydro=26, and natural gas=743 gCO2eq/kWh, while Khepri uses 12, 11, 24, and 490 respectively. Applying CodeCarbon's existing factors to the SE3 shares supplied in the discussion gives about 28.58 gCO2eq/kWh, versus 14.53 with the AR5 factors. Nuclear and wind dominate SE3, so this is a roughly 2x difference. Please either re-derive SE1-SE4 using CodeCarbon's factor table, or first establish/document a project-wide change of factor basis; in either case, the metadata claim that this is the same as CodeCarbon's static fallback methodology needs to be made precise.

  2. The published values do not cover all zone generation. Khepri's primary calculation removes production types without a verified factor from both numerator and denominator. The linked drift results show Other is about 15% of SE4's 2025 generation, and the SE3 explanation reports about 5.4% excluded there. CodeCarbon applies the stored number as a complete gCO2eq/kWh factor to all consumed energy, so 17.4/14.5 currently look more precise and complete than the derivation supports. Please provide a justified mapping or bounded/sensitivity result for excluded generation and select/document the value CodeCarbon should use; avoid calling the partial estimate the “true CI.”

Also, please put a resolvable, versioned provenance URL (preferably the archived v1.2 DOI, not only avalyset/khepri) in the shipped JSON metadata so future users can audit the values without finding this PR discussion.

Validation performed locally: 9 Nordic tests passed (plus 3 subtests), all 8 package-integrity tests passed, CI is green, and the PR merges cleanly with current origin/master.

@avalyset

Copy link
Copy Markdown
Author

Thanks @benoit-cty — this is a substantive review and you're right on all three points. Here's what we'll change, and one open choice for you.

1. Factor basis / metadata claim

You're right that the basis is different from carbon_intensity_per_source.json. Khepri derives the per-zone values from IPCC AR5 Annex III lifecycle medians (nuclear 12, wind 11, hydro 24, gas 490 gCO2eq/kWh), deliberately chosen so the whole dataset rests on one consistent peer-reviewed lifecycle basis and matches the DOI-frozen derivation.

The metadata wording that called this "same as CodeCarbon's static fallback methodology" was imprecise — I'll correct it. The basis is AR5, explicitly, not CodeCarbon's per-source table.

For what it's worth on the 2× concern: in the executed Nordic path the per-source table isn't actually reached — the stored per-zone values in nordic_emissions.json short-circuit it (emissions.py:254-256), so AR5-based per-zone values don't introduce a discontinuity against a live code path. That's a footnote, not a rebuttal.

I'd propose we keep AR5 and make the metadata precise rather than re-derive on the CodeCarbon table — re-deriving would contradict the published/DOI-frozen artifact. If you'd prefer internal consistency with the CC per-source table instead, that's your call as maintainer, but that's a project-wide basis change and should be documented as such, not folded silently into this PR.

2. Coverage / "true CI"

You're right — "true CI" is the wrong term for a partial estimate. We'll switch to "production-based CI" with an explicit disclosure of the excluded share.

The only excluded type occurring in the SE 2025 data is Other (ENTSO-E B20) — no verified AR5 factor, so it's removed from both numerator and denominator. Verified excluded shares:

Zone Production-based CI Excluded (Other, B20)
SE1 20.63 0.41 %
SE2 20.11 1.08 %
SE3 14.53 5.42 %
SE4 17.42 14.95 %

On sensitivity, I want to be honest about what we actually have rather than dress it up. compute_sensitivity() currently gives a single upper anchorOther set to 475 gCO2eq/kWh (a deliberately high world-average-scale proxy) — not a calibrated band. Sweeping Other from 0 to 475 brackets the estimate as:

Zone Other@0 headline Other@475
SE1 20.55 20.63 22.50
SE2 19.89 20.11 25.04
SE3 13.74 14.53 39.48
SE4 14.81 17.42 85.84

To be clear: 475 is a deliberately high proxy on the scale of a world-average grid — a conservative ceiling, not a claim about what Swedish Other actually is. SE4's upper end (85.84) illustrates the sensitivity of a 15 % unclassified share, not a real CI. A calibrated B20 proxy for Swedish bidding zones doesn't exist in the code today, so I won't present that range as a bounded interval.

So, your call on how you'd like this to land:

  • (a) ship the headline production-based CI with the disclosed excluded share above, plus this range clearly labelled as illustrative-only; or
  • (b) we build a calibrated Other/B20 proxy for the Swedish zones before merge, if you want a real bounded interval.

My recommendation is (a) now and (b) as a follow-up: a calibrated B20 proxy for the Swedish zones doesn't exist in the code yet, and promising a bounded band we don't have would defeat the point. Your call as maintainer, but that's what I'd ship.

3. Provenance

Agreed — I'll add the archived v1.2 version DOI 10.5281/zenodo.21097430 (the frozen record these values come from) to the nordic_emissions.json metadata as resolvable provenance, alongside the concept DOI 10.5281/zenodo.21042581 (all-versions) and the avalyset/khepri reference.

Summary of changes

  • Metadata: correct the basis wording to IPCC AR5 Annex III (drop the "same as CodeCarbon fallback" phrasing).
  • Terminology: "true CI" → "production-based CI", with the per-zone excluded-share disclosure.
  • Provenance: add the v1.2 version DOI 10.5281/zenodo.21097430 as the primary provenance pointer, with the concept DOI 10.5281/zenodo.21042581 as the all-versions anchor.
  • Open choice for you: illustrative range (a) vs. a calibrated Other/B20 band built before merge (b).

Thanks again for the careful read.

@benoit-cty

benoit-cty commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Sorry for the previous message it was send by an AI tool before I could review it.
Thanks a lot for your work. As @SaboniAmine just remind me, he is working on a project to use Electricity Maps data on year average for CodeCarbon so it will also have data for Sweden areas. We will compare these methodologies before merging.

@avalyset

Copy link
Copy Markdown
Author

Thanks @benoit-cty — no problem on the earlier message. The context on the Electricity Maps annual-average work you mentioned is helpful, and it sounds like a useful addition for CodeCarbon, especially for Swedish coverage.

The two look complementary, not competing. Electricity Maps gives a consumption-based, import-adjusted intensity; Khepri gives a production-based, per-bidding-zone CI — different signals for different uses, not two candidates for the same number.

They also sit on different code paths. Khepri replaces the static production fallback in nordic_emissions.json (used when no Electricity Maps token is configured), while an EM annual-average source would live on or near the API path (electricitymaps_api.py). Both of our PRs state explicitly that they don't touch that path, so the two coexist: fallback vs. token path.

On the boundary: for consumption-based or flow-traced signals, Electricity Maps is the right tool. Production-based per-zone is what Khepri claims, no more.

Our earlier reply still stands, and the a/b choice is yours — happy to proceed either way once you've compared.

Addresses the three points from @benoit-cty's review.

Factor basis: the metadata said the method was "the same approach as
CodeCarbon's static fallback methodology". It is not the same factor
basis - carbon_intensity_per_source.json carries 743 for natural gas
where IPCC AR5 Annex III carries 490. The metadata now names AR5 as
the basis and records the difference without ranking either one.

Overclaim: "true CI" in the notes is replaced with "production-based
CI". The method weights generation physically produced in a zone; it
does not flow-trace imports, so it is not the consumption-based
intensity that phrasing implies.

Provenance: adds the versioned DOI 10.5281/zenodo.21097430 (Khepri
v1.2), which is the frozen record the values come from, rather than
the concept DOI.

Emission factors are unchanged.
@avalyset

Copy link
Copy Markdown
Author

The three changes from my July 19 reply are in a61ff41.

Factor basis. The metadata now states the factors are IPCC AR5 Annex III lifecycle medians, and says plainly that this is a different basis from carbon_intensity_per_source.json — natural gas is 743 there and 490 in AR5. The earlier wording implied the two shared a basis, which was wrong. It describes the difference without arguing either way; the offer to re-derive against your table stands if you'd prefer that.

Terminology. Every "true CI" in the notes is now "production-based CI". You were right that the method doesn't compute what the old wording claimed. The same correction has been made in the accompanying manuscript.

Provenance. Added the versioned DOI the values come from: https://doi.org/10.5281/zenodo.21097430 (Khepri v1.2). Versioned rather than concept, so it resolves to the exact frozen record these numbers were derived from.

No emission factors changed, and tests/test_emissions.py is untouched — it reads emission_factor directly, so none of the metadata edits touch an assertion. 25 passed locally.

Still open from your side: the illustrative-range vs. calibrated-B20-band choice. I haven't assumed either, so say the word and I'll build it.

@inimaz — thanks for merging master in.

Per review on mlco2#1262: re-derived on CodeCarbon's own factor table instead of
IPCC AR5, so the shipped values are internally consistent with the rest of
the package.

SE1 20.6 -> 25.9, SE2 20.1 -> 25.7, SE3 14.5 -> 27.0, SE4 17.4 -> 24.7.

The review was right that the previous text's 'the main table difference is
gas' was wrong: for Sweden the differences that matter are nuclear (29 vs 12)
and wind (26 vs 11). SE3 moves 1.86x.

Production types with no entry in carbon_intensity_per_source.json (Other B20,
Other renewable, Waste, Biomass) are now kept in the denominator at factor 0
rather than dropped from both numerator and denominator, so the stored value is
a complete gCO2eq/kWh factor over all zone generation, as CodeCarbon applies it.
No factor was invented for them. Each zone block carries an explicit coverage
field; SE4 is the weak case at 85.05%.

Metadata now names both factor bases with all four differing sources, and keeps
the versioned DOI 10.5281/zenodo.21097430 while stating that the archived record
publishes AR5 values, which are not what this file ships.

test_get_emissions_PRIVATE_INFRA_NORDIC_REGION asserts SE2 directly: 0.0201 ->
0.0257. 9 Nordic tests (+3 subtests), 25 test_emissions, 8 package-integrity all
pass.
avalyset added a commit to avalyset/codecarbon that referenced this pull request Aug 30, 2026
Applies the review on mlco2#1262 to this PR as well, since both points applied here.

NO1 23.3 -> 26.0, NO2 23.9 -> 27.0, NO3 21.5 -> 25.8, NO4 39.6 -> 51.7,
NO5 24.5 -> 26.6.

Re-derived on CodeCarbon's own factor table instead of IPCC AR5. The previous
version presented AR5 as 'aligned with CodeCarbon's own static fallback', which
conflated 'weighted average over the mix' with 'the same factors' — the bases
differ at nuclear 29 vs 12, wind 26 vs 11, hydro 26 vs 24, gas 743 vs 490. That
claim is dropped, along with a methodology.md quote that had 'that are' removed
from inside the quotation marks.

Production types with no entry in carbon_intensity_per_source.json (Other
renewable, Waste, Biomass) are kept in the denominator at factor 0 rather than
dropped from both numerator and denominator, so the stored value is a complete
gCO2eq/kWh factor over all zone generation. No factor was invented. Coverage is
98.65-99.89% for all five zones and is disclosed per zone in the file.

NO4 remains the case for per-zone values: 51.7 against 25.8-27.0 for the other
four, driven by a 3.68% fossil-gas share at 743.

Nordic tests read the JSON dynamically and pass unchanged: 9 Nordic (+3
subtests), 25 test_emissions, 8 package-integrity.
@avalyset

Copy link
Copy Markdown
Author

Thanks @benoit-cty — both points are now implemented, and one of them showed that a claim in the PR text was simply wrong. Summary of what changed, on both PRs.

1. Factor basis — re-derived on your table

You offered two routes: re-derive on carbon_intensity_per_source.json, or first establish a project-wide basis change. We took the first. Re-deriving is the smaller ask, and a project-wide basis change is not ours to propose in a data PR.

The claim you flagged was wrong. The PR said the main table difference was gas and that gas is a negligible share of the Swedish mix, so the choice barely mattered. Both halves fail: for Sweden the differences that matter are nuclear (29 vs 12) and wind (26 vs 11), which dominate SE3 and SE1/SE2/SE4 respectively. Your ~2× figure for SE3 was right — we reproduce 1.86× on the full-year 2025 series (14.53 → 27.03). That sentence is deleted, not softened.

New shipped values, on your factors:

Zone AR5 (old) carbon_intensity_per_source.json (new)
SE1 20.6 25.9
SE2 20.1 25.7
SE3 14.5 27.0
SE4 17.4 24.7
NO1 23.3 26.0
NO2 23.9 27.0
NO3 21.5 25.8
NO4 39.6 51.7
NO5 24.5 26.6

2. Coverage — and what it costs the Swedish argument

Uncovered production types (Other B20, Other renewable, Waste, Biomass — none have an entry in your table) are now kept in the denominator with factor 0. The stored number is therefore a complete gCO2eq/kWh factor over all zone generation, matching how CodeCarbon applies it. No proxy factor was invented for them, and each zone block now carries an explicit coverage field.

Coverage: NO1–NO5 98.65–99.89 %, SE1 99.59 %, SE2 98.92 %, SE3 94.58 %, SE4 85.05 %. SE4 is the weak one — carried at 0, its value is a lower bound, and any positive factor for Other raises it. That is stated in the file, not just here.

This also settles the choice I asked you to make on 19 July, between (a) shipping the headline value with an illustrative sensitivity range and (b) building a calibrated Other/B20 proxy first. Neither is needed now: there is one number per zone, with its uncovered share disclosed in the file. No range, and no proxy factor invented for Other.

I should be straight about what this does to #1262. On your factor table the Swedish per-zone case is much weaker than that PR originally claimed:

  • The "wrong in both directions" argument is gone. On AR5 the placeholder overvalued SE3 and undervalued SE1/SE2; on your table 18.0 is too low for all four Swedish zones.
  • SE1–SE4 now span just 2.3 gCO2eq/kWh (24.7–27.0), because hydro and wind both carry 26 in your table. The per-zone differentiation that motivated data(nordic): per-zone carbon intensity for Swedish bidding zones SE1–SE4 #1262 largely vanishes on this basis.

So the remaining case for #1262 is narrow: 18.0 is uniformly 37–50 % too low for Sweden on CodeCarbon's own factors. If you would rather fix that with a single corrected Swedish value, or wait for @SaboniAmine's Electricity Maps work, closing #1262 is a defensible call and I will not argue it. #1260 (Norway) is the stronger PR — NO4 lands at 51.7 against 25.8–27.0 for the other four zones, a spread no single constant can cover.

3. Provenance

The versioned DOI is in the metadata of both files: 10.5281/zenodo.21097430 (Khepri v1.2).

One thing I made explicit rather than quiet: that archived record publishes these zones on the AR5 basis, so it carries different numbers (SE3 14.5, NO4 39.6) from what these PRs now ship. The metadata names both bases and the difference, so a future auditor following the DOI does not land on a mismatch with no explanation.

Also fixed

  • "true CI" is gone from both the shipped metadata and the PR text — these are production-based values with disclosed coverage.
  • data(nordic): per-zone carbon intensity for Norwegian bidding zones NO1–NO5 #1260 claimed its method was "aligned with CodeCarbon's own fallback" and quoted methodology.md. The alignment claim is removed (it conflated "weighted average over the mix" with "the same factors"), and so is the quote — I had dropped "that are" from inside the quotation marks, which I should not have done.

On the Electricity Maps comparison

Still yours to make, and nothing here pre-empts it. The two remain different signals — EM annual-average is consumption-based and import-adjusted, this is production-based per bidding zone — and they sit on different code paths, but I am not arguing precedence. If the comparison says Electricity Maps covers this adequately, that is a fine outcome.

Happy to adjust anything — the rounding, the coverage wording, or the amount of metadata carried per zone if you would rather the file stay minimal.

@avalyset

avalyset commented Sep 1, 2026

Copy link
Copy Markdown
Author

The method behind these values is now published as arXiv:2608.29717. I have updated the PR description to cite it alongside the existing Zenodo record.

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.

3 participants