Exemplo n.º 1
0
def assert_allclose_units(actual, desired, rtol=1e-7, atol=0, **kwargs):
    """Raise an error if two objects are not equal up to desired tolerance

    This is a wrapper for :func:`numpy.testing.assert_allclose` that also
    verifies unit consistency

    Parameters
    ----------
    actual : array-like
        Array obtained (possibly with attached units)
    desired : array-like
        Array to compare with (possibly with attached units)
    rtol : float, optional
        Relative tolerance, defaults to 1e-7
    atol : float or quantity, optional
        Absolute tolerance. If units are attached, they must be consistent
        with the units of ``actual`` and ``desired``. If no units are attached,
        assumes the same units as ``desired``. Defaults to zero.

    Notes
    -----
    Also accepts additional keyword arguments accepted by
    :func:`numpy.testing.assert_allclose`, see the documentation of that
    function for details.

    """
    # Create a copy to ensure this function does not alter input arrays
    act = unyt_array(actual)
    des = unyt_array(desired)

    try:
        des = des.in_units(act.units)
    except (UnitOperationError, UnitConversionError):
        raise AssertionError(
            "Units of actual (%s) and desired (%s) do not have "
            "equivalent dimensions" % (act.units, des.units))

    rt = unyt_array(rtol)
    if not rt.units.is_dimensionless:
        raise AssertionError("Units of rtol (%s) are not "
                             "dimensionless" % rt.units)

    if not isinstance(atol, unyt_array):
        at = unyt_quantity(atol, des.units)

    try:
        at = at.in_units(act.units)
    except UnitOperationError:
        raise AssertionError("Units of atol (%s) and actual (%s) do not have "
                             "equivalent dimensions" % (at.units, act.units))

    # units have been validated, so we strip units before calling numpy
    # to avoid spurious errors
    act = act.value
    des = des.value
    rt = rt.value
    at = at.value

    return assert_allclose(act, des, rt, at, **kwargs)
Exemplo n.º 2
0
 def __new__(cls, matrix, units):
     """Create `Tensor` instance."""
     if array(matrix).size != 6:
         raise TypeError("`matrix` must have six values")
     else:
         new = unyt_array(empty((3, 3)), units, ureg).view(cls)
         return new
Exemplo n.º 3
0
 def __new__(cls, matrix, units):
     """Create `Tensor` instance."""
     if array(matrix).size != 6:
         raise TypeError("`matrix` must have six values")
     else:
         new = unyt_array(empty((3, 3)), units, ureg).view(cls)
         return new
Exemplo n.º 4
0
def test_atol_conversion_error():
    a1 = unyt_array([1.0, 2.0, 3.0], "cm")
    a2 = unyt_array([1.0, 2.0, 3.0], "cm")
    with pytest.raises(AssertionError):
        assert_allclose_units(a1, a2, atol=unyt_quantity(0.0, "kg"))
Exemplo n.º 5
0
def test_runtime_error():
    a1 = unyt_array([1.0, 2.0, 3.0], "cm")
    a2 = unyt_array([1.0, 2.0, 3.0], "cm")
    with pytest.raises(RuntimeError):
        assert_allclose_units(a1, a2, rtol=unyt_quantity(1e-7, "cm"))
Exemplo n.º 6
0
def test_unequal_error():
    a1 = unyt_array([1.0, 2.0, 3.0], "cm")
    a2 = unyt_array([4.0, 5.0, 6.0], "cm")
    with pytest.raises(AssertionError):
        assert_allclose_units(a1, a2)
Exemplo n.º 7
0
def test_equality():
    a1 = unyt_array([1.0, 2.0, 3.0], "cm")
    a2 = unyt_array([1.0, 2.0, 3.0], "cm")
    assert_allclose_units(a1, a2)
Exemplo n.º 8
0
def uappend(self, val):
    import numpy as np
    return unyt_array(np.append(self.value, val), self.units)
Exemplo n.º 9
0
def array_ufunc(self, ufunc, method, *inputs, **kwargs):
    func = getattr(ufunc, method)
    if "out" not in kwargs:
        out = None
        out_func = None
    else:
        # we need to get both the actual "out" object and a view onto it
        # in case we need to do in-place operations
        out = kwargs.pop("out")[0]
        if out.dtype.kind in ("u", "i"):
            new_dtype = "f" + str(out.dtype.itemsize)
            float_values = out.astype(new_dtype)
            out.dtype = new_dtype
            np.copyto(out, float_values)
        out_func = out.view(np.ndarray)
    if len(inputs) == 1:
        # Unary ufuncs
        inp = inputs[0]
        u = getattr(inp, "units", None)
        if u.dimensions is angle and ufunc in trigonometric_operators:
            # ensure np.sin(90*degrees) works as expected
            inp = inp.in_units("radian").v
        # evaluate the ufunc
        out_arr = func(np.asarray(inp), out=out_func, **kwargs)
        if ufunc in (multiply, divide) and method == "reduce":
            # a reduction of a multiply or divide corresponds to
            # a repeated product which we implement as an exponent
            mul = 1
            power_sign = POWER_SIGN_MAPPING[ufunc]
            if "axis" in kwargs and kwargs["axis"] is not None:
                unit = u**(power_sign * inp.shape[kwargs["axis"]])
            else:
                unit = u**(power_sign * inp.size)
        else:
            # get unit of result
            mul, unit = self._ufunc_registry[ufunc](u)
        # use type(self) here so we can support user-defined
        # subclasses of unyt_array
        ret_class = type(self)
    elif len(inputs) == 2:
        # binary ufuncs
        i0 = inputs[0]
        i1 = inputs[1]
        # coerce inputs to be ndarrays if they aren't already
        inp0 = _coerce_iterable_units(i0)
        inp1 = _coerce_iterable_units(i1)
        u0 = getattr(i0, "units", None) or getattr(inp0, "units", None)
        u1 = getattr(i1, "units", None) or getattr(inp1, "units", None)
        ret_class = _get_binary_op_return_class(type(i0), type(i1))
        if u0 is None:
            u0 = Unit(registry=getattr(u1, "registry", None))
        if u1 is None and ufunc is not power:
            u1 = Unit(registry=getattr(u0, "registry", None))
        elif ufunc is power:
            u1 = inp1
            if inp0.shape != () and inp1.shape != ():
                raise UnitOperationError(ufunc, u0, u1)
            if isinstance(u1, unyt_array):
                if u1.units.is_dimensionless:
                    pass
                else:
                    raise UnitOperationError(ufunc, u0, u1)
            if u1.shape == ():
                u1 = float(u1)
            else:
                u1 = 1.0
        unit_operator = self._ufunc_registry[ufunc]
        if unit_operator in (_preserve_units, _comparison_unit, _arctan2_unit):
            # check "is" equality first for speed
            if u0 is not u1 and u0 != u1:
                # we allow adding, multiplying, comparisons with
                # zero-filled arrays, lists, etc or scalar zero. We
                # do not allow zero-filled unyt_array instances for
                # performance reasons. If we did allow it, every
                # binary operation would need to scan over all the
                # elements of both arrays to check for arrays filled
                # with zeros
                if not isinstance(i0, unyt_array) or not isinstance(
                        i1, unyt_array):
                    any_nonzero = [np.count_nonzero(i0), np.count_nonzero(i1)]
                    if any_nonzero[0] == 0:
                        u0 = u1
                    elif any_nonzero[1] == 0:
                        u1 = u0
                if not u0.same_dimensions_as(u1):
                    if unit_operator is _comparison_unit:
                        # we allow comparisons between data with
                        # units and dimensionless data
                        if u0.is_dimensionless:
                            u0 = u1
                        elif u1.is_dimensionless:
                            u1 = u0
                        else:
                            raise UnitOperationError(ufunc, u0, u1)
                    else:
                        raise UnitOperationError(ufunc, u0, u1)
                conv, offset = u1.get_conversion_factor(u0, inp1.dtype)
                new_dtype = np.dtype("f" + str(inp1.dtype.itemsize))
                conv = new_dtype.type(conv)
                '''if offset is not None:
                    raise InvalidUnitOperation(
                        "Quantities with units of Fahrenheit or Celsius "
                        "cannot by multiplied, divided, subtracted or "
                        "added with data that has different units."
                    )'''
                inp1 = np.asarray(inp1) * conv
        # get the unit of the result
        mul, unit = unit_operator(u0, u1)
        # actually evaluate the ufunc
        out_arr = func(inp0.view(np.ndarray),
                       inp1.view(np.ndarray),
                       out=out_func,
                       **kwargs)
        if unit_operator in (_multiply_units, _divide_units):
            if unit.is_dimensionless and unit.base_value != 1.0:
                if not u0.is_dimensionless:
                    if u0.dimensions == u1.dimensions:
                        out_arr = np.multiply(out_arr.view(np.ndarray),
                                              unit.base_value,
                                              out=out_func)
                        unit = Unit(registry=unit.registry)
            '''if (
                u0.base_offset
                and u0.dimensions is temperature
                or u1.base_offset
                and u1.dimensions is temperature
            ):
                print(u0.base_offset,u0.dimensions,u1.base_offset,u1.dimensions)
                raise InvalidUnitOperation(
                    "Quantities with units of Fahrenheit or Celsius TODO "
                    "cannot by multiplied, divide, subtracted or added."
                )'''
    else:
        raise RuntimeError(
            "Support for the %s ufunc with %i inputs has not been"
            "added to unyt_array." % (str(ufunc), len(inputs)))
    if unit is None:
        out_arr = np.array(out_arr, copy=False)
    elif ufunc in (modf, divmod_):
        out_arr = tuple((ret_class(o, unit) for o in out_arr))
    elif out_arr.size == 1:
        out_arr = unyt_quantity(np.asarray(out_arr), unit)
    else:
        if ret_class is unyt_quantity:
            # This happens if you do ndarray * unyt_quantity.
            # Explicitly casting to unyt_array avoids creating a
            # unyt_quantity with size > 1
            out_arr = unyt_array(out_arr, unit)
        else:
            out_arr = ret_class(out_arr, unit, bypass_validation=True)
    if out is not None:
        if mul != 1:
            multiply(out, mul, out=out)
            if np.shares_memory(out_arr, out):
                mul = 1
        if isinstance(out, unyt_array):
            try:
                out.units = out_arr.units
            except AttributeError:
                # out_arr is an ndarray
                out.units = Unit("", registry=self.units.registry)
    if mul == 1:
        return out_arr
    return mul * out_arr