Exemplo n.º 1
0
def _calendar_rules(cube, pp):
    """
    Rules for setting the calendar of the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    time_coord = scalar_coord(cube, 'time')
    if time_coord is not None:
        if time_coord.units.calendar == '360_day':
            pp.lbtim.ic = 2
        elif time_coord.units.calendar == 'gregorian':
            pp.lbtim.ic = 1
        elif time_coord.units.calendar == '365_day':
            pp.lbtim.ic = 4
    return pp
Exemplo n.º 2
0
def _calendar_rules(cube, pp):
    """
    Rules for setting the calendar of the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    time_coord = scalar_coord(cube, 'time')
    if time_coord is not None:
        if time_coord.units.calendar == '360_day':
            pp.lbtim.ic = 2
        elif time_coord.units.calendar == 'gregorian':
            pp.lbtim.ic = 1
        elif time_coord.units.calendar == '365_day':
            pp.lbtim.ic = 4
    return pp
Exemplo n.º 3
0
def _general_time_rules(cube, pp):
    """
    Rules for setting time metadata of the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    time_coord = scalar_coord(cube, 'time')
    fp_coord = scalar_coord(cube, 'forecast_period')
    frt_coord = scalar_coord(cube, 'forecast_reference_time')
    clim_season_coord = scalar_coord(cube, 'clim_season')

    cm_time_mean = scalar_cell_method(cube, 'mean', 'time')
    cm_time_min = scalar_cell_method(cube, 'minimum', 'time')
    cm_time_max = scalar_cell_method(cube, 'maximum', 'time')

    # No forecast.
    if time_coord is not None and fp_coord is None and frt_coord is None:
        pp.lbtim.ia = 0
        pp.lbtim.ib = 0
        pp.t1 = time_coord.units.num2date(time_coord.points[0])
        pp.t2 = netcdftime.datetime(0, 0, 0)

    # Forecast.
    if (time_coord is not None and not time_coord.has_bounds()
            and fp_coord is not None):
        pp.lbtim.ia = 0
        pp.lbtim.ib = 1
        pp.t1 = time_coord.units.num2date(time_coord.points[0])
        pp.t2 = time_coord.units.num2date(time_coord.points[0] -
                                          fp_coord.points[0])
        pp.lbft = fp_coord.points[0]

    # Time mean (non-climatological).
    # XXX This only works when we have a single timestep.
    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None and fp_coord is not None
            and fp_coord.has_bounds()):
        # XXX How do we know *which* time to use if there are more than
        # one? *Can* there be more than one?
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None and fp_coord is None
            and frt_coord is not None):
        # Handle missing forecast period using time and forecast ref time.
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        stop = time_coord.units.convert(time_coord.bounds[0, 1],
                                        'hours since epoch')
        start = frt_coord.units.convert(frt_coord.points[0],
                                        'hours since epoch')
        pp.lbft = stop - start

    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None
            and (fp_coord is not None or frt_coord is not None)
            and cm_time_mean is not None and cm_time_mean.intervals != ()
            and cm_time_mean.intervals[0].endswith('hour')):
        pp.lbtim.ia = int(cm_time_mean.intervals[0][:-5])

    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None
            and (fp_coord is not None or frt_coord is not None)
            and (cm_time_mean is None or cm_time_mean.intervals == ()
                 or not cm_time_mean.intervals[0].endswith('hour'))):
        pp.lbtim.ia = 0

    # If the cell methods contain a minimum then overwrite lbtim.ia with this
    # interval.
    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None
            and (fp_coord is not None or frt_coord is not None)
            and cm_time_min is not None and cm_time_min.intervals != ()
            and cm_time_min.intervals[0].endswith('hour')):
        # Set lbtim.ia with the integer part of the cell method's interval
        # e.g. if interval is '24 hour' then lbtim.ia becomes 24.
        pp.lbtim.ia = int(cm_time_min.intervals[0][:-5])

    # If the cell methods contain a maximum then overwrite lbtim.ia with this
    # interval.
    if (time_coord is not None and time_coord.has_bounds()
            and clim_season_coord is None
            and (fp_coord is not None or frt_coord is not None)
            and cm_time_max is not None and cm_time_max.intervals != ()
            and cm_time_max.intervals[0].endswith('hour')):
        # Set lbtim.ia with the integer part of the cell method's interval
        # e.g. if interval is '1 hour' then lbtim.ia becomes 1.
        pp.lbtim.ia = int(cm_time_max.intervals[0][:-5])

    if time_coord is not None and time_coord.has_bounds():
        lower_bound_yr =\
            time_coord.units.num2date(time_coord.bounds[0, 0]).year
        upper_bound_yr =\
            time_coord.units.num2date(time_coord.bounds[0, 1]).year
    else:
        lower_bound_yr = None
        upper_bound_yr = None

    # Climatological time means.
    if (time_coord is not None and time_coord.has_bounds()
            and lower_bound_yr == upper_bound_yr and fp_coord is not None
            and fp_coord.has_bounds() and clim_season_coord is not None
            and 'clim_season' in cube.cell_methods[-1].coord_names):
        # Climatological time mean - single year.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and time_coord.has_bounds()
          and lower_bound_yr != upper_bound_yr and fp_coord is not None
          and fp_coord.has_bounds() and clim_season_coord is not None
          and 'clim_season' in cube.cell_methods[-1].coord_names
          and clim_season_coord.points[0] == 'djf'):
        # Climatological time mean - spanning years - djf.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        if pp.t1.month == 12:
            pp.t1 = netcdftime.datetime(pp.t1.year)
        else:
            pp.t1 = netcdftime.datetime(pp.t1.year - 1, 12, 1, 0, 0, 0)
        pp.t2 = netcdftime.datetime(pp.t2.year, 3, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and time_coord.has_bounds()
          and lower_bound_yr != upper_bound_yr and fp_coord is not None
          and fp_coord.has_bounds() and clim_season_coord is not None
          and 'clim_season' in cube.cell_methods[-1].coord_names
          and clim_season_coord.points[0] == 'mam'):
        # Climatological time mean - spanning years - mam.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = netcdftime.datetime(pp.t1.year, 3, 1, 0, 0, 0)
        pp.t2 = netcdftime.datetime(pp.t2.year, 6, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and time_coord.has_bounds()
          and lower_bound_yr != upper_bound_yr and fp_coord is not None
          and fp_coord.has_bounds() and clim_season_coord is not None
          and 'clim_season' in cube.cell_methods[-1].coord_names
          and clim_season_coord.points[0] == 'jja'):
        # Climatological time mean - spanning years - jja.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = netcdftime.datetime(pp.t1.year, 6, 1, 0, 0, 0)
        pp.t2 = netcdftime.datetime(pp.t2.year, 9, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and time_coord.has_bounds()
          and lower_bound_yr != upper_bound_yr and fp_coord is not None
          and fp_coord.has_bounds() and clim_season_coord is not None
          and 'clim_season' in cube.cell_methods[-1].coord_names
          and clim_season_coord.points[0] == 'son'):
        # Climatological time mean - spanning years - son.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = netcdftime.datetime(pp.t1.year, 9, 1, 0, 0, 0)
        pp.t2 = netcdftime.datetime(pp.t2.year, 12, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    return pp
Exemplo n.º 4
0
def _vertical_rules(cube, pp):
    """
    Rules for setting vertical levels for the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    # Define commonly-used coords.
    air_pres_coord = scalar_coord(cube, 'air_pressure')
    apt_coord = scalar_coord(cube, 'air_potential_temperature')
    depth_coord = scalar_coord(cube, 'depth')
    height_coord = scalar_coord(cube, 'height')
    level_height_coord = scalar_coord(cube, 'level_height')
    mln_coord = scalar_coord(cube, 'model_level_number')
    pressure_coord = scalar_coord(cube, 'pressure')
    pseudo_level_coord = scalar_coord(cube, 'pseudo_level')
    sigma_coord = scalar_coord(cube, 'sigma')
    soil_mln_coord = scalar_coord(cube, 'soil_model_level_number')

    # Define commonly-used aux factories.
    try:
        height_factory = aux_factory(cube, HybridHeightFactory)
    except ValueError:
        height_factory = None
    try:
        pressure_factory = aux_factory(cube, HybridPressureFactory)
    except ValueError:
        pressure_factory = None

    # Set `lbuser[5]`.
    if (pseudo_level_coord is not None and not pseudo_level_coord.bounds):
        pp.lbuser[4] = pseudo_level_coord.points[0]

    # Single height level.
    if (height_coord is not None and not height_coord.bounds
            and height_coord.points[0] == 1.5
            and cube.name() == 'air_temperature'):
        pp.lbvc = 129
        pp.blev = -1

    if pp.lbvc == 0 and height_coord is not None and not height_coord.bounds:
        pp.lbvc = 1
        pp.blev = cube.coord('height').points[0]

    # Single air_pressure level.
    if air_pres_coord is not None and not air_pres_coord.bounds:
        pp.lbvc = 8
        pp.blev = air_pres_coord.points[0]

    # Single pressure level.
    if pressure_coord is not None and not pressure_coord.bounds:
        pp.lbvc = 8
        pp.blev = pressure_coord.points[0]

    # Single depth level (non cross-section).
    if (mln_coord is not None and not mln_coord.bounds
            and depth_coord is not None and not depth_coord.bounds):
        pp.lbvc = 2
        pp.lblev = mln_coord.points[0]
        pp.blev = depth_coord.points[0]

    # Single depth level (Non-dimensional soil model level).
    if (soil_mln_coord is not None and not soil_mln_coord.has_bounds()
            and air_pres_coord is None and depth_coord is None
            and height_coord is None and pressure_coord is None
            and cube.standard_name is not None
            and 'soil' in cube.standard_name):
        pp.lbvc = 6
        pp.lblev = soil_mln_coord.points[0]
        pp.blev = pp.lblev
        pp.brsvd[0] = 0
        pp.brlev = 0

    # Single depth level (soil depth).
    if (depth_coord is not None and depth_coord.has_bounds()
            and air_pres_coord is None and soil_mln_coord is None
            and mln_coord is None and height_coord is None
            and pressure_coord is None and cube.standard_name is not None
            and 'soil' in cube.standard_name):
        pp.lbvc = 6
        pp.blev = depth_coord.points[0]
        pp.brsvd[0] = depth_coord.bounds[0, 0]
        pp.brlev = depth_coord.bounds[0, 1]

    # Single potential-temperature level.
    if (apt_coord is not None and not apt_coord.bounds
            and air_pres_coord is None and depth_coord is None
            and height_coord is None and pressure_coord is None
            and mln_coord is None):
        pp.lbvc = 19
        pp.lblev = apt_coord.points[0]
        pp.blev = apt_coord.points[0]

    # Single hybrid_height level
    # (without aux factory e.g. due to missing orography).
    if (not has_aux_factory(cube, HybridHeightFactory)
            and mln_coord is not None and mln_coord.bounds is None
            and level_height_coord is not None
            and level_height_coord.bounds is not None
            and sigma_coord is not None and sigma_coord.bounds is not None):
        pp.lbvc = 65
        pp.lblev = mln_coord.points[0]
        pp.blev = level_height_coord.points[0]
        pp.brlev = level_height_coord.bounds[0, 0]
        pp.brsvd[0] = level_height_coord.bounds[0, 1]
        pp.bhlev = sigma_coord.points[0]
        pp.bhrlev = sigma_coord.bounds[0, 0]
        pp.brsvd[1] = sigma_coord.bounds[0, 1]

    # Single hybrid_height level (with aux factory).
    if (has_aux_factory(cube, HybridHeightFactory) and mln_coord is not None
            and mln_coord.bounds is None
            and height_factory.dependencies['delta'] is not None
            and height_factory.dependencies['delta'].bounds is not None
            and height_factory.dependencies['sigma'] is not None
            and height_factory.dependencies['sigma'].bounds is not None):
        pp.lbvc = 65
        pp.lblev = mln_coord.points[0]
        pp.blev = height_factory.dependencies['delta'].points[0]
        pp.brlev = height_factory.dependencies['delta'].bounds[0, 0]
        pp.brsvd[0] = height_factory.dependencies['delta'].bounds[0, 1]
        pp.bhlev = height_factory.dependencies['sigma'].points[0]
        pp.bhrlev = height_factory.dependencies['sigma'].bounds[0, 0]
        pp.brsvd[1] = height_factory.dependencies['sigma'].bounds[0, 1]

    # Single hybrid pressure level.
    if (has_aux_factory(cube, HybridPressureFactory) and mln_coord is not None
            and mln_coord.bounds is None
            and pressure_factory.dependencies['delta'] is not None
            and pressure_factory.dependencies['delta'].bounds is not None
            and pressure_factory.dependencies['sigma'] is not None
            and pressure_factory.dependencies['sigma'].bounds is not None):
        pp.lbvc = 9
        pp.lblev = mln_coord.points[0]
        pp.blev = pressure_factory.dependencies['sigma'].points[0]
        pp.brlev = pressure_factory.dependencies['sigma'].bounds[0, 0]
        pp.brsvd[0] = pressure_factory.dependencies['sigma'].bounds[0, 1]
        pp.bhlev = pressure_factory.dependencies['delta'].points[0]
        pp.bhrlev = pressure_factory.dependencies['delta'].bounds[0, 0]
        pp.brsvd[1] = pressure_factory.dependencies['delta'].bounds[0, 1]

    return pp
Exemplo n.º 5
0
def _vertical_rules(cube, pp):
    """
    Rules for setting vertical levels for the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    # Define commonly-used coords.
    air_pres_coord = scalar_coord(cube, 'air_pressure')
    apt_coord = scalar_coord(cube, 'air_potential_temperature')
    depth_coord = scalar_coord(cube, 'depth')
    height_coord = scalar_coord(cube, 'height')
    level_height_coord = scalar_coord(cube, 'level_height')
    mln_coord = scalar_coord(cube, 'model_level_number')
    pressure_coord = scalar_coord(cube, 'pressure')
    pseudo_level_coord = scalar_coord(cube, 'pseudo_level')
    sigma_coord = scalar_coord(cube, 'sigma')
    soil_mln_coord = scalar_coord(cube, 'soil_model_level_number')

    # Define commonly-used aux factories.
    try:
        height_factory = aux_factory(cube, HybridHeightFactory)
    except ValueError:
        height_factory = None
    try:
        pressure_factory = aux_factory(cube, HybridPressureFactory)
    except ValueError:
        pressure_factory = None

    # Set `lbuser[5]`.
    if (pseudo_level_coord is not None and
            not pseudo_level_coord.bounds):
        pp.lbuser[4] = pseudo_level_coord.points[0]

    # Single height level.
    if (height_coord is not None and
            not height_coord.bounds and
            height_coord.points[0] == 1.5 and
            cube.name() == 'air_temperature'):
        pp.lbvc = 129
        pp.blev = -1

    if pp.lbvc == 0 and height_coord is not None and not height_coord.bounds:
        pp.lbvc = 1
        pp.blev = cube.coord('height').points[0]

    # Single air_pressure level.
    if air_pres_coord is not None and not air_pres_coord.bounds:
        pp.lbvc = 8
        pp.blev = air_pres_coord.points[0]

    # Single pressure level.
    if pressure_coord is not None and not pressure_coord.bounds:
        pp.lbvc = 8
        pp.blev = pressure_coord.points[0]

    # Single depth level (non cross-section).
    if (mln_coord is not None and
            not mln_coord.bounds and
            depth_coord is not None and
            not depth_coord.bounds):
        pp.lbvc = 2
        pp.lblev = mln_coord.points[0]
        pp.blev = depth_coord.points[0]

    # Single depth level (Non-dimensional soil model level).
    if (soil_mln_coord is not None and
            not soil_mln_coord.has_bounds() and
            air_pres_coord is None and
            depth_coord is None and
            height_coord is None and
            pressure_coord is None and
            cube.standard_name is not None and
            'soil' in cube.standard_name):
        pp.lbvc = 6
        pp.lblev = soil_mln_coord.points[0]
        pp.blev = pp.lblev
        pp.brsvd[0] = 0
        pp.brlev = 0

    # Single depth level (soil depth).
    if (depth_coord is not None and
            depth_coord.has_bounds() and
            air_pres_coord is None and
            soil_mln_coord is None and
            mln_coord is None and
            height_coord is None and
            pressure_coord is None and
            cube.standard_name is not None and
            'soil' in cube.standard_name):
        pp.lbvc = 6
        pp.blev = depth_coord.points[0]
        pp.brsvd[0] = depth_coord.bounds[0, 0]
        pp.brlev = depth_coord.bounds[0, 1]

    # Single potential-temperature level.
    if (apt_coord is not None and
            not apt_coord.bounds and
            air_pres_coord is None and
            depth_coord is None and
            height_coord is None and
            pressure_coord is None and
            mln_coord is None):
        pp.lbvc = 19
        pp.lblev = apt_coord.points[0]
        pp.blev = apt_coord.points[0]

    # Single hybrid_height level
    # (without aux factory e.g. due to missing orography).
    if (not has_aux_factory(cube, HybridHeightFactory) and
            mln_coord is not None and
            mln_coord.bounds is None and
            level_height_coord is not None and
            level_height_coord.bounds is not None and
            sigma_coord is not None and
            sigma_coord.bounds is not None):
        pp.lbvc = 65
        pp.lblev = mln_coord.points[0]
        pp.blev = level_height_coord.points[0]
        pp.brlev = level_height_coord.bounds[0, 0]
        pp.brsvd[0] = level_height_coord.bounds[0, 1]
        pp.bhlev = sigma_coord.points[0]
        pp.bhrlev = sigma_coord.bounds[0, 0]
        pp.brsvd[1] = sigma_coord.bounds[0, 1]

    # Single hybrid_height level (with aux factory).
    if (has_aux_factory(cube, HybridHeightFactory) and
            mln_coord is not None and
            mln_coord.bounds is None and
            height_factory.dependencies['delta'] is not None and
            height_factory.dependencies['delta'].bounds is not None and
            height_factory.dependencies['sigma'] is not None and
            height_factory.dependencies['sigma'].bounds is not None):
        pp.lbvc = 65
        pp.lblev = mln_coord.points[0]
        pp.blev = height_factory.dependencies['delta'].points[0]
        pp.brlev = height_factory.dependencies['delta'].bounds[0, 0]
        pp.brsvd[0] = height_factory.dependencies['delta'].bounds[0, 1]
        pp.bhlev = height_factory.dependencies['sigma'].points[0]
        pp.bhrlev = height_factory.dependencies['sigma'].bounds[0, 0]
        pp.brsvd[1] = height_factory.dependencies['sigma'].bounds[0, 1]

    # Single hybrid pressure level.
    if (has_aux_factory(cube, HybridPressureFactory) and
            mln_coord is not None and
            mln_coord.bounds is None and
            pressure_factory.dependencies['delta'] is not None and
            pressure_factory.dependencies['delta'].bounds is not None and
            pressure_factory.dependencies['sigma'] is not None and
            pressure_factory.dependencies['sigma'].bounds is not None):
        pp.lbvc = 9
        pp.lblev = mln_coord.points[0]
        pp.blev = pressure_factory.dependencies['sigma'].points[0]
        pp.brlev = pressure_factory.dependencies['sigma'].bounds[0, 0]
        pp.brsvd[0] = pressure_factory.dependencies['sigma'].bounds[0, 1]
        pp.bhlev = pressure_factory.dependencies['delta'].points[0]
        pp.bhrlev = pressure_factory.dependencies['delta'].bounds[0, 0]
        pp.brsvd[1] = pressure_factory.dependencies['delta'].bounds[0, 1]

    return pp
Exemplo n.º 6
0
def _general_time_rules(cube, pp):
    """
    Rules for setting time metadata of the PP field.

    Args:
        cube: the cube being saved as a series of PP fields.
        pp: the current PP field having save rules applied.

    Returns:
        The PP field with updated metadata.

    """
    time_coord = scalar_coord(cube, 'time')
    fp_coord = scalar_coord(cube, 'forecast_period')
    frt_coord = scalar_coord(cube, 'forecast_reference_time')
    clim_season_coord = scalar_coord(cube, 'clim_season')

    cm_time_mean = scalar_cell_method(cube, 'mean', 'time')
    cm_time_min = scalar_cell_method(cube, 'minimum', 'time')
    cm_time_max = scalar_cell_method(cube, 'maximum', 'time')

    # No forecast.
    if time_coord is not None and fp_coord is None and frt_coord is None:
        pp.lbtim.ia = 0
        pp.lbtim.ib = 0
        pp.t1 = time_coord.units.num2date(time_coord.points[0])
        pp.t2 = cftime.datetime(0, 0, 0)

    # Forecast.
    if (time_coord is not None and
            not time_coord.has_bounds() and
            fp_coord is not None):
        pp.lbtim.ia = 0
        pp.lbtim.ib = 1
        pp.t1 = time_coord.units.num2date(time_coord.points[0])
        pp.t2 = time_coord.units.num2date(time_coord.points[0] -
                                          fp_coord.points[0])
        pp.lbft = fp_coord.points[0]

    # Time mean (non-climatological).
    # XXX This only works when we have a single timestep.
    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            fp_coord is not None and
            fp_coord.has_bounds()):
        # XXX How do we know *which* time to use if there are more than
        # one? *Can* there be more than one?
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            fp_coord is None and
            frt_coord is not None):
        # Handle missing forecast period using time and forecast ref time.
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        stop = time_coord.units.convert(time_coord.bounds[0, 1],
                                        'hours since epoch')
        start = frt_coord.units.convert(frt_coord.points[0],
                                        'hours since epoch')
        pp.lbft = stop - start

    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            cm_time_mean is not None):
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])

    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            cm_time_mean is not None and
            cm_time_mean.intervals != () and
            cm_time_mean.intervals[0].endswith('hour')):
        pp.lbtim.ia = int(cm_time_mean.intervals[0][:-5])

    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            (fp_coord is not None or frt_coord is not None) and
            (cm_time_mean is None or cm_time_mean.intervals == () or
             not cm_time_mean.intervals[0].endswith('hour'))):
        pp.lbtim.ia = 0

    # If the cell methods contain a minimum then overwrite lbtim.ia with this
    # interval.
    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            (fp_coord is not None or frt_coord is not None) and
            cm_time_min is not None and
            cm_time_min.intervals != () and
            cm_time_min.intervals[0].endswith('hour')):
        # Set lbtim.ia with the integer part of the cell method's interval
        # e.g. if interval is '24 hour' then lbtim.ia becomes 24.
        pp.lbtim.ia = int(cm_time_min.intervals[0][:-5])

    # If the cell methods contain a maximum then overwrite lbtim.ia with this
    # interval.
    if (time_coord is not None and
            time_coord.has_bounds() and
            clim_season_coord is None and
            (fp_coord is not None or frt_coord is not None) and
            cm_time_max is not None and
            cm_time_max.intervals != () and
            cm_time_max.intervals[0].endswith('hour')):
        # Set lbtim.ia with the integer part of the cell method's interval
        # e.g. if interval is '1 hour' then lbtim.ia becomes 1.
        pp.lbtim.ia = int(cm_time_max.intervals[0][:-5])

    if time_coord is not None and time_coord.has_bounds():
        lower_bound_yr =\
            time_coord.units.num2date(time_coord.bounds[0, 0]).year
        upper_bound_yr =\
            time_coord.units.num2date(time_coord.bounds[0, 1]).year
    else:
        lower_bound_yr = None
        upper_bound_yr = None

    # Climatological time means.
    if (time_coord is not None and
            time_coord.has_bounds() and
            lower_bound_yr == upper_bound_yr and
            fp_coord is not None and
            fp_coord.has_bounds() and
            clim_season_coord is not None and
            'clim_season' in cube.cell_methods[-1].coord_names):
        # Climatological time mean - single year.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 2
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and
            time_coord.has_bounds() and
            lower_bound_yr != upper_bound_yr and
            fp_coord is not None and
            fp_coord.has_bounds() and
            clim_season_coord is not None and
            'clim_season' in cube.cell_methods[-1].coord_names and
            clim_season_coord.points[0] == 'djf'):
        # Climatological time mean - spanning years - djf.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        if pp.t1.month == 12:
            pp.t1 = cftime.datetime(pp.t1.year)
        else:
            pp.t1 = cftime.datetime(pp.t1.year-1, 12, 1, 0, 0, 0)
        pp.t2 = cftime.datetime(pp.t2.year, 3, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and
            time_coord.has_bounds() and
            lower_bound_yr != upper_bound_yr and
            fp_coord is not None and
            fp_coord.has_bounds() and
            clim_season_coord is not None and
            'clim_season' in cube.cell_methods[-1].coord_names and
            clim_season_coord.points[0] == 'mam'):
        # Climatological time mean - spanning years - mam.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = cftime.datetime(pp.t1.year, 3, 1, 0, 0, 0)
        pp.t2 = cftime.datetime(pp.t2.year, 6, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and
            time_coord.has_bounds() and
            lower_bound_yr != upper_bound_yr and
            fp_coord is not None and
            fp_coord.has_bounds() and
            clim_season_coord is not None and
            'clim_season' in cube.cell_methods[-1].coord_names and
            clim_season_coord.points[0] == 'jja'):
        # Climatological time mean - spanning years - jja.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = cftime.datetime(pp.t1.year, 6, 1, 0, 0, 0)
        pp.t2 = cftime.datetime(pp.t2.year, 9, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    elif (time_coord is not None and
            time_coord.has_bounds() and
            lower_bound_yr != upper_bound_yr and
            fp_coord is not None and
            fp_coord.has_bounds() and
            clim_season_coord is not None and
            'clim_season' in cube.cell_methods[-1].coord_names and
            clim_season_coord.points[0] == 'son'):
        # Climatological time mean - spanning years - son.
        pp.lbtim.ia = 0
        pp.lbtim.ib = 3
        # TODO: wut?
        pp.t1 = time_coord.units.num2date(time_coord.bounds[0, 0])
        pp.t2 = time_coord.units.num2date(time_coord.bounds[0, 1])
        pp.t1 = cftime.datetime(pp.t1.year, 9, 1, 0, 0, 0)
        pp.t2 = cftime.datetime(pp.t2.year, 12, 1, 0, 0, 0)
        _conditional_warning(
            time_coord.bounds[0, 0] != time_coord.units.date2num(pp.t1),
            "modified t1 for climatological seasonal mean")
        _conditional_warning(
            time_coord.bounds[0, 1] != time_coord.units.date2num(pp.t2),
            "modified t2 for climatological seasonal mean")
        pp.lbft = fp_coord.units.convert(fp_coord.bounds[0, 1], 'hours')

    return pp