diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 63d638415..c01e74288 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,6 +14,19 @@ env: PYTEST_ADDOPTS: --color=yes jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v4 + with: + python-version: 3.8 + - uses: pre-commit/action@v3.0.0 + with: + extra_args: --files $(git diff origin/main --name-only) + pyflakes: runs-on: ubuntu-latest steps: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000..9ec90cc85 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,17 @@ +repos: + # https://pycqa.github.io/isort/docs/configuration/black_compatibility.html#integration-with-pre-commit + - repo: https://github.com/pycqa/isort + rev: 5.12.0 + hooks: + - id: isort + args: ["--profile", "black", "--filter-files"] + - repo: https://github.com/psf/black + rev: 22.6.0 + hooks: + - id: black-jupyter + # https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html?highlight=other%20tools#flake8 + - repo: https://github.com/PyCQA/flake8 + rev: 4.0.1 + hooks: + - id: flake8 + args: [--max-line-length=88, "--extend-ignore=E203,E712"] diff --git a/README.md b/README.md index ae223d5a7..2af8d8476 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ Repository for the analysis of MAGIC and MAGIC+LST1 data, based on [*ctapipe*](https://github.com/cta-observatory/ctapipe). * Code: https://github.com/cta-observatory/magic-cta-pipe +* Docs (preliminary): https://magic-cta-pipe.readthedocs.io/ v0.3.1 of *magic-cta-pipe* provides all the functionalities to perform a MAGIC+LST-1 or a MAGIC-only analysis. Both types of analyses can be performed using the scripts within the *lst1_magic* folder. See the [README](https://github.com/cta-observatory/magic-cta-pipe/blob/master/magicctapipe/scripts/lst1_magic/README.md) for more details on how to run the analysis. @@ -24,6 +25,8 @@ The following command will set up a conda virtual environment, add the necessary conda activate magic-lst1 pip install . +In general, *magic-cta-pipe* is still in heavy development phase, so expect large changes between different releases. + # Instructions for developers People who would like to join the development of *magic-cta-pipe*, please contact Alessio Berti () to get write access to the repository. @@ -40,6 +43,14 @@ flake8 magicctapipe # checks style and code errors black filename.py # reformats filename.py with black ``` +The *black* and *isort* auto-formatters are used for automatic adherence to the code style. To enforce running these tools whenever you make a commit, setup the [pre-commit hook](https://pre-commit.com/): + +```bash +$ pre-commit install +``` + +The pre-commit hook will then execute the tools with the same settings as when the a pull request is checked on github, and if any problems are reported the commit will be rejected. You then have to fix the reported issues before tying to commit again. + In general, if you want to add a new feature or fix a bug, please open a new issue, and then create a new branch to develop the new feature or code the bug fix. You can create an early pull request even if it is not complete yet, you can tag it as "Draft" so that it will not be merged, and other developers can already check it and provide comments. When the code is ready, remove the tag "Draft" and select two people to review the pull request (at the moment the merge is not blocked if no review is performed, but that may change in the future). When the review is complete, the branch will be merged into the main branch. Valid telescope names are LST-[1-4] and MAGIC-[I-II] ") + raise Exception( + f"{v} is not a valid telescope name (check the config file). Only MAGIC and LST telescopes can be analyzed --> Valid telescope names are LST-[1-4] and MAGIC-[I-II] " + ) subarray_lst_magic = SubarrayDescription( "LST-MAGIC-Array", TEL_POSITIONS, tel_descriptions diff --git a/magicctapipe/scripts/lst1_magic/lst1_magic_mc_dl0_to_dl1.py b/magicctapipe/scripts/lst1_magic/lst1_magic_mc_dl0_to_dl1.py index e4e0ba006..51fba65bb 100644 --- a/magicctapipe/scripts/lst1_magic/lst1_magic_mc_dl0_to_dl1.py +++ b/magicctapipe/scripts/lst1_magic/lst1_magic_mc_dl0_to_dl1.py @@ -21,8 +21,8 @@ (--config-file config_step1.yaml) """ -import argparse #Parser for command-line options, arguments etc -import logging #Used to manage the log file +import argparse # Parser for command-line options, arguments etc +import logging # Used to manage the log file import re import time from pathlib import Path @@ -40,12 +40,12 @@ ) from ctapipe.instrument import SubarrayDescription from ctapipe.io import EventSource, HDF5TableWriter +from traitlets.config import Config from magicctapipe.image import MAGICClean from magicctapipe.image.calib import calibrate -from magicctapipe.io import SimEventInfoContainer, format_object, check_input_list +from magicctapipe.io import SimEventInfoContainer, check_input_list, format_object from magicctapipe.utils import calculate_disp, calculate_impact -from traitlets.config import Config __all__ = ["mc_dl0_to_dl1"] @@ -57,9 +57,6 @@ PARTICLE_TYPES = {1: "gamma", 3: "electron", 14: "proton", 402: "helium"} - - - def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): """ Processes LST-1 and MAGIC events of simtel MC DL0 data and computes @@ -75,9 +72,13 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): Configuration for the LST-1 + MAGIC analysis """ - assigned_tel_ids = config["mc_tel_ids"] #This variable becomes the dictionary {'LST-1': 1, 'MAGIC-I': 2, 'MAGIC-II': 3} + assigned_tel_ids = config[ + "mc_tel_ids" + ] # This variable becomes the dictionary {'LST-1': 1, 'MAGIC-I': 2, 'MAGIC-II': 3} - logger.info("\nAssigned telescope IDs:") #Here we are just adding infos to the log file + logger.info( + "\nAssigned telescope IDs:" + ) # Here we are just adding infos to the log file logger.info(format_object(assigned_tel_ids)) # Load the input file @@ -85,7 +86,9 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): event_source = EventSource( input_file, - allowed_tels=list(filter(lambda check_id: check_id > 0, assigned_tel_ids.values())), #Here we load the events for all telescopes with ID > 0. + allowed_tels=list( + filter(lambda check_id: check_id > 0, assigned_tel_ids.values()) + ), # Here we load the events for all telescopes with ID > 0. focal_length_choice=focal_length, ) @@ -123,8 +126,6 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): logger.info("\nLST PSF modifier:") logger.info(format_object(config_lst["increase_psf"])) - - logger.info("\nLST tailcuts cleaning:") logger.info(format_object(config_lst["tailcuts_clean"])) @@ -134,8 +135,6 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): logger.info("\nLST dynamic cleaning:") logger.info(format_object(config_lst["dynamic_cleaning"])) - - use_only_main_island = config_lst["use_only_main_island"] logger.info(f"\nLST use only main island: {use_only_main_island}") @@ -182,29 +181,33 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): azimuth = Angle(sim_config["max_az"]).wrap_at("360 deg").degree logger.info(np.asarray(list(assigned_tel_ids.values()))) LSTs_IDs = np.asarray(list(assigned_tel_ids.values())[0:4]) - LSTs_in_use = np.where(LSTs_IDs > 0)[0] + 1 #Here we select which LSTs are/is in use - + LSTs_in_use = ( + np.where(LSTs_IDs > 0)[0] + 1 + ) # Here we select which LSTs are/is in use + if len(LSTs_in_use) == 0: - LSTs_in_use = ''.join(str(k) for k in LSTs_in_use) + LSTs_in_use = "".join(str(k) for k in LSTs_in_use) elif len(LSTs_in_use) > 0: - LSTs_in_use = 'LST'+'_LST'.join(str(k) for k in LSTs_in_use) - + LSTs_in_use = "LST" + "_LST".join(str(k) for k in LSTs_in_use) + MAGICs_IDs = np.asarray(list(assigned_tel_ids.values())[4:6]) - MAGICs_in_use = np.where(MAGICs_IDs > 0)[0] + 1 #Here we select which MAGICs are/is in use - + MAGICs_in_use = ( + np.where(MAGICs_IDs > 0)[0] + 1 + ) # Here we select which MAGICs are/is in use + if len(MAGICs_in_use) == 0: - MAGICs_in_use = ''.join(str(k) for k in MAGICs_in_use) + MAGICs_in_use = "".join(str(k) for k in MAGICs_in_use) elif len(MAGICs_in_use) > 0: - MAGICs_in_use = 'MAGIC'+'_MAGIC'.join(str(k) for k in MAGICs_in_use) + MAGICs_in_use = "MAGIC" + "_MAGIC".join(str(k) for k in MAGICs_in_use) magic_clean = {} for k in MAGICs_IDs: - if k > 0: + if k > 0: magic_clean[k] = MAGICClean(camera_geoms[k], config_magic["magic_clean"]) - + output_file = ( f"{output_dir}/dl1_{particle_type}_zd_{zenith.round(3)}deg_" f"az_{azimuth.round(3)}deg_{LSTs_in_use}_{MAGICs_in_use}_run{obs_id}.h5" - ) #The files are saved with the names of all telescopes involved + ) # The files are saved with the names of all telescopes involved # Loop over every shower event logger.info("\nProcessing the events...") @@ -217,21 +220,42 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): tels_with_trigger = event.trigger.tels_with_trigger # Check if the event triggers both M1 and M2 or not - magic_stereo=(set(MAGICs_IDs).issubset(set(tels_with_trigger))) and (MAGICs_in_use=="MAGIC1_MAGIC2") #If both have trigger, then magic_stereo = True - - for tel_id in tels_with_trigger: + magic_stereo = (set(MAGICs_IDs).issubset(set(tels_with_trigger))) and ( + MAGICs_in_use == "MAGIC1_MAGIC2" + ) # If both have trigger, then magic_stereo = True - if tel_id in LSTs_IDs: ##If the ID is in the LST list, we call calibrate on the LST() + for tel_id in tels_with_trigger: + + if ( + tel_id in LSTs_IDs + ): # If the ID is in the LST list, we call calibrate on the LST() # Calibrate the LST-1 event - signal_pixels, image, peak_time = calibrate(event=event, tel_id=tel_id, obs_id=obs_id, config=config_lst, camera_geoms=camera_geoms, calibrator=calibrator_lst, is_lst=True) + signal_pixels, image, peak_time = calibrate( + event=event, + tel_id=tel_id, + obs_id=obs_id, + config=config_lst, + camera_geoms=camera_geoms, + calibrator=calibrator_lst, + is_lst=True, + ) elif tel_id in MAGICs_IDs: # Calibrate the MAGIC event - signal_pixels, image, peak_time = calibrate(event=event, tel_id=tel_id, config=config_magic, magic_clean=magic_clean, calibrator=calibrator_magic, is_lst=False) + signal_pixels, image, peak_time = calibrate( + event=event, + tel_id=tel_id, + config=config_magic, + magic_clean=magic_clean, + calibrator=calibrator_magic, + is_lst=False, + ) else: logger.info( f"--> Telescope ID {tel_id} not in LST list or MAGIC list. Please check if the IDs are OK in the configuration file" ) - if not any(signal_pixels): #So: if there is no event, we skip it and go back to the loop in the next event + if not any( + signal_pixels + ): # So: if there is no event, we skip it and go back to the loop in the next event logger.info( f"--> {event.count} event (event ID: {event.index.event_id}, " f"telescope {tel_id}) could not survive the image cleaning. " @@ -257,7 +281,7 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): # Parametrize the image hillas_params = hillas_parameters(camera_geom_masked, image_masked) - # + # if any(np.isnan(value) for value in hillas_params.values()): logger.info( f"--> {event.count} event (event ID: {event.index.event_id}, " @@ -265,7 +289,6 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): ) continue - timing_params = timing_parameters( camera_geom_masked, image_masked, peak_time_masked, hillas_params ) @@ -328,10 +351,10 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): n_islands=n_islands, magic_stereo=magic_stereo, ) - + # Reset the telescope IDs event_info.tel_id = tel_id - + # Save the parameters to the output file writer.write( "parameters", @@ -352,7 +375,6 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): for k in IDs_in_use: tel_positions_lst_magic[k] = tel_positions[k] - position_mean tel_descriptions_lst_magic[k] = tel_descriptions[k] - subarray_lst_magic = SubarrayDescription( "LST-MAGIC-Array", tel_positions_lst_magic, tel_descriptions_lst_magic @@ -361,10 +383,10 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): logger.info("\nTelescope positions:") logger.info(format_object(tel_positions)) - + # Save the subarray description subarray_lst_magic.to_hdf(output_file) - + # Save the simulation configuration with HDF5TableWriter(output_file, group_name="simulation", mode="a") as writer: writer.write("config", sim_config) @@ -374,14 +396,13 @@ def mc_dl0_to_dl1(input_file, output_dir, config, focal_length): def main(): - """ Here we collect the input parameters from the command line, load the configuration file and run mc_dl0_to_dl1()""" - + """Here we collect the input parameters from the command line, load the configuration file and run mc_dl0_to_dl1()""" + start_time = time.time() parser = argparse.ArgumentParser() - - - #Here we are simply collecting the parameters from the command line, as input file, output directory, and configuration file + + # Here we are simply collecting the parameters from the command line, as input file, output directory, and configuration file parser.add_argument( "--input-file", "-i", @@ -408,26 +429,32 @@ def main(): default="./config.yaml", help="Path to a configuration file", ) - + parser.add_argument( "--focal_length_choice", - "-f", + "-f", dest="focal_length_choice", type=str, - choices = ["nominal", "effective"], + choices=["nominal", "effective"], default="effective", help='Choice of focal length, either "effective" or "nominal". The default (and standard) value is "effective"', ) - args = parser.parse_args() #Here we select all 3 parameters collected above + args = parser.parse_args() # Here we select all 3 parameters collected above - with open(args.config_file, "rb") as f: # "rb" mode opens the file in binary format for reading - config = yaml.safe_load(f) #Here we collect the inputs from the configuration file + with open( + args.config_file, "rb" + ) as f: # "rb" mode opens the file in binary format for reading + config = yaml.safe_load( + f + ) # Here we collect the inputs from the configuration file # Checking if the input telescope list is properly organized: check_input_list(config) - config['mc_tel_ids']=dict(sorted(config['mc_tel_ids'].items())) #Sorting needed to correctly name the output file + config["mc_tel_ids"] = dict( + sorted(config["mc_tel_ids"].items()) + ) # Sorting needed to correctly name the output file # Process the input data mc_dl0_to_dl1(args.input_file, args.output_dir, config, args.focal_length_choice) @@ -439,4 +466,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/magicctapipe/scripts/lst1_magic/lst1_magic_mc_muon_analysis.py b/magicctapipe/scripts/lst1_magic/lst1_magic_mc_muon_analysis.py index 72c5c5be9..734badd6b 100644 --- a/magicctapipe/scripts/lst1_magic/lst1_magic_mc_muon_analysis.py +++ b/magicctapipe/scripts/lst1_magic/lst1_magic_mc_muon_analysis.py @@ -10,25 +10,27 @@ --config-file ./config.yaml """ +import argparse +import logging import re import time -import yaml -import logging -import argparse -import numpy as np from pathlib import Path + +import numpy as np +import yaml from astropy.table import Table -from traitlets.config import Config -from ctapipe.io import EventSource from ctapipe.calib import CameraCalibrator from ctapipe.image import ( ImageExtractor, - tailcuts_clean, apply_time_delta_cleaning, number_of_islands, + tailcuts_clean, ) +from ctapipe.io import EventSource from lstchain.image.cleaning import apply_dynamic_cleaning from lstchain.image.muon import create_muon_table +from traitlets.config import Config + from magicctapipe.image import MAGICClean from magicctapipe.image.muons import perform_muon_analysis diff --git a/magicctapipe/scripts/lst1_magic/lst1_magic_stereo_reco.py b/magicctapipe/scripts/lst1_magic/lst1_magic_stereo_reco.py index 277de8d43..1d47010e1 100644 --- a/magicctapipe/scripts/lst1_magic/lst1_magic_stereo_reco.py +++ b/magicctapipe/scripts/lst1_magic/lst1_magic_stereo_reco.py @@ -44,7 +44,13 @@ ) from ctapipe.instrument import SubarrayDescription from ctapipe.reco import HillasReconstructor -from magicctapipe.io import format_object, get_stereo_events, save_pandas_data_in_table, check_input_list + +from magicctapipe.io import ( + check_input_list, + format_object, + get_stereo_events, + save_pandas_data_in_table, +) from magicctapipe.utils import calculate_impact, calculate_mean_direction __all__ = ["calculate_pointing_separation", "stereo_reconstruction"] @@ -71,12 +77,14 @@ def calculate_pointing_separation(event_data, config): Angular distance of the LST array and MAGIC pointing directions in units of degree """ - - assigned_tel_ids = config["mc_tel_ids"] #This variable becomes a dictionary, e.g.: {'LST-1': 1, 'LST-2': 0, 'LST-3': 0, 'LST-4': 0, 'MAGIC-I': 2, 'MAGIC-II': 3} + + assigned_tel_ids = config[ + "mc_tel_ids" + ] # This variable becomes a dictionary, e.g.: {'LST-1': 1, 'LST-2': 0, 'LST-3': 0, 'LST-4': 0, 'MAGIC-I': 2, 'MAGIC-II': 3} LSTs_IDs = np.asarray(list(assigned_tel_ids.values())[0:4]) - LSTs_IDs = list(LSTs_IDs[LSTs_IDs > 0]) #Here we list only the LSTs in use + LSTs_IDs = list(LSTs_IDs[LSTs_IDs > 0]) # Here we list only the LSTs in use MAGICs_IDs = np.asarray(list(assigned_tel_ids.values())[4:6]) - MAGICs_IDs = list(MAGICs_IDs[MAGICs_IDs > 0]) #Here we list only the MAGICs in use + MAGICs_IDs = list(MAGICs_IDs[MAGICs_IDs > 0]) # Here we list only the MAGICs in use # Extract LST events df_lst = event_data.query(f"tel_id == {LSTs_IDs}") @@ -86,8 +94,12 @@ def calculate_pointing_separation(event_data, config): df_magic = df_magic.loc[df_lst.index] # Calculate the mean of the LSTs, and also of the M1 and M2 pointing directions - pnt_az_LST, pnt_alt_LST = calculate_mean_direction(lon=df_lst["pointing_az"], lat=df_lst["pointing_alt"], unit="rad") - pnt_az_magic, pnt_alt_magic = calculate_mean_direction(lon=df_magic["pointing_az"], lat=df_magic["pointing_alt"], unit="rad") + pnt_az_LST, pnt_alt_LST = calculate_mean_direction( + lon=df_lst["pointing_az"], lat=df_lst["pointing_alt"], unit="rad" + ) + pnt_az_magic, pnt_alt_magic = calculate_mean_direction( + lon=df_magic["pointing_az"], lat=df_magic["pointing_alt"], unit="rad" + ) # Calculate the angular distance of their pointing directions theta = angular_separation( @@ -121,8 +133,10 @@ def stereo_reconstruction(input_file, output_dir, config, magic_only_analysis=Fa """ config_stereo = config["stereo_reco"] - assigned_tel_ids = config["mc_tel_ids"] #This variable becomes a dictionary, e.g.: {'LST-1': 1, 'LST-2': 0, 'LST-3': 0, 'LST-4': 0, 'MAGIC-I': 2, 'MAGIC-II': 3} - + assigned_tel_ids = config[ + "mc_tel_ids" + ] # This variable becomes a dictionary, e.g.: {'LST-1': 1, 'LST-2': 0, 'LST-3': 0, 'LST-4': 0, 'MAGIC-I': 2, 'MAGIC-II': 3} + # Load the input file logger.info(f"\nInput file: {input_file}") @@ -152,22 +166,26 @@ def stereo_reconstruction(input_file, output_dir, config, magic_only_analysis=Fa LSTs_IDs = np.asarray(list(assigned_tel_ids.values())[0:4]) if magic_only_analysis: - tel_id=np.asarray(list(assigned_tel_ids.values())[:]) - used_id=tel_id[tel_id!=0] - magic_ids=[item for item in used_id if item not in LSTs_IDs] - event_data.query(f"tel_id in {magic_ids}", inplace=True) # Here we select only the events with the MAGIC tel_ids + tel_id = np.asarray(list(assigned_tel_ids.values())[:]) + used_id = tel_id[tel_id != 0] + magic_ids = [item for item in used_id if item not in LSTs_IDs] + event_data.query( + f"tel_id in {magic_ids}", inplace=True + ) # Here we select only the events with the MAGIC tel_ids logger.info(f"\nQuality cuts: {config_stereo['quality_cuts']}") - event_data = get_stereo_events(event_data, config=config, quality_cuts=config_stereo["quality_cuts"]) + event_data = get_stereo_events( + event_data, config=config, quality_cuts=config_stereo["quality_cuts"] + ) # Check the angular distance of the LST and MAGIC pointing directions tel_ids = np.unique(event_data.index.get_level_values("tel_id")).tolist() - Number_of_LSTs_in_use = len(LSTs_IDs[LSTs_IDs > 0]) + Number_of_LSTs_in_use = len(LSTs_IDs[LSTs_IDs > 0]) MAGICs_IDs = np.asarray(list(assigned_tel_ids.values())[4:6]) Number_of_MAGICs_in_use = len(MAGICs_IDs[MAGICs_IDs > 0]) - Two_arrays_are_used = (Number_of_LSTs_in_use*Number_of_MAGICs_in_use > 0) - + Two_arrays_are_used = Number_of_LSTs_in_use * Number_of_MAGICs_in_use > 0 + if (not is_simulation) and (Two_arrays_are_used): logger.info( @@ -384,10 +402,10 @@ def main(): with open(args.config_file, "rb") as f: config = yaml.safe_load(f) - + # Checking if the input telescope list is properly organized: check_input_list(config) - + # Process the input data stereo_reconstruction(args.input_file, args.output_dir, config, args.magic_only) @@ -398,4 +416,4 @@ def main(): if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/magicctapipe/scripts/lst1_magic/lst1_magic_train_rfs.py b/magicctapipe/scripts/lst1_magic/lst1_magic_train_rfs.py index d5bc2fbf8..b95a98ccc 100644 --- a/magicctapipe/scripts/lst1_magic/lst1_magic_train_rfs.py +++ b/magicctapipe/scripts/lst1_magic/lst1_magic_train_rfs.py @@ -40,6 +40,7 @@ import numpy as np import pandas as pd import yaml + from magicctapipe.io import format_object, load_train_data_files from magicctapipe.io.io import GROUP_INDEX_TRAIN from magicctapipe.reco import DispRegressor, EnergyRegressor, EventClassifier @@ -54,7 +55,11 @@ logger = logging.getLogger(__name__) logger.addHandler(logging.StreamHandler()) logger.setLevel(logging.INFO) -TEL_NAMES = {1: "LST-1", 2: "MAGIC-I", 3: "MAGIC-II"} #TODO: REMOVE WHEN SWITCHING TO THE NEW RFs IMPLEMENTTATION (1 RF PER TELESCOPE) +TEL_NAMES = { + 1: "LST-1", + 2: "MAGIC-I", + 3: "MAGIC-II", +} # TODO: REMOVE WHEN SWITCHING TO THE NEW RFs IMPLEMENTTATION (1 RF PER TELESCOPE) # True event class of gamma and proton MCs diff --git a/magicctapipe/scripts/lst1_magic/magic_calib_to_dl1.py b/magicctapipe/scripts/lst1_magic/magic_calib_to_dl1.py index ed56cd2b0..971f67041 100644 --- a/magicctapipe/scripts/lst1_magic/magic_calib_to_dl1.py +++ b/magicctapipe/scripts/lst1_magic/magic_calib_to_dl1.py @@ -50,8 +50,14 @@ from ctapipe.instrument import SubarrayDescription from ctapipe.io import HDF5TableWriter from ctapipe_io_magic import MAGICEventSource + from magicctapipe.image import MAGICClean -from magicctapipe.io import RealEventInfoContainer, SimEventInfoContainer, format_object, check_input_list +from magicctapipe.io import ( + RealEventInfoContainer, + SimEventInfoContainer, + check_input_list, + format_object, +) from magicctapipe.utils import calculate_disp, calculate_impact __all__ = ["magic_calib_to_dl1"] @@ -89,7 +95,9 @@ def magic_calib_to_dl1(input_file, output_dir, config, max_events, process_run=F # Load the input file logger.info(f"\nInput file: {input_file}") - event_source = MAGICEventSource(input_file, process_run=process_run, max_events=max_events) + event_source = MAGICEventSource( + input_file, process_run=process_run, max_events=max_events + ) is_simulation = event_source.is_simulation logger.info(f"\nIs simulation: {is_simulation}") @@ -391,7 +399,9 @@ def main(): check_input_list(config) # Process the input data - magic_calib_to_dl1(args.input_file, args.output_dir, config, args.max_events, args.process_run) + magic_calib_to_dl1( + args.input_file, args.output_dir, config, args.max_events, args.process_run + ) logger.info("\nDone.") process_time = time.time() - start_time diff --git a/magicctapipe/scripts/lst1_magic/merge_hdf_files.py b/magicctapipe/scripts/lst1_magic/merge_hdf_files.py index 7b747935d..56376382e 100644 --- a/magicctapipe/scripts/lst1_magic/merge_hdf_files.py +++ b/magicctapipe/scripts/lst1_magic/merge_hdf_files.py @@ -38,7 +38,7 @@ import tables from ctapipe.instrument import SubarrayDescription -__all__ = ["merge_hdf_files","write_data_to_table"] +__all__ = ["merge_hdf_files", "write_data_to_table"] logger = logging.getLogger(__name__) logger.addHandler(logging.StreamHandler()) diff --git a/magicctapipe/scripts/lst1_magic/muon_analysis_LST_or_MAGIC_data.py b/magicctapipe/scripts/lst1_magic/muon_analysis_LST_or_MAGIC_data.py index 924e3e1bb..cfbd067f9 100644 --- a/magicctapipe/scripts/lst1_magic/muon_analysis_LST_or_MAGIC_data.py +++ b/magicctapipe/scripts/lst1_magic/muon_analysis_LST_or_MAGIC_data.py @@ -14,14 +14,15 @@ --config-file ./config.yaml """ import argparse -import yaml import logging -import numpy as np from pathlib import Path +import numpy as np +import yaml from astropy.table import Table from ctapipe.io import EventSource from lstchain.image.muon import create_muon_table + from magicctapipe.image import MAGICClean from magicctapipe.image.muons import perform_muon_analysis diff --git a/magicctapipe/scripts/mars/convert_superstar_to_dl1.py b/magicctapipe/scripts/mars/convert_superstar_to_dl1.py index 6d2e96fb3..3c5052eb5 100644 --- a/magicctapipe/scripts/mars/convert_superstar_to_dl1.py +++ b/magicctapipe/scripts/mars/convert_superstar_to_dl1.py @@ -1,32 +1,30 @@ #!/usr/bin/env python +import argparse import re import sys -import argparse from pathlib import Path -import uproot +import astropy.units as u import numpy as np - +import uproot from astropy.table import QTable, vstack -import astropy.units as u - from ctapipe.containers import ( CameraHillasParametersContainer, - LeakageContainer, CameraTimingParametersContainer, + LeakageContainer, ReconstructedGeometryContainer, ) -from ctapipe.io import HDF5TableWriter -from ctapipe.core.container import Container, Field from ctapipe.coordinates import CameraFrame +from ctapipe.core.container import Container, Field from ctapipe.instrument import ( - TelescopeDescription, - SubarrayDescription, - OpticsDescription, CameraDescription, CameraReadout, + OpticsDescription, + SubarrayDescription, + TelescopeDescription, ) +from ctapipe.io import HDF5TableWriter magic_optics = OpticsDescription( "MAGIC", diff --git a/magicctapipe/scripts/mars/effective_area_melibea.py b/magicctapipe/scripts/mars/effective_area_melibea.py index a7cd29009..74f2c2a4d 100644 --- a/magicctapipe/scripts/mars/effective_area_melibea.py +++ b/magicctapipe/scripts/mars/effective_area_melibea.py @@ -1,9 +1,7 @@ -import numpy as np - -from astropy.table import QTable, vstack import astropy.units as u - +import numpy as np import uproot +from astropy.table import QTable, vstack from pyirf.simulations import SimulatedEventsInfo melibea_columns = { diff --git a/magicctapipe/scripts/mars/magic_calibrated_to_dl1.py b/magicctapipe/scripts/mars/magic_calibrated_to_dl1.py index 658a9528f..9425e627b 100644 --- a/magicctapipe/scripts/mars/magic_calibrated_to_dl1.py +++ b/magicctapipe/scripts/mars/magic_calibrated_to_dl1.py @@ -2,27 +2,20 @@ # coding: utf-8 import argparse +import copy import glob import re -import yaml -import copy from pathlib import Path import numpy as np - -from ctapipe_io_magic import MAGICEventSource - +import yaml from ctapipe.containers import ( - IntensityStatisticsContainer, ImageParametersContainer, - TimingParametersContainer, + IntensityStatisticsContainer, PeakTimeStatisticsContainer, + TimingParametersContainer, ) - from ctapipe.coordinates import TelescopeFrame - -from ctapipe.io import DataWriter - from ctapipe.image import ( concentration_parameters, descriptive_statistics, @@ -30,16 +23,11 @@ morphology_parameters, timing_parameters, ) +from ctapipe.io import DataWriter +from ctapipe_io_magic import MAGICEventSource -from magicctapipe.image import ( - MAGICClean, - get_leakage, -) - -from magicctapipe.utils import ( - info_message, -) - +from magicctapipe.image import MAGICClean, get_leakage +from magicctapipe.utils import info_message DEFAULT_IMAGE_PARAMETERS = ImageParametersContainer() DEFAULT_TRUE_IMAGE_PARAMETERS = ImageParametersContainer() @@ -318,8 +306,8 @@ def magic_calibrated_to_dl1(input_mask, cleaning_config): ) try: - telescope_type = re.findall("(.*)[_\d]+", telescope)[0] - except: + telescope_type = re.findall("(.*)[_\\d]+", telescope)[0] + except Exception: ValueError( f'Can not recognize the telescope type from name "{telescope}"' ) diff --git a/magicctapipe/scripts/mars/mars_images_to_hdf5.py b/magicctapipe/scripts/mars/mars_images_to_hdf5.py index 745aee39f..daeea1ab1 100644 --- a/magicctapipe/scripts/mars/mars_images_to_hdf5.py +++ b/magicctapipe/scripts/mars/mars_images_to_hdf5.py @@ -7,18 +7,16 @@ by magic-cta-pipe. """ +import argparse import re import sys -import argparse from pathlib import Path import numpy as np -import uproot import tables - +import uproot from ctapipe.core.container import Container, Field -from ctapipe.io import HDF5TableWriter, HDF5TableReader - +from ctapipe.io import HDF5TableReader, HDF5TableWriter from ctapipe_io_magic import MARSDataLevel diff --git a/magicctapipe/scripts/performance/apply_rfs_MAGIC_LST.py b/magicctapipe/scripts/performance/apply_rfs_MAGIC_LST.py index eb2996cce..509b78778 100644 --- a/magicctapipe/scripts/performance/apply_rfs_MAGIC_LST.py +++ b/magicctapipe/scripts/performance/apply_rfs_MAGIC_LST.py @@ -1,24 +1,25 @@ # coding: utf-8 -import time import argparse import glob import os +import time + import pandas as pd from magicctapipe.reco.event_processing import ( - EnergyEstimatorPandas, DirectionEstimatorPandas, + EnergyEstimatorPandas, EventClassifierPandas, ) -from magicctapipe.utils.tels import check_tel_ids, get_array_tel_descriptions -from magicctapipe.utils.utils import print_title, info_message, print_elapsed_time from magicctapipe.utils.filedir import ( - load_cfg_file, - out_file_h5_reco, check_folder, + load_cfg_file, load_dl1_data_stereo, + out_file_h5_reco, ) +from magicctapipe.utils.tels import check_tel_ids, get_array_tel_descriptions +from magicctapipe.utils.utils import info_message, print_elapsed_time, print_title PARSER = argparse.ArgumentParser( description="Apply random forests. For stereo data.", @@ -169,7 +170,7 @@ def apply_rfs_stereo(config_file, only_mc_test, only_data_test): try: mc_ = pd.read_hdf(file, key="dl1/mc_header") mc_.to_hdf(out_file, key="dl2/mc_header") - except: + except Exception: mc_ = pd.read_hdf(file, key="dl2/mc_header") mc_.to_hdf(out_file, key="dl2/mc_header") except Exception as e: diff --git a/magicctapipe/scripts/performance/make_irfs_MAGIC_LST.py b/magicctapipe/scripts/performance/make_irfs_MAGIC_LST.py index 23aa7c243..d75396d3d 100644 --- a/magicctapipe/scripts/performance/make_irfs_MAGIC_LST.py +++ b/magicctapipe/scripts/performance/make_irfs_MAGIC_LST.py @@ -1,54 +1,50 @@ -import os -import time -import shutil +import argparse import logging import operator -import argparse +import os +import shutil +import time +import astropy.units as u import numpy as np from astropy import table -import astropy.units as u from astropy.io import fits - +from pyirf.benchmarks import angular_resolution, energy_bias_resolution from pyirf.binning import ( - create_bins_per_decade, add_overflow_bins, + create_bins_per_decade, create_histogram_table, ) +from pyirf.cut_optimization import optimize_gh_cut from pyirf.cuts import calculate_percentile_cut, evaluate_binned_cut -from pyirf.sensitivity import calculate_sensitivity, estimate_background -from pyirf.utils import calculate_theta, calculate_source_fov_offset -from pyirf.benchmarks import energy_bias_resolution, angular_resolution - -from pyirf.spectral import ( - calculate_event_weights, - PowerLaw, - CRAB_HEGRA, - IRFDOC_PROTON_SPECTRUM, - IRFDOC_ELECTRON_SPECTRUM, +from pyirf.io import ( + create_aeff2d_hdu, + create_background_2d_hdu, + create_energy_dispersion_hdu, + create_psf_table_hdu, + create_rad_max_hdu, ) -from pyirf.cut_optimization import optimize_gh_cut - from pyirf.irf import ( + background_2d, effective_area_per_energy, energy_dispersion, psf_table, - background_2d, ) - -from pyirf.io import ( - create_aeff2d_hdu, - create_psf_table_hdu, - create_energy_dispersion_hdu, - create_rad_max_hdu, - create_background_2d_hdu, +from pyirf.sensitivity import calculate_sensitivity, estimate_background +from pyirf.spectral import ( + CRAB_HEGRA, + IRFDOC_ELECTRON_SPECTRUM, + IRFDOC_PROTON_SPECTRUM, + PowerLaw, + calculate_event_weights, ) +from pyirf.utils import calculate_source_fov_offset, calculate_theta -from magicctapipe.utils.filedir import check_folder, load_cfg_file from magicctapipe.irfs.utils import ( plot_irfs_MAGIC_LST, read_dl2_mcp_to_pyirf_MAGIC_LST_list, ) +from magicctapipe.utils.filedir import check_folder, load_cfg_file from magicctapipe.utils.utils import print_elapsed_time, print_title PARSER = argparse.ArgumentParser( @@ -409,7 +405,7 @@ def make_irfs_MAGIC_LST(config_file): len(gammas_sel_en[gammas_sel_en["selected"] == True]) / len(gammas_sel_en) ) - except: + except Exception: gamma_efficiency["eff_gh"][i] = -1 gamma_efficiency["eff"][i] = -1 hdus.append(fits.BinTableHDU(gamma_efficiency, name="GAMMA_EFFICIENCY")) diff --git a/magicctapipe/scripts/performance/stereo_reco_MAGIC_LST.py b/magicctapipe/scripts/performance/stereo_reco_MAGIC_LST.py index 35a8558e8..b037cdb35 100644 --- a/magicctapipe/scripts/performance/stereo_reco_MAGIC_LST.py +++ b/magicctapipe/scripts/performance/stereo_reco_MAGIC_LST.py @@ -1,42 +1,38 @@ # coding: utf-8 -import os -import time +import argparse import copy import glob -import scipy +import os import select import sys -import argparse -import matplotlib.pyplot as plt +import time import astropy.units as u -from astropy.coordinates import SkyCoord, AltAz, EarthLocation +import matplotlib.pyplot as plt +import scipy +from astropy.coordinates import AltAz, EarthLocation, SkyCoord from astropy.time import Time - -from ctapipe.io import SimTelEventSource -from ctapipe.io import HDF5TableWriter from ctapipe.calib import CameraCalibrator +from ctapipe.containers import ImageParametersContainer from ctapipe.coordinates import TelescopeFrame from ctapipe.image.cleaning import tailcuts_clean from ctapipe.image.morphology import number_of_islands - +from ctapipe.io import HDF5TableWriter, SimTelEventSource from ctapipe.reco import HillasReconstructor -from ctapipe.containers import ImageParametersContainer from ctapipe.visualization import ArrayDisplay from magicctapipe.image import MAGICClean, clean_image_params, get_num_islands_MAGIC -from magicctapipe.utils import calculate_impact, check_folder -from magicctapipe.utils.filedir import load_cfg_file, out_file_h5 -from magicctapipe.utils.utils import print_title, print_elapsed_time -from magicctapipe.utils.tels import check_tel_ids from magicctapipe.reco.stereo import ( + StereoInfoContainer, check_stereo, write_hillas, write_stereo, - StereoInfoContainer, ) - +from magicctapipe.utils import calculate_impact, check_folder +from magicctapipe.utils.filedir import load_cfg_file, out_file_h5 +from magicctapipe.utils.tels import check_tel_ids +from magicctapipe.utils.utils import print_elapsed_time, print_title PARSER = argparse.ArgumentParser( description="Stereo Reconstruction MAGIC + LST", diff --git a/magicctapipe/scripts/performance/train_rfs_MAGIC_LST.py b/magicctapipe/scripts/performance/train_rfs_MAGIC_LST.py index a088b27b5..270d14ec1 100644 --- a/magicctapipe/scripts/performance/train_rfs_MAGIC_LST.py +++ b/magicctapipe/scripts/performance/train_rfs_MAGIC_LST.py @@ -1,49 +1,51 @@ # coding: utf-8 -import time -import os -import glob import argparse -import pandas as pd -import numpy as np +import glob +import os +import time + import matplotlib.pyplot as plt +import numpy as np +import pandas as pd from astropy.coordinates.angle_utilities import angular_separation -from magicctapipe.utils.plot import ( - save_plt, - load_default_plot_settings, - load_default_plot_settings_02, -) -from magicctapipe.utils.utils import print_title, info_message, print_elapsed_time -from magicctapipe.reco.energy_utils import plot_migmatrix, evaluate_performance_energy -from magicctapipe.reco.direction_utils import compute_separation_angle_direction from magicctapipe.reco.classifier_utils import ( - get_weights_classifier, check_train_test_intersections_classifier, + evaluate_performance_classifier, + get_weights_classifier, load_init_data_classifier, print_par_imp_classifier, - evaluate_performance_classifier, +) +from magicctapipe.reco.direction_utils import compute_separation_angle_direction +from magicctapipe.reco.energy_utils import evaluate_performance_energy, plot_migmatrix +from magicctapipe.reco.event_processing import ( + DirectionEstimatorPandas, + EnergyEstimatorPandas, + EventClassifierPandas, ) from magicctapipe.reco.global_utils import ( - get_weights_mc_dir_class, - compute_event_weights, check_train_test_intersections, -) -from magicctapipe.utils.tels import ( - check_tel_ids, - get_tel_name, - get_array_tel_descriptions, + compute_event_weights, + get_weights_mc_dir_class, ) from magicctapipe.utils.filedir import ( - load_cfg_file_check, check_folder, - load_dl1_data_stereo_list_selected, + load_cfg_file_check, load_dl1_data_stereo_list, + load_dl1_data_stereo_list_selected, ) - -from magicctapipe.reco.event_processing import EventClassifierPandas -from magicctapipe.reco.event_processing import EnergyEstimatorPandas -from magicctapipe.reco.event_processing import DirectionEstimatorPandas +from magicctapipe.utils.plot import ( + load_default_plot_settings, + load_default_plot_settings_02, + save_plt, +) +from magicctapipe.utils.tels import ( + check_tel_ids, + get_array_tel_descriptions, + get_tel_name, +) +from magicctapipe.utils.utils import info_message, print_elapsed_time, print_title PARSER = argparse.ArgumentParser( description="Trains random forests for stereo data", diff --git a/magicctapipe/utils/__init__.py b/magicctapipe/utils/__init__.py index 955927f23..d9c3f84ac 100644 --- a/magicctapipe/utils/__init__.py +++ b/magicctapipe/utils/__init__.py @@ -1,68 +1,51 @@ -from .badpixels import ( - MAGICBadPixelsCalc, -) - -from .camera_geometry import ( - scale_camera_geometry, - reflected_camera_geometry, -) - +from .badpixels import MAGICBadPixelsCalc +from .camera_geometry import reflected_camera_geometry, scale_camera_geometry from .filedir import ( + check_common_keys, + check_folder, + convert_np_list_dict, + drop_keys, load_cfg_file, load_cfg_file_check, - check_folder, - load_dl1_data_stereo_list_selected, - load_dl1_data_stereo_list, - load_dl1_data_stereo, load_dl1_data_mono, - drop_keys, - check_common_keys, - out_file_h5_no_run, + load_dl1_data_stereo, + load_dl1_data_stereo_list, + load_dl1_data_stereo_list_selected, out_file_h5, + out_file_h5_no_run, out_file_h5_reco, read_mc_header, save_yaml_np, - convert_np_list_dict, -) - -from .gti import ( - identify_time_edges, - intersect_time_intervals, - GTIGenerator, ) - from .functions import ( + HEIGHT_ORM, + LAT_ORM, + LON_ORM, calculate_disp, calculate_impact, calculate_mean_direction, calculate_off_coordinates, transform_altaz_to_radec, ) - -from .plot import ( - save_plt, - load_default_plot_settings, - load_default_plot_settings_02, -) - +from .gti import GTIGenerator, identify_time_edges, intersect_time_intervals +from .plot import load_default_plot_settings, load_default_plot_settings_02, save_plt from .tels import ( - tel_ids_2_num, - num_2_tel_ids, - get_tel_descriptions, + check_tel_ids, + convert_positions_dict, get_array_tel_descriptions, + get_tel_descriptions, get_tel_ids_dl1, - convert_positions_dict, - check_tel_ids, - intersec_tel_ids, get_tel_name, + intersec_tel_ids, + num_2_tel_ids, + tel_ids_2_num, ) - from .utils import ( info_message, - print_elapsed_time, make_elapsed_time_str, - print_title, make_title_str, + print_elapsed_time, + print_title, resource_file, ) @@ -112,4 +95,7 @@ "make_title_str", "resource_file", "get_key_if_exists", + "LON_ORM", + "LAT_ORM", + "HEIGHT_ORM", ] diff --git a/magicctapipe/utils/badpixels.py b/magicctapipe/utils/badpixels.py index 3654df6cd..e3a1dd1e3 100644 --- a/magicctapipe/utils/badpixels.py +++ b/magicctapipe/utils/badpixels.py @@ -1,5 +1,5 @@ -from astropy.time import Time import numpy as np +from astropy.time import Time from ctapipe.instrument import CameraGeometry __all__ = [ diff --git a/magicctapipe/utils/filedir.py b/magicctapipe/utils/filedir.py index 543a6d5af..c1ba12819 100644 --- a/magicctapipe/utils/filedir.py +++ b/magicctapipe/utils/filedir.py @@ -1,9 +1,10 @@ +import copy import os import sys -import yaml -import copy -import pandas as pd + import numpy as np +import pandas as pd +import yaml __all__ = [ "load_cfg_file", @@ -216,7 +217,7 @@ def load_dl1_data_stereo(file, drop=False, slope_abs=False): data = data_hillas.merge(data_stereo, on=common_keys) # Index data.set_index(["obs_id", "event_id", "tel_id"], inplace=True) - except: + except Exception: data = pd.read_hdf(file, key="dl2/reco") data.sort_index(inplace=True) # Get absolute value of slope, if needed diff --git a/magicctapipe/utils/functions.py b/magicctapipe/utils/functions.py index aebc363a9..49fe50995 100644 --- a/magicctapipe/utils/functions.py +++ b/magicctapipe/utils/functions.py @@ -20,6 +20,9 @@ "calculate_mean_direction", "calculate_off_coordinates", "transform_altaz_to_radec", + "LON_ORM", + "LAT_ORM", + "HEIGHT_ORM", ] # The geographic coordinate of ORM diff --git a/magicctapipe/utils/gti.py b/magicctapipe/utils/gti.py index 89e3641b7..d1b967a21 100644 --- a/magicctapipe/utils/gti.py +++ b/magicctapipe/utils/gti.py @@ -1,8 +1,8 @@ # coding: utf-8 +import pandas import scipy import uproot -import pandas from .utils import info_message diff --git a/magicctapipe/utils/plot.py b/magicctapipe/utils/plot.py index 64fca9ae9..762acae8c 100644 --- a/magicctapipe/utils/plot.py +++ b/magicctapipe/utils/plot.py @@ -1,4 +1,5 @@ import os + import matplotlib.pylab as plt __all__ = [ diff --git a/magicctapipe/utils/tels.py b/magicctapipe/utils/tels.py index b48ec692e..e934187f7 100644 --- a/magicctapipe/utils/tels.py +++ b/magicctapipe/utils/tels.py @@ -1,7 +1,6 @@ -from astropy import units as u import numpy as np - -from ctapipe.instrument import CameraGeometry, TelescopeDescription, OpticsDescription +from astropy import units as u +from ctapipe.instrument import CameraGeometry, OpticsDescription, TelescopeDescription __all__ = [ "tel_ids_2_num", diff --git a/magicctapipe/utils/utils.py b/magicctapipe/utils/utils.py index 7c90829d0..a291e0e27 100644 --- a/magicctapipe/utils/utils.py +++ b/magicctapipe/utils/utils.py @@ -1,4 +1,5 @@ import datetime + import numpy as np try: @@ -140,6 +141,7 @@ def make_title_str(title, style_char="=", in_space=3, width_char=80): s = f"{s1}{s2}{s3}" return s + def resource_file(filename): """Get the absoulte path of magicctapipe resource files.""" return files("magicctapipe").joinpath("resources", filename) diff --git a/notebooks/check_event_coincidence.ipynb b/notebooks/check_event_coincidence.ipynb index c9c23f245..d626053f9 100644 --- a/notebooks/check_event_coincidence.ipynb +++ b/notebooks/check_event_coincidence.ipynb @@ -64,9 +64,7 @@ "# === Settings ===\n", "# ================\n", "\n", - "input_file_mask = (\n", - " \"/home/evisentin/dataout_magic_lst/dl1/data/coincidence/lst_single_run_stereo/run_03265/\"\n", - ")\n", + "input_file_mask = \"/home/evisentin/dataout_magic_lst/dl1/data/coincidence/lst_single_run_stereo/run_03265/\"\n", "\n", "# ============\n", "# === Main ===\n", diff --git a/setup.py b/setup.py index 58ec11490..b283ec672 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,8 @@ # Licensed under a 3-clause BSD style license - see LICENSE.rst import os -from setuptools import setup, find_packages + +from setuptools import find_packages, setup entry_points = {} entry_points["console_scripts"] = [ @@ -34,20 +35,20 @@ use_scm_version={"write_to": os.path.join("magicctapipe", "_version.py")}, packages=find_packages(), install_requires=[ - 'lstchain~=0.9.6', - 'ctapipe~=0.12.0', - 'ctapipe_io_magic~=0.4.7', - 'ctaplot~=0.5.5', - 'gammapy~=0.19.0', - 'uproot~=4.1', - 'numba<=0.56', - 'numpy<1.22.0a0', - 'joblib', - 'pandas', - 'pyirf~=0.6.0', - 'seaborn', - 'scikit-learn', - 'setuptools_scm', + "lstchain~=0.9.6", + "ctapipe~=0.12.0", + "ctapipe_io_magic~=0.4.7", + "ctaplot~=0.5.5", + "gammapy~=0.19.0", + "uproot~=4.1", + "numba<=0.56", + "numpy<1.22.0a0", + "joblib", + "pandas", + "pyirf~=0.6.0", + "seaborn", + "scikit-learn", + "setuptools_scm", ], entry_points=entry_points, extras_require={