示例#1
0
def seasonal_mean(cube):
    seasons = ['winter', 'summer']
    icc.add_season(cube, 'time', 'clim_season')
    icc.add_season_year(cube, 'time', 'season_year')
    season_mean_cube_list = iris.cube.CubeList([])
    season_max_cube_list = iris.cube.CubeList([])
    season_min_cube_list = iris.cube.CubeList([])
    for season in seasons:
        if season == 'winter':
            months = 'djf'
        if season == 'spring':
            months = 'mam'
        if season == 'summer':
            months = 'jja'
        if season == 'autumn':
            months = 'son'
        single_season_cube = cube.extract(iris.Constraint(clim_season=months))
        season_mean_cube = single_season_cube.aggregated_by(
            ['clim_season', 'season_year'], iris.analysis.MEAN)
        season_mean_cube.rename(season_mean_cube.name() + '_' + season +
                                '_mean')
        season_mean_cube_list.append(season_mean_cube)
        season_max = single_season_cube.aggregated_by(
            ['clim_season', 'season_year'], iris.analysis.MAX)
        season_max.rename(season_max.name() + '_' + season + '_max')
        season_max_cube_list.append(season_max)
        season_min = single_season_cube.aggregated_by(
            ['clim_season', 'season_year'], iris.analysis.MIN)
        season_min.rename(season_min.name() + '_' + season + '_min')
        season_min_cube_list.append(season_min)
    return [season_mean_cube_list, season_max_cube_list, season_min_cube_list]
示例#2
0
	def add_times(cube, time):
		coord_cat.add_month(cube, time, name='month')
		coord_cat.add_season(cube, time, name='clim_season')
		coord_cat.add_year(cube, time, name='year')
		coord_cat.add_day_of_year(cube, time, name='day_number')
		coord_cat.add_season_year(cube, time, name='season_year')
		return cube
 def test_add_custom_season_year(self):
     # custom season years match standard season years?
     seasons = ('djf', 'mam', 'jja', 'son')
     ccat.add_season_year(self.cube, 'time', name='year_std')
     ccat.add_custom_season_year(self.cube, 'time', seasons,
                                 name='year_custom')
     coord_std = self.cube.coord('year_std')
     coord_custom = self.cube.coord('year_custom')
     self.assertArrayEqual(coord_custom.points, coord_std.points)
示例#4
0
 def test_add_season_nonstandard(self):
     # season categorisations work for non-standard seasons?
     cube = self.cube
     time_coord = self.time_coord
     seasons = ['djfm', 'amjj', 'ason']
     ccat.add_season(cube, time_coord, name='seasons', seasons=seasons)
     ccat.add_season_number(cube, time_coord, name='season_numbers',
                            seasons=seasons)
     ccat.add_season_year(cube, time_coord, name='season_years',
                          seasons=seasons)
     self.assertCML(cube, ('categorisation', 'customcheck.cml'))
示例#5
0
 def test_add_season_nonstandard(self):
     # season categorisations work for non-standard seasons?
     cube = self.cube
     time_coord = self.time_coord
     seasons = ["djfm", "amjj", "ason"]
     ccat.add_season(cube, time_coord, name="seasons", seasons=seasons)
     ccat.add_season_number(
         cube, time_coord, name="season_numbers", seasons=seasons
     )
     ccat.add_season_year(
         cube, time_coord, name="season_years", seasons=seasons
     )
     self.assertCML(cube, ("categorisation", "customcheck.cml"))
def select_certain_months(cubes, lbmon):
    """
    Select data from CubeList that matches the specified months.

    :param CubeList cubes: Iris CubeList.
    :param list lbmon: List with month numbers, e.g. lbmon=[5,6,7] for Mai,
        June, and July.
    :returns: CubeList with Cubes containing only data for the specified mnth.
    :rtype: CubeList
    :raises: `AssertionError` if `cubes` is not an `iris.cube.CubeList`.
    """
    # add 'month number' coordinate
    add_time_coord = {
        'monthly':
        lambda cube: coord_cat.add_month_number(
            cube, 'time', name='month_number'),
        'seasonal':
        lambda cube: coord_cat.add_season(cube, 'time', name='clim_season'),
        'annual':
        lambda cube: coord_cat.add_season_year(
            cube, 'time', name='season_year')
    }
    assert isinstance(cubes, iris.cube.CubeList)

    for cube in cubes:
        add_time_coord['monthly'](cube)

    # filter by month number
    month_constraint = iris.Constraint(month_number=lbmon)
    return cubes.extract(
        month_constraint)  # CubeList.extract returns always CubeList
    def test_basic(self):
        cube = self.cube
        time_coord = self.time_coord

        ccat.add_year(cube, time_coord, 'my_year')
        ccat.add_day_of_month(cube, time_coord, 'my_day_of_month')
        ccat.add_day_of_year(cube, time_coord, 'my_day_of_year')

        ccat.add_month(cube, time_coord, 'my_month')
        with warnings.catch_warnings(record=True):
            ccat.add_month_shortname(cube, time_coord, 'my_month_shortname')
        ccat.add_month_fullname(cube, time_coord, 'my_month_fullname')
        ccat.add_month_number(cube, time_coord, 'my_month_number')

        ccat.add_weekday(cube, time_coord, 'my_weekday')
        ccat.add_weekday_number(cube, time_coord, 'my_weekday_number')
        with warnings.catch_warnings(record=True):
            ccat.add_weekday_shortname(cube, time_coord,
                                       'my_weekday_shortname')
        ccat.add_weekday_fullname(cube, time_coord, 'my_weekday_fullname')

        ccat.add_season(cube, time_coord, 'my_season')
        ccat.add_season_number(cube, time_coord, 'my_season_number')
        with warnings.catch_warnings(record=True):
            ccat.add_season_month_initials(cube, time_coord,
                                           'my_season_month_initials')
        ccat.add_season_year(cube, time_coord, 'my_season_year')

        # also test 'generic' categorisation interface
        def _month_in_quarter(coord, pt_value):
            date = coord.units.num2date(pt_value)
            return (date.month - 1) % 3

        ccat.add_categorised_coord(cube,
                                   'my_month_in_quarter',
                                   time_coord,
                                   _month_in_quarter)

        # To ensure consistent results between 32-bit and 64-bit
        # platforms, ensure all the numeric categorisation coordinates
        # are always stored as int64.
        for coord in cube.coords():
            if coord.long_name is not None and coord.points.dtype.kind == 'i':
                coord.points = coord.points.astype(np.int64)

        # check values
        self.assertCML(cube, ('categorisation', 'quickcheck.cml'))
示例#8
0
    def test_basic(self):
        cube = self.cube
        time_coord = self.time_coord

        ccat.add_year(cube, time_coord, 'my_year')
        ccat.add_day_of_month(cube, time_coord, 'my_day_of_month')
        ccat.add_day_of_year(cube, time_coord, 'my_day_of_year')

        ccat.add_month(cube, time_coord, 'my_month')
        with warnings.catch_warnings(record=True):
            ccat.add_month_shortname(cube, time_coord, 'my_month_shortname')
        ccat.add_month_fullname(cube, time_coord, 'my_month_fullname')
        ccat.add_month_number(cube, time_coord, 'my_month_number')

        ccat.add_weekday(cube, time_coord, 'my_weekday')
        ccat.add_weekday_number(cube, time_coord, 'my_weekday_number')
        with warnings.catch_warnings(record=True):
            ccat.add_weekday_shortname(cube, time_coord,
                                       'my_weekday_shortname')
        ccat.add_weekday_fullname(cube, time_coord, 'my_weekday_fullname')

        ccat.add_season(cube, time_coord, 'my_season')
        ccat.add_season_number(cube, time_coord, 'my_season_number')
        with warnings.catch_warnings(record=True):
            ccat.add_season_month_initials(cube, time_coord,
                                           'my_season_month_initials')
        ccat.add_season_year(cube, time_coord, 'my_season_year')

        # also test 'generic' categorisation interface
        def _month_in_quarter(coord, pt_value):
            date = coord.units.num2date(pt_value)
            return (date.month - 1) % 3

        ccat.add_categorised_coord(cube, 'my_month_in_quarter', time_coord,
                                   _month_in_quarter)

        # To ensure consistent results between 32-bit and 64-bit
        # platforms, ensure all the numeric categorisation coordinates
        # are always stored as int64.
        for coord in cube.coords():
            if coord.long_name is not None and coord.points.dtype.kind == 'i':
                coord.points = coord.points.astype(np.int64)

        # check values
        self.assertCML(cube, ('categorisation', 'quickcheck.cml'))
    def test_basic(self):
        #make a series of 'day numbers' for the time, that slide across month boundaries
        day_numbers =  np.arange(0, 600, 27, dtype=np.int32)
        
        cube = iris.cube.Cube(day_numbers, long_name='test cube', units='metres')

        #use day numbers as data values also (don't actually use this for anything)
        cube.data = day_numbers 
        
        time_coord = iris.coords.DimCoord(
            day_numbers, standard_name='time', units=iris.unit.Unit('days since epoch', 'gregorian'))
        cube.add_dim_coord(time_coord, 0)

        #add test coordinates for examples wanted    
        ccat.add_year(cube, time_coord)
        ccat.add_day_of_month(cube, 'time')    #NB test passing coord-name instead of coord itself

        ccat.add_month(cube, time_coord)
        ccat.add_month_shortname(cube, time_coord, name='month_short')
        ccat.add_month_fullname(cube, time_coord, name='month_full')
        ccat.add_month_number(cube, time_coord, name='month_number')
        
        ccat.add_weekday(cube, time_coord)
        ccat.add_weekday_number(cube, time_coord, name='weekday_number')
        ccat.add_weekday_shortname(cube, time_coord, name='weekday_short')
        ccat.add_weekday_fullname(cube, time_coord, name='weekday_full')

        ccat.add_season(cube, time_coord)
        ccat.add_season_number(cube, time_coord, name='season_number')
        ccat.add_season_month_initials(cube, time_coord, name='season_months')
        ccat.add_season_year(cube, time_coord, name='year_ofseason')
        
        #also test 'generic' categorisation interface
        def _month_in_quarter(coord, pt_value):
            date = coord.units.num2date(pt_value)
            return (date.month - 1) % 3

        ccat.add_categorised_coord(cube, 'month_in_quarter', time_coord, _month_in_quarter)

        for coord_name in ['month_number', 'month_in_quarter', 'weekday_number', 'season_number', 'year_ofseason', 'year', 'day']:
            cube.coord(coord_name).points = cube.coord(coord_name).points.astype(np.int64)

        #check values
        self.assertCML(cube, ('categorisation', 'quickcheck.cml'))
def seasonal_mean(mycube):
    """
    Function to compute seasonal means with MEAN.

    Chunks time in 3-month periods and computes means over them;
    Returns a cube.
    """
    if 'clim_season' not in mycube.coords():
        coord_cat.add_season(mycube, 'time', name='clim_season')
    if 'season_year' not in mycube.coords():
        coord_cat.add_season_year(mycube, 'time', name='season_year')
    annual_seasonal_mean = mycube.aggregated_by(['clim_season', 'season_year'],
                                                iris.analysis.MEAN)

    def spans_three_months(time):
        """Check for three months."""
        return (time.bound[1] - time.bound[0]) == 90  # days

    three_months_bound = iris.Constraint(time=spans_three_months)
    return annual_seasonal_mean.extract(three_months_bound)
示例#11
0
    def test_basic(self):
        cube = self.cube
        time_coord = self.time_coord

        ccat.add_year(cube, time_coord, "my_year")
        ccat.add_day_of_month(cube, time_coord, "my_day_of_month")
        ccat.add_day_of_year(cube, time_coord, "my_day_of_year")

        ccat.add_month(cube, time_coord, "my_month")
        ccat.add_month_fullname(cube, time_coord, "my_month_fullname")
        ccat.add_month_number(cube, time_coord, "my_month_number")

        ccat.add_weekday(cube, time_coord, "my_weekday")
        ccat.add_weekday_number(cube, time_coord, "my_weekday_number")
        ccat.add_weekday_fullname(cube, time_coord, "my_weekday_fullname")

        ccat.add_season(cube, time_coord, "my_season")
        ccat.add_season_number(cube, time_coord, "my_season_number")
        ccat.add_season_year(cube, time_coord, "my_season_year")

        # also test 'generic' categorisation interface
        def _month_in_quarter(coord, pt_value):
            date = coord.units.num2date(pt_value)
            return (date.month - 1) % 3

        ccat.add_categorised_coord(
            cube, "my_month_in_quarter", time_coord, _month_in_quarter
        )

        # To ensure consistent results between 32-bit and 64-bit
        # platforms, ensure all the numeric categorisation coordinates
        # are always stored as int64.
        for coord in cube.coords():
            if coord.long_name is not None and coord.points.dtype.kind == "i":
                coord.points = coord.points.astype(np.int64)

        # check values
        self.assertCML(cube, ("categorisation", "quickcheck.cml"))
def derive_variability(cube, runid, lon_range_pdo, lon_range_amo, savedir, remove_global_means, neofs, period_title=''):
    sst_cube = cube.copy()
    # mask the sea-ice regions
    sst_cube = mask_ice(sst_cube)
    
    sst_cube_monthanom, tmp_avg = remove_monthly_avg(sst_cube)
    tpi_ts = tpi_timeseries(sst_cube_monthanom)
    tpi_smooth = tpi_ts['tpi'].copy()
    smooth_data = utilities_mr.smooth(tpi_ts['tpi'].data, window_len=156, window = 'hanning')
    print len(smooth_data)
    tpi_smooth.data[:len(smooth_data)] = smooth_data

    #print 'tpi', tpi_ts['tpi']
    #print 'reg2', tpi_ts['reg2']

    file_out = os.path.join(savedir, runid+'_tpi_timeseries.nc')
    tpi_ts['tpi'].long_name = 'TPI timeseries'
    iris.save(tpi_ts['tpi'], file_out, netcdf_format="NETCDF3_CLASSIC")

    #print 'sst cube for nino ',sst_cube
    try:
        icc.add_season(sst_cube, 'time', name='clim_season')
        icc.add_season_year(sst_cube, 'time', name='season_year')
    except:
        pass
    #print 'sst cube for nino ',sst_cube
    #print 'sst cube for nino ',sst_cube.coord('clim_season')
    
    sst_djf = sst_cube.extract(DJF_constraint)
    nino_timeseries = nino34_timeseries(sst_djf)
    nino_timeseries.long_name = 'NINO3.4 timeseries'
    file_out = os.path.join(savedir, runid+'_nino34_timeseries'+period_title+'.nc')
    iris.io.save(nino_timeseries,file_out, netcdf_format="NETCDF3_CLASSIC")

    # calculate annual means
    sst_ann = sst_cube.aggregated_by('year', iris.analysis.MEAN)

# mask out land when using surface temperature
    if remove_global_means: sst_ann = remove_glob_avg(sst_ann)
    
# calculate PDO EOfs, timeseries and regression
    pdo_pc = calculate_pdo_eof_timeseries(runid, sst_ann, savedir, lat_range_pdo, lon_range_pdo, neofs, remove_global_means, period_title)
    #print 'pdo_pc ',pdo_pc

# calculate PDO SST regression and write file
    pltdir = os.path.join(savedir, 'pdo_patterns/plts/gc2/')
    if not os.path.exists(pltdir):
        os.makedirs(pltdir)
    pdofile = runid+'_pdo_eof1_pc_time_series_glob_mean_sst_removed'+period_title+'.nc'
    pdo_cube = iris.load(savedir + pdofile)[0]
    calc_regression(sst_ann, pltdir, pdo_cube, runid, 'PDO', period_title)

#calculate AMO timeseries
    amo_timeseries = calc_amo_timeseries(sst_ann, runid, lon_range_amo, lat_range_amo)
    amo_timeseries.long_name='AMO timeseries'
    file_out = os.path.join(savedir, runid+'_amo_timeseries_trenberth'+period_title+'.nc')
    iris.io.save(amo_timeseries,file_out, netcdf_format="NETCDF3_CLASSIC")

    
#calculate AMO regression and write files
    amodir = os.path.join(savedir, 'amo')
    if not os.path.exists(amodir): os.makedirs(amodir)
    calculate_amo_regression(sst_ann, amo_timeseries, lon_range_amo, lat_range_amo, amodir, runid, period_title)

    resol_model = ['HadISST']; title = 'HadISST'; desc_model = ['Nino3.4','AMO','PDO','TPI']; 
    ymin = [-3, -0.3, -0.2, -1.]
    ymax = [3, 0.3, 0.2, 1.]
    fig = plt.figure(figsize=(8,11),dpi=100)#dpi=300
    for i, ts in enumerate([nino_timeseries, amo_timeseries, pdo_pc, tpi_ts['tpi']]):
        subpl=fig.add_subplot(4,1,i+1)
        period = 'year'
        if i == 3: period = 'month'
        plot_timeseries(i, runid, ts, resol_model[0], desc_model[i], title, \
                        subpl, ymin[i], ymax[i], period = period)
    plt.savefig(os.path.join(savedir,runid+'_modes_timeseries'+period_title+'.png'))
    plt.show()
    del sst_cube
示例#13
0
def consecutive_dry_days(cube, period='year', length=6, threshold=1.):
    """
    calculate consecutive dry days within an iris.cube.Cube
    Args:
    * cube (iris.cube.Cube):
        An iris.cube.Cube holding precipiation amount in mm/day
    * period (string):
        Period over that the CDD will be calculated. Can be 'year', 'season'
        or 'month'. If period is 'season' or 'month' the CDD will be averaged
        over the years
    Kwargs:
    * length (int):
        The number of days without rainfall that define a dry period
    * threshold (float):
        The upper limit of daily rainfall in mm that indicates
        'no precipitation'
    Returns:
        An iris.cube.CubeList that holds two iris.cube.Cubes with the longest
        period of dry days in the given period and the mean of the number of
        dry periods with respect to the given length
    """
    def _cdd_index(array, axis, threshold):
        """
        Calculate the consecutive dry days index.
        This function is used as an iris.analysis.Aggregator
        Args:
        * array (numpy.array or numpy.ma.array):
            array that holds the precipitation data
        * axis (int):
            the number of the time-axis
        * threshold (float):
            the threshold that indicates a precipiation-less day
        Returns:
            the aggregation result, collapsing the 'axis' dimension of
            the 'data' argument
        """
        from pycat.analysis.utils import (_get_max_true_block_length,
                                          _get_true_block_lengths)

        up_down = _get_true_block_lengths(array < threshold, axis)
        return _get_max_true_block_length(up_down)

    def _cdd_periods(array, axis, threshold, length):
        """
        Calculate the number of consecutive dry days periods.
        This function is used as an iris.analysis.Aggregator
        Args:
        * array (numpy.array or numpy.ma.array):
            array that holds the precipitation data
        * axis (int):
            the number of the time-axis
        * threshold (float):
            the threshold that indicates a precipiation-less day
        * length (int):
            number of days that a dry period must last
        Returns:
            the aggregation result, collapsing the 'axis' dimension
            of the 'data' argument
        """
        from pycat.analysis.utils import (_get_len_true_block_length,
                                          _get_true_block_lengths)

        up_down = _get_true_block_lengths(array < threshold, axis)
        return _get_len_true_block_length(up_down, length)

    # build the iris.analysis.Aggregators
    cdd_index = Aggregator('cdd_index', _cdd_index)
    cdd_periods = Aggregator('cdd_periods', _cdd_periods)

    # check if the cube already has the needed auxiliary coordinates
    if period == 'season':
        # add the season_year auxiliary coordinate
        try:
            years = np.unique(cube.coord('season_year').points)
        except CoordinateNotFoundError:
            ccat.add_season_year(cube, 'time')
            years = np.unique(cube.coord('season_year').points)
        constraint_year_key = 'season_year'
    else:
        # add calendar years
        try:
            years = np.unique(cube.coord('year').points)
        except CoordinateNotFoundError:
            ccat.add_year(cube, 'time')
            years = np.unique(cube.coord('year').points)
        constraint_year_key = 'year'

    if period in ['season', 'month']:
        try:
            index_period = np.unique(cube.coord('%s_number' % period).points)
        except CoordinateNotFoundError:
            cat = getattr(ccat, 'add_%s_number' % period)
            cat(cube, 'time')
            index_period = np.unique(cube.coord('%s_number' % period).points)

    # create time-axis of resulting cubes
    time_dimension = _make_time_dimension(
        cube.coord('time').units.num2date(cube.coord('time').points[0]),
        cube.coord('time').units.num2date(cube.coord('time').points[-1]),
        period=period)
    # create the empty resulting cubes
    dim_coords_and_dims = []
    slices = []
    for coord in cube.dim_coords:
        if coord.units.is_time_reference():
            dim_coords_and_dims.append(
                (time_dimension, cube.coord_dims(coord)))
            slices.append(0)
            time_axis = cube.coord_dims(coord)[0]
        else:
            dim_coords_and_dims.append((coord, cube.coord_dims(coord)))
            slices.append(slice(None, None, None))

    cdd_index_cube = _create_cube(
        long_name='Consecutive dry days is the greatest number of '
        'consecutive days per time period with daily '
        'precipitation amount below %s mm.' % threshold,
        var_name='consecutive_dry_days_index_per_time_period',
        units=iris.unit.Unit('1'),
        dim_coords_and_dims=dim_coords_and_dims)

    cdd_periods_cube = _create_cube(
        long_name='Number of cdd periods in given time period '
        'with more than %d days.' % length,
        var_name='number_of_cdd_periods_with_more_than_'
        '%ddays_per_time_period' % length,
        units=iris.unit.Unit('1'),
        dim_coords_and_dims=dim_coords_and_dims)

    # differentiate between the considered period
    if period == 'year':
        # just run the aggregation over all given years resulting in
        # the maximum cdd length and the number of cdd periods for each year
        for year in years:
            tmp_cube = cube.extract(iris.Constraint(year=year))
            slices[time_axis] = year - years[0]
            cdd_index_data = tmp_cube.collapsed('time',
                                                cdd_index,
                                                threshold=threshold).data
            cdd_periods_data = tmp_cube.collapsed('time',
                                                  cdd_periods,
                                                  threshold=threshold,
                                                  length=length).data

            cdd_index_cube.data[slices] = cdd_index_data
            cdd_periods_cube.data[slices] = cdd_periods_data

        return iris.cube.CubeList((cdd_index_cube, cdd_periods_cube))

    else:
        # run the aggregation over all seasons/months of all years
        # afterwards aggregate the seasons/month by MAX Aggregator
        # for the cdd_index and the MEAN Aggregator for cdd_periods
        for year in years:
            for p in index_period:
                constraint_dict = {
                    '%s_number' % period: p,
                    constraint_year_key: year
                }
                tmp_cube = cube.extract(iris.Constraint(**constraint_dict))
                if tmp_cube:
                    # the extraction can lead to empty cubes for seasons
                    # in the last year
                    time_index = (year - years[0]) * len(index_period) + p
                    # months numbers start at 1
                    if period == 'month':
                        time_index -= 1
                    slices[time_axis] = time_index
                    cdd_index_data = tmp_cube.collapsed(
                        'time', cdd_index, threshold=threshold).data
                    cdd_periods_data = tmp_cube.collapsed('time',
                                                          cdd_periods,
                                                          threshold=threshold,
                                                          length=length).data

                    cdd_index_cube.data[slices] = cdd_index_data
                    cdd_periods_cube.data[slices] = cdd_periods_data

        # aggregate over seasons/months
        cat = getattr(ccat, 'add_%s' % period)
        cat(cdd_index_cube, 'time')
        cat(cdd_periods_cube, 'time')

        cdd_index_mean = cdd_index_cube.aggregated_by(period,
                                                      iris.analysis.MEAN)
        cdd_periods_mean = cdd_periods_cube.aggregated_by(
            period, iris.analysis.MEAN)

        cdd_index_mean.remove_coord('time')
        cdd_periods_mean.remove_coord('time')

        return iris.cube.CubeList((cdd_index_mean, cdd_periods_mean))
示例#14
0
文件: indices.py 项目: mwjury/pyCAT
def consecutive_dry_days(cube, period='year', length=6, threshold=1.):
    """
    calculate consecutive dry days within an iris.cube.Cube

    Args:

    * cube (iris.cube.Cube):
        An iris.cube.Cube holding precipiation amount in mm/day
    * period (string):
        Period over that the CDD will be calculated. Can be 'year', 'season'
        or 'month'. If period is 'season' or 'month' the CDD will be averaged
        over the years

    Kwargs:

    * length (int):
        The number of days without rainfall that define a dry period

    * threshold (float):
        The upper limit of daily rainfall in mm that indicates 'no precipitation'

    Returns:

        An iris.cube.CubeList that holds two iris.cube.Cubes with the longest
        period of dry days in the given period and the mean of the number of
        dry periods with respect to the given length
    """
    def _cdd_index(array, axis, threshold):
        """
        Calculate the consecutive dry days index.

        This function is used as an iris.analysis.Aggregator

        Args:

        * array (numpy.array or numpy.ma.array):
            array that holds the precipitation data
        
        * axis (int):
            the number of the time-axis

        * threshold (float):
            the threshold that indicates a precipiation-less day

        Returns:
            the aggregation result, collapsing the 'axis' dimension of the 'data' argument
        """
        from pycat.analysis.utils import _get_max_true_block_length, _get_true_block_lengths

        up_down = _get_true_block_lengths(array<threshold, axis)
        return _get_max_true_block_length(up_down)

    def _cdd_periods(array, axis, threshold, length):
        """
        Calculate the number of consecutive dry days periods.

        This function is used as an iris.analysis.Aggregator

        Args:

        * array (numpy.array or numpy.ma.array):
            array that holds the precipitation data
        
        * axis (int):
            the number of the time-axis

        * threshold (float):
            the threshold that indicates a precipiation-less day

        * length (int):
            number of days that a dry period must last

        Returns:
            the aggregation result, collapsing the 'axis' dimension of the 'data' argument
        """
        from pycat.analysis.utils import _get_len_true_block_length, _get_true_block_lengths

        up_down = _get_true_block_lengths(array<threshold, axis)
        return _get_len_true_block_length(up_down, length)

    # build the iris.analysis.Aggregators
    cdd_index = Aggregator('cdd_index', _cdd_index)
    cdd_periods = Aggregator('cdd_periods', _cdd_periods)

    # check if the cube already has the needed auxiliary coordinates
    if period == 'season':
        # add the season_year auxiliary coordinate
        try:
            years = np.unique(cube.coord('season_year').points)
        except CoordinateNotFoundError:
            ccat.add_season_year(cube, 'time')
            years = np.unique(cube.coord('season_year').points)
        constraint_year_key = 'season_year'
    else:
        # add calendar years
        try:
            years = np.unique(cube.coord('year').points)
        except CoordinateNotFoundError:
            ccat.add_year(cube, 'time')
            years = np.unique(cube.coord('year').points)
        constraint_year_key = 'year'
        
    if period in ['season', 'month']:
        try:
            index_period = np.unique(cube.coord('%s_number'%period).points)
        except CoordinateNotFoundError:
            cat = getattr(ccat, 'add_%s_number' % period)
            cat(cube, 'time')
            index_period = np.unique(cube.coord('%s_number'%period).points)

    # create time-axis of resulting cubes
    time_dimension = _make_time_dimension(
        cube.coord('time').units.num2date(cube.coord('time').points[0]),
        cube.coord('time').units.num2date(cube.coord('time').points[-1]),
        period=period)
    # create the empty resulting cubes
    dim_coords_and_dims = []
    slices = []
    for coord in cube.dim_coords:
        if coord.units.is_time_reference():
            dim_coords_and_dims.append((time_dimension, cube.coord_dims(coord)))
            slices.append(0)
            time_axis = cube.coord_dims(coord)[0]
        else:
            dim_coords_and_dims.append((coord, cube.coord_dims(coord)))
            slices.append(slice(None,None,None))

    cdd_index_cube = _create_cube(
        long_name='Consecutive dry days is the greatest number of consecutive days per time period with daily precipitation amount below %s mm.' % threshold,
        var_name='consecutive_dry_days_index_per_time_period',
        units=iris.unit.Unit('1'),
        dim_coords_and_dims=dim_coords_and_dims)

    cdd_periods_cube = _create_cube(
        long_name='Number of cdd periods in given time period with more than %d days.' % length,
        var_name='number_of_cdd_periods_with_more_than_%ddays_per_time_period' % length,
        units=iris.unit.Unit('1'),
        dim_coords_and_dims=dim_coords_and_dims)

    # differentiate between the considered period
    if period == 'year':
        # just run the aggregation over all given years resulting in
        # the maximum cdd length and the number of cdd periods for each year
        for year in years:
            tmp_cube = cube.extract(iris.Constraint(year=year))
            slices[time_axis] = year-years[0]
            cdd_index_data = tmp_cube.collapsed(
                'time', cdd_index, threshold=threshold).data
            cdd_periods_data = tmp_cube.collapsed(
                'time', cdd_periods, threshold=threshold, length=length).data
            
            cdd_index_cube.data[slices] = cdd_index_data
            cdd_periods_cube.data[slices] = cdd_periods_data

        return iris.cube.CubeList(
            (cdd_index_cube, cdd_periods_cube)
        )

    else:
        # run the aggregation over all seasons/months of all years
        # afterwards aggregate the seasons/month by MAX Aggregator
        # for the cdd_index and the MEAN Aggregator for cdd_periods
        for year in years:
            for p in index_period:
                constraint_dict = {'%s_number' % period: p,
                                   constraint_year_key: year}
                tmp_cube = cube.extract(iris.Constraint(**constraint_dict))
                if tmp_cube:
                    # the extraction can lead to empty cubes for seasons
                    # in the last year
                    time_index = (year-years[0])*len(index_period) + p
                    # months numbers start at 1
                    if period == 'month':
                        time_index -= 1
                    slices[time_axis] = time_index
                    cdd_index_data = tmp_cube.collapsed(
                        'time', cdd_index, threshold=threshold).data
                    cdd_periods_data = tmp_cube.collapsed(
                        'time', cdd_periods, threshold=threshold, length=length).data
            
                    cdd_index_cube.data[slices] = cdd_index_data
                    cdd_periods_cube.data[slices] = cdd_periods_data
                    
        # aggregate over seasons/months
        cat = getattr(ccat, 'add_%s' % period)
        cat(cdd_index_cube, 'time')
        cat(cdd_periods_cube, 'time')
        
        cdd_index_mean = cdd_index_cube.aggregated_by(period, iris.analysis.MEAN)
        cdd_periods_mean = cdd_periods_cube.aggregated_by(period, iris.analysis.MEAN)

        cdd_index_mean.remove_coord('time')
        cdd_periods_mean.remove_coord('time')
        return iris.cube.CubeList(
            (cdd_index_mean, cdd_periods_mean)
        )