Skip to content

Commit

Permalink
Merge pull request #65 from cta-observatory/hst
Browse files Browse the repository at this point in the history
Introducing hardware stereo trigger
  • Loading branch information
aleberti authored Jul 3, 2023
2 parents 4f1af39 + 4c00bfd commit 46994db
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 23 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,4 @@ jobs:
run: |
pytest --cov=ctapipe_io_magic --cov-report=xml
- uses: codecov/codecov-action@v1
- uses: codecov/codecov-action@v3
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ conda activate ctapipe-io_magic
pip install .
```

### Test Data
#### Test Data

To run the tests, a set of non-public files is needed. If you are a member of MAGIC, ask one of the project maintainers for the credentials and then run:

Expand Down
129 changes: 115 additions & 14 deletions ctapipe_io_magic/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
DATA_MONO_SUMT_TRIGGER_PATTERN,
PEDESTAL_TRIGGER_PATTERN,
DATA_STEREO_TRIGGER_PATTERN,
DATA_TOPOLOGICAL_TRIGGER,
DATA_MAGIC_LST_TRIGGER,
)

__all__ = ["MAGICEventSource", "MARSDataLevel", "__version__"]
Expand All @@ -74,6 +76,8 @@
DATA_STEREO_TRIGGER_PATTERN: EventType.SUBARRAY,
DATA_MONO_SUMT_TRIGGER_PATTERN: EventType.SUBARRAY,
PEDESTAL_TRIGGER_PATTERN: EventType.SKY_PEDESTAL,
DATA_TOPOLOGICAL_TRIGGER: EventType.SUBARRAY,
DATA_MAGIC_LST_TRIGGER: EventType.SUBARRAY,
}


Expand Down Expand Up @@ -208,7 +212,7 @@ def __init__(self, input_url=None, config=None, parent=None, **kwargs):
if self.is_simulation:
self._simulation_config = self.parse_simulation_header()

self.is_stereo, self.is_sumt = self.parse_data_info()
self.is_stereo, self.is_sumt, self.is_hast = self.parse_data_info()

if self.is_simulation and self.use_mc_mono_events and not self.is_stereo:
logger.warning(
Expand Down Expand Up @@ -515,11 +519,13 @@ def parse_data_info(self):

is_stereo = []
is_sumt = []
is_hast = []

if not self.is_simulation:
prescaler_mono_nosumt = [1, 1, 0, 1, 0, 0, 0, 0]
prescaler_mono_sumt = [0, 1, 0, 1, 0, 1, 0, 0]
prescaler_stereo = [0, 1, 0, 1, 0, 0, 0, 1]
prescaler_hast = [0, 1, 1, 1, 0, 0, 0, 1]

# L1_table_mono = "L1_4NN"
# L1_table_stereo = "L1_3NN"
Expand All @@ -543,7 +549,8 @@ def parse_data_info(self):
)
stereo = True
sumt = False
return stereo, sumt
hast = False
return stereo, sumt, hast

prescaler_size = prescaler_array.size
if prescaler_size > 1:
Expand All @@ -556,10 +563,16 @@ def parse_data_info(self):
or prescaler == prescaler_mono_sumt
):
stereo = False
hast = False
elif prescaler == prescaler_stereo:
stereo = True
hast = False
elif prescaler == prescaler_hast:
stereo = True
hast = True
else:
stereo = True
hast = False

sumt = False
if stereo:
Expand Down Expand Up @@ -587,6 +600,7 @@ def parse_data_info(self):

is_stereo.append(stereo)
is_sumt.append(sumt)
is_hast.append(hast)

else:
for rootf in self.files_:
Expand Down Expand Up @@ -615,9 +629,11 @@ def parse_data_info(self):
stereo = True

is_stereo.append(stereo)
is_hast.append(False)

is_stereo = np.unique(is_stereo).tolist()
is_sumt = np.unique(is_sumt).tolist()
is_hast = np.unique(is_hast).tolist()

if len(is_stereo) > 1:
raise ValueError(
Expand All @@ -631,7 +647,13 @@ def parse_data_info(self):
not an issue, check that this is what you really want to do."
)

return is_stereo[0], is_sumt[0]
if len(is_hast) > 1:
logger.warning(
"Found data with both stereo and hardware stereo trigger. While this is \
not an issue, check that this is what you really want to do."
)

return is_stereo[0], is_sumt[0], is_hast[0]

def prepare_subarray_info(self):
"""
Expand Down Expand Up @@ -1085,10 +1107,27 @@ def get_event_time_difference(self):

time_diffs = np.array([])

data_trigger_pattern = DATA_STEREO_TRIGGER_PATTERN
if not self.is_stereo:
if self.is_sumt:
data_trigger_pattern = DATA_MONO_SUMT_TRIGGER_PATTERN
else:
data_trigger_pattern = DATA_MONO_TRIGGER_PATTERN
if self.is_hast:
event_cut = (
f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})"
f" | (MTriggerPattern.fPrescaled == {DATA_TOPOLOGICAL_TRIGGER})"
f" | (MTriggerPattern.fPrescaled == {DATA_MAGIC_LST_TRIGGER})"
)
else:
event_cut = (
f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})",
)

for uproot_file in self.files_:
event_info = uproot_file["Events"].arrays(
expressions=["MRawEvtHeader.fTimeDiff"],
cut=f"(MTriggerPattern.fPrescaled == {DATA_STEREO_TRIGGER_PATTERN})",
cut=event_cut,
library="np",
)

Expand Down Expand Up @@ -1239,6 +1278,7 @@ def _set_active_run(self, uproot_file):
uproot_file,
self.is_simulation,
self.is_stereo,
self.is_hast,
self.use_mc_mono_events,
self.is_sumt,
)
Expand Down Expand Up @@ -1277,7 +1317,6 @@ def _event_generator(self):
event.index.obs_id = self.obs_ids[0]

tel_id = self.telescope
event.trigger.tels_with_trigger = [tel_id]

counter = 0

Expand Down Expand Up @@ -1380,6 +1419,39 @@ def _event_generator(self):
# Loop over the events:
for i_event in range(n_events):
event.count = counter

if not self.is_simulation:
if (
event_data["trigger_pattern"][i_event]
== DATA_STEREO_TRIGGER_PATTERN
):
event.trigger.tels_with_trigger = [1, 2]
elif (
event_data["trigger_pattern"][i_event]
== DATA_TOPOLOGICAL_TRIGGER
):
event.trigger.tels_with_trigger = [tel_id, 3]
elif (
event_data["trigger_pattern"][i_event] == DATA_MAGIC_LST_TRIGGER
):
event.trigger.tels_with_trigger = [1, 2, 3]
else:
event.trigger.tels_with_trigger = [tel_id]
else:
if self.is_stereo and not self.use_mc_mono_events:
event.trigger.tels_with_trigger = [1, 2]
else:
event.trigger.tels_with_trigger = [tel_id]

if self.allowed_tels:
tels_with_trigger = np.intersect1d(
event.trigger.tels_with_trigger,
self.subarray.tel_ids,
assume_unique=True,
)

event.trigger.tels_with_trigger = tels_with_trigger.tolist()

event.index.event_id = event_data["event_number"][i_event]

event.trigger.event_type = MAGIC_TO_CTA_EVENT_TYPE.get(
Expand Down Expand Up @@ -1489,6 +1561,7 @@ def __init__(
uproot_file,
is_mc,
is_stereo,
is_hast,
use_mc_mono_events,
use_sumt_events,
n_cam_pixels=1039,
Expand All @@ -1515,6 +1588,7 @@ def __init__(
self.use_mc_mono_events = use_mc_mono_events
self.use_sumt_events = use_sumt_events
self.n_cam_pixels = n_cam_pixels
self.is_hast = is_hast

# Load the input data:
calib_data = self._load_data()
Expand Down Expand Up @@ -1627,9 +1701,16 @@ def _load_data(self):
data_trigger_pattern = DATA_MONO_SUMT_TRIGGER_PATTERN
else:
data_trigger_pattern = DATA_MONO_TRIGGER_PATTERN
events_cut[
"cosmic_events"
] = f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})"
if self.is_hast:
events_cut["cosmic_events"] = (
f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})"
f" | (MTriggerPattern.fPrescaled == {DATA_TOPOLOGICAL_TRIGGER})"
f" | (MTriggerPattern.fPrescaled == {DATA_MAGIC_LST_TRIGGER})"
)
else:
events_cut[
"cosmic_events"
] = f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})"
# Only for cosmic events because MC data do not have pedestal events:
events_cut[
"pedestal_events"
Expand Down Expand Up @@ -1686,10 +1767,29 @@ def _load_data(self):
)
logger.info("Using fDAQEvtNumber to generate event IDs.")
else:
calib_data[event_type]["event_number"] = np.array(
common_info["MRawEvtHeader.fStereoEvtNumber"], dtype=int
)
logger.info("Using fStereoEvtNumber to generate event IDs.")
if self.is_hast:
subrun_id = self.uproot_file["RunHeaders"][
"MRawRunHeader.fSubRunIndex"
].array(library="np")[0]
stereo_ids = common_info["MRawEvtHeader.fStereoEvtNumber"]
daq_ids = common_info["MRawEvtHeader.fDAQEvtNumber"]
calib_data[event_type]["event_number"] = np.array(
[
f"{subrun_id}{daq_ids[event_idx]:07}"
if common_info["MTriggerPattern.fPrescaled"][event_idx]
== DATA_TOPOLOGICAL_TRIGGER
else stereo_ids[event_idx]
for event_idx in range(
common_info["MTriggerPattern.fPrescaled"].size
)
],
dtype=int,
)
else:
calib_data[event_type]["event_number"] = np.array(
common_info["MRawEvtHeader.fStereoEvtNumber"], dtype=int
)
logger.info("Using fStereoEvtNumber to generate event IDs.")

# Set pixel-wise charge and peak time information.
# The length of the pixel array is 1183, but here only the first part of the pixel
Expand Down Expand Up @@ -1911,8 +2011,9 @@ def _load_data(self):
# Check for bit flips in the stereo event IDs:
uplim_total_jumps = 100

stereo_event_number = calib_data["cosmic_events"][
"event_number"
stereo_event_number = calib_data["cosmic_events"]["event_number"][
calib_data["cosmic_events"]["trigger_pattern"]
== DATA_STEREO_TRIGGER_PATTERN
].astype(int)
number_difference = np.diff(stereo_event_number)

Expand Down
5 changes: 5 additions & 0 deletions ctapipe_io_magic/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@
MC_SUMT_TRIGGER_PATTERN = 32
# also for data taken in stereo with SumTrigger
DATA_STEREO_TRIGGER_PATTERN = 128
# additional trigger patterns for hardware stereo trigger
# topological trigger is one MAGIC and LST
DATA_TOPOLOGICAL_TRIGGER = 4
# stereo + topological trigger is M1+M2+LST
DATA_MAGIC_LST_TRIGGER = 132
Loading

0 comments on commit 46994db

Please sign in to comment.