Skip to content

Conversation

@ehewins
Copy link
Contributor

@ehewins ehewins commented Sep 17, 2023

Description

This pull request covers the last part of my summer project: the manipulations.py rewrite. It also includes the new analytical unit tests, from branch 44-new-unit-tests-for-slicer-averagers, except in this branch they point towards the new manipulations module instead of the old one.

The details of the manipulations.py rewrite are as follows:

  • Parent classes for Cartesian and Polar regions of interest setup have now been added, drastically reducing the amount of repeated code.
  • The directional averagers (SlabX, Ring, SectorQ, etc) now all use a common DirectionalAverage class for the bulk of their computation. This too reduces repeated code and increases maintainability.
  • The method of weighting datapoints based on their position relative to the ROI (inside or outside) has been retooled to make it easier to implement fractional binning later down the line.
  • All the data manipulators require an nbins parameter now, whereas before some needed bin_width instead.
  • Instead of measuring phi angles from the negative x-axis, they are measured from the positive x-axis and are supplied to the averagers with the same -pi to pi interval used by the slicers themselves.

The main functionality change is how DirectionalAverage considers the weights of the datapoints. In both the new and old manipulations.py these weights are either 0 or 1, though now it should be a lot easier to implement fractional weights. Previously, the weight assigned to a datapoint was calculated on-the-fly as part of a for loop which ran over each datapoint and was also responsible for part of the averaging process. The new methodology separates out the weighting and averaging processes, creating an nxm weights matrix where n is the number of bins the data is sorted into, and m is the number of datapoints. The averaging is then done using matrix multiplication and numpy functions. This new method will certainly be more memory heavy than the old method, and it could be slower for large datasets. My hope is however that the switch from python functions to numpy functions will mitigate this, and the improvement to the quality of results once fractional weights are added should more than justify the resource usage.

Dependencies needed: scipy - needed for new unit tests.
Note that merging this pull request requires that a sister pull request in the sasview repo also be merged: SasView/sasview#2615

Fixes #46 (issue announcing rewrite plan) and fixes #44 (issue about dodgy unit tests) and fixes #43 (wedge averaging had issues with full-circle ROIs).

How Has This Been Tested?

There are two ways this rewrite has been tested. The less rigorous method involved using this branch plus the sasview branch mentioned above to confirm that the plotted results look the same before and after using the same data file, slicer and slicer parameters. The more rigorous method involves the new unit tests. When the file utest_averaging_analytical.py from the branch 44-new-unit-tests-for-slicer-averagers is run, you see that averagers from the previous version of manipulations.py all pass these tests. The version of utest_averaging_analytical.py included in this branch tests the new averagers, and these also pass the tests. Note there are other minor changes made to the tests for compatibility with the new averagers.

Work still to be done

At a superficial level, there are some files which need renaming. The new version of manipulations.py goes by the name new_manipulations.py. Once everyone is satisfied with this new version, it should be renamed to replace the old one. There are references to new_manipulations in utest_averaging_analytical.py on the sasdata side, as well as in Plotter2D.py, AnnulusSlicer.py, BoxSlicer.py, BoxSum.py, WedgeSlicer.py, and SectorSlicer.py on the sasview side. These should also be changed accordingly.

There is also some more serious work to do before this rewrite can truly be considered complete. For the most part we've already reached feature parity with the old manipulations.py, but there are some blind spots which need acknowledging.

  1. In the old version of manipulations.py, the _Sector class had the option to bin the data logarithmically. As far as I can tell this functionality was never called upon by sasview. There may be users who use it in custom scripts however, so it would still be worth implementing here. As a bonus, the feature would now be available to all slicers.
  2. Currently, no attention is paid to the data's resolution function. In the old manipulations.py, CircularAverage and _Sector call a function by the name of get_dq_data. I don't quite understand the maths, but I get the impression from Lucas that it doesn't function as it should. The last thing needed to complete this rewrite would be proper consideration of the dqx_data and dqy_data properties of the supplied Data2D object to compute the dx property of the returned Data1D object. The maths involved here is a little outside my comfort zone, so I'd be happy to delegate this responsibility to someone else.
  3. I'd like to restructure the unit tests file. At the moment the tests are grouped in classes based on the averager they're testing, but if instead they were grouped by the kind of test being performed I could use unittest's setUp and tearDown methods to reduce the amount of repeated code. They also only test the averagers under expected conditions. Although the old tests were no more rigorous, I think it would be good write some tests for edge cases.

Review Checklist (please remove items if they don't apply):

  • Code has been reviewed
  • Functionality has been tested
  • Windows installer (GH artifact) has been tested (installed and worked)
  • MacOSX installer (GH artifact) has been tested (installed and worked)
  • User documentation is available and complete (if required)
  • Developers documentation is available and complete (if required)
  • The introduced changes comply with SasView license (BSD 3-Clause)

Copy link
Collaborator

@krzywon krzywon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code looks good. Locally, I am seeing two errors during testing related to zero division, but the ci succeeds, so likely a local environment issue. I think replacing the scipy requirement with something already included in the package would be good, but not a show-stopper.

Keeping the existing manipulations file is a good idea now that I've had a minute to think about it, but a deprecation message would be helpful. That is something that can be added later.

@lucas-wilkins
Copy link
Contributor

lucas-wilkins commented Sep 27, 2023 via email

@wpotrzebowski
Copy link

Required for 6.0.0

@wpotrzebowski
Copy link

@lucas-wilkins will take a look at it

Copy link
Collaborator

@krzywon krzywon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ready now, but I made changes, so I would suggest an alternate review prior to merging.

@lucas-wilkins
Copy link
Contributor

I've not checked the behaviour of the slicers, but I assume that is still all good.

@wpotrzebowski
Copy link

@smk78 will try to take a look

@smk78
Copy link
Contributor

smk78 commented Dec 6, 2023

Testing https://github.com/SasView/sasview/actions/runs/7079272736 on W10/x64:

After loading 2D datasets (one .dat, one .h5) & doing Create New > Right-click > Slicers, I see:

  1. Perform Circular Average:
    Nothing happens but you get an error:
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\Plotter2D.py", line 349, in onCircularAverage File "sas\qtgui\Plotting\Plotter2D.py", line 318, in circularAverage File "sasdata\data_util\averaging.py", line 585, in __call__ File "sasdata\data_util\averaging.py", line 91, in __init__ TypeError: Parameter 'nbins' must be an integer

But files (obviously) have integer numbers of bins.

  1. Sector (Q View):
    Sector plot appears. Slicer tool appears.
    Slicer tool can be rotated by mouse, but slicer width bars don't move. But, if you click on a box on a slicer width bar the tool stops responding but if you click in the 2D plot the sector plot updates, so I think there is something weird with the updating of the tool on the 2D plot?
    Also, there appears to be no feedback to Slicer Parameters box.
    Trying to use Slicer Parameters box to control this slicer does nothing and throws errors like this:
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\SlicerModel.py", line 66, in setParamsFromModelItem File "sas\qtgui\Plotting\Slicers\SectorSlicer.py", line 274, in setParams File "sas\qtgui\Plotting\Slicers\SectorSlicer.py", line 383, in update AttributeError: 'LineInteractor' object has no attribute 'alpha'
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\SlicerModel.py", line 56, in setParamsFromModelItem File "sas\qtgui\Plotting\Slicers\SectorSlicer.py", line 248, in getParams ValueError: Phi left and phi right are different 0.518482, -0.005688
  1. Annulus (Phi View)
    Annulus plot appears. Slicer tool appears.
    Slicer tool can be manipulated by mouse. Radius values update in the Slicer Parameters box. Slicer tool can be controlled from the Slicer Parameters box. Results look sensible.
    But, I do note that that the two rings of the tool can be dragged through each other, which is a bit confusing.
    And at some point this error popped up:
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\Plotter2D.py", line 349, in onCircularAverage File "sas\qtgui\Plotting\Plotter2D.py", line 318, in circularAverage File "sasdata\data_util\averaging.py", line 585, in __call__ File "sasdata\data_util\averaging.py", line 91, in __init__ TypeError: Parameter 'nbins' must be an integer
  1. Box Sum
    Box Sum box appears. Slicer tool appears.
    Slicer tool can be manipulated by mouse. Box size values and Box parameters update in the Slicer Parameters box. Until... you try and manipulate the tool from the Box Sum box. Nothing then happens and the tool stops working.
    Also, for the slicers that use the Slicer Parameters box there is an Apply button to action changes made in the Slicer Parameters box. The Box Sum box does not have an Apply button.

More to follow...

@smk78
Copy link
Contributor

smk78 commented Dec 6, 2023

  1. Box Averaging in Qx or Qy
    Box Average plot appears. Slicer tool appears. Slicer tool can be manipulated by mouse. Plot updates. Until... you try and manipulate the tool from the Slicer Parameters box. Nothing then happens and the tool stops working.
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\SlicerModel.py", line 66, in setParamsFromModelItem File "sas\qtgui\Plotting\Slicers\BoxSlicer.py", line 242, in setParams KeyError: 'qy_max'
ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\SlicerModel.py", line 66, in setParamsFromModelItem File "sas\qtgui\Plotting\Slicers\BoxSlicer.py", line 242, in setParams KeyError: 'qy_max'
  1. Wedge Averaging in Q
    Wedge Average plot appears. Slicer tool appears. Slicer tool can be manipulated by mouse. Plot updates. And... with this slicer you can control the tool from the Slicer Parameters box!

  2. Wedge Averaging in Phi
    Slicer tool appears but then throws an immediate error:

ERROR: Traceback (most recent call last): File "sas\qtgui\Plotting\Plotter2D.py", line 480, in onWedgeAveragingPhi File "sas\qtgui\Plotting\Plotter2D.py", line 399, in setSlicer File "sas\qtgui\Plotting\Slicers\WedgeSlicer.py", line 355, in __init__ File "sas\qtgui\Plotting\Slicers\WedgeSlicer.py", line 79, in __init__ File "sas\qtgui\Plotting\Slicers\WedgeSlicer.py", line 361, in _post_data File "sas\qtgui\Plotting\Slicers\WedgeSlicer.py", line 177, in _post_data File "sasdata\data_util\averaging.py", line 899, in __call__ ValueError: operands could not be broadcast together with shapes (82,) (100,) (82,)

This tool is also persistent; ie, once it has appeared, if you change the slicer it is not cleared on the plot.

Also, weirdly, the top 14% of the 2D image gets erased with this slicer!

  1. General point
    In the right-click context menu the Slicers are called:

image

But in the Slicer Parameters box they are called:

image

And the plot labels are:
(unable to generate plot for Circular Average)
SectorQ...
AnnulusPhi...
SlabX...
SlabY...
WedgeQ...
(unable to generate plot for Wedge Averaging in Phi)

I think some consistency of naming would be helpful.

Copy link
Contributor

@smk78 smk78 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have performed functionality review on W10/x64. Unfortunately, there are some issues. Please see comments on this PR.

@lucas-wilkins
Copy link
Contributor

Also, weirdly, the top 14% of the 2D image gets erased with this slicer!

Are you sure its not 18%

@lucas-wilkins
Copy link
Contributor

Note to @krzywon - I'm working on this now, that's why everything is failing. Will finish fixing soon.

@smk78
Copy link
Contributor

smk78 commented Dec 7, 2023

Also, weirdly, the top 14% of the 2D image gets erased with this slicer!

Are you sure its not 18%

I was going by the ticks on the axis! A seventh being ~14%.

@krzywon krzywon changed the base branch from master to release_0.9.0 December 19, 2023 15:41
ehewins and others added 24 commits October 27, 2025 14:01
@DrPaulSharp DrPaulSharp force-pushed the 46-manipulations-dot-py-rewrite branch from 03c45b5 to cc05ea7 Compare October 27, 2025 14:05
codescene-delta-analysis[bot]

This comment was marked as outdated.

@DrPaulSharp DrPaulSharp changed the base branch from release_0.9.0 to master October 27, 2025 14:10
Copy link

@codescene-delta-analysis codescene-delta-analysis bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gates Failed
New code is healthy (2 new files with code health below 9.00)
Enforce critical code health rules (1 file with Low Cohesion)

Gates Passed
1 Quality Gates Passed

See analysis details in CodeScene

Reason for failure
New code is healthy Violations Code Health Impact
utest_averaging_analytical.py 4 rules 6.29 Suppress
averaging.py 4 rules 6.99 Suppress
Enforce critical code health rules Violations Code Health Impact
utest_averaging_analytical.py 1 critical rule 6.29 Suppress

Quality Gate Profile: The Bare Minimum
Want more control? Customize Code Health rules or catch issues early with our IDE extension and CLI tool.

Comment on lines +166 to +185
def test_slabx_init(self):
"""
Test that SlabX's __init__ method does what it's supposed to.
"""
qx_min = 1
qx_max = 2
qy_min = 3
qy_max = 4
nbins = 100
fold = True

slab_object = SlabX(qx_min=qx_min, qx_max=qx_max, qy_min=qy_min,
qy_max=qy_max, nbins=nbins, fold=fold)

self.assertEqual(slab_object.qx_min, qx_min)
self.assertEqual(slab_object.qx_max, qx_max)
self.assertEqual(slab_object.qy_min, qy_min)
self.assertEqual(slab_object.qy_max, qy_max)
self.assertEqual(slab_object.nbins, nbins)
self.assertEqual(slab_object.fold, fold)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Code Duplication
The module contains 27 functions with similar structure: BoxavgTests.test_boxavg_init,BoxavgTests.test_boxavg_multiple_detectors,BoxavgTests.test_boxavg_subset_total,BoxavgTests.test_boxavg_total and 23 more functions

Suppress

Comment on lines +35 to +78
def __init__(self, data2d=None, err_data=None):
if data2d is not None:
matrix = np.asarray(data2d)
else:
msg = "Data must be supplied to convert to Data2D"
raise ValueError(msg)

if matrix.ndim != 2:
msg = "Supplied array must have 2 dimensions to convert to Data2D"
raise ValueError(msg)

if err_data is not None:
err_data = np.asarray(err_data)
if err_data.shape != matrix.shape:
msg = "Data and errors must have the same shape"
raise ValueError(msg)

# qmax can be any number, 1 just makes things simple.
self.qmax = 1
qx_bins = np.linspace(start=-1 * self.qmax,
stop=self.qmax,
num=matrix.shape[1],
endpoint=True)
qy_bins = np.linspace(start=-1 * self.qmax,
stop=self.qmax,
num=matrix.shape[0],
endpoint=True)

# Creating arrays in Data2D's preferred format.
data2d = matrix.flatten()
if err_data is None or np.any(err_data <= 0):
# Error data of some kind is needed, so we fabricate some
err_data = np.sqrt(np.abs(data2d)) # TODO - use different approach
else:
err_data = err_data.flatten()
qx_data = np.tile(qx_bins, (len(qy_bins), 1)).flatten()
qy_data = np.tile(qy_bins, (len(qx_bins), 1)).swapaxes(0, 1).flatten()
q_data = np.sqrt(qx_data * qx_data + qy_data * qy_data)
mask = np.ones(len(data2d), dtype=bool)

# Creating a Data2D object to use for testing the averagers.
self.data = data_info.Data2D(data=data2d, err_data=err_data,
qx_data=qx_data, qy_data=qy_data,
q_data=q_data, mask=mask)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Complex Method
MatrixToData2D.init has a cyclomatic complexity of 12, threshold = 9

Suppress

Comment on lines +276 to +293
def __init__(self, qx_min: float = 0, qx_max: float = 0,
qy_min: float = 0, qy_max: float = 0) -> None:
"""
Assign the variables used to label the properties of the Data2D object.
Also establish the upper and lower bounds defining the ROI.
The units of these parameters are A^-1
:param qx_min: Lower bound of the ROI along the Q_x direction.
:param qx_max: Upper bound of the ROI along the Q_x direction.
:param qy_min: Lower bound of the ROI along the Q_y direction.
:param qy_max: Upper bound of the ROI along the Q_y direction.
"""

super().__init__()
self.qx_min = qx_min
self.qx_max = qx_max
self.qy_min = qy_min
self.qy_max = qy_max

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Code Duplication
The module contains 14 functions with similar structure: Boxavg.init,Boxsum.init,CartesianROI.init,CircularAverage.call and 10 more functions

Suppress

Comment on lines +66 to +122
def __init__(self,
major_axis: ArrayLike,
minor_axis: ArrayLike,
major_lims: tuple[float, float] | None = None,
minor_lims: tuple[float, float] | None = None,
nbins: int = 100):
"""
Set up direction of averaging, limits on the ROI, & the number of bins.
:param major_axis: Coordinate data for axis onto which the 2D data is
projected.
:param minor_axis: Coordinate data for the axis perpendicular to the
major axis.
:param major_lims: Lower and upper bounds of the ROI along the major
axis. Given as a 2 element tuple/list.
:param minor_lims: Lower and upper bounds of the ROI along the minor
axis. Given as a 2 element tuple/list.
:param nbins: The number of bins the major axis is divided up into.
"""

if any(not hasattr(coordinate_data, "__array__") for
coordinate_data in (major_axis, minor_axis)):
msg = "Must provide major & minor coordinate arrays for binning."
raise ValueError(msg)

if any(lims is not None and len(lims) != 2 for
lims in (major_lims, minor_lims)):
msg = "Limits arrays must have 2 elements or be NoneType"
raise ValueError(msg)

if not isinstance(nbins, int):
# TODO: Make classes that depend on this provide ints, its quite a thing to fix though
try:
nbins = int(nbins)
except:
msg = f"Parameter 'nbins' must should be convertable to an integer via int(), got type {type(nbins)} (={nbins})"
raise TypeError(msg)

self.major_axis = np.asarray(major_axis)
self.minor_axis = np.asarray(minor_axis)
if self.major_axis.size != self.minor_axis.size:
msg = "Major and minor axes must have same length"
raise ValueError(msg)
# In some cases all values from a given axis are part of the ROI.
# An alternative approach may be needed for fractional weights.
if major_lims is None:
self.major_lims = (self.major_axis.min(), self.major_axis.max())
else:
self.major_lims = major_lims
if minor_lims is None:
self.minor_lims = (self.minor_axis.min(), self.minor_axis.max())
else:
self.minor_lims = minor_lims
self.nbins = nbins
# Assume a linear spacing for now, but allow for log, fibonacci, etc. implementations in the future
# Add one to bin because this is for the limits, not centroids.
self.bin_limits = np.linspace(self.major_lims[0], self.major_lims[1], self.nbins + 1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Complex Method
DirectionalAverage.init has a cyclomatic complexity of 17, threshold = 9

Suppress

Comment on lines +66 to +122
def __init__(self,
major_axis: ArrayLike,
minor_axis: ArrayLike,
major_lims: tuple[float, float] | None = None,
minor_lims: tuple[float, float] | None = None,
nbins: int = 100):
"""
Set up direction of averaging, limits on the ROI, & the number of bins.
:param major_axis: Coordinate data for axis onto which the 2D data is
projected.
:param minor_axis: Coordinate data for the axis perpendicular to the
major axis.
:param major_lims: Lower and upper bounds of the ROI along the major
axis. Given as a 2 element tuple/list.
:param minor_lims: Lower and upper bounds of the ROI along the minor
axis. Given as a 2 element tuple/list.
:param nbins: The number of bins the major axis is divided up into.
"""

if any(not hasattr(coordinate_data, "__array__") for
coordinate_data in (major_axis, minor_axis)):
msg = "Must provide major & minor coordinate arrays for binning."
raise ValueError(msg)

if any(lims is not None and len(lims) != 2 for
lims in (major_lims, minor_lims)):
msg = "Limits arrays must have 2 elements or be NoneType"
raise ValueError(msg)

if not isinstance(nbins, int):
# TODO: Make classes that depend on this provide ints, its quite a thing to fix though
try:
nbins = int(nbins)
except:
msg = f"Parameter 'nbins' must should be convertable to an integer via int(), got type {type(nbins)} (={nbins})"
raise TypeError(msg)

self.major_axis = np.asarray(major_axis)
self.minor_axis = np.asarray(minor_axis)
if self.major_axis.size != self.minor_axis.size:
msg = "Major and minor axes must have same length"
raise ValueError(msg)
# In some cases all values from a given axis are part of the ROI.
# An alternative approach may be needed for fractional weights.
if major_lims is None:
self.major_lims = (self.major_axis.min(), self.major_axis.max())
else:
self.major_lims = major_lims
if minor_lims is None:
self.minor_lims = (self.minor_axis.min(), self.minor_axis.max())
else:
self.minor_lims = minor_lims
self.nbins = nbins
# Assume a linear spacing for now, but allow for log, fibonacci, etc. implementations in the future
# Add one to bin because this is for the limits, not centroids.
self.bin_limits = np.linspace(self.major_lims[0], self.major_lims[1], self.nbins + 1)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
DirectionalAverage.init has 5 arguments, max arguments = 4

Suppress

Comment on lines +446 to +463
def __init__(self, qx_min: float = 0, qx_max: float = 0, qy_min: float = 0,
qy_max: float = 0, nbins: int = 100, fold: bool = False):
"""
Set up the ROI boundaries, the binning of the output 1D data, and fold.
The units of these parameters are A^-1
:param qx_min: Lower bound of the ROI along the Q_x direction.
:param qx_max: Upper bound of the ROI along the Q_x direction.
:param qy_min: Lower bound of the ROI along the Q_y direction.
:param qy_max: Upper bound of the ROI along the Q_y direction.
:param nbins: The number of bins data is sorted into along Q_x.
:param fold: Whether the two halves of the ROI along Q_x should be
folded together during averaging.
"""
super().__init__(qx_min=qx_min, qx_max=qx_max,
qy_min=qy_min, qy_max=qy_max)
self.nbins = nbins
self.fold = fold

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
SlabX.init has 6 arguments, max arguments = 4

Suppress

Comment on lines +510 to +527
def __init__(self, qx_min: float = 0, qx_max: float = 0, qy_min: float = 0,
qy_max: float = 0, nbins: int = 100, fold: bool = False):
"""
Set up the ROI boundaries, the binning of the output 1D data, and fold.
The units of these parameters are A^-1
:param qx_min: Lower bound of the ROI along the Q_x direction.
:param qx_max: Upper bound of the ROI along the Q_x direction.
:param qy_min: Lower bound of the ROI along the Q_y direction.
:param qy_max: Upper bound of the ROI along the Q_y direction.
:param nbins: The number of bins data is sorted into along Q_y.
:param fold: Whether the two halves of the ROI along Q_y should be
folded together during averaging.
"""
super().__init__(qx_min=qx_min, qx_max=qx_max,
qy_min=qy_min, qy_max=qy_max)
self.nbins = nbins
self.fold = fold

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
SlabY.init has 6 arguments, max arguments = 4

Suppress

Comment on lines +675 to +693
def __init__(self, r_min: float, r_max: float, phi_min: float,
phi_max: float, nbins: int = 100, fold: bool = True) -> None:
"""
Set up the ROI boundaries, the binning of the output 1D data, and fold.
The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_min: Lower limit for |Q| values to use during averaging.
:param r_max: Upper limit for |Q| values to use during averaging.
:param phi_min: Lower limit for φ values (in the primary ROI).
:param phi_max: Upper limit for φ values (in the primary ROI).
:param nbins: The number of bins data is sorted into along the |Q| axis
:param fold: Whether the primary and secondary ROIs should be folded
together during averaging.
"""
super().__init__(r_min=r_min, r_max=r_max,
phi_min=phi_min, phi_max=phi_max)

self.nbins = nbins
self.fold = fold

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
SectorQ.init has 6 arguments, max arguments = 4

Suppress

Comment on lines +790 to +804
def __init__(self, r_min: float, r_max: float, phi_min: float,
phi_max: float, nbins: int = 100) -> None:
"""
Set up the ROI boundaries, and the binning of the output 1D data.
The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_min: Lower limit for |Q| values to use during averaging.
:param r_max: Upper limit for |Q| values to use during averaging.
:param phi_min: Lower limit for φ values (in the primary ROI).
:param phi_max: Upper limit for φ values (in the primary ROI).
:param nbins: The number of bins data is sorted into along the |Q| axis
"""
super().__init__(r_min=r_min, r_max=r_max,
phi_min=phi_min, phi_max=phi_max)
self.nbins = nbins

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
WedgeQ.init has 5 arguments, max arguments = 4

Suppress

Comment on lines +856 to +870
def __init__(self, r_min: float, r_max: float, phi_min: float,
phi_max: float, nbins: int = 100) -> None:
"""
Set up the ROI boundaries, and the binning of the output 1D data.
The units are A^-1 for radial parameters, and radians for anglar ones.
:param r_min: Lower limit for |Q| values to use during averaging.
:param r_max: Upper limit for |Q| values to use during averaging.
:param phi_min: Lower limit for φ values to use during averaging.
:param phi_max: Upper limit for φ values to use during averaging.
:param nbins: The number of bins data is sorted into along the φ axis.
"""
super().__init__(r_min=r_min, r_max=r_max,
phi_min=phi_min, phi_max=phi_max)
self.nbins = nbins

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ New issue: Excess Number of Function Arguments
WedgePhi.init has 5 arguments, max arguments = 4

Suppress

@DrPaulSharp DrPaulSharp changed the title Finishing the manipulatons.py Rewrite Finishing the manipulations.py Rewrite Oct 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Plan to Restructure/Rewrite manipulations.py Reworking the Slicer Unit Tests _Sector method can't average reliably for circular regions.

8 participants