def SPIxMonthly(incube, season, ncfile): """ Calculate SPI for given months up to one year. The SPI is the anomaly with respect to the chosen scenario baseline period. period - climatological_mean / climatological_std :param incube: precipitation cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file """ print 'Calculating SPI for ' + str(season) fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) _calcUnit(incube, ncfile) incube.data = np.ma.masked_invalid(incube.data) c_monthly = atlas_utils.time_slicer(incube, fdict['scenario']) # shorten time period c_monthly = c_monthly.aggregated_by(['year'], iris.analysis.MEAN) # This is the monthly climatology (mean and std deviation) std = c_monthly.collapsed('year', iris.analysis.STD_DEV) mean = c_monthly.collapsed('year', iris.analysis.MEAN) # We need to change the shape of the monthly climatologies to match the shape of the timeseries (in the cube c_monthly) mean = ma.masked_invalid(mean.data) # this must be a numpy array, not a cube! std = ma.masked_invalid(std.data) # this must be a numpy array, not a cube! clim_mean_data = np.repeat(mean.reshape(1, mean.shape[0], mean.shape[1]), c_monthly.shape[0], axis=0) # np.tile(mean.data, (c_monthly.shape[0] / mean.shape[0], 1, 1)) clim_std_data = np.repeat(std.reshape(1, std.shape[0], std.shape[1]), c_monthly.shape[0], axis=0) # np.tile(std.data, (c_monthly.shape[0] / std.shape[0], 1, 1)) clim_mean_cube = c_monthly.copy(clim_mean_data) clim_std_cube = c_monthly.copy(clim_std_data) spi = (c_monthly - clim_mean_cube) / clim_std_cube spi.data = np.ma.masked_invalid(spi.data) tseries = spi.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(spi, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) iris.save(tseries, ncfile) # monthly time series, 12 entries iris.save(c2d, nc2d)
def _annualnbDay(incube, season, ncfile, threshold=None): ''' Calculate number of days per year with higher value than threshold. Choose threshold as needed e.g. 30mm for extreme and 1mm for rainy day. :param incube: cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' if not threshold: print 'No threshold given, stopping calculation' return print ncfile print "Calculating number of days with variable above " + str(threshold) fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) incube.coord('latitude').guess_bounds() incube.coord('longitude').guess_bounds() if "_pr_" in ncfile: incube.convert_units('kg m-2 day-1') if ("_tas_" in ncfile) or ('_tasmax_' in ncfile) or ('_tasmin_' in ncfile): incube.convert_units('Celsius') bigger = incube.aggregated_by('year', iris.analysis.COUNT, function=lambda values: values >= threshold) bigger.units = incube.units bigger.data = bigger.data.astype(float) tseries = bigger.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) bigger2d = atlas_utils.time_slicer(bigger, fdict['scenario']) c2d = bigger2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(bigger, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) pdb.set_trace() iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def _xDaySumAnnualMax(incube, season, ncfile, nb_days=None): """ :param incube: cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :param nb_days: number of days over which to sum up the variable :return: single model netcdf file """ if not nb_days: print 'Number of days for aggregation not given, stopping calculation' return print ncfile print "Aggregating variable for " + str(nb_days) + "-day moving windows." fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) if ("tas_" in ncfile) or ("tasmax_" in ncfile) or ("tasmin_" in ncfile): print( 'This aggregation makes no sense for temperature. Stopping calculation' ) return _calcUnit(incube, ncfile) incube.data = np.ma.masked_invalid(incube.data) calc = incube.rolling_window('time', iris.analysis.SUM, nb_days) calc.data = np.ma.masked_invalid(calc.data) calc = calc.aggregated_by(['year'], iris.analysis.MAX) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) # monthly time series, 12 entries iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def _annualMeanThresh(incube, season, ncfile, lower_threshold=None): ''' Calculates the annual mean over the time period TODO: allow lower threshold to compute mean based on e.g. RAINY days rather than all days :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating annual mean for ' + season fdict = atlas_utils.split_filename_path(ncfile) slicer = _getSeasConstr(season) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') incube = incube.extract(slicer) _calcUnit(incube, ncfile) csum = incube.aggregated_by(['year'], iris.analysis.SUM) ccount = incube.aggregated_by( ['year'], iris.analysis.COUNT, function=lambda values: values >= lower_threshold) ccount.data = np.array(ccount.data, dtype=float) ccount.data[ccount.data == 0] = np.nan ccount.data = np.ma.masked_invalid(ccount.data) calc = csum / ccount calc.units = incube.units calc.long_name = incube.long_name calc.data = np.ma.masked_invalid(calc.data) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) # yearly time series iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def monthlyClimatologicalMean(incube, season, ncfile): ''' Calculates the climatological monthly means over the time period TODO: allow lower threshold to compute mean based on e.g. RAINY days rather than all days :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season constraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating climatological monthly mean' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') # slicer = _getSeasConstr(season) # cubein = incube.extract(slicer) if "_pr_" in ncfile: incube.convert_units('kg m-2 day-1') if ("_tas_" in ncfile) or ('_tasmax_' in ncfile) or ('_tasmin_' in ncfile): incube.convert_units('Celsius') tcalc = incube.aggregated_by(['month_number', 'year'], iris.analysis.MEAN) # prepare trend calc tcalc.units = incube.units trend2d = trend(tcalc, season, ncfile) incube = atlas_utils.time_slicer( incube, fdict['scenario']) # time slicing for 2d and time series calc = incube.aggregated_by('month_number', iris.analysis.MEAN) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) c2d = calc.collapsed('year', iris.analysis.MEDIAN) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) # monthly time series, 12 entries iris.save(tseries, ncfile) # this is simply an average of all 12 entries on a map..nothing to do with monthly Clim iris.save(c2d, nc2d) # this gives a single map showing the annual (rather than trend specific months) trend # since the trend function aggregates seperate months iris.save(trend2d, nctrend2d)
def annualMax(cubein, season, ncfile): ''' Calculates the annual maximum of a variable. :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating annual maximum' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in cubein.coords()]: iris.coord_categorisation.add_month_number(cubein, 'time', name='month_number') if 'year' not in [coord.name() for coord in cubein.coords()]: iris.coord_categorisation.add_year(cubein, 'time', name='year') slicer = _getSeasConstr(season) cubein = cubein.extract(slicer) if "_pr_" in ncfile: try: cubein.convert_units('kg m-2 day-1') except: return if ("_tas_" in ncfile) or ('_tasmax_' in ncfile) or ('_tasmin_' in ncfile): try: cubein.convert_units('Celsius') except: return calc = cubein.aggregated_by(['year'], iris.analysis.MAX) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def monthlyClimatologicalMean(incube, season, ncfile): ''' Calculates the climatological monthly means over the time period TODO: allow lower threshold to compute mean based on e.g. RAINY days rather than all days :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season constraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating climatological monthly mean' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') # slicer = _getSeasConstr(season) # cubein = incube.extract(slicer) _calcUnit(incube, ncfile) incube.data = np.ma.masked_invalid(incube.data) tcalc = incube.aggregated_by(['month_number', 'year'], iris.analysis.MEAN) # prepare trend calc tcalc.units = incube.units #trend2d = trend(tcalc, season, ncfile) incube = atlas_utils.time_slicer( incube, fdict['scenario']) # time slicing for 2d and time series calc = incube.aggregated_by('month_number', iris.analysis.MEAN) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) #c2d = calc.collapsed('year', iris.analysis.MEDIAN) #nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) #nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) # monthly time series, 12 entries iris.save(tseries, ncfile)
def annualMin(incube, season, ncfile): ''' Calculates the annual minimum of the variable. :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating annual minimum' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) _calcUnit(incube, ncfile) incube.data = np.ma.masked_invalid(incube.data) calc = incube.aggregated_by(['year'], iris.analysis.MIN) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def annualTotalRain(incube, season, ncfile): ''' Calculates the total rain over the time period :param incube: precipitation cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print ncfile print 'Calculating total rain' fdict = atlas_utils.split_filename_path(ncfile) slicer = _getSeasConstr(season) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') incube = incube.extract(slicer) incube.convert_units('kg m-2 day-1') calc = incube.aggregated_by(['year'], iris.analysis.SUM) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def modelRank_scatter_single(incubes, outpath, region, anomaly=False): """ Scatter plot showing the model ranks for a metric (anomaly) for a single scenario :param incubes: wildcard path to all tseries multi-model cubes :param outpath: the path where the plot is saved :param region: the region dictionary as defined in constants :param anomaly: boolean, switch for anomaly calculation :return: plot """ fname = incubes + '_tseries.nc' fdict = atlas_utils.split_filename_path(fname) if fdict['aggregation'] not in cnst.METRIC_AGGS[fdict['metric']]: return ano = glob.glob(fname) if len(ano) != 1: sys.exit('Found too many files, need one file') ano = ano[0] fdict = atlas_utils.split_filename_path(ano) scen = fdict['scenario'] metric = fdict['metric'] variable = fdict['variable'] season = fdict['season'] bc = fdict['bc_res'] if (anomaly == True) & (scen == 'historical'): return cube = iris.load_cube(ano) cube = atlas_utils.time_slicer(cube, fdict['scenario']) cube = cube.collapsed('year', iris.analysis.MEDIAN) if anomaly: ano_hist = ano.replace(fdict['scenario'], 'historical') hist = iris.load_cube(ano_hist) hist = atlas_utils.time_slicer(hist, 'historical') hist = hist.collapsed('year', iris.analysis.MEDIAN) data = atlas_utils.anomalies(hist, cube, percentage=False) data_perc = atlas_utils.anomalies(hist, cube, percentage=True) data = np.ndarray.tolist(data.data) data.sort() data_perc = np.ndarray.tolist(data_perc.data) data_perc.sort() plot_dic1 = { 'data': data, 'ftag': scen + 'Anomaly', 'ylabel': lblr.getYlab(metric, variable, anom="anomaly"), # vname + ' anomaly: ' + fdict['metric'], 'minmax': atlas_utils.data_minmax(data) } if not 'tas' in variable: plot_dic2 = { 'data': data_perc, 'ftag': scen + 'PercentageAnomaly', 'ylabel': lblr.getYlab(metric, variable, anom="percentageAnomaly"), # vname + ' anomaly percentage: ' + fdict['metric'], 'minmax': atlas_utils.data_minmax(data_perc) } toplot = [plot_dic1, plot_dic2] else: toplot = [plot_dic1] else: cube = cube.data cube = np.ndarray.tolist(cube) cube.sort() plot_dic1 = { 'data': cube, 'ftag': scen, 'ylabel': lblr.getYlab(metric, variable, anom=""), 'minmax': atlas_utils.data_minmax(cube) } toplot = [plot_dic1] for p in toplot: cms = 'seismic' if 'tas' in variable else 'seismic_r' cm = plt.get_cmap(cms) f = plt.figure(figsize=(8.5, 6)) if np.nanmax(p['data']) - np.nanmin(p['data']) > np.nanmax(p['data']): plt.hlines(0, 0, len(p['data']), linestyle='dashed', color='grey') for i in range(0, len(p['data'])): plt.scatter(i, p['data'][i], c=p['data'][i], vmin=p['minmax'][0], vmax=p['minmax'][1], cmap=cm, edgecolors='k', s=50, zorder=10) plt.ylabel(p['ylabel']) if cnst.LANGUAGE == 'ENGLISH': plt.xlabel("Model Rank") else: plt.xlabel(u"Classement de modèles") plt.title(lblr.getTitle(metric, variable, season, scen, bc, region[1], anom=p['ftag']), fontsize=11) plt.tick_params(axis='x', which='both', bottom='off', top='off') plt.savefig(outpath + os.sep + fdict['metric'] + '_' + fdict['variable'] + '_' + fdict['bc_res'] + '_' + fdict['season'] + '_' + region[0] + '_allModelRank_' + p['ftag'] + '.png', dpi=400) plt.close(f)
def onsetMarteau(incube, season, ncfile): """ Calculate Marteau monsoon onset. Season is fixed to onset period :param incube: precipitation cube from CMIP5 raw data :param ncfile: full string for output netcdf file :return: single model netcdf file """ season = 'mjjas' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') if 'day_of_year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_day_of_year(incube, 'time', name='day_of_year') slicer = _getSeasConstr(season) cubein = incube.extract(slicer) cube2plot = cubein _calcUnit(cube2plot, ncfile) yrs = cube2plot.aggregated_by('year', iris.analysis.MEAN) empty = yrs.data years = yrs.coord('year').points dates = [] tester = 14 onsets = np.zeros((empty.shape[0], empty.shape[1], empty.shape[2]), float) for yr in years: yrslice = iris.Constraint(year=lambda cell: cell == yr) holdr = cubein.extract(yrslice) strt_date = holdr.coord('day_of_year').points[0] holdr = holdr.data for x in range(0, holdr.shape[2]): for y in range(0, holdr.shape[1]): latmean = 0 cnt0 = 0 A = 0 B = 0 C = 0 for t in xrange(0, holdr.shape[0] - tester - 6): if holdr[t, y, x] > 1.0: A4 = 1 else: A4 = 0 if holdr[t + 1, y, x] + holdr[t, y, x] > 20.0: A1 = 1 else: A1 = 0 A2 = 1 t2 = 2 while t2 < tester - 6: drytest = holdr[t + t2, y, x] + holdr[ t + t2 + 1, y, x] + holdr[t + t2 + 2, y, x] + holdr[ t + t2 + 3, y, x] + holdr[t + t2 + 4, y, x] + holdr[ t + t2 + 5, y, x] + holdr[t + t2 + 6, y, x] if drytest < 5.0: A2 = 0 break else: t2 = t2 + 1 A3 = A4 + A2 + A1 if A3 == 3: latmean = t + strt_date onsets[yr - years[0], y, x] = latmean break else: continue break if latmean == 0: onsets[yr - years[0], y, x] = np.nan yrs.data = ma.masked_invalid(onsets) tseries = yrs.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) yrs2d = atlas_utils.time_slicer(yrs, fdict['scenario']) c2d = yrs2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(yrs, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def nbModels_histogram_single(incubes, outpath, region, anomaly=False): """ Histogram plot showing the number of models within different ranges of the metric (anomaly) value for a single scenario :param incubes: wildcard path to all tseries multi-model cubes :param outpath: the path where the plot is saved :param region: the region dictionary as defined in constants :param anomaly: boolean, switch for anomaly calculation :return: plot """ fname = incubes + '_tseries.nc' fdict = atlas_utils.split_filename_path(fname) if fdict['aggregation'] not in cnst.METRIC_AGGS[fdict['metric']]: return ano = glob.glob(fname) if len(ano) != 1: print incubes pdb.set_trace() sys.exit('Found too many or no files, need one file') ano = ano[0] fdict = atlas_utils.split_filename_path(ano) scen = fdict['scenario'] metric = fdict['metric'] variable = fdict['variable'] season = fdict['season'] bc = fdict['bc_res'] if (anomaly == True) & (scen == 'historical'): return cube = iris.load_cube(ano) cube = atlas_utils.time_slicer(cube, fdict['scenario']) cube.data = np.ma.masked_invalid(cube.data) cube = cube.collapsed('year', iris.analysis.MEDIAN) if anomaly: ano_hist = ano.replace(fdict['scenario'], 'historical') hist = iris.load_cube(ano_hist) hist = atlas_utils.time_slicer(hist, 'historical') hist.data = np.ma.masked_invalid(hist.data) hist = hist.collapsed('year', iris.analysis.MEDIAN) data = atlas_utils.anomalies(hist, cube, percentage=False) data_perc = atlas_utils.anomalies(hist, cube, percentage=True) data = np.ma.masked_invalid(data.data) data_perc = np.ma.masked_invalid(data_perc.data) if np.nansum(data) and np.ma.count(data): if np.nanmax(data) - np.nanmin(data) > np.nanmax(data): levels = atlas_utils.binlevels(data) else: levels = np.linspace(np.nanmin(data), np.nanmax(data), 14) else: levels = np.arange(-1, 1, 10) data = np.zeros_like(data) if np.nansum(data_perc) and np.ma.count(data): if np.nanmax(data_perc) - np.nanmin(data_perc) > np.nanmax( data_perc): plevels = atlas_utils.binlevels(data_perc) else: plevels = np.linspace(np.nanmin(data_perc), np.nanmax(data_perc), 14) else: plevels = np.arange(-1, 1, 10) data_perc = np.zeros_like(data_perc) try: histo, h = np.histogram(data[np.isfinite(data)], bins=levels) except ValueError: pdb.set_trace() try: histop, hp = np.histogram(data_perc[np.isfinite(data_perc)], bins=plevels) except ValueError: pdb.set_trace() plot_dic1 = { 'data': histo, 'ftag': scen + 'Anomaly', 'ylabel': lblr.getYlab(metric, variable, anom="anomaly"), 'bins': h } if not 'tas' in variable: plot_dic2 = { 'data': histop, 'ftag': scen + 'PercentageAnomaly', 'ylabel': lblr.getYlab(metric, variable, anom="percentageAnomaly"), 'bins': hp } toplot = [plot_dic1, plot_dic2] else: toplot = [plot_dic1] else: cube = cube.data if np.nanmax(cube) - np.nanmin(cube) > np.nanmax(cube): levels = atlas_utils.binlevels(cube) else: levels = np.linspace(cube.min(), cube.max(), 10) histo, h = np.histogram(cube, bins=levels) plot_dic1 = { 'data': histo, 'ftag': scen, 'ylabel': lblr.getYlab(metric, variable, anom=""), 'bins': h } toplot = [plot_dic1] for p in toplot: f = plt.figure(figsize=lblr.getFigSize(None, 'nbModelHistogram')) ax = f.add_subplot(111) bin = p['bins'] middle = bin[0:-1] + ((bin[1::] - bin[0:-1]) / 2) barlist = ax.bar(bin[0:-1] + ((bin[1::] - bin[0:-1]) / 2), p['data'], edgecolor='black', width=(bin[1::] - bin[0:-1]), color='lightblue') dummy = np.array(barlist) for d in dummy[middle < 0]: d.set_color('lightslategray') d.set_edgecolor('black') try: ax.set_xlim( np.floor( np.nanmin((bin[0:-1])[(p['data']) > 0]) - (bin[1] - bin[0])), np.ceil( np.nanmax((bin[1::])[(p['data']) > 0]) + (bin[-1] - bin[-2]))) except ValueError: pass ax.set_xlabel(p['ylabel']) if cnst.LANGUAGE == 'ENGLISH': ax.set_ylabel('Number of models') else: ax.set_ylabel(u'Nombre de modèles') if not np.nansum(p['data']): ax.text(0.5, 0.5, 'Zero values', zorder=10) print metric, 'set text' ax.set_title(lblr.getTitle(metric, variable, season, scen, bc, region[1], anom=p['ftag']), fontsize=11) plt.savefig(outpath + os.sep + fdict['metric'] + '_' + fdict['variable'] + '_' + fdict['bc_res'] + '_' + fdict['season'] + '_' + region[0] + '_nbModelHistogram_' + p['ftag'] + '.png') plt.close(f)
def pet(cubein, season, ncfile): ''' Calculates the potential evapotranspiration on a daily timestep, and aggregates over a time period :param incube: precipitation cube from CMIP5 raw data. Note that this is used as a template to retrieve the other variables required :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :return: single model netcdf file ''' print 'Calculating Potential Evapotranspiration' # Retrieve all data first tasmin = cubein[0] tasmax = cubein[1] rsds = cubein[2] tasmin.data = ma.masked_invalid(tasmin.data) tasmax.data = ma.masked_invalid(tasmax.data) rsds.data = ma.masked_invalid(rsds.data) # Get details fdict = atlas_utils.split_filename_path(ncfile) # NB: Had to do it this way because iris didn't like power function try: tas_dif = tasmax - tasmin except ValueError: print 'Cubes not of same length (timestep?), returning' return tas_dif.data = tas_dif.data**0.5 # NB: SW incoming radiation (in W m-2) needs to be converted to MJ m-2 and then mm day-1 equivalent using the following: # 1 W m-2 = 0.0864 MJ m-2 day-1 # The value of 0.408 is the inverse of the latent heat flux of vaporization at 20C, changing the extraterrestrial radiation units from MJ m-2 day-1 into mm day-1 of evaporation equivalent (Allen et al., 1998) # pet = 0.0023 * ((tasmax+tasmin)/2 + 17.8) * (tasmax-tasmin)**0.5 * rsds try: pet = 0.0023 * ( (tasmax + tasmin) / 2 + 17.8) * tas_dif * (rsds * 0.0864 * 0.408) except ValueError: # TODO: Work out why some models fail this test (e.g. bcc-csm1-1, HadGEM2-AO, CMCC-CM) print 'There was a value error for ' + ncfile return #Now aggregate to the season and according to the aggregation types slicer = _getSeasConstr(season) if 'month_number' not in [coord.name() for coord in pet.coords()]: iris.coord_categorisation.add_month_number(pet, 'time', name='month_number') if 'year' not in [coord.name() for coord in pet.coords()]: iris.coord_categorisation.add_year(pet, 'time', name='year') pet = pet.extract(slicer) pet.units = cf_units.Unit( 'mm day-1' ) # Over-rides units because temp and radiation values are used to give an approximation of potential evapotranspiration in mm per day pet.data = ma.masked_invalid(pet.data) calc = pet.aggregated_by(['year'], iris.analysis.MEAN) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)
def trend(cubein, season, ncfile): ''' Calculates the annual trend either for 1950-2000 or for 2010-2060 Can calculate the trend for one period, or (if 'month_number' is a coordinate name), it will calculate the trend for each month :param incube: timeseries of a metric either for 1 period (e.g. JAS), or for all months of the year :param season: season name as recognised by the function _getSeasConstr :param ncfile: full string for output netcdf file returns: trend for a single model netcdf TODO: Review code & check that behaviour for multiple months is as expected ''' fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in cubein.coords()]: iris.coord_categorisation.add_month_number(cubein, 'time', name='month_number') if 'year' not in [coord.name() for coord in cubein.coords()]: iris.coord_categorisation.add_year(cubein, 'time', name='year') incube = atlas_utils.time_slicer(cubein, fdict['scenario']) # Make sure there aren't any invalid grid cells in the data incube.data = ma.masked_invalid(incube.data) # Are we quantifying the trend for each month of the year ('ann') or just for one period (e.g. JAS)? if "pr_" in ncfile and not "SPIxMonthly" in ncfile: incube_y = incube.aggregated_by(['year'], iris.analysis.SUM) incube_y.convert_units('kg m-2 month-1') if ("tas_" in ncfile) or ('tasmax_' in ncfile) or ('tasmin_' in ncfile): incube_y = incube.aggregated_by(['year'], iris.analysis.MEAN) try: incube_y.convert_units('Celsius') except ValueError: if np.mean(incube_y.data) > 100: incube_y.units = cf_units.Unit('K') else: incube_y.units = cf_units.Unit('Celsius') else: incube_y = incube.aggregated_by(['year'], iris.analysis.MEAN) month_numbers = np.unique(incube_y.coord('month_number').points) if len(month_numbers) > 1: if "pr_" in ncfile: incube_ym = incube.aggregated_by(['year', 'month_number'], iris.analysis.SUM) incube_ym.convert_units('kg m-2 month-1') if ("tas_" in ncfile) or ("tasmax_" in ncfile) or ("tasmin_" in ncfile): incube_ym = incube.aggregated_by(['year', 'month_number'], iris.analysis.MEAN) incube_ym.convert_units('Celsius') slopedata1mon = np.zeros(incube_ym[0].shape).reshape( (1, incube_ym[0].shape[0], incube_ym[0].shape[1])) slopedata = np.repeat(slopedata1mon, 12, axis=0) moncoord = iris.coords.DimCoord(points=np.arange(1, 13), long_name='month_number', units='1') slope = iris.cube.Cube(slopedata, long_name='Trend', units=incube_ym.units, dim_coords_and_dims=[ (moncoord, 0), (incube.coord('latitude'), 1), (incube.coord('longitude'), 2) ]) for mon in month_numbers: # print mon slicer = iris.Constraint(month_number=lambda cell: cell == mon) incube1mon = incube_ym.extract(slicer) for x in np.arange(len(incube.coord('longitude').points)): for y in np.arange(len(incube.coord('latitude').points)): if np.all(incube1mon.data[:, y, x].mask): slope.data[mon - 1, y, x] = ma.masked else: # Outputs: slope, intercept, r-value, pvalue, std_err # for py34: reg.slope reg = stats.linregress(np.arange(incube1mon.shape[0]), incube1mon.data[:, y, x]) slope.data[mon - 1, y, x] = reg[0] else: # No need to loop through months in this case slopedata = np.zeros(incube_y[0].shape) slope = iris.cube.Cube(slopedata, long_name='Trend', units=incube_y.units, dim_coords_and_dims=[ (incube.coord('latitude'), 0), (incube.coord('longitude'), 1) ]) for x in np.arange(len(incube.coord('longitude').points)): for y in np.arange(len(incube.coord('latitude').points)): if np.all(incube_y.data[:, y, x].mask): slope.data[y, x] = ma.masked else: # Outputs: slope, intercept, r-value, pvalue, std_err # for py34: reg.slope reg = stats.linregress(np.arange(incube_y.shape[0]), incube_y.data[:, y, x]) slope.data[y, x] = reg[0] return (slope)
def boxplot_scenarios(incubes, outpath, region, anomaly=False): """ Produces a boxplot with a box for each scenario, indicating the model spread. :param incubes: wildcard path to all tseries multi-model cubes :param outpath: the path where the plot is saved :param region: the region dictionary as defined in constants :param anomaly: boolean, switch for anomaly calculation :return: plot """ fname = incubes + '_tseries.nc' fdict = atlas_utils.split_filename_path(fname) if fdict['aggregation'] not in cnst.METRIC_AGGS[fdict['metric']]: return ano_list = glob.glob(fname) ano_list = atlas_utils.order(ano_list) ldata = [] scen = [] perc = [] for ano in ano_list: print ano fdict = atlas_utils.split_filename_path(ano) metric = fdict['metric'] variable = fdict['variable'] season = fdict['season'] scenario = fdict['scenario'] bc = fdict['bc_res'] if (anomaly == True) & (fdict['scenario'] == 'historical'): continue cube = iris.load_cube(ano) cube = atlas_utils.time_slicer(cube, fdict['scenario']) cube = cube.collapsed('year', iris.analysis.MEDIAN) if anomaly: ano_hist = ano.replace(fdict['scenario'], 'historical') hist = iris.load_cube(ano_hist) hist = atlas_utils.time_slicer(hist, 'historical') hist = hist.collapsed('year', iris.analysis.MEDIAN) data = atlas_utils.anomalies(hist, cube, percentage=False) data_perc = atlas_utils.anomalies(hist, cube, percentage=True) an = 'anomaly' ylabel = lblr.getYlab(metric, variable, anom='absolute') an_p = 'percentageAnomaly' ylabel_p = lblr.getYlab(metric, variable, anom='percentage') dat_perc = np.ndarray.tolist(data_perc.data) perc.append(dat_perc) else: data = cube an = 'scenarios' ylabel = lblr.getYlab(metric, variable, anom=None) dat = data.data dat = np.ndarray.tolist(dat) ldata.append(dat) scen.append(fdict['scenario']) plot_dic1 = {'data': ldata, 'xlabel': scen, 'ftag': an, 'ylabel': ylabel} toplot = [plot_dic1] if anomaly and not 'tas' in variable: plot_dic2 = { 'data': perc, 'xlabel': scen, 'ftag': an_p, 'ylabel': ylabel_p } toplot = [plot_dic1, plot_dic2] for p in toplot: f = plt.figure(figsize=(8, 7)) try: bp = plt.boxplot(p['data'], labels=p['xlabel'], sym='', patch_artist=True, notch=False, zorder=9, whis=[10, 90]) except ValueError: print('Boxplot data has a problem, please check. Cannot continue') pdb.set_trace() plt.title(lblr.getTitle(metric, variable, season, scenario, bc, region[1], anom=p['ftag']), fontsize=10) if cnst.LANGUAGE == 'ENGLISH': plt.xlabel('Scenario') plt.ylabel(p['ylabel']) else: plt.xlabel(u'Scénario') plt.ylabel(p['ylabel']) for median, box, lab in zip(bp['medians'], bp['boxes'], p['xlabel']): box.set(facecolor='none') median.set(linewidth=0) #, color='steelblue') for id, d in enumerate(p['data']): try: plt.scatter([id + 1] * len(d), d, marker='_', color='firebrick', s=60, zorder=9) except: pdb.set_trace() if np.nanmax(d) - np.nanmin(d) > np.nanmax(d): plt.hlines(0, 0, len(d), linestyle='dashed', color='grey') plt.savefig(outpath + os.sep + fdict['metric'] + '_' + fdict['variable'] + '_' + fdict['bc_res'] + '_' + fdict['season'] + '_' + region[0] + '_allModelBoxplot_' + p['ftag'] + '.png') plt.close(f)
def barplot_scenarios(incubes, outpath, region, anomaly=False): """ Barplot showing the average of the (anomaly) metric over the climatological period for all individual models and scenarios. :param incubes: wildcard path to all tseries multi-model cubes :param outpath: the path where the plot is saved :param region: the region dictionary as defined in constants :param anomaly: boolean, switch for anomaly calculation :return: plot """ fname = incubes + '_tseries.nc' fdict = atlas_utils.split_filename_path(fname) if fdict['aggregation'] not in cnst.METRIC_AGGS[fdict['metric']]: return # This creates a list of '*allModels_tseries.nc' files, one element for each scenario ano_list = glob.glob(fname) ano_list = atlas_utils.order(ano_list) ldata = [] scen = [] perc = [] lmodels = [] for ano in ano_list: fdict = atlas_utils.split_filename_path(ano) scenario = fdict['scenario'] metric = fdict['metric'] variable = fdict['variable'] season = fdict['season'] bc = fdict['bc_res'] if (anomaly == True) & (fdict['scenario'] == 'historical'): continue cube = iris.load_cube(ano) cube = atlas_utils.time_slicer(cube, fdict['scenario']) cube = cube.collapsed('year', iris.analysis.MEDIAN) lmodels.append(np.ndarray.tolist(cube.coord('model_name').points)) if anomaly: ano_hist = ano.replace(fdict['scenario'], 'historical') hist = iris.load_cube(ano_hist) hist = atlas_utils.time_slicer(hist, 'historical') hist = hist.collapsed('year', iris.analysis.MEDIAN) data = atlas_utils.anomalies(hist, cube, percentage=False) data_perc = atlas_utils.anomalies(hist, cube, percentage=True) an = 'anomaly' ylabel = lblr.getYlab(metric, variable, anom="anomaly") an_p = 'percentageAnomaly' ylabel_p = lblr.getYlab(metric, variable, anom="percentageAnomaly") dat_perc = np.ndarray.tolist(data_perc.data) perc.append(dat_perc) else: data = cube an = 'scenarios' ylabel = lblr.getYlab(metric, variable, anom="") dat = data.data dat = np.ndarray.tolist(dat) ldata.append(dat) scen.append(scenario) plot_dic1 = { 'data': ldata, 'xlabel': scen, 'ftag': an, 'ylabel': ylabel, 'models': lmodels } toplot = [plot_dic1] if anomaly and not 'tas' in variable: plot_dic2 = { 'data': perc, 'xlabel': scen, 'ftag': an_p, 'ylabel': ylabel_p, 'models': lmodels } toplot = [plot_dic1, plot_dic2] for p in toplot: if len(p['data']) < 4: xp = len(p['data']) yp = 1 xx = 8 yy = 7 else: xp = 2 yp = 2 xx = 13 yy = 8 f = plt.figure(figsize=(xx, yy)) for id in range(len(p['data'])): ax = f.add_subplot(xp, yp, id + 1) b = plt.bar(range(len(p['models'][id])), p['data'][id], align='edge', label=p['models'][id], color='darkseagreen') # plt.subplots_adjust(bottom=0.8) xticks_pos = [ 0.65 * patch.get_width() + patch.get_xy()[0] for patch in b ] plt.xticks(xticks_pos, p['models'][id], ha='right', rotation=45) plt.ylabel(p['ylabel']) plt.title(p['xlabel'][id]) plt.tight_layout(h_pad=0.2, rect=(0, 0, 1, 0.92)) plt.suptitle(lblr.getTitle(metric, variable, season, scen, bc, region[1], anom=p['ftag']), fontsize=10) plt.savefig(outpath + os.sep + fdict['metric'] + '_' + fdict['variable'] + '_' + fdict['bc_res'] + '_' + fdict['season'] + '_' + region[0] + '_allModelHisto_' + p['ftag'] + '.png') plt.close(f)
def _countSpells(incube, season, ncfile, spell_length=None, lower_threshold=None, upper_threshold=None): """ Calculates the number of periods of a spell of length 'spell length' above or equal 'lower threshold' or below 'upper threshold'. :param incube: single variable cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :param spell_length: length :param lower_threshold: :param upper_threshold: :return: single model netcdf file """ def count_func(data, threshold, axis, spell_length): # print(data.shape) # print(axis) if axis < 0: # just cope with negative axis numbers axis += data.ndim # print(axis) # Threshold the data to find the 'significant' points. if lower_threshold: data_hits = data >= threshold if upper_threshold: data_hits = data < threshold # Make an array with data values "windowed" along the time axis. hit_windows = iris.util.rolling_window( data_hits, window=spell_length, axis=axis) # rolling window along time axis # Find the windows "full of True-s" (along the added 'window axis'). full_windows = np.all(hit_windows, axis=axis + 1) # Count points fulfilling the condition (along the time axis). spell_point_counts = np.nansum(full_windows, axis=axis, dtype=int) return spell_point_counts if not spell_length: print "No spell length given, please provide. Stopping calculation" return if (upper_threshold and lower_threshold): print "Upper and lower threshold given. Please provide exactly one (upper or lower) threshold. Stopping calculation" return if not (upper_threshold or lower_threshold): print "No threshold given. Please provide one (upper or lower) threshold. Stopping calculation" return fdict = atlas_utils.split_filename_path(ncfile) if upper_threshold: threshold = upper_threshold else: threshold = lower_threshold print ncfile print "Calculating number of spells equal or longer than " + str( spell_length) + ' days.' if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) incube.coord('latitude').guess_bounds() incube.coord('longitude').guess_bounds() _calcUnit(incube, ncfile) incube.data = ma.masked_invalid(incube.data) # Start spell calculation # Make an aggregator from days count SPELL_COUNT = iris.analysis.Aggregator('spell_count', count_func, units_func=lambda units: 1) # Calculate the statistic calc = incube.aggregated_by(['year'], SPELL_COUNT, threshold=threshold, spell_length=spell_length) calc.units = incube.units calc.data = np.array(calc.data, dtype=float) calc.data = ma.masked_invalid(calc.data) tseries = calc.collapsed(['longitude', 'latitude'], iris.analysis.MEDIAN) calc2d = atlas_utils.time_slicer(calc, fdict['scenario']) c2d = calc2d.collapsed('year', iris.analysis.MEDIAN) trend2d = trend(calc, season, ncfile) nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) # full time period iris.save(c2d, nc2d) # sliced time period iris.save(trend2d, nctrend2d) # values for trend period
def nbModels_histogram_scenarios(incubes, outpath, region, anomaly=False): """ Histogram plot showing the number of models within different ranges of the metric (anomaly) value for ALL scenarios :param incubes: wildcard path to all tseries multi-model cubes :param outpath: the path where the plot is saved :param region: the region dictionary as defined in constants :param anomaly: boolean, switch for anomaly calculation :return: plot """ fname = incubes + '_tseries.nc' fdict = atlas_utils.split_filename_path(fname) if fdict['aggregation'] not in cnst.METRIC_AGGS[fdict['metric']]: return ano_list = glob.glob(fname) ano_list = atlas_utils.order(ano_list) ldata = [] scen = [] perc = [] for ano in ano_list: toplot = None fdict = atlas_utils.split_filename_path(ano) scenario = fdict['scenario'] metric = fdict['metric'] variable = fdict['variable'] season = fdict['season'] bc = fdict['bc_res'] if (anomaly == True) & (fdict['scenario'] == 'historical'): continue vname = cnst.VARNAMES[fdict['variable']] cube = iris.load_cube(ano) cube = atlas_utils.time_slicer(cube, fdict['scenario']) cube = cube.collapsed('year', iris.analysis.MEDIAN) if anomaly: ano_hist = ano.replace(fdict['scenario'], 'historical') hist = iris.load_cube(ano_hist) hist = atlas_utils.time_slicer(hist, 'historical') hist = hist.collapsed('year', iris.analysis.MEDIAN) data = atlas_utils.anomalies(hist, cube, percentage=False) data_perc = atlas_utils.anomalies(hist, cube, percentage=True) data = data.data data_perc = data_perc.data hhisto, bi = np.histogram(data, bins=np.linspace(data.min(), data.max(), 10)) try: hhistop, bip = np.histogram(data_perc, bins=np.linspace( data_perc.min(), data_perc.max(), 10)) except: continue an = 'anomaly' ylabel = lblr.getYlab(metric, variable, anom="anomaly") an_p = 'percentageAnomaly' ylabel_p = lblr.getYlab(metric, variable, anom="percentageAnomaly") ldata.append((hhisto, bi)) perc.append((hhistop, bip)) else: cube = cube.data hhisto, bi = np.histogram(cube, bins=np.linspace(cube.min(), cube.max(), 10)) ldata.append((hhisto, bi)) ylabel = lblr.getYlab(metric, variable, anom="") an = 'scenarios' scen.append(fdict['scenario']) plot_dic1 = { 'data': ldata, 'ftag': an, 'ylabel': ylabel, } toplot = [plot_dic1] if anomaly and not 'tas' in variable: plot_dic2 = { 'data': perc, 'ftag': an_p, 'ylabel': ylabel_p, } toplot = [plot_dic1, plot_dic2] if toplot: # This will only fail if both the historical and future are zero for p in toplot: if len(p['data']) < 4: xp = len(p['data']) yp = 1 xx = 8 yy = 6 else: xp = 2 yp = 2 xx = 9 yy = 5 f = plt.figure(figsize=(xx, yy)) for id in range(len(p['data'])): ax = f.add_subplot(xp, yp, id + 1) bin = p['data'][id][1] ax.bar(bin[0:-1] + ((bin[1::] - bin[0:-1]) / 2), p['data'][id][0], edgecolor='black', width=(bin[1::] - bin[0:-1]), align='edge', color='darkseagreen') ax.set_xlabel(p['ylabel']) if cnst.LANGUAGE == 'ENGLISH': ax.set_ylabel('Number of models') else: ax.set_ylabel(u'Nombre de modèles') ax.set_title(lblr.getTitle(metric, variable, season, scenario, bc, region[1], anom=p['ftag']), fontsize=10) plt.tight_layout() plt.savefig(outpath + os.sep + fdict['metric'] + '_' + fdict['variable'] + '_' + fdict['bc_res'] + '_' + fdict['season'] + '_' + region[0] + '_MultiNbModelHistogram_' + p['ftag'] + '.png') plt.close(f)
def _annualnbDayPerc(incube, season, ncfile, upper_threshold=None, lower_threshold=None): ''' Calculate percentage of days per year with higher value than threshold. Choose thresholds as needed e.g. 30mm for extreme and 1mm for rainy day. :param incube: precipitation cube from CMIP5 raw data :param season: string indicating season, read by _getSeasConstr() to get season contraint :param ncfile: full string for output netcdf file :param upper_threshold: threshold (>=) defining the value to be exceeded (e.g. extreme value or rainy day value :param lower_threshold: threshold (>=) defining the minimum value of valid pixels (eg. 0 or 0.1 or 1) :return: single model netcdf file ''' if not upper_threshold: print "No threshold given, please provide upper threshold, stopping calculation" return print ncfile print "Calculating percentage of days with variable above " + str( upper_threshold) fdict = atlas_utils.split_filename_path(ncfile) if 'month_number' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_month_number(incube, 'time', name='month_number') if 'year' not in [coord.name() for coord in incube.coords()]: iris.coord_categorisation.add_year(incube, 'time', name='year') slicer = _getSeasConstr(season) incube = incube.extract(slicer) incube.coord('latitude').guess_bounds() incube.coord('longitude').guess_bounds() _calcUnit(incube, ncfile) bigger = incube.aggregated_by( 'year', iris.analysis.COUNT, function=lambda values: values >= upper_threshold) monthcount = incube.aggregated_by( 'year', iris.analysis.COUNT, function=lambda values: values >= lower_threshold) bigger.data = np.ma.masked_invalid(bigger.data.astype(float)) monthcount.data = monthcount.data.astype(float) monthcount[monthcount == 0] = np.nan monthcount.data = np.ma.masked_invalid(monthcount.data) trend_data = bigger / monthcount * 100 trend_data.units = incube.units trend2d = trend(trend_data, season, ncfile) bigger_tseries = bigger.collapsed(['longitude', 'latitude'], iris.analysis.SUM) bigger2d = atlas_utils.time_slicer(bigger, fdict['scenario']) bigger_c2d = bigger2d.collapsed('year', iris.analysis.SUM) monthcount_tseries = monthcount.collapsed(['longitude', 'latitude'], iris.analysis.SUM) monthcount_tseries[monthcount_tseries == 0] = np.nan monthcount_tseries.data = np.ma.masked_invalid(monthcount_tseries.data) monthcount2d = atlas_utils.time_slicer(monthcount, fdict['scenario']) monthcount_c2d = monthcount2d.collapsed('year', iris.analysis.SUM) monthcount_c2d[monthcount_c2d == 0] = np.nan monthcount_c2d.data = np.ma.masked_invalid(monthcount_c2d.data) tseries = (bigger_tseries / monthcount_tseries) * 100 c2d = (bigger_c2d / monthcount_c2d) * 100 nc2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[1]) nctrend2d = ncfile.replace(cnst.AGGREGATION[0], cnst.AGGREGATION[2]) iris.save(tseries, ncfile) iris.save(c2d, nc2d) iris.save(trend2d, nctrend2d)