[yt-svn] commit/yt: jzuhone: Merged in ngoldbaum/yt/yt-3.0 (pull request #926)

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Thu May 29 09:47:21 PDT 2014


1 new commit in yt:

https://bitbucket.org/yt_analysis/yt/commits/f1fa8e20ab51/
Changeset:   f1fa8e20ab51
Branch:      yt-3.0
User:        jzuhone
Date:        2014-05-29 18:47:13
Summary:     Merged in ngoldbaum/yt/yt-3.0 (pull request #926)

Improved compatibility for YTArray operations that include iterables.
Affected #:  3 files

diff -r 3cfb38afe34da0d502ee9ddfb303426dfe9e053e -r f1fa8e20ab513e87f18447c48c299cbee5fdfd39 yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -36,7 +36,8 @@
     YTUnitOperationError, YTUfuncUnitError
 from yt.testing import fake_random_pf, requires_module
 from yt.funcs import fix_length
-
+from yt.units.unit_symbols import \
+    cm, m, g
 
 def operate_and_compare(a, b, op, answer):
     # Test generator for YTArrays tests
@@ -56,32 +57,46 @@
     # Same units
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'cm')
+    a3 = [4*cm, 5*cm, 6*cm]
     answer = YTArray([5, 7, 9], 'cm')
 
     yield operate_and_compare, a1, a2, operator.add, answer
     yield operate_and_compare, a2, a1, operator.add, answer
+    yield operate_and_compare, a1, a3, operator.add, answer
+    yield operate_and_compare, a3, a1, operator.add, answer
+    yield operate_and_compare, a2, a1, np.add, answer
     yield operate_and_compare, a1, a2, np.add, answer
-    yield operate_and_compare, a2, a1, np.add, answer
+    yield operate_and_compare, a1, a3, np.add, answer
+    yield operate_and_compare, a3, a1, np.add, answer
 
     # different units
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'm')
+    a3 = [4*m, 5*m, 6*m]
     answer1 = YTArray([401, 502, 603], 'cm')
     answer2 = YTArray([4.01, 5.02, 6.03], 'm')
 
     yield operate_and_compare, a1, a2, operator.add, answer1
     yield operate_and_compare, a2, a1, operator.add, answer2
+    yield operate_and_compare, a1, a3, operator.add, answer1
+    yield operate_and_compare, a3, a1, operator.add, answer1
     yield assert_raises, YTUfuncUnitError, np.add, a1, a2
+    yield assert_raises, YTUfuncUnitError, np.add, a1, a3
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
     a2 = array([4, 5, 6])
+    a3 = [4, 5, 6]
     answer = YTArray([5, 7, 9])
 
     yield operate_and_compare, a1, a2, operator.add, answer
     yield operate_and_compare, a2, a1, operator.add, answer
+    yield operate_and_compare, a1, a3, operator.add, answer
+    yield operate_and_compare, a3, a1, operator.add, answer
     yield operate_and_compare, a1, a2, np.add, answer
     yield operate_and_compare, a2, a1, np.add, answer
+    yield operate_and_compare, a1, a3, np.add, answer
+    yield operate_and_compare, a3, a1, np.add, answer
 
     # Catch the different dimensions error
     a1 = YTArray([1, 2, 3], 'm')
@@ -100,34 +115,49 @@
     # Same units
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'cm')
+    a3 = [4*cm, 5*cm, 6*cm]
     answer1 = YTArray([-3, -3, -3], 'cm')
     answer2 = YTArray([3, 3, 3], 'cm')
 
     yield operate_and_compare, a1, a2, operator.sub, answer1
     yield operate_and_compare, a2, a1, operator.sub, answer2
+    yield operate_and_compare, a1, a3, operator.sub, answer1
+    yield operate_and_compare, a3, a1, operator.sub, answer2
     yield operate_and_compare, a1, a2, np.subtract, answer1
     yield operate_and_compare, a2, a1, np.subtract, answer2
+    yield operate_and_compare, a1, a3, np.subtract, answer1
+    yield operate_and_compare, a3, a1, np.subtract, answer2
 
     # different units
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'm')
+    a3 = [4*m, 5*m, 6*m]
     answer1 = YTArray([-399, -498, -597], 'cm')
     answer2 = YTArray([3.99, 4.98, 5.97], 'm')
+    answer3 = YTArray([399, 498, 597], 'cm')
 
     yield operate_and_compare, a1, a2, operator.sub, answer1
     yield operate_and_compare, a2, a1, operator.sub, answer2
+    yield operate_and_compare, a1, a3, operator.sub, answer1
+    yield operate_and_compare, a3, a1, operator.sub, answer3
     yield assert_raises, YTUfuncUnitError, np.subtract, a1, a2
+    yield assert_raises, YTUfuncUnitError, np.subtract, a1, a3
 
     # Test dimensionless quantities
     a1 = YTArray([1, 2, 3])
     a2 = array([4, 5, 6])
+    a3 = [4, 5, 6]
     answer1 = YTArray([-3, -3, -3])
     answer2 = YTArray([3, 3, 3])
 
     yield operate_and_compare, a1, a2, operator.sub, answer1
     yield operate_and_compare, a2, a1, operator.sub, answer2
+    yield operate_and_compare, a1, a3, operator.sub, answer1
+    yield operate_and_compare, a3, a1, operator.sub, answer2
     yield operate_and_compare, a1, a2, np.subtract, answer1
     yield operate_and_compare, a2, a1, np.subtract, answer2
+    yield operate_and_compare, a1, a3, np.subtract, answer1
+    yield operate_and_compare, a3, a1, np.subtract, answer2
 
     # Catch the different dimensions error
     a1 = YTArray([1, 2, 3], 'm')
@@ -146,54 +176,79 @@
     # Same units
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'cm')
+    a3 = [4*cm, 5*cm, 6*cm]
     answer = YTArray([4, 10, 18], 'cm**2')
 
     yield operate_and_compare, a1, a2, operator.mul, answer
     yield operate_and_compare, a2, a1, operator.mul, answer
+    yield operate_and_compare, a1, a3, operator.mul, answer
+    yield operate_and_compare, a3, a1, operator.mul, answer
     yield operate_and_compare, a1, a2, np.multiply, answer
     yield operate_and_compare, a2, a1, np.multiply, answer
+    yield operate_and_compare, a1, a3, np.multiply, answer
+    yield operate_and_compare, a3, a1, np.multiply, answer
 
     # different units, same dimension
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'm')
+    a3 = [4*m, 5*m, 6*m]
     answer1 = YTArray([400, 1000, 1800], 'cm**2')
     answer2 = YTArray([.04, .10, .18], 'm**2')
     answer3 = YTArray([4, 10, 18], 'cm*m')
 
     yield operate_and_compare, a1, a2, operator.mul, answer1
     yield operate_and_compare, a2, a1, operator.mul, answer2
+    yield operate_and_compare, a1, a3, operator.mul, answer1
+    yield operate_and_compare, a3, a1, operator.mul, answer2
     yield operate_and_compare, a1, a2, np.multiply, answer3
     yield operate_and_compare, a2, a1, np.multiply, answer3
+    yield operate_and_compare, a1, a3, np.multiply, answer3
+    yield operate_and_compare, a3, a1, np.multiply, answer3
 
     # different dimensions
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = YTArray([4, 5, 6], 'g')
+    a3 = [4*g, 5*g, 6*g]
     answer = YTArray([4, 10, 18], 'cm*g')
 
     yield operate_and_compare, a1, a2, operator.mul, answer
     yield operate_and_compare, a2, a1, operator.mul, answer
+    yield operate_and_compare, a1, a3, operator.mul, answer
+    yield operate_and_compare, a3, a1, operator.mul, answer
     yield operate_and_compare, a1, a2, np.multiply, answer
     yield operate_and_compare, a2, a1, np.multiply, answer
+    yield operate_and_compare, a1, a3, np.multiply, answer
+    yield operate_and_compare, a3, a1, np.multiply, answer
 
     # One dimensionless, one unitful
     a1 = YTArray([1, 2, 3], 'cm')
     a2 = array([4, 5, 6])
+    a3 = [4, 5, 6]
     answer = YTArray([4, 10, 18], 'cm')
 
     yield operate_and_compare, a1, a2, operator.mul, answer
     yield operate_and_compare, a2, a1, operator.mul, answer
+    yield operate_and_compare, a1, a3, operator.mul, answer
+    yield operate_and_compare, a3, a1, operator.mul, answer
     yield operate_and_compare, a1, a2, np.multiply, answer
     yield operate_and_compare, a2, a1, np.multiply, answer
+    yield operate_and_compare, a1, a3, np.multiply, answer
+    yield operate_and_compare, a3, a1, np.multiply, answer
 
     # Both dimensionless quantities
     a1 = YTArray([1, 2, 3])
     a2 = array([4, 5, 6])
+    a3 = [4, 5, 6]
     answer = YTArray([4, 10, 18])
 
     yield operate_and_compare, a1, a2, operator.mul, answer
     yield operate_and_compare, a2, a1, operator.mul, answer
+    yield operate_and_compare, a1, a3, operator.mul, answer
+    yield operate_and_compare, a3, a1, operator.mul, answer
     yield operate_and_compare, a1, a2, np.multiply, answer
     yield operate_and_compare, a2, a1, np.multiply, answer
+    yield operate_and_compare, a1, a3, np.multiply, answer
+    yield operate_and_compare, a3, a1, np.multiply, answer
 
 
 def test_division():
@@ -205,17 +260,23 @@
     # Same units
     a1 = YTArray([1., 2., 3.], 'cm')
     a2 = YTArray([4., 5., 6.], 'cm')
+    a3 = [4*cm, 5*cm, 6*cm]
     answer1 = YTArray([0.25, 0.4, 0.5])
     answer2 = YTArray([4, 2.5, 2])
 
     yield operate_and_compare, a1, a2, operator.div, answer1
     yield operate_and_compare, a2, a1, operator.div, answer2
+    yield operate_and_compare, a1, a3, operator.div, answer1
+    yield operate_and_compare, a3, a1, operator.div, answer2
     yield operate_and_compare, a1, a2, np.divide, answer1
     yield operate_and_compare, a2, a1, np.divide, answer2
+    yield operate_and_compare, a1, a3, np.divide, answer1
+    yield operate_and_compare, a3, a1, np.divide, answer2
 
     # different units, same dimension
     a1 = YTArray([1., 2., 3.], 'cm')
     a2 = YTArray([4., 5., 6.], 'm')
+    a3 = [4*m, 5*m, 6*m]
     answer1 = YTArray([.0025, .004, .005])
     answer2 = YTArray([400, 250, 200])
     answer3 = YTArray([0.25, 0.4, 0.5], 'cm/m')
@@ -223,41 +284,60 @@
 
     yield operate_and_compare, a1, a2, operator.div, answer1
     yield operate_and_compare, a2, a1, operator.div, answer2
+    yield operate_and_compare, a1, a3, operator.div, answer1
+    yield operate_and_compare, a3, a1, operator.div, answer2
     yield operate_and_compare, a1, a2, np.divide, answer3
     yield operate_and_compare, a2, a1, np.divide, answer4
+    yield operate_and_compare, a1, a3, np.divide, answer3
+    yield operate_and_compare, a3, a1, np.divide, answer4
 
     # different dimensions
     a1 = YTArray([1., 2., 3.], 'cm')
     a2 = YTArray([4., 5., 6.], 'g')
+    a3 = [4*g, 5*g, 6*g]
     answer1 = YTArray([0.25, 0.4, 0.5], 'cm/g')
     answer2 = YTArray([4, 2.5, 2], 'g/cm')
 
     yield operate_and_compare, a1, a2, operator.div, answer1
     yield operate_and_compare, a2, a1, operator.div, answer2
+    yield operate_and_compare, a1, a3, operator.div, answer1
+    yield operate_and_compare, a3, a1, operator.div, answer2
     yield operate_and_compare, a1, a2, np.divide, answer1
     yield operate_and_compare, a2, a1, np.divide, answer2
+    yield operate_and_compare, a1, a3, np.divide, answer1
+    yield operate_and_compare, a3, a1, np.divide, answer2
 
     # One dimensionless, one unitful
     a1 = YTArray([1., 2., 3.], 'cm')
     a2 = array([4., 5., 6.])
+    a3 = [4, 5, 6]
     answer1 = YTArray([0.25, 0.4, 0.5], 'cm')
     answer2 = YTArray([4, 2.5, 2], '1/cm')
 
     yield operate_and_compare, a1, a2, operator.div, answer1
     yield operate_and_compare, a2, a1, operator.div, answer2
+    yield operate_and_compare, a1, a3, operator.div, answer1
+    yield operate_and_compare, a3, a1, operator.div, answer2
     yield operate_and_compare, a1, a2, np.divide, answer1
     yield operate_and_compare, a2, a1, np.divide, answer2
+    yield operate_and_compare, a1, a3, np.divide, answer1
+    yield operate_and_compare, a3, a1, np.divide, answer2
 
     # Both dimensionless quantities
     a1 = YTArray([1., 2., 3.])
     a2 = array([4., 5., 6.])
+    a3 = [4, 5, 6]
     answer1 = YTArray([0.25, 0.4, 0.5])
     answer2 = YTArray([4, 2.5, 2])
 
     yield operate_and_compare, a1, a2, operator.div, answer1
     yield operate_and_compare, a2, a1, operator.div, answer2
-    yield operate_and_compare, a1, a2, np.divide, answer1
-    yield operate_and_compare, a2, a1, np.divide, answer2
+    yield operate_and_compare, a1, a3, operator.div, answer1
+    yield operate_and_compare, a3, a1, operator.div, answer2
+    yield operate_and_compare, a1, a3, np.divide, answer1
+    yield operate_and_compare, a3, a1, np.divide, answer2
+    yield operate_and_compare, a1, a3, np.divide, answer1
+    yield operate_and_compare, a3, a1, np.divide, answer2
 
 
 def test_power():
@@ -662,7 +742,7 @@
     yt_arr2 = YTArray.from_astropy(ap_arr)
 
     ap_quan = 10.*_astropy.units.Msun**0.5/(_astropy.units.kpc**3)
-    yt_quan = YTQuantity(10.,"sqrt(Msun)/kpc**3")
+    yt_quan = YTQuantity(10., "sqrt(Msun)/kpc**3")
     yt_quan2 = YTQuantity.from_astropy(ap_quan)
 
     yield assert_array_equal, ap_arr, yt_arr.to_astropy()
@@ -687,6 +767,7 @@
     nu = YTASubclass([10, 11, 12], '')
     nda = np.array([3, 4, 5])
     yta = YTArray([6, 7, 8], 'mg')
+    loq = [YTQuantity(6, 'mg'), YTQuantity(7, 'mg'), YTQuantity(8, 'mg')]
     ytq = YTQuantity(4, 'cm')
     ndf = np.float64(3)
 
@@ -695,7 +776,7 @@
         assert_isinstance(op(inst2, inst1), compare_class)
 
     for op in (operator.mul, operator.div, operator.truediv):
-        for inst in (b, ytq, ndf, yta, nda):
+        for inst in (b, ytq, ndf, yta, nda, loq):
             yield op_comparison, op, a, inst, YTASubclass
 
         yield op_comparison, op, ytq, nda, YTArray
@@ -705,6 +786,7 @@
         yield op_comparison, op, nu, nda, YTASubclass
         yield op_comparison, op, a, b, YTASubclass
         yield op_comparison, op, a, yta, YTASubclass
+        yield op_comparison, op, a, loq, YTASubclass
 
     yield assert_isinstance, a[0], YTQuantity
     yield assert_isinstance, a[:], YTASubclass

diff -r 3cfb38afe34da0d502ee9ddfb303426dfe9e053e -r f1fa8e20ab513e87f18447c48c299cbee5fdfd39 yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -33,7 +33,7 @@
 from yt.units.dimensions import dimensionless
 from yt.utilities.exceptions import \
     YTUnitOperationError, YTUnitConversionError, \
-    YTUfuncUnitError
+    YTUfuncUnitError, YTIterableUnitCoercionError
 from numbers import Number as numeric_type
 from yt.utilities.on_demand_imports import _astropy
 from sympy import Rational
@@ -118,31 +118,47 @@
 def comparison_unit(unit1, unit2):
     return None
 
+NULL_UNIT = Unit()
+
+def coerce_iterable_units(input_object):
+    if isinstance(input_object, np.ndarray):
+        return input_object
+    if iterable(input_object):
+        if any([isinstance(o, YTArray) for o in input_object]):
+            ff = getattr(input_object[0], 'units', NULL_UNIT, )
+            if any([ff != getattr(_, 'units', NULL_UNIT) for _ in input_object]):
+                raise YTIterableUnitCoercionError(input_object)
+            # This will create a copy of the data in the iterable.
+            return YTArray(input_object)
+        return input_object
+    else:
+        return input_object
+
 def sanitize_units_mul(this_object, other_object):
-    ret = other_object
+    inp = coerce_iterable_units(this_object)
+    ret = coerce_iterable_units(other_object)
     # If the other object is a YTArray and has the same dimensions as the object
     # under consideration, convert so we don't mix units with the same
     # dimensions.
-    if isinstance(other_object, YTArray):
-        if this_object.units.same_dimensions_as(other_object.units):
-            ret = other_object.in_units(this_object.units)
+    if isinstance(ret, YTArray):
+        if inp.units.same_dimensions_as(ret.units):
+            ret.in_units(inp.units)
     return ret
 
 def sanitize_units_add(this_object, other_object, op_string):
+    inp = coerce_iterable_units(this_object)
+    ret = coerce_iterable_units(other_object)
     # Make sure the other object is a YTArray before we use the `units`
     # attribute.
-    if isinstance(other_object, YTArray):
-        if not this_object.units.same_dimensions_as(other_object.units):
-            raise YTUnitOperationError(op_string, this_object.units,
-                                       other_object.units)
-        ret = other_object.in_units(this_object.units)
+    if isinstance(ret, YTArray):
+        if not inp.units.same_dimensions_as(ret.units):
+            raise YTUnitOperationError(op_string, inp.units, ret.units)
+        ret = ret.in_units(inp.units)
     # If the other object is not a YTArray, the only valid case is adding
     # dimensionless things.
     else:
-        if not this_object.units.is_dimensionless:
-            raise YTUnitOperationError(op_string, this_object.units,
-                                       dimensionless)
-        ret = other_object
+        if not inp.units.is_dimensionless:
+            raise YTUnitOperationError(op_string, inp.units, dimensionless)
     return ret
 
 unary_operators = (
@@ -269,7 +285,7 @@
         elif isinstance(input_array, np.ndarray):
             pass
         elif iterable(input_array):
-            if isinstance(input_array[0], YTQuantity):
+            if isinstance(input_array[0], YTArray):
                 return YTArray(np.array(input_array, dtype=dtype),
                                input_array[0].units)
 
@@ -903,17 +919,19 @@
                 raise YTUnitOperationError(context[0], u)
             ret_class = type(self)
         elif context[0] in binary_operators:
-            unit1 = getattr(context[1][0], 'units', None)
-            unit2 = getattr(context[1][1], 'units', None)
-            cls1 = type(context[1][0])
-            cls2 = type(context[1][1])
+            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 = context[1][1]
+                unit2 = oper2
                 if isinstance(unit2, np.ndarray):
                     if isinstance(unit2, YTArray):
                         if unit2.units.is_dimensionless:
@@ -1029,9 +1047,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)):
+    if cls1 is np.ndarray or issubclass(cls1, (numeric_type, np.number, list, tuple)):
         return cls2
-    if cls2 is np.ndarray or issubclass(cls2, (numeric_type, np.number)):
+    if cls2 is np.ndarray or issubclass(cls2, (numeric_type, np.number, list, tuple)):
         return cls1
     if issubclass(cls1, YTQuantity):
         return cls2
@@ -1042,5 +1060,5 @@
     if issubclass(cls2, cls1):
         return cls2
     else:
-        raise RuntimeError("Operations are only defined on pairs of objects"
-                           "in which one is a subclass of the other")
+        raise RuntimeError("Undefined operation for a YTArray subclass. "
+                           "Received operand types (%s) and (%s)" % (cls1, cls2))

diff -r 3cfb38afe34da0d502ee9ddfb303426dfe9e053e -r f1fa8e20ab513e87f18447c48c299cbee5fdfd39 yt/utilities/exceptions.py
--- a/yt/utilities/exceptions.py
+++ b/yt/utilities/exceptions.py
@@ -193,9 +193,18 @@
 
     def __str__(self):
         err = "The NumPy %s operation is only allowed on objects with " \
-        "identical units. Convert one of the arrays to the other\'s " \
-        "units first. Received units (%s) and (%s)." % \
-        (self.ufunc, self.unit1, self.unit2)
+              "identical units. Convert one of the arrays to the other\'s " \
+              "units first. Received units (%s) and (%s)." % \
+              (self.ufunc, self.unit1, self.unit2)
+        return err
+
+class YTIterableUnitCoercionError(YTException):
+    def __init__(self, quantity_list):
+        self.quantity_list = quantity_list
+
+    def __str__(self):
+        err = "Received a list or tuple of quantities with nonuniform units: " \
+              "%s" % self.quantity_list
         return err
 
 class YTHubRegisterError(YTException):

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