<html><body>
<p>2 new commits in yt:</p>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/100c520a9d33/">https://bitbucket.org/yt_analysis/yt/commits/100c520a9d33/</a> Changeset:   100c520a9d33 Branch:      yt User:        ngoldbaum Date:        2016-05-10 21:04:50+00:00 Summary:     Allow adding or subtracting arrays filled with zeros without checking units</p>
<p>This is a behavior change that allows the python builtin sum() function to work correctly. It also matches the behavior of astropy's units.</p>
<p>Overall this should have minimal performance implications for scripts that did not raise errors before this pull request, since most code changes are in paths that would have raised errors before.</p>
<p>There is one place where we need to see if there are any nonzero elements in either array to ensure that units are propogated properly. Hopefully for most real data that isn't filled with zeros this will be a relatively fast, but it is a bit of overhead. Any suggestions on how to avoid doing it would be very much appreciated.</p>
<p>I've also added tests for this new behavior, and added a test for the `sum` builtin when applied to a YTArray. Affected #:  2 files</p>
<p>diff -r 78591f39a47d294c9ff8d5be7121fbe51e5a2acd -r 100c520a9d33f7529d2421fb52ec48244898e339 yt/units/tests/test_ytarray.py --- a/yt/units/tests/test_ytarray.py +++ b/yt/units/tests/test_ytarray.py @@ -112,6 +112,20 @@</p>
<pre>    yield assert_raises, YTUnitOperationError, operator.add, a1, a2
    yield assert_raises, YTUnitOperationError, operator.iadd, a1, a2
</pre>
<p>+    # adding with zero is allowed irrespective of the units +    zeros = np.zeros(3) +    zeros_yta_dimless = YTArray(zeros, ‘dimensionless’) +    zeros_yta_length = YTArray(zeros, ‘m’) +    zeros_yta_mass = YTArray(zeros, ‘kg’) +    operands = [0, YTQuantity(0), YTQuantity(0, 'kg'), zeros, zeros_yta_dimless, +                zeros_yta_length, zeros_yta_mass] + +    for op in [operator.add, np.add]: +        for operand in operands: +            yield operate_and_compare, a1, operand, op, a1 +            yield operate_and_compare, operand, a1, op, a1 +            yield operate_and_compare, 4*m, operand, op, 4*m +            yield operate_and_compare, operand, 4*m, op, 4*m</p>
<pre>def test_subtraction():
    """</pre>
<p>@@ -173,6 +187,20 @@</p>
<pre>    yield assert_raises, YTUnitOperationError, operator.sub, a1, a2
    yield assert_raises, YTUnitOperationError, operator.isub, a1, a2
</pre>
<p>+    # subtracting with zero is allowed irrespective of the units +    zeros = np.zeros(3) +    zeros_yta_dimless = YTArray(zeros, ‘dimensionless’) +    zeros_yta_length = YTArray(zeros, ‘m’) +    zeros_yta_mass = YTArray(zeros, ‘kg’) +    operands = [0, YTQuantity(0), YTQuantity(0, 'kg'), zeros, zeros_yta_dimless, +                zeros_yta_length, zeros_yta_mass] + +    for op in [operator.sub, np.subtract]: +        for operand in operands: +            yield operate_and_compare, a1, operand, op, a1 +            yield operate_and_compare, operand, a1, op, -a1 +            yield operate_and_compare, 4*m, operand, op, 4*m +            yield operate_and_compare, operand, 4*m, op, -4*m</p>
<pre>def test_multiplication():
    """</pre>
<p>@@ -1163,3 +1191,10 @@</p>
<pre>degree_values = np.random.random(10)*degree
radian_values = degree_values.in_units('radian')
assert_array_equal(ufunc(degree_values), ufunc(radian_values))</pre>
<p>+ +def test_builtin_sum(): +    from yt.units import km + +    arr = [1, 2, 3]*km +    assert_equal(sum(arr), 6*km) +</p>
<p>diff -r 78591f39a47d294c9ff8d5be7121fbe51e5a2acd -r 100c520a9d33f7529d2421fb52ec48244898e339 yt/units/yt_array.py --- a/yt/units/yt_array.py +++ b/yt/units/yt_array.py @@ -148,12 +148,18 @@</p>
<pre># attribute.
if isinstance(ret, YTArray):
    if not inp.units.same_dimensions_as(ret.units):</pre>
<p>+            # handle special case of adding or subtracting with zero or +            # array filled with zero +            if not np.any(other_object): +                return ret.view(np.ndarray) +            elif not np.any(this_object): +                return ret</p>
<pre>raise YTUnitOperationError(op_string, inp.units, ret.units)
         ret = ret.in_units(inp.units)</pre>
<ul><li><p># If the other object is not a YTArray, the only valid case is adding</p></li>
<li><p># dimensionless things. else:</p></li>
<li><p>if not inp.units.is_dimensionless:</p></li></ul>
<p>+        # If the other object is not a YTArray, then one of the arrays must be +        # dimensionless or filled with zeros +        if not inp.units.is_dimensionless and np.any(ret):</p>
<pre>            raise YTUnitOperationError(op_string, inp.units, dimensionless)
    return ret
</pre>
<p>@@ -1202,13 +1208,21 @@</p>
<pre>        unit2 = 1.0
unit_operator = self._ufunc_registry[context[0]]
if unit_operator in (preserve_units, comparison_unit, arctan2_unit):</pre>
<ul><li><p># Allow comparisons with dimensionless quantities.</p></li>
<li><p>if (unit1 != unit2 and</p></li>
<li><p>not unit2.is_dimensionless and not unit1.is_dimensionless):</p></li>
<li><p>if not unit1.same_dimensions_as(unit2):</p></li>
<li><p>raise YTUnitOperationError(context[0], unit1, unit2)</p></li>
<li><p>else:</p></li>
<li><p>raise YTUfuncUnitError(context[0], unit1, unit2)</p></li></ul>
<p>+                # 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)</p>
<pre>unit = unit_operator(unit1, unit2)
if unit_operator in (multiply_units, divide_units):
    if unit.is_dimensionless and unit.base_value != 1.0:</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/bdf16ce177f5/">https://bitbucket.org/yt_analysis/yt/commits/bdf16ce177f5/</a> Changeset:   bdf16ce177f5 Branch:      yt User:        jzuhone Date:        2016-05-12 13:54:37+00:00 Summary:     Merged in ngoldbaum/yt (pull request #2171)</p>
<p>Allow adding or subtracting arrays filled with zeros without checking units Affected #:  2 files</p>
<p>diff -r 6068831a37f4d92db1f3788c867e56efb286e587 -r bdf16ce177f5fd1d88b07c227b0e5759fba0ecea yt/units/tests/test_ytarray.py --- a/yt/units/tests/test_ytarray.py +++ b/yt/units/tests/test_ytarray.py @@ -112,6 +112,20 @@</p>
<pre>    yield assert_raises, YTUnitOperationError, operator.add, a1, a2
    yield assert_raises, YTUnitOperationError, operator.iadd, a1, a2
</pre>
<p>+    # adding with zero is allowed irrespective of the units +    zeros = np.zeros(3) +    zeros_yta_dimless = YTArray(zeros, ‘dimensionless’) +    zeros_yta_length = YTArray(zeros, ‘m’) +    zeros_yta_mass = YTArray(zeros, ‘kg’) +    operands = [0, YTQuantity(0), YTQuantity(0, 'kg'), zeros, zeros_yta_dimless, +                zeros_yta_length, zeros_yta_mass] + +    for op in [operator.add, np.add]: +        for operand in operands: +            yield operate_and_compare, a1, operand, op, a1 +            yield operate_and_compare, operand, a1, op, a1 +            yield operate_and_compare, 4*m, operand, op, 4*m +            yield operate_and_compare, operand, 4*m, op, 4*m</p>
<pre>def test_subtraction():
    """</pre>
<p>@@ -173,6 +187,20 @@</p>
<pre>    yield assert_raises, YTUnitOperationError, operator.sub, a1, a2
    yield assert_raises, YTUnitOperationError, operator.isub, a1, a2
</pre>
<p>+    # subtracting with zero is allowed irrespective of the units +    zeros = np.zeros(3) +    zeros_yta_dimless = YTArray(zeros, ‘dimensionless’) +    zeros_yta_length = YTArray(zeros, ‘m’) +    zeros_yta_mass = YTArray(zeros, ‘kg’) +    operands = [0, YTQuantity(0), YTQuantity(0, 'kg'), zeros, zeros_yta_dimless, +                zeros_yta_length, zeros_yta_mass] + +    for op in [operator.sub, np.subtract]: +        for operand in operands: +            yield operate_and_compare, a1, operand, op, a1 +            yield operate_and_compare, operand, a1, op, -a1 +            yield operate_and_compare, 4*m, operand, op, 4*m +            yield operate_and_compare, operand, 4*m, op, -4*m</p>
<pre>def test_multiplication():
    """</pre>
<p>@@ -1163,3 +1191,10 @@</p>
<pre>degree_values = np.random.random(10)*degree
radian_values = degree_values.in_units('radian')
assert_array_equal(ufunc(degree_values), ufunc(radian_values))</pre>
<p>+ +def test_builtin_sum(): +    from yt.units import km + +    arr = [1, 2, 3]*km +    assert_equal(sum(arr), 6*km) +</p>
<p>diff -r 6068831a37f4d92db1f3788c867e56efb286e587 -r bdf16ce177f5fd1d88b07c227b0e5759fba0ecea yt/units/yt_array.py --- a/yt/units/yt_array.py +++ b/yt/units/yt_array.py @@ -148,12 +148,18 @@</p>
<pre># attribute.
if isinstance(ret, YTArray):
    if not inp.units.same_dimensions_as(ret.units):</pre>
<p>+            # handle special case of adding or subtracting with zero or +            # array filled with zero +            if not np.any(other_object): +                return ret.view(np.ndarray) +            elif not np.any(this_object): +                return ret</p>
<pre>raise YTUnitOperationError(op_string, inp.units, ret.units)
         ret = ret.in_units(inp.units)</pre>
<ul><li><p># If the other object is not a YTArray, the only valid case is adding</p></li>
<li><p># dimensionless things. else:</p></li>
<li><p>if not inp.units.is_dimensionless:</p></li></ul>
<p>+        # If the other object is not a YTArray, then one of the arrays must be +        # dimensionless or filled with zeros +        if not inp.units.is_dimensionless and np.any(ret):</p>
<pre>            raise YTUnitOperationError(op_string, inp.units, dimensionless)
    return ret
</pre>
<p>@@ -1202,13 +1208,21 @@</p>
<pre>        unit2 = 1.0
unit_operator = self._ufunc_registry[context[0]]
if unit_operator in (preserve_units, comparison_unit, arctan2_unit):</pre>
<ul><li><p># Allow comparisons with dimensionless quantities.</p></li>
<li><p>if (unit1 != unit2 and</p></li>
<li><p>not unit2.is_dimensionless and not unit1.is_dimensionless):</p></li>
<li><p>if not unit1.same_dimensions_as(unit2):</p></li>
<li><p>raise YTUnitOperationError(context[0], unit1, unit2)</p></li>
<li><p>else:</p></li>
<li><p>raise YTUfuncUnitError(context[0], unit1, unit2)</p></li></ul>
<p>+                # 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)</p>
<pre>unit = unit_operator(unit1, unit2)
if unit_operator in (multiply_units, divide_units):
    if unit.is_dimensionless and unit.base_value != 1.0:</pre>
<p>Repository URL: <a href="https://bitbucket.org/yt_analysis/yt/">https://bitbucket.org/yt_analysis/yt/</a></p>
<p>—</p>
<p>This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.</p>

<img src="http://link.bitbucket.org/wf/open?upn=ll4ctv0L-2ByeRZFC1LslHcg6aJmnQ70VruLbmeLQr27AwBiX-2FWfau7ZmLw1CuhTZIGehamTagMXVKEmxGjgLqiUGJKAMdGsjoMxKcaE1YcGorYKHzRNB-2Bk30h50lc7id6z2A-2Fa-2Fi4OhDcTA0brI5o6D2nsc3u3-2Fg66MFDX7R99p1yt4mKuxl-2FPxfr6H6X8raZbcZpWbroIXSF-2BlIO08is9ZCZlGxJ4ZmOloMGKkLMgpw-3D" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;"/>
</body></html>