diff --git a/src/tof/chopper.py b/src/tof/chopper.py index 19306eb..2c22c35 100644 --- a/src/tof/chopper.py +++ b/src/tof/chopper.py @@ -29,10 +29,6 @@ class Chopper: ---------- frequency: The frequency of the chopper. Must be positive. - open: - The opening angles of the chopper cutouts. - close: - The closing angles of the chopper cutouts. distance: The distance from the source to the chopper. phase: @@ -41,17 +37,32 @@ class Chopper: to the chopper rotation direction. For example, if the chopper rotates clockwise, a phase of 10 degrees will shift all window angles by 10 degrees in the anticlockwise direction, which will result in the windows opening later. + open: + The opening angles of the chopper cutouts. + close: + The closing angles of the chopper cutouts. + center: + The center of the chopper cutouts. + width: + The width of the chopper cutouts. name: The name of the chopper. + + Notes + ----- + Either `open` and `close` or `center` and `width` must be provided, but not both. """ def __init__( self, + *, frequency: sc.Variable, - open: sc.Variable, - close: sc.Variable, distance: sc.Variable, phase: sc.Variable, + open: sc.Variable | None = None, + close: sc.Variable | None = None, + center: sc.Variable | None = None, + width: sc.Variable | None = None, direction: Direction = Clockwise, name: str = "", ): @@ -64,6 +75,20 @@ def __init__( f", got {direction}." ) self.direction = direction + # Check that either open/close or center/width are provided, but not both + if tuple(x for x in (open, close, center, width) if x is not None) not in ( + (open, close), + (center, width), + ): + raise ValueError( + "Either open/close or center/width must be provided, got" + f" open={open}, close={close}, center={center}, width={width}." + ) + if open is None: + half_width = width * 0.5 + open = center - half_width + close = center + half_width + self.open = (open if open.dims else open.flatten(to='cutout')).to( dtype=float, copy=False ) diff --git a/tests/chopper_test.py b/tests/chopper_test.py index add8a31..c81a970 100644 --- a/tests/chopper_test.py +++ b/tests/chopper_test.py @@ -291,3 +291,43 @@ def test_bad_direction_raises(): distance=d, direction=1, ) + + +def test_chopper_create_from_centers_and_widths(): + f = 10.0 * Hz + center = sc.array(dims=['cutout'], values=[15.0, 46.0], unit='deg') + width = sc.array(dims=['cutout'], values=[10.0, 16.0], unit='deg') + chopper = tof.Chopper( + frequency=f, + center=center, + width=width, + phase=0.0 * deg, + distance=5.0 * meter, + ) + assert sc.allclose(chopper.open, center - width / 2) + assert sc.allclose(chopper.close, center + width / 2) + + expected = tof.Chopper( + frequency=f, + open=sc.array(dims=['cutout'], values=[10.0, 38.0], unit='deg'), + close=sc.array(dims=['cutout'], values=[20.0, 54.0], unit='deg'), + phase=0.0 * deg, + distance=5.0 * meter, + ) + assert sc.allclose(chopper.open, expected.open) + assert sc.allclose(chopper.close, expected.close) + + +def test_chopper_create_raises_when_both_edges_and_centers_are_supplied(): + with pytest.raises( + ValueError, match="Either open/close or center/width must be provided" + ): + tof.Chopper( + frequency=10.0 * Hz, + open=10.0 * deg, + close=20.0 * deg, + center=15.0 * deg, + width=10.0 * deg, + phase=0.0 * deg, + distance=5.0 * meter, + )