Repository: yt-project/unyt
Pull request #134 (2020-01-29 14:47:32)

fix invalid logarithmic unit operations
logarithmic quantities only support:
- addition, subtraction
- dimensionless multiplication and division
- raise to power 1.0


Patch:
diff --git a/unyt/unit_object.py b/unyt/unit_object.py
index 8406d48..fad605f 100644
--- a/unyt/unit_object.py
+++ b/unyt/unit_object.py
@@ -42,6 +42,7 @@ from unyt.dimensions import (
     dimensionless,
     temperature,
     current_mks,
+    logarithmic,
 )
 import unyt.dimensions as dims
 from unyt.equivalencies import equivalence_registry
@@ -378,6 +379,10 @@ class Unit(object):
             data = np.array(u, subok=True)
             unit = getattr(u, "units", None)
             if unit is not None:
+                if self.dimensions is logarithmic:
+                    raise InvalidUnitOperation(
+                        "Tried to multiply '%s' and '%s'." % (self, unit)
+                    )
                 units = unit * self
             else:
                 units = self
@@ -389,6 +394,10 @@ class Unit(object):
             if data.shape == ():
                 return _import_cache_singleton.uq(data, units, bypass_validation=True)
             return _import_cache_singleton.ua(data, units, bypass_validation=True)
+        elif self.dimensions is logarithmic and not u.is_dimensionless:
+            raise InvalidUnitOperation("Tried to multiply '%s' and '%s'." % (self, u))
+        elif u.dimensions is logarithmic and not self.is_dimensionless:
+            raise InvalidUnitOperation("Tried to multiply '%s' and '%s'." % (self, u))
 
         base_offset = 0.0
         if self.base_offset or u.base_offset:
@@ -422,6 +431,10 @@ class Unit(object):
                     "Tried to divide a Unit object by '%s' (type %s). This "
                     "behavior is undefined." % (u, type(u))
                 )
+        elif self.dimensions is logarithmic and not u.is_dimensionless:
+            raise InvalidUnitOperation("Tried to divide '%s' and '%s'." % (self, u))
+        elif u.dimensions is logarithmic and not self.is_dimensionless:
+            raise InvalidUnitOperation("Tried to divide '%s' and '%s'." % (self, u))
 
         base_offset = 0.0
         if self.base_offset or u.base_offset:
@@ -455,6 +468,9 @@ class Unit(object):
                 "it to a float." % (p, type(p))
             )
 
+        if self.dimensions is logarithmic and p != 1.0:
+            raise InvalidUnitOperation("Tried to raise '%s' to power '%s'" % (self, p))
+
         return Unit(
             self.expr ** p,
             base_value=(self.base_value ** p),


Test patch:
diff --git a/unyt/tests/test_units.py b/unyt/tests/test_units.py
index 44f98f0..c79141f 100644
--- a/unyt/tests/test_units.py
+++ b/unyt/tests/test_units.py
@@ -446,6 +446,16 @@ def test_invalid_operations():
         u1 /= u2
     with pytest.raises(InvalidUnitOperation):
         u1 / "hello!"
+    with pytest.raises(InvalidUnitOperation):
+        Unit("B") * Unit("V")
+    with pytest.raises(InvalidUnitOperation):
+        Unit("V") * Unit("B")
+    with pytest.raises(InvalidUnitOperation):
+        Unit("V") / Unit("Np")
+    with pytest.raises(InvalidUnitOperation):
+        Unit("dB") / Unit("dB")
+    with pytest.raises(InvalidUnitOperation):
+        Unit("B") ** 2
 
 
 def test_base_equivalent():
@@ -814,3 +824,4 @@ def test_bel_neper():
     c = 2 * Unit("Np")
     d = 20 * Unit("decineper")
     assert (c == d).all()
+    assert Unit("dB") ** 1 == Unit("dB")
diff --git a/unyt/tests/test_unyt_array.py b/unyt/tests/test_unyt_array.py
index cf75463..e8c9493 100644
--- a/unyt/tests/test_unyt_array.py
+++ b/unyt/tests/test_unyt_array.py
@@ -2355,3 +2355,15 @@ def test_name_attribute():
     assert f.name is None
     h = f.to("J", equivalence="thermal")
     assert h.name is None
+
+
+def test_neper_bel():
+    assert 0 * Unit("dB") + 20 * Unit("dB") == unyt_quantity(20, "dB")
+    with pytest.raises(InvalidUnitOperation):
+        unyt_array([1, 10], "V") * Unit("dB")
+    with pytest.raises(InvalidUnitOperation):
+        Unit("Np") * unyt_array([1, 10], "s")
+    with pytest.raises(InvalidUnitOperation):
+        unyt_array([0, 20], "dB") ** 2
+    with pytest.raises(InvalidUnitOperation):
+        np.power(unyt_array([0, 20], "dB"), -2)