Exemple #1
0
    def test_equal(self):
        # equal if they're equivalent and the unit conversion is the identity
        us = self.get_test_reftimes() \
            + self.get_test_reftimes(calendar='julian') \
            + self.get_test_reftimes(unit='minutes', time= '12:34:56',
            calendar='proleptic_gregorian') \
            + [units.Units('days'), units.Units('minutes')]

        self.multi_compare_id(us, 'equals')
Exemple #2
0
    def test_isreftime(self):
        self.assertFalse(units.Units('days').isreftime)
        self.assertFalse(units.Units('years').isreftime)
        self.assertFalse(units.Units('kg').isreftime)

        for u in self.get_test_reftimes():
            with self.subTest(test_u=u):
                self.assertTrue(u.isreftime)

        for u in self.get_test_reftimes(calendar='julian'):
            with self.subTest(test_u=u):
                self.assertTrue(u.isreftime)

        for u in self.get_test_reftimes(time='12:34:56', calendar='noleap'):
            with self.subTest(test_u=u):
                self.assertTrue(u.isreftime)

        for u in self.get_test_reftimes(unit='minutes',
                                        time='12:34:56',
                                        calendar='proleptic_gregorian'):
            with self.subTest(test_u=u):
                self.assertTrue(u.isreftime)
Exemple #3
0
    def test_reftime_base_eq(self):
        # true if base units are equal
        us = self.get_test_reftimes() \
            + self.get_test_reftimes(calendar='julian')
        u_day = [units.Units('days')]

        self.multi_compare_id(us, 'reftime_base_eq')
        self.multi_compare(u_day, us, 'reftime_base_eq', True)
        self.multi_compare(us, u_day, 'reftime_base_eq', True)

        us_2 = self.get_test_reftimes(unit='minutes',
                                      time='12:34:56',
                                      calendar='proleptic_gregorian')
        u_min = [units.Units('minutes')]

        self.multi_compare_id(us_2, 'reftime_base_eq')
        self.multi_compare(u_min, us_2, 'reftime_base_eq', True)
        self.multi_compare(us_2, u_min, 'reftime_base_eq', True)

        self.multi_compare(u_min, u_day, 'reftime_base_eq', False)
        self.multi_compare(u_min, us, 'reftime_base_eq', False)
        self.multi_compare(u_day, us_2, 'reftime_base_eq', False)
        self.multi_compare(us, us_2, 'reftime_base_eq', False)
Exemple #4
0
    def test_equivalent(self):
        # equivalent if it's well-defined to do a unit conversion from one to the other
        us = self.get_test_reftimes()
        u_day = [units.Units('days')]

        self.multi_compare(us, us, 'equivalent', True)
        self.multi_compare(u_day, us, 'equivalent', False)
        self.multi_compare(us, u_day, 'equivalent', False)

        us_2 = self.get_test_reftimes(calendar='julian')
        self.multi_compare(us, us_2, 'equivalent', False)

        us_2 = self.get_test_reftimes(unit='minutes',
                                      time='12:34:56',
                                      calendar='proleptic_gregorian')
        self.multi_compare(us, us_2, 'equivalent', False)
class PrecipRateToFluxFunction(PreprocessorFunctionBase):
    """A PreprocessorFunction which converts the dependent variable's units, for
    the specific case of precipitation. Flux and precip rate differ by a factor
    of the density of water, so can't be handled by the udunits2 implementation
    provided by :class:`~src.units.Units`. Instead, they're handled here as a
    special case. The general case of unit conversion is handled by
    :class:`ConvertUnitsFunction`.

    CF ``standard_names`` recognized for the conversion are ``precipitation_flux``,
    ``convective_precipitation_flux``, ``large_scale_precipitation_flux``, and
    likewise for ``*_rate``.
    """
    # Incorrect but matches convention for this conversion.
    _liquid_water_density = units.Units('1000.0 kg m-3')
    # list of regcognized standard_names for which transformation is applicable
    # NOTE: not exhaustive
    _std_name_tuples = [
        # flux in CF, rate is not
        ("precipitation_rate", "precipitation_flux"),
        # both in CF
        ("convective_precipitation_rate", "convective_precipitation_flux"),
        # not in CF; here for compatibility with NCAR-CAM
        ("large_scale_precipitation_rate", "large_scale_precipitation_flux")
    ]
    _rate_d = {tup[0]: tup[1] for tup in _std_name_tuples}
    _flux_d = {tup[1]: tup[0] for tup in _std_name_tuples}

    @edit_request_wrapper
    def edit_request(self, v, pod, data_mgr):
        """Edit *pod*\'s Varlist prior to query. If the
        :class:`~src.diagnostic.VarlistEntry` *v* has a ``standard_name`` in the
        recognized list, insert an alternate VarlistEntry whose translation
        requests the complementary type of variable (i.e., if given rate, add an
        entry for flux; if given flux, add an entry for rate.)

        The signature of this method is altered by the :func:`edit_request_wrapper`
        decorator.
        """
        std_name = getattr(v, 'standard_name', "")
        if std_name not in self._rate_d and std_name not in self._flux_d:
            # logic not applicable to this VE; do nothing
            return None
        # construct dummy var to translate (rather than modifying std_name & units)
        # on v's translation) because v may not have a translation
        if std_name in self._rate_d:
            # requested rate, so add alternate for flux
            v_to_translate = copy_as_alternate(
                v,
                data_mgr,
                standard_name=self._rate_d[std_name],
                units=units.to_cfunits(v.units) * self._liquid_water_density)
        elif std_name in self._flux_d:
            # requested flux, so add alternate for rate
            v_to_translate = copy_as_alternate(
                v,
                data_mgr,
                standard_name=self._flux_d[std_name],
                units=units.to_cfunits(v.units) / self._liquid_water_density)

        translate = core.VariableTranslator()
        try:
            new_tv = translate.translate(data_mgr.attrs.convention,
                                         v_to_translate)
        except KeyError as exc:
            pod.log.debug(("%s edit_request on %s: caught %r when trying to "
                           "translate '%s'; varlist unaltered."),
                          self.__class__.__name__, v.full_name, exc,
                          v_to_translate.standard_name)
            return None
        new_v = copy_as_alternate(v, data_mgr)
        new_v.translation = new_tv
        return new_v

    def process(self, var, ds):
        """Convert units of dependent variable *ds* between precip rate and
        precip flux, as specified by the desired units given in *var*. If the
        ``standard_name`` of *ds* is not in the recognized list, return it
        unaltered.
        """
        std_name = getattr(var, 'standard_name', "")
        if std_name not in self._rate_d and std_name not in self._flux_d:
            # logic not applicable to this VE; do nothing
            return ds
        if units.units_equivalent(var.units, var.translation.units):
            # units can be converted by ConvertUnitsFunction; do nothing
            return ds

        # var.translation.units set by edit_request will have been overwritten by
        # DefaultDatasetParser to whatever they are in ds. Change them back.
        tv = var.translation  # abbreviate
        if std_name in self._rate_d:
            # requested rate, received alternate for flux
            new_units = tv.units / self._liquid_water_density
        elif std_name in self._flux_d:
            # requested flux, received alternate for rate
            new_units = tv.units * self._liquid_water_density

        var.log.debug(
            ("Assumed implicit factor of water density in units for %s: "
             "given %s, will convert as %s."),
            var.full_name,
            tv.units,
            new_units,
            tags=util.ObjectLogTag.NC_HISTORY)
        ds[tv.name].attrs['units'] = str(new_units)
        tv.units = new_units
        # actual conversion done by ConvertUnitsFunction; this assures
        # units.convert_dataarray is called with correct parameters.
        return ds
Exemple #6
0
class PrecipRateToFluxFunction(PreprocessorFunctionBase):
    """Convert units on the dependent variable of var, as well as its
    (non-time) dimension coordinate axes, from what's specified in the dataset
    attributes to what's given in the :class:`~src.diagnostic.VarlistEntry`.
    """
    # Incorrect but matches convention for this conversion.
    _liquid_water_density = units.Units('1000.0 kg m-3')
    # list of regcognized standard_names for which transformation is applicable
    # NOTE: not exhaustive
    _std_name_tuples = [
        # flux in CF, rate is not
        ("precipitation_rate", "precipitation_flux"),
        # both in CF
        ("convective_precipitation_rate", "convective_precipitation_flux"),
        # not in CF; here for compatibility with NCAR-CAM
        ("large_scale_precipitation_rate", "large_scale_precipitation_flux")
    ]
    _rate_d = {tup[0]: tup[1] for tup in _std_name_tuples}
    _flux_d = {tup[1]: tup[0] for tup in _std_name_tuples}

    @edit_request_wrapper
    def edit_request(self, v, pod, data_mgr):
        """Edit the POD's Varlist prior to query. If v has a standard_name in the
        list above, insert an alternate varlist entry whose translation requests
        the complementary type of variable (ie, if given rate, add an entry for
        flux; if given flux, add an entry for rate.)
        """
        std_name = getattr(v, 'standard_name', "")
        if std_name not in self._rate_d and std_name not in self._flux_d:
            # logic not applicable to this VE; do nothing
            return None
        # construct dummy var to translate (rather than modifying std_name & units)
        # on v's translation) because v may not have a translation
        if std_name in self._rate_d:
            # requested rate, so add alternate for flux
            v_to_translate = copy_as_alternate(
                v,
                data_mgr,
                standard_name=self._rate_d[std_name],
                units=units.to_cfunits(v.units) * self._liquid_water_density)
        elif std_name in self._flux_d:
            # requested flux, so add alternate for rate
            v_to_translate = copy_as_alternate(
                v,
                data_mgr,
                standard_name=self._flux_d[std_name],
                units=units.to_cfunits(v.units) / self._liquid_water_density)

        translate = core.VariableTranslator()
        try:
            new_tv = translate.translate(data_mgr.attrs.convention,
                                         v_to_translate)
        except KeyError as exc:
            pod.log.debug(("%s edit_request on %s: caught %r when trying to "
                           "translate '%s'; varlist unaltered."),
                          self.__class__.__name__, v.full_name, exc,
                          v_to_translate.standard_name)
            return None
        new_v = copy_as_alternate(v, data_mgr)
        new_v.translation = new_tv
        return new_v

    def process(self, var, ds):
        std_name = getattr(var, 'standard_name', "")
        if std_name not in self._rate_d and std_name not in self._flux_d:
            # logic not applicable to this VE; do nothing
            return ds
        if units.units_equivalent(var.units, var.translation.units):
            # units can be converted by ConvertUnitsFunction; do nothing
            return ds

        # var.translation.units set by edit_request will have been overwritten by
        # DefaultDatasetParser to whatever they are in ds. Change them back.
        tv = var.translation  # abbreviate
        if std_name in self._rate_d:
            # requested rate, received alternate for flux
            new_units = tv.units / self._liquid_water_density
        elif std_name in self._flux_d:
            # requested flux, received alternate for rate
            new_units = tv.units * self._liquid_water_density

        var.log.debug(
            ("Assumed implicit factor of water density in units for %s: "
             "given %s, will convert as %s."),
            var.full_name,
            tv.units,
            new_units,
            tags=util.ObjectLogTag.NC_HISTORY)
        ds[tv.name].attrs['units'] = str(new_units)
        tv.units = new_units
        # actual conversion done by ConvertUnitsFunction; this assures
        # units.convert_dataarray is called with correct parameters.
        return ds
Exemple #7
0
 def get_test_reftimes(self, unit=None, time=None, calendar=None):
     return [units.Units(s, calendar=calendar) \
         for s in self.get_test_date_strings(unit, time)]