From 67d07a087efd3fc28a6fea43ac888dbe9a336bda Mon Sep 17 00:00:00 2001 From: Neil Vaytet Date: Mon, 4 May 2026 16:27:49 +0200 Subject: [PATCH 1/4] allow 0 frequency choppers --- src/tof/chopper.py | 9 ++++++++- tests/chopper_test.py | 14 ++++++++++++++ tests/model_test.py | 25 +++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/tof/chopper.py b/src/tof/chopper.py index 0d56a3f..f6c1f00 100644 --- a/src/tof/chopper.py +++ b/src/tof/chopper.py @@ -142,7 +142,7 @@ def __init__( widths: sc.Variable | None = None, direction: Direction = Clockwise, ): - if frequency <= (0.0 * frequency.unit): + if frequency < (0.0 * frequency.unit): raise ValueError(f"Chopper frequency must be positive, got {frequency:c}.") self.frequency = frequency.to(dtype=float, copy=False) if direction not in (Clockwise, AntiClockwise): @@ -208,6 +208,13 @@ def open_close_times( time_limit = sc.scalar(0.0, unit='us') if unit is None: unit = time_limit.unit + + if self.frequency.value == 0.0: + return ( + sc.array(dims=['cutout'], values=[-np.inf], unit=unit), + sc.array(dims=['cutout'], values=[np.inf], unit=unit), + ) + nrot = max(int(sc.ceil((time_limit * self.frequency).to(unit='')).value), 1) # We make a unique dim name that is different from self.open.dim and # self.close.dim to make use of automatic broadcasting below. diff --git a/tests/chopper_test.py b/tests/chopper_test.py index 02742f9..875b87c 100644 --- a/tests/chopper_test.py +++ b/tests/chopper_test.py @@ -688,3 +688,17 @@ def test_from_nexus_clockwise(): nexus_chopper['slit_edges'][1::2] - nexus_chopper['beam_position'], ) assert chopper.direction == tof.Clockwise + + +def test_chopper_zero_frequency(): + chopper = tof.Chopper( + frequency=0.0 * Hz, + open=10.0 * deg, + close=20.0 * deg, + phase=0.0 * deg, + distance=5.0 * meter, + name='chopper', + ) + topen, tclose = chopper.open_close_times(0.0 * sec) + assert sc.identical(topen[0], -np.inf * sec) + assert sc.identical(tclose[0], np.inf * sec) diff --git a/tests/model_test.py b/tests/model_test.py index 15c8733..c072e63 100644 --- a/tests/model_test.py +++ b/tests/model_test.py @@ -697,3 +697,28 @@ def test_model_run_without_components_raises(dummy_source): ValueError, match="Cannot run model: no components have been defined." ): model.run() + + +def test_chopper_with_zero_frequency_blocks_nothing(make_source): + chopper = tof.Chopper( + frequency=0.0 * Hz, + open=10.0 * deg, + close=20.0 * deg, + phase=0.0 * deg, + distance=5.0 * meter, + name='chopper', + ) + detector = tof.Detector(distance=20 * meter, name='detector') + + N = 10_000 + + source = tof.Source(facility='ess', neutrons=N) + model = tof.Model(source=source, choppers=[chopper], detectors=[detector]) + res = model.run() + + assert res.choppers['chopper'].toa.data.sum().value == N + assert res.choppers['chopper'].toa.data.masks['blocked_by_me'].sum().value == 0 + assert res.detectors['detector'].toa.data.sum().value == N + assert ( + res.detectors['detector'].toa.data.masks['blocked_by_others'].sum().value == 0 + ) From 13e9d9cd3b7de2478185ac4f152ab70c4ae49ce0 Mon Sep 17 00:00:00 2001 From: Neil Vaytet <39047984+nvaytet@users.noreply.github.com> Date: Tue, 5 May 2026 08:40:07 +0200 Subject: [PATCH 2/4] Update src/tof/chopper.py Co-authored-by: Simon Heybrock <12912489+SimonHeybrock@users.noreply.github.com> --- src/tof/chopper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tof/chopper.py b/src/tof/chopper.py index f6c1f00..d894633 100644 --- a/src/tof/chopper.py +++ b/src/tof/chopper.py @@ -143,7 +143,7 @@ def __init__( direction: Direction = Clockwise, ): if frequency < (0.0 * frequency.unit): - raise ValueError(f"Chopper frequency must be positive, got {frequency:c}.") + raise ValueError(f"Chopper frequency must be non-negative, got {frequency:c}.") self.frequency = frequency.to(dtype=float, copy=False) if direction not in (Clockwise, AntiClockwise): raise ValueError( From 78b3aab8f48faf68f00944ed123b4dfbbcb856f8 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, 5 May 2026 06:40:38 +0000 Subject: [PATCH 3/4] Apply automatic formatting --- src/tof/chopper.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tof/chopper.py b/src/tof/chopper.py index d894633..57a654c 100644 --- a/src/tof/chopper.py +++ b/src/tof/chopper.py @@ -143,7 +143,9 @@ def __init__( direction: Direction = Clockwise, ): if frequency < (0.0 * frequency.unit): - raise ValueError(f"Chopper frequency must be non-negative, got {frequency:c}.") + raise ValueError( + f"Chopper frequency must be non-negative, got {frequency:c}." + ) self.frequency = frequency.to(dtype=float, copy=False) if direction not in (Clockwise, AntiClockwise): raise ValueError( From 10814c68d9c78931bb49f22aca12bb2bcd8fdb31 Mon Sep 17 00:00:00 2001 From: Neil Vaytet Date: Tue, 5 May 2026 08:45:56 +0200 Subject: [PATCH 4/4] fix error message --- tests/chopper_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/chopper_test.py b/tests/chopper_test.py index 875b87c..4619e2c 100644 --- a/tests/chopper_test.py +++ b/tests/chopper_test.py @@ -149,7 +149,7 @@ def test_phase_int(): def test_frequency_must_be_positive(): - with pytest.raises(ValueError, match="Chopper frequency must be positive"): + with pytest.raises(ValueError, match="Chopper frequency must be non-negative"): tof.Chopper( frequency=-1.0 * Hz, open=0.0 * deg,