From 17267d4250ba06779bdc91c6ee9aa71cfc920f2b Mon Sep 17 00:00:00 2001 From: Le Roux Erwan <erwan.le-roux@irstea.fr> Date: Mon, 8 Feb 2021 15:52:09 +0100 Subject: [PATCH] [projections] add adamont_v2 --- .../adamont_data/abstract_adamont_study.py | 95 +++++++++++++++++-- .../adamont_data/adamont/adamont_snowfall.py | 8 +- .../adamont_data/adamont/adamont_variables.py | 9 ++ .../adamont_data/adamont_studies.py | 5 +- ...ples_adamont_v2.py => adamont_v2_utils.py} | 16 +--- .../safran/safran_max_snowf.py | 13 ++- extreme_data/utils.py | 1 + .../projected_data/main_projection.py | 11 ++- .../test_adamont_study.py | 33 +++---- 9 files changed, 138 insertions(+), 53 deletions(-) rename extreme_data/meteo_france_data/adamont_data/{get_list_gcm_rcm_couples_adamont_v2.py => adamont_v2_utils.py} (90%) create mode 100644 extreme_data/utils.py diff --git a/extreme_data/meteo_france_data/adamont_data/abstract_adamont_study.py b/extreme_data/meteo_france_data/adamont_data/abstract_adamont_study.py index 63fc61f9..f059bc91 100644 --- a/extreme_data/meteo_france_data/adamont_data/abstract_adamont_study.py +++ b/extreme_data/meteo_france_data/adamont_data/abstract_adamont_study.py @@ -1,5 +1,6 @@ import os import os.path as op +import subprocess from collections import OrderedDict from datetime import datetime, timedelta from enum import Enum @@ -9,21 +10,28 @@ import numpy as np from netCDF4._netCDF4 import Dataset from extreme_data.meteo_france_data.adamont_data.adamont.adamont_variables import AbstractAdamontVariable +from extreme_data.meteo_france_data.adamont_data.adamont_gcm_rcm_couples import get_gcm_rcm_couple_adamont_to_full_name from extreme_data.meteo_france_data.adamont_data.adamont_scenario import scenario_to_str, AdamontScenario, \ get_year_min_and_year_max_from_scenario, gcm_rcm_couple_to_full_name, get_suffix_for_the_nc_file, \ scenario_to_real_scenarios, get_year_max from extreme_data.meteo_france_data.adamont_data.utils.utils import massif_number_to_massif_name -ADAMONT_PATH = r"/home/erwan/Documents/projects/spatiotemporalextremes/local/spatio_temporal_datasets/ADAMONT" +from extreme_data.utils import DATA_PATH + +ADAMONT_PATH = op.join(DATA_PATH, 'ADAMONT') +ADAMONT_v2_PATH = op.join(DATA_PATH, 'ADAMONT_v2') +ADAMONT_v2_WEBPATH = """https://climatedata.umr-cnrm.fr/public/dcsc/projects/ADAMONT_MONTAGNE_2020/INDICATEURS_ANNUELS/alp_flat/""" from cached_property import cached_property from extreme_data.meteo_france_data.scm_models_data.abstract_study import AbstractStudy from extreme_data.meteo_france_data.scm_models_data.utils import Season, FrenchRegion, french_region_to_str + class WrongYearMinOrYearMax(Exception): pass + class AbstractAdamontStudy(AbstractStudy): YEAR_MIN = 1950 YEAR_MAX = 2100 @@ -32,7 +40,9 @@ class AbstractAdamontStudy(AbstractStudy): year_min=None, year_max=None, multiprocessing=True, season=Season.annual, french_region=FrenchRegion.alps, - scenario=AdamontScenario.histo, gcm_rcm_couple=('CNRM-CM5', 'ALADIN53')): + scenario=AdamontScenario.histo, + gcm_rcm_couple=('CNRM-CM5', 'ALADIN53'), + adamont_version=2): # Load the default year_min & year_max for the scenario if not specified year_min_scenario, year_max_scenario = get_year_min_and_year_max_from_scenario(scenario, gcm_rcm_couple) # Raise exception @@ -40,24 +50,81 @@ class AbstractAdamontStudy(AbstractStudy): year_min = year_min_scenario else: if year_min < year_min_scenario: - raise WrongYearMinOrYearMax('year min is {} and should be larger than {}'.format(year_min, year_min_scenario)) + raise WrongYearMinOrYearMax( + 'year min is {} and should be larger than {}'.format(year_min, year_min_scenario)) if year_max is None: year_max = year_max_scenario super().__init__(variable_class=variable_class, altitude=altitude, year_min=year_min, year_max=year_max, multiprocessing=multiprocessing, season=season, french_region=french_region) + self.adamont_version = adamont_version self.gcm_rcm_couple = gcm_rcm_couple - self.gcm_rcm_full_name = gcm_rcm_couple_to_full_name[gcm_rcm_couple] + self.gcm_rcm_full_name = get_gcm_rcm_couple_adamont_to_full_name(version=self.adamont_version)[gcm_rcm_couple] self.scenario = scenario assert issubclass(self.variable_class, AbstractAdamontVariable) # Assert the massif_name are in the same order for i, massif_name in enumerate(self.all_massif_names()): assert massif_name == massif_number_to_massif_name[i + 1] + assert self.adamont_version in [1, 2] @property def variable_name(self): return scenario_to_str(self.scenario) + ' ' + super().variable_name + @cached_property + def year_to_annual_maxima(self) -> OrderedDict: + if self.adamont_version == 1: + return super().year_to_annual_maxima + else: + return self.load_year_to_annual_maxima_version_2() + + # Loading part for adamont v2 + + def load_year_to_annual_maxima_version_2(self): + year_to_annual_maxima = OrderedDict() + for dataset in self.datasets: + annual_maxima = np.array(dataset.variables[self.variable_class.indicator_name_for_maxima]) + assert annual_maxima.shape[1] == len(self.column_mask) + annual_maxima = annual_maxima[:, self.column_mask] + year_to_annual_maxima = OrderedDict() + year_min, year_max = get_year_min_and_year_max_from_scenario(self.scenario, self.gcm_rcm_couple) + years = list(range(year_min, year_max + 1)) + for year, maxima in zip(years, annual_maxima): + if self.year_min <= year <= self.year_max: + year_to_annual_maxima[year] = maxima + return year_to_annual_maxima + + def _load_dataset(self, scenario): + scenario_name = scenario_to_str(scenario) + nc_filename = self.nc_filename_adamont_v2(scenario) + nc_folder = op.join(ADAMONT_v2_PATH, self.variable_folder_name, scenario_name) + nc_filepath = op.join(nc_folder, nc_filename) + # Assert that the file is present, otherwise download it + if not op.exists(nc_filepath): + self._download_year_to_annual_maxima_version_2(scenario, nc_folder) + # Load the file + dataset = Dataset(filename=nc_filepath) + return dataset + + def _download_year_to_annual_maxima_version_2(self, scenario, path_folder): + scenario_name = self._scenario_to_str_adamont_v2(scenario) + directory = self.gcm_rcm_full_name + '_' + scenario_name + filename = self.nc_filename_adamont_v2(scenario) + full_path = op.join(ADAMONT_v2_WEBPATH, directory, filename) + # Download file + request = 'wget {} -P {}'.format(full_path, path_folder) + subprocess.run(request, shell=True) + + def nc_filename_adamont_v2(self, scenario): + scenario_name = self._scenario_to_str_adamont_v2(scenario) + return '_'.join([self.variable_class.indicator_name_for_maxima, self.gcm_rcm_full_name, scenario_name]) + '.nc' + + def _scenario_to_str_adamont_v2(self, scenario): + scenario_name = scenario_to_str(scenario) + if scenario is AdamontScenario.histo: + scenario_name += 'RICAL' + return scenario_name + # Loading part @cached_property @@ -99,7 +166,8 @@ class AbstractAdamontStudy(AbstractStudy): # Load efficiently the variable object # Multiprocessing is set to False, because this is not a time consuming part data_list_list = [year_to_data_list[year] for year in self.ordered_years] - year_to_variable_object = self.efficient_variable_loading(self.ordered_years, data_list_list, multiprocessing=False) + year_to_variable_object = self.efficient_variable_loading(self.ordered_years, data_list_list, + multiprocessing=False) return year_to_variable_object def load_variable_object(self, data_list): @@ -110,22 +178,28 @@ class AbstractAdamontStudy(AbstractStudy): @cached_property def flat_mask(self): zs_list = [int(e) for e in np.array(self.datasets[0].variables['ZS'])] + zs_list[-10:] = [np.nan for _ in range(10)] if len(self.datasets) > 1: zs_list_bis = [int(e) for e in np.array(self.datasets[1].variables['ZS'])] - assert all([a == b for a, b in zip(zs_list, zs_list_bis)]) + zs_list_bis[-10:] = [np.nan for _ in range(10)] + assert all([(a == b) or (np.isnan(a) and np.isnan(b)) for a, b in zip(zs_list, zs_list_bis)]) return np.array(zs_list) == self.altitude @cached_property def study_massif_names(self) -> List[str]: - massif_ids = np.array(self.datasets[0].variables['MASSIF_NUMBER'])[self.flat_mask] + massif_key = 'MASSIF_NUMBER' if self.adamont_version == 1 else 'massif_number' + massif_ids = np.array(self.datasets[0].variables[massif_key])[self.flat_mask] if len(self.datasets) > 1: - massif_ids_bis = np.array(self.datasets[1].variables['MASSIF_NUMBER'])[self.flat_mask] + massif_ids_bis = np.array(self.datasets[1].variables[massif_key])[self.flat_mask] assert all(massif_ids == massif_ids_bis) return [massif_number_to_massif_name[massif_id] for massif_id in massif_ids] @cached_property def datasets(self): - return [Dataset(file_path) for file_path in self.nc_file_paths] + if self.adamont_version == 1: + return [Dataset(file_path) for file_path in self.nc_file_paths] + else: + return [self._load_dataset(scenario) for scenario in self.adamont_real_scenarios] # PATHS @@ -152,7 +226,8 @@ class AbstractAdamontStudy(AbstractStudy): @property def nc_file_paths(self): file_paths = [] - for scenario, scenario_name, files_path in zip(self.adamont_real_scenarios, self.scenario_names, self.nc_files_paths): + for scenario, scenario_name, files_path in zip(self.adamont_real_scenarios, self.scenario_names, + self.nc_files_paths): suffix_nc_file = get_suffix_for_the_nc_file(scenario, self.gcm_rcm_couple) nc_file = '{}_FORCING_{}_{}_{}_{}.nc'.format(self.variable_folder_name, self.gcm_rcm_full_name, scenario_name, diff --git a/extreme_data/meteo_france_data/adamont_data/adamont/adamont_snowfall.py b/extreme_data/meteo_france_data/adamont_data/adamont/adamont_snowfall.py index 719e9293..e36570cc 100644 --- a/extreme_data/meteo_france_data/adamont_data/adamont/adamont_snowfall.py +++ b/extreme_data/meteo_france_data/adamont_data/adamont/adamont_snowfall.py @@ -17,11 +17,13 @@ class AdamontSnowfall(AbstractAdamontStudy): year_min=None, year_max=None, multiprocessing=True, season=Season.annual, french_region=FrenchRegion.alps, - scenario=AdamontScenario.histo, gcm_rcm_couple=('CNRM-CM5', 'ALADIN53')): + scenario=AdamontScenario.histo, gcm_rcm_couple=('CNRM-CM5', 'ALADIN53'), + adamont_version=2): super().__init__(SafranSnowfallSimulationVariable, altitude, year_min, year_max, - multiprocessing, season, french_region, scenario, gcm_rcm_couple) + multiprocessing, season, french_region, scenario, gcm_rcm_couple, adamont_version) + if __name__ == '__main__': - study = AdamontSnowfall(altitude=1800) + study = AdamontSnowfall(altitude=1800, adamont_version=2) print(study.year_to_annual_maxima) diff --git a/extreme_data/meteo_france_data/adamont_data/adamont/adamont_variables.py b/extreme_data/meteo_france_data/adamont_data/adamont/adamont_variables.py index e58b8489..81609475 100644 --- a/extreme_data/meteo_france_data/adamont_data/adamont/adamont_variables.py +++ b/extreme_data/meteo_france_data/adamont_data/adamont/adamont_variables.py @@ -2,6 +2,7 @@ import numpy as np from extreme_data.meteo_france_data.scm_models_data.abstract_variable import AbstractVariable from extreme_data.meteo_france_data.scm_models_data.safran.safran_variable import SafranSnowfallVariable +from root_utils import classproperty class AbstractAdamontVariable(AbstractVariable): @@ -10,6 +11,9 @@ class AbstractAdamontVariable(AbstractVariable): def variable_name_for_folder_and_nc_file(cls): return cls.keyword() + @classmethod + def indicator_name_for_maxima(cls): + raise NotImplementedError class SafranSnowfallSimulationVariable(AbstractAdamontVariable): UNIT = SafranSnowfallVariable.UNIT @@ -26,3 +30,8 @@ class SafranSnowfallSimulationVariable(AbstractAdamontVariable): @classmethod def variable_name_for_folder_and_nc_file(cls): return 'Snow' + + @classproperty + def indicator_name_for_maxima(cls): + return 'max-1day-snowf' + diff --git a/extreme_data/meteo_france_data/adamont_data/adamont_studies.py b/extreme_data/meteo_france_data/adamont_data/adamont_studies.py index 548d8a3c..5d0e54f9 100644 --- a/extreme_data/meteo_france_data/adamont_data/adamont_studies.py +++ b/extreme_data/meteo_france_data/adamont_data/adamont_studies.py @@ -31,7 +31,7 @@ class AdamontStudies(object): return list(self.gcm_rcm_couple_to_study.values()) @cached_property - def study(self) -> AbstractStudy: + def study(self) -> AbstractAdamontStudy: return self.study_list[0] @property @@ -88,7 +88,8 @@ class AdamontStudies(object): ax.set_ylim((ylim_min, ylim_max * 1.5)) ax.tick_params(axis='both', which='major', labelsize=13) handles, labels = ax.get_legend_handles_labels() - ax.legend(handles[::-1], labels[::-1], ncol=2, prop={'size': 7}) + ncol = 2 if self.study.adamont_version == 1 else 3 + ax.legend(handles[::-1], labels[::-1], ncol=ncol, prop={'size': 7}) plot_name = 'Annual maxima of {} in {} at {} m'.format(ADAMONT_STUDY_CLASS_TO_ABBREVIATION[self.study_class], massif_name.replace('_', ' '), self.study.altitude) diff --git a/extreme_data/meteo_france_data/adamont_data/get_list_gcm_rcm_couples_adamont_v2.py b/extreme_data/meteo_france_data/adamont_data/adamont_v2_utils.py similarity index 90% rename from extreme_data/meteo_france_data/adamont_data/get_list_gcm_rcm_couples_adamont_v2.py rename to extreme_data/meteo_france_data/adamont_data/adamont_v2_utils.py index e46fafbc..17660905 100644 --- a/extreme_data/meteo_france_data/adamont_data/get_list_gcm_rcm_couples_adamont_v2.py +++ b/extreme_data/meteo_france_data/adamont_data/adamont_v2_utils.py @@ -75,11 +75,11 @@ s = set() for l in links.split('\n'): print(l) if '_' in l and 'SAFRAN' not in l: - gcm, rcm, _ = l.split('_') - s.add((gcm, rcm)) + rcm, gcm, _ = l.split('_') + s.add((gcm, rcm, rcm + '_' + gcm)) print(len(s)) -for c in s: - print(c) +for t in s: + print("('{}', '{}'): '{}',".format(*t)) """ ('SMHI-RCA4', 'ICHEC-EC-EARTH') @@ -106,12 +106,4 @@ for c in s: """ -def get_year_min_and_year_max_used_to_compute_quantile(rcm): - if rcm == 'MOHC-HadGEM2-ES': - reanalysis_years = (1988, 2011) - model_year = (1982, 2005) - else: - reanalysis_years = (1981, 2011) - model_year = (1975, 2005) - return reanalysis_years, model_year diff --git a/extreme_data/meteo_france_data/scm_models_data/safran/safran_max_snowf.py b/extreme_data/meteo_france_data/scm_models_data/safran/safran_max_snowf.py index 43881f41..05fd2d9e 100644 --- a/extreme_data/meteo_france_data/scm_models_data/safran/safran_max_snowf.py +++ b/extreme_data/meteo_france_data/scm_models_data/safran/safran_max_snowf.py @@ -6,21 +6,21 @@ from cached_property import cached_property from netCDF4._netCDF4 import Dataset from extreme_data.meteo_france_data.scm_models_data.safran.safran import SafranSnowfall1Day, Safran +from extreme_data.utils import DATA_PATH class AbstractSafranSnowfallMaxFiles(SafranSnowfall1Day): - path = """/home/erwan/Documents/projects/spatiotemporalextremes/local/spatio_temporal_datasets""" def __init__(self, safran_year, **kwargs): super().__init__(**kwargs) self.year_max = max(safran_year, self.year_max) - self.nc_filepath = op.join(self.path, 'SAFRAN_montagne-CROCUS_{}'.format(safran_year), + self.nc_filepath = op.join(DATA_PATH, 'SAFRAN_montagne-CROCUS_{}'.format(safran_year), 'max-1day-snowf_SAFRAN.nc') self.safran_year = safran_year @property def ordered_years(self): - return [i for i in list(range(1959, self.safran_year+1)) if self.year_min <= i <= self.year_max] + return [i for i in list(range(1959, self.safran_year + 1)) if self.year_min <= i <= self.year_max] @cached_property def year_to_annual_maxima(self) -> OrderedDict: @@ -44,9 +44,12 @@ class SafranSnowfall2020(AbstractSafranSnowfallMaxFiles): super().__init__(2020, **kwargs) - - class SafranSnowfall2019(AbstractSafranSnowfallMaxFiles): def __init__(self, **kwargs): super().__init__(2019, **kwargs) + + +if __name__ == '__main__': + study = SafranSnowfall2020(altitude=1800) + print(len(study.column_mask)) diff --git a/extreme_data/utils.py b/extreme_data/utils.py new file mode 100644 index 00000000..f2631eb5 --- /dev/null +++ b/extreme_data/utils.py @@ -0,0 +1 @@ +DATA_PATH = """/home/erwan/Documents/projects/spatiotemporalextremes/local/spatio_temporal_datasets""" diff --git a/projects/projected_snowfall/projected_data/main_projection.py b/projects/projected_snowfall/projected_data/main_projection.py index 71d33c7e..93ebb901 100644 --- a/projects/projected_snowfall/projected_data/main_projection.py +++ b/projects/projected_snowfall/projected_data/main_projection.py @@ -18,11 +18,11 @@ from projects.projected_snowfall.comparison_with_scm.comparison_historical_visua def main(): - fast = None - adamont_scenario = AdamontScenario.rcp85_extended + fast = True + adamont_scenario = [AdamontScenario.histo, AdamontScenario.rcp85_extended][0] year_min = 1982 if adamont_scenario is AdamontScenario.rcp85_extended else 2006 # Set the year_min and year_max for the comparison - if fast is True: + if fast is None: year_max = [2030][0] massif_names = ['Vanoise'] altitudes = [1800] @@ -31,7 +31,7 @@ def main(): # year_min = [1951][0] year_max = [2005][0] massif_names = ['Vercors'] - altitudes = [900, 1200, 1500, 1800, 2100, 2400] + altitudes = [900, 1200, 1500, 1800, 2100, 2400][3:4] else: year_max = [2100][0] massif_names = None @@ -46,7 +46,8 @@ def main(): adamont_studies = AdamontStudies(adamont_study_class, gcm_rcm_couples, altitude=altitude, year_min=year_min, year_max=year_max, season=season, - scenario=adamont_scenario) + scenario=adamont_scenario, + adamont_version=2) adamont_studies.plot_maxima_time_series_adamont(massif_names) diff --git a/test/test_extreme_data/test_meteo_france_data/test_adamont_study.py b/test/test_extreme_data/test_meteo_france_data/test_adamont_study.py index 902639cc..f577dd69 100644 --- a/test/test_extreme_data/test_meteo_france_data/test_adamont_study.py +++ b/test/test_extreme_data/test_meteo_france_data/test_adamont_study.py @@ -7,22 +7,23 @@ from extreme_data.meteo_france_data.adamont_data.adamont.adamont_snowfall import class TestAdamontStudy(unittest.TestCase): def test_load_adamont_snowfall(self): - study_list = [ - AdamontSnowfall(altitude=900), - AdamontSnowfall(altitude=1800) - ] - study_list.extend([ - AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp45), - AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp85), - AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp85_extended) - ]) - study_list.extend([AdamontSnowfall(altitude=900, gcm_rcm_couple=gcm_rcm_couple) - for gcm_rcm_couple in gcm_rcm_couple_to_full_name.keys()]) - for study in study_list: - annual_maxima_for_year_min = study.year_to_annual_maxima[study.year_min] - # print(study.altitude, study.scenario_name, study.gcm_rcm_couple) - # print(len(study.massif_name_to_annual_maxima['Vanoise'])) - self.assertTrue(True) + for version in [1, 2]: + study_list = [ + AdamontSnowfall(altitude=900, adamont_version=version), + AdamontSnowfall(altitude=1800, adamont_version=version) + ] + study_list.extend([ + AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp45, adamont_version=version), + AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp85, adamont_version=version), + AdamontSnowfall(altitude=900, scenario=AdamontScenario.rcp85_extended, adamont_version=version) + ]) + study_list.extend([AdamontSnowfall(altitude=900, gcm_rcm_couple=gcm_rcm_couple, adamont_version=version) + for gcm_rcm_couple in gcm_rcm_couple_to_full_name.keys()]) + for study in study_list: + annual_maxima_for_year_min = study.year_to_annual_maxima[study.year_min] + # print(study.altitude, study.scenario_name, study.gcm_rcm_couple) + # print(len(study.massif_name_to_annual_maxima['Vanoise'])) + self.assertTrue(True) if __name__ == '__main__': -- GitLab