def test_dimensionality_error(self): ex = DimensionalityError("a", "b") self.assertEqual(str(ex), "Cannot convert from 'a' to 'b'") ex = DimensionalityError("a", "b", "c") self.assertEqual(str(ex), "Cannot convert from 'a' (c) to 'b' ()") ex = DimensionalityError("a", "b", "c", "d", extra_msg=": msg") self.assertEqual(str(ex), "Cannot convert from 'a' (c) to 'b' (d): msg")
def test_dimensionality_error(self): ex = DimensionalityError("a", "b") assert str(ex) == "Cannot convert from 'a' to 'b'" ex = DimensionalityError("a", "b", "c") assert str(ex) == "Cannot convert from 'a' (c) to 'b' ()" ex = DimensionalityError("a", "b", "c", "d", extra_msg=": msg") assert str(ex) == "Cannot convert from 'a' (c) to 'b' (d): msg"
def test_str_errors(self): self.assertEqual(str(UndefinedUnitError('rabbits')), "'{0!s}' is not defined in the unit registry".format('rabbits')) self.assertEqual(str(UndefinedUnitError(('rabbits', 'horses'))), "'{0!s}' are not defined in the unit registry".format(('rabbits', 'horses'))) self.assertEqual(u(str(DimensionalityError('meter', 'second'))), "Cannot convert from 'meter' to 'second'") self.assertEqual(str(DimensionalityError('meter', 'second', 'length', 'time')), "Cannot convert from 'meter' (length) to 'second' (time)")
def __init__(self, name="", **kwargs): c1 = set(kwargs.keys()) == {'magnitude', 'units'} c2 = set(kwargs.keys()) == {'quantity'} self.name = name # why bitwise exclusive or opposed to bitwise | or? assert c1 ^ c2 if c1: magnitude = kwargs['magnitude'] units = kwargs['units'] if isinstance(magnitude, numbers.Number): self.magnitude = ufloat(magnitude, 0) else: self.magnitude = magnitude self.units = units self.value = Q_(self.magnitude, self.units) if c2: quantity = kwargs['quantity'] self.value = quantity if c1 ^ c2: try: assert self.class_units.dimensionality == Q_( 1, self.units).dimensionality except AssertionError: raise DimensionalityError(self.class_units.units, self.value.units)
def xr_check_dimensionality(da: xr.DataArray, units_ref: Union[str, pint.Unit]): """Check if the dimensionality of a ``DataArray`` is compatible with reference unit. Parameters ---------- da: The data array that should be checked. units_ref: The reference unit Raises ------ pint.DimensionalityError The error is raised if the check fails """ if units_ref is None: return units_ref = U_(units_ref) units = da.weldx.units if units is None or not units.is_compatible_with(units_ref): raise DimensionalityError( units, units_ref, extra_msg= f"\nDataArray units are '{units}'. This is incompatible with " f"the expected dimensionality '{units_ref.dimensionality}'", )
def test_pickle_definition_syntax_error(self): # OffsetUnitCalculusError raised from a custom ureg must be pickleable even if # the ureg is not registered as the application ureg ureg = UnitRegistry(filename=None) ureg.define("foo = [bar]") ureg.define("bar = 2 foo") q1 = ureg.Quantity("1 foo") q2 = ureg.Quantity("1 bar") for protocol in range(pickle.HIGHEST_PROTOCOL + 1): for ex in [ DefinitionSyntaxError("foo", filename="a.txt", lineno=123), RedefinitionError("foo", "bar"), UndefinedUnitError("meter"), DimensionalityError("a", "b", "c", "d", extra_msg=": msg"), OffsetUnitCalculusError( Quantity("1 kg")._units, Quantity("1 s")._units), OffsetUnitCalculusError(q1._units, q2._units), ]: with self.subTest(protocol=protocol, etype=type(ex)): pik = pickle.dumps(ureg.Quantity("1 foo"), protocol) with self.assertRaises(UndefinedUnitError): pickle.loads(pik) # assert False, ex.__reduce__() ex2 = pickle.loads(pickle.dumps(ex, protocol)) assert type(ex) is type(ex2) self.assertEqual(ex.args, ex2.args) self.assertEqual(ex.__dict__, ex2.__dict__) self.assertEqual(str(ex), str(ex2))
def convert_universal( I, from_unit, to_unit, spec=None, per_nm_is_like="mW/sr/cm2/nm", per_cm_is_like="mW/sr/cm2/cm_1", ): """ Return variable var in whatever unit, and converts to to_unit Also deal with cases where var is in ~1/nm (per_nm_is_like) or ~1/cm-1 (per_cm_is_like) Parameters ---------- var: str variable to get. Usually 'radiance' or 'radiance_noslit' to_unit: str unit to convert variable to Other Parameters ---------------- spec: :class:`~radis.spectrum.spectrum.Spectrum` object needed to get wavenumber in case we need to do a change of variable within the integral Notes ----- wavenumber is needed in case we convert from ~1/nm to ~1/cm-1 (requires a change of variable in the integral) """ Iunit0 = from_unit Iunit = to_unit try: if is_homogeneous(Iunit0, per_nm_is_like) and is_homogeneous( Iunit, per_cm_is_like ): w_cm = spec.get_wavenumber() I = convert_rad2cm(I, w_cm, Iunit0, Iunit) # note that there may still be a new DimensionalityError # raise here if the input was non-sense. elif is_homogeneous(Iunit0, per_cm_is_like) and is_homogeneous( Iunit, per_nm_is_like ): w_cm = spec.get_wavenumber() I = convert_rad2nm(I, w_cm, Iunit0, Iunit) # note that there may still be a new DimensionalityError # raise here if the input was non-sense. else: # general case: just convert I = conv2(I, Iunit0, Iunit) except DimensionalityError: raise DimensionalityError(Iunit0, Iunit) return I
def convert_user_input(self, value): """Validate and convert an input value to its 'external' form""" if self.units is not None: q = Q_(value) if not q.dimensionality == self.units.dimensionality: raise DimensionalityError(q.units, self.units) return Q_(self.convert_raw_input(q.magnitude), q.units) else: return self.convert_raw_input(value)
def test_errors(self): x = ('meter', ) msg = "'meter' is not defined in the unit registry" self.assertEqual(str(UndefinedUnitError(x)), msg) self.assertEqual(str(UndefinedUnitError(list(x))), msg) self.assertEqual(str(UndefinedUnitError(set(x))), msg) msg = "Cannot convert from 'a' (c) to 'b' (d)msg" ex = DimensionalityError('a', 'b', 'c', 'd', 'msg') self.assertEqual(str(ex), msg)
def write_converter(quantity): """Return the magnitude of quantity in terms of {unit} :param quantity: pint quantity :return: magnitude in terms of {unit} """ try: base_unit_value = quantity.to(ureg(unit_str)) except AttributeError: raise DimensionalityError(unit_str, None) return base_unit_value.magnitude
def __setitem__(self, key, value): # Overrides pint's built-in version of this ... this is apparently way faster try: self.magnitude[key] = value.value_in(self.units) except AttributeError: if not hasattr(value, 'value_in'): # deal with missing `value_in` method if self.dimensionless: # case 1: this is OK if self is dimensionless self.magnitude[key] = value else: # case 2: User tried to pass a number without units raise DimensionalityError('%s cannot be assigned to array with dimensions %s' % (value, self.units)) else: # case 3: attribute error is unrelated to this raise
def of_division(self, numerator, denominator): "Cached unit division. Requires Quantity inputs." if numerator.units is denominator.units: return 1 key = (id(numerator.units), id(denominator.units)) if key not in self.division_cache: if numerator.units and denominator.units: conversion = numerator.units / denominator.units else: conversion = numerator.units or 1 / denominator.units try: self.division_cache[key] = float(conversion) except DimensionalityError: raise DimensionalityError(numerator, denominator) return self.division_cache[key]
def __set__(self, instance, value): unit = getattr(instance, self.unit_name) if isinstance(value, ureg.Quantity) and unit is not None: try: value = value.to(unit) except DimensionalityError as e: raise DimensionalityError( e.units1, e.units2, e.dim1, e.dim2, 'Wrong dimensions when setting {} with value {}'.format( self.name, value)) rng = getattr(instance, self.rng_name) if rng is not None: if not Guarded.in_range(value, rng): raise OutOfRangeError(value, rng, self.name) Guarded.cite_value(value) setattr(instance, self.guard_name, value)
def __setitem__(self, key, value): # Overrides pint's built-in version of this ... this is apparently way faster try: self.magnitude[key] = value.value_in(self.units) except AttributeError: if not hasattr(value, 'value_in'): # deal with missing `value_in` method if self.dimensionless: # case 1: this is OK if self is dimensionless self.magnitude[key] = value elif not isinstance( value, numbers.Number): # case 2: this is not a number raise TypeError('"%s" is not a valid numeric value' % value) else: # case 3: wrong units raise DimensionalityError(self.units, ureg.dimensionless) else: # case 3: attribute error is unrelated to this raise
def convert(self, value): """ Returns quantity converted to these units Args: value (MdtQuantity or Numeric): value to convert Returns: MdtQuantity: converted value Raises: DimensionalityError: if the quantity does not have these units' dimensionality """ if hasattr(value, 'to'): return value.to(self) elif self.dimensionless: return value * self else: raise DimensionalityError('Cannot convert "%s" to units of "%s"' % (value, self))
def __init__(self, *args, **kwargs): self.base_units = kwargs.pop('base_units', None) if not self.base_units: raise ValueError( 'QuantityFormField requires a base_units kwarg of a single unit type (eg: grams)' ) self.units = kwargs.pop('unit_choices', [self.base_units]) if self.base_units not in self.units: self.units.append(self.base_units) base_unit = getattr(ureg, self.base_units) for _unit in self.units: unit = getattr(ureg, _unit) if unit.dimensionality != base_unit.dimensionality: raise DimensionalityError(base_unit, unit) kwargs.update({'widget': QuantityWidget(allowed_types=self.units)}) super(QuantityFormField, self).__init__(*args, **kwargs)
def conv2(quantity, fromunit, tounit): ''' Converts `quantity` from unit `fromunit` to unit `tounit` Parameters ---------- quantity: array quantity to convert fromunit: str input unit tounit: str output unit Note ---- 1. The output is still non dimensional. We don't transform `quantity` into a pint array (or neq.phys.uarray) because this may create a performance drop in computationaly-expensive task. Instead, we assume we know for sure the units in which some of our quantities will be created, and just want to let the users choose another output unit 2. because angles are dimensionless a 'mW/cm2/sr/nm' > 'mW/cm2/nm' conversion is considered valid, while we expected the Luminance to be converted to an exitance/irradiance and thus multiplied by Pi ! Here we prevent this behavior by considering ''' try: a = Q_(quantity, fromunit) a = a.to(tounit) # Hardcoded: 'pint' considers angles to be dimensionless (which they are) # so a 'mW/cm2/sr/nm' > 'mW/cm2/nm' conversion is then considered valid, # while we expected the Luminance to be converted to an Exitance/Irradiance # and thus multiplied by Pi !! # Here we prevent this behavior: if 'sr' in fromunit and 'sr' not in tounit: raise DimensionalityError(fromunit, tounit) if 'sr' in tounit and 'sr' not in fromunit: raise DimensionalityError(fromunit, tounit) except TypeError: if 'cm-1' in fromunit or 'cm-1' in tounit: # raise TypeError('Use cm_1 instead of cm-1 else it triggers errors in '+\ # 'pint (symbolic unit converter)') return conv2(quantity, fromunit.replace('cm-1', 'cm_1'), tounit.replace('cm-1', 'cm_1')) else: raise return a.magnitude
def xr_check_coords(coords: Union[xr.DataArray, Mapping[str, Any]], ref: dict) -> bool: """Validate the coordinates of the DataArray against a reference dictionary. The reference dictionary should have the dimensions as keys and those contain dictionaries with the following keywords (all optional): ``values`` Specify exact coordinate values to match. ``dtype`` : str or type Ensure coordinate dtype matches at least one of the given dtypes. ``optional`` : boolean default ``False`` - if ``True``, the dimension has to be in the DataArray dax ``dimensionality`` : str or pint.Unit Check if ``.attrs["units"]`` is the requested dimensionality ``units`` : str or pint.Unit Check if ``.attrs["units"]`` matches the requested unit Parameters ---------- coords xarray object or coordinate mapping that should be validated ref reference dictionary Returns ------- bool True, if the test was a success, else an exception is raised Examples -------- >>> import numpy as np >>> import pandas as pd >>> import xarray as xr >>> import weldx as wx >>> dax = xr.DataArray( ... data=np.ones((3, 2, 3)), ... dims=["d1", "d2", "d3"], ... coords={ ... "d1": np.array([-1, 0, 2], dtype=int), ... "d2": pd.DatetimeIndex(["2020-05-01", "2020-05-03"]), ... "d3": ["x", "y", "z"], ... } ... ) >>> ref = dict( ... d1={"optional": True, "values": np.array([-1, 0, 2], dtype=int)}, ... d2={ ... "values": pd.DatetimeIndex(["2020-05-01", "2020-05-03"]), ... "dtype": ["datetime64[ns]", "timedelta64[ns]"], ... }, ... d3={"values": ["x", "y", "z"], "dtype": "<U1"}, ... ) >>> wx.util.xr_check_coords(dax, ref) True """ # only process the coords of the xarray if isinstance(coords, (xr.DataArray, xr.Dataset)): coords = coords.coords for key, check in ref.items(): # check if the optional key is set to true if "optional" in check and check["optional"] and key not in coords: # skip this key - it is not in dax continue if key not in coords: # Attributes not found in coords raise KeyError(f"Could not find required coordinate '{key}'.") # only if the key "values" is given do the validation if "values" in check and not np.all( coords[key].values == check["values"]): raise ValueError(f"Value mismatch in DataArray and ref['{key}']" f"\n{coords[key].values}" f"\n{check['values']}") # only if the key "dtype" is given do the validation if "dtype" in check: dtype_list = check["dtype"] if not isinstance(dtype_list, list): dtype_list = [dtype_list] if not any( _check_dtype(coords[key].dtype, var_dtype) for var_dtype in dtype_list): raise TypeError( f"Mismatch in the dtype of the DataArray and ref['{key}']") if UNITS_KEY in check: units = coords[key].attrs.get(UNITS_KEY, None) if not units or not U_(units) == U_(check[UNITS_KEY]): raise ValueError( f"Unit mismatch in coordinate '{key}'\n" f"Coordinate has unit '{units}', expected '{check['units']}'" ) if "dimensionality" in check: units = coords[key].attrs.get(UNITS_KEY, None) dim = check["dimensionality"] if units is None or not U_(units).is_compatible_with(dim): raise DimensionalityError( units, check["dimensionality"], extra_msg= f"\nDimensionality mismatch in coordinate '{key}'\n" f"Coordinate has unit '{units}', expected '{dim}'", ) return True