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')
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)
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)
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
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
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)]