Skip to content

Commit 32fc311

Browse files
authored
Merge pull request #2552 from cta-observatory/fix_stereo_trigger_prod6
Fix StereoTrigger non-deterministically discarding LST-1 in prod6 files
2 parents 3132584 + 358955c commit 32fc311

File tree

4 files changed

+86
-10
lines changed

4 files changed

+86
-10
lines changed

docs/changes/2552.bugfix.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Fix ``SoftwareTrigger`` not correctly handling different telescope
2+
types that have the same string representation, e.g. the four LSTs
3+
in prod6 files.
4+
5+
Telescopes that have the same string representation now always are treated
6+
as one group in ``SoftwareTrigger``.

src/ctapipe/instrument/tests/test_trigger.py

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import json
2+
from copy import deepcopy
23

34
import numpy as np
45
import pytest
56
from numpy.testing import assert_equal
67

78
from ctapipe.containers import ArrayEventContainer
9+
from ctapipe.instrument import SubarrayDescription
810
from ctapipe.io import EventSource
911

1012

@@ -235,3 +237,68 @@ def test_software_trigger_simtel_process(tmp_path):
235237
)
236238

237239
assert len(events_no_trigger) > len(events_trigger)
240+
241+
242+
def test_different_telescope_type_same_string(subarray_prod5_paranal):
243+
"""
244+
Test that for a subarray that contains two different telescope types
245+
with the same string representation, the subarray trigger is working
246+
as expected, treating these telescopes as a single group.
247+
"""
248+
from ctapipe.instrument.trigger import SoftwareTrigger
249+
250+
lst234 = deepcopy(subarray_prod5_paranal.tel[2])
251+
# make LST-1 slightly different from LSTs 2-4
252+
lst1 = deepcopy(subarray_prod5_paranal.tel[1])
253+
lst1.optics.mirror_area *= 0.9
254+
255+
assert lst1 != lst234
256+
257+
subarray = SubarrayDescription(
258+
name="test",
259+
tel_positions={
260+
tel_id: subarray_prod5_paranal.positions[tel_id] for tel_id in (1, 2, 3, 4)
261+
},
262+
tel_descriptions={
263+
1: lst1,
264+
2: lst234,
265+
3: lst234,
266+
4: lst234,
267+
},
268+
reference_location=subarray_prod5_paranal.reference_location,
269+
)
270+
271+
trigger = SoftwareTrigger(
272+
subarray=subarray,
273+
min_telescopes=2,
274+
min_telescopes_of_type=[
275+
("type", "*", 0),
276+
("type", "LST*", 2),
277+
],
278+
)
279+
280+
# all four LSTs, nothing to change
281+
event = ArrayEventContainer()
282+
event.trigger.tels_with_trigger = [1, 2, 3, 4]
283+
assert trigger(event)
284+
assert_equal(event.trigger.tels_with_trigger, [1, 2, 3, 4])
285+
286+
# two LSTs, nothing to change
287+
event = ArrayEventContainer()
288+
event.trigger.tels_with_trigger = [1, 2]
289+
assert trigger(event)
290+
assert_equal(event.trigger.tels_with_trigger, [1, 2])
291+
292+
# two LSTs, nothing to change
293+
event = ArrayEventContainer()
294+
event.trigger.tels_with_trigger = [1, 3]
295+
assert trigger(event)
296+
assert_equal(event.trigger.tels_with_trigger, [1, 3])
297+
298+
# one LST, remove
299+
event = ArrayEventContainer()
300+
event.trigger.tels_with_trigger = [
301+
1,
302+
]
303+
assert not trigger(event)
304+
assert_equal(event.trigger.tels_with_trigger, [])

src/ctapipe/instrument/trigger.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,16 @@ class SoftwareTrigger(TelescopeComponent):
5959
def __init__(self, subarray, *args, **kwargs):
6060
super().__init__(subarray, *args, **kwargs)
6161

62-
self._ids_by_type = {
63-
str(type): set(self.subarray.get_tel_ids_for_type(type))
64-
for type in self.subarray.telescope_types
65-
}
62+
# we are grouping telescopes by the str repr of the type
63+
# this is needed since e.g. in prod6, LST-1 is slightly different
64+
# from LST-2 to LST-4, but we still want the trigger to work with all
65+
# LSTs
66+
self._ids_by_type = {}
67+
for tel in self.subarray.telescope_types:
68+
tel_str = str(tel)
69+
if tel_str not in self._ids_by_type:
70+
self._ids_by_type[tel_str] = set()
71+
self._ids_by_type[tel_str].update(self.subarray.get_tel_ids_for_type(tel))
6672

6773
def __call__(self, event: ArrayEventContainer) -> bool:
6874
"""
@@ -79,24 +85,22 @@ def __call__(self, event: ArrayEventContainer) -> bool:
7985
"""
8086

8187
tels_removed = set()
82-
for tel_type in self.subarray.telescope_types:
83-
tel_type_str = str(tel_type)
84-
min_tels = self.min_telescopes_of_type.tel[tel_type_str]
88+
for tel_type, tel_ids in self._ids_by_type.items():
89+
min_tels = self.min_telescopes_of_type.tel[tel_type]
8590

8691
# no need to check telescopes for which we have no min requirement
8792
if min_tels == 0:
8893
continue
8994

9095
tels_with_trigger = set(event.trigger.tels_with_trigger)
91-
tel_ids = self._ids_by_type[tel_type_str]
9296
tels_in_event = tels_with_trigger.intersection(tel_ids)
9397

9498
if len(tels_in_event) < min_tels:
9599
for tel_id in tels_in_event:
96100
self.log.debug(
97101
"Removing tel_id %d of type %s from event due to type requirement",
98102
tel_id,
99-
tel_type_str,
103+
tel_type,
100104
)
101105

102106
# remove from tels_with_trigger

src/ctapipe/tools/process.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,6 @@ def start(self):
301301
disable=not self.progress_bar,
302302
):
303303
self.log.debug("Processing event_id=%s", event.index.event_id)
304-
305304
if not self.event_type_filter(event):
306305
continue
307306

0 commit comments

Comments
 (0)