def cfunits_conform(self, to_units, from_units=None): """ Conform value units in-place. If there are scale or offset parameters in the attribute dictionary, they will be removed. :param to_units: Target conform units. :type to_units: str or units object :param from_units: Overload source units. :type from_units: str or units object :raises: NoUnitsError """ if from_units is None and self.units is None: raise NoUnitsError(self) # Use overloaded value for source units. from_units = self.cfunits if from_units is None else from_units # Get the conform value before swapping the units. Conversion inside time dimensions may be negatively affected # otherwise. to_conform_value = self._get_to_conform_value_() # Update the units attribute with the destination units. Do this before conversion to not enter recursion when # setting the new value. self.units = to_units # Conform the units. new_value = get_conformed_units(to_conform_value, from_units, to_units) self._set_to_conform_value_(new_value) # Let the data type load from the value array. self._dtype = None # Remove any compression attributes if present. for remove in NETCDF_ATTRIBUTES_TO_REMOVE_ON_VALUE_CHANGE: self.attrs.pop(remove, None)
def bounds(self, value): self._bounds = get_none_or_2d(value) if self._bounds is not None and self._original_units is not None: are_units_equal = get_are_units_equal((self.units, self._original_units)) if not are_units_equal: self._bounds = get_conformed_units(self._bounds, self._original_units, self.conform_units_to) if value is not None: self._validate_bounds_()
def cfunits_conform(self, *args, **kwargs): # Get the from units before conforming the value. The units are changed in the value conform. from_units = kwargs.get('from_units') or self.cfunits # Store the original units to use for bounds conversion. self._original_units = self.cfunits # Conform the value. AbstractSourcedVariable.cfunits_conform(self, *args, **kwargs) # Conform the units if self._bounds is not None: self._bounds = get_conformed_units(self._bounds, from_units, args[0])
def test_get_field_with_overloaded_units(self): rd = self.test_data.get_rd('cancm4_tas', kwds={'conform_units_to': 'celsius'}) preload = [False, True] for pre in preload: field = rd.get() # Conform units argument needs to be attached to a field variable. units_celsius = get_units_object('celsius') self.assertEqual(field.variables['tas']._conform_units_to, units_celsius) sub = field.get_time_region({'year': [2009], 'month': [5]}) if pre: # If we wanted to load the data prior to subset then do so and manually perform the units conversion. to_test = sub.variables['tas'].value.copy() get_conformed_units(to_test, sub.variables['tas'].cfunits, units_celsius) # Assert the conform attribute makes it though the subset self.assertEqual(sub.variables['tas']._conform_units_to, units_celsius) value = sub.variables['tas'].value self.assertAlmostEqual(np.ma.mean(value), 5.921925206338206) self.assertAlmostEqual(np.ma.median(value), 10.745431900024414) if pre: # Assert the manually converted array matches the loaded value. self.assertNumpyAll(to_test, value)
def test_get_conformed_units(self): value = np.array([5, 6, 7]) res = get_conformed_units(value, 'celsius', 'fahrenheit') try: import cf_units # Test original values are unchanged. self.assertEqual(np.mean(value), 6) test_value = res except ImportError: import cfunits # Test inplace conversion is used. test_value = value # Test values are conformed. self.assertAlmostEqual(np.mean(test_value), 42.799999999999876)