def identify_gaps_in_files(data_req):
    """
    Check the files in the data request to see if there
    appears to be any files missing from this dataset.
    """
    one_day = 1.
    data_files = data_req.datafile_set.order_by('name')
    gap_found = False
    msg = ''
    for index, data_file in enumerate(data_files):
        if index == 0:
            continue
        start_time = cf_units.num2date(data_file.start_time,
                                       data_file.time_units,
                                       data_file.calendar)
        previous_end_time = cf_units.num2date(data_files[index - 1].end_time,
                                              data_files[index - 1].time_units,
                                              data_files[index - 1].calendar)
        difference = start_time - previous_end_time
        if difference.days > one_day:
            gap_found = True
            msg = f'{difference.days} day gap prior to {data_file.name}'
        else:
            # End of gap so report if there was a gap
            if gap_found:
                print(msg)
                gap_found = False

    if gap_found:
        # Gap extends to the end of files so report last message
        print(msg)
Exemple #2
0
    def _autoparse_filename_(self, fname):
        """
        Determine if autoparsing the filename needs to be done

        Parameters:
            fname (str): The original name of the file

        Returns:
            str: The new name for the file
        """

        if '{' in fname:

            possible_tvars = []
            possible_inputs = list(self.inputs)
            if self._filedesc.autoparse_time_variable:
                possible_tvars.append(self._filedesc.autoparse_time_variable)
                possible_inputs += self._hidden_inputs
            else:
                for var in self._filedesc.variables:
                    vdesc = self._filedesc.variables[var]
                    if var in ('time', 'time1', 'time2', 'time3'):
                        possible_tvars.append(var)
                    elif vdesc.cfunits().is_time_reference() and len(vdesc.dimensions) == 1:
                        possible_tvars.append(var)
                    elif 'standard_name' in vdesc.attributes and vdesc.attributes['standard_name'] == 'time':
                        possible_tvars.append(var)
                    elif 'axis' in vdesc.attributes and vdesc.attributes['axis'] == 'T':
                        possible_tvars.append(var)
            if len(possible_tvars) == 0:
                msg = 'Could not identify a time variable to autoparse filename {!r}'.format(fname)
                warn(msg, DateTimeAutoParseWarning)
                return fname
            possible_tnodes = {vnode.label:vnode for vnode in possible_inputs
                               if vnode.label in possible_tvars}
            if len(possible_tnodes) == 0:
                raise ValueError('Time variable input missing for file {!r}'.format(fname))
            tnode = possible_tnodes['time'] if 'time' in possible_tnodes else possible_tnodes.values()[0]

            t1 = tnode[0:1]
            t2 = tnode[-1:]

            while '{' in fname:
                beg = fname.find('{')
                end = fname.find('}', beg)
                if end == -1:
                    raise ValueError(
                        'Filename {!r} has unbalanced special characters'.format(fname))
                prefix = fname[:beg]
                fmtstr1, fmtstr2 = fname[beg + 1:end].split('-')
                suffix = fname[end + 1:]

                datestr1 = num2date(t1.data[0], str(t1.units), t1.units.calendar).strftime(
                    fmtstr1).replace(' ', '0')
                datestr2 = num2date(t2.data[0], str(t2.units), t2.units.calendar).strftime(
                    fmtstr2).replace(' ', '0')

                fname = '{}{}-{}{}'.format(prefix, datestr1, datestr2, suffix)

        return fname
Exemple #3
0
 def test_num2date_wrong_calendar(self):
     with self.assertRaisesRegex(
         ValueError, "illegal calendar or reference date"
     ):
         num2date(
             1,
             "days since 1970-01-01",
             calendar="360_day",
             only_use_cftime_datetimes=False,
             only_use_python_datetimes=True,
         )
def __check_date_alignment_in_file__(date):
    """
    Evaluates the dates between time steps to verify that
    no gaps are found in the time dimension.

    Parameters:
        date (dict): Contains file information, such as time period(t_per), time step(t_step), 
                          first/last step(t0 and tn), slice counts(cnt).

    """
    t_step = date['t_step']
    t_per = date['t_per']
    t = date['time']
    prev_time = t[0]

    # If the time period in monthly, then the time difference between slices is not uniform and
    # requires looking into the month length array to get correct day #'s
    # between slices.
    if t_per == 'mon':
        if t_per == 'mon':
            date1 = (parser.parse(str(cf_units.num2date(t[0],
                                                        date['units'], calendar=date['calendar']))).timetuple())
            if date1[1] == 12:
                next_val = 1
            else:
                next_val = date1[1] + 1
        for i in range(1, len(t)):
            new_date = (parser.parse(str(cf_units.num2date(t[i],
                                                           date['units'], calendar=date['calendar']))).timetuple())
            if (next_val == new_date[1]):
                date1 = (parser.parse(str(cf_units.num2date(t[i],
                                                            date['units'], calendar=date['calendar']))).timetuple())
                if date1[1] == 12:
                    next_val = 1
                else:
                    next_val = date1[1] + 1
            else:
                print "Disconnect? Expected: ", next_val, " Got: ", new_date[1], ' around time step: ', i
                return 1
    # All other time periods should have the same number of days between
    # slices.
    else:
        for i in range(1, len(t)):
            if (prev_time + t_step == t[i]):
                # Time step looks okay
                prev_time = t[i]
            else:
                print "Disconnect? Expected: ", str(prev_time + t_step), " Got: ", t[i], ' around time step: ', i
                return 1
    return 0
def __check_date_alignment__(keys, date_info):
    """
    Evaluates the dates between files to verify that
    no gaps are found in the time dimension.

    Parameters:
        keys (list): A list of time slice references that are in correct time order.
        date_info (dict): Contains file information, such as time period(t_per), time step(t_step), 
            first/last step(t0 and tn), slice counts(cnt).
    """
    prev_last = date_info[keys[0]]['tn']
    t_step = date_info[keys[0]]['t_step']

    if date_info[keys[0]]['t_per'] == 'mon':
        date = (parser.parse(str(cf_units.num2date(date_info[keys[0]]['tn'],
                                                   date_info[keys[0]]['units'], calendar=date_info[keys[0]]['calendar']))).timetuple())
        if date[1] == 12:
            next_val = 1
        else:
            next_val = date[1] + 1

    else:
        next_val = prev_last + t_step

    for i in range(1, len(keys)):
        if date_info[keys[i]]['t_per'] == 'mon':
            new_date = (parser.parse(str(cf_units.num2date(date_info[keys[i]]['t0'],
                                                           date_info[keys[i]]['units'], calendar=date_info[keys[i]]['calendar']))).timetuple())
            if (next_val == new_date[1]):
                date = (parser.parse(str(cf_units.num2date(date_info[keys[i]]['tn'],
                                                           date_info[keys[i]]['units'], calendar=date_info[keys[i]]['calendar']))).timetuple())
                if date[1] == 12:
                    next_val = 1
                else:
                    next_val = date[1] + 1
            else:
                print "Disconnect? Expected: ", next_val, " Got: ", new_date[1]
                return 1
        else:
            if (next_val == date_info[keys[i]]['t0']):
                # print "Looks
                # okay",date_info[keys[i]]['t0'],'-',date_info[keys[i]]['tn']
                prev_last = date_info[keys[i]]['tn']
                next_val = prev_last + t_step
            else:
                print "Disconnect? Expected: ", next_val, " Got: ", date_info[keys[i]]['t0']
                return 1

    return 0
Exemple #6
0
 def create_data_object(self, filenames, variable):
     data_dict = {}  #initialise data dictionary
     inData = netCDF4.Dataset(filenames[0])  #open netCDF file
     data_dict['longitude'] = np.array(
         inData.variables['lon'])  #extract longitudes
     data_dict['latitude'] = np.array(
         inData.variables['lat'])  #extract latitudes
     origTimes = np.array(inData.variables['time_counter'])  #extract times
     #Convert time to days since
     niceDateTime = cf_units.num2date(origTimes,
                                      'seconds since 1999-01-01 00:00:00',
                                      'gregorian')
     data_dict['time'] = cf_units.date2num(
         niceDateTime, 'days since 1600-01-01 00:00:00', 'gregorian')
     data_dict[variable] = np.array(
         inData.variables[variable])  #extract requested variable
     inData.close()  #close netCDF file
     coords = self._create_coord_list(filenames, data_dict)
     return UngriddedData(
         data_dict[variable],
         Metadata(name=variable,
                  long_name=variable,
                  shape=(len(data_dict), ),
                  missing_value=-999.0,
                  units="1"), coords)
Exemple #7
0
    def start_time(self):
        std_units = Settings.get_solo().standard_time_units

        start_times = self.datafile_set.values_list('start_time', 'time_units',
            'calendar')

        if not start_times:
            return None

        std_times = [
            (standardise_time_unit(time, unit, std_units, cal), cal)
            for time, unit, cal in start_times
        ]

        none_values_removed = [(std_time, cal)
                               for std_time, cal in std_times
                               if std_time is not None]

        if not none_values_removed:
            return None

        earliest_time, calendar = min(none_values_removed, key=lambda x: x[0])

        earliest_obj = cf_units.num2date(earliest_time, std_units, calendar)

        return earliest_obj.strftime('%Y-%m-%d')
Exemple #8
0
 def end_string(self):
     if self.end_time is not None and self.time_units and self.calendar:
         return safe_strftime(
             cf_units.num2date(self.end_time, self.time_units,
                                  self.calendar), '%Y-%m-%d')
     else:
         return None
Exemple #9
0
def construct_time_string(time_point, time_units, calendar, frequency):
    """
    Calculate the time string to the appropriate resolution for use in CMIP6
    filenames according to http://goo.gl/v1drZl

    :param float time_point: the start time
    :param str time_units: the time's units
    :param str calendar: the time's calendar
    :param str frequency: the variables' frequnecy string
    :returns: the time point
    :rtype: str
    :raises NotImplementedError: if the frequency isn't known
    """
    formats = {
        'ann': '%Y',
        'mon': '%Y%m',
        'day': '%Y%m%d',
        '6hr': '%Y%m%d%H%M',
        '3hr': '%Y%m%d%H%M',
        '1hr': '%Y%m%d%H%M',
    }

    try:
        time_fmt = formats[frequency]
    except KeyError:
        msg = 'No time format known for frequency string {}'.format(frequency)
        raise NotImplementedError(msg)

    datetime_obj = cf_units.num2date(time_point, time_units, calendar)

    return datetime_obj.strftime(time_fmt)
Exemple #10
0
def standardise_time_unit(time_float, time_unit, standard_unit, calendar):
    """
    Standardise a floating point time in one time unit by returning the
    corresponding time in the `standard_unit`. The original value is returned if
    it is already in the `standard_unit`. None is returned if the `time_float`
    is None.

    :param float time_float: The time to change
    :param str time_unit: The original time's units
    :param str standard_unit: The new unit
    :param str calendar: The cftime calendar
    :returns: A floating point representation of the old time in
        `standard_unit`
    """
    if (time_float is None or time_unit is None or
            standard_unit is None or calendar is None):
        return None

    if time_unit == standard_unit:
        return time_float

    date_time = cf_units.num2date(time_float, time_unit, calendar)
    corrected_time = cf_units.date2num(date_time, standard_unit, calendar)

    return corrected_time
Exemple #11
0
def calc_last_day_in_month(year, month, calendar):
    """
    Calculate the last day of the specified month using the calendar given.

    :param int year: The year
    :param int month: The month
    :param str calendar: The calendar to use, which must be supported by
        cf_units
    :returns: The last day of the specified month
    :rtype: int
    """
    ref_units = 'days since 1969-07-21'

    if month == 12:
        start_next_month_obj = netcdftime.datetime(year + 1, 1, 1)
    else:
        start_next_month_obj = netcdftime.datetime(year, month + 1, 1)

    start_next_month = cf_units.date2num(start_next_month_obj, ref_units,
                                         calendar)

    end_this_month = cf_units.num2date(start_next_month - 1, ref_units,
                                       calendar)

    return end_this_month.day
Exemple #12
0
    def end_time(self):
        std_units = Settings.get_solo().standard_time_units

        end_times = self.obs_files.values_list('end_time', 'time_units',
                                               'calendar')

        if not end_times:
            return None

        std_times = [
            (standardise_time_unit(time, unit, std_units, cal), cal)
            for time, unit, cal in end_times
        ]

        none_values_removed = [(std_time, cal)
                               for std_time, cal in std_times
                               if std_time is not None]

        if not none_values_removed:
            return None

        latest_time, calendar = max(none_values_removed, key=lambda x: x[0])

        latest_obj = cf_units.num2date(latest_time, std_units, calendar)

        return latest_obj
Exemple #13
0
 def test_fix_metadata(self):
     """Test fix for bad calendar."""
     cube = self.fix.fix_metadata([self.cube])[0]
     time = cube.coord('time')
     dates = num2date(time.points, time.units.name, time.units.calendar)
     self.assertEqual(time.units.calendar, 'gregorian')
     self.assertEqual(dates[0].strftime('%Y%m%d%H%M'), ' 30001161200')
     self.assertEqual(dates[1].strftime('%Y%m%d%H%M'), '185001161200')
Exemple #14
0
def test_get_cube_time_axis_in_calendar(test_generic_tas_cube, out_calendar):
    tcn = test_generic_tas_cube.cube.coord_dims("time")[0]
    ttime = test_generic_tas_cube.cube.dim_coords[tcn]
    expected = cf_units.num2date(ttime.points, ttime.units.name, out_calendar)

    result = get_scm_cube_time_axis_in_calendar(test_generic_tas_cube,
                                                out_calendar)
    np.testing.assert_array_equal(result, expected)
def num2date(time_value, unit, calendar):
    if ('common_year' in unit):
        my_unit = unit.replace('common_year', 'day')
        my_time_value = time_value * 365
    else:
        my_unit = unit
        my_time_value = time_value
    return cf_units.num2date(my_time_value, my_unit, calendar)
Exemple #16
0
def num2date(time_value, unit, calendar):
## fix for fractional time bounds
    if (math.floor(time_value) != time_value):
        time_value = int(round(time_value))
    if ('common_year' in unit):
        my_unit = unit.replace('common_year', 'day')
        my_time_value = int(round(time_value)) * 365
    else:
        my_unit = unit
        my_time_value = time_value
    return cf_units.num2date(my_time_value, my_unit, calendar)
Exemple #17
0
def test_assert_all_time_axes_same(test_generic_tas_cube):
    tcn = test_generic_tas_cube.cube.coord_dims("time")[0]
    ttime = test_generic_tas_cube.cube.dim_coords[tcn]
    ttime_axis = cf_units.num2date(ttime.points, ttime.units.name, "gregorian")

    assert_all_time_axes_same([ttime_axis, ttime_axis])

    otime_axis = ttime_axis - datetime.timedelta(10)

    error_msg = re.escape("all the time axes should be the same")
    with pytest.raises(AssertionError, match=error_msg):
        assert_all_time_axes_same([otime_axis, ttime_axis])
Exemple #18
0
def get_branch_year(data_cube, control_time_units):
    """Get the year of the branching in control run."""

    if not control_time_units:
        control_time_units = gio.fix_time_descriptor(data_cube.attributes['parent_time_units'])
    else:
        control_time_units = control_time_units.replace("_", " ")
    branch_time = data_cube.attributes['branch_time_in_parent']
    branch_datetime = cf_units.num2date(branch_time, control_time_units, cf_units.CALENDAR_STANDARD)
    branch_year = branch_datetime.year
    print(f"Branch year: {branch_year}")

    return branch_year
 def create_data_object(self, filenames, variable):
     data_dict = {} #initialise data dictionary
     inData = netCDF4.Dataset(filenames[0]) #open netCDF file
     data_dict['longitude'] = np.array(inData.variables['lon']) #extract longitudes
     data_dict['latitude'] = np.array(inData.variables['lat']) #extract latitudes
     origTimes = np.array(inData.variables['time_counter']) #extract times
     #Convert time to days since 
     niceDateTime = cf_units.num2date(origTimes,'seconds since 1999-01-01 00:00:00', 'gregorian')
     data_dict['time'] = cf_units.date2num(niceDateTime,'days since 1600-01-01 00:00:00', 'gregorian')
     data_dict[variable] = np.array(inData.variables[variable])  #extract requested variable
     inData.close() #close netCDF file
     coords = self._create_coord_list(filenames,data_dict)
     return UngriddedData(data_dict[variable],Metadata(name=variable,long_name=variable,shape=(len(data_dict),),missing_value=-999.0,units="1"),coords)
Exemple #20
0
 def _create_coord_list(self, filenames, data=None):
     if data is None:
         data = {}  #initialise data dictionary
         inData = netCDF4.Dataset(filenames[0])  #open netCDF file
         data['longitude'] = np.array(
             inData.variables['longitude'])  #extract longitudes
         data['latitude'] = np.array(
             inData.variables['latitude'])  #extract latitudes
         origTimes = np.array(inData.variables['time'])  #extract times
         #convert to days since 1600-01-01 (cis col doesn't work otherwise - not sure why...):
         niceDateTime = cf_units.num2date(origTimes,
                                          'days since 1990-01-01 00:00:00',
                                          'gregorian')
         data['time'] = cf_units.date2num(niceDateTime,
                                          'days since 1600-01-01 00:00:00',
                                          'gregorian')
         inData.close()  #close netCDF file
     coords = CoordList()  #initialise coordinate list
     #Append latitudes and longitudes to coordinate list:
     coords.append(
         Coord(
             data['longitude'],
             Metadata(name="longitude",
                      long_name='longitude',
                      standard_name='longitude',
                      shape=(len(data), ),
                      missing_value=-999.0,
                      units="degrees_east",
                      range=(-180, 180)), "x"))
     coords.append(
         Coord(
             data['latitude'],
             Metadata(name="latitude",
                      long_name='latitude',
                      standard_name='latitude',
                      shape=(len(data), ),
                      missing_value=-999.0,
                      units="degrees_north",
                      range=(-90, 90)), "y"))
     coords.append(
         Coord(
             data['time'],
             Metadata(name="time",
                      long_name='time',
                      standard_name='time',
                      shape=(len(data), ),
                      missing_value=-999.0,
                      units="days since 1600-01-01 00:00:00"), "t"))
     return coords
 def _create_coord_list(self, filenames, data=None):
     if data is None:
         data = {} #initialise data dictionary
         inData = netCDF4.Dataset(filenames[0]) #open netCDF file
         data['longitude'] = np.array(inData.variables['lon']) #extract longitudes
         data['latitude'] = np.array(inData.variables['lat']) #extract latitudes
         origTimes = np.array(inData.variables['time_counter']) #extract times
         #Convert time to days since 
         niceDateTime = cf_units.num2date(origTimes,'seconds since 1999-01-01 00:00:00', 'gregorian')
         data['time_counter'] = cf_units.date2num(niceDateTime,'days since 1600-01-01 00:00:00', 'gregorian')
         inData.close() #close netCDF file
     coords = CoordList() #initialise coordinate list
     #Append latitudes and longitudes to coordinate list:
     coords.append(Coord(data['longitude'],Metadata(name="longitude",long_name='longitude',standard_name='longitude',shape=(len(data),),missing_value=-999.0,units="degrees_east",range=(-180, 180)),"x"))
     coords.append(Coord(data['latitude'],Metadata(name="latitude",long_name='latitude',standard_name='latitude',shape=(len(data),),missing_value=-999.0,units="degrees_north",range=(-90, 90)),"y"))
     coords.append(Coord(data['time'],Metadata(name="time",long_name='time',standard_name='time',shape=(len(data),),missing_value=-999.0,units="days since 1600-01-01 00:00:00"),"t"))
     return coords
Exemple #22
0
def get_scm_cube_time_axis_in_calendar(scm_cube, calendar):
    """Gets a cube's time axis in a given calendar

    Parameters
    ----------
    scm_cube : :obj:`SCMCube`
        An ``SCMCube`` instance.
    calendar : str
        The calendar to return the time axis in e.g. '365_day', 'gregorian'.

    Returns
    -------
    np.ndarray
        Array of datetimes, containing the cube's calendar.
    """
    time_coord_number = scm_cube.cube.coord_dims("time")[0]
    time = scm_cube.cube.dim_coords[time_coord_number]
    return cf_units.num2date(time.points, time.units.name, calendar)
Exemple #23
0
def _fix_time_monthly(cube):
    """Fix time by setting it to 15th of month."""
    # Read dataset time unit and calendar from file
    dataset_time_unit = str(cube.coord('time').units)
    dataset_time_calender = cube.coord('time').units.calendar
    # Convert datetime
    time_as_datetime = cf_units.num2date(
        cube.coord('time').core_points(), dataset_time_unit,
        dataset_time_calender)
    newtime = []
    for timepoint in time_as_datetime:
        midpoint = datetime(timepoint.year, timepoint.month, 15)
        newtime.append(midpoint)

    newtime = cf_units.date2num(newtime, dataset_time_unit,
                                dataset_time_calender)
    # Put them on the file
    cube.coord('time').points = newtime
    cube.coord('time').bounds = None
    return cube
def time_processing_period(cube, grib):
    """
    For template 4.8 (time mean, time max, etc).

    The time range is taken from the 'time' coordinate bounds.
    If the cell-method coordinate is not 'time' itself, the type of statistic
    will not be derived and the save process will be aborted.

    """
    # We could probably split this function up a bit

    # Can safely assume bounded pt.
    pt_coord = cube.coord("time")
    end = cf_units.num2date(pt_coord.bounds[0, 1], pt_coord.units.name,
                            pt_coord.units.calendar)

    gribapi.grib_set_long(grib, "yearOfEndOfOverallTimeInterval", end.year)
    gribapi.grib_set_long(grib, "monthOfEndOfOverallTimeInterval", end.month)
    gribapi.grib_set_long(grib, "dayOfEndOfOverallTimeInterval", end.day)
    gribapi.grib_set_long(grib, "hourOfEndOfOverallTimeInterval", end.hour)
    gribapi.grib_set_long(grib, "minuteOfEndOfOverallTimeInterval", end.minute)
    gribapi.grib_set_long(grib, "secondOfEndOfOverallTimeInterval", end.second)

    gribapi.grib_set_long(grib, "numberOfTimeRange", 1)
    gribapi.grib_set_long(grib, "numberOfMissingInStatisticalProcess", 0)

    type_of_statistical_processing(cube, grib, pt_coord)

    # Type of time increment, e.g incrementing fp, incrementing ref
    # time, etc. (code table 4.11)
    gribapi.grib_set_long(grib, "typeOfTimeIncrement", 255)
    # time unit for period over which statistical processing is done (hours)
    gribapi.grib_set_long(grib, "indicatorOfUnitForTimeRange", 1)
    # period over which statistical processing is done
    gribapi.grib_set_long(grib, "lengthOfTimeRange",
                          float(pt_coord.bounds[0, 1] - pt_coord.bounds[0, 0]))
    # time unit between successive source fields (not setting this at present)
    gribapi.grib_set_long(grib, "indicatorOfUnitForTimeIncrement", 255)
    # between successive source fields (just set to 0 for now)
    gribapi.grib_set_long(grib, "timeIncrement", 0)
Exemple #25
0
def time_processing_period(cube, grib):
    """
    For template 4.8 (time mean, time max, etc).

    The time range is taken from the 'time' coordinate bounds.
    If the cell-method coordinate is not 'time' itself, the type of statistic
    will not be derived and the save process will be aborted.

    """
    # We could probably split this function up a bit

    # Can safely assume bounded pt.
    pt_coord = cube.coord("time")
    end = cf_units.num2date(pt_coord.bounds[0, 1], pt_coord.units.name,
                            pt_coord.units.calendar)

    gribapi.grib_set_long(grib, "yearOfEndOfOverallTimeInterval", end.year)
    gribapi.grib_set_long(grib, "monthOfEndOfOverallTimeInterval", end.month)
    gribapi.grib_set_long(grib, "dayOfEndOfOverallTimeInterval", end.day)
    gribapi.grib_set_long(grib, "hourOfEndOfOverallTimeInterval", end.hour)
    gribapi.grib_set_long(grib, "minuteOfEndOfOverallTimeInterval", end.minute)
    gribapi.grib_set_long(grib, "secondOfEndOfOverallTimeInterval", end.second)

    gribapi.grib_set_long(grib, "numberOfTimeRange", 1)
    gribapi.grib_set_long(grib, "numberOfMissingInStatisticalProcess", 0)

    type_of_statistical_processing(cube, grib, pt_coord)

    # Type of time increment, e.g incrementing fp, incrementing ref
    # time, etc. (code table 4.11)
    gribapi.grib_set_long(grib, "typeOfTimeIncrement", 255)
    # time unit for period over which statistical processing is done (hours)
    gribapi.grib_set_long(grib, "indicatorOfUnitForTimeRange", 1)
    # period over which statistical processing is done
    gribapi.grib_set_long(grib, "lengthOfTimeRange",
                          float(pt_coord.bounds[0, 1] - pt_coord.bounds[0, 0]))
    # time unit between successive source fields (not setting this at present)
    gribapi.grib_set_long(grib, "indicatorOfUnitForTimeIncrement", 255)
    # between successive source fields (just set to 0 for now)
    gribapi.grib_set_long(grib, "timeIncrement", 0)
Exemple #26
0
# -*- coding: utf-8 -*-
"""
Created on Tue Apr 17 11:34:03 2018

@author: jonasg
"""
from matplotlib.pyplot import close
import numpy as np
import iris
from pyaerocom.io.testfiles import get
from pyaerocom import GriddedData
from pyaerocom.plot.mapping import plot_map

if __name__ == "__main__":
    close("all")

    files = get()
    data = GriddedData(files['models']['aatsr_su_v4.3'], var_name="od550aer")

    cube = data.grid

    iris.coord_categorisation.add_month(cube, "time", name='months')
    iris.coord_categorisation.add_year(cube, "time", name='years')
    cube_monthly = cube.aggregated_by(['months', 'years'], iris.analysis.MEAN)

    cube_mean = cube_monthly.collapsed
    ts = data.time
    import cf_units

    times = cf_units.num2date(ts.points, ts.units.name, ts.units.calendar)
    def estimate_coefficients_for_ngr(self, current_forecast,
                                      historic_forecast, truth):
        """
        Using Nonhomogeneous Gaussian Regression/Ensemble Model Output
        Statistics, estimate the required coefficients from historical
        forecasts.

        The main contents of this method is:

        1. Metadata checks to ensure that the current forecast, historic
           forecast and truth exist in a form that can be processed.
        2. Loop through times within the concatenated current forecast cube:

           1. Extract the desired forecast period from the historic forecasts
              to match the current forecasts. Apply unit conversion to ensure
              that historic forecasts have the desired units for calibration.
           2. Extract the relevant truth to co-incide with the time within
              the historic forecasts. Apply unit conversion to ensure
              that the truth has the desired units for calibration.
           3. Calculate mean and variance.
           4. Calculate initial guess at coefficient values by performing a
              linear regression, if requested, otherwise default values are
              used.
           5. Perform minimisation.

        Args:
            current_forecast (Iris Cube or CubeList):
                The cube containing the current forecast.
            historical_forecast (Iris Cube or CubeList):
                The cube or cubelist containing the historical forecasts used
                for calibration.
            truth (Iris Cube or CubeList):
                The cube or cubelist containing the truth used for calibration.

        Returns:
            (tuple): tuple containing:
                **optimised_coeffs** (Dictionary):
                    Dictionary containing a list of the optimised coefficients
                    for each date.
                **coeff_names** (List):
                    The name of each coefficient.

        """
        def convert_to_cubelist(cubes, cube_type="forecast"):
            """
            Convert cube to cubelist, if necessary.

            Args:
                cubes (Iris Cube or Iris CubeList):
                    Cube to be converted to CubeList.
                cube_type (String):
                    String to describe the cube, which is being converted to a
                    CubeList.

            Raises
            ------
            TypeError: The input cube is not an Iris cube.

            """
            if not isinstance(cubes, iris.cube.CubeList):
                cubes = iris.cube.CubeList([cubes])
            for cube in cubes:
                if not isinstance(cube, iris.cube.Cube):
                    msg = ("The input data within the {} "
                           "is not an Iris Cube.".format(cube_type))
                    raise TypeError(msg)
            return cubes

        # Ensure predictor_of_mean_flag is valid.
        check_predictor_of_mean_flag(self.predictor_of_mean_flag)

        # Setting default values for optimised_coeffs and coeff_names.
        optimised_coeffs = {}
        coeff_names = ["gamma", "delta", "a", "beta"]

        # Set default values for whether there are NaN values within the
        # initial guess.
        nan_in_initial_guess = False

        for var in [current_forecast, historic_forecast, truth]:
            if (isinstance(var, iris.cube.Cube)
                    or isinstance(var, iris.cube.CubeList)):
                current_forecast_cubes = current_forecast
                historic_forecast_cubes = historic_forecast
                truth_cubes = truth
            else:
                msg = ("{} is not a Cube or CubeList."
                       "Returning default values for optimised_coeffs {} "
                       "and coeff_names {}.").format(var, optimised_coeffs,
                                                     coeff_names)
                warnings.warn(msg)
                return optimised_coeffs, coeff_names

        current_forecast_cubes = (convert_to_cubelist(
            current_forecast_cubes, cube_type="current forecast"))
        historic_forecast_cubes = (convert_to_cubelist(
            historic_forecast_cubes, cube_type="historic forecast"))
        truth_cubes = convert_to_cubelist(truth_cubes, cube_type="truth")

        if (len(current_forecast_cubes) == 0
                or len(historic_forecast_cubes) == 0 or len(truth_cubes) == 0):
            msg = ("Insufficient input data present to estimate "
                   "coefficients using NGR. "
                   "\nNumber of current_forecast_cubes: {}"
                   "\nNumber of historic_forecast_cubes: {}"
                   "\nNumber of truth_cubes: {}".format(
                       len(current_forecast_cubes),
                       len(historic_forecast_cubes), len(truth_cubes)))
            warnings.warn(msg)
            return optimised_coeffs, coeff_names

        current_forecast_cubes = concatenate_cubes(current_forecast_cubes)
        historic_forecast_cubes = concatenate_cubes(historic_forecast_cubes)
        truth_cubes = concatenate_cubes(truth_cubes)

        for current_forecast_cube in current_forecast_cubes.slices_over(
                "time"):
            date = unit.num2date(
                current_forecast_cube.coord("time").points,
                current_forecast_cube.coord("time").units.name,
                current_forecast_cube.coord("time").units.calendar)[0]
            # Extract desired forecast_period from historic_forecast_cubes.
            forecast_period_constr = iris.Constraint(
                forecast_period=current_forecast_cube.coord(
                    "forecast_period").points)
            historic_forecast_cube = historic_forecast_cubes.extract(
                forecast_period_constr)

            # Extract truth matching the time of the historic forecast.
            reference_time = iris_time_to_datetime(
                historic_forecast_cube.coord("time").copy())
            truth_constr = iris.Constraint(
                forecast_reference_time=reference_time)
            truth_cube = truth_cubes.extract(truth_constr)

            if truth_cube is None:
                msg = ("Unable to calibrate for the time points {} "
                       "as no truth data is available."
                       "Moving on to try to calibrate "
                       "next time point.".format(
                           historic_forecast_cube.coord("time").points))
                warnings.warn(msg)
                continue

            # Make sure inputs have the same units.
            historic_forecast_cube.convert_units(self.desired_units)
            truth_cube.convert_units(self.desired_units)

            if self.predictor_of_mean_flag.lower() in ["mean"]:
                no_of_realizations = None
                forecast_predictor = historic_forecast_cube.collapsed(
                    "realization", iris.analysis.MEAN)
            elif self.predictor_of_mean_flag.lower() in ["realizations"]:
                no_of_realizations = len(
                    historic_forecast_cube.coord("realization").points)
                forecast_predictor = historic_forecast_cube

            forecast_var = historic_forecast_cube.collapsed(
                "realization", iris.analysis.VARIANCE)

            # Computing initial guess for EMOS coefficients
            # If no initial guess from a previous iteration, or if there
            # are NaNs in the initial guess, calculate an initial guess.
            if "initial_guess" not in locals() or nan_in_initial_guess:
                initial_guess = self.compute_initial_guess(
                    truth_cube,
                    forecast_predictor,
                    self.predictor_of_mean_flag,
                    self.ESTIMATE_COEFFICIENTS_FROM_LINEAR_MODEL_FLAG,
                    no_of_realizations=no_of_realizations)

            if np.any(np.isnan(initial_guess)):
                nan_in_initial_guess = True

            if not nan_in_initial_guess:
                # Need to access the x attribute returned by the
                # minimisation function.
                optimised_coeffs[date] = (
                    self.minimiser.crps_minimiser_wrapper(
                        initial_guess, forecast_predictor, truth_cube,
                        forecast_var, self.predictor_of_mean_flag,
                        self.distribution.lower()))
                initial_guess = optimised_coeffs[date]
            else:
                optimised_coeffs[date] = initial_guess

        return optimised_coeffs, coeff_names
Exemple #28
0
def plotmaps(data, verbose = False, filter ="WORLD", var="od550aer",
             plotdir="./"):
    """plot aerocom standard maps

    Will plot every supplied time step"""

    if not isinstance(data, GriddedData):
        raise TypeError("Need pyaerocom.GriddedData as input")
    #define color bar;
    #will be moved somewhere else and variable specific at some point
    colorbar_levels = [0., 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8,
                       0.9, 1.0]

    colorbar_ticks = [0., 0.02, 0.04, 0.06, 0.08, 0.1, 0.3, 0.5, 0.7, 0.9]
    aod_data = {
        'red': ((0., 0, 0), (0.07, 0, 0), (0.1, 0.678, 0.678,), (0.26, 1, 1), (0.85, 1, 1), (1, 0.545, 0.545)),
        'green': ((0., 0, 0), (0.07, 1, 1), (0.24, 1, 1), (0.91, 0, 0), (1, 0, 0)),
        'blue': ((0., 1, 1), (0.07, 1, 1), (0.1, 0.133, 0.133), (0.25, 0, 0), (1, 0, 0))}

    colormap = LinearSegmentedColormap('aod_jet', aod_data)
    plt.register_cmap(cmap = colormap)
    TIME_VAR_NAME = 'time'

    PlotDailyFlag = False
    PlotMonthlyFlag = True

    for model in data:
        #assume that we have single cube for the moment
        #iterate over the time dimension
        cube = data[model].data
        #model = 'AATSR_ORAC_v4.01'

        cube.coord('latitude').guess_bounds()
        cube.coord('longitude').guess_bounds()

        #plot daily data
        if PlotDailyFlag:
            for time_sub_cube in cube.slices_over(TIME_VAR_NAME):
                pre_grid_areas = iris.analysis.cartography.area_weights(time_sub_cube)
                #pdb.set_trace()
                #print(time_sub_cube)

                # Perform the area-weighted mean for each of the datasets using the
                # computed grid-box areas.
                weighted_mean = time_sub_cube.collapsed(['latitude', 'longitude'],
                                                        iris.analysis.MEAN,
                                                        weights = pre_grid_areas)

                time = (unit.num2date(time_sub_cube.coord(TIME_VAR_NAME).points,
                                      time_sub_cube.coord(TIME_VAR_NAME).units.name,
                                      time_sub_cube.coord(TIME_VAR_NAME).units.calendar))

                date = str(time[0]).split(' ')[0].replace('-','')
                Year = date[0:4]
                TsStr = 'm'+date
                PlotType = 'MAP'

                title = " ".join([var, date, 'mean: {:6.3f}'.format(float(weighted_mean.data))])
                #ABS550_AER_an2012_mALLYEAR_WORLD_ZONALOBS_AERONETSky.ps.png
                #OD550_AER_an2017_d20171125_WORLD_MAP.ps.png
                plotfilename = os.path.join(plotdir, '_'.join([var, "an" + Year, TsStr, filter, PlotType]) + ".png")
                if verbose:
                    print(plotfilename)
                plot = iplt.pcolormesh(time_sub_cube[:, :], cmap = colormap, vmin=0., vmax=max(colorbar_levels))
                # pdb.set_trace()
                plot.axes.set_aspect(1.8)
                LatsToPlot = time_sub_cube.coord(axis='X').points
                LonsToPlot = time_sub_cube.coord(axis='Y').points
                axis = plt.axis([LatsToPlot.min(), LatsToPlot.max(), LonsToPlot.min(), LonsToPlot.max()])
                ax = plot.axes
                ax.annotate('source: AEROCOM', xy=(0.88, 0.04), xycoords='figure fraction',
                            horizontalalignment='right', fontsize=10, bbox=dict(boxstyle='square', facecolor='none',
                                                                                edgecolor='black'))
                ax.annotate(model, xy=(-174., -83.), xycoords='data', horizontalalignment='left', fontsize=13,
                            color='black', bbox=dict(boxstyle='square', facecolor='white', edgecolor='none', alpha=0.7))

                # plt.ylabel(_title(plot_defn.coords[0], with_units=True))
                # plot_defn = iplt._get_plot_defn(cube, mode, ndims)

                plt.colorbar(spacing='uniform', ticks=colorbar_ticks, 
                             boundaries=colorbar_levels, extend='max')

                ax.coastlines()
                ax.set_xticks([-180., -120., -60., 0., 60, 120, 180], crs=ccrs.PlateCarree())
                ax.set_yticks([-90., -60, -30, 0., 30, 60, 90], crs=ccrs.PlateCarree())
                lon_formatter = LongitudeFormatter(number_format='.1f', degree_symbol='')
                lat_formatter = LatitudeFormatter(number_format='.1f', degree_symbol='')
                ax.xaxis.set_major_formatter(lon_formatter)
                ax.yaxis.set_major_formatter(lat_formatter)

                plt.xlabel = 'longitude'
                plt.ylabel = 'latitude'

                plt.title(title)
                plt.savefig(plotfilename, dpi=300)
                plt.close()
                #pdb.set_trace()

        elif PlotMonthlyFlag:
            #calculate monthly data

            iris.coord_categorisation.add_month(cube, TIME_VAR_NAME, name='month_number')
            iris.coord_categorisation.add_year(cube, TIME_VAR_NAME, name='month_year')
            cube_monthly = cube.aggregated_by(['month_number', 'month_year'], iris.analysis.MEAN)

            for time_sub_cube in cube_monthly.slices_over(TIME_VAR_NAME):
                pre_grid_areas = iris.analysis.cartography.area_weights(time_sub_cube)
                # pdb.set_trace()
                # print(time_sub_cube)

                # Perform the area-weighted mean for each of the datasets using the
                # computed grid-box areas.
                weighted_mean = time_sub_cube.collapsed(['latitude', 'longitude'],
                                                        iris.analysis.MEAN,
                                                        weights=pre_grid_areas)

                time = (unit.num2date(time_sub_cube.coord(TIME_VAR_NAME).points,
                                      time_sub_cube.coord(TIME_VAR_NAME).units.name,
                                      time_sub_cube.coord(TIME_VAR_NAME).units.calendar))

                date = str(time[0]).split(' ')[0].replace('-', ' ')[0:7]
                Year = date[0:4]
                TsStr = 'm' + date[-2:]
                PlotType = 'MAP'

                title = " ".join([var, date, 'mean: {:6.3f}'.format(float(weighted_mean.data))])
                # ABS550_AER_an2012_mALLYEAR_WORLD_ZONALOBS_AERONETSky.ps.png
                # OD550_AER_an2017_d20171125_WORLD_MAP.ps.png
                plotfilename = os.path.join(plotdir, '_'.join([var, "an" + Year, TsStr, filter, PlotType]) + ".png")
                if verbose:
                    print(plotfilename)
                LatsToPlot = time_sub_cube.coord(axis='X').points
                LonsToPlot = time_sub_cube.coord(axis='Y').points
                xticks = [-180., -120., -60., 0., 60, 120, 180]
                yticks = [-90., -60, -30, 0., 30, 60, 90]
                plot_ts_map(time_sub_cube, title, plotfilename, LatsToPlot, LonsToPlot, colorbar_ticks,
                            colorbar_levels, colormap, model, xticks, yticks)
Exemple #29
0
def retrieval_years(request):
    if request.method == 'POST':
        data_req_ids = []
        # loop through the items in the POST from the form and identify any
        # DataRequest object ids that have been requested
        for key in request.POST:
            components = re.match(r'^request_data_req_(\d+)$', key)
            if components:
                data_req_ids.append(int(components.group(1)))
        # get a string representation of each id
        data_req_strs = [
            str(DataRequest.objects.filter(id=req).first())
            for req in data_req_ids
        ]

        start_year_objects = [
            (DataRequest.objects.get(id=req).datafile_set.aggregate(
                Min('start_time'))['start_time__min'],
             DataRequest.objects.get(id=req).datafile_set.first().time_units,
             DataRequest.objects.get(id=req).datafile_set.first().calendar)
            for req in data_req_ids
        ]
        start_year_objects_no_nones = [
            (start_time, time_units, calendar)
            for start_time, time_units, calendar in start_year_objects
            if start_time is not None
        ]

        if start_year_objects_no_nones:
            earliest_year_float, time_units, calendar = min(
                start_year_objects_no_nones, key=lambda x: x[0])
            earliest_year = cf_units.num2date(earliest_year_float, time_units,
                                              calendar).strftime('%Y')
        else:
            earliest_year = None

        end_year_objects = [
            (DataRequest.objects.get(id=req).datafile_set.aggregate(
                Max('end_time'))['end_time__max'],
             DataRequest.objects.get(id=req).datafile_set.first().time_units,
             DataRequest.objects.get(id=req).datafile_set.first().calendar)
            for req in data_req_ids
        ]
        end_year_objects_no_nones = [
            (end_time, time_units, calendar)
            for end_time, time_units, calendar in end_year_objects
            if end_time is not None
        ]

        if end_year_objects_no_nones:
            latest_year_float, time_units, calendar = max(
                end_year_objects_no_nones, key=lambda x: x[0])
            end_year = cf_units.num2date(latest_year_float, time_units,
                                         calendar).strftime('%Y')
        else:
            end_year = None

        # generate the confirmation page
        return render(
            request, 'pdata_app/retrieval_request_choose_years.html', {
                'request': request,
                'data_reqs': data_req_strs,
                'page_title': 'Choose Retrieval Years',
                'return_url': request.POST['variables_received_url'],
                'data_request_ids': ','.join(map(str, data_req_ids)),
                'earliest_year': earliest_year,
                'end_year': end_year
            })
    else:
        return render(request, 'pdata_app/retrieval_request_error.html', {
            'request': request,
            'page_title': 'Retrieval Request'
        })
Exemple #30
0
    def _apply_params(self, forecast_predictors, forecast_vars,
                      optimised_coeffs, coeff_names, predictor_of_mean_flag):
        """
        Function to apply EMOS coefficients to all required dates.

        Args:
            forecast_predictors (Iris cube):
                Cube containing the forecast predictor e.g. ensemble mean
                or ensemble members.
            forecast_vars (Iris cube.):
                Cube containing the forecast variance e.g. ensemble variance.
            optimised_coeffs (List):
                Coefficients for all dates.
            coeff_names (List):
                Coefficient names.
            predictor_of_mean_flag (String):
                String to specify the input to calculate the calibrated mean.
                Currently the ensemble mean ("mean") and the ensemble members
                ("members") are supported as the predictors.

        Returns:
            (tuple) : tuple containing:
                **calibrated_forecast_predictor_all_dates** (CubeList):
                    List of cubes containing the calibrated forecast predictor.
                **calibrated_forecast_var_all_dates** (CubeList):
                    List of cubes containing the calibrated forecast variance.
                **calibrated_forecast_coefficients_all_dates** (CubeList):
                    List of cubes containing the coefficients used for
                    calibration.

        """
        calibrated_forecast_predictor_all_dates = iris.cube.CubeList()
        calibrated_forecast_var_all_dates = iris.cube.CubeList()
        calibrated_forecast_coefficients_all_dates = iris.cube.CubeList()

        for forecast_predictor, forecast_var in zip(
                forecast_predictors.slices_over("time"),
                forecast_vars.slices_over("time")):

            date = unit.num2date(
                forecast_predictor.coord("time").points,
                forecast_predictor.coord("time").units.name,
                forecast_predictor.coord("time").units.calendar)[0]

            with iris.FUTURE.context(cell_datetime_objects=True):
                constr = iris.Constraint(time=date)
                forecast_predictor_at_date = forecast_predictor.extract(constr)
                forecast_var_at_date = forecast_var.extract(constr)

            # If the coefficients are not available for the date, use the
            # raw ensemble forecast as the calibrated ensemble forecast.
            if date not in optimised_coeffs.keys():
                msg = ("Ensemble calibration not available "
                       "for forecasts with start time of {}. "
                       "Coefficients not available".format(
                           date.strftime("%Y%m%d%H%M")))
                warnings.warn(msg)
                calibrated_forecast_predictor_at_date = (
                    forecast_predictor_at_date.copy())
                calibrated_forecast_var_at_date = forecast_var_at_date.copy()
                optimised_coeffs[date] = np.full(len(coeff_names), np.nan)
                coeff_cubes = self._create_coefficient_cube(
                    forecast_predictor_at_date, optimised_coeffs, coeff_names)
            else:
                optimised_coeffs_at_date = (optimised_coeffs[date])

                # Assigning coefficients to coefficient names.
                if len(optimised_coeffs_at_date) == len(coeff_names):
                    optimised_coeffs_at_date = dict(
                        zip(coeff_names, optimised_coeffs_at_date))
                elif len(optimised_coeffs_at_date) > len(coeff_names):
                    excess_beta = (
                        optimised_coeffs_at_date[len(coeff_names):].tolist())
                    optimised_coeffs_at_date = (dict(
                        zip(coeff_names, optimised_coeffs_at_date)))
                    optimised_coeffs_at_date["beta"] = np.array(
                        [optimised_coeffs_at_date["beta"]] + excess_beta)
                else:
                    msg = ("Number of coefficient names {} with names {} "
                           "is not equal to the number of "
                           "optimised_coeffs_at_date values {} "
                           "with values {} or the number of "
                           "coefficients is not greater than the "
                           "number of coefficient names. Can not continue "
                           "if the number of coefficient names out number "
                           "the number of coefficients".format(
                               len(coeff_names), coeff_names,
                               len(optimised_coeffs_at_date),
                               optimised_coeffs_at_date))
                    raise ValueError(msg)

                if predictor_of_mean_flag.lower() in ["mean"]:
                    # Calculate predicted mean = a + b*X, where X is the
                    # raw ensemble mean. In this case, b = beta.
                    beta = [
                        optimised_coeffs_at_date["a"],
                        optimised_coeffs_at_date["beta"]
                    ]
                    forecast_predictor_flat = (
                        forecast_predictor_at_date.data.flatten())
                    new_col = np.ones(forecast_predictor_flat.shape)
                    all_data = np.column_stack(
                        (new_col, forecast_predictor_flat))
                    predicted_mean = np.dot(all_data, beta)
                    calibrated_forecast_predictor_at_date = (
                        forecast_predictor_at_date)
                elif predictor_of_mean_flag.lower() in ["members"]:
                    # Calculate predicted mean = a + b*X, where X is the
                    # raw ensemble mean. In this case, b = beta^2.
                    beta = np.concatenate([[optimised_coeffs_at_date["a"]],
                                           optimised_coeffs_at_date["beta"]**2
                                           ])
                    forecast_predictor = (enforce_coordinate_ordering(
                        forecast_predictor, "realization"))
                    forecast_predictor_flat = (
                        convert_cube_data_to_2d(forecast_predictor_at_date))
                    forecast_var_flat = forecast_var_at_date.data.flatten()

                    new_col = np.ones(forecast_var_flat.shape)
                    all_data = (np.column_stack(
                        (new_col, forecast_predictor_flat)))
                    predicted_mean = np.dot(all_data, beta)
                    # Calculate mean of ensemble members, as only the
                    # calibrated ensemble mean will be returned.
                    calibrated_forecast_predictor_at_date = (
                        forecast_predictor_at_date.collapsed(
                            "realization", iris.analysis.MEAN))

                xlen = len(forecast_predictor_at_date.coord(axis="x").points)
                ylen = len(forecast_predictor_at_date.coord(axis="y").points)
                predicted_mean = np.reshape(predicted_mean, (ylen, xlen))
                calibrated_forecast_predictor_at_date.data = predicted_mean

                # Calculating the predicted variance, based on the
                # raw variance S^2, where predicted variance = c + dS^2,
                # where c = (gamma)^2 and d = (delta)^2
                predicted_var = (optimised_coeffs_at_date["gamma"]**2 +
                                 optimised_coeffs_at_date["delta"]**2 *
                                 forecast_var_at_date.data)

                calibrated_forecast_var_at_date = forecast_var_at_date
                calibrated_forecast_var_at_date.data = predicted_var

                coeff_cubes = self._create_coefficient_cube(
                    calibrated_forecast_predictor_at_date,
                    optimised_coeffs[date], coeff_names)

            calibrated_forecast_predictor_all_dates.append(
                calibrated_forecast_predictor_at_date)
            calibrated_forecast_var_all_dates.append(
                calibrated_forecast_var_at_date)
            calibrated_forecast_coefficients_all_dates.extend(coeff_cubes)

        return (calibrated_forecast_predictor_all_dates,
                calibrated_forecast_var_all_dates,
                calibrated_forecast_coefficients_all_dates)
Exemple #31
0
def main(inargs):
    """Run the program."""

    metadata_dict = {}

    # Read data
    control_cube, control_history = read_data(inargs.control_files, inargs.variable,
                                              inargs.grid_point, convert_to_annual=inargs.annual)
    metadata_dict[inargs.control_files[0]] = control_history
    coord_names = [coord.name() for coord in control_cube.dim_coords]
    time_var = coord_names[0]
    assert time_var in ['time', 'year']

    experiment_cube, experiment_history = read_data(inargs.experiment_files, inargs.variable,
                                                    inargs.grid_point, convert_to_annual=inargs.annual)
    metadata_dict[inargs.experiment_files[0]] = experiment_history

    if inargs.dedrifted_files:
        dedrifted_cube, dedrifted_history = read_data(inargs.dedrifted_files, inargs.variable,
                                                      inargs.grid_point, convert_to_annual=inargs.annual)
        metadata_dict[inargs.dedrifted_files[0]] = dedrifted_history   

    if inargs.coefficient_file:
        cubic_data, a_cube = cubic_fit(inargs.coefficient_file, inargs.grid_point,
                                       control_cube.coord(time_var).points)
        #TODO: coeff metadata    

    # Time axis adjustment
    if time_var == 'time':
        first_data_cube = iris.load_cube(inargs.experiment_files[0], gio.check_iris_var(inargs.variable))
        if inargs.grid_point:
            first_data_cube = select_point(first_data_cube, inargs.grid_point, timeseries=True)
        if inargs.annual:
            first_data_cube = timeseries.convert_to_annual(first_data_cube)
        time_diff, branch_time, new_time_unit = remove_drift.time_adjustment(first_data_cube, control_cube, 'annual',
                                                                             branch_time=inargs.branch_time)
        print(f'branch time: {branch_time - 182.5}')
        time_coord = experiment_cube.coord('time')
        time_coord.convert_units(new_time_unit)
        experiment_time_values = time_coord.points.astype(numpy.float32) - time_diff
    elif time_var == 'year':
        if not inargs.branch_year == None:
            branch_year = inargs.branch_year
        else:
            if not inargs.control_time_units:
                control_time_units = gio.fix_time_descriptor(experiment_cube.attributes['parent_time_units'])
            else:
                control_time_units = inargs.control_time_units.replace("_", " ")
            branch_time = experiment_cube.attributes['branch_time_in_parent']
            branch_datetime = cf_units.num2date(branch_time, control_time_units, cf_units.CALENDAR_STANDARD)
            branch_year = branch_datetime.year
        print(f'branch year: {branch_year}')
        experiment_time_values = numpy.arange(branch_year, branch_year + experiment_cube.shape[0])

    # Plot
    fig = plt.figure(figsize=[14, 7])
    plt.plot(control_cube.coord(time_var).points, control_cube.data, label='control')
    plt.plot(experiment_time_values, experiment_cube.data, label='experiment')
    if inargs.dedrifted_files:
        plt.plot(experiment_time_values, dedrifted_cube.data, label='dedrifted')
    if inargs.coefficient_file:
        plt.plot(control_cube.coord(time_var).points, cubic_data, label='cubic fit')
    if inargs.outlier_threshold:
        data, outlier_idx = timeseries.outlier_removal(control_cube.data, inargs.outlier_threshold)
        plt.plot(control_cube.coord(time_var).points[outlier_idx], control_cube.data[outlier_idx],
                 marker='o', linestyle='none', color='r', alpha=0.3)
    if inargs.ylim:
        ymin, ymax = inargs.ylim
        plt.ylim(ymin, ymax)
    plt.ylabel(f"{gio.check_iris_var(inargs.variable)} ({control_cube.units})")
    if time_var == 'time':
        plt.xlabel(str(new_time_unit))
    else:
        plt.xlabel('control run year')
    plt.legend()
    if inargs.grid_point:
        title = get_title(inargs.control_files[0], inargs.variable, inargs.grid_point)
        plt.title(title)

    # Save output
    plt.savefig(inargs.outfile, bbox_inches='tight')
    
    log_text = cmdprov.new_log(infile_history=metadata_dict, git_repo=repo_dir)
    log_file = re.sub('.png', '.met', inargs.outfile)
    cmdprov.write_log(log_file, log_text)
Exemple #32
0
def __get_time_info__(f, io):
    """
    Evaluates the time slices in the input files to verify
    that there are the correct number of slices in the file
    and that the slices are contiguous.  It also pulls off
    other information, such as start/en times, time period/spacing,
    slice count, and the average of the slices (for ordering purposes).

    Parameters:
        f (climIO file): A pointer to an open netCDF file.
        io (climIO): An object that contains a setof io commands to use.

    Returns:
        date_info (dict): Contains file information, such as time period(t_per), time step(t_step),
            first/last step(t0 and tn), slice counts(cnt).
        average (int): The average of all time slices.
    """
    date_info = {}
    _tc, _dim, att = io.get_var_info(f, "time")
    stand_cal = cf_units.Unit("days since 1-1-1 0:0:0",
                              calendar=att["calendar"])
    cal_unit = cf_units.Unit(att["units"], calendar=att["calendar"])
    if "bounds" in att.keys():
        # print 'Using bounds'
        tb = f.variables[att["bounds"]]
        j = len(tb)
        d0 = tb[0, 0]
        d1 = tb[1, 0]
        d2 = tb[2, 0]
        dn = tb[j - 1, 1] - 1
        time = tb[:, 0]
    else:
        # print 'Using time'
        tb = f.variables["time"]
        j = len(tb)
        d0 = tb[0]
        d1 = tb[1]
        d2 = tb[2]
        dn = tb[j - 1]
        time = tb[:]
    date_info["time"] = time

    # Get second and third time bounds to figure out the time period
    t1 = parser.parse(
        str(cf_units.num2date(d1, att["units"],
                              calendar=att["calendar"]))).timetuple()
    t2 = parser.parse(
        str(cf_units.num2date(d2, att["units"],
                              calendar=att["calendar"]))).timetuple()
    # Get time difference between the steps
    t_step = d2 - d1
    h = t2[3] - t1[3]
    if t1[3] != t2[3]:
        t_per = str(h) + "hour"
    elif t1[2] != t2[2]:
        t_per = "day"
    elif t1[1] != t2[1]:
        t_per = "mon"
    elif t1[0] != t2[0]:
        t_per = "year"
    else:
        t_per = "UNKNOWN"
    date_info["t_per"] = t_per
    date_info["t_step"] = t_step
    # Get first and last dates
    # t0 = (parser.parse(str(cf_units.num2date(d0, att['units'],
    #                                          calendar=att['calendar']))).timetuple())
    # tn = (parser.parse(str(cf_units.num2date(dn, att['units'],
    #                                          calendar=att['calendar']))).timetuple())
    date_info["t0"] = cal_unit.convert(d0, stand_cal)
    date_info["tn"] = cal_unit.convert(dn, stand_cal)
    date_info["cnt"] = j
    average = (date_info["t0"] + date_info["tn"]) / 2
    date_info["units"] = att["units"]
    date_info["calendar"] = att["calendar"]

    # Check to see if the number of slices matches how many should be in the
    # date range
    # if t_per == 'year':
    #     _ok = (tn[0] - t0[0] == j)
    # elif t_per == 'mon':
    #     _ok = (((tn[0] - t0[0]) * 12) + (tn[1] - t0[1] + 1) == j)
    # elif t_per == 'day':
    #     _ok = ((dn - d0 + 1) == j)
    # elif 'hour' in t_per:
    #     cnt_per_day = 24.0 / h
    #     _ok = (((dn - d0) * cnt_per_day + 1) == j)

    return average, date_info
Exemple #33
0
 def _num2date(num):
     return cf_units.num2date(num, origin, calendar)