From ba88bf333d15cbbc50850205a679da285942304a Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 16 Feb 2026 16:34:58 +0000 Subject: [PATCH 1/6] Add rotation unit --- sasdata/quantities/_build_tables.py | 1 + sasdata/quantities/units.py | 3 +++ test/quantities/utest_units.py | 6 +++++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/sasdata/quantities/_build_tables.py b/sasdata/quantities/_build_tables.py index c07497471..ba07aee80 100644 --- a/sasdata/quantities/_build_tables.py +++ b/sasdata/quantities/_build_tables.py @@ -68,6 +68,7 @@ UnitData("y", None, None, "year", "years", 3600*24*365.2425, 0, 1, 0, 0, 0, 0, 0, []), UnitData("deg", None, None, "degree", "degrees", 180/np.pi, 0, 0, 0, 0, 0, 0, 1, []), UnitData("rad", None, None, "radian", "radians", 1, 0, 0, 0, 0, 0, 0, 1, []), + UnitData("rot", None, None, "rotation", "rotations", 2*np.pi, 0, 0, 0, 0, 0, 0, 1, []), UnitData("sr", None, None, "stradian", "stradians", 1, 0, 0, 0, 0, 0, 0, 2, []), UnitData("l", None, None, "litre", "litres", 1e-3, 3, 0, 0, 0, 0, 0, 0, []), UnitData("eV", None, None, "electronvolt", "electronvolts", 1.602176634e-19, 2, -2, 1, 0, 0, 0, 0, all_magnitudes), diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index 8af224043..a7c10d4f4 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -692,6 +692,7 @@ def __init__(self, name: str, units: list[NamedUnit]): years = NamedUnit(31556952.0, Dimensions(0, 1, 0, 0, 0, 0, 0),name='years',ascii_symbol='y',symbol='y') degrees = NamedUnit(57.29577951308232, Dimensions(0, 0, 0, 0, 0, 0, 1),name='degrees',ascii_symbol='deg',symbol='deg') radians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 1),name='radians',ascii_symbol='rad',symbol='rad') +rotations = NamedUnit(6.283185307179586, Dimensions(0, 0, 0, 0, 0, 0, 1),name='rotations',ascii_symbol='rot',symbol='rot') stradians = NamedUnit(1, Dimensions(0, 0, 0, 0, 0, 0, 2),name='stradians',ascii_symbol='sr',symbol='sr') litres = NamedUnit(0.001, Dimensions(3, 0, 0, 0, 0, 0, 0),name='litres',ascii_symbol='l',symbol='l') electronvolts = NamedUnit(1.602176634e-19, Dimensions(2, -2, 1, 0, 0, 0, 0),name='electronvolts',ascii_symbol='eV',symbol='eV') @@ -2052,6 +2053,7 @@ def __init__(self, name: str, units: list[NamedUnit]): "y": years, "deg": degrees, "rad": radians, + "rot": rotations, "sr": stradians, "l": litres, "eV": electronvolts, @@ -3385,6 +3387,7 @@ def __init__(self, name: str, units: list[NamedUnit]): units = [ degrees, radians, + rotations, ]) solid_angle = UnitGroup( diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index 258143e2a..7c5d84aa0 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -36,7 +36,11 @@ def run_test(self): EqualUnits("Resistance", units.ohms, units.volts / units.amperes, - 1e-3/units.millisiemens) + 1e-3/units.millisiemens), + + EquivalentButUnequalUnits("Angular frequency", + units.rotations / units.minutes, + units.degrees * units.hertz), ] From de3d04d6ae5010a1b6b290b8148ec708b8b6a299 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 16 Feb 2026 17:27:00 +0000 Subject: [PATCH 2/6] Implement scalar multiplication for units --- sasdata/quantities/_units_base.py | 17 ++++++++++------- sasdata/quantities/units.py | 17 ++++++++++------- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/sasdata/quantities/_units_base.py b/sasdata/quantities/_units_base.py index 17897f895..9e6401b53 100644 --- a/sasdata/quantities/_units_base.py +++ b/sasdata/quantities/_units_base.py @@ -214,17 +214,20 @@ def _components(self, tokens: Sequence["UnitToken"]): pass def __mul__(self: Self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + if isinstance(other, Unit): + return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + elif isinstance(other, (int, float)): + return Unit(other * self.scale, self.dimensions) + return NotImplemented def __truediv__(self: Self, other: "Unit"): - if not isinstance(other, Unit): + if isinstance(other, Unit): + return Unit(self.scale / other.scale, self.dimensions / other.dimensions) + elif isinstance(other, (int, float)): + return Unit(self.scale / other, self.dimensions) + else: return NotImplemented - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) diff --git a/sasdata/quantities/units.py b/sasdata/quantities/units.py index a7c10d4f4..7d03abda0 100644 --- a/sasdata/quantities/units.py +++ b/sasdata/quantities/units.py @@ -299,17 +299,20 @@ def _components(self, tokens: Sequence["UnitToken"]): pass def __mul__(self: Self, other: "Unit"): - if not isinstance(other, Unit): - return NotImplemented - - return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + if isinstance(other, Unit): + return Unit(self.scale * other.scale, self.dimensions * other.dimensions) + elif isinstance(other, (int, float)): + return Unit(other * self.scale, self.dimensions) + return NotImplemented def __truediv__(self: Self, other: "Unit"): - if not isinstance(other, Unit): + if isinstance(other, Unit): + return Unit(self.scale / other.scale, self.dimensions / other.dimensions) + elif isinstance(other, (int, float)): + return Unit(self.scale / other, self.dimensions) + else: return NotImplemented - return Unit(self.scale / other.scale, self.dimensions / other.dimensions) - def __rtruediv__(self: Self, other: "Unit"): if isinstance(other, Unit): return Unit(other.scale / self.scale, other.dimensions / self.dimensions) From bc4553639e8fcba4028475e154e3a45e804f3a7d Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Mon, 16 Feb 2026 17:27:00 +0000 Subject: [PATCH 3/6] Add test for angular velocity units --- test/quantities/utest_units.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index 7c5d84aa0..bc2f050d4 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -1,3 +1,4 @@ +import math import sasdata.quantities.units as units from sasdata.quantities.units import Unit @@ -42,6 +43,10 @@ def run_test(self): units.rotations / units.minutes, units.degrees * units.hertz), + EqualUnits("Angular frequency", + (units.rotations/units.minutes ), + (units.radians*units.hertz) * 2 * math.pi/60.0), + ] From bb377a2be050c2c168f349fc07b27115c9d77350 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 17 Feb 2026 11:56:28 +0000 Subject: [PATCH 4/6] Ensure that frequency and angular frequency are distinct --- test/quantities/utest_units.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index bc2f050d4..00715f751 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -26,6 +26,16 @@ def run_test(self): assert unit_1.equivalent(unit_2), "Units should be equivalent" assert unit_1 != unit_2, "Units should not be equal" +class DissimilarUnits: + def __init__(self, test_name: str, *units): + self.test_name = "Dissimilar: " + test_name + self.units: list[Unit] = list(units) + + def run_test(self): + for i, unit_1 in enumerate(self.units): + for unit_2 in self.units[i+1:]: + assert not unit_1.equivalent(unit_2), "Units should not be equivalent" + tests = [ @@ -47,6 +57,10 @@ def run_test(self): (units.rotations/units.minutes ), (units.radians*units.hertz) * 2 * math.pi/60.0), + DissimilarUnits("Frequency and Angular frequency", + (units.rotations/units.minutes), + (units.hertz)), + ] From 3de2ff204d737b01223f33e5c07f84d9121d8fb4 Mon Sep 17 00:00:00 2001 From: Adam Washington Date: Tue, 17 Feb 2026 12:04:21 +0000 Subject: [PATCH 5/6] Minor formatting improvements --- test/quantities/utest_units.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index 00715f751..c32d6ac0c 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -10,7 +10,7 @@ def __init__(self, test_name: str, *units): def run_test(self): for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i+1:]: + for unit_2 in self.units[i + 1 :]: assert unit_1.equivalent(unit_2), "Units should be equivalent" assert unit_1 == unit_2, "Units should be equal" @@ -22,10 +22,11 @@ def __init__(self, test_name: str, *units): def run_test(self): for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i+1:]: + for unit_2 in self.units[i + 1 :]: assert unit_1.equivalent(unit_2), "Units should be equivalent" assert unit_1 != unit_2, "Units should not be equal" + class DissimilarUnits: def __init__(self, test_name: str, *units): self.test_name = "Dissimilar: " + test_name @@ -33,7 +34,7 @@ def __init__(self, test_name: str, *units): def run_test(self): for i, unit_1 in enumerate(self.units): - for unit_2 in self.units[i+1:]: + for unit_2 in self.units[i + 1 :]: assert not unit_1.equivalent(unit_2), "Units should not be equivalent" From a49969c628d1407c77b9d4c1d493e20f403d17ac Mon Sep 17 00:00:00 2001 From: "pre-commit-ci-lite[bot]" <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 14:43:02 +0000 Subject: [PATCH 6/6] [pre-commit.ci lite] apply automatic fixes for ruff linting errors --- test/quantities/utest_units.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/quantities/utest_units.py b/test/quantities/utest_units.py index c32d6ac0c..3bc775313 100644 --- a/test/quantities/utest_units.py +++ b/test/quantities/utest_units.py @@ -1,4 +1,5 @@ import math + import sasdata.quantities.units as units from sasdata.quantities.units import Unit