Skip to content

Commit 987fbdf

Browse files
authored
Merge pull request #1224 from kbarkhad/issue_swedish_region
Fix: Look up failure for nordic regions using online emissions tracker
2 parents eeb0e0a + d4bc1fe commit 987fbdf

2 files changed

Lines changed: 98 additions & 16 deletions

File tree

codecarbon/core/emissions.py

Lines changed: 26 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
from codecarbon.external.logger import logger
1717
from codecarbon.input import DataSource, DataSourceException
1818

19+
_NORDIC_REGIONS_BY_COUNTRY = {
20+
"SWE": {"SE1", "SE2", "SE3", "SE4"},
21+
"NOR": {"NO1", "NO2", "NO3", "NO4", "NO5"},
22+
"FIN": {"FI"},
23+
}
24+
1925

2026
class Emissions:
2127
def __init__(
@@ -169,8 +175,11 @@ def get_private_infra_emissions(self, energy: Energy, geo: GeoMetadata) -> float
169175
+ " >>> Using CodeCarbon's data."
170176
)
171177

178+
country_iso_code = (
179+
geo.country_iso_code.upper() if geo.country_iso_code is not None else None
180+
)
172181
compute_with_regional_data: bool = (geo.region is not None) and (
173-
geo.country_iso_code.upper() in ["USA", "CAN", "SWE", "NOR", "FIN"]
182+
country_iso_code in ["USA", "CAN"] or self._is_supported_nordic_region(geo)
174183
)
175184

176185
if compute_with_regional_data:
@@ -187,24 +196,10 @@ def get_private_infra_emissions(self, energy: Energy, geo: GeoMetadata) -> float
187196
def _try_get_nordic_region_emissions(
188197
self, energy: Energy, geo: GeoMetadata
189198
) -> Optional[float]:
190-
nordic_regions = {
191-
"SE1",
192-
"SE2",
193-
"SE3",
194-
"SE4",
195-
"NO1",
196-
"NO2",
197-
"NO3",
198-
"NO4",
199-
"NO5",
200-
"FI",
201-
}
202-
if geo.region is None:
199+
if not self._is_supported_nordic_region(geo):
203200
return None
204201

205202
region_upper = geo.region.upper()
206-
if region_upper not in nordic_regions:
207-
return None
208203

209204
try:
210205
nordic_data = self._data_source.get_nordic_country_energy_mix_data()
@@ -225,6 +220,16 @@ def _try_get_nordic_region_emissions(
225220
)
226221
return None
227222

223+
@staticmethod
224+
def _is_supported_nordic_region(geo: GeoMetadata) -> bool:
225+
if geo.country_iso_code is None or geo.region is None:
226+
return False
227+
228+
country_regions = _NORDIC_REGIONS_BY_COUNTRY.get(
229+
geo.country_iso_code.upper(), set()
230+
)
231+
return geo.region.upper() in country_regions
232+
228233
def get_region_emissions(self, energy: Energy, geo: GeoMetadata) -> float:
229234
"""
230235
Computes emissions for a region on private infra.
@@ -248,6 +253,11 @@ def get_region_emissions(self, energy: Energy, geo: GeoMetadata) -> float:
248253
nordic_emissions = self._try_get_nordic_region_emissions(energy, geo)
249254
if nordic_emissions is not None:
250255
return nordic_emissions
256+
if (
257+
geo.country_iso_code is not None
258+
and geo.country_iso_code.upper() in _NORDIC_REGIONS_BY_COUNTRY
259+
):
260+
return self.get_country_emissions(energy, geo)
251261

252262
# Handle USA and Canada regional data
253263
try:

tests/test_emissions.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,15 @@ def test_get_emissions_PRIVATE_INFRA_unknown_country(self):
174174
assert isinstance(emissions, float)
175175
self.assertAlmostEqual(emissions, 0.475, places=2)
176176

177+
def test_get_emissions_PRIVATE_INFRA_without_country_metadata(self):
178+
emissions = self._emissions.get_private_infra_emissions(
179+
Energy.from_energy(kWh=1),
180+
GeoMetadata(country_iso_code=None, country_name=None, region=None),
181+
)
182+
183+
assert isinstance(emissions, float)
184+
self.assertAlmostEqual(emissions, 0.475, places=2)
185+
177186
@patch("codecarbon.core.electricitymaps_api.get_emissions")
178187
def test_private_infra_uses_forced_intensity_when_set(self, mocked_get_emissions):
179188
emissions_calculator = Emissions(
@@ -270,6 +279,69 @@ def test_try_get_nordic_region_emissions_returns_none_for_non_nordic_region(self
270279
# THEN
271280
self.assertIsNone(emissions)
272281

282+
def test_nordic_admin_regions_fall_back_to_country_without_regional_lookup(self):
283+
energy = Energy.from_energy(kWh=1.0)
284+
admin_regions = [
285+
("SWE", "Sweden", "Stockholm County"),
286+
("NOR", "Norway", "Oslo"),
287+
("FIN", "Finland", "Uusimaa"),
288+
]
289+
290+
for country_iso_code, country_name, region in admin_regions:
291+
with self.subTest(country_iso_code=country_iso_code, region=region):
292+
geo = GeoMetadata(
293+
country_iso_code=country_iso_code,
294+
country_name=country_name,
295+
region=region,
296+
)
297+
expected_emissions = self._emissions.get_country_emissions(energy, geo)
298+
299+
with (
300+
patch.object(
301+
self._data_source, "get_nordic_country_energy_mix_data"
302+
) as get_nordic_data,
303+
patch.object(
304+
self._data_source, "get_country_emissions_data"
305+
) as get_regional_emissions_data,
306+
patch.object(
307+
self._data_source, "get_country_energy_mix_data"
308+
) as get_regional_energy_mix_data,
309+
patch("codecarbon.core.emissions.logger.error") as log_error,
310+
patch("codecarbon.core.emissions.logger.warning") as log_warning,
311+
):
312+
emissions = self._emissions.get_private_infra_emissions(energy, geo)
313+
314+
self.assertAlmostEqual(emissions, expected_emissions, places=6)
315+
get_nordic_data.assert_not_called()
316+
get_regional_emissions_data.assert_not_called()
317+
get_regional_energy_mix_data.assert_not_called()
318+
log_error.assert_not_called()
319+
log_warning.assert_not_called()
320+
321+
def test_nordic_region_missing_static_data_falls_back_to_country(self):
322+
energy = Energy.from_energy(kWh=1.0)
323+
geo = GeoMetadata(country_iso_code="SWE", country_name="Sweden", region="SE2")
324+
expected_emissions = self._emissions.get_country_emissions(energy, geo)
325+
326+
with (
327+
patch.object(
328+
self._data_source,
329+
"get_nordic_country_energy_mix_data",
330+
return_value={"data": {}},
331+
),
332+
patch.object(
333+
self._data_source, "get_country_emissions_data"
334+
) as get_regional_emissions_data,
335+
patch.object(
336+
self._data_source, "get_country_energy_mix_data"
337+
) as get_regional_energy_mix_data,
338+
):
339+
emissions = self._emissions.get_private_infra_emissions(energy, geo)
340+
341+
self.assertAlmostEqual(emissions, expected_emissions, places=6)
342+
get_regional_emissions_data.assert_not_called()
343+
get_regional_energy_mix_data.assert_not_called()
344+
273345
def test_try_get_nordic_region_emissions_returns_none_if_region_data_missing(self):
274346
# GIVEN
275347
energy = Energy.from_energy(kWh=1.0)

0 commit comments

Comments
 (0)