Skip to content

Commit

Permalink
add Angle dimension
Browse files Browse the repository at this point in the history
  • Loading branch information
gertjanvanzwieten committed Sep 29, 2022
1 parent 1ba224b commit 23f287e
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 2 deletions.
15 changes: 15 additions & 0 deletions nutils/SI.py
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,18 @@ def _dispatch(op, *args, **kwargs):
args = [q.__value for q in stack_args],
elif name in ('shape', 'ndim', 'size', 'sign'):
Dim = None
elif name in ('sin', 'cos', 'tan'):
if not isinstance(args[0], Angle):
raise TypeError(f'trigonometric functions require angle [Φ], got {type(args[0]).__name__}')
Dim = Dimensionless
elif name in ('arcsin', 'arccos', 'arctan'):
if not isinstance(args[0], Dimensionless):
raise TypeError(f'inverse trigonometric functions require dimensionless [], got {type(args[0]).__name__}')
Dim = Angle
elif name == 'arctan2':
if type(args[0]) != type(args[1]):
raise TypeError(f'arguments of arctan2 must have equal dimension, got {type(args[0]).__name__} and {type(args[1]).__name__}')
Dim = Angle
else:
return NotImplemented
try:
Expand Down Expand Up @@ -315,6 +327,7 @@ def _split_factors(s):
Temperature = Dimension.create('θ')
AmountOfSubstance = Dimension.create('N')
LuminousFlux = LuminousIntensity = Dimension.create('J')
Angle = Dimension.create('Φ')

Area = Length**2
Volume = Length**3
Expand Down Expand Up @@ -358,6 +371,7 @@ def _split_factors(s):
units.K = Temperature(1.)
units.mol = AmountOfSubstance(1.)
units.cd = LuminousIntensity(1.)
units.rad = Angle(1.)

units.N = 'kg*m/s2' # newton
units.Pa = 'N/m2' # pascal
Expand Down Expand Up @@ -388,3 +402,4 @@ def _split_factors(s):
units.t = '1000kg' # ton
units.Da = '1.66053904020yg' # dalton
units.eV = '.1602176634aJ' # electronvolt
units.deg = '0.017453292519943295rad' # degree
27 changes: 25 additions & 2 deletions tests/test_SI.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,14 @@ def test_pickle(self):

class Quantity(unittest.TestCase):

def assertWrapped(self, quantity, expect_dim, expect_val):
def assertWrapped(self, quantity, expect_dim, expect_val, tol=0):
dim, val = quantity.__unwrap_quantity__()
self.assertEqual(dim, expect_dim)
self.assertEqual(numpy.asarray(val).tolist(), numpy.asarray(expect_val).tolist())
if not tol:
self.assertEqual(numpy.asarray(val).tolist(), numpy.asarray(expect_val).tolist())
else:
self.assertEqual(numpy.shape(val), numpy.shape(expect_val))
self.assertTrue(numpy.allclose(val, expect_val, atol=tol))

def test_fromstring(self):
self.assertWrapped(SI.parse('5kN'), SI.Force, 5000)
Expand Down Expand Up @@ -258,3 +262,22 @@ def test_round(self):
with self.assertRaises(ValueError):
round(F)
self.assertEqual(round(F / SI.units.N), 2)

def test_angle(self):
φ = SI.Angle('30deg')
self.assertWrapped(numpy.sin(φ), SI.Dimensionless, .5, tol=1e-10)
self.assertWrapped(numpy.cos(φ), SI.Dimensionless, numpy.sqrt(3)/2, tol=1e-10)
self.assertWrapped(numpy.tan(φ), SI.Dimensionless, 1/numpy.sqrt(3), tol=1e-10)
with self.assertRaisesRegex(TypeError, r'trigonometric functions require angle \[Φ\], got \[L\]'):
numpy.sin(SI.parse('2m'))
v = SI.Dimensionless(1.)
self.assertWrapped(numpy.arcsin(v), SI.Angle, numpy.pi/2, tol=1e-10)
self.assertWrapped(numpy.arccos(v), SI.Angle, 0, tol=1e-10)
self.assertWrapped(numpy.arctan(v), SI.Angle, numpy.pi/4, tol=1e-10)
with self.assertRaisesRegex(TypeError, r'inverse trigonometric functions require dimensionless \[\], got \[L\]'):
numpy.arcsin(SI.parse('2m'))
a = SI.Length('1m')
b = SI.Length('-1m')
self.assertWrapped(numpy.arctan2(a, b), SI.Angle, .75*numpy.pi, tol=1e-10)
with self.assertRaisesRegex(TypeError, r'arguments of arctan2 must have equal dimension, got \[L\] and \[M\]'):
numpy.arctan2(SI.parse('2m'), SI.parse('1kg'))

0 comments on commit 23f287e

Please sign in to comment.