From ec92a5c2225e6a134d9f3596b6ad99c7eb75d547 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 22 Dec 2023 15:33:46 +0100 Subject: [PATCH 01/28] Take info on prescaler and trigger from previous subrun if not available in current file. --- ctapipe_io_magic/__init__.py | 143 +++++++++++++++++++++++++---------- 1 file changed, 101 insertions(+), 42 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index 846c928..ed82c6d 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -521,6 +521,13 @@ def parse_data_info(self): is_sumt = [] is_hast = [] + stereo_prev = None + sumt_prev = None + hast_prev = None + + has_prescaler_info = True + has_trigger_table_info = True + 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] @@ -545,55 +552,103 @@ def parse_data_info(self): ].array(library="np") except AssertionError: logger.warning( - "No prescaler info found. Will assume standard stereo data." + f"No prescaler factors branch found in {rootf.file_path}." ) - stereo = True - sumt = False - hast = False - return stereo, sumt, hast - - prescaler_size = prescaler_array.size - if prescaler_size > 1: - prescaler = list(prescaler_array[1]) - else: - prescaler = list(prescaler_array[0]) - - if ( - prescaler == prescaler_mono_nosumt - 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 + if stereo_prev is not None and hast_prev is not None: + logger.warning("Assuming previous subrun information.") + stereo = stereo_prev + hast = hast_prev + else: + logger.warning("Assuming standard stereo data.") + stereo = True + hast = False + + has_prescaler_info = False + + if has_prescaler_info: + prescaler = None + prescaler_size = prescaler_array.size + if prescaler_size > 1: + prescaler = list(prescaler_array[1]) + elif prescaler_size == 1: + prescaler = list(prescaler_array[0]) + else: + logger.warning( + f"No prescaler info found in {rootf.file_path}." + ) + if stereo_prev is not None and hast_prev is not None: + logger.warning("Assuming previous subrun information.") + stereo = stereo_prev + hast = hast_prev + else: + logger.warning("Assuming standard stereo data.") + stereo = True + hast = False + + if prescaler is not None: + if ( + prescaler == prescaler_mono_nosumt + 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: # here we take the 2nd element for the same reason as above # L3Table is empty for mono data i.e. taken with one telescope only # if both telescopes take data with no L3, L3Table is filled anyway - L3Table_array = L3T_tree["MReportL3T.fTablename"].array( - library="np" - ) - L3Table_size = L3Table_array.size - if L3Table_size > 1: - L3Table = L3Table_array[1] - else: - L3Table = L3Table_array[0] - - if L3Table == L3_table_sumt: - sumt = True - elif L3Table == L3_table_nosumt: - sumt = False - else: - sumt = False + try: + L3Table_array = L3T_tree["MReportL3T.fTablename"].array( + library="np" + ) + except AssertionError: + logger.warning( + f"No trigger table branch found in {rootf.file_path}." + ) + if sumt_prev is not None: + logger.warning("Assuming previous subrun information.") + sumt = sumt_prev + else: + logger.warning("Assuming standard trigger data.") + sumt = False + + has_trigger_table_info = False + + if has_trigger_table_info: + L3Table = None + L3Table_size = L3Table_array.size + if L3Table_size > 1: + L3Table = L3Table_array[1] + elif L3Table_size == 1: + L3Table = L3Table_array[0] + else: + logger.warning( + f"No trigger table info found in {rootf.file_path}." + ) + if sumt_prev is not None: + logger.warning("Assuming previous subrun information.") + sumt = sumt_prev + else: + logger.warning("Assuming standard trigger data.") + sumt = False + + if L3Table is not None: + if L3Table == L3_table_sumt: + sumt = True + elif L3Table == L3_table_nosumt: + sumt = False + else: + sumt = False else: if prescaler == prescaler_mono_sumt: sumt = True @@ -602,6 +657,10 @@ def parse_data_info(self): is_sumt.append(sumt) is_hast.append(hast) + stereo_prev = stereo + sumt_prev = sumt + hast_prev = hast + else: for rootf in self.files_: # looking into MC data, when SumT is simulated, trigger pattern of all events is set to 32 (bit 5), except the first (set to 0) From 080d89d1ddd7ca007fa637e10942f69e3b3e058f Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 22 Dec 2023 15:43:10 +0100 Subject: [PATCH 02/28] Add file check for Events and RunHeaders trees. --- ctapipe_io_magic/__init__.py | 76 +++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 5 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index 846c928..9c058d8 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -88,6 +88,24 @@ def load_camera_geometry(): return CameraGeometry.from_table(f) +class MissingInputFilesError(Exception): + """ + Exception raised when the files check fails. + """ + + def __init__(self, message): + self.message = message + + +class FailedFileCheckError(Exception): + """ + Exception raised when the files check fails. + """ + + def __init__(self, message): + self.message = message + + class MissingDriveReportError(Exception): """ Exception raised when a subrun does not have drive reports. @@ -179,7 +197,10 @@ def __init__(self, input_url=None, config=None, parent=None, **kwargs): reg_comp_mc = re.compile(regex_mc) ls = Path(path).iterdir() + + self.file_list = [] self.file_list_drive = [] + self.excluded_files_ = [] for file_path in ls: if ( @@ -188,6 +209,12 @@ def __init__(self, input_url=None, config=None, parent=None, **kwargs): ): self.file_list_drive.append(file_path) + if not len(self.file_list_drive): + raise MissingInputFilesError( + f"No input files found in {path}. Exiting." + f"Check your input: {input_url}." + ) + self.file_list_drive.sort() if self.process_run: @@ -197,6 +224,12 @@ def __init__(self, input_url=None, config=None, parent=None, **kwargs): # Retrieving the list of run numbers corresponding to the data files self.files_ = [uproot.open(rootf) for rootf in self.file_list] + + is_check_valid = self.check_files() + + if not is_check_valid: + raise FailedFileCheckError("Validity check for the files failed. Exiting.") + run_info = self.parse_run_info() self.run_id = run_info[0][0] @@ -279,6 +312,8 @@ def close(self): for rootf in self.files_: rootf.close() + for rootf in self.excluded_files_: + rootf.close() @staticmethod def is_compatible(file_path): @@ -405,6 +440,39 @@ def get_run_info_from_name(file_name): return run_number, is_mc, telescope, datalevel + def check_files(self): + """Check the the input files contain needed trees.""" + + needed_trees = ["RunHeaders", "Events"] + num_files = len(self.files_) + + if num_files == 1 and "Drive" not in self.files_[0]: + logger.error(f"Cannot proceed without Drive information for a single file.") + return False + + for rootf in self.files_: + for tree in needed_trees: + if tree not in rootf.keys(cycle=False): + logger.warning( + f"File {rootf.file_path} does not have the tree {tree}." + ) + if tree == "RunHeaders" or tree == "Events": + logger.error( + f"Cannot proceed without RunHeaders or Events tree." + f"File {rootf.file_path} will be excluded." + ) + self.file_list.remove(rootf.file_path) + self.file_list_drive.remove(rootf.file_path) + self.files_.remove(rootf) + self.excluded_files_.append(rootf) + break + + if len(self.excluded_files_) == num_files: + logger.error("All input files were excluded.") + return False + + return True + def parse_run_info(self): """ Parses run info from the TTrees in the ROOT file @@ -1116,13 +1184,11 @@ def get_event_time_difference(self): 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})" + f" | (MTriggerPattern.fPrescaled == {DATA_TOPOLOGICAL_TRIGGER})" + f" | (MTriggerPattern.fPrescaled == {DATA_MAGIC_LST_TRIGGER})" ) else: - event_cut = ( - f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})", - ) + event_cut = (f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})",) for uproot_file in self.files_: event_info = uproot_file["Events"].arrays( From 4b9d2296143f0e26182b192252791fd3e082697d Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Thu, 18 Jan 2024 22:03:49 +0100 Subject: [PATCH 03/28] Correct check of Drive tree existence. --- ctapipe_io_magic/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index 9c058d8..d6dfe1f 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -441,12 +441,12 @@ def get_run_info_from_name(file_name): return run_number, is_mc, telescope, datalevel def check_files(self): - """Check the the input files contain needed trees.""" + """Check the the input files contain the needed trees.""" needed_trees = ["RunHeaders", "Events"] num_files = len(self.files_) - if num_files == 1 and "Drive" not in self.files_[0]: + if num_files == 1 and "Drive" not in self.files_[0].keys(cycle=False): logger.error(f"Cannot proceed without Drive information for a single file.") return False From b2a3ed83705e4eca2e4c5eaa79f6f9600c77105f Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Thu, 18 Jan 2024 22:08:40 +0100 Subject: [PATCH 04/28] Fail check if Triger tree is missing for a single file. --- ctapipe_io_magic/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index d6dfe1f..ac6e4a5 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -450,6 +450,10 @@ def check_files(self): logger.error(f"Cannot proceed without Drive information for a single file.") return False + if num_files == 1 and "Trigger" not in self.files_[0].keys(cycle=False): + logger.error(f"Cannot proceed without Trigger information for a single file.") + return False + for rootf in self.files_: for tree in needed_trees: if tree not in rootf.keys(cycle=False): From 9456b5c5c2ed57996f5f065397f0ec94aa3b2ac3 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Thu, 18 Jan 2024 22:14:56 +0100 Subject: [PATCH 05/28] Fix check for MC files. --- ctapipe_io_magic/__init__.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index ac6e4a5..d7da9c0 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -446,12 +446,22 @@ def check_files(self): needed_trees = ["RunHeaders", "Events"] num_files = len(self.files_) - if num_files == 1 and "Drive" not in self.files_[0].keys(cycle=False): + if ( + num_files == 1 + and "Drive" not in self.files_[0].keys(cycle=False) + and "OriginalMC" not in self.files_[0].keys(cycle=False) + ): logger.error(f"Cannot proceed without Drive information for a single file.") return False - if num_files == 1 and "Trigger" not in self.files_[0].keys(cycle=False): - logger.error(f"Cannot proceed without Trigger information for a single file.") + if ( + num_files == 1 + and "Trigger" not in self.files_[0].keys(cycle=False) + and "OriginalMC" not in self.files_[0].keys(cycle=False) + ): + logger.error( + f"Cannot proceed without Trigger information for a single file." + ) return False for rootf in self.files_: From cc71d512bcfa9b866f1e187890ae9ac314538740 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 10:52:17 +0100 Subject: [PATCH 06/28] Move exceptions to separate file. --- ctapipe_io_magic/__init__.py | 14 +++++++++++++- ctapipe_io_magic/exceptions.py | 25 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 ctapipe_io_magic/exceptions.py diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index d7da9c0..f99928d 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -44,6 +44,11 @@ ) from .mars_datalevels import MARSDataLevel +from .exceptions import ( + MissingInputFilesError, + FailedFileCheckError, + MissingDriveReportError, +) from .version import __version__ from .constants import ( @@ -57,7 +62,14 @@ DATA_MAGIC_LST_TRIGGER, ) -__all__ = ["MAGICEventSource", "MARSDataLevel", "__version__"] +__all__ = [ + "MAGICEventSource", + "MARSDataLevel", + "MissingInputFilesError", + "FailedFileCheckError", + "MissingDriveReportError", + "__version__", +] logger = logging.getLogger(__name__) diff --git a/ctapipe_io_magic/exceptions.py b/ctapipe_io_magic/exceptions.py new file mode 100644 index 0000000..37a46e8 --- /dev/null +++ b/ctapipe_io_magic/exceptions.py @@ -0,0 +1,25 @@ +class MissingInputFilesError(Exception): + """ + Exception raised when there are no input files. + """ + + def __init__(self, message): + self.message = message + + +class FailedFileCheckError(Exception): + """ + Exception raised when the files check fails. + """ + + def __init__(self, message): + self.message = message + + +class MissingDriveReportError(Exception): + """ + Exception raised when a subrun does not have drive reports. + """ + + def __init__(self, message): + self.message = message From 56c1b8267a6ac7ef6a22c2eefec0febe613d770e Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 10:52:37 +0100 Subject: [PATCH 07/28] Remainder of previous commit. --- ctapipe_io_magic/__init__.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index f99928d..b061525 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -100,33 +100,6 @@ def load_camera_geometry(): return CameraGeometry.from_table(f) -class MissingInputFilesError(Exception): - """ - Exception raised when the files check fails. - """ - - def __init__(self, message): - self.message = message - - -class FailedFileCheckError(Exception): - """ - Exception raised when the files check fails. - """ - - def __init__(self, message): - self.message = message - - -class MissingDriveReportError(Exception): - """ - Exception raised when a subrun does not have drive reports. - """ - - def __init__(self, message): - self.message = message - - class MAGICEventSource(EventSource): """ EventSource for MAGIC calibrated data. From 3c2cb51c46aa69b6724d1f838c6d472ba741338e Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 10:52:51 +0100 Subject: [PATCH 08/28] Small fixes. --- ctapipe_io_magic/__init__.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index b061525..823d552 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -436,7 +436,7 @@ def check_files(self): and "Drive" not in self.files_[0].keys(cycle=False) and "OriginalMC" not in self.files_[0].keys(cycle=False) ): - logger.error(f"Cannot proceed without Drive information for a single file.") + logger.error("Cannot proceed without Drive information for a single file.") return False if ( @@ -445,7 +445,7 @@ def check_files(self): and "OriginalMC" not in self.files_[0].keys(cycle=False) ): logger.error( - f"Cannot proceed without Trigger information for a single file." + "Cannot proceed without Trigger information for a single file." ) return False @@ -457,11 +457,9 @@ def check_files(self): ) if tree == "RunHeaders" or tree == "Events": logger.error( - f"Cannot proceed without RunHeaders or Events tree." + f"Cannot proceed without RunHeaders or Events tree. " f"File {rootf.file_path} will be excluded." ) - self.file_list.remove(rootf.file_path) - self.file_list_drive.remove(rootf.file_path) self.files_.remove(rootf) self.excluded_files_.append(rootf) break From 7fbad48430488cd25e9e81a407266a3492617565 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 10:53:03 +0100 Subject: [PATCH 09/28] Add tests for the files check. --- .../tests/test_magic_event_source.py | 54 +++++++++++++++++-- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index 154551a..1f6e3ea 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -13,6 +13,21 @@ test_calibrated_real_dir / "20210314_M2_05095172.002_Y_CrabNebula-W0.40+035.root", ] +test_calibrated_real_only_events = [ + test_calibrated_real_dir + / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root", +] + +test_calibrated_real_only_drive = [ + test_calibrated_real_dir + / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root", +] + +test_calibrated_real_only_runh = [ + test_calibrated_real_dir + / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root", +] + test_calibrated_real_hast = [ test_calibrated_real_dir / "20230324_M1_05106879.001_Y_1ES0806+524-W0.40+000.root", test_calibrated_real_dir / "20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root", @@ -32,6 +47,12 @@ test_calibrated_real + test_calibrated_simulated + test_calibrated_real_hast ) +test_calibrated_missing_trees = ( + test_calibrated_real_only_events + + test_calibrated_real_only_drive + + test_calibrated_real_only_runh +) + data_dict = dict() data_dict["20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root"] = dict() @@ -335,9 +356,18 @@ def test_number_of_events(dataset): count_2_tel_m1_m2 += 1 assert count_3_tel == data_dict[source.input_url.name]["n_events_3_tel"] - assert count_2_tel_m1_lst == data_dict[source.input_url.name]["n_events_2_tel_m1_lst"] - assert count_2_tel_m2_lst == data_dict[source.input_url.name]["n_events_2_tel_m2_lst"] - assert count_2_tel_m1_m2 == data_dict[source.input_url.name]["n_events_2_tel_m1_m2"] + assert ( + count_2_tel_m1_lst + == data_dict[source.input_url.name]["n_events_2_tel_m1_lst"] + ) + assert ( + count_2_tel_m2_lst + == data_dict[source.input_url.name]["n_events_2_tel_m2_lst"] + ) + assert ( + count_2_tel_m1_m2 + == data_dict[source.input_url.name]["n_events_2_tel_m1_m2"] + ) # if '_M1_' in dataset.name: # assert run['data'].n_cosmics_stereo_events_m1 == data_dict[source.input_url.name]['n_events_stereo'] @@ -478,6 +508,24 @@ def test_focal_length_choice(dataset): ) +@pytest.mark.parametrize("dataset", test_calibrated_missing_trees) +def test_check_files(dataset): + from ctapipe_io_magic import MAGICEventSource, FailedFileCheckError + + with pytest.raises(FailedFileCheckError): + MAGICEventSource(input_url=dataset, process_run=False) + + +def test_check_missing_files(): + from ctapipe_io_magic import MAGICEventSource, MissingInputFilesError + + with pytest.raises(MissingInputFilesError): + MAGICEventSource( + input_url="20501312_M1_05095172.001_Y_FakeSource-W0.40+035.root", + process_run=False, + ) + + # def test_eventseeker(): # dataset = get_dataset_path("20131004_M1_05029747.003_Y_MagicCrab-W0.40+035.root") # From c341c1fec52869e24bf28314e86a6eaf31fc4c29 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 11:00:11 +0100 Subject: [PATCH 10/28] Add files with missing trees. --- download_test_data.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/download_test_data.sh b/download_test_data.sh index 88f5fb0..f4d2bba 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -10,6 +10,9 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.001_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt From 4ce429f531e710e3d14bbe518a8c8502cdc571ad Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 11:06:54 +0100 Subject: [PATCH 11/28] Change name of files with missing trees. --- ctapipe_io_magic/tests/test_magic_event_source.py | 6 +++--- download_test_data.sh | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index 1f6e3ea..efbe1ce 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -15,17 +15,17 @@ test_calibrated_real_only_events = [ test_calibrated_real_dir - / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root", + / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_events.root", ] test_calibrated_real_only_drive = [ test_calibrated_real_dir - / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root", + / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_drive.root", ] test_calibrated_real_only_runh = [ test_calibrated_real_dir - / "20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root", + / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root", ] test_calibrated_real_hast = [ diff --git a/download_test_data.sh b/download_test_data.sh index f4d2bba..eaacc64 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -10,9 +10,9 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.001_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt From b9a105de9a1dc07d3062b8417873f95c0baae0f2 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 11:25:36 +0100 Subject: [PATCH 12/28] Add file with only Trigger tree. --- ctapipe_io_magic/tests/test_magic_event_source.py | 6 ++++++ download_test_data.sh | 1 + 2 files changed, 7 insertions(+) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index efbe1ce..4071c20 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -28,6 +28,11 @@ / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root", ] +test_calibrated_real_only_trigger = [ + test_calibrated_real_dir + / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_trigger.root", +] + test_calibrated_real_hast = [ test_calibrated_real_dir / "20230324_M1_05106879.001_Y_1ES0806+524-W0.40+000.root", test_calibrated_real_dir / "20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root", @@ -51,6 +56,7 @@ test_calibrated_real_only_events + test_calibrated_real_only_drive + test_calibrated_real_only_runh + + test_calibrated_real_only_trigger ) data_dict = dict() diff --git a/download_test_data.sh b/download_test_data.sh index eaacc64..273ec41 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -13,6 +13,7 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_trigger.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt From 069b6f48cd2520f47ead87b033fc369b3ac0d58b Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Fri, 19 Jan 2024 11:32:14 +0100 Subject: [PATCH 13/28] Drop python 3.8. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec3aca..a7e0c10 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,8 +18,8 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11"] - ctapipe-version: ["v0.19.2",] + python-version: ["3.9", "3.10", "3.11"] + ctapipe-version: ["v0.19.2"] defaults: run: From e63a75e1cd5c0325894b3e97df8425b53d04b81b Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Mon, 22 Jan 2024 16:27:09 +0100 Subject: [PATCH 14/28] Remove __init__ for Exception derived classes. --- ctapipe_io_magic/exceptions.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/ctapipe_io_magic/exceptions.py b/ctapipe_io_magic/exceptions.py index 37a46e8..f8132ff 100644 --- a/ctapipe_io_magic/exceptions.py +++ b/ctapipe_io_magic/exceptions.py @@ -3,8 +3,7 @@ class MissingInputFilesError(Exception): Exception raised when there are no input files. """ - def __init__(self, message): - self.message = message + pass class FailedFileCheckError(Exception): @@ -12,8 +11,7 @@ class FailedFileCheckError(Exception): Exception raised when the files check fails. """ - def __init__(self, message): - self.message = message + pass class MissingDriveReportError(Exception): @@ -21,5 +19,4 @@ class MissingDriveReportError(Exception): Exception raised when a subrun does not have drive reports. """ - def __init__(self, message): - self.message = message + pass From b58a94481377280bcaaf772ca9d939e71b374a07 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Mon, 22 Jan 2024 16:57:57 +0100 Subject: [PATCH 15/28] Exit from processing if at least one file is invalid (missing RunHeaders or Events tree). --- ctapipe_io_magic/__init__.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index 823d552..5048e96 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -185,7 +185,6 @@ def __init__(self, input_url=None, config=None, parent=None, **kwargs): self.file_list = [] self.file_list_drive = [] - self.excluded_files_ = [] for file_path in ls: if ( @@ -297,8 +296,6 @@ def close(self): for rootf in self.files_: rootf.close() - for rootf in self.excluded_files_: - rootf.close() @staticmethod def is_compatible(file_path): @@ -449,6 +446,8 @@ def check_files(self): ) return False + num_invalid_files = 0 + for rootf in self.files_: for tree in needed_trees: if tree not in rootf.keys(cycle=False): @@ -457,18 +456,16 @@ def check_files(self): ) if tree == "RunHeaders" or tree == "Events": logger.error( - f"Cannot proceed without RunHeaders or Events tree. " - f"File {rootf.file_path} will be excluded." + f"File {rootf.file_path} does not have a {tree} tree. " + f"Please check the file and try again. If the file " + f"cannot be recovered, exclude it from the analysis." ) - self.files_.remove(rootf) - self.excluded_files_.append(rootf) - break + num_invalid_files += 1 - if len(self.excluded_files_) == num_files: - logger.error("All input files were excluded.") + if num_invalid_files > 0: return False - - return True + else: + return True def parse_run_info(self): """ From 7c666e2ba5f7936d9a4e4759d0d225e101ec700a Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 12:14:05 +0100 Subject: [PATCH 16/28] Put test files with missing trees in a separate directory. --- .../tests/test_magic_event_source.py | 8 ++++---- download_test_data.sh | 19 +++++++++++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index 4071c20..85ff1f9 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -15,22 +15,22 @@ test_calibrated_real_only_events = [ test_calibrated_real_dir - / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_events.root", + / "missing_trees/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root", ] test_calibrated_real_only_drive = [ test_calibrated_real_dir - / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_drive.root", + / "missing_trees/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root", ] test_calibrated_real_only_runh = [ test_calibrated_real_dir - / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root", + / "missing_trees/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root", ] test_calibrated_real_only_trigger = [ test_calibrated_real_dir - / "20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_trigger.root", + / "missing_trees/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_trigger.root", ] test_calibrated_real_hast = [ diff --git a/download_test_data.sh b/download_test_data.sh index 273ec41..c887359 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -10,10 +10,10 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.001_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095173.001_Y_CrabNebula-W0.40+035_only_trigger.root" >> test_data_real.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real_missing_trees.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real_missing_trees.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real_missing_trees.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_trigger.root" >> test_data_real_missing_trees.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt @@ -41,6 +41,17 @@ if ! wget \ echo "Problem in downloading the test data set (calibrated) for real data." fi +if ! wget \ + -i test_data_real_missing_trees.txt \ + --user="$TEST_DATA_USER" \ + --password="$TEST_DATA_PASSWORD" \ + --no-check-certificate \ + --no-verbose \ + --timestamping \ + --directory-prefix=test_data/real/calibrated/missing_trees; then + echo "Problem in downloading the test data set (calibrated with missing trees) for real data." +fi + if ! wget \ -i test_data_simulated.txt \ --user="$TEST_DATA_USER" \ From 5d07cf675ef0e3071dff5a90082c0620058202e6 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 15:35:33 +0100 Subject: [PATCH 17/28] Download test data for the broken subruns. --- download_test_data.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/download_test_data.sh b/download_test_data.sh index c887359..cca0185 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -10,11 +10,14 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.001_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20230324_M2_05106879.002_Y_1ES0806+524-W0.40+000.root" >> test_data_real.txt -echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root" >> test_data_real_missing_trees.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_events.root" > test_data_real_missing_trees.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_drive.root" >> test_data_real_missing_trees.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_runh.root" >> test_data_real_missing_trees.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_trigger.root" >> test_data_real_missing_trees.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" > test_data_real_missing_prescaler_trigger.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.002_Y_CrabNebula-W0.40+035_no_prescaler_trigger.root" >> test_data_real_missing_prescaler_trigger.txt + echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M2_za35to50_8_824318_Y_w0.root" >> test_data_simulated.txt @@ -52,6 +55,17 @@ if ! wget \ echo "Problem in downloading the test data set (calibrated with missing trees) for real data." fi +if ! wget \ + -i test_data_real_missing_prescaler_trigger.txt \ + --user="$TEST_DATA_USER" \ + --password="$TEST_DATA_PASSWORD" \ + --no-check-certificate \ + --no-verbose \ + --timestamping \ + --directory-prefix=test_data/real/calibrated/missing_prescaler_trigger; then + echo "Problem in downloading the test data set (calibrated with missing prescaler and trigger trees) for real data." +fi + if ! wget \ -i test_data_simulated.txt \ --user="$TEST_DATA_USER" \ From 424826543baa04ceb191b3b243d64838bfa0f0a4 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 15:35:57 +0100 Subject: [PATCH 18/28] Check if trigger and l3t trees are in the files. --- ctapipe_io_magic/__init__.py | 78 ++++++++++++++++++++++++------------ 1 file changed, 53 insertions(+), 25 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index a9b308d..fd5003b 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -603,21 +603,43 @@ def parse_data_info(self): L3_table_sumt = "L3T_SUMSUM_100_SYNC" for rootf in self.files_: - trigger_tree = rootf["Trigger"] - L3T_tree = rootf["L3T"] + has_trigger_info = True + try: + trigger_tree = rootf["Trigger"] + except uproot.exceptions.KeyInFileError: + logger.warning( + f"No Trigger tree found in {rootf.file_path}." + ) + has_trigger_info = False + + has_l3_info = True - # here we take the 2nd element (if possible) because sometimes - # the first trigger report has still the old prescaler values from a previous run try: - prescaler_array = trigger_tree[ - "MTriggerPrescFact.fPrescFact" - ].array(library="np") - except AssertionError: + L3T_tree = rootf["L3T"] + except uproot.exceptions.KeyInFileError: logger.warning( - f"No prescaler factors branch found in {rootf.file_path}." + f"No L3T tree found in {rootf.file_path}." ) + has_l3_info = False + + # here we take the 2nd element (if possible) because sometimes + # the first trigger report has still the old prescaler values from a previous run + if has_trigger_info: + try: + prescaler_array = trigger_tree[ + "MTriggerPrescFact.fPrescFact" + ].array(library="np") + except AssertionError: + logger.warning( + f"No prescaler factors branch found in {rootf.file_path}." + ) + has_prescaler_info = False + + if not has_trigger_info or not has_prescaler_info: if stereo_prev is not None and hast_prev is not None: - logger.warning("Assuming previous subrun information.") + logger.warning( + "Assuming previous subrun information for trigger settings." + ) stereo = stereo_prev hast = hast_prev else: @@ -625,8 +647,6 @@ def parse_data_info(self): stereo = True hast = False - has_prescaler_info = False - if has_prescaler_info: prescaler = None prescaler_size = prescaler_array.size @@ -639,7 +659,9 @@ def parse_data_info(self): f"No prescaler info found in {rootf.file_path}." ) if stereo_prev is not None and hast_prev is not None: - logger.warning("Assuming previous subrun information.") + logger.warning( + "Assuming previous subrun information for trigger settings." + ) stereo = stereo_prev hast = hast_prev else: @@ -669,23 +691,27 @@ def parse_data_info(self): # here we take the 2nd element for the same reason as above # L3Table is empty for mono data i.e. taken with one telescope only # if both telescopes take data with no L3, L3Table is filled anyway - try: - L3Table_array = L3T_tree["MReportL3T.fTablename"].array( - library="np" - ) - except AssertionError: - logger.warning( - f"No trigger table branch found in {rootf.file_path}." - ) + if has_l3_info: + try: + L3Table_array = L3T_tree["MReportL3T.fTablename"].array( + library="np" + ) + except AssertionError: + logger.warning( + f"No trigger table branch found in {rootf.file_path}." + ) + has_trigger_table_info = False + + if not has_l3_info or not has_trigger_table_info: if sumt_prev is not None: - logger.warning("Assuming previous subrun information.") + logger.warning( + "Assuming previous subrun information trigger table information." + ) sumt = sumt_prev else: logger.warning("Assuming standard trigger data.") sumt = False - has_trigger_table_info = False - if has_trigger_table_info: L3Table = None L3Table_size = L3Table_array.size @@ -698,7 +724,9 @@ def parse_data_info(self): f"No trigger table info found in {rootf.file_path}." ) if sumt_prev is not None: - logger.warning("Assuming previous subrun information.") + logger.warning( + "Assuming previous subrun information trigger table information." + ) sumt = sumt_prev else: logger.warning("Assuming standard trigger data.") From 249ba569409d39323cd8afd21e3b7d78bd9f6fe5 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 15:36:13 +0100 Subject: [PATCH 19/28] Add test for the broken subruns. --- .../tests/test_magic_event_source.py | 34 +++++++------------ 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index 85ff1f9..fac297f 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -33,6 +33,13 @@ / "missing_trees/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035_only_trigger.root", ] +test_calibrated_real_without_prescaler_trigger = [ + test_calibrated_real_dir + / "missing_prescaler_trigger/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root", + test_calibrated_real_dir + / "missing_prescaler_trigger/20210314_M1_05095172.002_Y_CrabNebula-W0.40+035_no_prescaler_trigger.root", +] + test_calibrated_real_hast = [ test_calibrated_real_dir / "20230324_M1_05106879.001_Y_1ES0806+524-W0.40+000.root", test_calibrated_real_dir / "20230324_M1_05106879.002_Y_1ES0806+524-W0.40+000.root", @@ -532,24 +539,9 @@ def test_check_missing_files(): ) -# def test_eventseeker(): -# dataset = get_dataset_path("20131004_M1_05029747.003_Y_MagicCrab-W0.40+035.root") -# -# with MAGICEventSource(input_url=dataset) as source: -# seeker = EventSeeker(source) -# event = seeker.get_event_index(0) -# assert event.count == 0 -# assert event.index.event_id == 29795 -# -# event = seeker.get_event_index(2) -# assert event.count == 2 -# assert event.index.event_id == 29798 -# -# def test_eventcontent(): -# dataset = get_dataset_path("20131004_M1_05029747.003_Y_MagicCrab-W0.40+035.root") -# -# with MAGICEventSource(input_url=dataset) as source: -# seeker = EventSeeker(source) -# event = seeker.get_event_index(0) -# assert event.dl1.tel[1].image[0] == -0.53125 -# assert event.dl1.tel[1].peak_time[0] == 49.125 +def test_broken_subruns(): + from ctapipe_io_magic import MAGICEventSource + + input_file = test_calibrated_real_dir / "missing_prescaler_trigger/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" + + MAGICEventSource(input_url=input_file, process_run=True,) From dfd7fdbd145921cffd1886eedfb5c5ca94a36227 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 15:37:18 +0100 Subject: [PATCH 20/28] Remove temporary files. --- download_test_data.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/download_test_data.sh b/download_test_data.sh index cca0185..658fb31 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -77,4 +77,4 @@ if ! wget \ echo "Problem in downloading the test data set (calibrated) for simulated data." fi -rm -f test_data_real.txt test_data_simulated.txt +rm -f test_data_real.txt test_data_simulated.txt test_data_real_missing_trees.txt test_data_real_missing_prescaler_trigger.txt From ccd9d67da3f241fb96c1a87baafeee4e6b71679b Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 17:06:28 +0100 Subject: [PATCH 21/28] Add test for missing arrays in Trigger and L3T trees. --- .../tests/test_magic_event_source.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ctapipe_io_magic/tests/test_magic_event_source.py b/ctapipe_io_magic/tests/test_magic_event_source.py index fac297f..fbac975 100644 --- a/ctapipe_io_magic/tests/test_magic_event_source.py +++ b/ctapipe_io_magic/tests/test_magic_event_source.py @@ -539,9 +539,23 @@ def test_check_missing_files(): ) -def test_broken_subruns(): +def test_broken_subruns_missing_trees(): from ctapipe_io_magic import MAGICEventSource input_file = test_calibrated_real_dir / "missing_prescaler_trigger/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" MAGICEventSource(input_url=input_file, process_run=True,) + + +def test_broken_subruns_missing_arrays(): + from ctapipe_io_magic import MAGICEventSource + + input_file = ( + test_calibrated_real_dir + / "missing_arrays/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" + ) + + MAGICEventSource( + input_url=input_file, + process_run=True, + ) From 11e0800c9b2ebf7cb998a17e3b6f176fefb032bd Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 17:06:37 +0100 Subject: [PATCH 22/28] Add new test files. --- download_test_data.sh | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/download_test_data.sh b/download_test_data.sh index 658fb31..4b67b18 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -18,6 +18,9 @@ echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" > test_data_real_missing_prescaler_trigger.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.002_Y_CrabNebula-W0.40+035_no_prescaler_trigger.root" >> test_data_real_missing_prescaler_trigger.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.001_Y_CrabNebula-W0.40+035.root" > test_data_real_missing_arrays.txt +echo "https://www.magic.iac.es/mcp-testdata/test_data/real/calibrated/20210314_M1_05095172.002_Y_CrabNebula-W0.40+035_no_arrays.root" >> test_data_real_missing_arrays.txt + echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824318_Y_w0.root" > test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M1_za35to50_8_824319_Y_w0.root" >> test_data_simulated.txt echo "https://www.magic.iac.es/mcp-testdata/test_data/simulated/calibrated/GA_M2_za35to50_8_824318_Y_w0.root" >> test_data_simulated.txt @@ -66,6 +69,17 @@ if ! wget \ echo "Problem in downloading the test data set (calibrated with missing prescaler and trigger trees) for real data." fi +if ! wget \ + -i test_data_real_missing_arrays.txt \ + --user="$TEST_DATA_USER" \ + --password="$TEST_DATA_PASSWORD" \ + --no-check-certificate \ + --no-verbose \ + --timestamping \ + --directory-prefix=test_data/real/calibrated/missing_arrays; then + echo "Problem in downloading the test data set (calibrated with missing prescaler and trigger arrays) for real data." +fi + if ! wget \ -i test_data_simulated.txt \ --user="$TEST_DATA_USER" \ @@ -77,4 +91,4 @@ if ! wget \ echo "Problem in downloading the test data set (calibrated) for simulated data." fi -rm -f test_data_real.txt test_data_simulated.txt test_data_real_missing_trees.txt test_data_real_missing_prescaler_trigger.txt +rm -f test_data_real.txt test_data_simulated.txt test_data_real_missing_trees.txt test_data_real_missing_prescaler_trigger.txt test_data_real_missing_arrays.txt From f7f666f3c9fb87a47cc722e8e0594c2c91958b46 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 17:20:22 +0100 Subject: [PATCH 23/28] Add dependabot configuration. --- .github/dependabot.yml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..308e1e2 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,7 @@ +# Maintain dependencies for GitHub Actions +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" From eb626c30ee42cb88d2219d2ac8ca205903e29a80 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Tue, 13 Feb 2024 17:23:09 +0100 Subject: [PATCH 24/28] Update deploy workflow. --- .github/workflows/deploy.yml | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 873a5cd..1697ed4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -10,23 +10,19 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - # make sure we have version info - - run: git fetch --tags - - name: Set up Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.9" - name: Install dependencies run: | python --version - pip install -U pip setuptools wheel setuptools_scm[toml] - python setup.py sdist bdist_wheel + pip install -U build + python -m build - name: Publish package - uses: pypa/gh-action-pypi-publish@master + uses: pypa/gh-action-pypi-publish@release/v1 with: user: __token__ password: ${{ secrets.pypi_password }} From b40863125770da6f8398817d2eee747d4a5d610f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:27:19 +0000 Subject: [PATCH 25/28] Bump codecov/codecov-action from 3 to 4 Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 3 to 4. - [Release notes](https://github.com/codecov/codecov-action/releases) - [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md) - [Commits](https://github.com/codecov/codecov-action/compare/v3...v4) --- updated-dependencies: - dependency-name: codecov/codecov-action dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a7e0c10..ebecd7f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,4 +68,4 @@ jobs: run: | pytest --cov=ctapipe_io_magic --cov-report=xml - - uses: codecov/codecov-action@v3 + - uses: codecov/codecov-action@v4 From 6f9c0b3f8d6edb1178582bdc1d264caef429d1a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Feb 2024 16:27:23 +0000 Subject: [PATCH 26/28] Bump release-drafter/release-drafter from 5 to 6 Bumps [release-drafter/release-drafter](https://github.com/release-drafter/release-drafter) from 5 to 6. - [Release notes](https://github.com/release-drafter/release-drafter/releases) - [Commits](https://github.com/release-drafter/release-drafter/compare/v5...v6) --- updated-dependencies: - dependency-name: release-drafter/release-drafter dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/release-drafter.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml index 17fdb96..99fe14d 100644 --- a/.github/workflows/release-drafter.yml +++ b/.github/workflows/release-drafter.yml @@ -9,6 +9,6 @@ jobs: update_release_draft: runs-on: ubuntu-latest steps: - - uses: release-drafter/release-drafter@v5 + - uses: release-drafter/release-drafter@v6 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} From ecc74c6836961f4d97c595b66d79e0696f7bac6e Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Thu, 15 Feb 2024 17:09:32 +0100 Subject: [PATCH 27/28] Download test data using for loop. --- download_test_data.sh | 73 ++++++++++++------------------------------- 1 file changed, 20 insertions(+), 53 deletions(-) diff --git a/download_test_data.sh b/download_test_data.sh index 4b67b18..5e1dd5f 100755 --- a/download_test_data.sh +++ b/download_test_data.sh @@ -36,59 +36,26 @@ if [ -z "$TEST_DATA_PASSWORD" ]; then echo fi -if ! wget \ - -i test_data_real.txt \ - --user="$TEST_DATA_USER" \ - --password="$TEST_DATA_PASSWORD" \ - --no-check-certificate \ - --no-verbose \ - --timestamping \ - --directory-prefix=test_data/real/calibrated; then - echo "Problem in downloading the test data set (calibrated) for real data." -fi - -if ! wget \ - -i test_data_real_missing_trees.txt \ - --user="$TEST_DATA_USER" \ - --password="$TEST_DATA_PASSWORD" \ - --no-check-certificate \ - --no-verbose \ - --timestamping \ - --directory-prefix=test_data/real/calibrated/missing_trees; then - echo "Problem in downloading the test data set (calibrated with missing trees) for real data." -fi - -if ! wget \ - -i test_data_real_missing_prescaler_trigger.txt \ - --user="$TEST_DATA_USER" \ - --password="$TEST_DATA_PASSWORD" \ - --no-check-certificate \ - --no-verbose \ - --timestamping \ - --directory-prefix=test_data/real/calibrated/missing_prescaler_trigger; then - echo "Problem in downloading the test data set (calibrated with missing prescaler and trigger trees) for real data." -fi - -if ! wget \ - -i test_data_real_missing_arrays.txt \ - --user="$TEST_DATA_USER" \ - --password="$TEST_DATA_PASSWORD" \ - --no-check-certificate \ - --no-verbose \ - --timestamping \ - --directory-prefix=test_data/real/calibrated/missing_arrays; then - echo "Problem in downloading the test data set (calibrated with missing prescaler and trigger arrays) for real data." -fi - -if ! wget \ - -i test_data_simulated.txt \ - --user="$TEST_DATA_USER" \ - --password="$TEST_DATA_PASSWORD" \ - --no-check-certificate \ - --no-verbose \ - --timestamping \ - --directory-prefix=test_data/simulated/calibrated; then - echo "Problem in downloading the test data set (calibrated) for simulated data." +declare -A TEST_FILES_DOWNLOAD + +TEST_FILES_DOWNLOAD[test_data_real]="test_data/real/calibrated" +TEST_FILES_DOWNLOAD[test_data_real_missing_trees]="test_data/real/calibrated/missing_trees" +TEST_FILES_DOWNLOAD[test_data_real_missing_prescaler_trigger]="test_data/real/calibrated/missing_prescaler_trigger" +TEST_FILES_DOWNLOAD[test_data_real_missing_arrays]="test_data/real/calibrated/missing_arrays" +TEST_FILES_DOWNLOAD[test_data_simulated]="test_data/simulated/calibrated" + +for key in "${!TEST_FILES_DOWNLOAD[@]}" +do + if ! wget \ + -i "${key}.txt" \ + --user="$TEST_DATA_USER" \ + --password="$TEST_DATA_PASSWORD" \ + --no-check-certificate \ + --no-verbose \ + --timestamping \ + --directory-prefix="${TEST_FILES_DOWNLOAD[${key}]}"; then + echo "Problem in downloading the test data set from ${key}.txt." fi +done rm -f test_data_real.txt test_data_simulated.txt test_data_real_missing_trees.txt test_data_real_missing_prescaler_trigger.txt test_data_real_missing_arrays.txt From f336e9f984e72fe537678c6b31c0663de541a6d1 Mon Sep 17 00:00:00 2001 From: Alessio Berti Date: Thu, 15 Feb 2024 17:11:49 +0100 Subject: [PATCH 28/28] Add warning if "strange" prescaler value is read. --- ctapipe_io_magic/__init__.py | 49 ++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/ctapipe_io_magic/__init__.py b/ctapipe_io_magic/__init__.py index fd5003b..4b73773 100644 --- a/ctapipe_io_magic/__init__.py +++ b/ctapipe_io_magic/__init__.py @@ -607,9 +607,7 @@ def parse_data_info(self): try: trigger_tree = rootf["Trigger"] except uproot.exceptions.KeyInFileError: - logger.warning( - f"No Trigger tree found in {rootf.file_path}." - ) + logger.warning(f"No Trigger tree found in {rootf.file_path}.") has_trigger_info = False has_l3_info = True @@ -617,9 +615,7 @@ def parse_data_info(self): try: L3T_tree = rootf["L3T"] except uproot.exceptions.KeyInFileError: - logger.warning( - f"No L3T tree found in {rootf.file_path}." - ) + logger.warning(f"No L3T tree found in {rootf.file_path}.") has_l3_info = False # here we take the 2nd element (if possible) because sometimes @@ -655,9 +651,7 @@ def parse_data_info(self): elif prescaler_size == 1: prescaler = list(prescaler_array[0]) else: - logger.warning( - f"No prescaler info found in {rootf.file_path}." - ) + logger.warning(f"No prescaler info found in {rootf.file_path}.") if stereo_prev is not None and hast_prev is not None: logger.warning( "Assuming previous subrun information for trigger settings." @@ -683,6 +677,9 @@ def parse_data_info(self): stereo = True hast = True else: + logger.warning( + f"Prescaler different from the default mono, stereo or hast was found: {prescaler}. Please check your data." + ) stereo = True hast = False @@ -1611,9 +1608,9 @@ def _event_generator(self): if not self.use_pedestals: badrmspixel_mask = self._get_badrmspixel_mask(event) - event.mon.tel[ - tel_id - ].pixel_status.pedestal_failing_pixels = badrmspixel_mask + event.mon.tel[tel_id].pixel_status.pedestal_failing_pixels = ( + badrmspixel_mask + ) # Set the telescope pointing container: event.pointing.array_azimuth = event_data["pointing_az"][i_event].to( @@ -1833,9 +1830,9 @@ def _load_data(self): if self.use_sumt_events: mc_trigger_pattern = MC_SUMT_TRIGGER_PATTERN if self.use_mc_mono_events or not self.is_stereo: - events_cut[ - "cosmic_events" - ] = f"(MTriggerPattern.fPrescaled == {mc_trigger_pattern})" + events_cut["cosmic_events"] = ( + f"(MTriggerPattern.fPrescaled == {mc_trigger_pattern})" + ) else: events_cut["cosmic_events"] = ( f"(MTriggerPattern.fPrescaled == {mc_trigger_pattern})" @@ -1855,13 +1852,13 @@ def _load_data(self): f" | (MTriggerPattern.fPrescaled == {DATA_MAGIC_LST_TRIGGER})" ) else: - events_cut[ - "cosmic_events" - ] = f"(MTriggerPattern.fPrescaled == {data_trigger_pattern})" + 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" - ] = f"(MTriggerPattern.fPrescaled == {PEDESTAL_TRIGGER_PATTERN})" + events_cut["pedestal_events"] = ( + f"(MTriggerPattern.fPrescaled == {PEDESTAL_TRIGGER_PATTERN})" + ) logger.info(f"Cosmic events selection: {events_cut['cosmic_events']}") @@ -1922,10 +1919,12 @@ def _load_data(self): 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] + ( + 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 )