From d2aa3572e5d1785995bbf947727d382e47be58de Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Wed, 26 Mar 2025 17:16:05 +0100 Subject: [PATCH 1/7] create enum class for iso codes and add function get_regions_per_countries --- climada/entity/impact_funcs/trop_cyclone.py | 291 ++++++++++++-------- 1 file changed, 182 insertions(+), 109 deletions(-) diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 692e0ef92..9029fef6f 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -22,6 +22,7 @@ __all__ = ["ImpfTropCyclone", "ImpfSetTropCyclone", "IFTropCyclone"] import logging +from enum import Enum import numpy as np import pandas as pd @@ -34,6 +35,130 @@ LOGGER = logging.getLogger(__name__) +class country_code(Enum): + """ + Enum class that links ISO country codes (both iso3a and iso3n) to specific regions and + associated impact function IDs. + + Attributes: + iso3a (dict): A mapping of region names to lists of 3-letter ISO country codes (iso3a). + iso3n (dict): A mapping of region names to lists of numeric ISO country codes (iso3n). + impf_id (dict): A mapping of region names to corresponding impact function IDs. + region_name (dict): A mapping of region names to their descriptive names. + """ + + # fmt: off + iso3n = { + "NA1": [ + 660, 28, 32, 533, 44, 52, 84, 60, 68, 132, + 136, 152, 170, 188, 192, 212, 214, 218, 222, 238, + 254, 308, 312, 320, 328, 332, 340, 388, 474, 484, + 500, 558, 591, 600, 604, 630, 654, 659, 662, 670, + 534, 740, 780, 796, 858, 862, 92, 850, + ], + "NA2": [124, 840], + "NI": [ + 4, 51, 31, 48, 50, 64, 262, 232, 231, 268, + 356, 364, 368, 376, 400, 398, 414, 417, 422, 462, + 496, 104, 524, 512, 586, 634, 682, 706, 144, 760, + 762, 795, 800, 784, 860, 887, + ], + "OC": [ + 16, 36, 184, 242, 258, 316, 296, 584, 583, 520, + 540, 554, 570, 574, 580, 585, 598, 612, 882, 90, + 626, 772, 776, 798, 548, 876, + ], + "SI": [174, 180, 748, 450, 454, 466, 480, 508, 710, 834, 716], + "WP1": [116, 360, 418, 458, 764, 704], + "WP2": [608], + "WP3": [156], + "WP4": [344, 392, 410, 446, 158], + "ROW": [ + 8, 12, 20, 24, 10, 40, 112, 56, 204, 535, + 70, 72, 74, 76, 86, 96, 100, 854, 108, 120, + 140, 148, 162, 166, 178, 191, 531, 196, 203, 384, + 208, 818, 226, 233, 234, 246, 250, 260, 266, 270, + 276, 288, 292, 300, 304, 831, 324, 624, 334, 336, + 348, 352, 372, 833, 380, 832, 404, 408, 983, 428, + 426, 430, 434, 438, 440, 442, 470, 478, 175, 498, + 492, 499, 504, 516, 528, 562, 566, 807, 578, 275, + 616, 620, 642, 643, 646, 638, 652, 663, 666, 674, + 678, 686, 688, 690, 694, 702, 703, 705, 239, 728, + 724, 729, 744, 752, 756, 768, 788, 792, 804, 826, + 581, 732, 894, 248, + ], + } + iso3a = { + "NA1": [ + "AIA", "ATG", "ARG", "ABW", "BHS", "BRB", "BLZ", "BMU", "BOL", "CPV", + "CYM", "CHL", "COL", "CRI", "CUB", "DMA", "DOM", "ECU", "SLV", "FLK", + "GUF", "GRD", "GLP", "GTM", "GUY", "HTI", "HND", "JAM", "MTQ", "MEX", + "MSR", "NIC", "PAN", "PRY", "PER", "PRI", "SHN", "KNA", "LCA", "VCT", + "SXM", "SUR", "TTO", "TCA", "URY", "VEN", "VGB", "VIR", + ], + "NA2": ["CAN", "USA"], + "NI": [ + "AFG", "ARM", "AZE", "BHR", "BGD", "BTN", "DJI", "ERI", "ETH", "GEO", + "IND", "IRN", "IRQ", "ISR", "JOR", "KAZ", "KWT", "KGZ", "LBN", "MDV", + "MNG", "MMR", "NPL", "OMN", "PAK", "QAT", "SAU", "SOM", "LKA", "SYR", + "TJK", "TKM", "UGA", "ARE", "UZB", "YEM", + ], + "OC": [ + "ASM", "AUS", "COK", "FJI", "PYF", "GUM", "KIR", "MHL", "FSM", "NRU", + "NCL", "NZL", "NIU", "NFK", "MNP", "PLW", "PNG", "PCN", "WSM", "SLB", + "TLS", "TKL", "TON", "TUV", "VUT", "WLF", + ], + "SI": [ + "COM", "COD", "SWZ", "MDG", "MWI", "MLI", "MUS", "MOZ", "ZAF", "TZA", + "ZWE", + ], + "WP1": ["KHM", "IDN", "LAO", "MYS", "THA", "VNM"], + "WP2": ["PHL"], + "WP3": ["CHN"], + "WP4": ["HKG", "JPN", "KOR", "MAC", "TWN"], + "ROW": [ + "ALB", "DZA", "AND", "AGO", "ATA", "AUT", "BLR", "BEL", "BEN", "BES", + "BIH", "BWA", "BVT", "BRA", "IOT", "BRN", "BGR", "BFA", "BDI", "CMR", + "CAF", "TCD", "CXR", "CCK", "COG", "HRV", "CUW", "CYP", "CZE", "CIV", + "DNK", "EGY", "GNQ", "EST", "FRO", "FIN", "FRA", "ATF", "GAB", "GMB", + "DEU", "GHA", "GIB", "GRC", "GRL", "GGY", "GIN", "GNB", "HMD", "VAT", + "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "KEN", "PRK", "XKX", "LVA", + "LSO", "LBR", "LBY", "LIE", "LTU", "LUX", "MLT", "MRT", "MYT", "MDA", + "MCO", "MNE", "MAR", "NAM", "NLD", "NER", "NGA", "MKD", "NOR", "PSE", + "POL", "PRT", "ROU", "RUS", "RWA", "REU", "BLM", "MAF", "SPM", "SMR", + "STP", "SEN", "SRB", "SYC", "SLE", "SGP", "SVK", "SVN", "SGS", "SSD", + "ESP", "SDN", "SJM", "SWE", "CHE", "TGO", "TUN", "TUR", "UKR", "GBR", + "UMI", "ESH", "ZMB", "ALA", + ], + } + # fmt: on + impf_id = { + "NA1": 1, + "NA2": 2, + "NI": 3, + "OC": 4, + "SI": 5, + "WP1": 6, + "WP2": 7, + "WP3": 8, + "WP4": 9, + "ROW": 10, + } + + region_name = { + "NA1": "Caribbean and Mexico", + "NA2": "USA and Canada", + "NI": "North Indian", + "OC": "Oceania", + "SI": "South Indian", + "WP1": "South East Asia", + "WP2": "Philippines", + "WP3": "China Mainland", + "WP4": "North West Pacific", + "ROW": "Rest of The World", + } + + class ImpfTropCyclone(ImpactFunc): """Impact functions for tropical cyclones.""" @@ -311,118 +436,66 @@ def get_countries_per_region(region=None): """ if not region: region = "all" - # fmt: off - iso3n = { - "NA1": [ - 660, 28, 32, 533, 44, 52, 84, 60, 68, 132, - 136, 152, 170, 188, 192, 212, 214, 218, 222, 238, - 254, 308, 312, 320, 328, 332, 340, 388, 474, 484, - 500, 558, 591, 600, 604, 630, 654, 659, 662, 670, - 534, 740, 780, 796, 858, 862, 92, 850, - ], - "NA2": [124, 840], - "NI": [ - 4, 51, 31, 48, 50, 64, 262, 232, 231, 268, - 356, 364, 368, 376, 400, 398, 414, 417, 422, 462, - 496, 104, 524, 512, 586, 634, 682, 706, 144, 760, - 762, 795, 800, 784, 860, 887, - ], - "OC": [ - 16, 36, 184, 242, 258, 316, 296, 584, 583, 520, - 540, 554, 570, 574, 580, 585, 598, 612, 882, 90, - 626, 772, 776, 798, 548, 876, - ], - "SI": [174, 180, 748, 450, 454, 466, 480, 508, 710, 834, 716], - "WP1": [116, 360, 418, 458, 764, 704], - "WP2": [608], - "WP3": [156], - "WP4": [344, 392, 410, 446, 158], - "ROW": [ - 8, 12, 20, 24, 10, 40, 112, 56, 204, 535, - 70, 72, 74, 76, 86, 96, 100, 854, 108, 120, - 140, 148, 162, 166, 178, 191, 531, 196, 203, 384, - 208, 818, 226, 233, 234, 246, 250, 260, 266, 270, - 276, 288, 292, 300, 304, 831, 324, 624, 334, 336, - 348, 352, 372, 833, 380, 832, 404, 408, 983, 428, - 426, 430, 434, 438, 440, 442, 470, 478, 175, 498, - 492, 499, 504, 516, 528, 562, 566, 807, 578, 275, - 616, 620, 642, 643, 646, 638, 652, 663, 666, 674, - 678, 686, 688, 690, 694, 702, 703, 705, 239, 728, - 724, 729, 744, 752, 756, 768, 788, 792, 804, 826, - 581, 732, 894, 248, - ], - } - iso3a = { - "NA1": [ - "AIA", "ATG", "ARG", "ABW", "BHS", "BRB", "BLZ", "BMU", "BOL", "CPV", - "CYM", "CHL", "COL", "CRI", "CUB", "DMA", "DOM", "ECU", "SLV", "FLK", - "GUF", "GRD", "GLP", "GTM", "GUY", "HTI", "HND", "JAM", "MTQ", "MEX", - "MSR", "NIC", "PAN", "PRY", "PER", "PRI", "SHN", "KNA", "LCA", "VCT", - "SXM", "SUR", "TTO", "TCA", "URY", "VEN", "VGB", "VIR", - ], - "NA2": ["CAN", "USA"], - "NI": [ - "AFG", "ARM", "AZE", "BHR", "BGD", "BTN", "DJI", "ERI", "ETH", "GEO", - "IND", "IRN", "IRQ", "ISR", "JOR", "KAZ", "KWT", "KGZ", "LBN", "MDV", - "MNG", "MMR", "NPL", "OMN", "PAK", "QAT", "SAU", "SOM", "LKA", "SYR", - "TJK", "TKM", "UGA", "ARE", "UZB", "YEM", - ], - "OC": [ - "ASM", "AUS", "COK", "FJI", "PYF", "GUM", "KIR", "MHL", "FSM", "NRU", - "NCL", "NZL", "NIU", "NFK", "MNP", "PLW", "PNG", "PCN", "WSM", "SLB", - "TLS", "TKL", "TON", "TUV", "VUT", "WLF", - ], - "SI": [ - "COM", "COD", "SWZ", "MDG", "MWI", "MLI", "MUS", "MOZ", "ZAF", "TZA", - "ZWE", - ], - "WP1": ["KHM", "IDN", "LAO", "MYS", "THA", "VNM"], - "WP2": ["PHL"], - "WP3": ["CHN"], - "WP4": ["HKG", "JPN", "KOR", "MAC", "TWN"], - "ROW": [ - "ALB", "DZA", "AND", "AGO", "ATA", "AUT", "BLR", "BEL", "BEN", "BES", - "BIH", "BWA", "BVT", "BRA", "IOT", "BRN", "BGR", "BFA", "BDI", "CMR", - "CAF", "TCD", "CXR", "CCK", "COG", "HRV", "CUW", "CYP", "CZE", "CIV", - "DNK", "EGY", "GNQ", "EST", "FRO", "FIN", "FRA", "ATF", "GAB", "GMB", - "DEU", "GHA", "GIB", "GRC", "GRL", "GGY", "GIN", "GNB", "HMD", "VAT", - "HUN", "ISL", "IRL", "IMN", "ITA", "JEY", "KEN", "PRK", "XKX", "LVA", - "LSO", "LBR", "LBY", "LIE", "LTU", "LUX", "MLT", "MRT", "MYT", "MDA", - "MCO", "MNE", "MAR", "NAM", "NLD", "NER", "NGA", "MKD", "NOR", "PSE", - "POL", "PRT", "ROU", "RUS", "RWA", "REU", "BLM", "MAF", "SPM", "SMR", - "STP", "SEN", "SRB", "SYC", "SLE", "SGP", "SVK", "SVN", "SGS", "SSD", - "ESP", "SDN", "SJM", "SWE", "CHE", "TGO", "TUN", "TUR", "UKR", "GBR", - "UMI", "ESH", "ZMB", "ALA", - ], - } - # fmt: on - impf_id = { - "NA1": 1, - "NA2": 2, - "NI": 3, - "OC": 4, - "SI": 5, - "WP1": 6, - "WP2": 7, - "WP3": 8, - "WP4": 9, - "ROW": 10, - } - region_name = dict() - region_name["NA1"] = "Caribbean and Mexico" - region_name["NA2"] = "USA and Canada" - region_name["NI"] = "North Indian" - region_name["OC"] = "Oceania" - region_name["SI"] = "South Indian" - region_name["WP1"] = "South East Asia" - region_name["WP2"] = "Philippines" - region_name["WP3"] = "China Mainland" - region_name["WP4"] = "North West Pacific" if region == "all": - return region_name, impf_id, iso3n, iso3a + return ( + country_code.region_name, + country_code.impf_id, + country_code.iso3n, + country_code.iso3a, + ) + + return ( + country_code.region_name[region], + country_code.impf_id[region], + country_code.iso3n[region], + country_code.iso3a[region], + ) + + @staticmethod + def get_regions_per_countries( + countries: list = None, code_type: str = "iso3a" + ) -> tuple: + """Return the impact function id and the region corresponding to a list of countries, + or a single country. + + Parameters: + ----------- + countries : list + List containing the ISO code of the country, which should be either + a string if the code is iso3a or an integer if iso3n. For example, for Switzerland: + the iso3a code is "CHE" and the iso3n is 756. + code_type : str + Either "iso3a" or "iso3n". + + Returns: + -------- + impf_ids : list + List of impact function ids matching the countries. + regions_ids : list + List of the regions that match the countries. + regions_names : list + List of the regions that match the countries. + """ - return region_name[region], impf_id[region], iso3n[region], iso3a[region] + if code_type not in {"iso3a", "iso3n"}: + raise ValueError("code_type must be either 'iso3a' or 'iso3n'") + + country_dict = getattr(country_code, code_type).value + # Find region + regions_ids = [ + key + for country in countries + for key, value in country_dict.items() + if country in value + ] + # Find impact function id + impf_ids = [country_code.impf_id.value[region] for region in regions_ids] + regions_name = [ + country_code.region_name.value[region] for region in regions_ids + ] + + return impf_ids, regions_ids, regions_name @deprecated( From a915abae8f0a7b17adea6d406024c0f7e7ff72b7 Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Wed, 26 Mar 2025 20:19:51 +0100 Subject: [PATCH 2/7] fix pylit and update changelog --- CHANGELOG.md | 1 + climada/entity/impact_funcs/trop_cyclone.py | 44 ++++++++++----------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f023e151..77a90f854 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -71,6 +71,7 @@ Removed: ### Added +- `climada.entity.impact_funcs.trop_cyclone.ImpfSetTropCyclone.get_countries_per_region` function [#1034](https://github.com/CLIMADA-project/climada_python/pull/1034) - `climada.hazard.tc_tracks.TCTracks.subset_years` function [#1023](https://github.com/CLIMADA-project/climada_python/pull/1023) - `climada.hazard.tc_tracks.TCTracks.from_FAST` function, add Australia basin (AU) [#993](https://github.com/CLIMADA-project/climada_python/pull/993) - Add `osm-flex` package to CLIMADA core [#981](https://github.com/CLIMADA-project/climada_python/pull/981) diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 9029fef6f..4cc9f743d 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -35,7 +35,7 @@ LOGGER = logging.getLogger(__name__) -class country_code(Enum): +class CountryCode(Enum): """ Enum class that links ISO country codes (both iso3a and iso3n) to specific regions and associated impact function IDs. @@ -48,7 +48,7 @@ class country_code(Enum): """ # fmt: off - iso3n = { + ISO3N = { "NA1": [ 660, 28, 32, 533, 44, 52, 84, 60, 68, 132, 136, 152, 170, 188, 192, 212, 214, 218, 222, 238, @@ -88,7 +88,7 @@ class country_code(Enum): 581, 732, 894, 248, ], } - iso3a = { + ISO3A = { "NA1": [ "AIA", "ATG", "ARG", "ABW", "BHS", "BRB", "BLZ", "BMU", "BOL", "CPV", "CYM", "CHL", "COL", "CRI", "CUB", "DMA", "DOM", "ECU", "SLV", "FLK", @@ -132,7 +132,7 @@ class country_code(Enum): ], } # fmt: on - impf_id = { + IMPF_ID = { "NA1": 1, "NA2": 2, "NI": 3, @@ -145,7 +145,7 @@ class country_code(Enum): "ROW": 10, } - region_name = { + REGION_NAME = { "NA1": "Caribbean and Mexico", "NA2": "USA and Canada", "NI": "North Indian", @@ -439,22 +439,22 @@ def get_countries_per_region(region=None): if region == "all": return ( - country_code.region_name, - country_code.impf_id, - country_code.iso3n, - country_code.iso3a, + CountryCode.REGION_NAME, + CountryCode.IMPF_ID, + CountryCode.ISO3N, + CountryCode.ISO3A, ) return ( - country_code.region_name[region], - country_code.impf_id[region], - country_code.iso3n[region], - country_code.iso3a[region], + CountryCode.REGION_NAME[region], + CountryCode.IMPF_ID[region], + CountryCode.ISO3N[region], + CountryCode.ISO3A[region], ) @staticmethod def get_regions_per_countries( - countries: list = None, code_type: str = "iso3a" + countries: list = None, code_type: str = "ISO3A" ) -> tuple: """Return the impact function id and the region corresponding to a list of countries, or a single country. @@ -463,10 +463,10 @@ def get_regions_per_countries( ----------- countries : list List containing the ISO code of the country, which should be either - a string if the code is iso3a or an integer if iso3n. For example, for Switzerland: - the iso3a code is "CHE" and the iso3n is 756. + a string if the code is iso3a or an integer if ISO3N. For example, for Switzerland: + the ISO3A code is "CHE" and the ISO3N is 756. code_type : str - Either "iso3a" or "iso3n". + Either "ISO3A" or "ISO3N". Returns: -------- @@ -478,10 +478,10 @@ def get_regions_per_countries( List of the regions that match the countries. """ - if code_type not in {"iso3a", "iso3n"}: + if code_type not in {"ISO3A", "ISO3N"}: raise ValueError("code_type must be either 'iso3a' or 'iso3n'") - country_dict = getattr(country_code, code_type).value + country_dict = getattr(CountryCode, code_type).value # Find region regions_ids = [ key @@ -490,10 +490,8 @@ def get_regions_per_countries( if country in value ] # Find impact function id - impf_ids = [country_code.impf_id.value[region] for region in regions_ids] - regions_name = [ - country_code.region_name.value[region] for region in regions_ids - ] + impf_ids = [CountryCode.IMPF_ID.value[region] for region in regions_ids] + regions_name = [CountryCode.REGION_NAME.value[region] for region in regions_ids] return impf_ids, regions_ids, regions_name From fb7d78de455db4159490e7a03be5636e55622ad1 Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Wed, 26 Mar 2025 21:48:25 +0100 Subject: [PATCH 3/7] fix failing tests --- climada/entity/impact_funcs/trop_cyclone.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 4cc9f743d..ecbe4802b 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -439,17 +439,17 @@ def get_countries_per_region(region=None): if region == "all": return ( - CountryCode.REGION_NAME, - CountryCode.IMPF_ID, - CountryCode.ISO3N, - CountryCode.ISO3A, + CountryCode.REGION_NAME.value, + CountryCode.IMPF_ID.value, + CountryCode.ISO3N.value, + CountryCode.ISO3A.value, ) return ( - CountryCode.REGION_NAME[region], - CountryCode.IMPF_ID[region], - CountryCode.ISO3N[region], - CountryCode.ISO3A[region], + CountryCode.REGION_NAME.value[region], + CountryCode.IMPF_ID.value[region], + CountryCode.ISO3N.value[region], + CountryCode.ISO3A.value[region], ) @staticmethod From bd62ddc36920b25eae855432bccf540e8cab0c13 Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Wed, 26 Mar 2025 22:55:43 +0100 Subject: [PATCH 4/7] add test --- climada/entity/impact_funcs/test/test_tc.py | 40 +++++++++++++++++++++ climada/entity/impact_funcs/trop_cyclone.py | 6 ++++ 2 files changed, 46 insertions(+) diff --git a/climada/entity/impact_funcs/test/test_tc.py b/climada/entity/impact_funcs/test/test_tc.py index ffa502b51..dd4d3d7b8 100644 --- a/climada/entity/impact_funcs/test/test_tc.py +++ b/climada/entity/impact_funcs/test/test_tc.py @@ -168,6 +168,46 @@ def test_get_countries_per_region(self): self.assertListEqual(out[2], [124, 840]) self.assertListEqual(out[3], ["CAN", "USA"]) + def test_get_region_per_countries(self): + """Test static get_regions_per_countries()""" + ifs = ImpfSetTropCyclone() + out = ifs.get_regions_per_countries(countries=["CHE"], code_type="ISO3A") + self.assertEqual(out[0][0], 10) + self.assertEqual(out[1][0], "ROW") + self.assertEqual(out[2][0], "Rest of The World") + out = ifs.get_regions_per_countries(countries=[756], code_type="ISO3N") + self.assertEqual(out[0][0], 10) + self.assertEqual(out[1][0], "ROW") + self.assertEqual(out[2][0], "Rest of The World") + with self.assertRaises(ValueError) as context: + ImpfSetTropCyclone.get_regions_per_countries( + countries=["SWE"], code_type="invalid_code" + ) + self.assertEqual( + str(context.exception), "code_type must be either 'iso3a' or 'iso3n'" + ) + with self.assertRaises(ValueError) as context: + ImpfSetTropCyclone.get_regions_per_countries( + countries=["SWE", 840], code_type="ISO3A" + ) + self.assertEqual( + str(context.exception), "All elements in the list must be of the same type." + ) + with self.assertRaises(ValueError) as context: + ImpfSetTropCyclone.get_regions_per_countries( + countries=[840, 124], code_type="ISO3A" + ) + self.assertEqual( + str(context.exception), "ISO3A code type cannot have integer values." + ) + with self.assertRaises(ValueError) as context: + ImpfSetTropCyclone.get_regions_per_countries( + countries=["MEX"], code_type="ISO3N" + ) + self.assertEqual( + str(context.exception), "ISO3N code type cannot have string values." + ) + # Execute Tests if __name__ == "__main__": diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index ecbe4802b..88eb66ac7 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -480,6 +480,12 @@ def get_regions_per_countries( if code_type not in {"ISO3A", "ISO3N"}: raise ValueError("code_type must be either 'iso3a' or 'iso3n'") + elif not all(isinstance(country, type(countries[0])) for country in countries): + raise ValueError("All elements in the list must be of the same type.") + elif code_type == "ISO3A" and isinstance((countries[0]), int): + raise ValueError("ISO3A code type cannot have integer values.") + elif code_type == "ISO3N" and isinstance((countries[0]), str): + raise ValueError("ISO3N code type cannot have string values.") country_dict = getattr(CountryCode, code_type).value # Find region From 7fa70e5377b615cf3e79d2915f62876449c54c46 Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Thu, 27 Mar 2025 10:32:57 +0100 Subject: [PATCH 5/7] change variable names in list comprehension --- climada/entity/impact_funcs/trop_cyclone.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 88eb66ac7..00cc92da5 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -487,13 +487,13 @@ def get_regions_per_countries( elif code_type == "ISO3N" and isinstance((countries[0]), str): raise ValueError("ISO3N code type cannot have string values.") - country_dict = getattr(CountryCode, code_type).value + region_country_dict = getattr(CountryCode, code_type).value # Find region regions_ids = [ - key + region_id for country in countries - for key, value in country_dict.items() - if country in value + for region_id, countr_in_region_id in region_country_dict.items() + if country in countr_in_region_id ] # Find impact function id impf_ids = [CountryCode.IMPF_ID.value[region] for region in regions_ids] From 4c08a08172545f150d60177bf43ac343f77bbc35 Mon Sep 17 00:00:00 2001 From: Nicolas Colombi <115944312+NicolasColombi@users.noreply.github.com> Date: Wed, 9 Apr 2025 07:52:57 +0200 Subject: [PATCH 6/7] Update climada/entity/impact_funcs/trop_cyclone.py Co-authored-by: Chahan M. Kropf --- climada/entity/impact_funcs/trop_cyclone.py | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 00cc92da5..86a6f932b 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -40,12 +40,17 @@ class CountryCode(Enum): Enum class that links ISO country codes (both iso3a and iso3n) to specific regions and associated impact function IDs. - Attributes: - iso3a (dict): A mapping of region names to lists of 3-letter ISO country codes (iso3a). - iso3n (dict): A mapping of region names to lists of numeric ISO country codes (iso3n). - impf_id (dict): A mapping of region names to corresponding impact function IDs. - region_name (dict): A mapping of region names to their descriptive names. - """ + Attributes + ---------- + ISO3A: dict + A mapping of region names to lists of 3-letter ISO country codes (iso3a). + ISO3N: dict + A mapping of region names to lists of numeric ISO country codes (iso3n). + IMPF_ID: dict + A mapping of region names to corresponding impact function IDs. + REGION_NAME: dict + A mapping of region names to their descriptive names. + """" # fmt: off ISO3N = { From 7205ee70fb601cb72fcd6e71aa13d27d34cb892a Mon Sep 17 00:00:00 2001 From: Nicolas Colombi Date: Wed, 9 Apr 2025 08:21:33 +0200 Subject: [PATCH 7/7] update strings and func name --- climada/entity/impact_funcs/test/test_tc.py | 16 +++++++++------- climada/entity/impact_funcs/trop_cyclone.py | 20 ++++++++++++-------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/climada/entity/impact_funcs/test/test_tc.py b/climada/entity/impact_funcs/test/test_tc.py index dd4d3d7b8..18cf9b852 100644 --- a/climada/entity/impact_funcs/test/test_tc.py +++ b/climada/entity/impact_funcs/test/test_tc.py @@ -169,39 +169,41 @@ def test_get_countries_per_region(self): self.assertListEqual(out[3], ["CAN", "USA"]) def test_get_region_per_countries(self): - """Test static get_regions_per_countries()""" + """Test get_impf_id_regions_per_countries()""" ifs = ImpfSetTropCyclone() - out = ifs.get_regions_per_countries(countries=["CHE"], code_type="ISO3A") + out = ifs.get_impf_id_regions_per_countries( + countries=["CHE"], code_type="ISO3A" + ) self.assertEqual(out[0][0], 10) self.assertEqual(out[1][0], "ROW") self.assertEqual(out[2][0], "Rest of The World") - out = ifs.get_regions_per_countries(countries=[756], code_type="ISO3N") + out = ifs.get_impf_id_regions_per_countries(countries=[756], code_type="ISO3N") self.assertEqual(out[0][0], 10) self.assertEqual(out[1][0], "ROW") self.assertEqual(out[2][0], "Rest of The World") with self.assertRaises(ValueError) as context: - ImpfSetTropCyclone.get_regions_per_countries( + ImpfSetTropCyclone.get_impf_id_regions_per_countries( countries=["SWE"], code_type="invalid_code" ) self.assertEqual( str(context.exception), "code_type must be either 'iso3a' or 'iso3n'" ) with self.assertRaises(ValueError) as context: - ImpfSetTropCyclone.get_regions_per_countries( + ImpfSetTropCyclone.get_impf_id_regions_per_countries( countries=["SWE", 840], code_type="ISO3A" ) self.assertEqual( str(context.exception), "All elements in the list must be of the same type." ) with self.assertRaises(ValueError) as context: - ImpfSetTropCyclone.get_regions_per_countries( + ImpfSetTropCyclone.get_impf_id_regions_per_countries( countries=[840, 124], code_type="ISO3A" ) self.assertEqual( str(context.exception), "ISO3A code type cannot have integer values." ) with self.assertRaises(ValueError) as context: - ImpfSetTropCyclone.get_regions_per_countries( + ImpfSetTropCyclone.get_impf_id_regions_per_countries( countries=["MEX"], code_type="ISO3N" ) self.assertEqual( diff --git a/climada/entity/impact_funcs/trop_cyclone.py b/climada/entity/impact_funcs/trop_cyclone.py index 86a6f932b..01a8da77b 100644 --- a/climada/entity/impact_funcs/trop_cyclone.py +++ b/climada/entity/impact_funcs/trop_cyclone.py @@ -50,7 +50,7 @@ class CountryCode(Enum): A mapping of region names to corresponding impact function IDs. REGION_NAME: dict A mapping of region names to their descriptive names. - """" + """ # fmt: off ISO3N = { @@ -458,7 +458,7 @@ def get_countries_per_region(region=None): ) @staticmethod - def get_regions_per_countries( + def get_impf_id_regions_per_countries( countries: list = None, code_type: str = "ISO3A" ) -> tuple: """Return the impact function id and the region corresponding to a list of countries, @@ -467,20 +467,24 @@ def get_regions_per_countries( Parameters: ----------- countries : list - List containing the ISO code of the country, which should be either - a string if the code is iso3a or an integer if ISO3N. For example, for Switzerland: - the ISO3A code is "CHE" and the ISO3N is 756. + List containing the ISO codes of the country, which should be either + in string format if the code is "ISO3A" or an integer if "ISO3N", see code_type below. code_type : str - Either "ISO3A" or "ISO3N". + Either "ISO3A" or "ISO3N". "ISO3A" stands for "ISO 3166-1 alpha-3" which is a + three-letter country code, "ISO3N" stands for "ISO 3166-1 numeric" which is a + three-digit country code, the numeric version of "ISO3A". For example, for Switzerland: + the "ISO3A" code is "CHE" and the "ISO3N" is 756. Returns: -------- impf_ids : list List of impact function ids matching the countries. regions_ids : list - List of the regions that match the countries. + List of the region ids. Regions are a container of countries as defined in: + https://nhess.copernicus.org/articles/21/393/2021/nhess-21-393-2021.pdf, and implemented + in the CountryCode Enum Class. Example: "NA1", "NA2", ... regions_names : list - List of the regions that match the countries. + List of the regions names. Example: "Caribbean and Mexico", "USA and Canada", ... """ if code_type not in {"ISO3A", "ISO3N"}: