From e1866a3a0b4c0798c20fc875e2f2a4cb15e242c1 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 22 May 2024 09:39:45 +1000 Subject: [PATCH 01/27] add seaice recipe files --- .../seaice_area_extents/seaice_mapextents.py | 133 ++++++++++++++++ .../seaice_area_extents/seaicearea_trends.py | 150 ++++++++++++++++++ .../recipes/recipe_seaice_extents_sh.yml | 50 ++++++ 3 files changed, 333 insertions(+) create mode 100755 esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py create mode 100755 esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py create mode 100644 esmvaltool/recipes/recipe_seaice_extents_sh.yml diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py new file mode 100755 index 0000000000..862a174f6c --- /dev/null +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -0,0 +1,133 @@ +"""diagnostic script to plot extent and differences based on code + from Anton's COSIMA cookbook notebook + +""" + +import cartopy.crs as crs +import xarray as xr +import matplotlib.pyplot as plt +import xesmf +import calendar +import matplotlib.lines as mlines +import numpy as np +import pandas as pd + +import os +import logging +from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure +from esmvaltool.diag_scripts.shared._base import get_plot_filename + + +# This part sends debug statements to stdout +logger = logging.getLogger(os.path.basename(__file__)) + +def map_diff(mod_si_ls, obs_si, months): + + # get lat max for regridding + latmax = obs_si.lat.max().values.item() + + # fig set up, width for 2 models, check len mod_si_ls + figure = plt.figure(figsize=(9,len(months)*4)) + j=0 # to iterate through positions on figure + + for mon in months: + cdr = obs_si.siconc.sel(time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') + i=1 + for mod_label,mod_si in mod_si_ls.items(): + + lat_min = mod_si.lat.min().values.item() + mod_si = mod_si.where(mod_si.lat lat_min, drop=True) + + # regrid model data to observations + regridder_ACCESS_sh = xesmf.Regridder( + mod_si.isel(time=0).drop(['i','j']), + regrd_out.isel(time=0), + 'bilinear', + periodic=True, + unmapped_to_nan=True + ) + + da = mod_si.siconc.sel(time=mod_si.siconc.time.dt.month.isin(mon)).mean('time') + mod_regrid = regridder_ACCESS_sh(da) + diff_ds = mod_regrid - cdr + + ax = plt.subplot(len(months), 3, i+j*3, projection=crs.SouthPolarStereo(true_scale_latitude=-70)) + + diffmap = ax.contourf( + diff_ds.x, diff_ds.y, diff_ds, + levels=np.arange(-90,91,20),cmap='RdBu' + ) + cs_cdr = cdr.plot.contour(levels=[15], ax=ax) + cs_mod = mod_regrid.plot.contour(levels=[15], ax=ax, colors=['black']) + + plt.title(calendar.month_abbr[mon]+' '+mod_label) + + i+=1 + j+=1 + + color_cdr = cs_cdr.collections[0].get_edgecolor() + line_cdr = mlines.Line2D([], [], color=color_cdr, label="Observed Extent") + + color_mod = cs_mod.collections[0].get_edgecolor() + line_mod = mlines.Line2D([], [], color=color_mod, label="Modelled Extent") + + plt.legend(handles=[line_cdr,line_mod], loc='center left', bbox_to_anchor=(1.2,0.5)) + cax = plt.axes([0.7,0.55,0.04,0.3]) + _ = plt.colorbar(diffmap, cax=cax, label='Difference in \nSea Ice Concentration') + return figure + + +def main(cfg): + """Compute.""" + # Get a description of the preprocessed data that we will use as input. + input_data = cfg['input_data'].values() + data = [] + + # Find input datasets to use + for dataset in input_data: + + input_file = [dataset['filename'], dataset['dataset']] + # drop areacello dataset for map + if dataset['short_name'] == 'siconc': + data.append(input_file) + + df = pd.DataFrame(data, columns=['filename','dataset']) + + logger.info(df) + mod_si_dict ={} + for fp, dt in df.itertuples(index=False): + if dt == 'NSIDC-G02202-sh': + # Load the data + obs_si = xr.open_dataset(fp) + else: + mod_si_dict[dt] = xr.open_dataset(fp) + + + logger.info("creating map differences") + mapfig = map_diff(mod_si_dict, obs_si, cfg['months']) + # Save output + output_path = get_plot_filename('map_difference', cfg) + # mapfig.savefig(output_path) # use esmvaltool convenience function + + provenance_record = get_provenance_record(df['filename'].to_list()) + save_figure(output_path, provenance_record, cfg, figure=mapfig) + +def get_provenance_record(ancestor_files): + record = { + 'ancestors': ancestor_files, + 'authors': [ + 'chun_felicity', + ], + 'caption': '', + 'domains': ['shpolar'], + 'plot_types': ['polar'], + 'references': [], + 'statistics': ['diff'], + } + return record + +if __name__ == '__main__': + + with run_diagnostic() as config: + main(config) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py new file mode 100755 index 0000000000..f8dcd886b7 --- /dev/null +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -0,0 +1,150 @@ +"""diagnostic script to plot minima and maxima trends based on code + from Anton's COSIMA cookbook notebook + +""" + +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +import xarray as xr +import os +import logging +from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure +from esmvaltool.diag_scripts.shared._base import get_plot_filename + + +# This part sends debug statements to stdout +logger = logging.getLogger(os.path.basename(__file__)) + +def sea_ice_area(sic,area,coordls, range=[0.15,1]): + #sic percent is in 0-100. Mulitply by portion so divide by 100 ## + sic = sic/100 + return (sic*area).where((sic>=range[0])*(sic<=range[1])).sum(coordls) + +def sea_ice_area_obs(ds): + sic = ds.siconc # + area_km2 = ds.areacello/1e6 # + result = sea_ice_area(sic,area_km2,['x','y']).to_dataset(name='cdr_area') + + #Theres a couple of data gaps which should be nan + result.loc[{'time':'1988-01-01'}] = np.nan + result.loc[{'time':'1987-12'}] = np.nan + + return result.sel(time=slice('1979','2018')) + +def sea_ice_area_model_sh(ds): + sic = ds.siconc.where(ds.siconc.lat < -20, drop=True) # + + area_km2=ds.areacello/1e6 ##area convert to km2 + + return sea_ice_area(sic,area_km2,['i','j']).to_dataset(name='si_area') + +def min_and_max(ds): + def min_and_max_year(da): + result = xr.Dataset() + result['min'] = da.min() + result['max'] = da.max() + return result + annual_min_max_ds=ds.si_area.groupby('time.year').apply(min_and_max_year) + return annual_min_max_ds + +def plot_trend(model_min_max, obs_a, minmax): + """ + model_min_max: dictionary of model label and xarray ds with min and max. + obs_a: xarray of observations area + """ + + figure, _axes = plt.subplots() + + # both min and max # multiple models? min_max dict + # add note for years change? + for mod_label,model_min_max_dt in model_min_max.items(): + model_min_max_dt[minmax].plot(label=mod_label) + + if minmax == 'max': + obs_a.cdr_area.groupby('time.year').max().plot(label='Obs CDR') + plt.title('Trends in Sea-Ice Maxima') + elif minmax == 'min': + obs_a.cdr_area.groupby('time.year').min().plot(label='Obs CDR') + plt.title('Trends in Sea-Ice Minima') + + + plt.ylabel('Sea-Ice Area (km2)') + + _ = plt.legend() + return figure + + +def main(cfg): + """Compute sea ice area for each input dataset.""" + + input_data = cfg['input_data'].values() + + data = [] + + for dataset in input_data: + # Load the data + input_file = [dataset['filename'], dataset['short_name'], dataset['dataset']] + # key for different models + logger.info(f"dataset: {dataset['long_name']}") + data.append(input_file) + + df = pd.DataFrame(data, columns=['filename','short_name','dataset']) # + + logger.info(df[['short_name', 'dataset']]) + + min_max = {} + # sort to ensure order of reading + for fp, sn, dt in df.sort_values(['dataset','short_name']).itertuples(index=False): + if dt == 'NSIDC-G02202-sh': + if sn == 'areacello': + area_obs = xr.open_dataset(fp) + else: + obs_si = xr.open_dataset(fp) + else: # other models + if sn == 'areacello': + area_mod = xr.open_dataset(fp) + dta = dt + else: + mod_si = xr.open_dataset(fp) + + # make sure correct area with model? + if dta == dt: + mod_si['areacello'] = area_mod['areacello'] # + model_area_dt = sea_ice_area_model_sh(mod_si) + model_min_max_dt = min_and_max(model_area_dt) + model_min_max_dt['year'] = model_min_max_dt.year + 1652 # make years compariable + min_max[dt] = model_min_max_dt + else: + logger.warning(f"..{dt} missing a variable?") + + obs_si['areacello'] = area_obs['areacello'] + obs_a = sea_ice_area_obs(obs_si) + + provenance_record = get_provenance_record(df['filename'].to_list()) + for m in ['min','max']: + fig = plot_trend(min_max, obs_a, m) + # Save output + output_path = get_plot_filename(f'{m}_trend', cfg) + # fig.savefig(output_path) # use esmvaltool convenience function + save_figure(output_path, provenance_record, cfg, figure=fig) + + +def get_provenance_record(ancestor_files): + record = { + 'ancestors': ancestor_files, + 'authors': [ + 'chun_felicity', + ], + 'caption': 'added 1652 years to model years for comparability', + 'domains': ['shpolar'], + 'plot_types': ['times'], + 'references': [], + 'statistics': ['mean'], + } + return record + +if __name__ == '__main__': + + with run_diagnostic() as config: + main(config) diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml new file mode 100644 index 0000000000..e074130f40 --- /dev/null +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -0,0 +1,50 @@ +# ESMValTool +# recipe_seaice_extents_sh.yml +--- +documentation: + title: SH sea ice area recipe + description: | + This is an example recipe, shared in + CMIP7 hackathon 2024, CSIRO Aspendale (Melbourne). + Converted a COSIMA cookbook recipe with help + from Anton Steketee + + authors: + - chun_felicity + + realms: + - seaIce + + projects: + - access-nri + +datasets: +# these years are comparable to 1958 -2018 # re-adjust years in script (+1652) + - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, + exp: piControl, ensemble: r1i1p1f1, start_year: 306, end_year: 366} + - {dataset: ACCESS-OM2, activity: OMIP ,project: CMIP6, grid: gn, + exp: omip2, ensemble: r1i1p1f1, start_year: 306, end_year: 366} +# observations + - {dataset: NSIDC-G02202-sh, project: OBS6, tier: 3, + type: reanaly, version: 4, start_year: 1979, end_year: 2018} + + +diagnostics: + + sea_ice_sh: + description: sea ice area and mapping sea ice concentration + variables: + ## 2 variables - sea ice concentration and cell area to compute sea ice area + si_fraction: + short_name: siconc + mip: SImon + area: + short_name: areacello + mip: Ofx + scripts: + map_extents: + months: [2,9] # months to map (feb, sep) + script: seaice_area_extents/seaice_mapextents.py + trends: + script: seaice_area_extents/seaicearea_trends.py + \ No newline at end of file From faf1f0495c141bf3c68c05091e38c0d7622218c3 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 22 May 2024 11:24:23 +1000 Subject: [PATCH 02/27] code cleaning --- .../seaice_area_extents/seaice_mapextents.py | 100 +++++++------ .../seaice_area_extents/seaicearea_trends.py | 134 ++++++++++-------- 2 files changed, 132 insertions(+), 102 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 862a174f6c..7f0f7d0318 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -1,19 +1,21 @@ -"""diagnostic script to plot extent and differences based on code - from Anton's COSIMA cookbook notebook +"""diagnostic script to plot extent and differences +based on code from Anton Steketee's COSIMA cookbook notebook +https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples + /SeaIce_Obs_Model_Compare.html """ -import cartopy.crs as crs +import logging +import os +import calendar +from cartopy import crs import xarray as xr -import matplotlib.pyplot as plt import xesmf -import calendar +import matplotlib.pyplot as plt import matplotlib.lines as mlines import numpy as np import pandas as pd -import os -import logging from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure from esmvaltool.diag_scripts.shared._base import get_plot_filename @@ -21,50 +23,55 @@ # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) -def map_diff(mod_si_ls, obs_si, months): +def map_diff(mod_si_ls, obs_si, months): + "create figure mapping extents for models and months" # get lat max for regridding latmax = obs_si.lat.max().values.item() # fig set up, width for 2 models, check len mod_si_ls - figure = plt.figure(figsize=(9,len(months)*4)) - j=0 # to iterate through positions on figure + figure = plt.figure(figsize = (9, len(months) * 4)) + j = 0 # to iterate through positions on figure for mon in months: - cdr = obs_si.siconc.sel(time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') - i=1 - for mod_label,mod_si in mod_si_ls.items(): - + cdr = obs_si.siconc.sel(time=obs_si.siconc.time.dt.month.isin(mon) + ).mean('time') + i = 1 + for mod_label, mod_si in mod_si_ls.items(): + lat_min = mod_si.lat.min().values.item() - mod_si = mod_si.where(mod_si.lat lat_min, drop=True) # regrid model data to observations - regridder_ACCESS_sh = xesmf.Regridder( - mod_si.isel(time=0).drop(['i','j']), - regrd_out.isel(time=0), - 'bilinear', + regridder_access_sh = xesmf.Regridder( + mod_si.isel(time=0).drop(['i','j']), + regrd_out.isel(time=0), + 'bilinear', periodic=True, unmapped_to_nan=True ) - da = mod_si.siconc.sel(time=mod_si.siconc.time.dt.month.isin(mon)).mean('time') - mod_regrid = regridder_ACCESS_sh(da) + model_mean = mod_si.siconc.sel(time=mod_si.siconc.time.dt.month.isin(mon) + ).mean('time') + mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr - - ax = plt.subplot(len(months), 3, i+j*3, projection=crs.SouthPolarStereo(true_scale_latitude=-70)) - - diffmap = ax.contourf( + + axes = plt.subplot(len(months), 3, i + j * 3, projection= + crs.SouthPolarStereo(true_scale_latitude=-70)) + + diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, - levels=np.arange(-90,91,20),cmap='RdBu' + levels=np.arange(-90,91,20), cmap='RdBu' ) - cs_cdr = cdr.plot.contour(levels=[15], ax=ax) - cs_mod = mod_regrid.plot.contour(levels=[15], ax=ax, colors=['black']) - - plt.title(calendar.month_abbr[mon]+' '+mod_label) + cs_cdr = cdr.plot.contour(levels=[15], ax=axes) + cs_mod = mod_regrid.plot.contour(levels=[15], ax=axes, + colors=['black']) - i+=1 - j+=1 + plt.title(calendar.month_abbr[mon] + ' ' + mod_label) + + i += 1 + j += 1 color_cdr = cs_cdr.collections[0].get_edgecolor() line_cdr = mlines.Line2D([], [], color=color_cdr, label="Observed Extent") @@ -72,9 +79,11 @@ def map_diff(mod_si_ls, obs_si, months): color_mod = cs_mod.collections[0].get_edgecolor() line_mod = mlines.Line2D([], [], color=color_mod, label="Modelled Extent") - plt.legend(handles=[line_cdr,line_mod], loc='center left', bbox_to_anchor=(1.2,0.5)) + plt.legend(handles=[line_cdr,line_mod], loc='center left', + bbox_to_anchor=(1.2,0.5)) cax = plt.axes([0.7,0.55,0.04,0.3]) - _ = plt.colorbar(diffmap, cax=cax, label='Difference in \nSea Ice Concentration') + _ = plt.colorbar(diffmap, cax=cax, + label='Difference in \nSea Ice Concentration') return figure @@ -92,17 +101,16 @@ def main(cfg): if dataset['short_name'] == 'siconc': data.append(input_file) - df = pd.DataFrame(data, columns=['filename','dataset']) + inputs_df = pd.DataFrame(data, columns=['filename', 'dataset']) - logger.info(df) - mod_si_dict ={} - for fp, dt in df.itertuples(index=False): - if dt == 'NSIDC-G02202-sh': + logger.info(inputs_df) + mod_si_dict = {} + for filepath, data_name in inputs_df.itertuples(index=False): + if data_name == 'NSIDC-G02202-sh': # Load the data - obs_si = xr.open_dataset(fp) + obs_si = xr.open_dataset(filepath) else: - mod_si_dict[dt] = xr.open_dataset(fp) - + mod_si_dict[data_name] = xr.open_dataset(filepath) logger.info("creating map differences") mapfig = map_diff(mod_si_dict, obs_si, cfg['months']) @@ -110,10 +118,12 @@ def main(cfg): output_path = get_plot_filename('map_difference', cfg) # mapfig.savefig(output_path) # use esmvaltool convenience function - provenance_record = get_provenance_record(df['filename'].to_list()) + provenance_record = get_provenance_record(inputs_df['filename'].to_list()) save_figure(output_path, provenance_record, cfg, figure=mapfig) + def get_provenance_record(ancestor_files): + "build provenance dictionary" record = { 'ancestors': ancestor_files, 'authors': [ @@ -124,10 +134,12 @@ def get_provenance_record(ancestor_files): 'plot_types': ['polar'], 'references': [], 'statistics': ['diff'], - } + } return record + if __name__ == '__main__': + with run_diagnostic() as config: main(config) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index f8dcd886b7..8925997a3e 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -1,14 +1,18 @@ -"""diagnostic script to plot minima and maxima trends based on code - from Anton's COSIMA cookbook notebook +""" +diagnostic script to plot minima and maxima trends +based on code from Anton Steketee's COSIMA cookbook notebook +https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples + /SeaIce_Obs_Model_Compare.html """ +import logging +import os import matplotlib.pyplot as plt import numpy as np import pandas as pd import xarray as xr -import os -import logging + from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure from esmvaltool.diag_scripts.shared._base import get_plot_filename @@ -16,49 +20,61 @@ # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) -def sea_ice_area(sic,area,coordls, range=[0.15,1]): - #sic percent is in 0-100. Mulitply by portion so divide by 100 ## - sic = sic/100 - return (sic*area).where((sic>=range[0])*(sic<=range[1])).sum(coordls) -def sea_ice_area_obs(ds): - sic = ds.siconc # - area_km2 = ds.areacello/1e6 # - result = sea_ice_area(sic,area_km2,['x','y']).to_dataset(name='cdr_area') +def sea_ice_area(sic, area, coordls): + "sic percent is in 0-100. Mulitply by portion so divide by 100" + sic = sic / 100 + # valid sic between 0.15 and 1 + return (sic * area).where((sic >= 0.15) * (sic <= 1)).sum(coordls) + + +def sea_ice_area_obs(xdataset): + "compute sea ice area for obs dataset" + sic = xdataset.siconc + area_km2 = xdataset.areacello/1e6 + result = sea_ice_area(sic, area_km2, ['x','y']).to_dataset(name='cdr_area') - #Theres a couple of data gaps which should be nan - result.loc[{'time':'1988-01-01'}] = np.nan - result.loc[{'time':'1987-12'}] = np.nan + # Theres a couple of data gaps which should be nan + result.loc[{'time': '1988-01-01'}] = np.nan + result.loc[{'time': '1987-12'}] = np.nan - return result.sel(time=slice('1979','2018')) + return result.sel(time=slice('1979','2018')) -def sea_ice_area_model_sh(ds): - sic = ds.siconc.where(ds.siconc.lat < -20, drop=True) # + +def sea_ice_area_model_sh(xdataset): + "compute sea ice area for model dataset" + sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) - area_km2=ds.areacello/1e6 ##area convert to km2 + area_km2 = xdataset.areacello / 1e6 # area convert to km2 + + return sea_ice_area(sic, area_km2, ['i', 'j']).to_dataset(name='si_area') - return sea_ice_area(sic,area_km2,['i','j']).to_dataset(name='si_area') -def min_and_max(ds): - def min_and_max_year(da): +def min_and_max(dataset): + "compute min and max for dataset" + def min_and_max_year(yeardata): result = xr.Dataset() - result['min'] = da.min() - result['max'] = da.max() + result['min'] = yeardata.min() + result['max'] = yeardata.max() return result - annual_min_max_ds=ds.si_area.groupby('time.year').apply(min_and_max_year) + annual_min_max_ds = dataset.si_area.groupby('time.year').apply(min_and_max_year) return annual_min_max_ds def plot_trend(model_min_max, obs_a, minmax): """ + function to plot min or max trend + + Parameters + ---------- model_min_max: dictionary of model label and xarray ds with min and max. obs_a: xarray of observations area + minmax: 'min' or 'max' """ figure, _axes = plt.subplots() - - # both min and max # multiple models? min_max dict - # add note for years change? - for mod_label,model_min_max_dt in model_min_max.items(): + + # add note for years change + for mod_label, model_min_max_dt in model_min_max.items(): model_min_max_dt[minmax].plot(label=mod_label) if minmax == 'max': @@ -68,7 +84,6 @@ def plot_trend(model_min_max, obs_a, minmax): obs_a.cdr_area.groupby('time.year').min().plot(label='Obs CDR') plt.title('Trends in Sea-Ice Minima') - plt.ylabel('Sea-Ice Area (km2)') _ = plt.legend() @@ -77,60 +92,61 @@ def plot_trend(model_min_max, obs_a, minmax): def main(cfg): """Compute sea ice area for each input dataset.""" - input_data = cfg['input_data'].values() - data = [] for dataset in input_data: # Load the data input_file = [dataset['filename'], dataset['short_name'], dataset['dataset']] - # key for different models - logger.info(f"dataset: {dataset['long_name']}") + # key for different models + logger.info("dataset: %s", dataset['long_name']) data.append(input_file) - df = pd.DataFrame(data, columns=['filename','short_name','dataset']) # + inputfiles_df = pd.DataFrame(data, columns=['filename','short_name','dataset']) - logger.info(df[['short_name', 'dataset']]) + logger.info(inputfiles_df[['short_name', 'dataset']]) min_max = {} # sort to ensure order of reading - for fp, sn, dt in df.sort_values(['dataset','short_name']).itertuples(index=False): - if dt == 'NSIDC-G02202-sh': - if sn == 'areacello': - area_obs = xr.open_dataset(fp) + for filepath, shortname, data_name in inputfiles_df.sort_values(['dataset','short_name'] + ).itertuples(index=False): + if data_name == 'NSIDC-G02202-sh': + if shortname == 'areacello': + area_obs = xr.open_dataset(filepath) else: - obs_si = xr.open_dataset(fp) - else: # other models - if sn == 'areacello': - area_mod = xr.open_dataset(fp) - dta = dt + obs_si = xr.open_dataset(filepath) + else: # other models + if shortname == 'areacello': + area_mod = xr.open_dataset(filepath) + dt_label = data_name else: - mod_si = xr.open_dataset(fp) + mod_si = xr.open_dataset(filepath) - # make sure correct area with model? - if dta == dt: - mod_si['areacello'] = area_mod['areacello'] # - model_area_dt = sea_ice_area_model_sh(mod_si) + # make sure correct area with model + if dt_label == data_name: + mod_si['areacello'] = area_mod['areacello'] + model_area_dt = sea_ice_area_model_sh(mod_si) model_min_max_dt = min_and_max(model_area_dt) - model_min_max_dt['year'] = model_min_max_dt.year + 1652 # make years compariable - min_max[dt] = model_min_max_dt + # make years in ACCESS model compariable + model_min_max_dt['year'] = model_min_max_dt.year + 1652 + min_max[data_name] = model_min_max_dt else: - logger.warning(f"..{dt} missing a variable?") - + logger.warning("..%s missing a variable?", data_name) + obs_si['areacello'] = area_obs['areacello'] obs_a = sea_ice_area_obs(obs_si) provenance_record = get_provenance_record(df['filename'].to_list()) - for m in ['min','max']: - fig = plot_trend(min_max, obs_a, m) + for trend_type in ['min', 'max']: + fig = plot_trend(min_max, obs_a, trend_type) # Save output - output_path = get_plot_filename(f'{m}_trend', cfg) - # fig.savefig(output_path) # use esmvaltool convenience function + output_path = get_plot_filename(f'{trend_type}_trend', cfg) + save_figure(output_path, provenance_record, cfg, figure=fig) def get_provenance_record(ancestor_files): + "build provenance record" record = { 'ancestors': ancestor_files, 'authors': [ @@ -144,7 +160,9 @@ def get_provenance_record(ancestor_files): } return record + if __name__ == '__main__': + with run_diagnostic() as config: main(config) From 9bb7e61fc91f4cf183dd1c6a526f3dfe447fc18b Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 22 May 2024 11:24:23 +1000 Subject: [PATCH 03/27] code cleaning --- .../seaice_area_extents/seaice_mapextents.py | 12 +++--- .../seaice_area_extents/seaicearea_trends.py | 37 ++++++++++--------- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 7f0f7d0318..7114ac0796 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -25,7 +25,7 @@ def map_diff(mod_si_ls, obs_si, months): - "create figure mapping extents for models and months" + """create figure mapping extents for models and months""" # get lat max for regridding latmax = obs_si.lat.max().values.item() @@ -53,12 +53,12 @@ def map_diff(mod_si_ls, obs_si, months): ) model_mean = mod_si.siconc.sel(time=mod_si.siconc.time.dt.month.isin(mon) - ).mean('time') + ).mean('time') mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr - axes = plt.subplot(len(months), 3, i + j * 3, projection= - crs.SouthPolarStereo(true_scale_latitude=-70)) + axes = plt.subplot(len(months), 3, i + j * 3, + projection=crs.SouthPolarStereo(true_scale_latitude=-70)) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, @@ -116,14 +116,12 @@ def main(cfg): mapfig = map_diff(mod_si_dict, obs_si, cfg['months']) # Save output output_path = get_plot_filename('map_difference', cfg) - # mapfig.savefig(output_path) # use esmvaltool convenience function - provenance_record = get_provenance_record(inputs_df['filename'].to_list()) save_figure(output_path, provenance_record, cfg, figure=mapfig) def get_provenance_record(ancestor_files): - "build provenance dictionary" + """build provenance dictionary""" record = { 'ancestors': ancestor_files, 'authors': [ diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 8925997a3e..a8d6cadb82 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -1,5 +1,5 @@ """ -diagnostic script to plot minima and maxima trends +diagnostic script to plot minima and maxima trends based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -22,16 +22,16 @@ def sea_ice_area(sic, area, coordls): - "sic percent is in 0-100. Mulitply by portion so divide by 100" + """sic percent is in 0-100. Mulitply by portion so divide by 100""" sic = sic / 100 # valid sic between 0.15 and 1 return (sic * area).where((sic >= 0.15) * (sic <= 1)).sum(coordls) def sea_ice_area_obs(xdataset): - "compute sea ice area for obs dataset" + """compute sea ice area for obs dataset""" sic = xdataset.siconc - area_km2 = xdataset.areacello/1e6 + area_km2 = xdataset.areacello / 1e6 result = sea_ice_area(sic, area_km2, ['x','y']).to_dataset(name='cdr_area') # Theres a couple of data gaps which should be nan @@ -42,7 +42,7 @@ def sea_ice_area_obs(xdataset): def sea_ice_area_model_sh(xdataset): - "compute sea ice area for model dataset" + """compute sea ice area for model dataset""" sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) area_km2 = xdataset.areacello / 1e6 # area convert to km2 @@ -51,13 +51,14 @@ def sea_ice_area_model_sh(xdataset): def min_and_max(dataset): - "compute min and max for dataset" + """compute min and max for dataset""" def min_and_max_year(yeardata): result = xr.Dataset() result['min'] = yeardata.min() result['max'] = yeardata.max() return result - annual_min_max_ds = dataset.si_area.groupby('time.year').apply(min_and_max_year) + annual_min_max_ds = dataset.si_area.groupby('time.year' + ).apply(min_and_max_year) return annual_min_max_ds def plot_trend(model_min_max, obs_a, minmax): @@ -97,19 +98,21 @@ def main(cfg): for dataset in input_data: # Load the data - input_file = [dataset['filename'], dataset['short_name'], dataset['dataset']] + input_file = [dataset['filename'], dataset['short_name'], + dataset['dataset']] # key for different models logger.info("dataset: %s", dataset['long_name']) data.append(input_file) - inputfiles_df = pd.DataFrame(data, columns=['filename','short_name','dataset']) - + inputfiles_df = pd.DataFrame(data, columns=['filename', 'short_name', + 'dataset']) + # sort to ensure order of reading + inputfiles_df.sort_values(['dataset','short_name'], inplace=True) logger.info(inputfiles_df[['short_name', 'dataset']]) min_max = {} - # sort to ensure order of reading - for filepath, shortname, data_name in inputfiles_df.sort_values(['dataset','short_name'] - ).itertuples(index=False): + + for filepath, shortname, data_name in inputfiles_df.itertuples(index=False): if data_name == 'NSIDC-G02202-sh': if shortname == 'areacello': area_obs = xr.open_dataset(filepath) @@ -136,17 +139,17 @@ def main(cfg): obs_si['areacello'] = area_obs['areacello'] obs_a = sea_ice_area_obs(obs_si) - provenance_record = get_provenance_record(df['filename'].to_list()) + provenance = get_provenance_record(inputfiles_df['filename'].to_list()) for trend_type in ['min', 'max']: fig = plot_trend(min_max, obs_a, trend_type) # Save output output_path = get_plot_filename(f'{trend_type}_trend', cfg) - save_figure(output_path, provenance_record, cfg, figure=fig) + save_figure(output_path, provenance, cfg, figure=fig) def get_provenance_record(ancestor_files): - "build provenance record" + """build provenance record""" record = { 'ancestors': ancestor_files, 'authors': [ @@ -157,7 +160,7 @@ def get_provenance_record(ancestor_files): 'plot_types': ['times'], 'references': [], 'statistics': ['mean'], - } + } return record From 17de6e0c483ba11a30627e98d3483a18d028483c Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 22 May 2024 14:03:30 +1000 Subject: [PATCH 04/27] code formatting --- .../seaice_area_extents/seaice_mapextents.py | 29 +++++++++---------- .../seaice_area_extents/seaicearea_trends.py | 19 ++++++------ 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 7114ac0796..139bffa1d0 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -1,4 +1,4 @@ -"""diagnostic script to plot extent and differences +"""diagnostic script to plot extent and differences based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -30,7 +30,7 @@ def map_diff(mod_si_ls, obs_si, months): latmax = obs_si.lat.max().values.item() # fig set up, width for 2 models, check len mod_si_ls - figure = plt.figure(figsize = (9, len(months) * 4)) + figure = plt.figure(figsize=(9, len(months) * 4)) j = 0 # to iterate through positions on figure for mon in months: @@ -45,27 +45,27 @@ def map_diff(mod_si_ls, obs_si, months): # regrid model data to observations regridder_access_sh = xesmf.Regridder( - mod_si.isel(time=0).drop(['i','j']), + mod_si.isel(time=0).drop(['i', 'j']), regrd_out.isel(time=0), 'bilinear', periodic=True, unmapped_to_nan=True ) - model_mean = mod_si.siconc.sel(time=mod_si.siconc.time.dt.month.isin(mon) - ).mean('time') + model_mean = mod_si.siconc.sel(time=mod_si.siconc.time + .dt.month.isin(mon)).mean('time') mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr - axes = plt.subplot(len(months), 3, i + j * 3, - projection=crs.SouthPolarStereo(true_scale_latitude=-70)) + proj = crs.SouthPolarStereo(true_scale_latitude=-70) + axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, - levels=np.arange(-90,91,20), cmap='RdBu' + levels=np.arange(-90, 91, 20), cmap='RdBu' ) cs_cdr = cdr.plot.contour(levels=[15], ax=axes) - cs_mod = mod_regrid.plot.contour(levels=[15], ax=axes, + cs_mod = mod_regrid.plot.contour(levels=[15], ax=axes, colors=['black']) plt.title(calendar.month_abbr[mon] + ' ' + mod_label) @@ -79,10 +79,10 @@ def map_diff(mod_si_ls, obs_si, months): color_mod = cs_mod.collections[0].get_edgecolor() line_mod = mlines.Line2D([], [], color=color_mod, label="Modelled Extent") - plt.legend(handles=[line_cdr,line_mod], loc='center left', - bbox_to_anchor=(1.2,0.5)) - cax = plt.axes([0.7,0.55,0.04,0.3]) - _ = plt.colorbar(diffmap, cax=cax, + plt.legend(handles=[line_cdr, line_mod], loc='center left', + bbox_to_anchor=(1.2, 0.5)) + cax = plt.axes([0.7, 0.55, 0.04, 0.3]) + _ = plt.colorbar(diffmap, cax=cax, label='Difference in \nSea Ice Concentration') return figure @@ -95,7 +95,7 @@ def main(cfg): # Find input datasets to use for dataset in input_data: - + input_file = [dataset['filename'], dataset['dataset']] # drop areacello dataset for map if dataset['short_name'] == 'siconc': @@ -138,6 +138,5 @@ def get_provenance_record(ancestor_files): if __name__ == '__main__': - with run_diagnostic() as config: main(config) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index a8d6cadb82..6cf6bc91c5 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -32,19 +32,20 @@ def sea_ice_area_obs(xdataset): """compute sea ice area for obs dataset""" sic = xdataset.siconc area_km2 = xdataset.areacello / 1e6 - result = sea_ice_area(sic, area_km2, ['x','y']).to_dataset(name='cdr_area') + result = sea_ice_area(sic, area_km2, + ['x', 'y']).to_dataset(name='cdr_area') # Theres a couple of data gaps which should be nan result.loc[{'time': '1988-01-01'}] = np.nan result.loc[{'time': '1987-12'}] = np.nan - return result.sel(time=slice('1979','2018')) + return result.sel(time=slice('1979', '2018')) def sea_ice_area_model_sh(xdataset): """compute sea ice area for model dataset""" sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) - + area_km2 = xdataset.areacello / 1e6 # area convert to km2 return sea_ice_area(sic, area_km2, ['i', 'j']).to_dataset(name='si_area') @@ -61,6 +62,7 @@ def min_and_max_year(yeardata): ).apply(min_and_max_year) return annual_min_max_ds + def plot_trend(model_min_max, obs_a, minmax): """ function to plot min or max trend @@ -73,7 +75,7 @@ def plot_trend(model_min_max, obs_a, minmax): """ figure, _axes = plt.subplots() - + # add note for years change for mod_label, model_min_max_dt in model_min_max.items(): model_min_max_dt[minmax].plot(label=mod_label) @@ -107,19 +109,19 @@ def main(cfg): inputfiles_df = pd.DataFrame(data, columns=['filename', 'short_name', 'dataset']) # sort to ensure order of reading - inputfiles_df.sort_values(['dataset','short_name'], inplace=True) + inputfiles_df.sort_values(['dataset', 'short_name'], inplace=True) logger.info(inputfiles_df[['short_name', 'dataset']]) min_max = {} - for filepath, shortname, data_name in inputfiles_df.itertuples(index=False): + for filepath, short, data_name in inputfiles_df.itertuples(index=False): if data_name == 'NSIDC-G02202-sh': - if shortname == 'areacello': + if short == 'areacello': area_obs = xr.open_dataset(filepath) else: obs_si = xr.open_dataset(filepath) else: # other models - if shortname == 'areacello': + if short == 'areacello': area_mod = xr.open_dataset(filepath) dt_label = data_name else: @@ -166,6 +168,5 @@ def get_provenance_record(ancestor_files): if __name__ == '__main__': - with run_diagnostic() as config: main(config) From bae13775a442f150ab225d453c5f924907e05d19 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 5 Jun 2024 12:29:16 +1000 Subject: [PATCH 05/27] edit references --- esmvaltool/config-references.yml | 10 ++++++++++ esmvaltool/recipes/recipe_seaice_extents_sh.yml | 4 ++++ 2 files changed, 14 insertions(+) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index b5f43bc911..b4b1de05c7 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -157,6 +157,11 @@ authors: name: Chen, Jack institute: NCAR, USA orcid: + chun_felicity: + name: Chun, Felicity + institute: ACCESS-NRI, Australia + orcid: https://orcid.org/0009-0007-0845-0953 + github: flicj191 cionni_irene: name: Cionni, Irene institute: ENEA, Italy @@ -717,6 +722,10 @@ authors: name: Stevens, Mark institute: NCAR, US orcid: + steketee_anton: + name: Steketee, Anton + institute: ACCESS-NRI + orcid: # Former viewers (not active viewers) adeniyi_kemisola: name: Adeniyi, Kemisola @@ -766,6 +775,7 @@ authors: projects: 4c: EU H2020 project 4C + access-nri: The Australian Earth System Simulator (ACCESS-NRI) applicate: EU Horizon 2020 Advanced prediction in polar regions and beyond c3s-magic: Copernicus Climate Change Service 34a Lot 2 (MAGIC) project climval: BMBF MiKlip Project ClimVal diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index e074130f40..84682bd48a 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -11,6 +11,10 @@ documentation: authors: - chun_felicity + - steketee_anton + + maintainer: + - chun_felicity realms: - seaIce From a7f09bd40810ddbc478071f634085051ecc214f4 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 5 Jun 2024 14:14:02 +1000 Subject: [PATCH 06/27] codacy issues --- .../seaice_area_extents/seaice_mapextents.py | 24 +++++++------- .../seaice_area_extents/seaicearea_trends.py | 32 ++++++++----------- 2 files changed, 26 insertions(+), 30 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 139bffa1d0..6aefd8628a 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -1,4 +1,4 @@ -"""diagnostic script to plot extent and differences +"""diagnostic script to plot extent and differences. based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -8,7 +8,7 @@ import logging import os import calendar -from cartopy import crs +from cartopy.crs import SouthPolarStereo import xarray as xr import xesmf import matplotlib.pyplot as plt @@ -25,7 +25,7 @@ def map_diff(mod_si_ls, obs_si, months): - """create figure mapping extents for models and months""" + """create figure mapping extents for models and months.""" # get lat max for regridding latmax = obs_si.lat.max().values.item() @@ -57,8 +57,8 @@ def map_diff(mod_si_ls, obs_si, months): mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr - proj = crs.SouthPolarStereo(true_scale_latitude=-70) - axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) + axes = plt.subplot(len(months), 3, i + j * 3, + projection=SouthPolarStereo(true_scale_latitude=-70)) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, @@ -72,12 +72,14 @@ def map_diff(mod_si_ls, obs_si, months): i += 1 j += 1 - - color_cdr = cs_cdr.collections[0].get_edgecolor() - line_cdr = mlines.Line2D([], [], color=color_cdr, label="Observed Extent") - - color_mod = cs_mod.collections[0].get_edgecolor() - line_mod = mlines.Line2D([], [], color=color_mod, label="Modelled Extent") + + line_cdr = mlines.Line2D([], [], + color=cs_cdr.collections[0].get_edgecolor(), + label="Observed Extent") + + line_mod = mlines.Line2D([], [], + color=cs_mod.collections[0].get_edgecolor(), + label="Modelled Extent") plt.legend(handles=[line_cdr, line_mod], loc='center left', bbox_to_anchor=(1.2, 0.5)) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 6cf6bc91c5..85aebd8437 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -1,5 +1,4 @@ -""" -diagnostic script to plot minima and maxima trends +"""diagnostic script to plot minima and maxima trends. based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -22,14 +21,14 @@ def sea_ice_area(sic, area, coordls): - """sic percent is in 0-100. Mulitply by portion so divide by 100""" + """sic percent is in 0-100. Mulitply by portion so divide by 100.""" sic = sic / 100 # valid sic between 0.15 and 1 return (sic * area).where((sic >= 0.15) * (sic <= 1)).sum(coordls) def sea_ice_area_obs(xdataset): - """compute sea ice area for obs dataset""" + """compute sea ice area for obs dataset.""" sic = xdataset.siconc area_km2 = xdataset.areacello / 1e6 result = sea_ice_area(sic, area_km2, @@ -43,7 +42,7 @@ def sea_ice_area_obs(xdataset): def sea_ice_area_model_sh(xdataset): - """compute sea ice area for model dataset""" + """compute sea ice area for model dataset.""" sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) area_km2 = xdataset.areacello / 1e6 # area convert to km2 @@ -52,7 +51,7 @@ def sea_ice_area_model_sh(xdataset): def min_and_max(dataset): - """compute min and max for dataset""" + """compute min and max for dataset.""" def min_and_max_year(yeardata): result = xr.Dataset() result['min'] = yeardata.min() @@ -95,16 +94,13 @@ def plot_trend(model_min_max, obs_a, minmax): def main(cfg): """Compute sea ice area for each input dataset.""" - input_data = cfg['input_data'].values() data = [] - for dataset in input_data: - # Load the data - input_file = [dataset['filename'], dataset['short_name'], - dataset['dataset']] - # key for different models + for dataset in cfg['input_data'].values(): + # data values to iterate logger.info("dataset: %s", dataset['long_name']) - data.append(input_file) + data.append([dataset['filename'], dataset['short_name'], + dataset['dataset']]) inputfiles_df = pd.DataFrame(data, columns=['filename', 'short_name', 'dataset']) @@ -139,15 +135,13 @@ def main(cfg): logger.warning("..%s missing a variable?", data_name) obs_si['areacello'] = area_obs['areacello'] - obs_a = sea_ice_area_obs(obs_si) provenance = get_provenance_record(inputfiles_df['filename'].to_list()) for trend_type in ['min', 'max']: - fig = plot_trend(min_max, obs_a, trend_type) + fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type) # Save output - output_path = get_plot_filename(f'{trend_type}_trend', cfg) - - save_figure(output_path, provenance, cfg, figure=fig) + save_figure(get_plot_filename(f'{trend_type}_trend', cfg), + provenance, cfg, figure=fig) def get_provenance_record(ancestor_files): @@ -162,7 +156,7 @@ def get_provenance_record(ancestor_files): 'plot_types': ['times'], 'references': [], 'statistics': ['mean'], - } + } return record From 36d149b05a8221b1743271b3c8e31a92dc7dd102 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 5 Jun 2024 14:47:16 +1000 Subject: [PATCH 07/27] codacy clean up --- .../seaice_area_extents/seaice_mapextents.py | 56 ++++++++++--------- .../seaice_area_extents/seaicearea_trends.py | 17 +++--- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 6aefd8628a..736d2ca998 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -1,4 +1,4 @@ -"""diagnostic script to plot extent and differences. +"""Diagnostic script to plot extent and differences. based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -8,7 +8,7 @@ import logging import os import calendar -from cartopy.crs import SouthPolarStereo +import cartopy.crs.SouthPolarStereo as SPS import xarray as xr import xesmf import matplotlib.pyplot as plt @@ -23,9 +23,30 @@ # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) +def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): + """Regrid and compute difference grid.""" + lat_min = mod_si.lat.min().values.item() + mod_si = mod_si.where(mod_si.lat < latmax, drop=True) + regrd_out = obs_si.where(obs_si.lat > lat_min, drop=True) + + # regrid model data to observations + regridder_access_sh = xesmf.Regridder( + mod_si.isel(time=0).drop(['i', 'j']), + regrd_out.isel(time=0), + 'bilinear', + periodic=True, + unmapped_to_nan=True + ) + + model_mean = mod_si.siconc.sel(time=mod_si.siconc.time + .dt.month.isin(mon)).mean('time') + mod_regrid = regridder_access_sh(model_mean) + diff_ds = mod_regrid - cdr + return diff_ds, mod_regrid + def map_diff(mod_si_ls, obs_si, months): - """create figure mapping extents for models and months.""" + """Create figure mapping extents for models and months.""" # get lat max for regridding latmax = obs_si.lat.max().values.item() @@ -39,26 +60,11 @@ def map_diff(mod_si_ls, obs_si, months): i = 1 for mod_label, mod_si in mod_si_ls.items(): - lat_min = mod_si.lat.min().values.item() - mod_si = mod_si.where(mod_si.lat < latmax, drop=True) - regrd_out = obs_si.where(obs_si.lat > lat_min, drop=True) - - # regrid model data to observations - regridder_access_sh = xesmf.Regridder( - mod_si.isel(time=0).drop(['i', 'j']), - regrd_out.isel(time=0), - 'bilinear', - periodic=True, - unmapped_to_nan=True - ) - - model_mean = mod_si.siconc.sel(time=mod_si.siconc.time - .dt.month.isin(mon)).mean('time') - mod_regrid = regridder_access_sh(model_mean) - diff_ds = mod_regrid - cdr + diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, + cdr, mon, latmax) - axes = plt.subplot(len(months), 3, i + j * 3, - projection=SouthPolarStereo(true_scale_latitude=-70)) + axes = plt.subplot(len(months), 3, i + j * 3, + projection=SPS(true_scale_latitude=-70)) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, @@ -72,7 +78,7 @@ def map_diff(mod_si_ls, obs_si, months): i += 1 j += 1 - + line_cdr = mlines.Line2D([], [], color=cs_cdr.collections[0].get_edgecolor(), label="Observed Extent") @@ -123,7 +129,7 @@ def main(cfg): def get_provenance_record(ancestor_files): - """build provenance dictionary""" + """Build provenance dictionary.""" record = { 'ancestors': ancestor_files, 'authors': [ @@ -134,7 +140,7 @@ def get_provenance_record(ancestor_files): 'plot_types': ['polar'], 'references': [], 'statistics': ['diff'], - } + } return record diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 85aebd8437..0adca612c4 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -1,4 +1,4 @@ -"""diagnostic script to plot minima and maxima trends. +"""Diagnostic script to plot minima and maxima trends. based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples @@ -21,14 +21,14 @@ def sea_ice_area(sic, area, coordls): - """sic percent is in 0-100. Mulitply by portion so divide by 100.""" + """Percent sic is in 0-100. Mulitply by portion so divide by 100.""" sic = sic / 100 # valid sic between 0.15 and 1 return (sic * area).where((sic >= 0.15) * (sic <= 1)).sum(coordls) def sea_ice_area_obs(xdataset): - """compute sea ice area for obs dataset.""" + """Compute sea ice area for obs dataset.""" sic = xdataset.siconc area_km2 = xdataset.areacello / 1e6 result = sea_ice_area(sic, area_km2, @@ -42,7 +42,7 @@ def sea_ice_area_obs(xdataset): def sea_ice_area_model_sh(xdataset): - """compute sea ice area for model dataset.""" + """Compute sea ice area for model dataset.""" sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) area_km2 = xdataset.areacello / 1e6 # area convert to km2 @@ -51,7 +51,7 @@ def sea_ice_area_model_sh(xdataset): def min_and_max(dataset): - """compute min and max for dataset.""" + """Compute min and max for dataset.""" def min_and_max_year(yeardata): result = xr.Dataset() result['min'] = yeardata.min() @@ -63,8 +63,7 @@ def min_and_max_year(yeardata): def plot_trend(model_min_max, obs_a, minmax): - """ - function to plot min or max trend + """function to plot min or max trend. Parameters ---------- @@ -100,7 +99,7 @@ def main(cfg): # data values to iterate logger.info("dataset: %s", dataset['long_name']) data.append([dataset['filename'], dataset['short_name'], - dataset['dataset']]) + dataset['dataset']]) inputfiles_df = pd.DataFrame(data, columns=['filename', 'short_name', 'dataset']) @@ -145,7 +144,7 @@ def main(cfg): def get_provenance_record(ancestor_files): - """build provenance record""" + """Build provenance record.""" record = { 'ancestors': ancestor_files, 'authors': [ From a60d21813a8903d2de89da645284aee5e1b4e77e Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 5 Jun 2024 15:49:58 +1000 Subject: [PATCH 08/27] flake clean up --- .../diag_scripts/seaice_area_extents/seaice_mapextents.py | 8 +++++--- .../diag_scripts/seaice_area_extents/seaicearea_trends.py | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 736d2ca998..5acfbb4572 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -8,7 +8,7 @@ import logging import os import calendar -import cartopy.crs.SouthPolarStereo as SPS +from cartopy.crs import SouthPolarStereo import xarray as xr import xesmf import matplotlib.pyplot as plt @@ -23,6 +23,7 @@ # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) + def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): """Regrid and compute difference grid.""" lat_min = mod_si.lat.min().values.item() @@ -39,9 +40,10 @@ def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): ) model_mean = mod_si.siconc.sel(time=mod_si.siconc.time - .dt.month.isin(mon)).mean('time') + .dt.month.isin(mon)).mean('time') mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr + return diff_ds, mod_regrid @@ -64,7 +66,7 @@ def map_diff(mod_si_ls, obs_si, months): cdr, mon, latmax) axes = plt.subplot(len(months), 3, i + j * 3, - projection=SPS(true_scale_latitude=-70)) + projection=SouthPolarStereo(true_scale_latitude=-70)) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 0adca612c4..d9726b032c 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -63,7 +63,8 @@ def min_and_max_year(yeardata): def plot_trend(model_min_max, obs_a, minmax): - """function to plot min or max trend. + """ + function to plot min or max trend Parameters ---------- From e03c8f511665e57b02c772919cc6858ce8302393 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Tue, 11 Jun 2024 10:27:41 +1000 Subject: [PATCH 09/27] flake clean up --- .../diag_scripts/seaice_area_extents/seaice_mapextents.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 5acfbb4572..8a8c5e5e4d 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -52,6 +52,8 @@ def map_diff(mod_si_ls, obs_si, months): # get lat max for regridding latmax = obs_si.lat.max().values.item() + proj = SouthPolarStereo(true_scale_latitude=-70) + # fig set up, width for 2 models, check len mod_si_ls figure = plt.figure(figsize=(9, len(months) * 4)) j = 0 # to iterate through positions on figure @@ -65,8 +67,7 @@ def map_diff(mod_si_ls, obs_si, months): diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, latmax) - axes = plt.subplot(len(months), 3, i + j * 3, - projection=SouthPolarStereo(true_scale_latitude=-70)) + axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) diffmap = axes.contourf( diff_ds.x, diff_ds.y, diff_ds, From e8ad989d3ecf45affa8bde7effbb9dae0496b061 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 28 Jun 2024 12:00:54 +1000 Subject: [PATCH 10/27] add docs, authors and save data --- .zenodo.json | 14 +++- CITATION.cff | 5 ++ .../seaice_extents_sh/map_difference.png | Bin 0 -> 83496 bytes .../figures/seaice_extents_sh/min_trend.png | Bin 0 -> 68649 bytes doc/sphinx/source/recipes/index.rst | 1 + .../recipes/recipe_seaice_extents_sh.rst | 72 ++++++++++++++++++ .../seaice_area_extents/seaice_mapextents.py | 23 ++++-- .../seaice_area_extents/seaicearea_trends.py | 15 +++- 8 files changed, 119 insertions(+), 11 deletions(-) create mode 100644 doc/sphinx/source/recipes/figures/seaice_extents_sh/map_difference.png create mode 100644 doc/sphinx/source/recipes/figures/seaice_extents_sh/min_trend.png create mode 100644 doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst diff --git a/.zenodo.json b/.zenodo.json index c6a731981f..dd1d1315ea 100644 --- a/.zenodo.json +++ b/.zenodo.json @@ -171,6 +171,11 @@ "name": "Hagemann, Stefan", "orcid": "0000-0001-5444-2945" }, + { + "affiliation": "University of Canterbury, New Zealand", + "name": "Hardacre, Catherine", + "orcid": "0000-0001-9093-4656" + }, { "affiliation": "ISAC-CNR, Italy", "name": "von Hardenberg, Jost", @@ -380,15 +385,20 @@ "affiliation": "DLR, Germany", "name": "Bonnet, Pauline", "orcid": "0000-0003-3780-0784" + }, + { + "affiliation": "ACCESS-NRI, Australia", + "name": "Chun, Felicity", + "orcid": "0009-0007-0845-0953" } ], "description": "ESMValTool: A community diagnostic and performance metrics tool for routine evaluation of Earth system models in CMIP.", "license": { "id": "Apache-2.0" }, - "publication_date": "2023-07-06", + "publication_date": "2023-12-20", "title": "ESMValTool", - "version": "v2.9.0", + "version": "v2.10.0", "communities": [ { "identifier": "is-enes3" diff --git a/CITATION.cff b/CITATION.cff index cd621538b7..b903a4287c 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -391,6 +391,11 @@ authors: family-names: Bonnet given-names: Pauline orcid: "https://orcid.org/0000-0003-3780-0784" + - + affiliation: "ACCESS-NRI, Australia" + family-names: Chun + given-names: Felicity + orcid: "https://orcid.org/0009-0007-0845-0953" cff-version: 1.2.0 date-released: 2023-12-20 diff --git a/doc/sphinx/source/recipes/figures/seaice_extents_sh/map_difference.png b/doc/sphinx/source/recipes/figures/seaice_extents_sh/map_difference.png new file mode 100644 index 0000000000000000000000000000000000000000..fbd840196f09795704704db73aad035906281edb GIT binary patch literal 83496 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 aRxb#RU=n&-N;PQ&peZY;%NNL*`2PojDGz7> literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/recipes/figures/seaice_extents_sh/min_trend.png b/doc/sphinx/source/recipes/figures/seaice_extents_sh/min_trend.png new file mode 100644 index 0000000000000000000000000000000000000000..1f4fb60ae5c5f0916bb7147b9b237716febd2716 GIT binary patch literal 68649 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

YmyNU$r<+Zb^`qwq3n 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 literal 0 HcmV?d00001 diff --git a/doc/sphinx/source/recipes/index.rst b/doc/sphinx/source/recipes/index.rst index 0f0ce7667d..e8b0f5072b 100644 --- a/doc/sphinx/source/recipes/index.rst +++ b/doc/sphinx/source/recipes/index.rst @@ -146,6 +146,7 @@ Other recipe_rainfarm recipe_seaice recipe_seaice_drift + recipe_seaice_extents_sh recipe_seaice_feedback recipe_shapeselect recipes_testing diff --git a/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst b/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst new file mode 100644 index 0000000000..e4ade3e849 --- /dev/null +++ b/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst @@ -0,0 +1,72 @@ +.. _recipes_seaice_extents_sh: + +Sea Ice area and extents +======================== + +Overview +-------- + +This recipe plots sea ice concentration from CICE (sea ice model) output +around the southern polar region and compares it to the NSIDC CDR +(National Snow and Ice Data Centre, Climate Data Record) dataset. + + +Available recipes and diagnostics +--------------------------------- + +Recipes are stored in esmvaltool/recipes/ + + * recipe_seaice_extents_sh.yml + +Diagnostics are stored in esmvaltool/diag_scripts/seaice_area_extents/ + + * seaicearea_trends.py: plot minima and maxima sea ice area trends + * seaice_mapextents.py: plot sea ice extent and differences to Observations + + +User settings in recipe +----------------------- + +#. Script seaice_mapextents.py + + *Required settings for script* + + * `months`: months by month number which the mean are to be plotted + + +Variables +--------- + +* siconc (seaIce, monthly, longitude latitude time) +* areacello (fx) + + +Observations and reformat scripts +--------------------------------- + +*Note: (1) obs4MIPs data can be used directly without any preprocessing; +(2) see headers of reformat scripts for non-obs4MIPs data for download +instructions.* + +* NSIDC CDR sh (siconc - esmvaltool/cmorizers/data/formatters/datasets/nsidc_g02202_sh.py) + + +References +---------- + +* COSIMA(Consortium for Ocean-Sea Ice Modelling in Australia) recipe: https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples/SeaIce_Obs_Model_Compare.html + +Example plots +------------- + +.. _trends: +.. figure:: /recipes/figures/seaice_extents_sh/min_trend.png + :align: center + + Minima trends of sea ice area with observation data. ACCESS OM model data years from 0, added 1652 years to model years for comparability. + +.. _map extents: +.. figure:: /recipes/figures/seaice_extents_sh/map_difference.png + :align: center + + Difference and extents of models with observations for selected months. \ No newline at end of file diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 8a8c5e5e4d..1a5a78a2c9 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -16,7 +16,11 @@ import numpy as np import pandas as pd -from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure +from esmvaltool.diag_scripts.shared import ( + run_diagnostic, + save_figure, + save_data +) from esmvaltool.diag_scripts.shared._base import get_plot_filename @@ -47,7 +51,7 @@ def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): return diff_ds, mod_regrid -def map_diff(mod_si_ls, obs_si, months): +def map_diff(mod_si_ls, obs_si, months, cfg, prov): """Create figure mapping extents for models and months.""" # get lat max for regridding latmax = obs_si.lat.max().values.item() @@ -66,7 +70,12 @@ def map_diff(mod_si_ls, obs_si, months): diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, latmax) - + # save plot data + save_data(mod_label + calendar.month_abbr[mon] + '_mean', + prov, cfg, mod_regrid.to_iris()) + save_data(mod_label + calendar.month_abbr[mon] + '_obs_diff', + prov, cfg, diff_ds.to_iris()) + axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) diffmap = axes.contourf( @@ -124,10 +133,13 @@ def main(cfg): mod_si_dict[data_name] = xr.open_dataset(filepath) logger.info("creating map differences") - mapfig = map_diff(mod_si_dict, obs_si, cfg['months']) + provenance_record = get_provenance_record(inputs_df['filename'].to_list()) + + mapfig = map_diff(mod_si_dict, obs_si, cfg['months'], + cfg, provenance_record) # Save output output_path = get_plot_filename('map_difference', cfg) - provenance_record = get_provenance_record(inputs_df['filename'].to_list()) + save_figure(output_path, provenance_record, cfg, figure=mapfig) @@ -137,6 +149,7 @@ def get_provenance_record(ancestor_files): 'ancestors': ancestor_files, 'authors': [ 'chun_felicity', + 'steketee_anton' ], 'caption': '', 'domains': ['shpolar'], diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index d9726b032c..6405d393d7 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -12,7 +12,9 @@ import pandas as pd import xarray as xr -from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure +from esmvaltool.diag_scripts.shared import ( + run_diagnostic, save_figure, save_data +) from esmvaltool.diag_scripts.shared._base import get_plot_filename @@ -62,7 +64,7 @@ def min_and_max_year(yeardata): return annual_min_max_ds -def plot_trend(model_min_max, obs_a, minmax): +def plot_trend(model_min_max, obs_a, minmax, prov, cfg): """ function to plot min or max trend @@ -78,6 +80,9 @@ def plot_trend(model_min_max, obs_a, minmax): # add note for years change for mod_label, model_min_max_dt in model_min_max.items(): model_min_max_dt[minmax].plot(label=mod_label) + # save data + save_data(mod_label + minmax, prov, cfg, + model_min_max_dt[minmax].to_iris()) if minmax == 'max': obs_a.cdr_area.groupby('time.year').max().plot(label='Obs CDR') @@ -138,7 +143,8 @@ def main(cfg): provenance = get_provenance_record(inputfiles_df['filename'].to_list()) for trend_type in ['min', 'max']: - fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type) + fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type, + provenance, cfg) # Save output save_figure(get_plot_filename(f'{trend_type}_trend', cfg), provenance, cfg, figure=fig) @@ -150,11 +156,12 @@ def get_provenance_record(ancestor_files): 'ancestors': ancestor_files, 'authors': [ 'chun_felicity', + 'steketee_anton' ], 'caption': 'added 1652 years to model years for comparability', 'domains': ['shpolar'], 'plot_types': ['times'], - 'references': [], + 'references': ['access_nri'], 'statistics': ['mean'], } return record From 475bb8018b35da5fd40e358bc9bad2d472899bed Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 28 Jun 2024 12:20:11 +1000 Subject: [PATCH 11/27] formatting --- .../seaice_area_extents/seaice_mapextents.py | 71 +++++++++---------- .../seaice_area_extents/seaicearea_trends.py | 33 +++++---- 2 files changed, 49 insertions(+), 55 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 1a5a78a2c9..a916ed5715 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -16,14 +16,10 @@ import numpy as np import pandas as pd -from esmvaltool.diag_scripts.shared import ( - run_diagnostic, - save_figure, - save_data -) +from esmvaltool.diag_scripts.shared import (run_diagnostic, save_figure, + save_data) from esmvaltool.diag_scripts.shared._base import get_plot_filename - # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) @@ -35,16 +31,14 @@ def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): regrd_out = obs_si.where(obs_si.lat > lat_min, drop=True) # regrid model data to observations - regridder_access_sh = xesmf.Regridder( - mod_si.isel(time=0).drop(['i', 'j']), - regrd_out.isel(time=0), - 'bilinear', - periodic=True, - unmapped_to_nan=True - ) - - model_mean = mod_si.siconc.sel(time=mod_si.siconc.time - .dt.month.isin(mon)).mean('time') + regridder_access_sh = xesmf.Regridder(mod_si.isel(time=0).drop(['i', 'j']), + regrd_out.isel(time=0), + 'bilinear', + periodic=True, + unmapped_to_nan=True) + + model_mean = mod_si.siconc.sel( + time=mod_si.siconc.time.dt.month.isin(mon)).mean('time') mod_regrid = regridder_access_sh(model_mean) diff_ds = mod_regrid - cdr @@ -63,27 +57,29 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): j = 0 # to iterate through positions on figure for mon in months: - cdr = obs_si.siconc.sel(time=obs_si.siconc.time.dt.month.isin(mon) - ).mean('time') + cdr = obs_si.siconc.sel( + time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') i = 1 for mod_label, mod_si in mod_si_ls.items(): - diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, - cdr, mon, latmax) + diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, + latmax) # save plot data - save_data(mod_label + calendar.month_abbr[mon] + '_mean', - prov, cfg, mod_regrid.to_iris()) - save_data(mod_label + calendar.month_abbr[mon] + '_obs_diff', - prov, cfg, diff_ds.to_iris()) - + save_data(mod_label + calendar.month_abbr[mon] + '_mean', prov, + cfg, mod_regrid.to_iris()) + save_data(mod_label + calendar.month_abbr[mon] + '_obs_diff', prov, + cfg, diff_ds.to_iris()) + axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) - diffmap = axes.contourf( - diff_ds.x, diff_ds.y, diff_ds, - levels=np.arange(-90, 91, 20), cmap='RdBu' - ) + diffmap = axes.contourf(diff_ds.x, + diff_ds.y, + diff_ds, + levels=np.arange(-90, 91, 20), + cmap='RdBu') cs_cdr = cdr.plot.contour(levels=[15], ax=axes) - cs_mod = mod_regrid.plot.contour(levels=[15], ax=axes, + cs_mod = mod_regrid.plot.contour(levels=[15], + ax=axes, colors=['black']) plt.title(calendar.month_abbr[mon] + ' ' + mod_label) @@ -99,10 +95,12 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): color=cs_mod.collections[0].get_edgecolor(), label="Modelled Extent") - plt.legend(handles=[line_cdr, line_mod], loc='center left', + plt.legend(handles=[line_cdr, line_mod], + loc='center left', bbox_to_anchor=(1.2, 0.5)) cax = plt.axes([0.7, 0.55, 0.04, 0.3]) - _ = plt.colorbar(diffmap, cax=cax, + _ = plt.colorbar(diffmap, + cax=cax, label='Difference in \nSea Ice Concentration') return figure @@ -135,8 +133,8 @@ def main(cfg): logger.info("creating map differences") provenance_record = get_provenance_record(inputs_df['filename'].to_list()) - mapfig = map_diff(mod_si_dict, obs_si, cfg['months'], - cfg, provenance_record) + mapfig = map_diff(mod_si_dict, obs_si, cfg['months'], cfg, + provenance_record) # Save output output_path = get_plot_filename('map_difference', cfg) @@ -147,10 +145,7 @@ def get_provenance_record(ancestor_files): """Build provenance dictionary.""" record = { 'ancestors': ancestor_files, - 'authors': [ - 'chun_felicity', - 'steketee_anton' - ], + 'authors': ['chun_felicity', 'steketee_anton'], 'caption': '', 'domains': ['shpolar'], 'plot_types': ['polar'], diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 6405d393d7..9e638a7d09 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -12,12 +12,10 @@ import pandas as pd import xarray as xr -from esmvaltool.diag_scripts.shared import ( - run_diagnostic, save_figure, save_data -) +from esmvaltool.diag_scripts.shared import (run_diagnostic, save_figure, + save_data) from esmvaltool.diag_scripts.shared._base import get_plot_filename - # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) @@ -54,13 +52,15 @@ def sea_ice_area_model_sh(xdataset): def min_and_max(dataset): """Compute min and max for dataset.""" + def min_and_max_year(yeardata): result = xr.Dataset() result['min'] = yeardata.min() result['max'] = yeardata.max() return result - annual_min_max_ds = dataset.si_area.groupby('time.year' - ).apply(min_and_max_year) + + annual_min_max_ds = dataset.si_area.groupby('time.year').apply( + min_and_max_year) return annual_min_max_ds @@ -81,7 +81,7 @@ def plot_trend(model_min_max, obs_a, minmax, prov, cfg): for mod_label, model_min_max_dt in model_min_max.items(): model_min_max_dt[minmax].plot(label=mod_label) # save data - save_data(mod_label + minmax, prov, cfg, + save_data(mod_label + minmax, prov, cfg, model_min_max_dt[minmax].to_iris()) if minmax == 'max': @@ -104,11 +104,11 @@ def main(cfg): for dataset in cfg['input_data'].values(): # data values to iterate logger.info("dataset: %s", dataset['long_name']) - data.append([dataset['filename'], dataset['short_name'], - dataset['dataset']]) + data.append( + [dataset['filename'], dataset['short_name'], dataset['dataset']]) - inputfiles_df = pd.DataFrame(data, columns=['filename', 'short_name', - 'dataset']) + inputfiles_df = pd.DataFrame(data, + columns=['filename', 'short_name', 'dataset']) # sort to ensure order of reading inputfiles_df.sort_values(['dataset', 'short_name'], inplace=True) logger.info(inputfiles_df[['short_name', 'dataset']]) @@ -143,21 +143,20 @@ def main(cfg): provenance = get_provenance_record(inputfiles_df['filename'].to_list()) for trend_type in ['min', 'max']: - fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type, + fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type, provenance, cfg) # Save output save_figure(get_plot_filename(f'{trend_type}_trend', cfg), - provenance, cfg, figure=fig) + provenance, + cfg, + figure=fig) def get_provenance_record(ancestor_files): """Build provenance record.""" record = { 'ancestors': ancestor_files, - 'authors': [ - 'chun_felicity', - 'steketee_anton' - ], + 'authors': ['chun_felicity', 'steketee_anton'], 'caption': 'added 1652 years to model years for comparability', 'domains': ['shpolar'], 'plot_types': ['times'], From 21be9a8724b5c6aff777fd81c3b000268712447d Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 28 Jun 2024 12:31:55 +1000 Subject: [PATCH 12/27] remove blank line --- esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py | 1 - 1 file changed, 1 deletion(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 9e638a7d09..49d3116df3 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -74,7 +74,6 @@ def plot_trend(model_min_max, obs_a, minmax, prov, cfg): obs_a: xarray of observations area minmax: 'min' or 'max' """ - figure, _axes = plt.subplots() # add note for years change From 87a6116fab4e6ea8405b85cb1e681c40baf6fdca Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 28 Jun 2024 12:36:40 +1000 Subject: [PATCH 13/27] codacy docstring --- .../diag_scripts/seaice_area_extents/seaicearea_trends.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 49d3116df3..e67b219ca5 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -65,8 +65,7 @@ def min_and_max_year(yeardata): def plot_trend(model_min_max, obs_a, minmax, prov, cfg): - """ - function to plot min or max trend + """Plot min or max trend. Parameters ---------- From 78d7e847e0840993d1bafbb97106abd7e16b919f Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 28 Jun 2024 12:43:36 +1000 Subject: [PATCH 14/27] docstring edit --- .../seaice_area_extents/seaicearea_trends.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index e67b219ca5..0b441c42b5 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -67,11 +67,12 @@ def min_and_max_year(yeardata): def plot_trend(model_min_max, obs_a, minmax, prov, cfg): """Plot min or max trend. - Parameters - ---------- - model_min_max: dictionary of model label and xarray ds with min and max. - obs_a: xarray of observations area - minmax: 'min' or 'max' + Keyword arguments: + model_min_max -- dictionary of model label and xarray ds with min and max. + obs_a -- xarray of observations area + minmax -- 'min' or 'max' str + prov -- provenance record for saving data + cfg -- for saving data """ figure, _axes = plt.subplots() From 9495f3fe8c9f32c00d08fdab52c72d24f4ca37af Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 12 Jul 2024 16:22:02 +1000 Subject: [PATCH 15/27] minor code edits --- .../diag_scripts/seaice_area_extents/seaice_mapextents.py | 8 ++++---- .../diag_scripts/seaice_area_extents/seaicearea_trends.py | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index a916ed5715..99ef870bdb 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -65,9 +65,9 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, latmax) # save plot data - save_data(mod_label + calendar.month_abbr[mon] + '_mean', prov, + save_data(''.join([mod_label, calendar.month_abbr[mon], '_mean']), prov, cfg, mod_regrid.to_iris()) - save_data(mod_label + calendar.month_abbr[mon] + '_obs_diff', prov, + save_data(''.join([mod_label, calendar.month_abbr[mon], '_obs_diff']), prov, cfg, diff_ds.to_iris()) axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) @@ -82,7 +82,7 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): ax=axes, colors=['black']) - plt.title(calendar.month_abbr[mon] + ' ' + mod_label) + plt.title(' '.join([calendar.month_abbr[mon], mod_label])) i += 1 j += 1 @@ -121,7 +121,7 @@ def main(cfg): inputs_df = pd.DataFrame(data, columns=['filename', 'dataset']) - logger.info(inputs_df) + logger.info("input siconc data:", inputs_df) mod_si_dict = {} for filepath, data_name in inputs_df.itertuples(index=False): if data_name == 'NSIDC-G02202-sh': diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 0b441c42b5..593a1e7446 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -35,6 +35,7 @@ def sea_ice_area_obs(xdataset): ['x', 'y']).to_dataset(name='cdr_area') # Theres a couple of data gaps which should be nan + # if result.isin([{'time': '1988-01-01'},{'time': '1987-12'}]): result.loc[{'time': '1988-01-01'}] = np.nan result.loc[{'time': '1987-12'}] = np.nan @@ -80,7 +81,7 @@ def plot_trend(model_min_max, obs_a, minmax, prov, cfg): for mod_label, model_min_max_dt in model_min_max.items(): model_min_max_dt[minmax].plot(label=mod_label) # save data - save_data(mod_label + minmax, prov, cfg, + save_data(''.join([mod_label, minmax]), prov, cfg, model_min_max_dt[minmax].to_iris()) if minmax == 'max': From 0bfbd99abc5ebc3044297e82a67f2d6d58343e31 Mon Sep 17 00:00:00 2001 From: Felicity Chun <32269066+flicj191@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:00:46 +1000 Subject: [PATCH 16/27] Apply suggestions from review Co-authored-by: Anton Steketee <79179784+anton-seaice@users.noreply.github.com> --- doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst | 2 +- .../diag_scripts/seaice_area_extents/seaice_mapextents.py | 5 ++--- .../diag_scripts/seaice_area_extents/seaicearea_trends.py | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst b/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst index e4ade3e849..a7466a642d 100644 --- a/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst +++ b/doc/sphinx/source/recipes/recipe_seaice_extents_sh.rst @@ -54,7 +54,7 @@ instructions.* References ---------- -* COSIMA(Consortium for Ocean-Sea Ice Modelling in Australia) recipe: https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples/SeaIce_Obs_Model_Compare.html +https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html Example plots ------------- diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 99ef870bdb..5e61febe05 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -1,8 +1,7 @@ """Diagnostic script to plot extent and differences. -based on code from Anton Steketee's COSIMA cookbook notebook -https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples - /SeaIce_Obs_Model_Compare.html +based on code from Anton Steketee's COSIMA recipes notebook +https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html """ import logging diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 593a1e7446..e37dfb5545 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -1,7 +1,7 @@ """Diagnostic script to plot minima and maxima trends. based on code from Anton Steketee's COSIMA cookbook notebook -https://cosima-recipes.readthedocs.io/en/latest/DocumentedExamples +https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html /SeaIce_Obs_Model_Compare.html """ From 704e769e284242159e4a7b9e9b9753b1121ef6e4 Mon Sep 17 00:00:00 2001 From: Felicity Chun <32269066+flicj191@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:01:34 +1000 Subject: [PATCH 17/27] Update esmvaltool/config-references.yml Co-authored-by: Anton Steketee <79179784+anton-seaice@users.noreply.github.com> --- esmvaltool/config-references.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/esmvaltool/config-references.yml b/esmvaltool/config-references.yml index 2b54910e05..9ee87cbcac 100644 --- a/esmvaltool/config-references.yml +++ b/esmvaltool/config-references.yml @@ -730,7 +730,8 @@ authors: steketee_anton: name: Steketee, Anton institute: ACCESS-NRI - orcid: + orcid: https://orcid.org/0009-0002-9081-4106 + github: anton-seaice # Former viewers (not active viewers) adeniyi_kemisola: name: Adeniyi, Kemisola From 547ae6361127454ddaaea7f1c7c82ccfee45a300 Mon Sep 17 00:00:00 2001 From: Felicity Chun <32269066+flicj191@users.noreply.github.com> Date: Mon, 15 Jul 2024 15:06:04 +1000 Subject: [PATCH 18/27] Apply enumerate Co-authored-by: Anton Steketee <79179784+anton-seaice@users.noreply.github.com> --- .../diag_scripts/seaice_area_extents/seaice_mapextents.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 5e61febe05..3befb50a18 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -55,11 +55,11 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): figure = plt.figure(figsize=(9, len(months) * 4)) j = 0 # to iterate through positions on figure - for mon in months: + for j, mon in enumerate(months): cdr = obs_si.siconc.sel( time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') i = 1 - for mod_label, mod_si in mod_si_ls.items(): + for i, (mod_label, mod_si) in enumerate(mod_si_ls.items()): diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, latmax) From a63fa68731241689c79fc175bc3f33e7333c8165 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Tue, 16 Jul 2024 09:33:41 +1000 Subject: [PATCH 19/27] edits --- .../seaice_area_extents/seaice_mapextents.py | 16 ++++++---------- .../seaice_area_extents/seaicearea_trends.py | 18 +++++++++--------- .../recipes/recipe_seaice_extents_sh.yml | 12 ++++++++---- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 3befb50a18..8d464edf62 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -53,21 +53,20 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): # fig set up, width for 2 models, check len mod_si_ls figure = plt.figure(figsize=(9, len(months) * 4)) - j = 0 # to iterate through positions on figure for j, mon in enumerate(months): cdr = obs_si.siconc.sel( time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') - i = 1 - for i, (mod_label, mod_si) in enumerate(mod_si_ls.items()): + + for i, (mod_label, mod_si) in enumerate(mod_si_ls.items(), start=1): diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, latmax) # save plot data - save_data(''.join([mod_label, calendar.month_abbr[mon], '_mean']), prov, - cfg, mod_regrid.to_iris()) - save_data(''.join([mod_label, calendar.month_abbr[mon], '_obs_diff']), prov, - cfg, diff_ds.to_iris()) + save_data(''.join([mod_label, calendar.month_abbr[mon], '_mean']), + prov, cfg, mod_regrid.to_iris()) + save_data(''.join([mod_label, calendar.month_abbr[mon], '_obs_diff']), + prov, cfg, diff_ds.to_iris()) axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) @@ -83,9 +82,6 @@ def map_diff(mod_si_ls, obs_si, months, cfg, prov): plt.title(' '.join([calendar.month_abbr[mon], mod_label])) - i += 1 - j += 1 - line_cdr = mlines.Line2D([], [], color=cs_cdr.collections[0].get_edgecolor(), label="Observed Extent") diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index e37dfb5545..4db1f1753f 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -2,7 +2,6 @@ based on code from Anton Steketee's COSIMA cookbook notebook https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html - /SeaIce_Obs_Model_Compare.html """ import logging @@ -35,9 +34,9 @@ def sea_ice_area_obs(xdataset): ['x', 'y']).to_dataset(name='cdr_area') # Theres a couple of data gaps which should be nan - # if result.isin([{'time': '1988-01-01'},{'time': '1987-12'}]): - result.loc[{'time': '1988-01-01'}] = np.nan - result.loc[{'time': '1987-12'}] = np.nan + if result.isin([{'time': '1988-01-01'},{'time': '1987-12'}]): + result.loc[{'time': '1988-01-01'}] = np.nan + result.loc[{'time': '1987-12'}] = np.nan return result.sel(time=slice('1979', '2018')) @@ -132,10 +131,11 @@ def main(cfg): if dt_label == data_name: mod_si['areacello'] = area_mod['areacello'] model_area_dt = sea_ice_area_model_sh(mod_si) - model_min_max_dt = min_and_max(model_area_dt) - # make years in ACCESS model compariable - model_min_max_dt['year'] = model_min_max_dt.year + 1652 - min_max[data_name] = model_min_max_dt + model_minmax_dt = min_and_max(model_area_dt) + # make years in ACCESS OM2 model compariable (366 is 2018) + if model_minmax_dt['year'].max().values <= 366: + model_minmax_dt['year'] = model_minmax_dt.year + 1652 + min_max[data_name] = model_minmax_dt else: logger.warning("..%s missing a variable?", data_name) @@ -157,7 +157,7 @@ def get_provenance_record(ancestor_files): record = { 'ancestors': ancestor_files, 'authors': ['chun_felicity', 'steketee_anton'], - 'caption': 'added 1652 years to model years for comparability', + 'caption': 'added 1652 years to OM2 model years for comparability', 'domains': ['shpolar'], 'plot_types': ['times'], 'references': ['access_nri'], diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index 84682bd48a..33afde20aa 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -23,14 +23,18 @@ documentation: - access-nri datasets: -# these years are comparable to 1958 -2018 # re-adjust years in script (+1652) + - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, - exp: piControl, ensemble: r1i1p1f1, start_year: 306, end_year: 366} + exp: historical, ensemble: r1i1p1f1, start_year: 1981, end_year: 2010} + +# the years 306-366 are comparable to 1958 -2018 +# re-adjust years in trends script (+1652) - {dataset: ACCESS-OM2, activity: OMIP ,project: CMIP6, grid: gn, - exp: omip2, ensemble: r1i1p1f1, start_year: 306, end_year: 366} + exp: omip2, ensemble: r1i1p1f1, start_year: 329, end_year: 358} + # observations - {dataset: NSIDC-G02202-sh, project: OBS6, tier: 3, - type: reanaly, version: 4, start_year: 1979, end_year: 2018} + type: reanaly, version: 4, start_year: 1981, end_year: 2010} #- 2018 diagnostics: From 1760bec2299753b714e9e51d34152bd67c596157 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Tue, 23 Jul 2024 10:02:46 +1000 Subject: [PATCH 20/27] preprocessers for trends --- .../recipes/recipe_seaice_extents_sh.yml | 93 ++++++++++++++++--- 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index 33afde20aa..bc510f869a 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -22,37 +22,104 @@ documentation: projects: - access-nri +# datasets: + +# - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, +# exp: historical, ensemble: r1i1p1f1, start_year: 1981, end_year: 2010} + +# # the years 306-366 are comparable to 1958 -2018 +# # re-adjust years in trends script (+1652) +# - {dataset: ACCESS-OM2, activity: OMIP ,project: CMIP6, grid: gn, +# exp: omip2, ensemble: r1i1p1f1, start_year: 329, end_year: 358} + +# # observations +# - {dataset: NSIDC-G02202-sh, project: OBS6, tier: 3, +# type: reanaly, version: 4, start_year: 1981, end_year: 2010} #- 2018 + + +# diagnostics: + +# sea_ice_sh: +# description: sea ice area and mapping sea ice concentration +# variables: +# ## 2 variables - sea ice concentration and cell area to compute sea ice area +# si_fraction: +# short_name: siconc +# mip: SImon +# area: +# short_name: areacello +# mip: Ofx +# scripts: +# map_extents: +# months: [2,9] # months to map (feb, sep) +# script: seaice_area_extents/seaice_mapextents.py +# trends: +# script: seaice_area_extents/seaicearea_trends.py + +preprocessors: + trends_min: &trends + mask_outside_range: + minimum: 15 + maximum: 100 + extract_region: + start_longitude: 0 + end_longitude: 360 + start_latitude: -90 + end_latitude: 0 + area_statistics: + operator: sum + annual_statistics: + operator: min + convert_units: + units: km2 + + trends_max: + <<: *trends + annual_statistics: + operator: max + convert_units: + units: km2 + datasets: - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, - exp: historical, ensemble: r1i1p1f1, start_year: 1981, end_year: 2010} + exp: historical, ensemble: r1i1p1f1, start_year: 1981, end_year: 2010} # the years 306-366 are comparable to 1958 -2018 # re-adjust years in trends script (+1652) - {dataset: ACCESS-OM2, activity: OMIP ,project: CMIP6, grid: gn, - exp: omip2, ensemble: r1i1p1f1, start_year: 329, end_year: 358} + exp: omip2, ensemble: r1i1p1f1, start_year: 329, end_year: 358, offset_years: 1652} # observations - {dataset: NSIDC-G02202-sh, project: OBS6, tier: 3, - type: reanaly, version: 4, start_year: 1981, end_year: 2010} #- 2018 + type: reanaly, version: 4, start_year: 1981, end_year: 2010, + supplementary_variables: [{short_name: areacello, mip: Ofx}] + } #- 2018 diagnostics: - sea_ice_sh: - description: sea ice area and mapping sea ice concentration + sea_ice_extents_sh: + description: sea ice extents variables: - ## 2 variables - sea ice concentration and cell area to compute sea ice area - si_fraction: - short_name: siconc + siconc: mip: SImon - area: - short_name: areacello - mip: Ofx scripts: map_extents: months: [2,9] # months to map (feb, sep) script: seaice_area_extents/seaice_mapextents.py + sea_ice_trends_sh: + description: sea ice trends + variables: + siconc_min: + short_name: siconc + mip: SImon + preprocessor: trends_min + siconc_max: + short_name: siconc + mip: SImon + preprocessor: trends_max + scripts: trends: - script: seaice_area_extents/seaicearea_trends.py - \ No newline at end of file + script: /home/189/fc6164/esmValTool/working_nb/seaice_wip/seaicearea_trends_cube.py + # script: seaice_area_extents/seaicearea_trends.py \ No newline at end of file From 92ec3302229f3e3e6b52996b2c128342f4eed3ff Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Thu, 29 Aug 2024 13:44:14 +1000 Subject: [PATCH 21/27] change trends script for preprocessed data --- .../seaice_area_extents/seaicearea_trends.py | 197 +++++------------- .../recipes/recipe_seaice_extents_sh.yml | 39 +--- 2 files changed, 49 insertions(+), 187 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 4db1f1753f..d07fdd3e71 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -4,166 +4,65 @@ https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html """ -import logging -import os import matplotlib.pyplot as plt -import numpy as np -import pandas as pd -import xarray as xr +from iris import quickplot + +import iris +import os +import logging +from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure -from esmvaltool.diag_scripts.shared import (run_diagnostic, save_figure, - save_data) -from esmvaltool.diag_scripts.shared._base import get_plot_filename # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) -def sea_ice_area(sic, area, coordls): - """Percent sic is in 0-100. Mulitply by portion so divide by 100.""" - sic = sic / 100 - # valid sic between 0.15 and 1 - return (sic * area).where((sic >= 0.15) * (sic <= 1)).sum(coordls) - - -def sea_ice_area_obs(xdataset): - """Compute sea ice area for obs dataset.""" - sic = xdataset.siconc - area_km2 = xdataset.areacello / 1e6 - result = sea_ice_area(sic, area_km2, - ['x', 'y']).to_dataset(name='cdr_area') - - # Theres a couple of data gaps which should be nan - if result.isin([{'time': '1988-01-01'},{'time': '1987-12'}]): - result.loc[{'time': '1988-01-01'}] = np.nan - result.loc[{'time': '1987-12'}] = np.nan - - return result.sel(time=slice('1979', '2018')) - - -def sea_ice_area_model_sh(xdataset): - """Compute sea ice area for model dataset.""" - sic = xdataset.siconc.where(xdataset.siconc.lat < -20, drop=True) - - area_km2 = xdataset.areacello / 1e6 # area convert to km2 - - return sea_ice_area(sic, area_km2, ['i', 'j']).to_dataset(name='si_area') - - -def min_and_max(dataset): - """Compute min and max for dataset.""" - - def min_and_max_year(yeardata): - result = xr.Dataset() - result['min'] = yeardata.min() - result['max'] = yeardata.max() - return result - - annual_min_max_ds = dataset.si_area.groupby('time.year').apply( - min_and_max_year) - return annual_min_max_ds - - -def plot_trend(model_min_max, obs_a, minmax, prov, cfg): - """Plot min or max trend. - - Keyword arguments: - model_min_max -- dictionary of model label and xarray ds with min and max. - obs_a -- xarray of observations area - minmax -- 'min' or 'max' str - prov -- provenance record for saving data - cfg -- for saving data - """ - figure, _axes = plt.subplots() - - # add note for years change - for mod_label, model_min_max_dt in model_min_max.items(): - model_min_max_dt[minmax].plot(label=mod_label) - # save data - save_data(''.join([mod_label, minmax]), prov, cfg, - model_min_max_dt[minmax].to_iris()) - - if minmax == 'max': - obs_a.cdr_area.groupby('time.year').max().plot(label='Obs CDR') - plt.title('Trends in Sea-Ice Maxima') - elif minmax == 'min': - obs_a.cdr_area.groupby('time.year').min().plot(label='Obs CDR') - plt.title('Trends in Sea-Ice Minima') - - plt.ylabel('Sea-Ice Area (km2)') - - _ = plt.legend() - return figure - +def _prom_dim_coord(cube, _field, _filename): + iris.util.promote_aux_coord_to_dim_coord(cube, 'year') def main(cfg): """Compute sea ice area for each input dataset.""" - data = [] - - for dataset in cfg['input_data'].values(): - # data values to iterate - logger.info("dataset: %s", dataset['long_name']) - data.append( - [dataset['filename'], dataset['short_name'], dataset['dataset']]) - - inputfiles_df = pd.DataFrame(data, - columns=['filename', 'short_name', 'dataset']) - # sort to ensure order of reading - inputfiles_df.sort_values(['dataset', 'short_name'], inplace=True) - logger.info(inputfiles_df[['short_name', 'dataset']]) - - min_max = {} - - for filepath, short, data_name in inputfiles_df.itertuples(index=False): - if data_name == 'NSIDC-G02202-sh': - if short == 'areacello': - area_obs = xr.open_dataset(filepath) - else: - obs_si = xr.open_dataset(filepath) - else: # other models - if short == 'areacello': - area_mod = xr.open_dataset(filepath) - dt_label = data_name - else: - mod_si = xr.open_dataset(filepath) - - # make sure correct area with model - if dt_label == data_name: - mod_si['areacello'] = area_mod['areacello'] - model_area_dt = sea_ice_area_model_sh(mod_si) - model_minmax_dt = min_and_max(model_area_dt) - # make years in ACCESS OM2 model compariable (366 is 2018) - if model_minmax_dt['year'].max().values <= 366: - model_minmax_dt['year'] = model_minmax_dt.year + 1652 - min_max[data_name] = model_minmax_dt - else: - logger.warning("..%s missing a variable?", data_name) - - obs_si['areacello'] = area_obs['areacello'] - - provenance = get_provenance_record(inputfiles_df['filename'].to_list()) - for trend_type in ['min', 'max']: - fig = plot_trend(min_max, sea_ice_area_obs(obs_si), trend_type, - provenance, cfg) - # Save output - save_figure(get_plot_filename(f'{trend_type}_trend', cfg), - provenance, - cfg, - figure=fig) - - -def get_provenance_record(ancestor_files): - """Build provenance record.""" - record = { - 'ancestors': ancestor_files, - 'authors': ['chun_felicity', 'steketee_anton'], - 'caption': 'added 1652 years to OM2 model years for comparability', - 'domains': ['shpolar'], - 'plot_types': ['times'], - 'references': ['access_nri'], - 'statistics': ['mean'], + provenance_record = { + 'caption': "sea ice trends southern hemisphere", + 'authors': [ + 'chun_felicity', + ], + 'references': [''], + 'ancestors': list(cfg['input_data'].keys()), } - return record + input_data = cfg['input_data'].values() + + datagroup = {} ## for each variable min and max + + for dataset in input_data: + # Load the data + if 'offset_years' in dataset: + input_file = (dataset['filename'], dataset['short_name'], dataset['dataset'], dataset['offset_years']) + else: + input_file = (dataset['filename'], dataset['short_name'], dataset['dataset'], None) + # key for different models + logger.info(f"dataset: {dataset['long_name']}") + if dataset['variable_group'] not in datagroup: + datagroup[dataset['variable_group']] = [] + datagroup[dataset['variable_group']].append(input_file) + + logger.info(datagroup) + + for variable_group, attributes in datagroup.items(): + plt.clf() + for (fp, sn, dt, offset) in attributes: + cube = iris.load_cube(fp,sn,_prom_dim_coord) + if offset: + cube.coord('year').points = [y + offset for y in cube.coord('year').points] + quickplot.plot(cube, label=dt) + + plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") # + plt.legend(loc='upper left') + plt.ylabel('Sea-Ice Area (km2)') + + filename = variable_group + + save_figure(filename, provenance_record, cfg, dpi=300) if __name__ == '__main__': diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index bc510f869a..c6cc07eda6 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -4,8 +4,6 @@ documentation: title: SH sea ice area recipe description: | - This is an example recipe, shared in - CMIP7 hackathon 2024, CSIRO Aspendale (Melbourne). Converted a COSIMA cookbook recipe with help from Anton Steketee @@ -22,40 +20,6 @@ documentation: projects: - access-nri -# datasets: - -# - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, -# exp: historical, ensemble: r1i1p1f1, start_year: 1981, end_year: 2010} - -# # the years 306-366 are comparable to 1958 -2018 -# # re-adjust years in trends script (+1652) -# - {dataset: ACCESS-OM2, activity: OMIP ,project: CMIP6, grid: gn, -# exp: omip2, ensemble: r1i1p1f1, start_year: 329, end_year: 358} - -# # observations -# - {dataset: NSIDC-G02202-sh, project: OBS6, tier: 3, -# type: reanaly, version: 4, start_year: 1981, end_year: 2010} #- 2018 - - -# diagnostics: - -# sea_ice_sh: -# description: sea ice area and mapping sea ice concentration -# variables: -# ## 2 variables - sea ice concentration and cell area to compute sea ice area -# si_fraction: -# short_name: siconc -# mip: SImon -# area: -# short_name: areacello -# mip: Ofx -# scripts: -# map_extents: -# months: [2,9] # months to map (feb, sep) -# script: seaice_area_extents/seaice_mapextents.py -# trends: -# script: seaice_area_extents/seaicearea_trends.py - preprocessors: trends_min: &trends mask_outside_range: @@ -121,5 +85,4 @@ diagnostics: preprocessor: trends_max scripts: trends: - script: /home/189/fc6164/esmValTool/working_nb/seaice_wip/seaicearea_trends_cube.py - # script: seaice_area_extents/seaicearea_trends.py \ No newline at end of file + script: seaice_area_extents/seaicearea_trends.py \ No newline at end of file From b51c995bacb972ea245aec463bab7aebc4b9cbc9 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Fri, 30 Aug 2024 13:50:02 +1000 Subject: [PATCH 22/27] clean up script, add preprocessor for map --- .../seaice_area_extents/seaicearea_trends.py | 21 ++++++++++--------- .../recipes/recipe_seaice_extents_sh.yml | 16 ++++++++++++++ 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index d07fdd3e71..adeb62a0b5 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -32,15 +32,17 @@ def main(cfg): } input_data = cfg['input_data'].values() - datagroup = {} ## for each variable min and max + datagroup = {} # for each variable min and max for dataset in input_data: # Load the data if 'offset_years' in dataset: - input_file = (dataset['filename'], dataset['short_name'], dataset['dataset'], dataset['offset_years']) + input_file = (dataset['filename'], dataset['short_name'], + dataset['dataset'], dataset['offset_years']) else: - input_file = (dataset['filename'], dataset['short_name'], dataset['dataset'], None) - # key for different models + input_file = (dataset['filename'], dataset['short_name'], + dataset['dataset'], None) + # key for different models logger.info(f"dataset: {dataset['long_name']}") if dataset['variable_group'] not in datagroup: datagroup[dataset['variable_group']] = [] @@ -51,18 +53,17 @@ def main(cfg): for variable_group, attributes in datagroup.items(): plt.clf() for (fp, sn, dt, offset) in attributes: - cube = iris.load_cube(fp,sn,_prom_dim_coord) + cube = iris.load_cube(fp, sn, _prom_dim_coord) if offset: - cube.coord('year').points = [y + offset for y in cube.coord('year').points] + cube.coord('year').points = [y + offset for y in + cube.coord('year').points] quickplot.plot(cube, label=dt) - plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") # + plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") plt.legend(loc='upper left') plt.ylabel('Sea-Ice Area (km2)') - filename = variable_group - - save_figure(filename, provenance_record, cfg, dpi=300) + save_figure(variable_group, provenance_record, cfg, dpi=300) if __name__ == '__main__': diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index c6cc07eda6..340f07b5b9 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -44,6 +44,21 @@ preprocessors: convert_units: units: km2 + map_extents: + regrid: + target_grid: 0.5x0.5 + scheme: linear + climate_statistics: + operator: mean + period: month + # extract_month: # move to script for multiple? + # month: 2 + extract_region: + start_longitude: 0 + end_longitude: 360 + start_latitude: -90 + end_latitude: -50 + datasets: - {dataset: ACCESS-ESM1-5, activity: CMIP ,project: CMIP6, grid: gn, @@ -68,6 +83,7 @@ diagnostics: variables: siconc: mip: SImon + preprocessor: map_extents scripts: map_extents: months: [2,9] # months to map (feb, sep) From cb64a3380711e707063dfb8c0df45b2817e6b267 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Tue, 10 Sep 2024 15:37:00 +1000 Subject: [PATCH 23/27] edit map preprocessors --- esmvaltool/recipes/recipe_seaice_extents_sh.yml | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index 340f07b5b9..2b30ff7517 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -45,19 +45,17 @@ preprocessors: units: km2 map_extents: - regrid: - target_grid: 0.5x0.5 - scheme: linear climate_statistics: operator: mean - period: month - # extract_month: # move to script for multiple? - # month: 2 - extract_region: + period: monthly + extract_region: start_longitude: 0 end_longitude: 360 start_latitude: -90 - end_latitude: -50 + end_latitude: -35 + regrid: + target_grid: 0.5x0.5 + scheme: linear datasets: From a5204f505a9acdd3edd4ae2c8f52ce8d25d3ab3c Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 11 Sep 2024 15:42:04 +1000 Subject: [PATCH 24/27] change to method to create extents figure --- .../seaice_area_extents/seaice_mapextents.py | 174 +++++++----------- .../seaice_area_extents/seaicearea_trends.py | 34 ++-- .../recipes/recipe_seaice_extents_sh.yml | 3 +- 3 files changed, 85 insertions(+), 126 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 8d464edf62..9f7ec7595c 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -5,98 +5,62 @@ """ import logging -import os import calendar from cartopy.crs import SouthPolarStereo -import xarray as xr -import xesmf + import matplotlib.pyplot as plt import matplotlib.lines as mlines import numpy as np -import pandas as pd +from esmvalcore.preprocessor import extract_month from esmvaltool.diag_scripts.shared import (run_diagnostic, save_figure, - save_data) -from esmvaltool.diag_scripts.shared._base import get_plot_filename + group_metadata, save_data) # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) -def model_regrid_diff(mod_si, obs_si, cdr, mon, latmax): - """Regrid and compute difference grid.""" - lat_min = mod_si.lat.min().values.item() - mod_si = mod_si.where(mod_si.lat < latmax, drop=True) - regrd_out = obs_si.where(obs_si.lat > lat_min, drop=True) - - # regrid model data to observations - regridder_access_sh = xesmf.Regridder(mod_si.isel(time=0).drop(['i', 'j']), - regrd_out.isel(time=0), - 'bilinear', - periodic=True, - unmapped_to_nan=True) - - model_mean = mod_si.siconc.sel( - time=mod_si.siconc.time.dt.month.isin(mon)).mean('time') - mod_regrid = regridder_access_sh(model_mean) - diff_ds = mod_regrid - cdr - - return diff_ds, mod_regrid - - -def map_diff(mod_si_ls, obs_si, months, cfg, prov): - """Create figure mapping extents for models and months.""" - # get lat max for regridding - latmax = obs_si.lat.max().values.item() - - proj = SouthPolarStereo(true_scale_latitude=-70) - - # fig set up, width for 2 models, check len mod_si_ls - figure = plt.figure(figsize=(9, len(months) * 4)) - - for j, mon in enumerate(months): - cdr = obs_si.siconc.sel( - time=obs_si.siconc.time.dt.month.isin(mon)).mean('time') - - for i, (mod_label, mod_si) in enumerate(mod_si_ls.items(), start=1): - - diff_ds, mod_regrid = model_regrid_diff(mod_si, obs_si, cdr, mon, - latmax) - # save plot data - save_data(''.join([mod_label, calendar.month_abbr[mon], '_mean']), - prov, cfg, mod_regrid.to_iris()) - save_data(''.join([mod_label, calendar.month_abbr[mon], '_obs_diff']), - prov, cfg, diff_ds.to_iris()) - - axes = plt.subplot(len(months), 3, i + j * 3, projection=proj) - - diffmap = axes.contourf(diff_ds.x, - diff_ds.y, - diff_ds, - levels=np.arange(-90, 91, 20), - cmap='RdBu') - cs_cdr = cdr.plot.contour(levels=[15], ax=axes) - cs_mod = mod_regrid.plot.contour(levels=[15], - ax=axes, - colors=['black']) - - plt.title(' '.join([calendar.month_abbr[mon], mod_label])) - - line_cdr = mlines.Line2D([], [], - color=cs_cdr.collections[0].get_edgecolor(), - label="Observed Extent") - - line_mod = mlines.Line2D([], [], - color=cs_mod.collections[0].get_edgecolor(), - label="Modelled Extent") - - plt.legend(handles=[line_cdr, line_mod], - loc='center left', - bbox_to_anchor=(1.2, 0.5)) - cax = plt.axes([0.7, 0.55, 0.04, 0.3]) - _ = plt.colorbar(diffmap, - cax=cax, - label='Difference in \nSea Ice Concentration') +def map_fig_diff(mod_dict, obs_si, months): + """Create map with model dictionary: labels, cubes""" + + # fig set up, width for 2 models, check len mod_dict + figure = plt.figure(figsize=(9,len(months)*3.5)) + j=0 # to iterate through positions on figure + + for mon in months: #[2,9] + i=1 + for mod_label, mod_si in mod_dict.items(): #iterate over model subset + + mod_cube = extract_month(mod_si, mon) + obs_cube = extract_month(obs_si, mon) + + out = mod_cube.copy() + diff = mod_cube.data - obs_cube.data + out.data = diff + + ax = plt.subplot(len(months), 3, i+j*3, projection=crs.SouthPolarStereo(true_scale_latitude=-70)) + + diffmap = iplt.contourf(out, levels=np.arange(-90,91,20), cmap='RdBu') #? + + iplt.contour(obs_cube, levels=[15], colors=['yellow']) + iplt.contour(mod_cube, levels=[15], linewidths=1.0, colors=['black']) + + plt.title(calendar.month_abbr[mon]+' '+mod_label) + + i+=1 + j+=1 + + line_cdr = mlines.Line2D([], [], color='yellow', label="Observed Extent") + line_mod = mlines.Line2D([], [], color='black', label="Modelled Extent") + + plt.legend(handles=[line_cdr,line_mod], loc='center left', bbox_to_anchor=(1.2,0.5)) + cax = plt.axes([0.7,0.55,0.04,0.3]) + _ = plt.colorbar(diffmap, cax=cax, label='Difference in \nSea Ice Concentration') + + plt.subplots_adjust(left=0.05, bottom=0.05, + right=0.95, top=0.95, + wspace=0.05, hspace=0.05) + return figure @@ -104,36 +68,26 @@ def main(cfg): """Compute.""" # Get a description of the preprocessed data that we will use as input. input_data = cfg['input_data'].values() - data = [] - - # Find input datasets to use - for dataset in input_data: - - input_file = [dataset['filename'], dataset['dataset']] - # drop areacello dataset for map - if dataset['short_name'] == 'siconc': - data.append(input_file) - - inputs_df = pd.DataFrame(data, columns=['filename', 'dataset']) - - logger.info("input siconc data:", inputs_df) - mod_si_dict = {} - for filepath, data_name in inputs_df.itertuples(index=False): - if data_name == 'NSIDC-G02202-sh': - # Load the data - obs_si = xr.open_dataset(filepath) - else: - mod_si_dict[data_name] = xr.open_dataset(filepath) - - logger.info("creating map differences") - provenance_record = get_provenance_record(inputs_df['filename'].to_list()) - - mapfig = map_diff(mod_si_dict, obs_si, cfg['months'], cfg, - provenance_record) - # Save output - output_path = get_plot_filename('map_difference', cfg) - - save_figure(output_path, provenance_record, cfg, figure=mapfig) + + groups = group_metadata(input_data, 'variable_group', sort='project') + for group_name in groups: + mod_dict={} + ancestor_filels = [] + logger.info("Processing variable %s", group_name) + for attributes in groups[group_name]: + if attributes['project'].startswith('OBS'): + logger.info("Processing OBS dataset %s", attributes['dataset']) + obs_si = iris.load_cube(attributes['filename']) + else: + logger.info("Processing dataset %s", attributes['dataset']) + mod_dict[attributes['dataset']] = iris.load_cube(attributes['filename']) + ancestor_filels.append(attributes['filename']) + + logger.info("creating map differences") + mapfig = map_fig_diff(mod_dict, obs_si, cfg['months']) + + provenance_record = get_provenance_record(ancestor_filels) + save_figure(group_name, provenance_record, cfg, figure=mapfig) def get_provenance_record(ancestor_files): diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index adeb62a0b5..d69a99d141 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -20,12 +20,30 @@ def _prom_dim_coord(cube, _field, _filename): iris.util.promote_aux_coord_to_dim_coord(cube, 'year') +def plot_trends(datagroup, provenance_record, cfg): + """Create plot for min and max groups""" + for variable_group, attributes in datagroup.items(): + plt.clf() + for (fp, sn, dt, offset) in attributes: + cube = iris.load_cube(fp, sn, _prom_dim_coord) + if offset: + cube.coord('year').points = [y + offset for y in + cube.coord('year').points] + quickplot.plot(cube, label=dt) + + plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") + plt.legend(loc='upper left') + plt.ylabel('Sea-Ice Area (km2)') + + save_figure(variable_group, provenance_record, cfg, dpi=300) + def main(cfg): """Compute sea ice area for each input dataset.""" provenance_record = { 'caption': "sea ice trends southern hemisphere", 'authors': [ 'chun_felicity', + 'steketee_anton' ], 'references': [''], 'ancestors': list(cfg['input_data'].keys()), @@ -49,21 +67,7 @@ def main(cfg): datagroup[dataset['variable_group']].append(input_file) logger.info(datagroup) - - for variable_group, attributes in datagroup.items(): - plt.clf() - for (fp, sn, dt, offset) in attributes: - cube = iris.load_cube(fp, sn, _prom_dim_coord) - if offset: - cube.coord('year').points = [y + offset for y in - cube.coord('year').points] - quickplot.plot(cube, label=dt) - - plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") - plt.legend(loc='upper left') - plt.ylabel('Sea-Ice Area (km2)') - - save_figure(variable_group, provenance_record, cfg, dpi=300) + plot_trends(datagroup, provenance_record, cfg) if __name__ == '__main__': diff --git a/esmvaltool/recipes/recipe_seaice_extents_sh.yml b/esmvaltool/recipes/recipe_seaice_extents_sh.yml index 2b30ff7517..dcfc7e4a25 100644 --- a/esmvaltool/recipes/recipe_seaice_extents_sh.yml +++ b/esmvaltool/recipes/recipe_seaice_extents_sh.yml @@ -52,7 +52,7 @@ preprocessors: start_longitude: 0 end_longitude: 360 start_latitude: -90 - end_latitude: -35 + end_latitude: -50 regrid: target_grid: 0.5x0.5 scheme: linear @@ -86,6 +86,7 @@ diagnostics: map_extents: months: [2,9] # months to map (feb, sep) script: seaice_area_extents/seaice_mapextents.py + sea_ice_trends_sh: description: sea ice trends variables: From e1f697f9f722860d92171623834332f1d01ae3ac Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 11 Sep 2024 16:11:31 +1000 Subject: [PATCH 25/27] clean up --- .../seaice_area_extents/seaice_mapextents.py | 99 ++++++++++--------- .../seaice_area_extents/seaicearea_trends.py | 34 ++++--- 2 files changed, 72 insertions(+), 61 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index 9f7ec7595c..cf94ce1e47 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -4,17 +4,23 @@ https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html """ -import logging import calendar -from cartopy.crs import SouthPolarStereo +import logging +import os -import matplotlib.pyplot as plt +import iris +import iris.plot as iplt import matplotlib.lines as mlines +import matplotlib.pyplot as plt import numpy as np - +from cartopy.crs import SouthPolarStereo from esmvalcore.preprocessor import extract_month -from esmvaltool.diag_scripts.shared import (run_diagnostic, save_figure, - group_metadata, save_data) + +from esmvaltool.diag_scripts.shared import ( + group_metadata, + run_diagnostic, + save_figure, +) # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) @@ -22,66 +28,69 @@ def map_fig_diff(mod_dict, obs_si, months): """Create map with model dictionary: labels, cubes""" - - # fig set up, width for 2 models, check len mod_dict - figure = plt.figure(figsize=(9,len(months)*3.5)) - j=0 # to iterate through positions on figure - - for mon in months: #[2,9] - i=1 - for mod_label, mod_si in mod_dict.items(): #iterate over model subset - + # figure set up, width 9 for 2 models + figure = plt.figure(figsize=(9, len(months)*3.5)) + j = 0 # to iterate through positions on figure + + for mon in months: # eg.[2,9] + i = 1 + obs_cube = extract_month(obs_si, mon) + for mod_label, mod_si in mod_dict.items(): # iterate over models mod_cube = extract_month(mod_si, mon) - obs_cube = extract_month(obs_si, mon) - + out = mod_cube.copy() diff = mod_cube.data - obs_cube.data out.data = diff - - ax = plt.subplot(len(months), 3, i+j*3, projection=crs.SouthPolarStereo(true_scale_latitude=-70)) - - diffmap = iplt.contourf(out, levels=np.arange(-90,91,20), cmap='RdBu') #? - + + plt.subplot(len(months), 3, i+j*3, + projection=SouthPolarStereo(true_scale_latitude=-70)) + + diffmap = iplt.contourf(out, levels=np.arange(-90, 91, 20), + cmap='RdBu') + iplt.contour(obs_cube, levels=[15], colors=['yellow']) - iplt.contour(mod_cube, levels=[15], linewidths=1.0, colors=['black']) - - plt.title(calendar.month_abbr[mon]+' '+mod_label) + iplt.contour(mod_cube, levels=[15], + linewidths=1.0, colors=['black']) - i+=1 - j+=1 + plt.title(calendar.month_abbr[mon] + ' ' + mod_label) + + i += 1 + j += 1 line_cdr = mlines.Line2D([], [], color='yellow', label="Observed Extent") line_mod = mlines.Line2D([], [], color='black', label="Modelled Extent") - - plt.legend(handles=[line_cdr,line_mod], loc='center left', bbox_to_anchor=(1.2,0.5)) - cax = plt.axes([0.7,0.55,0.04,0.3]) - _ = plt.colorbar(diffmap, cax=cax, label='Difference in \nSea Ice Concentration') - - plt.subplots_adjust(left=0.05, bottom=0.05, - right=0.95, top=0.95, - wspace=0.05, hspace=0.05) + + plt.legend(handles=[line_cdr, line_mod], loc='center left', + bbox_to_anchor=(1.2, 0.5)) + cax = plt.axes([0.7, 0.55, 0.04, 0.3]) + _ = plt.colorbar(diffmap, cax=cax, + label='Difference in \nSea Ice Concentration') + + plt.subplots_adjust(left=0.05, bottom=0.05, + right=0.95, top=0.95, + wspace=0.05, hspace=0.05) return figure def main(cfg): """Compute.""" - # Get a description of the preprocessed data that we will use as input. + # Get the preprocessed data that we will use as input. input_data = cfg['input_data'].values() - + groups = group_metadata(input_data, 'variable_group', sort='project') for group_name in groups: - mod_dict={} + mod_dict = {} ancestor_filels = [] logger.info("Processing variable %s", group_name) - for attributes in groups[group_name]: - if attributes['project'].startswith('OBS'): - logger.info("Processing OBS dataset %s", attributes['dataset']) - obs_si = iris.load_cube(attributes['filename']) + for attr in groups[group_name]: + if attr['project'].startswith('OBS'): + logger.info("Load OBS dataset %s", attr['dataset']) + obs_si = iris.load_cube(attr['filename']) else: - logger.info("Processing dataset %s", attributes['dataset']) - mod_dict[attributes['dataset']] = iris.load_cube(attributes['filename']) - ancestor_filels.append(attributes['filename']) + logger.info("load model dataset %s", attr['dataset']) + mod_dict[attr['dataset']] = iris.load_cube(attr['filename']) + ancestor_filels.append(attr['filename']) logger.info("creating map differences") mapfig = map_fig_diff(mod_dict, obs_si, cfg['months']) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index d69a99d141..51732651e0 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -4,15 +4,15 @@ https://cosima-recipes.readthedocs.io/en/latest/Examples/Sea_Ice_Area_Concentration_Volume_with_Obs.html """ +import logging +import os + +import iris import matplotlib.pyplot as plt from iris import quickplot -import iris -import os -import logging from esmvaltool.diag_scripts.shared import run_diagnostic, save_figure - # This part sends debug statements to stdout logger = logging.getLogger(os.path.basename(__file__)) @@ -20,22 +20,24 @@ def _prom_dim_coord(cube, _field, _filename): iris.util.promote_aux_coord_to_dim_coord(cube, 'year') + def plot_trends(datagroup, provenance_record, cfg): """Create plot for min and max groups""" for variable_group, attributes in datagroup.items(): - plt.clf() - for (fp, sn, dt, offset) in attributes: - cube = iris.load_cube(fp, sn, _prom_dim_coord) - if offset: - cube.coord('year').points = [y + offset for y in - cube.coord('year').points] - quickplot.plot(cube, label=dt) + plt.clf() + for (fp, sn, dt, offset) in attributes: + cube = iris.load_cube(fp, sn, _prom_dim_coord) + if offset: + cube.coord('year').points = [y + offset for y in + cube.coord('year').points] + quickplot.plot(cube, label=dt) + + plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") + plt.legend(loc='upper left') + plt.ylabel('Sea-Ice Area (km2)') - plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") - plt.legend(loc='upper left') - plt.ylabel('Sea-Ice Area (km2)') + save_figure(variable_group, provenance_record, cfg, dpi=300) - save_figure(variable_group, provenance_record, cfg, dpi=300) def main(cfg): """Compute sea ice area for each input dataset.""" @@ -48,7 +50,7 @@ def main(cfg): 'references': [''], 'ancestors': list(cfg['input_data'].keys()), } - input_data = cfg['input_data'].values() + input_data = cfg['input_data'].values() datagroup = {} # for each variable min and max From abf49c4f54f66c45885909218586d8a7cd63826d Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 11 Sep 2024 16:36:59 +1000 Subject: [PATCH 26/27] codacy clean --- .../seaice_area_extents/seaice_mapextents.py | 22 ++++++++++++------- .../seaice_area_extents/seaicearea_trends.py | 8 +++---- 2 files changed, 18 insertions(+), 12 deletions(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py index cf94ce1e47..78a8c930da 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaice_mapextents.py @@ -18,6 +18,7 @@ from esmvaltool.diag_scripts.shared import ( group_metadata, + select_metadata, run_diagnostic, save_figure, ) @@ -26,23 +27,23 @@ logger = logging.getLogger(os.path.basename(__file__)) -def map_fig_diff(mod_dict, obs_si, months): - """Create map with model dictionary: labels, cubes""" +def map_fig_diff(model_dict, obs_si, months): + """Create map with model dictionary: labels, cubes.""" # figure set up, width 9 for 2 models - figure = plt.figure(figsize=(9, len(months)*3.5)) + figure = plt.figure(figsize=(9, len(months) * 3.5)) j = 0 # to iterate through positions on figure for mon in months: # eg.[2,9] i = 1 obs_cube = extract_month(obs_si, mon) - for mod_label, mod_si in mod_dict.items(): # iterate over models + for mod_label, mod_si in model_dict.items(): mod_cube = extract_month(mod_si, mon) out = mod_cube.copy() diff = mod_cube.data - obs_cube.data out.data = diff - plt.subplot(len(months), 3, i+j*3, + plt.subplot(len(months), 3, i + j * 3, projection=SouthPolarStereo(true_scale_latitude=-70)) diffmap = iplt.contourf(out, levels=np.arange(-90, 91, 20), @@ -78,11 +79,16 @@ def main(cfg): # Get the preprocessed data that we will use as input. input_data = cfg['input_data'].values() - groups = group_metadata(input_data, 'variable_group', sort='project') - for group_name in groups: + groups = group_metadata(input_data, 'variable_group') + # assign obs_si - select + selection = select_metadata(input_data, project='OBS6') + obs_si = iris.load_cube(selection[0]['filename']) + + for group_name in groups.keys(): mod_dict = {} ancestor_filels = [] logger.info("Processing variable %s", group_name) + for attr in groups[group_name]: if attr['project'].startswith('OBS'): logger.info("Load OBS dataset %s", attr['dataset']) @@ -104,7 +110,7 @@ def get_provenance_record(ancestor_files): record = { 'ancestors': ancestor_files, 'authors': ['chun_felicity', 'steketee_anton'], - 'caption': '', + 'caption': 'siconc observations difference from model', 'domains': ['shpolar'], 'plot_types': ['polar'], 'references': [], diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 51732651e0..6154a4478b 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -22,15 +22,15 @@ def _prom_dim_coord(cube, _field, _filename): def plot_trends(datagroup, provenance_record, cfg): - """Create plot for min and max groups""" + """Create plot for min and max groups.""" for variable_group, attributes in datagroup.items(): plt.clf() - for (fp, sn, dt, offset) in attributes: - cube = iris.load_cube(fp, sn, _prom_dim_coord) + for (filep, vname, datalabel, offset) in attributes: + cube = iris.load_cube(filep, vname, _prom_dim_coord) if offset: cube.coord('year').points = [y + offset for y in cube.coord('year').points] - quickplot.plot(cube, label=dt) + quickplot.plot(cube, label=datalabel) plt.title(f"Trends in Sea-Ice {variable_group.split('_')[1]}ima") plt.legend(loc='upper left') From f6c682ffbe5a7113a54f880f7e00f237322c1878 Mon Sep 17 00:00:00 2001 From: Felicity Chun Date: Wed, 11 Sep 2024 16:46:51 +1000 Subject: [PATCH 27/27] format --- .../diag_scripts/seaice_area_extents/seaicearea_trends.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py index 6154a4478b..acebc67c9d 100755 --- a/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py +++ b/esmvaltool/diag_scripts/seaice_area_extents/seaicearea_trends.py @@ -63,7 +63,7 @@ def main(cfg): input_file = (dataset['filename'], dataset['short_name'], dataset['dataset'], None) # key for different models - logger.info(f"dataset: {dataset['long_name']}") + logger.info("Dataset: %s", {dataset['long_name']}) if dataset['variable_group'] not in datagroup: datagroup[dataset['variable_group']] = [] datagroup[dataset['variable_group']].append(input_file)