From 79396a6bfccc83f5ebb3bcc65972907fa2f85b9f Mon Sep 17 00:00:00 2001 From: krzywon Date: Mon, 16 Mar 2026 10:49:23 -0400 Subject: [PATCH 1/7] AnularSector -> AnnularSector --- sasdata/slicing/slicer_demo.py | 6 +- .../{AnularSector.py => AnnularSector.py} | 88 +++++++++---------- 2 files changed, 47 insertions(+), 47 deletions(-) rename sasdata/slicing/slicers/{AnularSector.py => AnnularSector.py} (90%) diff --git a/sasdata/slicing/slicer_demo.py b/sasdata/slicing/slicer_demo.py index 5626ded4d..b8ff0224e 100644 --- a/sasdata/slicing/slicer_demo.py +++ b/sasdata/slicing/slicer_demo.py @@ -4,7 +4,7 @@ import numpy as np from sasdata.slicing.meshes.voronoi_mesh import voronoi_mesh -from sasdata.slicing.slicers.AnularSector import AnularSector +from sasdata.slicing.slicers.AnnularSector import AnnularSector if __name__ == "__main__": q_range = 1.5 @@ -41,7 +41,7 @@ def lobe_test_function(x, y): phi0 = np.pi/2 - size phi1 = np.pi/2 + size - rebinner = AnularSector(q0, q1, phi0, phi1) + rebinner = AnnularSector(q0, q1, phi0, phi1) data_order_neg1.append(rebinner.sum(x, y, random_lobe_data, order=-1)) data_order_0.append(rebinner.sum(x, y, random_lobe_data, order=0)) @@ -94,7 +94,7 @@ def ring_test_function(x, y): phi0 = np.pi/2 - size phi1 = np.pi/2 + size - rebinner = AnularSector(q0, q1, phi0, phi1) + rebinner = AnnularSector(q0, q1, phi0, phi1) data_order_neg1.append(rebinner.average(x, y, grid_ring_data, order=-1)) data_order_0.append(rebinner.average(x, y, grid_ring_data, order=0)) diff --git a/sasdata/slicing/slicers/AnularSector.py b/sasdata/slicing/slicers/AnnularSector.py similarity index 90% rename from sasdata/slicing/slicers/AnularSector.py rename to sasdata/slicing/slicers/AnnularSector.py index 56ed5f262..92df63921 100644 --- a/sasdata/slicing/slicers/AnularSector.py +++ b/sasdata/slicing/slicers/AnnularSector.py @@ -1,44 +1,44 @@ -import numpy as np - -from sasdata.slicing.meshes.mesh import Mesh -from sasdata.slicing.rebinning import Rebinner - - -class AnularSector(Rebinner): - """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): - super().__init__() - - self.q0 = q0 - self.q1 = q1 - self.phi0 = phi0 - self.phi1 = phi1 - - self.points_per_degree = points_per_degree - - def _bin_mesh(self) -> Mesh: - - n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) - - angles = np.linspace(self.phi0, self.phi1, n_points) - - row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) - row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] - - points = np.concatenate((row1, row2), axis=1).T - - cells = [[i for i in range(2*n_points)]] - - return Mesh(points=points, cells=cells) - - def _bin_coordinates(self) -> np.ndarray: - return np.array([], dtype=float) - - -def main(): - """ Just show a random example""" - AnularSector(1, 2, 1, 2).bin_mesh.show() - - -if __name__ == "__main__": - main() +import numpy as np + +from sasdata.slicing.meshes.mesh import Mesh +from sasdata.slicing.rebinning import Rebinner + + +class AnnularSector(Rebinner): + """ A single annular sector (wedge sum)""" + def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): + super().__init__() + + self.q0 = q0 + self.q1 = q1 + self.phi0 = phi0 + self.phi1 = phi1 + + self.points_per_degree = points_per_degree + + def _bin_mesh(self) -> Mesh: + + n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) + + angles = np.linspace(self.phi0, self.phi1, n_points) + + row1 = self.q0 * np.array([np.cos(angles), np.sin(angles)]) + row2 = self.q1 * np.array([np.cos(angles), np.sin(angles)])[:, ::-1] + + points = np.concatenate((row1, row2), axis=1).T + + cells = [[i for i in range(2*n_points)]] + + return Mesh(points=points, cells=cells) + + def _bin_coordinates(self) -> np.ndarray: + return np.array([], dtype=float) + + +def main(): + """ Just show a random example""" + AnnularSector(1, 2, 1, 2).bin_mesh.show() + + +if __name__ == "__main__": + main() From c1c1409c39405beb2a5fa9d22cb3e55c275f83f2 Mon Sep 17 00:00:00 2001 From: krzywon Date: Mon, 16 Mar 2026 11:37:13 -0400 Subject: [PATCH 2/7] Add infinity, -infinity, and pi/2 to constants --- sasdata/quantities/constants.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/sasdata/quantities/constants.py b/sasdata/quantities/constants.py index ea260e24e..b2a17c236 100644 --- a/sasdata/quantities/constants.py +++ b/sasdata/quantities/constants.py @@ -1,4 +1,10 @@ import math +# Common Pi-related values Pi = math.pi TwoPi = 2 * math.pi +PiOverTwo = Pi / 2 + +# Infinite values +Inf = math.inf +NegInf = -math.inf From 0db9512b7daaff2a3e433aed1c6f0d578f0831d9 Mon Sep 17 00:00:00 2001 From: krzywon Date: Mon, 16 Mar 2026 11:38:04 -0400 Subject: [PATCH 3/7] Add an Annular slicer that builds off the wedge slicer, but with angular limits of 0.0 to 2*pi --- sasdata/slicing/slicers/Annular.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 sasdata/slicing/slicers/Annular.py diff --git a/sasdata/slicing/slicers/Annular.py b/sasdata/slicing/slicers/Annular.py new file mode 100644 index 000000000..2a195549a --- /dev/null +++ b/sasdata/slicing/slicers/Annular.py @@ -0,0 +1,17 @@ +from sasdata.quantities.constants import TwoPi +from sasdata.slicing.slicers.AnnularSector import AnnularSector + + +class Annular(AnnularSector): + """ Annual averaging, using the wedge as a basis. """ + def __init__(self, q0: float, q1: float, points_per_degree: int=2): + super().__init__(q0, q1, 0.0, TwoPi, points_per_degree) + + +def main(): + """ Just show a random example""" + Annular(1, 2).bin_mesh.show() + + +if __name__ == "__main__": + main() From fef272ac7a53cd5cda2e0cccf7c6ce4095d8f013 Mon Sep 17 00:00:00 2001 From: krzywon Date: Mon, 16 Mar 2026 11:38:36 -0400 Subject: [PATCH 4/7] Add a Sector slicer that builds off the wedge slicer, but with infinite Q limits --- sasdata/slicing/slicers/Sector.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 sasdata/slicing/slicers/Sector.py diff --git a/sasdata/slicing/slicers/Sector.py b/sasdata/slicing/slicers/Sector.py new file mode 100644 index 000000000..4483af611 --- /dev/null +++ b/sasdata/slicing/slicers/Sector.py @@ -0,0 +1,20 @@ +from sasdata.quantities.constants import Inf, NegInf, PiOverTwo +from sasdata.slicing.slicers.AnnularSector import AnnularSector + + +class Sector(AnnularSector): + """ Annual averaging, using the wedge as a basis. """ + def __init__(self, phi0: float, phi1: float, points_per_degree: int=2): + super().__init__(NegInf, Inf, phi0, phi1, points_per_degree) + + +def main(): + """ Just show a random example""" + sector = Sector(0, PiOverTwo) + sector.q0 = -3 + sector.q1 = 3 + sector.bin_mesh.show() + + +if __name__ == "__main__": + main() From afc6caad1e60edbd07f9fae03c1211c6deffe30b Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 17 Mar 2026 09:34:20 -0400 Subject: [PATCH 5/7] Use quantities for AnnularSector.py slicer values to ensure units are consistent --- sasdata/slicing/slicers/AnnularSector.py | 32 ++++++++++++++++++++---- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/sasdata/slicing/slicers/AnnularSector.py b/sasdata/slicing/slicers/AnnularSector.py index 92df63921..6fc565bf7 100644 --- a/sasdata/slicing/slicers/AnnularSector.py +++ b/sasdata/slicing/slicers/AnnularSector.py @@ -1,21 +1,43 @@ import numpy as np +from sasdata.quantities.quantity import Quantity +from sasdata.quantities.units import per_angstrom, radians from sasdata.slicing.meshes.mesh import Mesh from sasdata.slicing.rebinning import Rebinner +QuantityType = Quantity | float + class AnnularSector(Rebinner): """ A single annular sector (wedge sum)""" - def __init__(self, q0: float, q1: float, phi0: float, phi1: float, points_per_degree: int=2): + def __init__(self, q0: QuantityType, q1: QuantityType, phi0: QuantityType, + phi1: QuantityType, points_per_degree: int=2): super().__init__() - self.q0 = q0 - self.q1 = q1 - self.phi0 = phi0 - self.phi1 = phi1 + # Ensure all values are scaled to the proper units + self._q0 = q0.to_units_of(per_angstrom) if isinstance(q0, Quantity) else Quantity(q0, per_angstrom) + self._q1 = q1.to_units_of(per_angstrom) if isinstance(q1, Quantity) else Quantity(q1, per_angstrom) + self._phi0 = phi0.to_units_of(radians) if isinstance(phi0, Quantity) else Quantity(phi0, radians) + self._phi1 = phi1.to_units_of(radians) if isinstance(phi1, Quantity) else Quantity(phi1, radians) self.points_per_degree = points_per_degree + @property + def q0(self): + return self._q0.value + + @property + def q1(self): + return self._q1.value + + @property + def phi0(self): + return self._phi0.value + + @property + def phi1(self): + return self._phi1.value + def _bin_mesh(self) -> Mesh: n_points = np.max([int(1 + 180*self.points_per_degree*(self.phi1 - self.phi0) / np.pi), 2]) From b104fb60ce27009f550989c1b63128841c39c7fa Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 17 Mar 2026 09:35:05 -0400 Subject: [PATCH 6/7] Use the public numpy.typing package instead of numpy._typing --- sasdata/quantities/quantity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sasdata/quantities/quantity.py b/sasdata/quantities/quantity.py index 08e0be6fe..e38dc2eaa 100644 --- a/sasdata/quantities/quantity.py +++ b/sasdata/quantities/quantity.py @@ -5,7 +5,7 @@ import h5py import numpy as np -from numpy._typing import ArrayLike +from numpy.typing import ArrayLike from sasdata.quantities import units from sasdata.quantities.numerical_encoding import numerical_decode, numerical_encode From 2c6594d1147760f7dc17013f83fce27019f17066 Mon Sep 17 00:00:00 2001 From: krzywon Date: Tue, 17 Mar 2026 15:01:43 -0400 Subject: [PATCH 7/7] Update Annual averaging to accept quantities in its init --- sasdata/slicing/slicers/Annular.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/sasdata/slicing/slicers/Annular.py b/sasdata/slicing/slicers/Annular.py index 2a195549a..1d349120f 100644 --- a/sasdata/slicing/slicers/Annular.py +++ b/sasdata/slicing/slicers/Annular.py @@ -1,16 +1,22 @@ from sasdata.quantities.constants import TwoPi -from sasdata.slicing.slicers.AnnularSector import AnnularSector +from sasdata.quantities.quantity import Quantity +from sasdata.quantities.units import per_angstrom +from sasdata.slicing.slicers.AnnularSector import AnnularSector, QuantityType class Annular(AnnularSector): """ Annual averaging, using the wedge as a basis. """ - def __init__(self, q0: float, q1: float, points_per_degree: int=2): + def __init__(self, q0: QuantityType, q1: QuantityType, points_per_degree: int=2): super().__init__(q0, q1, 0.0, TwoPi, points_per_degree) def main(): """ Just show a random example""" - Annular(1, 2).bin_mesh.show() + a_float = Annular(1, 2) + q0 = Quantity(1, per_angstrom) + q1 = Quantity(2, per_angstrom) + a_quant = Annular(q0, q1) + assert a_float.bin_mesh.n_cells == a_quant.bin_mesh.n_cells if __name__ == "__main__":