From 79ffaf6ee86c9cd5ac36164d506ad0b2f3ff141c Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Wed, 28 Feb 2024 17:36:43 +0100 Subject: [PATCH 01/28] Look for the closest declination line to choose the RF model to produce the DL2 files --- src/osa/paths.py | 92 +++++++++++++++++++++++++++++++++ src/osa/scripts/datasequence.py | 3 +- src/osa/utils/utils.py | 21 ++++++++ 3 files changed, 115 insertions(+), 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 377a4e6e..c7cc04c1 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -7,9 +7,11 @@ from typing import List import subprocess import time +import os import lstchain from astropy.table import Table +from astropy.coordinates import SkyCoord from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) @@ -397,6 +399,7 @@ def create_longterm_symlink(cherenkov_job_id: str = None): else: log.warning(f"Job {cherenkov_job_id} (lstchain_cherenkov_transparency) did not finish successfully.") + def dl1_datacheck_longterm_file_exits() -> bool: """Return true if the longterm DL1 datacheck file was already produced.""" nightdir = utils.date_to_dir(options.date) @@ -404,3 +407,92 @@ def dl1_datacheck_longterm_file_exits() -> bool: longterm_file = longterm_dir / options.prod_id / nightdir / f"DL1_datacheck_{nightdir}.h5" return longterm_file.exists() + +def convert_dec_string(dec_str: str) -> float: + """Return the declination angle in degrees corresponding to a + given string of the form "dec_XXXX" or "dec_min_XXXX".""" + # Split the string into parts + parts = dec_str.split('_') + + # Extract the sign, degrees, and minutes + sign = 1 if 'min' not in parts else -1 + degrees = int(parts[-1]) + + # Calculate the numerical value + dec_value = sign * (degrees / 100) + + return dec_value + + +def get_corresponding_string(list1: list, list2: list) -> dict: + """Return a dictionary created from two given lists.""" + corresponding_dict = {} + for index, element in enumerate(list2): + corresponding_dict[element] = list1[index] + return corresponding_dict + + +def get_latest_RF_model_path(dec_str: str) -> Path: + """Get the path of the most recent version of RF models for a given declination, + excluding the ones produced for the source-dependent analysis.""" + BASE_MODELS = Path("/fefs/aswg/data/models/AllSky") + # make sure the RF models correspond to the current version of lstchain + current_version = get_major_version(utils.get_lstchain_version()) + list_nodes = sorted(BASE_MODELS.rglob(f"*{current_version}*/{dec_str}"), key=os.path.getmtime) + + log.debug(f"Found len(list_nodes) paths with {current_version} corresponding to {dec_str}:") + for path in list_nodes: + log.debug(path) + + # remove from the list the models produced for the source-dependent analysis + for i in list_nodes: + if "srcdep" in str(i): + list_nodes.remove(i) + + return list_nodes[-1] + + +def get_RF_model(run_str: str) -> Path: + """Get the path of the RF model to be used in the DL2 production for a given run.""" + run_catalog_dir = Path("/fefs/aswg/data/real/monitoring/RunCatalog") + run_catalog_file = run_catalog_dir / f"RunCatalog_{options.date}.ecsv" + run_catalog = Table.read(run_catalog_file) + run = run_catalog[run_catalog["run_id"]==int(run_str)] + + try: + target_name = run["source_name"] + source_coordinates = SkyCoord.from_name(target_name) + source_dec = source_coordinates.dec.value + + except TypeError: + source_dec = run["source_dec"][0] + + source_culmination = utils.culmination_angle(source_dec) + + dec_list = os.listdir("/fefs/aswg/data/mc/DL0/LSTProd2/TrainingDataset/Protons")[:-2] + + # Convert each string in the list to numerical values + dec_values = [convert_dec_string(dec) for dec in dec_list] + + closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + closest_dec_culmination = utils.culmination_angle(closest_declination) + log.debug( + f"The declination closest to {source_dec} is: {closest_declination}." + "Checking if the culmination angle is larger than the one of the target source." + ) + + if closest_dec_culmination > source_culmination: + # If the culmination angle of the closest declination line is larger than for the source, + # remove it from the declination lines list and look for the second closest declination line. + corresponding_dict = get_corresponding_string(dec_list, dec_values) + corresponding_string = corresponding_dict[closest_declination] + dec_values.remove(closest_declination) + dec_list.remove(corresponding_string) + closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + + log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") + + corresponding_dict = get_corresponding_string(dec_list, dec_values) + corresponding_string = corresponding_dict[closest_declination] + + return get_latest_RF_model_path(corresponding_string) \ No newline at end of file diff --git a/src/osa/scripts/datasequence.py b/src/osa/scripts/datasequence.py index 0a4fe02d..9b5a56fd 100644 --- a/src/osa/scripts/datasequence.py +++ b/src/osa/scripts/datasequence.py @@ -12,6 +12,7 @@ from osa.utils.cliopts import data_sequence_cli_parsing from osa.utils.logging import myLogger from osa.utils.utils import date_to_dir +from osa.paths import get_RF_model __all__ = ["data_sequence", "r0_to_dl1", "dl1_to_dl2", "dl1ab", "dl1_datacheck"] @@ -265,7 +266,7 @@ def dl1_to_dl2(run_str: str) -> int: dl1ab_subdirectory = Path(options.directory) / options.dl1_prod_id dl2_subdirectory = Path(options.directory) / options.dl2_prod_id dl2_config = Path(cfg.get("lstchain", "dl2_config")) - rf_models_directory = Path(cfg.get("lstchain", "RF_MODELS")) + rf_models_directory = get_RF_model(run_str) dl1_file = dl1ab_subdirectory / f"dl1_LST-1.Run{run_str}.h5" command = cfg.get("lstchain", "dl1_to_dl2") diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index b4e9adbe..245264b3 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -8,6 +8,8 @@ from datetime import datetime, timedelta from pathlib import Path from socket import gethostname +from gammapy.data import observatory_locations +from astropy import units as u import osa.paths from osa.configs import options @@ -15,6 +17,7 @@ from osa.utils.iofile import write_to_file from osa.utils.logging import myLogger + __all__ = [ "get_lstchain_version", "date_to_dir", @@ -285,3 +288,21 @@ def wait_for_daytime(start=8, end=18): while time.localtime().tm_hour <= start or time.localtime().tm_hour >= end: log.info("Waiting for sunrise to not interfere with the data-taking. Sleeping.") time.sleep(3600) + + +def culmination_angle(dec: int) -> float: + """ + Calculate culmination angle for a given declination. + + Parameters + ---------- + dec: int + declination in degrees + + Returns + ------- + Culmination angle in degrees + """ + location = observatory_locations["cta_north"] + Lat = location.lat # latitude of the LST1 site + return abs(Lat - dec*u.deg).value From ce2fab168128fc62db260038ae9b9ac3af4a2443 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 10:58:21 +0100 Subject: [PATCH 02/28] Get the path names from the cfg file --- src/osa/configs/sequencer.cfg | 1 + src/osa/paths.py | 28 ++++++---------------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/src/osa/configs/sequencer.cfg b/src/osa/configs/sequencer.cfg index a5442bb1..d17232e5 100644 --- a/src/osa/configs/sequencer.cfg +++ b/src/osa/configs/sequencer.cfg @@ -56,6 +56,7 @@ merge_dl1_datacheck: True dl1b_config: /software/lstchain/data/lstchain_standard_config.json dl2_config: /software/lstchain/data/lstchain_standard_config.json rf_models: /data/models/prod5/zenith_20deg/20201023_v0.6.3 +mc_prod: 20240131_allsky_v0.10.5_all_dec_base dl3_config: /software/lstchain/data/dl3_std_config.json max_tries: 3 diff --git a/src/osa/paths.py b/src/osa/paths.py index c7cc04c1..40a5d117 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -430,31 +430,11 @@ def get_corresponding_string(list1: list, list2: list) -> dict: for index, element in enumerate(list2): corresponding_dict[element] = list1[index] return corresponding_dict - - -def get_latest_RF_model_path(dec_str: str) -> Path: - """Get the path of the most recent version of RF models for a given declination, - excluding the ones produced for the source-dependent analysis.""" - BASE_MODELS = Path("/fefs/aswg/data/models/AllSky") - # make sure the RF models correspond to the current version of lstchain - current_version = get_major_version(utils.get_lstchain_version()) - list_nodes = sorted(BASE_MODELS.rglob(f"*{current_version}*/{dec_str}"), key=os.path.getmtime) - - log.debug(f"Found len(list_nodes) paths with {current_version} corresponding to {dec_str}:") - for path in list_nodes: - log.debug(path) - - # remove from the list the models produced for the source-dependent analysis - for i in list_nodes: - if "srcdep" in str(i): - list_nodes.remove(i) - - return list_nodes[-1] def get_RF_model(run_str: str) -> Path: """Get the path of the RF model to be used in the DL2 production for a given run.""" - run_catalog_dir = Path("/fefs/aswg/data/real/monitoring/RunCatalog") + run_catalog_dir = Path(cfg.get(options.tel_id, "RUN_CATALOG")) run_catalog_file = run_catalog_dir / f"RunCatalog_{options.date}.ecsv" run_catalog = Table.read(run_catalog_file) run = run_catalog[run_catalog["run_id"]==int(run_str)] @@ -495,4 +475,8 @@ def get_RF_model(run_str: str) -> Path: corresponding_dict = get_corresponding_string(dec_list, dec_values) corresponding_string = corresponding_dict[closest_declination] - return get_latest_RF_model_path(corresponding_string) \ No newline at end of file + rf_models_dir = Path(cfg.get("lstchain", "rf_models")) + mc_prod = cfg.get("lstchain", "mc_prod") + rf_model_path = rf_models_dir / mc_prod / corresponding_string + + return rf_model_path \ No newline at end of file From 147e88ce5ddca33b6b636ec9317641af92c1cbef Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 12:03:33 +0100 Subject: [PATCH 03/28] Get the source declination from the TCU database (not from the run catalog) --- src/osa/paths.py | 5 +++-- src/osa/utils/utils.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 40a5d117..83414951 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -438,14 +438,15 @@ def get_RF_model(run_str: str) -> Path: run_catalog_file = run_catalog_dir / f"RunCatalog_{options.date}.ecsv" run_catalog = Table.read(run_catalog_file) run = run_catalog[run_catalog["run_id"]==int(run_str)] + target_name = run["source_name"] try: - target_name = run["source_name"] source_coordinates = SkyCoord.from_name(target_name) source_dec = source_coordinates.dec.value except TypeError: - source_dec = run["source_dec"][0] + tcu_server = cfg.get("database", "tcu_db") + source_dec = utils.get_source_dec_from_TCU(target_name, tcu_server) source_culmination = utils.culmination_angle(source_dec) diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 245264b3..49e46905 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -10,6 +10,7 @@ from socket import gethostname from gammapy.data import observatory_locations from astropy import units as u +from pymongo import MongoClient import osa.paths from osa.configs import options @@ -306,3 +307,11 @@ def culmination_angle(dec: int) -> float: location = observatory_locations["cta_north"] Lat = location.lat # latitude of the LST1 site return abs(Lat - dec*u.deg).value + + +def get_source_dec_from_TCU(source_name: str, tcu_server: str) -> float: + """Get the declination of a given source from the TCU database.""" + client = MongoClient(tcu_server) + collection = client["lst1_config"]["structure_configurations"] + source_dec = collection.find_one({"name": source_name})["target"]["source_dec"] + return source_dec From 045fb011427474481ba7cebd1fa27a6798f3637f Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 13:01:29 +0100 Subject: [PATCH 04/28] Get the RF models for a given run (not subrun), run_str is run.subrun --- src/osa/scripts/datasequence.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osa/scripts/datasequence.py b/src/osa/scripts/datasequence.py index 9b5a56fd..293657fb 100644 --- a/src/osa/scripts/datasequence.py +++ b/src/osa/scripts/datasequence.py @@ -266,7 +266,7 @@ def dl1_to_dl2(run_str: str) -> int: dl1ab_subdirectory = Path(options.directory) / options.dl1_prod_id dl2_subdirectory = Path(options.directory) / options.dl2_prod_id dl2_config = Path(cfg.get("lstchain", "dl2_config")) - rf_models_directory = get_RF_model(run_str) + rf_models_directory = get_RF_model(run_str[:5]) dl1_file = dl1ab_subdirectory / f"dl1_LST-1.Run{run_str}.h5" command = cfg.get("lstchain", "dl1_to_dl2") From 5f5071f34b2952585f6b9a41fcc32c2e38e26485 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 13:04:19 +0100 Subject: [PATCH 05/28] Change name of variable --- src/osa/paths.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 83414951..482563b6 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -466,18 +466,18 @@ def get_RF_model(run_str: str) -> Path: # If the culmination angle of the closest declination line is larger than for the source, # remove it from the declination lines list and look for the second closest declination line. corresponding_dict = get_corresponding_string(dec_list, dec_values) - corresponding_string = corresponding_dict[closest_declination] + declination_str = corresponding_dict[closest_declination] dec_values.remove(closest_declination) - dec_list.remove(corresponding_string) + dec_list.remove(declination_str) closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") corresponding_dict = get_corresponding_string(dec_list, dec_values) - corresponding_string = corresponding_dict[closest_declination] + declination_str = corresponding_dict[closest_declination] rf_models_dir = Path(cfg.get("lstchain", "rf_models")) mc_prod = cfg.get("lstchain", "mc_prod") - rf_model_path = rf_models_dir / mc_prod / corresponding_string + rf_model_path = rf_models_dir / mc_prod / declination_str return rf_model_path \ No newline at end of file From 0bb0185a1e872ed2bf142ec94efdf6d27e695d06 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 15:01:38 +0100 Subject: [PATCH 06/28] Convert date to string in the correct format to create the path --- src/osa/paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 482563b6..8c75ea22 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -435,7 +435,7 @@ def get_corresponding_string(list1: list, list2: list) -> dict: def get_RF_model(run_str: str) -> Path: """Get the path of the RF model to be used in the DL2 production for a given run.""" run_catalog_dir = Path(cfg.get(options.tel_id, "RUN_CATALOG")) - run_catalog_file = run_catalog_dir / f"RunCatalog_{options.date}.ecsv" + run_catalog_file = run_catalog_dir / f"RunCatalog_{utils.date_to_dir(options.date)}.ecsv" run_catalog = Table.read(run_catalog_file) run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] From 2be646602a03b128408ea21e9d927e3dc84aab02 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 15:22:36 +0100 Subject: [PATCH 07/28] Adapt tests --- src/osa/scripts/tests/test_osa_scripts.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/osa/scripts/tests/test_osa_scripts.py b/src/osa/scripts/tests/test_osa_scripts.py index 1aee924f..e96e2c54 100644 --- a/src/osa/scripts/tests/test_osa_scripts.py +++ b/src/osa/scripts/tests/test_osa_scripts.py @@ -106,10 +106,10 @@ def test_simulate_processing( with open(json_file_dl2) as file: dl2 = yaml.safe_load(file) - assert len(dl2["entity"]) == 25 - assert len(dl2["activity"]) == 6 - assert len(dl2["used"]) == 21 - assert len(dl2["wasGeneratedBy"]) == 12 + assert len(dl2["entity"]) == 19 + assert len(dl2["activity"]) == 5 + assert len(dl2["used"]) == 15 + assert len(dl2["wasGeneratedBy"]) == 10 rc = run_program("simulate_processing", "-p") assert rc.returncode == 0 @@ -249,6 +249,8 @@ def test_datasequence(running_analysis_dir): run_number = "00003.0000" options.directory = running_analysis_dir + assert run_catalog.exists() + output = run_program( "datasequence", "--date=2020-01-17", From 51d40c41583c31f4980e3ba1ebbf00276b3936f6 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 29 Feb 2024 15:26:04 +0100 Subject: [PATCH 08/28] Add necessary argument --- src/osa/scripts/tests/test_osa_scripts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osa/scripts/tests/test_osa_scripts.py b/src/osa/scripts/tests/test_osa_scripts.py index e96e2c54..18c7f308 100644 --- a/src/osa/scripts/tests/test_osa_scripts.py +++ b/src/osa/scripts/tests/test_osa_scripts.py @@ -238,7 +238,7 @@ def test_closer( assert closed_seq_file.exists() -def test_datasequence(running_analysis_dir): +def test_datasequence(running_analysis_dir, run_catalog): drs4_file = "drs4_pedestal.Run00001.0000.fits" calib_file = "calibration.Run00002.0000.hdf5" timecalib_file = "time_calibration.Run00002.0000.hdf5" From cbdee3e0e6900ce7cf6b3d34ca67e6a2a90476fb Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Fri, 1 Mar 2024 17:10:11 +0100 Subject: [PATCH 09/28] Adapt tests + create dec_list looking into the RF_models directory --- src/osa/configs/sequencer.cfg | 2 +- src/osa/conftest.py | 18 ++++++++++++++++++ src/osa/paths.py | 16 ++++++++-------- src/osa/provenance/utils.py | 2 +- src/osa/scripts/tests/test_osa_scripts.py | 13 +++++++++++-- 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/src/osa/configs/sequencer.cfg b/src/osa/configs/sequencer.cfg index d17232e5..f541c923 100644 --- a/src/osa/configs/sequencer.cfg +++ b/src/osa/configs/sequencer.cfg @@ -22,6 +22,7 @@ DL1_DIR: %(BASE)s/DL1 DL1AB_DIR: %(BASE)s/DL1 DL2_DIR: %(BASE)s/DL2 DL3_DIR: %(BASE)s/DL3 +RF_MODELS: %(BASE)s/models/AllSky OSA_DIR: %(BASE)s/OSA CLOSER_DIR: %(OSA_DIR)s/Closer HIGH_LEVEL_DIR: %(OSA_DIR)s/HighLevel @@ -55,7 +56,6 @@ store_image_dl1ab: True merge_dl1_datacheck: True dl1b_config: /software/lstchain/data/lstchain_standard_config.json dl2_config: /software/lstchain/data/lstchain_standard_config.json -rf_models: /data/models/prod5/zenith_20deg/20201023_v0.6.3 mc_prod: 20240131_allsky_v0.10.5_all_dec_base dl3_config: /software/lstchain/data/dl3_std_config.json max_tries: 3 diff --git a/src/osa/conftest.py b/src/osa/conftest.py index d1bdc52c..bc81a80b 100644 --- a/src/osa/conftest.py +++ b/src/osa/conftest.py @@ -562,3 +562,21 @@ def database(base_test_dir): ) cursor.connection.commit() yield cursor + + +@pytest.fixture(scope="session") +def rf_models_dir(base_test_dir): + directory = base_test_dir / "models/AllSky" + directory.mkdir(parents=True, exist_ok=True) + return directory + + +@pytest.fixture(scope="session") +def rf_model_path(rf_models_dir): + mc_prod = "20240131_allsky_v0.10.5_all_dec_base" + declination_str = "dec_2276" + rf_model_path = rf_models_dir / mc_prod / declination_str + rf_model_path.mkdir(parents=True, exist_ok=True) + return rf_model_path + + diff --git a/src/osa/paths.py b/src/osa/paths.py index 8c75ea22..7b63f3aa 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -439,19 +439,21 @@ def get_RF_model(run_str: str) -> Path: run_catalog = Table.read(run_catalog_file) run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] - + try: - source_coordinates = SkyCoord.from_name(target_name) + source_coordinates = SkyCoord.from_name(target_name[0]) source_dec = source_coordinates.dec.value - + except TypeError: tcu_server = cfg.get("database", "tcu_db") source_dec = utils.get_source_dec_from_TCU(target_name, tcu_server) source_culmination = utils.culmination_angle(source_dec) - dec_list = os.listdir("/fefs/aswg/data/mc/DL0/LSTProd2/TrainingDataset/Protons")[:-2] - + rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) + mc_prod = cfg.get("lstchain", "mc_prod") + dec_list = os.listdir(rf_models_dir / mc_prod) + # Convert each string in the list to numerical values dec_values = [convert_dec_string(dec) for dec in dec_list] @@ -476,8 +478,6 @@ def get_RF_model(run_str: str) -> Path: corresponding_dict = get_corresponding_string(dec_list, dec_values) declination_str = corresponding_dict[closest_declination] - rf_models_dir = Path(cfg.get("lstchain", "rf_models")) - mc_prod = cfg.get("lstchain", "mc_prod") rf_model_path = rf_models_dir / mc_prod / declination_str - + return rf_model_path \ No newline at end of file diff --git a/src/osa/provenance/utils.py b/src/osa/provenance/utils.py index 92e5cb04..2f0f275b 100644 --- a/src/osa/provenance/utils.py +++ b/src/osa/provenance/utils.py @@ -40,7 +40,7 @@ def parse_variables(class_instance): configfile_dl1b = cfg.get("lstchain", "dl1b_config") configfile_dl2 = cfg.get("lstchain", "dl2_config") raw_dir = Path(cfg.get("LST1", "R0_DIR")) - rf_models_directory = Path(cfg.get("lstchain", "RF_MODELS")) + rf_models_directory = Path(cfg.get("LST1", "RF_MODELS")) dl1_dir = Path(cfg.get("LST1", "DL1_DIR")) dl2_dir = Path(cfg.get("LST1", "DL2_DIR")) calib_dir = Path(cfg.get("LST1", "CALIB_DIR")) diff --git a/src/osa/scripts/tests/test_osa_scripts.py b/src/osa/scripts/tests/test_osa_scripts.py index 18c7f308..8810eca6 100644 --- a/src/osa/scripts/tests/test_osa_scripts.py +++ b/src/osa/scripts/tests/test_osa_scripts.py @@ -238,7 +238,13 @@ def test_closer( assert closed_seq_file.exists() -def test_datasequence(running_analysis_dir, run_catalog): +def test_datasequence( + running_analysis_dir, + run_catalog, + run_catalog_dir, + rf_models_dir, + rf_model_path +): drs4_file = "drs4_pedestal.Run00001.0000.fits" calib_file = "calibration.Run00002.0000.hdf5" timecalib_file = "time_calibration.Run00002.0000.hdf5" @@ -246,10 +252,13 @@ def test_datasequence(running_analysis_dir, run_catalog): drive_file = "DrivePosition_20200117.txt" runsummary_file = "RunSummary_20200117.ecsv" prod_id = "v0.1.0" - run_number = "00003.0000" + run_number = "01807.0000" options.directory = running_analysis_dir + assert run_catalog_dir.exists() assert run_catalog.exists() + assert rf_models_dir.exists() + assert rf_model_path.exists() output = run_program( "datasequence", From 5ea46a41da63ad4487c75d4e44cffbe1b556205e Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Mon, 18 Mar 2024 16:44:14 +0100 Subject: [PATCH 10/28] Use only the TCU database to get the source coordinates (no access to internet) --- src/osa/paths.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 7b63f3aa..2a30c23e 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -440,14 +440,8 @@ def get_RF_model(run_str: str) -> Path: run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] - try: - source_coordinates = SkyCoord.from_name(target_name[0]) - source_dec = source_coordinates.dec.value - - except TypeError: - tcu_server = cfg.get("database", "tcu_db") - source_dec = utils.get_source_dec_from_TCU(target_name, tcu_server) - + tcu_server = cfg.get("database", "tcu_db") + source_dec = utils.get_source_dec_from_TCU(target_name[0], tcu_server) source_culmination = utils.culmination_angle(source_dec) rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) From f0e16807632dbeb8ba86a775c8efd372ff3ce1d6 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 21 Mar 2024 12:16:04 +0100 Subject: [PATCH 11/28] Use only dec strings with the format dec_(min)_XXXX --- src/osa/paths.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 2a30c23e..2f522adf 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -411,17 +411,22 @@ def dl1_datacheck_longterm_file_exits() -> bool: def convert_dec_string(dec_str: str) -> float: """Return the declination angle in degrees corresponding to a given string of the form "dec_XXXX" or "dec_min_XXXX".""" - # Split the string into parts - parts = dec_str.split('_') + + # Check if dec_str has a valid format + pattern = r'^dec(_min)?_[0-9]{4}$' + if re.match(pattern, dec_str): + + # Split the string into parts + parts = dec_str.split('_') - # Extract the sign, degrees, and minutes - sign = 1 if 'min' not in parts else -1 - degrees = int(parts[-1]) + # Extract the sign, degrees, and minutes + sign = 1 if 'min' not in parts else -1 + degrees = int(parts[-1]) - # Calculate the numerical value - dec_value = sign * (degrees / 100) + # Calculate the numerical value + dec_value = sign * (degrees / 100) - return dec_value + return dec_value def get_corresponding_string(list1: list, list2: list) -> dict: @@ -450,6 +455,7 @@ def get_RF_model(run_str: str) -> Path: # Convert each string in the list to numerical values dec_values = [convert_dec_string(dec) for dec in dec_list] + dec_values = [dec for dec in dec_values if dec is not None] closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) closest_dec_culmination = utils.culmination_angle(closest_declination) From d3f5d228499cbfac1d224665f45ed00d51a925ea Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 13:30:37 +0200 Subject: [PATCH 12/28] remove unused import --- src/osa/paths.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 2f522adf..f583eae8 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -11,7 +11,6 @@ import lstchain from astropy.table import Table -from astropy.coordinates import SkyCoord from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) From 434ad9b17a1410b59a391baf52da93a114c35060 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 14:42:38 +0200 Subject: [PATCH 13/28] check always if the culmination angle is larger than for the source --- src/osa/paths.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index f583eae8..bc61c752 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -463,7 +463,7 @@ def get_RF_model(run_str: str) -> Path: "Checking if the culmination angle is larger than the one of the target source." ) - if closest_dec_culmination > source_culmination: + while closest_dec_culmination > source_culmination: # If the culmination angle of the closest declination line is larger than for the source, # remove it from the declination lines list and look for the second closest declination line. corresponding_dict = get_corresponding_string(dec_list, dec_values) @@ -471,6 +471,7 @@ def get_RF_model(run_str: str) -> Path: dec_values.remove(closest_declination) dec_list.remove(declination_str) closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + closest_dec_culmination = culmination_angle(closest_declination) log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") From 78b918ea1225d2e8f71fe9351ec2c2552b76f433 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 15:42:40 +0200 Subject: [PATCH 14/28] return the declination as an astropy quantity --- src/osa/paths.py | 7 ++++--- src/osa/utils/utils.py | 12 ++++++------ 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index bc61c752..f300d1d7 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -11,6 +11,7 @@ import lstchain from astropy.table import Table +from astropy import units as u from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) @@ -407,7 +408,7 @@ def dl1_datacheck_longterm_file_exits() -> bool: return longterm_file.exists() -def convert_dec_string(dec_str: str) -> float: +def convert_dec_string(dec_str: str) -> u.Quantity: """Return the declination angle in degrees corresponding to a given string of the form "dec_XXXX" or "dec_min_XXXX".""" @@ -425,7 +426,7 @@ def convert_dec_string(dec_str: str) -> float: # Calculate the numerical value dec_value = sign * (degrees / 100) - return dec_value + return dec_value*u.deg def get_corresponding_string(list1: list, list2: list) -> dict: @@ -473,7 +474,7 @@ def get_RF_model(run_str: str) -> Path: closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) closest_dec_culmination = culmination_angle(closest_declination) - log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") + log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") corresponding_dict = get_corresponding_string(dec_list, dec_values) declination_str = corresponding_dict[closest_declination] diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 49e46905..6ecd514e 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -291,14 +291,14 @@ def wait_for_daytime(start=8, end=18): time.sleep(3600) -def culmination_angle(dec: int) -> float: +def culmination_angle(dec: u.Quantity) -> u.Quantity: """ Calculate culmination angle for a given declination. Parameters ---------- - dec: int - declination in degrees + dec: Quantity + declination coordinate in degrees Returns ------- @@ -306,12 +306,12 @@ def culmination_angle(dec: int) -> float: """ location = observatory_locations["cta_north"] Lat = location.lat # latitude of the LST1 site - return abs(Lat - dec*u.deg).value + return abs(Lat - dec*u.deg) -def get_source_dec_from_TCU(source_name: str, tcu_server: str) -> float: +def get_source_dec_from_TCU(source_name: str, tcu_server: str) -> u.Quantity: """Get the declination of a given source from the TCU database.""" client = MongoClient(tcu_server) collection = client["lst1_config"]["structure_configurations"] source_dec = collection.find_one({"name": source_name})["target"]["source_dec"] - return source_dec + return source_dec*u.deg From 22e7a2db25f7bfcf2006b31b9fd2768ec850acdf Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 15:44:45 +0200 Subject: [PATCH 15/28] rename directory --- src/osa/conftest.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/osa/conftest.py b/src/osa/conftest.py index e13ec0dd..12a390fe 100644 --- a/src/osa/conftest.py +++ b/src/osa/conftest.py @@ -574,17 +574,17 @@ def database(osa_dir): @pytest.fixture(scope="session") -def rf_models_dir(base_test_dir): +def rf_models_allsky_basedir(base_test_dir): directory = base_test_dir / "models/AllSky" directory.mkdir(parents=True, exist_ok=True) return directory @pytest.fixture(scope="session") -def rf_model_path(rf_models_dir): +def rf_model_path(rf_models_allsky_basedir): mc_prod = "20240131_allsky_v0.10.5_all_dec_base" declination_str = "dec_2276" - rf_model_path = rf_models_dir / mc_prod / declination_str + rf_model_path = rf_models_allsky_basedir / mc_prod / declination_str rf_model_path.mkdir(parents=True, exist_ok=True) return rf_model_path From 7207dbbac91aa399d62d32e39a5dbf47365ce9a6 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 15:49:29 +0200 Subject: [PATCH 16/28] fix mistake --- src/osa/paths.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index f300d1d7..ada41526 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -472,7 +472,7 @@ def get_RF_model(run_str: str) -> Path: dec_values.remove(closest_declination) dec_list.remove(declination_str) closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) - closest_dec_culmination = culmination_angle(closest_declination) + closest_dec_culmination = utils.culmination_angle(closest_declination) log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") From 0c15ed7f911990dba979d0912f1cb3a2b12cdccb Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 16:39:00 +0200 Subject: [PATCH 17/28] change directory name --- src/osa/paths.py | 2 +- src/osa/scripts/tests/test_osa_scripts.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index ada41526..c9a99bd1 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -413,7 +413,7 @@ def convert_dec_string(dec_str: str) -> u.Quantity: given string of the form "dec_XXXX" or "dec_min_XXXX".""" # Check if dec_str has a valid format - pattern = r'^dec(_min)?_[0-9]{4}$' + pattern = r'^dec_(\d{3,4})$|^dec_min_(\d{3,4})$' if re.match(pattern, dec_str): # Split the string into parts diff --git a/src/osa/scripts/tests/test_osa_scripts.py b/src/osa/scripts/tests/test_osa_scripts.py index a3267589..5b964a2d 100644 --- a/src/osa/scripts/tests/test_osa_scripts.py +++ b/src/osa/scripts/tests/test_osa_scripts.py @@ -244,7 +244,7 @@ def test_datasequence( running_analysis_dir, run_catalog, run_catalog_dir, - rf_models_dir, + rf_models_allsky_basedir, rf_model_path ): drs4_file = "drs4_pedestal.Run00001.0000.fits" @@ -259,7 +259,7 @@ def test_datasequence( assert run_catalog_dir.exists() assert run_catalog.exists() - assert rf_models_dir.exists() + assert rf_models_allsky_basedir.exists() assert rf_model_path.exists() output = run_program( From d6c45cd0cb4c4063857c0042d017d4af04597514 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 19 Sep 2024 19:34:58 +0200 Subject: [PATCH 18/28] adapt tests --- src/osa/paths.py | 10 ++++++++-- src/osa/utils/utils.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index c9a99bd1..09e86e10 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -12,6 +12,7 @@ import lstchain from astropy.table import Table from astropy import units as u +from astropy.coordinates import SkyCoord from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) @@ -445,8 +446,13 @@ def get_RF_model(run_str: str) -> Path: run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] - tcu_server = cfg.get("database", "tcu_db") - source_dec = utils.get_source_dec_from_TCU(target_name[0], tcu_server) + if options.test: + source_coordinates = SkyCoord.from_name(target_name[0]) + source_dec = source_coordinates.dec + else: + tcu_server = cfg.get("database", "tcu_db") + source_dec = utils.get_source_dec_from_TCU(target_name[0], tcu_server) + source_culmination = utils.culmination_angle(source_dec) rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 6ecd514e..5d326938 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -306,7 +306,7 @@ def culmination_angle(dec: u.Quantity) -> u.Quantity: """ location = observatory_locations["cta_north"] Lat = location.lat # latitude of the LST1 site - return abs(Lat - dec*u.deg) + return abs(Lat - dec) def get_source_dec_from_TCU(source_name: str, tcu_server: str) -> u.Quantity: From 0b25f3096f6d074a36a31a9b26569cd466333c65 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Fri, 20 Sep 2024 08:13:14 +0200 Subject: [PATCH 19/28] adapt tests --- src/osa/paths.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 09e86e10..a53f2775 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -446,13 +446,13 @@ def get_RF_model(run_str: str) -> Path: run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] - if options.test: + if options.test or options.simulate: source_coordinates = SkyCoord.from_name(target_name[0]) source_dec = source_coordinates.dec else: tcu_server = cfg.get("database", "tcu_db") source_dec = utils.get_source_dec_from_TCU(target_name[0], tcu_server) - + source_culmination = utils.culmination_angle(source_dec) rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) From 7bfbdb25e871a9a38a94da9d3c30c1e5a29cf8be Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 26 Sep 2024 15:31:28 +0200 Subject: [PATCH 20/28] do not check the culmination angle for sources between 22.76 and 34.76 dec --- src/osa/paths.py | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index a53f2775..fcf53320 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -465,20 +465,21 @@ def get_RF_model(run_str: str) -> Path: closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) closest_dec_culmination = utils.culmination_angle(closest_declination) - log.debug( - f"The declination closest to {source_dec} is: {closest_declination}." - "Checking if the culmination angle is larger than the one of the target source." - ) - - while closest_dec_culmination > source_culmination: - # If the culmination angle of the closest declination line is larger than for the source, - # remove it from the declination lines list and look for the second closest declination line. - corresponding_dict = get_corresponding_string(dec_list, dec_values) - declination_str = corresponding_dict[closest_declination] - dec_values.remove(closest_declination) - dec_list.remove(declination_str) - closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) - closest_dec_culmination = utils.culmination_angle(closest_declination) + + if source_dec < 22.76*u.deg or source_dec > 34.76*u.deg: + log.debug( + f"The declination closest to {source_dec} is: {closest_declination}." + "Checking if the culmination angle is larger than the one of the target source." + ) + while closest_dec_culmination > source_culmination: + # If the culmination angle of the closest declination line is larger than for the source, + # remove it from the declination lines list and look for the second closest declination line. + corresponding_dict = get_corresponding_string(dec_list, dec_values) + declination_str = corresponding_dict[closest_declination] + dec_values.remove(closest_declination) + dec_list.remove(declination_str) + closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + closest_dec_culmination = utils.culmination_angle(closest_declination) log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") From ae9ccc0117c157c7d26fd4ad9a0fd005824357f8 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Fri, 27 Sep 2024 12:58:54 +0200 Subject: [PATCH 21/28] get the general value of the two closest values --- src/osa/paths.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index fcf53320..4b74295a 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -13,6 +13,7 @@ from astropy.table import Table from astropy import units as u from astropy.coordinates import SkyCoord +from gammapy.data import observatory_locations from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) @@ -466,7 +467,13 @@ def get_RF_model(run_str: str) -> Path: closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) closest_dec_culmination = utils.culmination_angle(closest_declination) - if source_dec < 22.76*u.deg or source_dec > 34.76*u.deg: + location = observatory_locations["cta_north"] + Lat = location.lat # latitude of the LST1 site + closest_lines = sorted(sorted(dec_values, key=lambda x: abs(x - Lat))[:2]) + + if source_dec < closest_lines[0] or source_dec > closest_lines[1]: + # If the source declination is between the two MC lines closest to the latitude of + # the LST1 site, this check is not necessary. log.debug( f"The declination closest to {source_dec} is: {closest_declination}." "Checking if the culmination angle is larger than the one of the target source." From 754721dd0b019519e14675154a53c42ae251faa0 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 12 Dec 2024 20:38:08 +0100 Subject: [PATCH 22/28] add rf_model_path as an input argument for datasequence + move get_RF_model to utils.py --- src/osa/paths.py | 89 -------------------------------- src/osa/scripts/datasequence.py | 11 ++-- src/osa/utils/cliopts.py | 8 +++ src/osa/utils/utils.py | 91 +++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 94 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 4b74295a..0b27b7b6 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -12,8 +12,6 @@ import lstchain from astropy.table import Table from astropy import units as u -from astropy.coordinates import SkyCoord -from gammapy.data import observatory_locations from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) @@ -409,90 +407,3 @@ def dl1_datacheck_longterm_file_exits() -> bool: longterm_file = longterm_dir / options.prod_id / nightdir / f"DL1_datacheck_{nightdir}.h5" return longterm_file.exists() - -def convert_dec_string(dec_str: str) -> u.Quantity: - """Return the declination angle in degrees corresponding to a - given string of the form "dec_XXXX" or "dec_min_XXXX".""" - - # Check if dec_str has a valid format - pattern = r'^dec_(\d{3,4})$|^dec_min_(\d{3,4})$' - if re.match(pattern, dec_str): - - # Split the string into parts - parts = dec_str.split('_') - - # Extract the sign, degrees, and minutes - sign = 1 if 'min' not in parts else -1 - degrees = int(parts[-1]) - - # Calculate the numerical value - dec_value = sign * (degrees / 100) - - return dec_value*u.deg - - -def get_corresponding_string(list1: list, list2: list) -> dict: - """Return a dictionary created from two given lists.""" - corresponding_dict = {} - for index, element in enumerate(list2): - corresponding_dict[element] = list1[index] - return corresponding_dict - - -def get_RF_model(run_str: str) -> Path: - """Get the path of the RF model to be used in the DL2 production for a given run.""" - run_catalog_dir = Path(cfg.get(options.tel_id, "RUN_CATALOG")) - run_catalog_file = run_catalog_dir / f"RunCatalog_{utils.date_to_dir(options.date)}.ecsv" - run_catalog = Table.read(run_catalog_file) - run = run_catalog[run_catalog["run_id"]==int(run_str)] - target_name = run["source_name"] - - if options.test or options.simulate: - source_coordinates = SkyCoord.from_name(target_name[0]) - source_dec = source_coordinates.dec - else: - tcu_server = cfg.get("database", "tcu_db") - source_dec = utils.get_source_dec_from_TCU(target_name[0], tcu_server) - - source_culmination = utils.culmination_angle(source_dec) - - rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) - mc_prod = cfg.get("lstchain", "mc_prod") - dec_list = os.listdir(rf_models_dir / mc_prod) - - # Convert each string in the list to numerical values - dec_values = [convert_dec_string(dec) for dec in dec_list] - dec_values = [dec for dec in dec_values if dec is not None] - - closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) - closest_dec_culmination = utils.culmination_angle(closest_declination) - - location = observatory_locations["cta_north"] - Lat = location.lat # latitude of the LST1 site - closest_lines = sorted(sorted(dec_values, key=lambda x: abs(x - Lat))[:2]) - - if source_dec < closest_lines[0] or source_dec > closest_lines[1]: - # If the source declination is between the two MC lines closest to the latitude of - # the LST1 site, this check is not necessary. - log.debug( - f"The declination closest to {source_dec} is: {closest_declination}." - "Checking if the culmination angle is larger than the one of the target source." - ) - while closest_dec_culmination > source_culmination: - # If the culmination angle of the closest declination line is larger than for the source, - # remove it from the declination lines list and look for the second closest declination line. - corresponding_dict = get_corresponding_string(dec_list, dec_values) - declination_str = corresponding_dict[closest_declination] - dec_values.remove(closest_declination) - dec_list.remove(declination_str) - closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) - closest_dec_culmination = utils.culmination_angle(closest_declination) - - log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") - - corresponding_dict = get_corresponding_string(dec_list, dec_values) - declination_str = corresponding_dict[closest_declination] - - rf_model_path = rf_models_dir / mc_prod / declination_str - - return rf_model_path \ No newline at end of file diff --git a/src/osa/scripts/datasequence.py b/src/osa/scripts/datasequence.py index 293657fb..d34c0bd7 100644 --- a/src/osa/scripts/datasequence.py +++ b/src/osa/scripts/datasequence.py @@ -12,7 +12,6 @@ from osa.utils.cliopts import data_sequence_cli_parsing from osa.utils.logging import myLogger from osa.utils.utils import date_to_dir -from osa.paths import get_RF_model __all__ = ["data_sequence", "r0_to_dl1", "dl1_to_dl2", "dl1ab", "dl1_datacheck"] @@ -28,6 +27,7 @@ def data_sequence( run_summary: Path, pedestal_ids_file: Path, run_str: str, + rf_model_path: Path=None, ): """ Performs all the steps to process a whole run. @@ -91,7 +91,7 @@ def data_sequence( level = 0 log.info(f"No DL2 are going to be produced. Going to level {level}") else: - rc = dl1_to_dl2(run_str) + rc = dl1_to_dl2(run_str, rf_model_path) level -= 1 log.info(f"Going to level {level}") @@ -249,7 +249,7 @@ def dl1_datacheck(run_str: str) -> int: @trace -def dl1_to_dl2(run_str: str) -> int: +def dl1_to_dl2(run_str: str, rf_model_path: Path) -> int: """ It prepares and execute the dl1 to dl2 lstchain scripts that applies the already trained RFs models to DL1 files. It identifies the @@ -266,7 +266,6 @@ def dl1_to_dl2(run_str: str) -> int: dl1ab_subdirectory = Path(options.directory) / options.dl1_prod_id dl2_subdirectory = Path(options.directory) / options.dl2_prod_id dl2_config = Path(cfg.get("lstchain", "dl2_config")) - rf_models_directory = get_RF_model(run_str[:5]) dl1_file = dl1ab_subdirectory / f"dl1_LST-1.Run{run_str}.h5" command = cfg.get("lstchain", "dl1_to_dl2") @@ -274,7 +273,7 @@ def dl1_to_dl2(run_str: str) -> int: command, f"--input-file={dl1_file}", f"--output-dir={dl2_subdirectory}", - f"--path-models={rf_models_directory}", + f"--path-models={rf_model_path}", f"--config={dl2_config}", ] @@ -297,6 +296,7 @@ def main(): run_summary_file, pedestal_ids_file, run_number, + rf_model_path, ) = data_sequence_cli_parsing() if options.verbose: @@ -314,6 +314,7 @@ def main(): run_summary_file, pedestal_ids_file, run_number, + rf_model_path, ) sys.exit(rc) diff --git a/src/osa/utils/cliopts.py b/src/osa/utils/cliopts.py index 95bd41a7..af497ae4 100644 --- a/src/osa/utils/cliopts.py +++ b/src/osa/utils/cliopts.py @@ -210,6 +210,12 @@ def data_sequence_argparser(): type=Path, help="Path to a file containing the ids of the interleaved pedestal events", ) + parser.add_argument( + "--rf-model-path", + type=Path, + default=None, + help="Path to a the RF models to be used for the DL2 production", + ) parser.add_argument("run_number", help="Number of the run to be processed") parser.add_argument("tel_id", choices=["ST", "LST1", "LST2"]) return parser @@ -228,6 +234,7 @@ def data_sequence_cli_parsing(): options.prod_id = opts.prod_id options.no_dl2 = opts.no_dl2 options.tel_id = opts.tel_id + options.rf_model_path = opts.rf_model_path log.debug(f"The options and arguments are {opts}") @@ -246,6 +253,7 @@ def data_sequence_cli_parsing(): opts.run_summary, opts.pedestal_ids_file, opts.run_number, + opts.rf_model_path, ) diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 5d326938..ad36370d 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -4,12 +4,15 @@ import inspect import logging import os +import re import time from datetime import datetime, timedelta from pathlib import Path from socket import gethostname from gammapy.data import observatory_locations from astropy import units as u +from astropy.coordinates import SkyCoord +from astropy.table import Table from pymongo import MongoClient import osa.paths @@ -315,3 +318,91 @@ def get_source_dec_from_TCU(source_name: str, tcu_server: str) -> u.Quantity: collection = client["lst1_config"]["structure_configurations"] source_dec = collection.find_one({"name": source_name})["target"]["source_dec"] return source_dec*u.deg + + +def convert_dec_string(dec_str: str) -> u.Quantity: + """Return the declination angle in degrees corresponding to a + given string of the form "dec_XXXX" or "dec_min_XXXX".""" + + # Check if dec_str has a valid format + pattern = r'^dec_(\d{3,4})$|^dec_min_(\d{3,4})$' + if re.match(pattern, dec_str): + + # Split the string into parts + parts = dec_str.split('_') + + # Extract the sign, degrees, and minutes + sign = 1 if 'min' not in parts else -1 + degrees = int(parts[-1]) + + # Calculate the numerical value + dec_value = sign * (degrees / 100) + + return dec_value*u.deg + + +def get_corresponding_string(list1: list, list2: list) -> dict: + """Return a dictionary created from two given lists.""" + corresponding_dict = {} + for index, element in enumerate(list2): + corresponding_dict[element] = list1[index] + return corresponding_dict + + +def get_RF_model(run_str: str) -> Path: + """Get the path of the RF model to be used in the DL2 production for a given run.""" + run_catalog_dir = Path(cfg.get(options.tel_id, "RUN_CATALOG")) + run_catalog_file = run_catalog_dir / f"RunCatalog_{utils.date_to_dir(options.date)}.ecsv" + run_catalog = Table.read(run_catalog_file) + run = run_catalog[run_catalog["run_id"]==int(run_str)] + target_name = run["source_name"] + + if options.test or options.simulate: + source_coordinates = SkyCoord.from_name(target_name[0]) + source_dec = source_coordinates.dec + else: + tcu_server = cfg.get("database", "tcu_db") + source_dec = get_source_dec_from_TCU(target_name[0], tcu_server) + + source_culmination = culmination_angle(source_dec) + + rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) + mc_prod = cfg.get("lstchain", "mc_prod") + dec_list = os.listdir(rf_models_dir / mc_prod) + + # Convert each string in the list to numerical values + dec_values = [convert_dec_string(dec) for dec in dec_list] + dec_values = [dec for dec in dec_values if dec is not None] + + closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + closest_dec_culmination = culmination_angle(closest_declination) + + location = observatory_locations["cta_north"] + Lat = location.lat # latitude of the LST1 site + closest_lines = sorted(sorted(dec_values, key=lambda x: abs(x - Lat))[:2]) + + if source_dec < closest_lines[0] or source_dec > closest_lines[1]: + # If the source declination is between the two MC lines closest to the latitude of + # the LST1 site, this check is not necessary. + log.debug( + f"The declination closest to {source_dec} is: {closest_declination}." + "Checking if the culmination angle is larger than the one of the target source." + ) + while closest_dec_culmination > source_culmination: + # If the culmination angle of the closest declination line is larger than for the source, + # remove it from the declination lines list and look for the second closest declination line. + corresponding_dict = get_corresponding_string(dec_list, dec_values) + declination_str = corresponding_dict[closest_declination] + dec_values.remove(closest_declination) + dec_list.remove(declination_str) + closest_declination = min(dec_values, key=lambda x: abs(x - source_dec)) + closest_dec_culmination = culmination_angle(closest_declination) + + log.debug(f"The declination line to use for the DL2 production is: {closest_declination}") + + corresponding_dict = get_corresponding_string(dec_list, dec_values) + declination_str = corresponding_dict[closest_declination] + + rf_model_path = rf_models_dir / mc_prod / declination_str + + return rf_model_path From 37b8fef534fda5fca4bf81b49caa8b6f0283de68 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Thu, 12 Dec 2024 20:41:23 +0100 Subject: [PATCH 23/28] pass the rf_model_path when writing the sequence.py files --- src/osa/job.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/osa/job.py b/src/osa/job.py index b151a69e..27ec667b 100644 --- a/src/osa/job.py +++ b/src/osa/job.py @@ -23,7 +23,13 @@ ) from osa.utils.iofile import write_to_file from osa.utils.logging import myLogger -from osa.utils.utils import date_to_dir, time_to_seconds, stringify, date_to_iso +from osa.utils.utils import ( + date_to_dir, + time_to_seconds, + stringify, + date_to_iso, + get_RF_model, +) log = myLogger(logging.getLogger(__name__)) @@ -438,6 +444,9 @@ def data_sequence_job_template(sequence): ) ) + if not options.no_dl2: + commandargs.append(f"--rf-model-path={get_RF_model(str(sequence.run))}") + content = job_header + "\n" + PYTHON_IMPORTS if not options.test: From cc94ccb22b1d1f03994da4474273f3a6d7f9bd87 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Fri, 13 Dec 2024 17:33:48 +0100 Subject: [PATCH 24/28] adapt tests --- src/osa/tests/test_jobs.py | 17 +++++++++++++++-- src/osa/utils/utils.py | 16 ++++++++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/osa/tests/test_jobs.py b/src/osa/tests/test_jobs.py index 02302d4d..c28046e5 100644 --- a/src/osa/tests/test_jobs.py +++ b/src/osa/tests/test_jobs.py @@ -41,6 +41,7 @@ def test_preparejobs(running_analysis_dir, sequence_list): from osa.job import prepare_jobs options.simulate = False + options.test = True options.directory = running_analysis_dir prepare_jobs(sequence_list) expected_calib_script = os.path.join(running_analysis_dir, "sequence_LST1_01809.py") @@ -92,6 +93,7 @@ def test_job_header_template(sequence_list, running_analysis_dir): from osa.job import job_header_template # Extract the first sequence + options.test = False first_sequence = sequence_list[0] header = job_header_template(first_sequence) output_string1 = dedent( @@ -134,13 +136,17 @@ def test_create_job_template_scheduler( calibration_file, run_summary_file, pedestal_ids_file, + rf_model_path, ): from osa.job import data_sequence_job_template assert pedestal_ids_file.exists() + assert rf_model_path.exists() options.test = False options.simulate = False + options.no_dl2 = True + content1 = data_sequence_job_template(sequence_list[1]) expected_content1 = dedent( f"""\ @@ -171,6 +177,7 @@ def test_create_job_template_scheduler( 'datasequence', '--config', '{DEFAULT_CFG}', + '--no-dl2', '--date=2020-01-17', '--prod-id=v0.1.0', '--drs4-pedestal-file={drs4_baseline_file}', @@ -216,6 +223,7 @@ def test_create_job_template_scheduler( 'datasequence', '--config', '{DEFAULT_CFG}', + '--no-dl2', '--date=2020-01-17', '--prod-id=v0.1.0', '--drs4-pedestal-file={drs4_baseline_file}', @@ -246,6 +254,7 @@ def test_create_job_template_local( run_summary_file, pedestal_ids_file, r0_data, + rf_model_path, ): """Check the job file in local mode (assuming no scheduler).""" from osa.job import data_sequence_job_template @@ -260,9 +269,11 @@ def test_create_job_template_local( assert file.exists() assert pedestal_ids_file.exists() + assert rf_model_path.exists() options.test = True options.simulate = False + options.no_dl2 = False content1 = data_sequence_job_template(sequence_list[1]) expected_content1 = dedent( @@ -290,6 +301,7 @@ def test_create_job_template_local( '--systematic-correction-file={Path.cwd()}/test_osa/test_files0/monitoring/PixelCalibration/Cat-A/ffactor_systematics/20200725/pro/ffactor_systematics_20200725.h5', '--drive-file={Path.cwd()}/test_osa/test_files0/monitoring/DrivePositioning/DrivePosition_log_20200117.txt', '--run-summary={run_summary_file}', + '--rf-model-path={rf_model_path}', f'01807.{{subruns:04d}}', 'LST1' ]) @@ -323,6 +335,7 @@ def test_create_job_template_local( '--systematic-correction-file={Path.cwd()}/test_osa/test_files0/monitoring/PixelCalibration/Cat-A/ffactor_systematics/20200725/pro/ffactor_systematics_20200725.h5', '--drive-file={Path.cwd()}/test_osa/test_files0/monitoring/DrivePositioning/DrivePosition_log_20200117.txt', '--run-summary={run_summary_file}', + '--rf-model-path={rf_model_path}', f'--pedestal-ids-file={Path.cwd()}/test_osa/test_files0/auxiliary/PedestalFinder/20200117/pedestal_ids_Run01808.{{subruns:04d}}.h5', f'01808.{{subruns:04d}}', 'LST1' @@ -331,11 +344,11 @@ def test_create_job_template_local( sys.exit(proc.returncode)""" ) - options.simulate = True - assert content1 == expected_content1 assert content2 == expected_content2 + options.simulate = True + def test_create_job_scheduler_calibration(sequence_list): """Check the pilot job file for the calibration pipeline.""" diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index ad36370d..25ce4761 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -352,22 +352,26 @@ def get_corresponding_string(list1: list, list2: list) -> dict: def get_RF_model(run_str: str) -> Path: """Get the path of the RF model to be used in the DL2 production for a given run.""" run_catalog_dir = Path(cfg.get(options.tel_id, "RUN_CATALOG")) - run_catalog_file = run_catalog_dir / f"RunCatalog_{utils.date_to_dir(options.date)}.ecsv" + run_catalog_file = run_catalog_dir / f"RunCatalog_{date_to_dir(options.date)}.ecsv" run_catalog = Table.read(run_catalog_file) run = run_catalog[run_catalog["run_id"]==int(run_str)] target_name = run["source_name"] - if options.test or options.simulate: + rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) + mc_prod = cfg.get("lstchain", "mc_prod") + + if options.test: + rf_model_path = rf_models_dir / mc_prod / "dec_2276" + return rf_model_path.resolve() + elif options.simulate: source_coordinates = SkyCoord.from_name(target_name[0]) source_dec = source_coordinates.dec else: tcu_server = cfg.get("database", "tcu_db") source_dec = get_source_dec_from_TCU(target_name[0], tcu_server) - + source_culmination = culmination_angle(source_dec) - - rf_models_dir = Path(cfg.get("LST1", "RF_MODELS")) - mc_prod = cfg.get("lstchain", "mc_prod") + dec_list = os.listdir(rf_models_dir / mc_prod) # Convert each string in the list to numerical values From 97f78fe984cf72017d386ed4137efb2159fc259c Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Fri, 13 Dec 2024 17:37:12 +0100 Subject: [PATCH 25/28] remove unused imports --- src/osa/paths.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/osa/paths.py b/src/osa/paths.py index 0b27b7b6..3fc8a914 100644 --- a/src/osa/paths.py +++ b/src/osa/paths.py @@ -7,11 +7,9 @@ from typing import List import subprocess import time -import os import lstchain from astropy.table import Table -from astropy import units as u from lstchain.onsite import (find_systematics_correction_file, find_time_calibration_file, find_filter_wheels) From 9f59675fc5ea43803e00e987054afdff6471a1e7 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Sun, 15 Dec 2024 18:23:07 +0100 Subject: [PATCH 26/28] adapt tests --- src/osa/scripts/tests/test_osa_scripts.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/osa/scripts/tests/test_osa_scripts.py b/src/osa/scripts/tests/test_osa_scripts.py index 5b964a2d..07bf75ad 100644 --- a/src/osa/scripts/tests/test_osa_scripts.py +++ b/src/osa/scripts/tests/test_osa_scripts.py @@ -106,10 +106,10 @@ def test_simulate_processing( with open(json_file_dl2) as file: dl2 = yaml.safe_load(file) - assert len(dl2["entity"]) == 19 - assert len(dl2["activity"]) == 5 - assert len(dl2["used"]) == 15 - assert len(dl2["wasGeneratedBy"]) == 10 + assert len(dl2["entity"]) == 25 + assert len(dl2["activity"]) == 6 + assert len(dl2["used"]) == 21 + assert len(dl2["wasGeneratedBy"]) == 12 rc = run_program("simulate_processing", "-p") assert rc.returncode == 0 From 5b875b361857587dd5e8c192c8d28b5f0058b2e6 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Mon, 16 Dec 2024 15:50:29 +0100 Subject: [PATCH 27/28] remove unnecessary part --- src/osa/utils/utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 25ce4761..2abfa6fb 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -363,9 +363,6 @@ def get_RF_model(run_str: str) -> Path: if options.test: rf_model_path = rf_models_dir / mc_prod / "dec_2276" return rf_model_path.resolve() - elif options.simulate: - source_coordinates = SkyCoord.from_name(target_name[0]) - source_dec = source_coordinates.dec else: tcu_server = cfg.get("database", "tcu_db") source_dec = get_source_dec_from_TCU(target_name[0], tcu_server) From 2137efd09790840dbb6e1525256ac215c7868e43 Mon Sep 17 00:00:00 2001 From: Maria Lainez <98marialainez@gmail.com> Date: Mon, 16 Dec 2024 15:51:53 +0100 Subject: [PATCH 28/28] remove unused import --- src/osa/utils/utils.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/osa/utils/utils.py b/src/osa/utils/utils.py index 2abfa6fb..d0a557ad 100644 --- a/src/osa/utils/utils.py +++ b/src/osa/utils/utils.py @@ -11,7 +11,6 @@ from socket import gethostname from gammapy.data import observatory_locations from astropy import units as u -from astropy.coordinates import SkyCoord from astropy.table import Table from pymongo import MongoClient