[yt-svn] commit/yt: 8 new changesets

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Tue Jun 20 08:30:46 PDT 2017


8 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/9d774ed941ee/
Changeset:   9d774ed941ee
User:        ngoldbaum
Date:        2017-05-11 15:37:14+00:00
Summary:     add support for the new ufuncs in Numpy 1.13
Affected #:  2 files

diff -r 4409c8396c9ad1eafd15f08052aec9b2f2682a0a -r 9d774ed941ee41db0bc3b0458fc3e96f6c0ff40e yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -687,6 +687,13 @@
     assert_equal(np.copy(quan), quan)
     assert_array_equal(np.copy(arr), arr)
 
+# needed so the tests function on older numpy versions that have
+# different sets of ufuncs
+def yield_np_ufuncs(ufunc_list):
+    for u in ufunc_list:
+        ufunc = getattr(np, u, None)
+        if ufunc is not None:
+            yield ufunc
 
 def unary_ufunc_comparison(ufunc, a):
     out = a.copy()
@@ -697,12 +704,11 @@
         ret = ufunc(a)
         assert_true(not hasattr(ret, 'units'))
         assert_array_equal(ret, ufunc(a))
-    elif ufunc in (np.exp, np.exp2, np.log, np.log2, np.log10, np.expm1,
-                   np.log1p, np.sin, np.cos, np.tan, np.arcsin, np.arccos,
-                   np.arctan, np.sinh, np.cosh, np.tanh, np.arccosh,
-                   np.arcsinh, np.arctanh, np.deg2rad, np.rad2deg,
-                   np.isfinite, np.isinf, np.isnan, np.signbit, np.sign,
-                   np.rint, np.logical_not):
+    elif ufunc in yield_np_ufuncs([
+            'exp', 'exp2', 'log', 'log2', 'log10', 'expm1', 'log1p', 'sin',
+            'cos', 'tan', 'arcsin', 'arccos', 'arctan', 'sinh', 'cosh', 'tanh',
+            'arccosh', 'arcsinh', 'arctanh', 'deg2rad', 'rad2deg', 'isfinite',
+            'isinf', 'isnan', 'signbit', 'sign', 'rint', 'logical_not']):
         # These operations should return identical results compared to numpy.
         with np.errstate(invalid='ignore'):
             try:
@@ -716,14 +722,16 @@
             # In-place copies do not drop units.
             assert_true(hasattr(out, 'units'))
             assert_true(not hasattr(ret, 'units'))
-    elif ufunc in (np.absolute, np.fabs, np.conjugate, np.floor, np.ceil,
-                   np.trunc, np.negative, np.spacing):
+    elif ufunc in yield_np_ufuncs(
+            ['absolute', 'fabs', 'conjugate', 'floor', 'ceil', 'trunc',
+             'negative', 'spacing', 'positive']):
         ret = ufunc(a, out=out)
 
         assert_array_equal(ret, out)
         assert_array_equal(ret.to_ndarray(), ufunc(a_array))
         assert_true(ret.units == out.units)
-    elif ufunc in (np.ones_like, np.square, np.sqrt, np.reciprocal):
+    elif ufunc in yield_np_ufuncs(
+            ['ones_like', 'square', 'sqrt', 'reciprocal']):
         if ufunc is np.ones_like:
             ret = ufunc(a)
         else:
@@ -756,6 +764,8 @@
         assert_array_equal(ret2, npret2)
     elif ufunc is np.invert:
         assert_raises(TypeError, ufunc, a)
+    elif hasattr(np, 'isnat') and ufunc is np.isnat:
+        assert_raises(ValueError, ufunc, a)
     else:
         # There shouldn't be any untested ufuncs.
         assert_true(False)
@@ -763,19 +773,20 @@
 
 def binary_ufunc_comparison(ufunc, a, b):
     out = a.copy()
-    if ufunc in (np.add, np.subtract, np.remainder, np.fmod, np.mod,
-                 np.arctan2, np.hypot, np.greater, np.greater_equal, np.less,
-                 np.less_equal, np.equal, np.not_equal, np.logical_and,
-                 np.logical_or, np.logical_xor, np.maximum, np.minimum,
-                 np.fmax, np.fmin, np.nextafter):
+    if ufunc in yield_np_ufuncs([
+            'add', 'subtract', 'remainder', 'fmod', 'mod', 'arctan2', 'hypot',
+            'greater', 'greater_equal', 'less', 'less_equal', 'equal',
+            'not_equal', 'logical_and', 'logical_or', 'logical_xor', 'maximum',
+            'minimum', 'fmax', 'fmin', 'nextafter', 'heaviside']):
         if a.units != b.units and a.units.dimensions == b.units.dimensions:
             assert_raises(YTUfuncUnitError, ufunc, a, b)
             return
         elif a.units != b.units:
             assert_raises(YTUnitOperationError, ufunc, a, b)
             return
-    if ufunc in (np.bitwise_and, np.bitwise_or, np.bitwise_xor,
-                 np.left_shift, np.right_shift, np.ldexp):
+    if ufunc in yield_np_ufuncs(
+            ['bitwise_and', 'bitwise_or', 'bitwise_xor', 'left_shift',
+             'right_shift', 'ldexp']):
         assert_raises(TypeError, ufunc, a, b)
         return
 
@@ -790,7 +801,10 @@
                    np.logical_xor):
         assert_true(not isinstance(ret, YTArray) and
                     isinstance(ret, np.ndarray))
-    assert_array_equal(ret, out)
+    if isinstance(ret, tuple):
+        assert_array_equal(ret[0], out)
+    else:
+        assert_array_equal(ret, out)
     if (ufunc in (np.divide, np.true_divide, np.arctan2) and
         (a.units.dimensions == b.units.dimensions)):
         assert_array_almost_equal(
@@ -801,12 +815,15 @@
 
 def test_ufuncs():
     for ufunc in unary_operators:
+        if ufunc is None:
+            continue
         unary_ufunc_comparison(ufunc, YTArray([.3, .4, .5], 'cm'))
         unary_ufunc_comparison(ufunc, YTArray([12, 23, 47], 'g'))
         unary_ufunc_comparison(ufunc, YTArray([2, 4, -6], 'erg/m**3'))
 
     for ufunc in binary_operators:
-
+        if ufunc is None:
+            continue
         # arr**arr is undefined for arrays with units because
         # each element of the result would have different units.
         if ufunc is np.power:

diff -r 4409c8396c9ad1eafd15f08052aec9b2f2682a0a -r 9d774ed941ee41db0bc3b0458fc3e96f6c0ff40e yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -30,6 +30,12 @@
     isreal, iscomplex, isfinite, isinf, isnan, signbit, copysign, nextafter, \
     modf, ldexp, frexp, fmod, floor, ceil, trunc, fabs, spacing
 
+try:
+    # numpy 1.13 or newer
+    from numpy import positive, divmod as divmod_, isnat, heaviside
+except ImportError:
+    positive, divmod_, isnat, heaviside = (None,)*4
+
 from yt.units.unit_object import Unit, UnitParseError
 from yt.units.unit_registry import UnitRegistry
 from yt.units.dimensions import \
@@ -206,7 +212,7 @@
     log10, expm1, log1p, sqrt, square, reciprocal, sin, cos, tan, arcsin,
     arccos, arctan, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad,
     rad2deg, invert, logical_not, isreal, iscomplex, isfinite, isinf, isnan,
-    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing
+    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing, positive, isnat
 )
 
 binary_operators = (
@@ -214,7 +220,7 @@
     remainder, mod, arctan2, hypot, bitwise_and, bitwise_or, bitwise_xor,
     left_shift, right_shift, greater, greater_equal, less, less_equal,
     not_equal, equal, logical_and, logical_or, logical_xor, maximum, minimum,
-    fmax, fmin, copysign, nextafter, ldexp, fmod,
+    fmax, fmin, copysign, nextafter, ldexp, fmod, divmod_, heaviside
 )
 
 trigonometric_operators = (
@@ -367,6 +373,10 @@
         ceil: passthrough_unit,
         trunc: passthrough_unit,
         spacing: passthrough_unit,
+        positive: passthrough_unit,
+        divmod_: passthrough_unit,
+        isnat: return_without_unit,
+        heaviside: preserve_units,
     }
 
     __array_priority__ = 2.0


https://bitbucket.org/yt_analysis/yt/commits/586ddfcbd2aa/
Changeset:   586ddfcbd2aa
User:        ngoldbaum
Date:        2017-05-11 20:26:06+00:00
Summary:     add __array_ufunc__
Affected #:  2 files

diff -r 9d774ed941ee41db0bc3b0458fc3e96f6c0ff40e -r 586ddfcbd2aa804e22d876b9ae67a7e6e1fdf8dd yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -791,6 +791,7 @@
         return
 
     ret = ufunc(a, b, out=out)
+    ret = ufunc(a, b)
 
     if ufunc is np.multiply:
         assert_true(ret.units == a.units*b.units)

diff -r 9d774ed941ee41db0bc3b0458fc3e96f6c0ff40e -r 586ddfcbd2aa804e22d876b9ae67a7e6e1fdf8dd yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -84,7 +84,7 @@
 def multiply_units(unit1, unit2):
     return unit1 * unit2
 
-def preserve_units(unit1, unit2):
+def preserve_units(unit1, unit2=None):
     return unit1
 
 @lru_cache(maxsize=128, typed=False)
@@ -103,16 +103,16 @@
 def reciprocal_unit(unit):
     return unit**-1
 
-def passthrough_unit(unit):
+def passthrough_unit(unit, unit2=None):
     return unit
 
-def return_without_unit(unit):
+def return_without_unit(unit, unit2=None):
     return None
 
 def arctan2_unit(unit1, unit2):
     return NULL_UNIT
 
-def comparison_unit(unit1, unit2):
+def comparison_unit(unit1, unit2=None):
     return None
 
 def invert_units(unit):
@@ -1177,6 +1177,101 @@
         else:
             return ret
 
+    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
+        if 'out' in kwargs:
+            out = kwargs['out'][0].view(np.ndarray)
+        else:
+            out = None
+        if method == '__call__':
+            func = ufunc
+        else:
+            func = getattr(ufunc, method)
+        if len(inputs) == 1:
+            u = getattr(inputs[0], 'units', None)
+            if u is None:
+                u = NULL_UNIT
+            if u.dimensions is angle and ufunc in trigonometric_operators:
+                inp = inputs[0].in_units('radian')
+            else:
+                inp = inputs[0]
+            if out is not None:
+                func(np.asarray(inp), out=out)
+                out_arr = out
+            else:
+                out_arr = func(np.asarray(inp))
+            unit = self._ufunc_registry[ufunc](u)
+            ret_class = type(self)
+        elif len(inputs) == 2:
+            if out is not None:
+                func(np.asarray(inputs[0]), np.asarray(inputs[1]), out=out)
+                out_arr = out
+            else:
+                out_arr = func(np.asarray(inputs[0]), np.asarray(inputs[1]))
+            oper1 = coerce_iterable_units(inputs[0])
+            oper2 = coerce_iterable_units(inputs[1])
+            cls1 = type(oper1)
+            cls2 = type(oper2)
+            unit1 = getattr(oper1, 'units', None)
+            unit2 = getattr(oper2, 'units', None)
+            ret_class = get_binary_op_return_class(cls1, cls2)
+            if unit1 is None:
+                unit1 = Unit(registry=getattr(unit2, 'registry', None))
+            if unit2 is None and ufunc is not power:
+                unit2 = Unit(registry=getattr(unit1, 'registry', None))
+            elif ufunc is power:
+                unit2 = oper2
+                if isinstance(unit2, np.ndarray):
+                    if isinstance(unit2, YTArray):
+                        if unit2.units.is_dimensionless:
+                            pass
+                        else:
+                            raise YTUnitOperationError(ufunc, unit1, unit2)
+                    unit2 = 1.0
+            unit_operator = self._ufunc_registry[ufunc]
+            if unit_operator in (preserve_units, comparison_unit, arctan2_unit):
+                # Allow comparisons, addition, and subtraction with
+                # dimensionless quantities or arrays filled with zeros.
+                u1d = unit1.is_dimensionless
+                u2d = unit2.is_dimensionless
+                if unit1 != unit2:
+                    any_nonzero = [np.any(oper1), np.any(oper2)]
+                    if any_nonzero[0] == np.bool_(False):
+                        unit1 = unit2
+                    elif any_nonzero[1] == np.bool_(False):
+                        unit2 = unit1
+                    elif not any([u1d, u2d]):
+                        if not unit1.same_dimensions_as(unit2):
+                            raise YTUnitOperationError(ufunc, unit1, unit2)
+                        else:
+                            raise YTUfuncUnitError(ufunc, unit1, unit2)
+            unit = unit_operator(unit1, unit2)
+            if unit_operator in (multiply_units, divide_units):
+                if unit.is_dimensionless and unit.base_value != 1.0:
+                    if not unit1.is_dimensionless:
+                        if unit1.dimensions == unit2.dimensions:
+                            out_arr = np.multiply(out_arr.view(np.ndarray),
+                                                  unit.base_value, out=out)
+                            unit = Unit(registry=unit.registry)
+        else:
+            raise RuntimeError("Support for the %s ufunc has not been added "
+                               "to YTArray." % str(ufunc))
+        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 = YTQuantity(np.asarray(out_arr), unit)
+        else:
+            if ret_class is YTQuantity:
+                # This happens if you do ndarray * YTQuantity. Explicitly
+                # casting to YTArray avoids creating a YTQuantity with size > 1
+                out_arr = YTArray(np.asarray(out_arr), unit)
+            else:
+                out_arr = ret_class(np.asarray(out_arr), unit)
+        if 'out' in kwargs and hasattr(kwargs['out'][0], 'units'):
+            kwargs['out'][0].units = unit
+        return out_arr
+        
     def __array_wrap__(self, out_arr, context=None):
         ret = super(YTArray, self).__array_wrap__(out_arr, context)
         if isinstance(ret, YTQuantity) and ret.shape != ():


https://bitbucket.org/yt_analysis/yt/commits/57a64cd5554c/
Changeset:   57a64cd5554c
User:        ngoldbaum
Date:        2017-05-24 02:00:22+00:00
Summary:     Fix for change of deprecated behavior in numpy 1.13
Affected #:  1 file

diff -r 586ddfcbd2aa804e22d876b9ae67a7e6e1fdf8dd -r 57a64cd5554cd804f12dfdc2fc9884cd2ecc0228 yt/utilities/tests/test_minimal_representation.py
--- a/yt/utilities/tests/test_minimal_representation.py
+++ b/yt/utilities/tests/test_minimal_representation.py
@@ -1,3 +1,4 @@
+import numpy as np
 import os.path
 import yt
 from yt.config import ytcfg
@@ -36,7 +37,7 @@
 
     def fail_for_different_method():
         proj2_c = ds.proj(field, "z", data_source=sp, method="mip")
-        return (proj2[field] == proj2_c[field]).all()
+        return np.equal(proj2[field], proj2_c[field]).all()
     assert_raises(YTUnitOperationError, fail_for_different_method)
 
     def fail_for_different_source():


https://bitbucket.org/yt_analysis/yt/commits/d96560624ad6/
Changeset:   d96560624ad6
User:        ngoldbaum
Date:        2017-05-24 02:00:54+00:00
Summary:     Finish update YTArray after adding __array_ufunc__
Affected #:  2 files

diff -r 57a64cd5554cd804f12dfdc2fc9884cd2ecc0228 -r d96560624ad6a756b3cb6808ade6984057e13e1c yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -23,6 +23,7 @@
 import shutil
 import tempfile
 
+from distutils.version import LooseVersion
 from nose.tools import assert_true
 from numpy.testing import \
     assert_array_equal, \
@@ -88,9 +89,17 @@
     operate_and_compare(a1, a2, operator.add, answer1)
     operate_and_compare(a2, a1, operator.add, answer2)
     operate_and_compare(a1, a3, operator.add, answer1)
-    operate_and_compare(a3, a1, operator.add, answer1)
-    assert_raises(YTUfuncUnitError, np.add, a1, a2)
-    assert_raises(YTUfuncUnitError, np.add, a1, a3)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        operate_and_compare(a3, a1, operator.add, answer1)
+        assert_raises(YTUfuncUnitError, np.add, a1, a2)
+        assert_raises(YTUfuncUnitError, np.add, a1, a3)
+    else:
+        operate_and_compare(a3, a1, operator.add, answer2)
+        operate_and_compare(a1, a2, np.add, answer1)
+        operate_and_compare(a2, a1, np.add, answer2)
+        operate_and_compare(a1, a3, np.add, answer1)
+        operate_and_compare(a3, a1, np.add, answer2)
+        
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
@@ -163,8 +172,14 @@
     operate_and_compare(a2, a1, operator.sub, answer2)
     operate_and_compare(a1, a3, operator.sub, answer1)
     operate_and_compare(a3, a1, operator.sub, answer3)
-    assert_raises(YTUfuncUnitError, np.subtract, a1, a2)
-    assert_raises(YTUfuncUnitError, np.subtract, a1, a3)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        assert_raises(YTUfuncUnitError, np.subtract, a1, a2)
+        assert_raises(YTUfuncUnitError, np.subtract, a1, a3)
+    else:
+        operate_and_compare(a1, a2, np.subtract, answer1)
+        operate_and_compare(a2, a1, np.subtract, answer2)
+        operate_and_compare(a1, a3, np.subtract, answer1)
+        operate_and_compare(a3, a1, np.subtract, answer3)
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
@@ -327,10 +342,16 @@
     operate_and_compare(a2, a1, op, answer2)
     operate_and_compare(a1, a3, op, answer1)
     operate_and_compare(a3, a1, op, answer2)
-    operate_and_compare(a1, a2, np.divide, answer3)
-    operate_and_compare(a2, a1, np.divide, answer4)
-    operate_and_compare(a1, a3, np.divide, answer3)
-    operate_and_compare(a3, a1, np.divide, answer4)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        operate_and_compare(a1, a2, np.divide, answer3)
+        operate_and_compare(a2, a1, np.divide, answer4)
+        operate_and_compare(a1, a3, np.divide, answer3)
+        operate_and_compare(a3, a1, np.divide, answer4)
+    else:
+        operate_and_compare(a1, a2, np.divide, answer1)
+        operate_and_compare(a2, a1, np.divide, answer2)
+        operate_and_compare(a1, a3, np.divide, answer1)
+        operate_and_compare(a3, a1, np.divide, answer2)
 
     # different dimensions
     a1 = YTArray([1., 2., 3.], 'cm')
@@ -437,8 +458,11 @@
     for op, answer in zip(ops, answers):
         operate_and_compare(a1, dimless, op, answer)
 
-    for op in ops:
-        assert_raises(YTUfuncUnitError, op, a1, a3)
+    for op, answer in zip(ops, answers):
+        if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+            assert_raises(YTUfuncUnitError, op, a1, a3)
+        else:
+            operate_and_compare(a1, a3, op, answer)
 
     for op, answer in zip(ops, answers):
         operate_and_compare(a1, a3.in_units('cm'), op, answer)
@@ -597,10 +621,15 @@
     a_selection = a[0]
 
     assert_array_equal(a_slice, YTArray([0, 1, 2], 'cm'))
+    assert_equal(a_slice.units, a.units)
     assert_array_equal(a_fancy_index, YTArray([1, 1, 3, 5], 'cm'))
+    assert_equal(a_fancy_index.units, a.units)
     assert_array_equal(a_array_fancy_index, YTArray([[1, 1, ], [3, 5]], 'cm'))
+    assert_equal(a_array_fancy_index.units, a.units)
     assert_array_equal(a_boolean_index, YTArray([6, 7, 8, 9], 'cm'))
+    assert_equal(a_boolean_index.units, a.units)
     assert_isinstance(a_selection, YTQuantity)
+    assert_equal(a_selection.units, a.units)
 
     # .base points to the original array for a numpy view.  If it is not a
     # view, .base is None.
@@ -725,6 +754,7 @@
     elif ufunc in yield_np_ufuncs(
             ['absolute', 'fabs', 'conjugate', 'floor', 'ceil', 'trunc',
              'negative', 'spacing', 'positive']):
+
         ret = ufunc(a, out=out)
 
         assert_array_equal(ret, out)
@@ -779,8 +809,9 @@
             'not_equal', 'logical_and', 'logical_or', 'logical_xor', 'maximum',
             'minimum', 'fmax', 'fmin', 'nextafter', 'heaviside']):
         if a.units != b.units and a.units.dimensions == b.units.dimensions:
-            assert_raises(YTUfuncUnitError, ufunc, a, b)
-            return
+            if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+                assert_raises(YTUfuncUnitError, ufunc, a, b)
+                return
         elif a.units != b.units:
             assert_raises(YTUnitOperationError, ufunc, a, b)
             return
@@ -810,7 +841,7 @@
         (a.units.dimensions == b.units.dimensions)):
         assert_array_almost_equal(
             np.array(ret), ufunc(np.array(a.in_cgs()), np.array(b.in_cgs())))
-    else:
+    elif LooseVersion(np.__version__) < LooseVersion('1.13.0'):
         assert_array_almost_equal(np.array(ret), ufunc(np.array(a), np.array(b)))
 
 
@@ -846,6 +877,32 @@
         for pair in itertools.product([a, b, c, d, e], repeat=2):
             binary_ufunc_comparison(ufunc, pair[0], pair[1])
 
+def test_reductions():
+    arr = YTArray([[1, 2, 3], [4, 5, 6]], 'cm')
+    answers = {
+        'prod': (YTQuantity(720, 'cm**6'),
+                 YTArray([4, 10, 18], 'cm**2'),
+                 YTArray([6, 120], 'cm**3')),
+        'sum': (YTQuantity(21, 'cm'),
+                YTArray([ 5., 7., 9.], 'cm'),
+                YTArray([6, 15], 'cm'),),
+        'mean': (YTQuantity(3.5, 'cm'),
+                 YTArray([ 2.5, 3.5, 4.5], 'cm'),
+                 YTArray([2, 5], 'cm')),
+        'std': (YTQuantity(1.707825127659933, 'cm'),
+                YTArray([ 1.5, 1.5, 1.5], 'cm'),
+                YTArray([0.81649658, 0.81649658], 'cm')),
+    }
+    for op, (result1, result2, result3) in answers.items():
+        ev_result = getattr(arr, op)()
+        assert_almost_equal(ev_result, result1)
+        assert_almost_equal(ev_result.units, result1.units)
+        assert_isinstance(ev_result, YTQuantity)
+        for axis, result in [(0, result2), (1, result3), (-1, result3)]:
+            ev_result = getattr(arr, op)(axis=axis)
+            assert_almost_equal(ev_result, result)
+            assert_almost_equal(ev_result.units, result.units)
+            assert_isinstance(ev_result, YTArray)
 
 def test_convenience():
 
@@ -1251,3 +1308,13 @@
     assert_almost_equal(float(l2.in_cgs()), 0.9)
     assert_almost_equal(float(ds1.quan(0.3, 'unitary').in_cgs()), 0.3)
     assert_almost_equal(float(ds2.quan(0.3, 'unitary').in_cgs()), 0.9)
+
+def test_ones_and_zeros_like():
+    data = YTArray([1, 2, 3], 'cm')
+    zd = np.zeros_like(data)
+    od = np.ones_like(data)
+
+    assert_equal(zd, YTArray([0, 0, 0], 'cm'))
+    assert_equal(zd.units, data.units)
+    assert_equal(od, YTArray([1, 1, 1], 'cm'))
+    assert_equal(od.units, data.units)

diff -r 57a64cd5554cd804f12dfdc2fc9884cd2ecc0228 -r d96560624ad6a756b3cb6808ade6984057e13e1c yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -22,7 +22,7 @@
     add, subtract, multiply, divide, logaddexp, logaddexp2, true_divide, \
     floor_divide, negative, power, remainder, mod, absolute, rint, \
     sign, conj, exp, exp2, log, log2, log10, expm1, log1p, sqrt, square, \
-    reciprocal, ones_like, sin, cos, tan, arcsin, arccos, arctan, arctan2, \
+    reciprocal, sin, cos, tan, arcsin, arccos, arctan, arctan2, \
     hypot, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad, rad2deg, \
     bitwise_and, bitwise_or, bitwise_xor, invert, left_shift, right_shift, \
     greater, greater_equal, less, less_equal, not_equal, equal, logical_and, \
@@ -58,6 +58,7 @@
 from .pint_conversions import convert_pint_units
 
 NULL_UNIT = Unit()
+POWER_SIGN_MAPPING = {multiply: 1, divide: -1}
 
 # redefine this here to avoid a circular import from yt.funcs
 def iterable(obj):
@@ -123,6 +124,68 @@
     raise TypeError(
         "Bit-twiddling operators are not defined for YTArray instances")
 
+def get_inp_u_unary(ufunc, inputs, out_arr=None):
+    inp = inputs[0]
+    u = getattr(inp, 'units', None)
+    if u is None:
+        u = NULL_UNIT
+    if u.dimensions is angle and ufunc in trigonometric_operators:
+        inp = inp.in_units('radian').v
+        if out_arr is not None:
+            out_arr = ufunc(inp).view(np.ndarray)
+    return out_arr, inp, u
+
+def get_inp_u_binary(ufunc, inputs):
+    inp1 = coerce_iterable_units(inputs[0])
+    inp2 = coerce_iterable_units(inputs[1])
+    unit1 = getattr(inp1, 'units', None)
+    unit2 = getattr(inp2, 'units', None)
+    ret_class = get_binary_op_return_class(type(inp1), type(inp2))
+    if unit1 is None:
+        unit1 = Unit(registry=getattr(unit2, 'registry', None))
+    if unit2 is None and ufunc is not power:
+        unit2 = Unit(registry=getattr(unit1, 'registry', None))
+    elif ufunc is power:
+        unit2 = inp2
+        if isinstance(unit2, np.ndarray):
+            if isinstance(unit2, YTArray):
+                if unit2.units.is_dimensionless:
+                    pass
+                else:
+                    raise YTUnitOperationError(ufunc, unit1, unit2)
+            unit2 = 1.0
+    return (inp1, inp2), (unit1, unit2), ret_class
+
+def handle_preserve_units(inps, units, ufunc, ret_class, raise_error=False):
+    # Allow comparisons, addition, and subtraction with
+    # dimensionless quantities or arrays filled with zeros.
+    if units[0] != units[1]:
+        u1d = units[0].is_dimensionless
+        u2d = units[1].is_dimensionless
+        any_nonzero = [np.any(inps[0]), np.any(inps[1])]
+        if any_nonzero[0] == np.bool_(False):
+            units = (units[1], units[1])
+        elif any_nonzero[1] == np.bool_(False):
+            units = (units[0], units[0])
+        elif not any([u1d, u2d]):
+            if not units[0].same_dimensions_as(units[1]):
+                raise YTUnitOperationError(ufunc, *units)
+            else:
+                if raise_error:
+                    raise YTUfuncUnitError(ufunc, *units)
+                inps = (inps[0], ret_class(inps[1]).to(
+                    ret_class(inps[0]).units))
+    return inps, units
+
+def handle_multiply_divide_units(unit, units, out, out_arr):
+    if unit.is_dimensionless and unit.base_value != 1.0:
+        if not units[0].is_dimensionless:
+            if units[0].dimensions == units[1].dimensions:
+                out_arr = np.multiply(out_arr.view(np.ndarray),
+                                      unit.base_value, out=out)
+                unit = Unit(registry=unit.registry)
+    return out, out_arr, unit
+
 def coerce_iterable_units(input_object):
     if isinstance(input_object, np.ndarray):
         return input_object
@@ -208,11 +271,11 @@
     return other_units
 
 unary_operators = (
-    negative, absolute, rint, ones_like, sign, conj, exp, exp2, log, log2,
+    negative, absolute, rint, sign, conj, exp, exp2, log, log2,
     log10, expm1, log1p, sqrt, square, reciprocal, sin, cos, tan, arcsin,
     arccos, arctan, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad,
     rad2deg, invert, logical_not, isreal, iscomplex, isfinite, isinf, isnan,
-    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing, positive, isnat
+    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing, positive, isnat,
 )
 
 binary_operators = (
@@ -267,8 +330,8 @@
 
     >>> import numpy as np
     >>> a = YTArray(np.arange(8), 'g/cm**3')
-    >>> np.ones_like(a)
-    YTArray([1, 1, 1, 1, 1, 1, 1, 1]) g/cm**3
+    >>> np.abs(a)
+    YTArray([8, 8, 8, 8, 8, 8, 8, 8]) g/cm**3
 
     and strip them when it would be annoying to deal with them.
 
@@ -321,7 +384,6 @@
         sqrt: sqrt_unit,
         square: square_unit,
         reciprocal: reciprocal_unit,
-        ones_like: passthrough_unit,
         sin: return_without_unit,
         cos: return_without_unit,
         tan: return_without_unit,
@@ -404,7 +466,7 @@
             ret = input_array.view(cls)
             if input_units is None:
                 if registry is None:
-                    pass
+                    ret.units = input_array.units
                 else:
                     units = Unit(str(input_array.units), registry=registry)
                     ret.units = units
@@ -445,14 +507,6 @@
 
         return obj
 
-    def __array_finalize__(self, obj):
-        """
-
-        """
-        if obj is None and hasattr(self, 'units'):
-            return
-        self.units = getattr(obj, 'units', NULL_UNIT)
-
     def __repr__(self):
         """
 
@@ -785,7 +839,7 @@
         info: dictionary
             A dictionary of supplementary info to write to append as attributes
             to the dataset.
-            
+
         group_name: string
             An optional group to write the arrays to. If not specified, the arrays
             are datasets at the top level by default.
@@ -835,7 +889,8 @@
 
     @classmethod
     def from_hdf5(cls, filename, dataset_name=None, group_name=None):
-        r"""Attempts read in and convert a dataset in an hdf5 file into a YTArray.
+        r"""Attempts read in and convert a dataset in an hdf5 file into a 
+        YTArray.
 
         Parameters
         ----------
@@ -847,8 +902,8 @@
             attribute, attempt to infer units as well.
 
         group_name: string
-            An optional group to read the arrays from. If not specified, the arrays
-            are datasets at the top level by default.
+            An optional group to read the arrays from. If not specified, the 
+            arrays are datasets at the top level by default.
 
         """
         import h5py
@@ -893,464 +948,409 @@
 
     @property
     def unit_quantity(self):
-        """Get a YTQuantity with the same unit as this array and a value of 1.0"""
+        """Get a YTQuantity with the same unit as this array and a value of 
+        1.0"""
         return YTQuantity(1.0, self.units)
 
     uq = unit_quantity
 
     @property
     def unit_array(self):
-        """Get a YTArray filled with ones with the same unit and shape as this array"""
+        """Get a YTArray filled with ones with the same unit and shape as this 
+        array"""
         return np.ones_like(self)
 
     ua = unit_array
 
-    #
-    # Start operation methods
-    #
-
-    def __add__(self, right_object):
-        """
-        Add this ytarray to the object on the right of the `+` operator. Must
-        check for the correct (same dimension) units.
-
-        """
-        ro = sanitize_units_add(self, right_object, "addition")
-        return super(YTArray, self).__add__(ro)
-
-    def __radd__(self, left_object):
-        """ See __add__. """
-        lo = sanitize_units_add(self, left_object, "addition")
-        return super(YTArray, self).__radd__(lo)
-
-    def __iadd__(self, other):
-        """ See __add__. """
-        oth = sanitize_units_add(self, other, "addition")
-        np.add(self, oth, out=self)
-        return self
-
-    def __sub__(self, right_object):
-        """
-        Subtract the object on the right of the `-` from this ytarray. Must
-        check for the correct (same dimension) units.
-
-        """
-        ro = sanitize_units_add(self, right_object, "subtraction")
-        return super(YTArray, self).__sub__(ro)
-
-    def __rsub__(self, left_object):
-        """ See __sub__. """
-        lo = sanitize_units_add(self, left_object, "subtraction")
-        return super(YTArray, self).__rsub__(lo)
-
-    def __isub__(self, other):
-        """ See __sub__. """
-        oth = sanitize_units_add(self, other, "subtraction")
-        np.subtract(self, oth, out=self)
-        return self
-
-    def __neg__(self):
-        """ Negate the data. """
-        return super(YTArray, self).__neg__()
-
-    def __pos__(self):
-        """ Posify the data. """
-        return type(self)(super(YTArray, self).__pos__(), self.units)
-
-    def __mul__(self, right_object):
-        """
-        Multiply this YTArray by the object on the right of the `*` operator.
-        The unit objects handle being multiplied.
-
-        """
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__mul__(ro)
-
-    def __rmul__(self, left_object):
-        """ See __mul__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rmul__(lo)
-
-    def __imul__(self, other):
-        """ See __mul__. """
-        oth = sanitize_units_mul(self, other)
-        np.multiply(self, oth, out=self)
-        return self
-
-    def __div__(self, right_object):
-        """
-        Divide this YTArray by the object on the right of the `/` operator.
-
-        """
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__div__(ro)
-
-    def __rdiv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rdiv__(lo)
-
-    def __idiv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.divide(self, oth, out=self)
-        return self
-
-    def __truediv__(self, right_object):
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__truediv__(ro)
-
-    def __rtruediv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rtruediv__(lo)
-
-    def __itruediv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.true_divide(self, oth, out=self)
-        return self
-
-    def __floordiv__(self, right_object):
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__floordiv__(ro)
-
-    def __rfloordiv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rfloordiv__(lo)
-
-    def __ifloordiv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.floor_divide(self, oth, out=self)
-        return self
-
-    def __or__(self, right_object):
-        return super(YTArray, self).__or__(right_object)
-
-    def __ror__(self, left_object):
-        return super(YTArray, self).__ror__(left_object)
-
-    def __ior__(self, other):
-        np.bitwise_or(self, other, out=self)
-        return self
-
-    def __xor__(self, right_object):
-        return super(YTArray, self).__xor__(right_object)
-
-    def __rxor__(self, left_object):
-        return super(YTArray, self).__rxor__(left_object)
-
-    def __ixor__(self, other):
-        np.bitwise_xor(self, other, out=self)
-        return self
-
-    def __and__(self, right_object):
-        return super(YTArray, self).__and__(right_object)
-
-    def __rand__(self, left_object):
-        return super(YTArray, self).__rand__(left_object)
-
-    def __iand__(self, other):
-        np.bitwise_and(self, other, out=self)
-        return self
-
-    def __pow__(self, power):
-        """
-        Raise this YTArray to some power.
-
-        Parameters
-        ----------
-        power : float or dimensionless YTArray.
-            The pow value.
-
-        """
-        if isinstance(power, YTArray):
-            if not power.units.is_dimensionless:
-                raise YTUnitOperationError('power', power.unit)
-
-        # Work around a sympy issue (I think?)
-        #
-        # If I don't do this, super(YTArray, self).__pow__ returns a YTArray
-        # with a unit attribute set to the sympy expression 1/1 rather than a
-        # dimensionless Unit object.
-        if self.units.is_dimensionless and power == -1:
-            ret = super(YTArray, self).__pow__(power)
-            return type(self)(ret, input_units='')
-
-        return super(YTArray, self).__pow__(power)
-
-    def __abs__(self):
-        """ Return a YTArray with the abs of the data. """
-        return super(YTArray, self).__abs__()
-
-    def sqrt(self):
-        """
-        Return sqrt of this YTArray. We take the sqrt for the array and use
-        take the 1/2 power of the units.
-
-        """
-        return type(self)(super(YTArray, self).sqrt(),
-                          input_units=self.units**0.5)
-
-    #
-    # Start comparison operators.
-    #
-
-    def __lt__(self, other):
-        """ Test if this is less than the object on the right. """
-        # converts if possible
-        oth = validate_comparison_units(self, other, 'less_than')
-        return super(YTArray, self).__lt__(oth)
-
-    def __le__(self, other):
-        """ Test if this is less than or equal to the object on the right. """
-        oth = validate_comparison_units(self, other, 'less_than or equal')
-        return super(YTArray, self).__le__(oth)
-
-    def __eq__(self, other):
-        """ Test if this is equal to the object on the right. """
-        # Check that other is a YTArray.
-        if other is None:
-            # self is a YTArray, so it can't be None.
-            return False
-        oth = validate_comparison_units(self, other, 'equal')
-        return super(YTArray, self).__eq__(oth)
-
-    def __ne__(self, other):
-        """ Test if this is not equal to the object on the right. """
-        # Check that the other is a YTArray.
-        if other is None:
-            return True
-        oth = validate_comparison_units(self, other, 'not equal')
-        return super(YTArray, self).__ne__(oth)
-
-    def __ge__(self, other):
-        """ Test if this is greater than or equal to other. """
-        # Check that the other is a YTArray.
-        oth = validate_comparison_units(self, other, 'greater than or equal')
-        return super(YTArray, self).__ge__(oth)
-
-    def __gt__(self, other):
-        """ Test if this is greater than the object on the right. """
-        # Check that the other is a YTArray.
-        oth = validate_comparison_units(self, other, 'greater than')
-        return super(YTArray, self).__gt__(oth)
-
-    #
-    # End comparison operators
-    #
-
-    #
-    # Begin reduction operators
-    #
-
-    @return_arr
-    def prod(self, axis=None, dtype=None, out=None):
-        if axis is not None:
-            units = self.units**self.shape[axis]
-        else:
-            units = self.units**self.size
-        return super(YTArray, self).prod(axis, dtype, out), units
-
-    @return_arr
-    def mean(self, axis=None, dtype=None, out=None):
-        return super(YTArray, self).mean(axis, dtype, out), self.units
-
-    @return_arr
-    def sum(self, axis=None, dtype=None, out=None):
-        return super(YTArray, self).sum(axis, dtype, out), self.units
-
-    @return_arr
-    def dot(self, b, out=None):
-        return super(YTArray, self).dot(b), self.units*b.units
-
-    @return_arr
-    def std(self, axis=None, dtype=None, out=None, ddof=0):
-        return super(YTArray, self).std(axis, dtype, out, ddof), self.units
-
     def __getitem__(self, item):
         ret = super(YTArray, self).__getitem__(item)
         if ret.shape == ():
             return YTQuantity(ret, self.units, bypass_validation=True)
         else:
+            if hasattr(self, 'units'):
+                ret.units = self.units
             return ret
 
-    def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
-        if 'out' in kwargs:
-            out = kwargs['out'][0].view(np.ndarray)
-        else:
-            out = None
-        if method == '__call__':
-            func = ufunc
-        else:
-            func = getattr(ufunc, method)
-        if len(inputs) == 1:
-            u = getattr(inputs[0], 'units', None)
-            if u is None:
-                u = NULL_UNIT
-            if u.dimensions is angle and ufunc in trigonometric_operators:
-                inp = inputs[0].in_units('radian')
-            else:
-                inp = inputs[0]
-            if out is not None:
-                func(np.asarray(inp), out=out)
-                out_arr = out
-            else:
-                out_arr = func(np.asarray(inp))
-            unit = self._ufunc_registry[ufunc](u)
-            ret_class = type(self)
-        elif len(inputs) == 2:
-            if out is not None:
-                func(np.asarray(inputs[0]), np.asarray(inputs[1]), out=out)
-                out_arr = out
+    #
+    # Start operation methods
+    #
+
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+
+        def __add__(self, right_object):
+            """
+            Add this ytarray to the object on the right of the `+` operator. 
+            Must check for the correct (same dimension) units.
+
+            """
+            ro = sanitize_units_add(self, right_object, "addition")
+            return super(YTArray, self).__add__(ro)
+
+        def __radd__(self, left_object):
+            """ See __add__. """
+            lo = sanitize_units_add(self, left_object, "addition")
+            return super(YTArray, self).__radd__(lo)
+
+        def __iadd__(self, other):
+            """ See __add__. """
+            oth = sanitize_units_add(self, other, "addition")
+            np.add(self, oth, out=self)
+            return self
+
+        def __sub__(self, right_object):
+            """
+            Subtract the object on the right of the `-` from this ytarray. Must
+            check for the correct (same dimension) units.
+
+            """
+            ro = sanitize_units_add(self, right_object, "subtraction")
+            return super(YTArray, self).__sub__(ro)
+
+        def __rsub__(self, left_object):
+            """ See __sub__. """
+            lo = sanitize_units_add(self, left_object, "subtraction")
+            return super(YTArray, self).__rsub__(lo)
+
+        def __isub__(self, other):
+            """ See __sub__. """
+            oth = sanitize_units_add(self, other, "subtraction")
+            np.subtract(self, oth, out=self)
+            return self
+
+        def __neg__(self):
+            """ Negate the data. """
+            return super(YTArray, self).__neg__()
+
+        def __mul__(self, right_object):
+            """
+            Multiply this YTArray by the object on the right of the `*` 
+            operator. The unit objects handle being multiplied.
+
+            """
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__mul__(ro)
+
+        def __rmul__(self, left_object):
+            """ See __mul__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rmul__(lo)
+
+        def __imul__(self, other):
+            """ See __mul__. """
+            oth = sanitize_units_mul(self, other)
+            np.multiply(self, oth, out=self)
+            return self
+
+        def __div__(self, right_object):
+            """
+            Divide this YTArray by the object on the right of the `/` operator.
+
+            """
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__div__(ro)
+
+        def __rdiv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rdiv__(lo)
+
+        def __idiv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.divide(self, oth, out=self)
+            return self
+
+        def __truediv__(self, right_object):
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__truediv__(ro)
+
+        def __rtruediv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rtruediv__(lo)
+
+        def __itruediv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.true_divide(self, oth, out=self)
+            return self
+
+        def __floordiv__(self, right_object):
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__floordiv__(ro)
+
+        def __rfloordiv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rfloordiv__(lo)
+
+        def __ifloordiv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.floor_divide(self, oth, out=self)
+            return self
+
+        def __or__(self, right_object):
+            return super(YTArray, self).__or__(right_object)
+
+        def __ror__(self, left_object):
+            return super(YTArray, self).__ror__(left_object)
+
+        def __ior__(self, other):
+            np.bitwise_or(self, other, out=self)
+            return self
+
+        def __xor__(self, right_object):
+            return super(YTArray, self).__xor__(right_object)
+
+        def __rxor__(self, left_object):
+            return super(YTArray, self).__rxor__(left_object)
+
+        def __ixor__(self, other):
+            np.bitwise_xor(self, other, out=self)
+            return self
+
+        def __and__(self, right_object):
+            return super(YTArray, self).__and__(right_object)
+
+        def __rand__(self, left_object):
+            return super(YTArray, self).__rand__(left_object)
+
+        def __iand__(self, other):
+            np.bitwise_and(self, other, out=self)
+            return self
+
+        def __pow__(self, power):
+            """
+            Raise this YTArray to some power.
+
+            Parameters
+            ----------
+            power : float or dimensionless YTArray.
+                The pow value.
+
+            """
+            if isinstance(power, YTArray):
+                if not power.units.is_dimensionless:
+                    raise YTUnitOperationError('power', power.unit)
+
+            # Work around a sympy issue (I think?)
+            #
+            # If I don't do this, super(YTArray, self).__pow__ returns a YTArray
+            # with a unit attribute set to the sympy expression 1/1 rather than
+            # a dimensionless Unit object.
+            if self.units.is_dimensionless and power == -1:
+                ret = super(YTArray, self).__pow__(power)
+                return type(self)(ret, input_units='')
+
+            return super(YTArray, self).__pow__(power)
+
+        def __abs__(self):
+            """ Return a YTArray with the abs of the data. """
+            return super(YTArray, self).__abs__()
+
+        def sqrt(self):
+            """
+            Return sqrt of this YTArray. We take the sqrt for the array and use
+            take the 1/2 power of the units.
+
+            """
+            return type(self)(super(YTArray, self).sqrt(),
+                              input_units=self.units**0.5)
+
+        #
+        # Start comparison operators.
+        #
+
+        def __lt__(self, other):
+            """ Test if this is less than the object on the right. """
+            # converts if possible
+            oth = validate_comparison_units(self, other, 'less_than')
+            return super(YTArray, self).__lt__(oth)
+
+        def __le__(self, other):
+            """Test if this is less than or equal to the object on the right. 
+            """
+            oth = validate_comparison_units(self, other, 'less_than or equal')
+            return super(YTArray, self).__le__(oth)
+
+        def __eq__(self, other):
+            """ Test if this is equal to the object on the right. """
+            # Check that other is a YTArray.
+            if other is None:
+                # self is a YTArray, so it can't be None.
+                return False
+            oth = validate_comparison_units(self, other, 'equal')
+            return super(YTArray, self).__eq__(oth)
+
+        def __ne__(self, other):
+            """ Test if this is not equal to the object on the right. """
+            # Check that the other is a YTArray.
+            if other is None:
+                return True
+            oth = validate_comparison_units(self, other, 'not equal')
+            return super(YTArray, self).__ne__(oth)
+
+        def __ge__(self, other):
+            """ Test if this is greater than or equal to other. """
+            # Check that the other is a YTArray.
+            oth = validate_comparison_units(
+                self, other, 'greater than or equal')
+            return super(YTArray, self).__ge__(oth)
+
+        def __gt__(self, other):
+            """ Test if this is greater than the object on the right. """
+            # Check that the other is a YTArray.
+            oth = validate_comparison_units(self, other, 'greater than')
+            return super(YTArray, self).__gt__(oth)
+
+        #
+        # End comparison operators
+        #
+
+        #
+        # Begin reduction operators
+        #
+
+        @return_arr
+        def prod(self, axis=None, dtype=None, out=None):
+            if axis is not None:
+                units = self.units**self.shape[axis]
             else:
-                out_arr = func(np.asarray(inputs[0]), np.asarray(inputs[1]))
-            oper1 = coerce_iterable_units(inputs[0])
-            oper2 = coerce_iterable_units(inputs[1])
-            cls1 = type(oper1)
-            cls2 = type(oper2)
-            unit1 = getattr(oper1, 'units', None)
-            unit2 = getattr(oper2, 'units', None)
-            ret_class = get_binary_op_return_class(cls1, cls2)
-            if unit1 is None:
-                unit1 = Unit(registry=getattr(unit2, 'registry', None))
-            if unit2 is None and ufunc is not power:
-                unit2 = Unit(registry=getattr(unit1, 'registry', None))
-            elif ufunc is power:
-                unit2 = oper2
-                if isinstance(unit2, np.ndarray):
-                    if isinstance(unit2, YTArray):
-                        if unit2.units.is_dimensionless:
-                            pass
-                        else:
-                            raise YTUnitOperationError(ufunc, unit1, unit2)
-                    unit2 = 1.0
-            unit_operator = self._ufunc_registry[ufunc]
-            if unit_operator in (preserve_units, comparison_unit, arctan2_unit):
-                # Allow comparisons, addition, and subtraction with
-                # dimensionless quantities or arrays filled with zeros.
-                u1d = unit1.is_dimensionless
-                u2d = unit2.is_dimensionless
-                if unit1 != unit2:
-                    any_nonzero = [np.any(oper1), np.any(oper2)]
-                    if any_nonzero[0] == np.bool_(False):
-                        unit1 = unit2
-                    elif any_nonzero[1] == np.bool_(False):
-                        unit2 = unit1
-                    elif not any([u1d, u2d]):
-                        if not unit1.same_dimensions_as(unit2):
-                            raise YTUnitOperationError(ufunc, unit1, unit2)
-                        else:
-                            raise YTUfuncUnitError(ufunc, unit1, unit2)
-            unit = unit_operator(unit1, unit2)
-            if unit_operator in (multiply_units, divide_units):
-                if unit.is_dimensionless and unit.base_value != 1.0:
-                    if not unit1.is_dimensionless:
-                        if unit1.dimensions == unit2.dimensions:
-                            out_arr = np.multiply(out_arr.view(np.ndarray),
-                                                  unit.base_value, out=out)
-                            unit = Unit(registry=unit.registry)
-        else:
-            raise RuntimeError("Support for the %s ufunc has not been added "
-                               "to YTArray." % str(ufunc))
-        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 = YTQuantity(np.asarray(out_arr), unit)
-        else:
-            if ret_class is YTQuantity:
-                # This happens if you do ndarray * YTQuantity. Explicitly
-                # casting to YTArray avoids creating a YTQuantity with size > 1
-                out_arr = YTArray(np.asarray(out_arr), unit)
+                units = self.units**self.size
+            return super(YTArray, self).prod(axis, dtype, out), units
+
+        @return_arr
+        def mean(self, axis=None, dtype=None, out=None):
+            return super(YTArray, self).mean(axis, dtype, out), self.units
+
+        @return_arr
+        def sum(self, axis=None, dtype=None, out=None):
+            return super(YTArray, self).sum(axis, dtype, out), self.units
+
+        @return_arr
+        def dot(self, b, out=None):
+            return super(YTArray, self).dot(b), self.units*b.units
+
+        @return_arr
+        def std(self, axis=None, dtype=None, out=None, ddof=0):
+            return super(YTArray, self).std(axis, dtype, out, ddof), self.units
+
+        def __array_wrap__(self, out_arr, context=None):
+            ret = super(YTArray, self).__array_wrap__(out_arr, context)
+            if isinstance(ret, YTQuantity) and ret.shape != ():
+                ret = ret.view(YTArray)
+            if context is None:
+                if ret.shape == ():
+                    return ret[()]
+                else:
+                    return ret
+            ufunc = context[0]
+            inputs = context[1]
+            if ufunc in unary_operators:
+                out_arr, inp, u = get_inp_u_unary(ufunc, inputs, out_arr)
+                unit = self._ufunc_registry[context[0]](u)
+                ret_class = type(self)
+            elif ufunc in binary_operators:
+                unit_operator = self._ufunc_registry[context[0]]
+                inps, units, ret_class = get_inp_u_binary(ufunc, inputs)
+                if unit_operator in (preserve_units, comparison_unit,
+                                     arctan2_unit):
+                    inps, units = handle_preserve_units(
+                        inps, units, ufunc, ret_class, raise_error=True)
+                unit = unit_operator(*units)
+                if unit_operator in (multiply_units, divide_units):
+                    out_arr, out_arr, unit = handle_multiply_divide_units(
+                        unit, units, out_arr, out_arr)
+            else:
+                raise RuntimeError(
+                    "Support for the %s ufunc has not been added "
+                    "to YTArray." % str(context[0]))
+            if unit is None:
+                out_arr = np.array(out_arr, copy=False)
+                return out_arr
+            out_arr.units = unit
+            if out_arr.size == 1:
+                return YTQuantity(np.array(out_arr), unit)
             else:
-                out_arr = ret_class(np.asarray(out_arr), unit)
-        if 'out' in kwargs and hasattr(kwargs['out'][0], 'units'):
-            kwargs['out'][0].units = unit
-        return out_arr
-        
-    def __array_wrap__(self, out_arr, context=None):
-        ret = super(YTArray, self).__array_wrap__(out_arr, context)
-        if isinstance(ret, YTQuantity) and ret.shape != ():
-            ret = ret.view(YTArray)
-        if context is None:
-            if ret.shape == ():
-                return ret[()]
+                if ret_class is YTQuantity:
+                    # This happens if you do ndarray * YTQuantity. Explicitly
+                    # casting to YTArray avoids creating a YTQuantity with
+                    # size > 1
+                    return YTArray(np.array(out_arr), unit)
+                return ret_class(np.array(out_arr, copy=False), unit)
+
+    else:  # numpy version equal to or newer than 1.13
+
+        def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
+            func = getattr(ufunc, method)
+            if 'out' in kwargs:
+                out_orig = kwargs.pop('out')
+                out = np.asarray(out_orig[0])
             else:
-                return ret
-        elif context[0] in unary_operators:
-            u = getattr(context[1][0], 'units', None)
-            if u is None:
-                u = NULL_UNIT
-            if u.dimensions is angle and context[0] in trigonometric_operators:
-                out_arr = context[0](
-                    context[1][0].in_units('radian').view(np.ndarray))
-            unit = self._ufunc_registry[context[0]](u)
-            ret_class = type(self)
-        elif context[0] in binary_operators:
-            oper1 = coerce_iterable_units(context[1][0])
-            oper2 = coerce_iterable_units(context[1][1])
-            cls1 = type(oper1)
-            cls2 = type(oper2)
-            unit1 = getattr(oper1, 'units', None)
-            unit2 = getattr(oper2, 'units', None)
-            ret_class = get_binary_op_return_class(cls1, cls2)
-            if unit1 is None:
-                unit1 = Unit(registry=getattr(unit2, 'registry', None))
-            if unit2 is None and context[0] is not power:
-                unit2 = Unit(registry=getattr(unit1, 'registry', None))
-            elif context[0] is power:
-                unit2 = oper2
-                if isinstance(unit2, np.ndarray):
-                    if isinstance(unit2, YTArray):
-                        if unit2.units.is_dimensionless:
-                            pass
-                        else:
-                            raise YTUnitOperationError(context[0], unit1, unit2)
-                    unit2 = 1.0
-            unit_operator = self._ufunc_registry[context[0]]
-            if unit_operator in (preserve_units, comparison_unit, arctan2_unit):
-                # Allow comparisons, addition, and subtraction with
-                # dimensionless quantities or arrays filled with zeros.
-                u1d = unit1.is_dimensionless
-                u2d = unit2.is_dimensionless
-                if unit1 != unit2:
-                    any_nonzero = [np.any(oper1), np.any(oper2)]
-                    if any_nonzero[0] is np.bool_(False):
-                        unit1 = unit2
-                    elif any_nonzero[1] is np.bool_(False):
-                        unit2 = unit1
-                    elif not any([u1d, u2d]):
-                        if not unit1.same_dimensions_as(unit2):
-                            raise YTUnitOperationError(context[0], unit1, unit2)
-                        else:
-                            raise YTUfuncUnitError(context[0], unit1, unit2)
-            unit = unit_operator(unit1, unit2)
-            if unit_operator in (multiply_units, divide_units):
-                if unit.is_dimensionless and unit.base_value != 1.0:
-                    if not unit1.is_dimensionless:
-                        if unit1.dimensions == unit2.dimensions:
-                            np.multiply(out_arr.view(np.ndarray),
-                                        unit.base_value, out=out_arr)
-                            unit = Unit(registry=unit.registry)
-        else:
-            raise RuntimeError("Support for the %s ufunc has not been added "
-                               "to YTArray." % str(context[0]))
-        if unit is None:
-            out_arr = np.array(out_arr, copy=False)
+                out = None
+            if len(inputs) == 1:
+                _, inp, u = get_inp_u_unary(ufunc, inputs)
+                out_arr = func(np.asarray(inp), **kwargs, out=out)
+                if ufunc in (multiply, divide) and method == 'reduce':
+                    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:
+                    unit = self._ufunc_registry[ufunc](u)
+                ret_class = type(self)
+            elif len(inputs) == 2:
+                unit_operator = self._ufunc_registry[ufunc]
+                inps, units, ret_class = get_inp_u_binary(ufunc, inputs)
+                if unit_operator in (preserve_units, comparison_unit,
+                                     arctan2_unit):
+                    inps, units = handle_preserve_units(
+                        inps, units, ufunc, ret_class)
+                unit = unit_operator(*units)
+                out_arr = func(np.asarray(inps[0]), np.asarray(inps[1]),
+                               **kwargs, out=out)
+                if unit_operator in (multiply_units, divide_units):
+                    out, out_arr, unit = handle_multiply_divide_units(
+                        unit, units, out, out_arr)
+            else:
+                raise RuntimeError(
+                    "Support for the %s ufunc with %i inputs has not been"
+                    "added to YTArray." % (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 = YTQuantity(np.asarray(out_arr), unit)
+            else:
+                if ret_class is YTQuantity:
+                    # This happens if you do ndarray * YTQuantity. Explicitly
+                    # casting to YTArray avoids creating a YTQuantity with
+                    # size > 1
+                    out_arr = YTArray(np.asarray(out_arr), unit)
+                else:
+                    out_arr = ret_class(np.asarray(out_arr), unit)
+            if out is not None:
+                out_orig[0].flat[:] = out.flat[:]
+                if isinstance(out_orig[0], YTArray):
+                    out_orig[0].units = unit
             return out_arr
-        out_arr.units = unit
-        if out_arr.size == 1:
-            return YTQuantity(np.array(out_arr), unit)
-        else:
-            if ret_class is YTQuantity:
-                # This happens if you do ndarray * YTQuantity. Explicitly
-                # casting to YTArray avoids creating a YTQuantity with size > 1
-                return YTArray(np.array(out_arr), unit)
-            return ret_class(np.array(out_arr, copy=False), unit)
+
+        def copy(self, order='C'):
+            return type(self)(np.copy(np.asarray(self)), self.units)
+    
+    def __array_finalize__(self, obj):
+        if obj is None and hasattr(self, 'units'):
+            return
+        self.units = getattr(obj, 'units', NULL_UNIT)
+
+    def __pos__(self):
+        """ Posify the data. """
+        # this needs to be defined for all numpy versions, see
+        # numpy issue #9081
+        return type(self)(super(YTArray, self).__pos__(), self.units)
 
 
     def __reduce__(self):
@@ -1430,8 +1430,8 @@
 
     >>> import numpy as np
     >>> a = YTQuantity(12, 'g/cm**3')
-    >>> np.ones_like(a)
-    1 g/cm**3
+    >>> np.abs(a)
+    12 g/cm**3
 
     and strip them when it would be annoying to deal with them.
 
@@ -1611,9 +1611,9 @@
 def get_binary_op_return_class(cls1, cls2):
     if cls1 is cls2:
         return cls1
-    if cls1 is np.ndarray or issubclass(cls1, (numeric_type, np.number, list, tuple)):
+    if cls1 in (np.ndarray, np.matrix, np.ma.masked_array) or issubclass(cls1, (numeric_type, np.number, list, tuple)):
         return cls2
-    if cls2 is np.ndarray or issubclass(cls2, (numeric_type, np.number, list, tuple)):
+    if cls2 in (np.ndarray, np.matrix, np.ma.masked_array) or issubclass(cls2, (numeric_type, np.number, list, tuple)):
         return cls1
     if issubclass(cls1, YTQuantity):
         return cls2


https://bitbucket.org/yt_analysis/yt/commits/85c5a86c110a/
Changeset:   85c5a86c110a
User:        ngoldbaum
Date:        2017-05-24 04:30:58+00:00
Summary:     fix python2/3 issues
Affected #:  1 file

diff -r d96560624ad6a756b3cb6808ade6984057e13e1c -r 85c5a86c110affbafbbbbbce08d102ebe63b5b1c yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -1291,7 +1291,7 @@
                 out = None
             if len(inputs) == 1:
                 _, inp, u = get_inp_u_unary(ufunc, inputs)
-                out_arr = func(np.asarray(inp), **kwargs, out=out)
+                out_arr = func(np.asarray(inp), out=out, **kwargs)
                 if ufunc in (multiply, divide) and method == 'reduce':
                     power_sign = POWER_SIGN_MAPPING[ufunc]
                     if 'axis' in kwargs and kwargs['axis'] is not None:
@@ -1310,7 +1310,7 @@
                         inps, units, ufunc, ret_class)
                 unit = unit_operator(*units)
                 out_arr = func(np.asarray(inps[0]), np.asarray(inps[1]),
-                               **kwargs, out=out)
+                               out=out, **kwargs)
                 if unit_operator in (multiply_units, divide_units):
                     out, out_arr, unit = handle_multiply_divide_units(
                         unit, units, out, out_arr)


https://bitbucket.org/yt_analysis/yt/commits/46ec2de8df02/
Changeset:   46ec2de8df02
User:        ngoldbaum
Date:        2017-05-24 04:46:29+00:00
Summary:     Deine dot for all numpy versions. Add tests for it.
Affected #:  2 files

diff -r 85c5a86c110affbafbbbbbce08d102ebe63b5b1c -r 46ec2de8df024c24d80d21c95f229664e53db5c8 yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -879,6 +879,13 @@
 
 def test_reductions():
     arr = YTArray([[1, 2, 3], [4, 5, 6]], 'cm')
+
+    ev_result = arr.dot(YTArray([1, 2, 3], 'cm'))
+    res = YTArray([ 14.,  32.], 'cm**2')
+    assert_equal(ev_result, res)
+    assert_equal(ev_result.units, res.units)
+    assert_isinstance(ev_result, YTArray)
+
     answers = {
         'prod': (YTQuantity(720, 'cm**6'),
                  YTArray([4, 10, 18], 'cm**2'),

diff -r 85c5a86c110affbafbbbbbce08d102ebe63b5b1c -r 46ec2de8df024c24d80d21c95f229664e53db5c8 yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -1149,15 +1149,6 @@
             """ Return a YTArray with the abs of the data. """
             return super(YTArray, self).__abs__()
 
-        def sqrt(self):
-            """
-            Return sqrt of this YTArray. We take the sqrt for the array and use
-            take the 1/2 power of the units.
-
-            """
-            return type(self)(super(YTArray, self).sqrt(),
-                              input_units=self.units**0.5)
-
         #
         # Start comparison operators.
         #
@@ -1229,10 +1220,6 @@
             return super(YTArray, self).sum(axis, dtype, out), self.units
 
         @return_arr
-        def dot(self, b, out=None):
-            return super(YTArray, self).dot(b), self.units*b.units
-
-        @return_arr
         def std(self, axis=None, dtype=None, out=None, ddof=0):
             return super(YTArray, self).std(axis, dtype, out, ddof), self.units
 
@@ -1352,6 +1339,9 @@
         # numpy issue #9081
         return type(self)(super(YTArray, self).__pos__(), self.units)
 
+    @return_arr
+    def dot(self, b, out=None):
+        return super(YTArray, self).dot(b), self.units*b.units
 
     def __reduce__(self):
         """Pickle reduction method


https://bitbucket.org/yt_analysis/yt/commits/8b580b48e135/
Changeset:   8b580b48e135
User:        ngoldbaum
Date:        2017-06-09 18:18:48+00:00
Summary:     slight docstring tweak
Affected #:  1 file

diff -r 46ec2de8df024c24d80d21c95f229664e53db5c8 -r 8b580b48e135636a529bcf434930702acba6dc8d yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -329,9 +329,9 @@
     NumPy ufuncs will pass through units where appropriate.
 
     >>> import numpy as np
-    >>> a = YTArray(np.arange(8), 'g/cm**3')
+    >>> a = YTArray(np.arange(8) - 4, 'g/cm**3')
     >>> np.abs(a)
-    YTArray([8, 8, 8, 8, 8, 8, 8, 8]) g/cm**3
+    YTArray([4, 3, 2, 1, 0, 1, 2, 3]) g/cm**3
 
     and strip them when it would be annoying to deal with them.
 


https://bitbucket.org/yt_analysis/yt/commits/60a10fe93978/
Changeset:   60a10fe93978
User:        MatthewTurk
Date:        2017-06-20 15:30:00+00:00
Summary:     Merge pull request #1422 from ngoldbaum/numpy-1.13

Update yt for Numpy 1.13
Affected #:  3 files

diff -r edf23011658c556b1d6d4adc9c0177d08d08ace1 -r 60a10fe93978952d4959283312badb40cd4f5a72 yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -23,6 +23,7 @@
 import shutil
 import tempfile
 
+from distutils.version import LooseVersion
 from nose.tools import assert_true
 from numpy.testing import \
     assert_array_equal, \
@@ -88,9 +89,17 @@
     operate_and_compare(a1, a2, operator.add, answer1)
     operate_and_compare(a2, a1, operator.add, answer2)
     operate_and_compare(a1, a3, operator.add, answer1)
-    operate_and_compare(a3, a1, operator.add, answer1)
-    assert_raises(YTUfuncUnitError, np.add, a1, a2)
-    assert_raises(YTUfuncUnitError, np.add, a1, a3)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        operate_and_compare(a3, a1, operator.add, answer1)
+        assert_raises(YTUfuncUnitError, np.add, a1, a2)
+        assert_raises(YTUfuncUnitError, np.add, a1, a3)
+    else:
+        operate_and_compare(a3, a1, operator.add, answer2)
+        operate_and_compare(a1, a2, np.add, answer1)
+        operate_and_compare(a2, a1, np.add, answer2)
+        operate_and_compare(a1, a3, np.add, answer1)
+        operate_and_compare(a3, a1, np.add, answer2)
+        
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
@@ -163,8 +172,14 @@
     operate_and_compare(a2, a1, operator.sub, answer2)
     operate_and_compare(a1, a3, operator.sub, answer1)
     operate_and_compare(a3, a1, operator.sub, answer3)
-    assert_raises(YTUfuncUnitError, np.subtract, a1, a2)
-    assert_raises(YTUfuncUnitError, np.subtract, a1, a3)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        assert_raises(YTUfuncUnitError, np.subtract, a1, a2)
+        assert_raises(YTUfuncUnitError, np.subtract, a1, a3)
+    else:
+        operate_and_compare(a1, a2, np.subtract, answer1)
+        operate_and_compare(a2, a1, np.subtract, answer2)
+        operate_and_compare(a1, a3, np.subtract, answer1)
+        operate_and_compare(a3, a1, np.subtract, answer3)
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
@@ -327,10 +342,16 @@
     operate_and_compare(a2, a1, op, answer2)
     operate_and_compare(a1, a3, op, answer1)
     operate_and_compare(a3, a1, op, answer2)
-    operate_and_compare(a1, a2, np.divide, answer3)
-    operate_and_compare(a2, a1, np.divide, answer4)
-    operate_and_compare(a1, a3, np.divide, answer3)
-    operate_and_compare(a3, a1, np.divide, answer4)
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+        operate_and_compare(a1, a2, np.divide, answer3)
+        operate_and_compare(a2, a1, np.divide, answer4)
+        operate_and_compare(a1, a3, np.divide, answer3)
+        operate_and_compare(a3, a1, np.divide, answer4)
+    else:
+        operate_and_compare(a1, a2, np.divide, answer1)
+        operate_and_compare(a2, a1, np.divide, answer2)
+        operate_and_compare(a1, a3, np.divide, answer1)
+        operate_and_compare(a3, a1, np.divide, answer2)
 
     # different dimensions
     a1 = YTArray([1., 2., 3.], 'cm')
@@ -437,8 +458,11 @@
     for op, answer in zip(ops, answers):
         operate_and_compare(a1, dimless, op, answer)
 
-    for op in ops:
-        assert_raises(YTUfuncUnitError, op, a1, a3)
+    for op, answer in zip(ops, answers):
+        if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+            assert_raises(YTUfuncUnitError, op, a1, a3)
+        else:
+            operate_and_compare(a1, a3, op, answer)
 
     for op, answer in zip(ops, answers):
         operate_and_compare(a1, a3.in_units('cm'), op, answer)
@@ -597,10 +621,15 @@
     a_selection = a[0]
 
     assert_array_equal(a_slice, YTArray([0, 1, 2], 'cm'))
+    assert_equal(a_slice.units, a.units)
     assert_array_equal(a_fancy_index, YTArray([1, 1, 3, 5], 'cm'))
+    assert_equal(a_fancy_index.units, a.units)
     assert_array_equal(a_array_fancy_index, YTArray([[1, 1, ], [3, 5]], 'cm'))
+    assert_equal(a_array_fancy_index.units, a.units)
     assert_array_equal(a_boolean_index, YTArray([6, 7, 8, 9], 'cm'))
+    assert_equal(a_boolean_index.units, a.units)
     assert_isinstance(a_selection, YTQuantity)
+    assert_equal(a_selection.units, a.units)
 
     # .base points to the original array for a numpy view.  If it is not a
     # view, .base is None.
@@ -687,6 +716,13 @@
     assert_equal(np.copy(quan), quan)
     assert_array_equal(np.copy(arr), arr)
 
+# needed so the tests function on older numpy versions that have
+# different sets of ufuncs
+def yield_np_ufuncs(ufunc_list):
+    for u in ufunc_list:
+        ufunc = getattr(np, u, None)
+        if ufunc is not None:
+            yield ufunc
 
 def unary_ufunc_comparison(ufunc, a):
     out = a.copy()
@@ -697,12 +733,11 @@
         ret = ufunc(a)
         assert_true(not hasattr(ret, 'units'))
         assert_array_equal(ret, ufunc(a))
-    elif ufunc in (np.exp, np.exp2, np.log, np.log2, np.log10, np.expm1,
-                   np.log1p, np.sin, np.cos, np.tan, np.arcsin, np.arccos,
-                   np.arctan, np.sinh, np.cosh, np.tanh, np.arccosh,
-                   np.arcsinh, np.arctanh, np.deg2rad, np.rad2deg,
-                   np.isfinite, np.isinf, np.isnan, np.signbit, np.sign,
-                   np.rint, np.logical_not):
+    elif ufunc in yield_np_ufuncs([
+            'exp', 'exp2', 'log', 'log2', 'log10', 'expm1', 'log1p', 'sin',
+            'cos', 'tan', 'arcsin', 'arccos', 'arctan', 'sinh', 'cosh', 'tanh',
+            'arccosh', 'arcsinh', 'arctanh', 'deg2rad', 'rad2deg', 'isfinite',
+            'isinf', 'isnan', 'signbit', 'sign', 'rint', 'logical_not']):
         # These operations should return identical results compared to numpy.
         with np.errstate(invalid='ignore'):
             try:
@@ -716,14 +751,17 @@
             # In-place copies do not drop units.
             assert_true(hasattr(out, 'units'))
             assert_true(not hasattr(ret, 'units'))
-    elif ufunc in (np.absolute, np.fabs, np.conjugate, np.floor, np.ceil,
-                   np.trunc, np.negative, np.spacing):
+    elif ufunc in yield_np_ufuncs(
+            ['absolute', 'fabs', 'conjugate', 'floor', 'ceil', 'trunc',
+             'negative', 'spacing', 'positive']):
+
         ret = ufunc(a, out=out)
 
         assert_array_equal(ret, out)
         assert_array_equal(ret.to_ndarray(), ufunc(a_array))
         assert_true(ret.units == out.units)
-    elif ufunc in (np.ones_like, np.square, np.sqrt, np.reciprocal):
+    elif ufunc in yield_np_ufuncs(
+            ['ones_like', 'square', 'sqrt', 'reciprocal']):
         if ufunc is np.ones_like:
             ret = ufunc(a)
         else:
@@ -756,6 +794,8 @@
         assert_array_equal(ret2, npret2)
     elif ufunc is np.invert:
         assert_raises(TypeError, ufunc, a)
+    elif hasattr(np, 'isnat') and ufunc is np.isnat:
+        assert_raises(ValueError, ufunc, a)
     else:
         # There shouldn't be any untested ufuncs.
         assert_true(False)
@@ -763,23 +803,26 @@
 
 def binary_ufunc_comparison(ufunc, a, b):
     out = a.copy()
-    if ufunc in (np.add, np.subtract, np.remainder, np.fmod, np.mod,
-                 np.arctan2, np.hypot, np.greater, np.greater_equal, np.less,
-                 np.less_equal, np.equal, np.not_equal, np.logical_and,
-                 np.logical_or, np.logical_xor, np.maximum, np.minimum,
-                 np.fmax, np.fmin, np.nextafter):
+    if ufunc in yield_np_ufuncs([
+            'add', 'subtract', 'remainder', 'fmod', 'mod', 'arctan2', 'hypot',
+            'greater', 'greater_equal', 'less', 'less_equal', 'equal',
+            'not_equal', 'logical_and', 'logical_or', 'logical_xor', 'maximum',
+            'minimum', 'fmax', 'fmin', 'nextafter', 'heaviside']):
         if a.units != b.units and a.units.dimensions == b.units.dimensions:
-            assert_raises(YTUfuncUnitError, ufunc, a, b)
-            return
+            if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+                assert_raises(YTUfuncUnitError, ufunc, a, b)
+                return
         elif a.units != b.units:
             assert_raises(YTUnitOperationError, ufunc, a, b)
             return
-    if ufunc in (np.bitwise_and, np.bitwise_or, np.bitwise_xor,
-                 np.left_shift, np.right_shift, np.ldexp):
+    if ufunc in yield_np_ufuncs(
+            ['bitwise_and', 'bitwise_or', 'bitwise_xor', 'left_shift',
+             'right_shift', 'ldexp']):
         assert_raises(TypeError, ufunc, a, b)
         return
 
     ret = ufunc(a, b, out=out)
+    ret = ufunc(a, b)
 
     if ufunc is np.multiply:
         assert_true(ret.units == a.units*b.units)
@@ -790,23 +833,29 @@
                    np.logical_xor):
         assert_true(not isinstance(ret, YTArray) and
                     isinstance(ret, np.ndarray))
-    assert_array_equal(ret, out)
+    if isinstance(ret, tuple):
+        assert_array_equal(ret[0], out)
+    else:
+        assert_array_equal(ret, out)
     if (ufunc in (np.divide, np.true_divide, np.arctan2) and
         (a.units.dimensions == b.units.dimensions)):
         assert_array_almost_equal(
             np.array(ret), ufunc(np.array(a.in_cgs()), np.array(b.in_cgs())))
-    else:
+    elif LooseVersion(np.__version__) < LooseVersion('1.13.0'):
         assert_array_almost_equal(np.array(ret), ufunc(np.array(a), np.array(b)))
 
 
 def test_ufuncs():
     for ufunc in unary_operators:
+        if ufunc is None:
+            continue
         unary_ufunc_comparison(ufunc, YTArray([.3, .4, .5], 'cm'))
         unary_ufunc_comparison(ufunc, YTArray([12, 23, 47], 'g'))
         unary_ufunc_comparison(ufunc, YTArray([2, 4, -6], 'erg/m**3'))
 
     for ufunc in binary_operators:
-
+        if ufunc is None:
+            continue
         # arr**arr is undefined for arrays with units because
         # each element of the result would have different units.
         if ufunc is np.power:
@@ -828,6 +877,39 @@
         for pair in itertools.product([a, b, c, d, e], repeat=2):
             binary_ufunc_comparison(ufunc, pair[0], pair[1])
 
+def test_reductions():
+    arr = YTArray([[1, 2, 3], [4, 5, 6]], 'cm')
+
+    ev_result = arr.dot(YTArray([1, 2, 3], 'cm'))
+    res = YTArray([ 14.,  32.], 'cm**2')
+    assert_equal(ev_result, res)
+    assert_equal(ev_result.units, res.units)
+    assert_isinstance(ev_result, YTArray)
+
+    answers = {
+        'prod': (YTQuantity(720, 'cm**6'),
+                 YTArray([4, 10, 18], 'cm**2'),
+                 YTArray([6, 120], 'cm**3')),
+        'sum': (YTQuantity(21, 'cm'),
+                YTArray([ 5., 7., 9.], 'cm'),
+                YTArray([6, 15], 'cm'),),
+        'mean': (YTQuantity(3.5, 'cm'),
+                 YTArray([ 2.5, 3.5, 4.5], 'cm'),
+                 YTArray([2, 5], 'cm')),
+        'std': (YTQuantity(1.707825127659933, 'cm'),
+                YTArray([ 1.5, 1.5, 1.5], 'cm'),
+                YTArray([0.81649658, 0.81649658], 'cm')),
+    }
+    for op, (result1, result2, result3) in answers.items():
+        ev_result = getattr(arr, op)()
+        assert_almost_equal(ev_result, result1)
+        assert_almost_equal(ev_result.units, result1.units)
+        assert_isinstance(ev_result, YTQuantity)
+        for axis, result in [(0, result2), (1, result3), (-1, result3)]:
+            ev_result = getattr(arr, op)(axis=axis)
+            assert_almost_equal(ev_result, result)
+            assert_almost_equal(ev_result.units, result.units)
+            assert_isinstance(ev_result, YTArray)
 
 def test_convenience():
 
@@ -1233,3 +1315,13 @@
     assert_almost_equal(float(l2.in_cgs()), 0.9)
     assert_almost_equal(float(ds1.quan(0.3, 'unitary').in_cgs()), 0.3)
     assert_almost_equal(float(ds2.quan(0.3, 'unitary').in_cgs()), 0.9)
+
+def test_ones_and_zeros_like():
+    data = YTArray([1, 2, 3], 'cm')
+    zd = np.zeros_like(data)
+    od = np.ones_like(data)
+
+    assert_equal(zd, YTArray([0, 0, 0], 'cm'))
+    assert_equal(zd.units, data.units)
+    assert_equal(od, YTArray([1, 1, 1], 'cm'))
+    assert_equal(od.units, data.units)

diff -r edf23011658c556b1d6d4adc9c0177d08d08ace1 -r 60a10fe93978952d4959283312badb40cd4f5a72 yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -22,7 +22,7 @@
     add, subtract, multiply, divide, logaddexp, logaddexp2, true_divide, \
     floor_divide, negative, power, remainder, mod, absolute, rint, \
     sign, conj, exp, exp2, log, log2, log10, expm1, log1p, sqrt, square, \
-    reciprocal, ones_like, sin, cos, tan, arcsin, arccos, arctan, arctan2, \
+    reciprocal, sin, cos, tan, arcsin, arccos, arctan, arctan2, \
     hypot, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad, rad2deg, \
     bitwise_and, bitwise_or, bitwise_xor, invert, left_shift, right_shift, \
     greater, greater_equal, less, less_equal, not_equal, equal, logical_and, \
@@ -30,6 +30,12 @@
     isreal, iscomplex, isfinite, isinf, isnan, signbit, copysign, nextafter, \
     modf, ldexp, frexp, fmod, floor, ceil, trunc, fabs, spacing
 
+try:
+    # numpy 1.13 or newer
+    from numpy import positive, divmod as divmod_, isnat, heaviside
+except ImportError:
+    positive, divmod_, isnat, heaviside = (None,)*4
+
 from yt.units.unit_object import Unit, UnitParseError
 from yt.units.unit_registry import UnitRegistry
 from yt.units.dimensions import \
@@ -52,6 +58,7 @@
 from .pint_conversions import convert_pint_units
 
 NULL_UNIT = Unit()
+POWER_SIGN_MAPPING = {multiply: 1, divide: -1}
 
 # redefine this here to avoid a circular import from yt.funcs
 def iterable(obj):
@@ -78,7 +85,7 @@
 def multiply_units(unit1, unit2):
     return unit1 * unit2
 
-def preserve_units(unit1, unit2):
+def preserve_units(unit1, unit2=None):
     return unit1
 
 @lru_cache(maxsize=128, typed=False)
@@ -97,16 +104,16 @@
 def reciprocal_unit(unit):
     return unit**-1
 
-def passthrough_unit(unit):
+def passthrough_unit(unit, unit2=None):
     return unit
 
-def return_without_unit(unit):
+def return_without_unit(unit, unit2=None):
     return None
 
 def arctan2_unit(unit1, unit2):
     return NULL_UNIT
 
-def comparison_unit(unit1, unit2):
+def comparison_unit(unit1, unit2=None):
     return None
 
 def invert_units(unit):
@@ -117,6 +124,68 @@
     raise TypeError(
         "Bit-twiddling operators are not defined for YTArray instances")
 
+def get_inp_u_unary(ufunc, inputs, out_arr=None):
+    inp = inputs[0]
+    u = getattr(inp, 'units', None)
+    if u is None:
+        u = NULL_UNIT
+    if u.dimensions is angle and ufunc in trigonometric_operators:
+        inp = inp.in_units('radian').v
+        if out_arr is not None:
+            out_arr = ufunc(inp).view(np.ndarray)
+    return out_arr, inp, u
+
+def get_inp_u_binary(ufunc, inputs):
+    inp1 = coerce_iterable_units(inputs[0])
+    inp2 = coerce_iterable_units(inputs[1])
+    unit1 = getattr(inp1, 'units', None)
+    unit2 = getattr(inp2, 'units', None)
+    ret_class = get_binary_op_return_class(type(inp1), type(inp2))
+    if unit1 is None:
+        unit1 = Unit(registry=getattr(unit2, 'registry', None))
+    if unit2 is None and ufunc is not power:
+        unit2 = Unit(registry=getattr(unit1, 'registry', None))
+    elif ufunc is power:
+        unit2 = inp2
+        if isinstance(unit2, np.ndarray):
+            if isinstance(unit2, YTArray):
+                if unit2.units.is_dimensionless:
+                    pass
+                else:
+                    raise YTUnitOperationError(ufunc, unit1, unit2)
+            unit2 = 1.0
+    return (inp1, inp2), (unit1, unit2), ret_class
+
+def handle_preserve_units(inps, units, ufunc, ret_class, raise_error=False):
+    # Allow comparisons, addition, and subtraction with
+    # dimensionless quantities or arrays filled with zeros.
+    if units[0] != units[1]:
+        u1d = units[0].is_dimensionless
+        u2d = units[1].is_dimensionless
+        any_nonzero = [np.any(inps[0]), np.any(inps[1])]
+        if any_nonzero[0] == np.bool_(False):
+            units = (units[1], units[1])
+        elif any_nonzero[1] == np.bool_(False):
+            units = (units[0], units[0])
+        elif not any([u1d, u2d]):
+            if not units[0].same_dimensions_as(units[1]):
+                raise YTUnitOperationError(ufunc, *units)
+            else:
+                if raise_error:
+                    raise YTUfuncUnitError(ufunc, *units)
+                inps = (inps[0], ret_class(inps[1]).to(
+                    ret_class(inps[0]).units))
+    return inps, units
+
+def handle_multiply_divide_units(unit, units, out, out_arr):
+    if unit.is_dimensionless and unit.base_value != 1.0:
+        if not units[0].is_dimensionless:
+            if units[0].dimensions == units[1].dimensions:
+                out_arr = np.multiply(out_arr.view(np.ndarray),
+                                      unit.base_value, out=out)
+                unit = Unit(registry=unit.registry)
+    return out, out_arr, unit
+
 def coerce_iterable_units(input_object):
     if isinstance(input_object, np.ndarray):
         return input_object
@@ -202,11 +271,11 @@
     return other_units
 
 unary_operators = (
-    negative, absolute, rint, ones_like, sign, conj, exp, exp2, log, log2,
+    negative, absolute, rint, sign, conj, exp, exp2, log, log2,
     log10, expm1, log1p, sqrt, square, reciprocal, sin, cos, tan, arcsin,
     arccos, arctan, sinh, cosh, tanh, arcsinh, arccosh, arctanh, deg2rad,
     rad2deg, invert, logical_not, isreal, iscomplex, isfinite, isinf, isnan,
-    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing
+    signbit, floor, ceil, trunc, modf, frexp, fabs, spacing, positive, isnat,
 )
 
 binary_operators = (
@@ -214,7 +283,7 @@
     remainder, mod, arctan2, hypot, bitwise_and, bitwise_or, bitwise_xor,
     left_shift, right_shift, greater, greater_equal, less, less_equal,
     not_equal, equal, logical_and, logical_or, logical_xor, maximum, minimum,
-    fmax, fmin, copysign, nextafter, ldexp, fmod,
+    fmax, fmin, copysign, nextafter, ldexp, fmod, divmod_, heaviside
 )
 
 trigonometric_operators = (
@@ -260,9 +329,9 @@
     NumPy ufuncs will pass through units where appropriate.
 
     >>> import numpy as np
-    >>> a = YTArray(np.arange(8), 'g/cm**3')
-    >>> np.ones_like(a)
-    YTArray([1, 1, 1, 1, 1, 1, 1, 1]) g/cm**3
+    >>> a = YTArray(np.arange(8) - 4, 'g/cm**3')
+    >>> np.abs(a)
+    YTArray([4, 3, 2, 1, 0, 1, 2, 3]) g/cm**3
 
     and strip them when it would be annoying to deal with them.
 
@@ -315,7 +384,6 @@
         sqrt: sqrt_unit,
         square: square_unit,
         reciprocal: reciprocal_unit,
-        ones_like: passthrough_unit,
         sin: return_without_unit,
         cos: return_without_unit,
         tan: return_without_unit,
@@ -367,6 +435,10 @@
         ceil: passthrough_unit,
         trunc: passthrough_unit,
         spacing: passthrough_unit,
+        positive: passthrough_unit,
+        divmod_: passthrough_unit,
+        isnat: return_without_unit,
+        heaviside: preserve_units,
     }
 
     __array_priority__ = 2.0
@@ -394,7 +466,7 @@
             ret = input_array.view(cls)
             if input_units is None:
                 if registry is None:
-                    pass
+                    ret.units = input_array.units
                 else:
                     units = Unit(str(input_array.units), registry=registry)
                     ret.units = units
@@ -435,14 +507,6 @@
 
         return obj
 
-    def __array_finalize__(self, obj):
-        """
-
-        """
-        if obj is None and hasattr(self, 'units'):
-            return
-        self.units = getattr(obj, 'units', NULL_UNIT)
-
     def __repr__(self):
         """
 
@@ -775,7 +839,7 @@
         info: dictionary
             A dictionary of supplementary info to write to append as attributes
             to the dataset.
-            
+
         group_name: string
             An optional group to write the arrays to. If not specified, the arrays
             are datasets at the top level by default.
@@ -825,7 +889,8 @@
 
     @classmethod
     def from_hdf5(cls, filename, dataset_name=None, group_name=None):
-        r"""Attempts read in and convert a dataset in an hdf5 file into a YTArray.
+        r"""Attempts read in and convert a dataset in an hdf5 file into a 
+        YTArray.
 
         Parameters
         ----------
@@ -837,8 +902,8 @@
             attribute, attempt to infer units as well.
 
         group_name: string
-            An optional group to read the arrays from. If not specified, the arrays
-            are datasets at the top level by default.
+            An optional group to read the arrays from. If not specified, the 
+            arrays are datasets at the top level by default.
 
         """
         import h5py
@@ -883,370 +948,400 @@
 
     @property
     def unit_quantity(self):
-        """Get a YTQuantity with the same unit as this array and a value of 1.0"""
+        """Get a YTQuantity with the same unit as this array and a value of 
+        1.0"""
         return YTQuantity(1.0, self.units)
 
     uq = unit_quantity
 
     @property
     def unit_array(self):
-        """Get a YTArray filled with ones with the same unit and shape as this array"""
+        """Get a YTArray filled with ones with the same unit and shape as this 
+        array"""
         return np.ones_like(self)
 
     ua = unit_array
 
-    #
-    # Start operation methods
-    #
-
-    def __add__(self, right_object):
-        """
-        Add this ytarray to the object on the right of the `+` operator. Must
-        check for the correct (same dimension) units.
-
-        """
-        ro = sanitize_units_add(self, right_object, "addition")
-        return super(YTArray, self).__add__(ro)
-
-    def __radd__(self, left_object):
-        """ See __add__. """
-        lo = sanitize_units_add(self, left_object, "addition")
-        return super(YTArray, self).__radd__(lo)
-
-    def __iadd__(self, other):
-        """ See __add__. """
-        oth = sanitize_units_add(self, other, "addition")
-        np.add(self, oth, out=self)
-        return self
-
-    def __sub__(self, right_object):
-        """
-        Subtract the object on the right of the `-` from this ytarray. Must
-        check for the correct (same dimension) units.
-
-        """
-        ro = sanitize_units_add(self, right_object, "subtraction")
-        return super(YTArray, self).__sub__(ro)
-
-    def __rsub__(self, left_object):
-        """ See __sub__. """
-        lo = sanitize_units_add(self, left_object, "subtraction")
-        return super(YTArray, self).__rsub__(lo)
-
-    def __isub__(self, other):
-        """ See __sub__. """
-        oth = sanitize_units_add(self, other, "subtraction")
-        np.subtract(self, oth, out=self)
-        return self
-
-    def __neg__(self):
-        """ Negate the data. """
-        return super(YTArray, self).__neg__()
-
-    def __pos__(self):
-        """ Posify the data. """
-        return type(self)(super(YTArray, self).__pos__(), self.units)
-
-    def __mul__(self, right_object):
-        """
-        Multiply this YTArray by the object on the right of the `*` operator.
-        The unit objects handle being multiplied.
-
-        """
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__mul__(ro)
-
-    def __rmul__(self, left_object):
-        """ See __mul__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rmul__(lo)
-
-    def __imul__(self, other):
-        """ See __mul__. """
-        oth = sanitize_units_mul(self, other)
-        np.multiply(self, oth, out=self)
-        return self
-
-    def __div__(self, right_object):
-        """
-        Divide this YTArray by the object on the right of the `/` operator.
-
-        """
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__div__(ro)
-
-    def __rdiv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rdiv__(lo)
-
-    def __idiv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.divide(self, oth, out=self)
-        return self
-
-    def __truediv__(self, right_object):
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__truediv__(ro)
-
-    def __rtruediv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rtruediv__(lo)
-
-    def __itruediv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.true_divide(self, oth, out=self)
-        return self
-
-    def __floordiv__(self, right_object):
-        ro = sanitize_units_mul(self, right_object)
-        return super(YTArray, self).__floordiv__(ro)
-
-    def __rfloordiv__(self, left_object):
-        """ See __div__. """
-        lo = sanitize_units_mul(self, left_object)
-        return super(YTArray, self).__rfloordiv__(lo)
-
-    def __ifloordiv__(self, other):
-        """ See __div__. """
-        oth = sanitize_units_mul(self, other)
-        np.floor_divide(self, oth, out=self)
-        return self
-
-    def __or__(self, right_object):
-        return super(YTArray, self).__or__(right_object)
-
-    def __ror__(self, left_object):
-        return super(YTArray, self).__ror__(left_object)
-
-    def __ior__(self, other):
-        np.bitwise_or(self, other, out=self)
-        return self
-
-    def __xor__(self, right_object):
-        return super(YTArray, self).__xor__(right_object)
-
-    def __rxor__(self, left_object):
-        return super(YTArray, self).__rxor__(left_object)
-
-    def __ixor__(self, other):
-        np.bitwise_xor(self, other, out=self)
-        return self
-
-    def __and__(self, right_object):
-        return super(YTArray, self).__and__(right_object)
-
-    def __rand__(self, left_object):
-        return super(YTArray, self).__rand__(left_object)
-
-    def __iand__(self, other):
-        np.bitwise_and(self, other, out=self)
-        return self
-
-    def __pow__(self, power):
-        """
-        Raise this YTArray to some power.
-
-        Parameters
-        ----------
-        power : float or dimensionless YTArray.
-            The pow value.
-
-        """
-        if isinstance(power, YTArray):
-            if not power.units.is_dimensionless:
-                raise YTUnitOperationError('power', power.unit)
-
-        # Work around a sympy issue (I think?)
-        #
-        # If I don't do this, super(YTArray, self).__pow__ returns a YTArray
-        # with a unit attribute set to the sympy expression 1/1 rather than a
-        # dimensionless Unit object.
-        if self.units.is_dimensionless and power == -1:
-            ret = super(YTArray, self).__pow__(power)
-            return type(self)(ret, input_units='')
-
-        return super(YTArray, self).__pow__(power)
-
-    def __abs__(self):
-        """ Return a YTArray with the abs of the data. """
-        return super(YTArray, self).__abs__()
-
-    def sqrt(self):
-        """
-        Return sqrt of this YTArray. We take the sqrt for the array and use
-        take the 1/2 power of the units.
-
-        """
-        return type(self)(super(YTArray, self).sqrt(),
-                          input_units=self.units**0.5)
-
-    #
-    # Start comparison operators.
-    #
-
-    def __lt__(self, other):
-        """ Test if this is less than the object on the right. """
-        # converts if possible
-        oth = validate_comparison_units(self, other, 'less_than')
-        return super(YTArray, self).__lt__(oth)
-
-    def __le__(self, other):
-        """ Test if this is less than or equal to the object on the right. """
-        oth = validate_comparison_units(self, other, 'less_than or equal')
-        return super(YTArray, self).__le__(oth)
-
-    def __eq__(self, other):
-        """ Test if this is equal to the object on the right. """
-        # Check that other is a YTArray.
-        if other is None:
-            # self is a YTArray, so it can't be None.
-            return False
-        oth = validate_comparison_units(self, other, 'equal')
-        return super(YTArray, self).__eq__(oth)
-
-    def __ne__(self, other):
-        """ Test if this is not equal to the object on the right. """
-        # Check that the other is a YTArray.
-        if other is None:
-            return True
-        oth = validate_comparison_units(self, other, 'not equal')
-        return super(YTArray, self).__ne__(oth)
-
-    def __ge__(self, other):
-        """ Test if this is greater than or equal to other. """
-        # Check that the other is a YTArray.
-        oth = validate_comparison_units(self, other, 'greater than or equal')
-        return super(YTArray, self).__ge__(oth)
-
-    def __gt__(self, other):
-        """ Test if this is greater than the object on the right. """
-        # Check that the other is a YTArray.
-        oth = validate_comparison_units(self, other, 'greater than')
-        return super(YTArray, self).__gt__(oth)
-
-    #
-    # End comparison operators
-    #
-
-    #
-    # Begin reduction operators
-    #
-
-    @return_arr
-    def prod(self, axis=None, dtype=None, out=None):
-        if axis is not None:
-            units = self.units**self.shape[axis]
-        else:
-            units = self.units**self.size
-        return super(YTArray, self).prod(axis, dtype, out), units
-
-    @return_arr
-    def mean(self, axis=None, dtype=None, out=None):
-        return super(YTArray, self).mean(axis, dtype, out), self.units
-
-    @return_arr
-    def sum(self, axis=None, dtype=None, out=None):
-        return super(YTArray, self).sum(axis, dtype, out), self.units
-
-    @return_arr
-    def dot(self, b, out=None):
-        return super(YTArray, self).dot(b), self.units*b.units
-
-    @return_arr
-    def std(self, axis=None, dtype=None, out=None, ddof=0):
-        return super(YTArray, self).std(axis, dtype, out, ddof), self.units
-
     def __getitem__(self, item):
         ret = super(YTArray, self).__getitem__(item)
         if ret.shape == ():
             return YTQuantity(ret, self.units, bypass_validation=True)
         else:
+            if hasattr(self, 'units'):
+                ret.units = self.units
             return ret
 
-    def __array_wrap__(self, out_arr, context=None):
-        ret = super(YTArray, self).__array_wrap__(out_arr, context)
-        if isinstance(ret, YTQuantity) and ret.shape != ():
-            ret = ret.view(YTArray)
-        if context is None:
-            if ret.shape == ():
-                return ret[()]
+    #
+    # Start operation methods
+    #
+
+    if LooseVersion(np.__version__) < LooseVersion('1.13.0'):
+
+        def __add__(self, right_object):
+            """
+            Add this ytarray to the object on the right of the `+` operator. 
+            Must check for the correct (same dimension) units.
+
+            """
+            ro = sanitize_units_add(self, right_object, "addition")
+            return super(YTArray, self).__add__(ro)
+
+        def __radd__(self, left_object):
+            """ See __add__. """
+            lo = sanitize_units_add(self, left_object, "addition")
+            return super(YTArray, self).__radd__(lo)
+
+        def __iadd__(self, other):
+            """ See __add__. """
+            oth = sanitize_units_add(self, other, "addition")
+            np.add(self, oth, out=self)
+            return self
+
+        def __sub__(self, right_object):
+            """
+            Subtract the object on the right of the `-` from this ytarray. Must
+            check for the correct (same dimension) units.
+
+            """
+            ro = sanitize_units_add(self, right_object, "subtraction")
+            return super(YTArray, self).__sub__(ro)
+
+        def __rsub__(self, left_object):
+            """ See __sub__. """
+            lo = sanitize_units_add(self, left_object, "subtraction")
+            return super(YTArray, self).__rsub__(lo)
+
+        def __isub__(self, other):
+            """ See __sub__. """
+            oth = sanitize_units_add(self, other, "subtraction")
+            np.subtract(self, oth, out=self)
+            return self
+
+        def __neg__(self):
+            """ Negate the data. """
+            return super(YTArray, self).__neg__()
+
+        def __mul__(self, right_object):
+            """
+            Multiply this YTArray by the object on the right of the `*` 
+            operator. The unit objects handle being multiplied.
+
+            """
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__mul__(ro)
+
+        def __rmul__(self, left_object):
+            """ See __mul__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rmul__(lo)
+
+        def __imul__(self, other):
+            """ See __mul__. """
+            oth = sanitize_units_mul(self, other)
+            np.multiply(self, oth, out=self)
+            return self
+
+        def __div__(self, right_object):
+            """
+            Divide this YTArray by the object on the right of the `/` operator.
+
+            """
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__div__(ro)
+
+        def __rdiv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rdiv__(lo)
+
+        def __idiv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.divide(self, oth, out=self)
+            return self
+
+        def __truediv__(self, right_object):
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__truediv__(ro)
+
+        def __rtruediv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rtruediv__(lo)
+
+        def __itruediv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.true_divide(self, oth, out=self)
+            return self
+
+        def __floordiv__(self, right_object):
+            ro = sanitize_units_mul(self, right_object)
+            return super(YTArray, self).__floordiv__(ro)
+
+        def __rfloordiv__(self, left_object):
+            """ See __div__. """
+            lo = sanitize_units_mul(self, left_object)
+            return super(YTArray, self).__rfloordiv__(lo)
+
+        def __ifloordiv__(self, other):
+            """ See __div__. """
+            oth = sanitize_units_mul(self, other)
+            np.floor_divide(self, oth, out=self)
+            return self
+
+        def __or__(self, right_object):
+            return super(YTArray, self).__or__(right_object)
+
+        def __ror__(self, left_object):
+            return super(YTArray, self).__ror__(left_object)
+
+        def __ior__(self, other):
+            np.bitwise_or(self, other, out=self)
+            return self
+
+        def __xor__(self, right_object):
+            return super(YTArray, self).__xor__(right_object)
+
+        def __rxor__(self, left_object):
+            return super(YTArray, self).__rxor__(left_object)
+
+        def __ixor__(self, other):
+            np.bitwise_xor(self, other, out=self)
+            return self
+
+        def __and__(self, right_object):
+            return super(YTArray, self).__and__(right_object)
+
+        def __rand__(self, left_object):
+            return super(YTArray, self).__rand__(left_object)
+
+        def __iand__(self, other):
+            np.bitwise_and(self, other, out=self)
+            return self
+
+        def __pow__(self, power):
+            """
+            Raise this YTArray to some power.
+
+            Parameters
+            ----------
+            power : float or dimensionless YTArray.
+                The pow value.
+
+            """
+            if isinstance(power, YTArray):
+                if not power.units.is_dimensionless:
+                    raise YTUnitOperationError('power', power.unit)
+
+            # Work around a sympy issue (I think?)
+            #
+            # If I don't do this, super(YTArray, self).__pow__ returns a YTArray
+            # with a unit attribute set to the sympy expression 1/1 rather than
+            # a dimensionless Unit object.
+            if self.units.is_dimensionless and power == -1:
+                ret = super(YTArray, self).__pow__(power)
+                return type(self)(ret, input_units='')
+
+            return super(YTArray, self).__pow__(power)
+
+        def __abs__(self):
+            """ Return a YTArray with the abs of the data. """
+            return super(YTArray, self).__abs__()
+
+        #
+        # Start comparison operators.
+        #
+
+        def __lt__(self, other):
+            """ Test if this is less than the object on the right. """
+            # converts if possible
+            oth = validate_comparison_units(self, other, 'less_than')
+            return super(YTArray, self).__lt__(oth)
+
+        def __le__(self, other):
+            """Test if this is less than or equal to the object on the right. 
+            """
+            oth = validate_comparison_units(self, other, 'less_than or equal')
+            return super(YTArray, self).__le__(oth)
+
+        def __eq__(self, other):
+            """ Test if this is equal to the object on the right. """
+            # Check that other is a YTArray.
+            if other is None:
+                # self is a YTArray, so it can't be None.
+                return False
+            oth = validate_comparison_units(self, other, 'equal')
+            return super(YTArray, self).__eq__(oth)
+
+        def __ne__(self, other):
+            """ Test if this is not equal to the object on the right. """
+            # Check that the other is a YTArray.
+            if other is None:
+                return True
+            oth = validate_comparison_units(self, other, 'not equal')
+            return super(YTArray, self).__ne__(oth)
+
+        def __ge__(self, other):
+            """ Test if this is greater than or equal to other. """
+            # Check that the other is a YTArray.
+            oth = validate_comparison_units(
+                self, other, 'greater than or equal')
+            return super(YTArray, self).__ge__(oth)
+
+        def __gt__(self, other):
+            """ Test if this is greater than the object on the right. """
+            # Check that the other is a YTArray.
+            oth = validate_comparison_units(self, other, 'greater than')
+            return super(YTArray, self).__gt__(oth)
+
+        #
+        # End comparison operators
+        #
+
+        #
+        # Begin reduction operators
+        #
+
+        @return_arr
+        def prod(self, axis=None, dtype=None, out=None):
+            if axis is not None:
+                units = self.units**self.shape[axis]
             else:
-                return ret
-        elif context[0] in unary_operators:
-            u = getattr(context[1][0], 'units', None)
-            if u is None:
-                u = NULL_UNIT
-            if u.dimensions is angle and context[0] in trigonometric_operators:
-                out_arr = context[0](
-                    context[1][0].in_units('radian').view(np.ndarray))
-            unit = self._ufunc_registry[context[0]](u)
-            ret_class = type(self)
-        elif context[0] in binary_operators:
-            oper1 = coerce_iterable_units(context[1][0])
-            oper2 = coerce_iterable_units(context[1][1])
-            cls1 = type(oper1)
-            cls2 = type(oper2)
-            unit1 = getattr(oper1, 'units', None)
-            unit2 = getattr(oper2, 'units', None)
-            ret_class = get_binary_op_return_class(cls1, cls2)
-            if unit1 is None:
-                unit1 = Unit(registry=getattr(unit2, 'registry', None))
-            if unit2 is None and context[0] is not power:
-                unit2 = Unit(registry=getattr(unit1, 'registry', None))
-            elif context[0] is power:
-                unit2 = oper2
-                if isinstance(unit2, np.ndarray):
-                    if isinstance(unit2, YTArray):
-                        if unit2.units.is_dimensionless:
-                            pass
-                        else:
-                            raise YTUnitOperationError(context[0], unit1, unit2)
-                    unit2 = 1.0
-            unit_operator = self._ufunc_registry[context[0]]
-            if unit_operator in (preserve_units, comparison_unit, arctan2_unit):
-                # Allow comparisons, addition, and subtraction with
-                # dimensionless quantities or arrays filled with zeros.
-                u1d = unit1.is_dimensionless
-                u2d = unit2.is_dimensionless
-                if unit1 != unit2:
-                    any_nonzero = [np.any(oper1), np.any(oper2)]
-                    if any_nonzero[0] is np.bool_(False):
-                        unit1 = unit2
-                    elif any_nonzero[1] is np.bool_(False):
-                        unit2 = unit1
-                    elif not any([u1d, u2d]):
-                        if not unit1.same_dimensions_as(unit2):
-                            raise YTUnitOperationError(context[0], unit1, unit2)
-                        else:
-                            raise YTUfuncUnitError(context[0], unit1, unit2)
-            unit = unit_operator(unit1, unit2)
-            if unit_operator in (multiply_units, divide_units):
-                if unit.is_dimensionless and unit.base_value != 1.0:
-                    if not unit1.is_dimensionless:
-                        if unit1.dimensions == unit2.dimensions:
-                            np.multiply(out_arr.view(np.ndarray),
-                                        unit.base_value, out=out_arr)
-                            unit = Unit(registry=unit.registry)
-        else:
-            raise RuntimeError("Support for the %s ufunc has not been added "
-                               "to YTArray." % str(context[0]))
-        if unit is None:
-            out_arr = np.array(out_arr, copy=False)
+                units = self.units**self.size
+            return super(YTArray, self).prod(axis, dtype, out), units
+
+        @return_arr
+        def mean(self, axis=None, dtype=None, out=None):
+            return super(YTArray, self).mean(axis, dtype, out), self.units
+
+        @return_arr
+        def sum(self, axis=None, dtype=None, out=None):
+            return super(YTArray, self).sum(axis, dtype, out), self.units
+
+        @return_arr
+        def std(self, axis=None, dtype=None, out=None, ddof=0):
+            return super(YTArray, self).std(axis, dtype, out, ddof), self.units
+
+        def __array_wrap__(self, out_arr, context=None):
+            ret = super(YTArray, self).__array_wrap__(out_arr, context)
+            if isinstance(ret, YTQuantity) and ret.shape != ():
+                ret = ret.view(YTArray)
+            if context is None:
+                if ret.shape == ():
+                    return ret[()]
+                else:
+                    return ret
+            ufunc = context[0]
+            inputs = context[1]
+            if ufunc in unary_operators:
+                out_arr, inp, u = get_inp_u_unary(ufunc, inputs, out_arr)
+                unit = self._ufunc_registry[context[0]](u)
+                ret_class = type(self)
+            elif ufunc in binary_operators:
+                unit_operator = self._ufunc_registry[context[0]]
+                inps, units, ret_class = get_inp_u_binary(ufunc, inputs)
+                if unit_operator in (preserve_units, comparison_unit,
+                                     arctan2_unit):
+                    inps, units = handle_preserve_units(
+                        inps, units, ufunc, ret_class, raise_error=True)
+                unit = unit_operator(*units)
+                if unit_operator in (multiply_units, divide_units):
+                    out_arr, out_arr, unit = handle_multiply_divide_units(
+                        unit, units, out_arr, out_arr)
+            else:
+                raise RuntimeError(
+                    "Support for the %s ufunc has not been added "
+                    "to YTArray." % str(context[0]))
+            if unit is None:
+                out_arr = np.array(out_arr, copy=False)
+                return out_arr
+            out_arr.units = unit
+            if out_arr.size == 1:
+                return YTQuantity(np.array(out_arr), unit)
+            else:
+                if ret_class is YTQuantity:
+                    # This happens if you do ndarray * YTQuantity. Explicitly
+                    # casting to YTArray avoids creating a YTQuantity with
+                    # size > 1
+                    return YTArray(np.array(out_arr), unit)
+                return ret_class(np.array(out_arr, copy=False), unit)
+
+    else:  # numpy version equal to or newer than 1.13
+
+        def __array_ufunc__(self, ufunc, method, *inputs, **kwargs):
+            func = getattr(ufunc, method)
+            if 'out' in kwargs:
+                out_orig = kwargs.pop('out')
+                out = np.asarray(out_orig[0])
+            else:
+                out = None
+            if len(inputs) == 1:
+                _, inp, u = get_inp_u_unary(ufunc, inputs)
+                out_arr = func(np.asarray(inp), out=out, **kwargs)
+                if ufunc in (multiply, divide) and method == 'reduce':
+                    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:
+                    unit = self._ufunc_registry[ufunc](u)
+                ret_class = type(self)
+            elif len(inputs) == 2:
+                unit_operator = self._ufunc_registry[ufunc]
+                inps, units, ret_class = get_inp_u_binary(ufunc, inputs)
+                if unit_operator in (preserve_units, comparison_unit,
+                                     arctan2_unit):
+                    inps, units = handle_preserve_units(
+                        inps, units, ufunc, ret_class)
+                unit = unit_operator(*units)
+                out_arr = func(np.asarray(inps[0]), np.asarray(inps[1]),
+                               out=out, **kwargs)
+                if unit_operator in (multiply_units, divide_units):
+                    out, out_arr, unit = handle_multiply_divide_units(
+                        unit, units, out, out_arr)
+            else:
+                raise RuntimeError(
+                    "Support for the %s ufunc with %i inputs has not been"
+                    "added to YTArray." % (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 = YTQuantity(np.asarray(out_arr), unit)
+            else:
+                if ret_class is YTQuantity:
+                    # This happens if you do ndarray * YTQuantity. Explicitly
+                    # casting to YTArray avoids creating a YTQuantity with
+                    # size > 1
+                    out_arr = YTArray(np.asarray(out_arr), unit)
+                else:
+                    out_arr = ret_class(np.asarray(out_arr), unit)
+            if out is not None:
+                out_orig[0].flat[:] = out.flat[:]
+                if isinstance(out_orig[0], YTArray):
+                    out_orig[0].units = unit
             return out_arr
-        out_arr.units = unit
-        if out_arr.size == 1:
-            return YTQuantity(np.array(out_arr), unit)
-        else:
-            if ret_class is YTQuantity:
-                # This happens if you do ndarray * YTQuantity. Explicitly
-                # casting to YTArray avoids creating a YTQuantity with size > 1
-                return YTArray(np.array(out_arr), unit)
-            return ret_class(np.array(out_arr, copy=False), unit)
+
+        def copy(self, order='C'):
+            return type(self)(np.copy(np.asarray(self)), self.units)
+    
+    def __array_finalize__(self, obj):
+        if obj is None and hasattr(self, 'units'):
+            return
+        self.units = getattr(obj, 'units', NULL_UNIT)
 
+    def __pos__(self):
+        """ Posify the data. """
+        # this needs to be defined for all numpy versions, see
+        # numpy issue #9081
+        return type(self)(super(YTArray, self).__pos__(), self.units)
+
+    @return_arr
+    def dot(self, b, out=None):
+        return super(YTArray, self).dot(b), self.units*b.units
 
     def __reduce__(self):
         """Pickle reduction method
@@ -1325,8 +1420,8 @@
 
     >>> import numpy as np
     >>> a = YTQuantity(12, 'g/cm**3')
-    >>> np.ones_like(a)
-    1 g/cm**3
+    >>> np.abs(a)
+    12 g/cm**3
 
     and strip them when it would be annoying to deal with them.
 
@@ -1506,9 +1601,9 @@
 def get_binary_op_return_class(cls1, cls2):
     if cls1 is cls2:
         return cls1
-    if cls1 is np.ndarray or issubclass(cls1, (numeric_type, np.number, list, tuple)):
+    if cls1 in (np.ndarray, np.matrix, np.ma.masked_array) or issubclass(cls1, (numeric_type, np.number, list, tuple)):
         return cls2
-    if cls2 is np.ndarray or issubclass(cls2, (numeric_type, np.number, list, tuple)):
+    if cls2 in (np.ndarray, np.matrix, np.ma.masked_array) or issubclass(cls2, (numeric_type, np.number, list, tuple)):
         return cls1
     if issubclass(cls1, YTQuantity):
         return cls2

diff -r edf23011658c556b1d6d4adc9c0177d08d08ace1 -r 60a10fe93978952d4959283312badb40cd4f5a72 yt/utilities/tests/test_minimal_representation.py
--- a/yt/utilities/tests/test_minimal_representation.py
+++ b/yt/utilities/tests/test_minimal_representation.py
@@ -1,3 +1,4 @@
+import numpy as np
 import os.path
 import yt
 from yt.config import ytcfg
@@ -36,7 +37,7 @@
 
     def fail_for_different_method():
         proj2_c = ds.proj(field, "z", data_source=sp, method="mip")
-        return (proj2[field] == proj2_c[field]).all()
+        return np.equal(proj2[field], proj2_c[field]).all()
     assert_raises(YTUnitOperationError, fail_for_different_method)
 
     def fail_for_different_source():

Repository URL: https://bitbucket.org/yt_analysis/yt/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.



More information about the yt-svn mailing list