Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add validation test for emissions factor references provided in zone configs #6001

Merged
merged 34 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
1464290
zone config: retain 'emissionFactors' key after configuration is loaded
jayaddison Oct 11, 2023
2a483ce
tests: add test case to validate the reference sources listed in zone…
jayaddison Oct 11, 2023
4075bdc
Run 'poetry run format'
jayaddison Oct 11, 2023
eb39522
Merge branch 'master' into tests/emissions-factor-reference-validation
jayaddison Oct 12, 2023
e4e701c
Zone model: add emissionFactors field to allow pydantic validation to…
jayaddison Oct 12, 2023
3ca1990
config.model: relocate 'Zone' class definition so that it can referen…
jayaddison Oct 12, 2023
88ea0a6
config.model: add ZoneEmissionFactors model to represent emissions fa…
jayaddison Oct 12, 2023
2de8e37
config.model: cleanup: remove outdated comment
jayaddison Oct 12, 2023
efd56dc
config.model: cleanup: use snake-casing
jayaddison Oct 12, 2023
009b652
tests: migrate to a pydantic-model-based reference-testing approach
jayaddison Oct 12, 2023
bf3d534
Revert "config.model: cleanup: use snake-casing"
jayaddison Oct 12, 2023
dc894e1
Revert "config.model: cleanup: remove outdated comment"
jayaddison Oct 12, 2023
a56fb1b
Revert "config.model: add ZoneEmissionFactors model to represent emis…
jayaddison Oct 12, 2023
9c45e87
Revert "config.model: relocate 'Zone' class definition so that it can…
jayaddison Oct 12, 2023
ec67a64
Revert "Zone model: add emissionFactors field to allow pydantic valid…
jayaddison Oct 12, 2023
24579d3
Revert "zone config: retain 'emissionFactors' key after configuration…
jayaddison Oct 12, 2023
46a5e02
tests: refactor zone reference-testing to use existing CO2EQ_CONFIG_M…
jayaddison Oct 12, 2023
9ea16a0
tests: apply isort-preferred module formatting
jayaddison Oct 12, 2023
d375bff
tests: every zone production mode estimate must provide a source refe…
jayaddison Oct 12, 2023
65e3856
config: refactor-out HOPS source reference into an origin document an…
jayaddison Oct 12, 2023
1280f08
config: refactor-out references to IEA MX energy generation ix report…
jayaddison Oct 12, 2023
a40152b
config: refactor-out references to IPCC 2014 Ocean (includes tidal) e…
jayaddison Oct 12, 2023
9558c35
config: refactor-out references to data partially derived from the Gu…
jayaddison Oct 12, 2023
cd94f6b
config: refactor-out references to Enerdata as a data source from zon…
jayaddison Oct 13, 2023
bb9317e
tests: factor-out special-case Electricity Maps reference by rephrasi…
jayaddison Oct 13, 2023
86df047
config: rephrase CA-AB source assumption note into a more grammatical…
jayaddison Oct 13, 2023
cf04019
config: fixup: data for IN-KA shouldn't be described as an assumption…
jayaddison Oct 13, 2023
1de0de2
config: factor-out sources for assumed 50-50 solar:wind renewables mi…
jayaddison Oct 13, 2023
42e27fe
tests: cleanup: 'emission_factors' and the 'zone_overrides' subfield …
jayaddison Oct 13, 2023
96974de
tests: add assertion that top-level sources configuration is present …
jayaddison Oct 16, 2023
423c304
tests: lint fixup: comment brevity to fix within line length limits
jayaddison Oct 16, 2023
b0acba5
tests: nitpick: standardize error message format, and include full pa…
jayaddison Oct 18, 2023
ff5cc0c
Merge branch 'master' into tests/emissions-factor-reference-validation
jayaddison Oct 23, 2023
e483392
config: add top-level 'source' entry to CH zone config for results de…
jayaddison Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion config/zones/CA-AB.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ emissionFactors:
source: Electricity Maps, 2021 average
value: 483.05444890890203
unknown:
source: 'Battle River dual fuel power plant: 50% coal and 50% natural gas'
source: 'assumes Battle River dual fuel power plant: 50% coal and 50% natural gas'
value: 655
fallbackZoneMixes:
powerOriginRatios:
Expand Down
4 changes: 2 additions & 2 deletions config/zones/CH.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ emissionFactors:
_comment:
'Source ENTSOE, Swiss government and Electricity Maps: https://github.com/electricitymaps/electricitymaps-contrib/pull/2898
and https://docs.google.com/spreadsheets/d/1FLHQ6e9Es08BIqX654BM3SEb_fuAk4k4O-6cmRXyw_E/'
source: 52% hydro storage, 48% unknown thermal
source: assumes 52% hydro storage, 48% unknown thermal
value: 288
lifecycle:
battery discharge:
Expand Down Expand Up @@ -147,7 +147,7 @@ emissionFactors:
_comment:
'Source ENTSOE, Swiss government and Electricity Maps: https://github.com/electricitymaps/electricitymaps-contrib/pull/2898
and https://docs.google.com/spreadsheets/d/1FLHQ6e9Es08BIqX654BM3SEb_fuAk4k4O-6cmRXyw_E/'
source: 52% hydro storage, 48% unknown thermal
source: assumes 52% hydro storage, 48% unknown thermal
value: 383
estimation_method: RECONSTRUCT_BREAKDOWN
fallbackZoneMixes:
Expand Down
4 changes: 2 additions & 2 deletions config/zones/GT.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ emissionFactors:
value: 269.6478450016261
unknown:
_url: https://docs.google.com/spreadsheets/d/1CegROfej9HqRZTfihpjPpgZTYPUHrTNgTdHWbbP3w74/edit#gid=291258352
source: Guatemala AMM 2021-2022 and IEA 2020
source: Guatemala AMM 2021-2022; IEA 2020
value: 383.240577
lifecycle:
battery discharge:
Expand Down Expand Up @@ -113,7 +113,7 @@ emissionFactors:
value: 311.9991841222269
unknown:
_url: https://docs.google.com/spreadsheets/d/1CegROfej9HqRZTfihpjPpgZTYPUHrTNgTdHWbbP3w74/edit#gid=291258352
source: Guatemala AMM 2021-2022 and IEA 2020
source: Guatemala AMM 2021-2022; IEA 2020
value: 549.9988504
fallbackZoneMixes:
powerOriginRatios:
Expand Down
2 changes: 1 addition & 1 deletion config/zones/IN-KA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ emissionFactors:
_comment: 'Source: Souther Regional Load Dispatch Center Allocation Limits'
_url:
- http://srldc.org/2018-19srallocation.aspx
source: calculated 20% nuclear, 80% thermal (coal)
source: assumes calculated 20% nuclear, 80% thermal (coal)
value: 658.4
fallbackZoneMixes:
powerOriginRatios:
Expand Down
3 changes: 1 addition & 2 deletions config/zones/PR.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -129,8 +129,7 @@ emissionFactors:
value: 604.8374514356746
unknown:
_comment: 'Source: https://indicadores.pr/dataset/generacion-consumo-costo-ingresos-y-clientes-del-sistema-electrico-de-puerto-rico/resource/fdad4f42-a4be-48bb-9478-a8fb75c000c6'
source: Based on average renewable generation October 2019-September 2020 (12%
hydro, 57% solar, 31% wind)
source: assumes 12% hydro, 57% solar, 31% wind based on average renewable generation October 2019-September 2020
value: 31.94
fallbackZoneMixes:
powerOriginRatios:
Expand Down
2 changes: 1 addition & 1 deletion config/zones/UA.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ emissionFactors:
_comment: estimated yearly share of solar & wind in this mixed renewable category
based on installed capacity in 2018
_url: https://en.wikipedia.org/wiki/Renewable_energy_in_Ukraine
source: renewable mix; assumes 50% solar, 50% wind
source: assumes 50% solar, 50% wind renewable mix
Copy link
Contributor Author

Choose a reason for hiding this comment

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

According to figures from the latest currently-available iteration of the same Wikipedia source page, in Y2021 the ratio of solar:wind capacity was 6227:1673.

However: those figures predate the conflict in Ukraine that began in Y2022 and so the numbers may not correspond to the situation more recently. Note also that data for Ukraine is currently unavailable on Electricity Maps (the parser has been down for a while; I'm not cross-linking the relevant issue thread here because it's not relevant to most of this pull request).

Copy link
Contributor Author

@jayaddison jayaddison Oct 13, 2023

Choose a reason for hiding this comment

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

Also: installed capacity ratio doesn't necessarily indicate actual output capacity. So even though the capacity ratio may have changed, the ratio itself may not be suitable to map 1-1 to the percentages assumed here.

value: 28
fallbackZoneMixes:
powerOriginRatios:
Expand Down
1 change: 0 additions & 1 deletion electricitymap/contrib/config/co2eq_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,5 @@ def generate_co2eq_parameters(
co2eq_parameters_lifecycle["emissionFactors"]["zoneOverrides"][
zone_key
] = zone_config["emissionFactors"][k]
del zone_config["emissionFactors"]

return co2eq_parameters_all, co2eq_parameters_direct, co2eq_parameters_lifecycle
52 changes: 29 additions & 23 deletions electricitymap/contrib/config/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,29 +96,6 @@ class Delays(StrictBaseModel):
productionPerUnit: PositiveInt | None


class Zone(StrictBaseModelWithAlias):
bounding_box: list[Point] | None
bypass_aggregation_checks: list[ZoneKey] | None = Field(
[], alias="bypassedSubZones"
)
capacity: Capacity | None
comment: str | None = Field(None, alias="_comment")
contributors: list[str] | None
delays: Delays | None
disclaimer: str | None
parsers: Parsers = Parsers()
price_displayed: bool | None
aggregates_displayed: list[str] | None
sub_zone_names: list[ZoneKey] | None = Field(None, alias="subZoneNames")
timezone: str | None
key: ZoneKey # This is not part of zones/{zone_key}.yaml, but added here to enable self referencing
estimation_method: str | None
sources: dict[str, Source] | None

def neighbors(self) -> list[ZoneKey]:
return ZONE_NEIGHBOURS.get(self.key, [])


class ExchangeParsers(ParsersBaseModel):
exchange: str | None
exchangeForecast: str | None
Expand Down Expand Up @@ -292,6 +269,35 @@ class CO2eqParameters(StrictBaseModelWithAlias):
emission_factors: EmissionFactors = Field(alias="emissionFactors")


class ZoneEmissionFactors(StrictBaseModel):
direct: AllModesEmissionFactors | None
lifecycle: AllModesEmissionFactors


class Zone(StrictBaseModelWithAlias):
bounding_box: list[Point] | None
bypass_aggregation_checks: list[ZoneKey] | None = Field(
[], alias="bypassedSubZones"
)
capacity: Capacity | None
comment: str | None = Field(None, alias="_comment")
contributors: list[str] | None
delays: Delays | None
disclaimer: str | None
parsers: Parsers = Parsers()
price_displayed: bool | None
aggregates_displayed: list[str] | None
sub_zone_names: list[ZoneKey] | None = Field(None, alias="subZoneNames")
timezone: str | None
key: ZoneKey # This is not part of zones/{zone_key}.yaml, but added here to enable self referencing
estimation_method: str | None
sources: dict[str, Source] | None
emission_factors: ZoneEmissionFactors | None = Field(None, alias="emissionFactors")

def neighbors(self) -> list[ZoneKey]:
return ZONE_NEIGHBOURS.get(self.key, [])


class ConfigModel(StrictBaseModel):
exchanges: dict[str, Exchange]
zones: dict[str, Zone]
Expand Down
39 changes: 39 additions & 0 deletions tests/test_config_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,45 @@ def test_pydantic_model(self):
CONFIG_MODEL.zones["US-NW-PSCO"].parsers.get_function("production")
)

# Add well-known sources that don't require config-based references here
GLOBAL_SOURCE_REFERENCES = {
"2018 average estimated from https://www.hops.hr/page-file/oEvvKj779KAhmQg10Gezt2/temeljni-podaci/Temeljni%20podaci%202018.pdf",
"2020 average by Electricity Maps. See disclaimer",
"BEIS 2021",
"CEA 2022",
"EIA 2020/BEIS 2021",
"Enerdata 2022",
"EU-ETS 2021",
"EU-ETS, ENTSO-E 2021",
"Guatemala AMM 2021-2022",
"IEA 2019",
"IEA 2020",
'Oberschelp, Christopher, et al. "Global emission hotspots of coal power generation."',
"Tidal (IPCC 2014)",
"https://www.iea.org/data-and-statistics/charts/electricity-generation-mix-in-mexico-1-jan-30-sep-2019-and-2020",
}

def test_zone_sources(self):
jayaddison marked this conversation as resolved.
Show resolved Hide resolved
for _, zone in CONFIG_MODEL.zones.items():
for _, production_mode in zone.emission_factors or ():
for _, estimate in production_mode or ():
if estimate is None:
continue
estimates = estimate if isinstance(estimate, list) else [estimate]
for estimate in estimates:
source_list = estimate.source
if source_list is None:
continue
for source in source_list.split(";"):
source = source.strip()
if source.startswith("assumes"):
continue
if source.startswith("Electricity Maps"):
continue
if source in self.GLOBAL_SOURCE_REFERENCES:
continue
self.assertIn(source, zone.sources)


if __name__ == "__main__":
unittest.main(buffer=True)