lstosa 0.10.18__py3-none-any.whl → 0.10.19__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/METADATA +2 -2
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/RECORD +41 -40
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/WHEEL +1 -1
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/entry_points.txt +1 -0
- osa/_version.py +9 -4
- osa/configs/options.py +2 -0
- osa/configs/sequencer.cfg +12 -4
- osa/conftest.py +127 -3
- osa/high_level/significance.py +3 -3
- osa/high_level/tests/test_significance.py +3 -0
- osa/job.py +48 -25
- osa/nightsummary/extract.py +11 -2
- osa/nightsummary/tests/test_extract.py +3 -0
- osa/paths.py +102 -23
- osa/provenance/capture.py +1 -1
- osa/provenance/config/definition.yaml +7 -0
- osa/provenance/utils.py +22 -7
- osa/scripts/autocloser.py +0 -10
- osa/scripts/calibration_pipeline.py +4 -0
- osa/scripts/closer.py +132 -53
- osa/scripts/copy_datacheck.py +5 -3
- osa/scripts/datasequence.py +45 -71
- osa/scripts/provprocess.py +16 -7
- osa/scripts/sequencer.py +34 -26
- osa/scripts/sequencer_catB_tailcuts.py +223 -0
- osa/scripts/sequencer_webmaker.py +4 -0
- osa/scripts/simulate_processing.py +4 -7
- osa/scripts/tests/test_osa_scripts.py +64 -20
- osa/scripts/update_source_catalog.py +5 -2
- osa/tests/test_jobs.py +28 -11
- osa/tests/test_paths.py +6 -6
- osa/utils/cliopts.py +37 -32
- osa/utils/register.py +18 -13
- osa/utils/tests/test_utils.py +14 -0
- osa/utils/utils.py +173 -56
- osa/workflow/dl3.py +1 -2
- osa/workflow/stages.py +16 -11
- osa/workflow/tests/test_dl3.py +2 -1
- osa/workflow/tests/test_stages.py +7 -4
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/LICENSE +0 -0
- {lstosa-0.10.18.dist-info → lstosa-0.10.19.dist-info}/top_level.txt +0 -0
osa/paths.py
CHANGED
|
@@ -2,17 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
4
|
import re
|
|
5
|
+
import sys
|
|
5
6
|
from datetime import datetime
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import List
|
|
8
9
|
import subprocess
|
|
9
10
|
import time
|
|
10
|
-
|
|
11
|
+
import json
|
|
11
12
|
import lstchain
|
|
12
13
|
from astropy.table import Table
|
|
13
14
|
from lstchain.onsite import (find_systematics_correction_file,
|
|
14
|
-
find_time_calibration_file
|
|
15
|
-
find_filter_wheels)
|
|
15
|
+
find_time_calibration_file)
|
|
16
16
|
|
|
17
17
|
from osa.configs import options
|
|
18
18
|
from osa.configs.config import DEFAULT_CFG, cfg
|
|
@@ -45,8 +45,8 @@ __all__ = [
|
|
|
45
45
|
|
|
46
46
|
|
|
47
47
|
DATACHECK_WEB_BASEDIR = Path(cfg.get("WEBSERVER", "DATACHECK"))
|
|
48
|
-
CALIB_BASEDIR = Path(cfg.get("LST1", "
|
|
49
|
-
DRS4_PEDESTAL_BASEDIR = Path(cfg.get("LST1", "
|
|
48
|
+
CALIB_BASEDIR = Path(cfg.get("LST1", "CAT_A_CALIB_DIR"))
|
|
49
|
+
DRS4_PEDESTAL_BASEDIR = Path(cfg.get("LST1", "CAT_A_PEDESTAL_DIR"))
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def analysis_path(tel) -> Path:
|
|
@@ -136,18 +136,7 @@ def get_calibration_filename(run_id: int, prod_id: str) -> Path:
|
|
|
136
136
|
return files[-1] # Get the latest production among the major lstchain version
|
|
137
137
|
|
|
138
138
|
date = utils.date_to_dir(get_run_date(run_id))
|
|
139
|
-
|
|
140
|
-
if options.test: # Run tests avoiding the access to the database
|
|
141
|
-
options.filters = 52
|
|
142
|
-
|
|
143
|
-
else:
|
|
144
|
-
mongodb = cfg.get("database", "caco_db")
|
|
145
|
-
try:
|
|
146
|
-
# Cast run_id to int to avoid problems with numpy int64 encoding in MongoDB
|
|
147
|
-
options.filters = find_filter_wheels(int(run_id), mongodb)
|
|
148
|
-
except IOError:
|
|
149
|
-
log.warning("No filter information found in database. Assuming positions 52.")
|
|
150
|
-
options.filters = 52
|
|
139
|
+
options.filters = utils.get_calib_filters(run_id)
|
|
151
140
|
|
|
152
141
|
return (
|
|
153
142
|
CALIB_BASEDIR
|
|
@@ -156,6 +145,15 @@ def get_calibration_filename(run_id: int, prod_id: str) -> Path:
|
|
|
156
145
|
).resolve()
|
|
157
146
|
|
|
158
147
|
|
|
148
|
+
def get_catB_calibration_filename(run_id: int) -> Path:
|
|
149
|
+
"""Return the Category-B calibration filename of a given run."""
|
|
150
|
+
date = utils.date_to_dir(options.date)
|
|
151
|
+
calib_prod_id = utils.get_lstchain_version()
|
|
152
|
+
catB_calib_dir = Path(cfg.get("LST1", "CAT_B_CALIB_BASE")) / "calibration" / date / calib_prod_id
|
|
153
|
+
filters = utils.get_calib_filters(run_id)
|
|
154
|
+
return catB_calib_dir / f"cat_B_calibration_filters_{filters}.Run{run_id:05d}.h5"
|
|
155
|
+
|
|
156
|
+
|
|
159
157
|
def pedestal_ids_file_exists(run_id: int) -> bool:
|
|
160
158
|
"""Look for the files with pedestal interleaved event identification."""
|
|
161
159
|
pedestal_ids_dir = Path(cfg.get("LST1", "PEDESTAL_FINDER_DIR"))
|
|
@@ -256,7 +254,10 @@ def sequence_calibration_files(sequence_list: List[Sequence]) -> None:
|
|
|
256
254
|
|
|
257
255
|
def get_datacheck_files(pattern: str, directory: Path) -> list:
|
|
258
256
|
"""Return a list of files matching the pattern."""
|
|
259
|
-
|
|
257
|
+
if pattern=="datacheck_dl1*.pdf":
|
|
258
|
+
return sorted(directory.glob("tailcut*/datacheck/"+pattern))
|
|
259
|
+
else:
|
|
260
|
+
return sorted(directory.glob(pattern))
|
|
260
261
|
|
|
261
262
|
|
|
262
263
|
def datacheck_directory(data_type: str, date: str) -> Path:
|
|
@@ -264,7 +265,7 @@ def datacheck_directory(data_type: str, date: str) -> Path:
|
|
|
264
265
|
if data_type in {"PEDESTAL", "CALIB"}:
|
|
265
266
|
directory = Path(cfg.get("LST1", f"{data_type}_DIR")) / date / "pro/log"
|
|
266
267
|
elif data_type == "DL1AB":
|
|
267
|
-
directory =
|
|
268
|
+
directory = Path(cfg.get("LST1", f"{data_type}_DIR")) / date / options.prod_id
|
|
268
269
|
elif data_type == "LONGTERM":
|
|
269
270
|
directory = Path(cfg.get("LST1", f"{data_type}_DIR")) / options.prod_id / date
|
|
270
271
|
else:
|
|
@@ -272,7 +273,7 @@ def datacheck_directory(data_type: str, date: str) -> Path:
|
|
|
272
273
|
return directory
|
|
273
274
|
|
|
274
275
|
|
|
275
|
-
def destination_dir(concept: str, create_dir: bool = True) -> Path:
|
|
276
|
+
def destination_dir(concept: str, create_dir: bool = True, dl1_prod_id: str = None, dl2_prod_id: str = None) -> Path:
|
|
276
277
|
"""
|
|
277
278
|
Create final destination directory for each data level.
|
|
278
279
|
See Also osa.utils.register_run_concept_files
|
|
@@ -303,7 +304,7 @@ def destination_dir(concept: str, create_dir: bool = True) -> Path:
|
|
|
303
304
|
Path(cfg.get(options.tel_id, "DL1_DIR"))
|
|
304
305
|
/ nightdir
|
|
305
306
|
/ options.prod_id
|
|
306
|
-
/
|
|
307
|
+
/ dl1_prod_id
|
|
307
308
|
/ "datacheck"
|
|
308
309
|
)
|
|
309
310
|
elif concept == "DL1AB":
|
|
@@ -311,13 +312,14 @@ def destination_dir(concept: str, create_dir: bool = True) -> Path:
|
|
|
311
312
|
Path(cfg.get(options.tel_id, "DL1_DIR"))
|
|
312
313
|
/ nightdir
|
|
313
314
|
/ options.prod_id
|
|
314
|
-
/
|
|
315
|
+
/ dl1_prod_id
|
|
315
316
|
)
|
|
316
317
|
elif concept in {"DL2", "DL3"}:
|
|
317
318
|
directory = (
|
|
318
319
|
(Path(cfg.get(options.tel_id, f"{concept}_DIR")) / nightdir)
|
|
319
320
|
/ options.prod_id
|
|
320
|
-
|
|
321
|
+
/ dl2_prod_id
|
|
322
|
+
)
|
|
321
323
|
elif concept in {"PEDESTAL", "CALIB", "TIMECALIB"}:
|
|
322
324
|
directory = (
|
|
323
325
|
Path(cfg.get(options.tel_id, f"{concept}_DIR"))
|
|
@@ -397,6 +399,7 @@ def create_longterm_symlink(cherenkov_job_id: str = None):
|
|
|
397
399
|
else:
|
|
398
400
|
log.warning(f"Job {cherenkov_job_id} (lstchain_cherenkov_transparency) did not finish successfully.")
|
|
399
401
|
|
|
402
|
+
|
|
400
403
|
def dl1_datacheck_longterm_file_exits() -> bool:
|
|
401
404
|
"""Return true if the longterm DL1 datacheck file was already produced."""
|
|
402
405
|
nightdir = utils.date_to_dir(options.date)
|
|
@@ -404,3 +407,79 @@ def dl1_datacheck_longterm_file_exits() -> bool:
|
|
|
404
407
|
longterm_file = longterm_dir / options.prod_id / nightdir / f"DL1_datacheck_{nightdir}.h5"
|
|
405
408
|
return longterm_file.exists()
|
|
406
409
|
|
|
410
|
+
|
|
411
|
+
def catB_closed_file_exists(run_id: int) -> bool:
|
|
412
|
+
catB_closed_file = Path(options.directory) / f"catB_{run_id:05d}.closed"
|
|
413
|
+
return catB_closed_file.exists()
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def catB_calibration_file_exists(run_id: int) -> bool:
|
|
417
|
+
catB_calib_base_dir = Path(cfg.get("LST1","CAT_B_CALIB_BASE"))
|
|
418
|
+
prod_id = utils.get_lstchain_version()
|
|
419
|
+
night_dir = utils.date_to_dir(options.date)
|
|
420
|
+
filters = utils.get_calib_filters(run_id)
|
|
421
|
+
catB_calib_dir = catB_calib_base_dir / "calibration" / night_dir / prod_id
|
|
422
|
+
catB_calib_file = catB_calib_dir / f"cat_B_calibration_filters_{filters}.Run{run_id:05d}.h5"
|
|
423
|
+
return catB_calib_file.exists()
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
def get_dl1_prod_id(config_filename):
|
|
427
|
+
with open(config_filename) as json_file:
|
|
428
|
+
data = json.load(json_file)
|
|
429
|
+
|
|
430
|
+
picture_thresh = data["tailcuts_clean_with_pedestal_threshold"]["picture_thresh"]
|
|
431
|
+
boundary_thresh = data["tailcuts_clean_with_pedestal_threshold"]["boundary_thresh"]
|
|
432
|
+
|
|
433
|
+
if boundary_thresh == 4:
|
|
434
|
+
return f"tailcut{picture_thresh}{boundary_thresh}"
|
|
435
|
+
else:
|
|
436
|
+
return f"tailcut{picture_thresh}{boundary_thresh:02d}"
|
|
437
|
+
|
|
438
|
+
|
|
439
|
+
def get_dl2_nsb_prod_id(rf_model: Path) -> str:
|
|
440
|
+
match = re.search(r'nsb_tuning_\d+\.\d+', str(rf_model))
|
|
441
|
+
if not match:
|
|
442
|
+
log.warning(f"No 'nsb_tuning_X.XX' pattern found in the path:\n{rf_model}")
|
|
443
|
+
sys.exit(1)
|
|
444
|
+
else:
|
|
445
|
+
return match.group(0)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def get_dl1_prod_id_and_config(run_id: int) -> str:
|
|
449
|
+
if not cfg.getboolean("lstchain", "apply_standard_dl1b_config"):
|
|
450
|
+
tailcuts_finder_dir = Path(cfg.get(options.tel_id, "TAILCUTS_FINDER_DIR"))
|
|
451
|
+
dl1b_config_file = tailcuts_finder_dir / f"dl1ab_Run{run_id:05d}.json"
|
|
452
|
+
if not dl1b_config_file.exists() and not options.simulate:
|
|
453
|
+
log.error(
|
|
454
|
+
f"The dl1b config file was not created yet for run {run_id:05d}. "
|
|
455
|
+
"Please try again later."
|
|
456
|
+
)
|
|
457
|
+
sys.exit(1)
|
|
458
|
+
else:
|
|
459
|
+
dl1_prod_id = get_dl1_prod_id(dl1b_config_file)
|
|
460
|
+
return dl1_prod_id, dl1b_config_file.resolve()
|
|
461
|
+
else:
|
|
462
|
+
dl1b_config_file = Path(cfg.get("lstchain", "dl1b_config"))
|
|
463
|
+
dl1_prod_id = cfg.get("LST1", "DL1_PROD_ID")
|
|
464
|
+
return dl1_prod_id, dl1b_config_file.resolve()
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def get_dl2_prod_id(run_id: int) -> str:
|
|
468
|
+
dl1_prod_id = get_dl1_prod_id_and_config(run_id)[0]
|
|
469
|
+
rf_model = utils.get_RF_model(run_id)
|
|
470
|
+
nsb_prod_id = get_dl2_nsb_prod_id(rf_model)
|
|
471
|
+
return f"{dl1_prod_id}/{nsb_prod_id}"
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def all_dl1ab_config_files_exist(date: str) -> bool:
|
|
475
|
+
nightdir = date.replace("-","")
|
|
476
|
+
run_summary_dir = Path(cfg.get(options.tel_id, "RUN_SUMMARY_DIR"))
|
|
477
|
+
run_summary_file = run_summary_dir / f"RunSummary_{nightdir}.ecsv"
|
|
478
|
+
summary_table = Table.read(run_summary_file)
|
|
479
|
+
data_runs = summary_table[summary_table["run_type"] == "DATA"]
|
|
480
|
+
for run_id in data_runs["run_id"]:
|
|
481
|
+
tailcuts_finder_dir = Path(cfg.get(options.tel_id, "TAILCUTS_FINDER_DIR"))
|
|
482
|
+
dl1b_config_file = tailcuts_finder_dir / f"dl1ab_Run{run_id:05d}.json"
|
|
483
|
+
if not dl1b_config_file.exists():
|
|
484
|
+
return False
|
|
485
|
+
return True
|
osa/provenance/capture.py
CHANGED
|
@@ -53,7 +53,7 @@ LOG_FILENAME = provconfig["handlers"]["provHandler"]["filename"]
|
|
|
53
53
|
PROV_PREFIX = provconfig["PREFIX"]
|
|
54
54
|
SUPPORTED_HASH_METHOD = ["md5"]
|
|
55
55
|
SUPPORTED_HASH_BUFFER = ["content", "path"]
|
|
56
|
-
REDUCTION_TASKS = ["r0_to_dl1", "dl1ab", "dl1_datacheck", "dl1_to_dl2"]
|
|
56
|
+
REDUCTION_TASKS = ["r0_to_dl1", "catB_calibration", "dl1ab", "dl1_datacheck", "dl1_to_dl2"]
|
|
57
57
|
|
|
58
58
|
# global variables
|
|
59
59
|
traced_entities = {}
|
|
@@ -200,6 +200,13 @@ activities:
|
|
|
200
200
|
# filepath: /fefs/aswg/data/real/DL1/20200218/v0.4.3_v00/
|
|
201
201
|
# size: 128
|
|
202
202
|
|
|
203
|
+
catB_calibration:
|
|
204
|
+
description:
|
|
205
|
+
"Create Cat-B calibration file for an observation run"
|
|
206
|
+
parameters:
|
|
207
|
+
usage:
|
|
208
|
+
generation:
|
|
209
|
+
|
|
203
210
|
dl1ab:
|
|
204
211
|
description:
|
|
205
212
|
"Create DL1AB files for an observation run"
|
osa/provenance/utils.py
CHANGED
|
@@ -10,7 +10,7 @@ from osa.utils.utils import date_to_dir, get_lstchain_version
|
|
|
10
10
|
|
|
11
11
|
__all__ = ["parse_variables", "get_log_config"]
|
|
12
12
|
|
|
13
|
-
REDUCTION_TASKS = ["r0_to_dl1", "dl1ab", "dl1_datacheck", "dl1_to_dl2"]
|
|
13
|
+
REDUCTION_TASKS = ["r0_to_dl1", "catB_calibration", "dl1ab", "dl1_datacheck", "dl1_to_dl2"]
|
|
14
14
|
|
|
15
15
|
|
|
16
16
|
def parse_variables(class_instance):
|
|
@@ -40,20 +40,18 @@ def parse_variables(class_instance):
|
|
|
40
40
|
configfile_dl1b = cfg.get("lstchain", "dl1b_config")
|
|
41
41
|
configfile_dl2 = cfg.get("lstchain", "dl2_config")
|
|
42
42
|
raw_dir = Path(cfg.get("LST1", "R0_DIR"))
|
|
43
|
-
rf_models_directory = Path(cfg.get("
|
|
43
|
+
rf_models_directory = Path(cfg.get("LST1", "RF_MODELS"))
|
|
44
44
|
dl1_dir = Path(cfg.get("LST1", "DL1_DIR"))
|
|
45
45
|
dl2_dir = Path(cfg.get("LST1", "DL2_DIR"))
|
|
46
|
-
calib_dir = Path(cfg.get("LST1", "
|
|
47
|
-
pedestal_dir = Path(cfg.get("LST1", "
|
|
46
|
+
calib_dir = Path(cfg.get("LST1", "CAT_A_CALIB_DIR"))
|
|
47
|
+
pedestal_dir = Path(cfg.get("LST1", "CAT_A_PEDESTAL_DIR"))
|
|
48
48
|
|
|
49
49
|
class_instance.SoftwareVersion = get_lstchain_version()
|
|
50
50
|
class_instance.ProcessingConfigFile = str(options.configfile)
|
|
51
51
|
class_instance.ObservationDate = flat_date
|
|
52
52
|
if class_instance.__name__ in REDUCTION_TASKS:
|
|
53
53
|
muon_dir = dl1_dir / flat_date / options.prod_id / "muons"
|
|
54
|
-
|
|
55
|
-
outdir_dl2 = dl2_dir / flat_date / options.prod_id / options.dl2_prod_id
|
|
56
|
-
|
|
54
|
+
|
|
57
55
|
if class_instance.__name__ in ["drs4_pedestal", "calibrate_charge"]:
|
|
58
56
|
# drs4_pedestal_run_id [0] 1804
|
|
59
57
|
# pedcal_run_id [1] 1805
|
|
@@ -111,6 +109,7 @@ def parse_variables(class_instance):
|
|
|
111
109
|
run = run_subrun.split(".")[0]
|
|
112
110
|
class_instance.ObservationRun = run
|
|
113
111
|
|
|
112
|
+
outdir_dl1 = dl1_dir / flat_date / options.prod_id
|
|
114
113
|
calibration_file = Path(class_instance.args[0]).resolve()
|
|
115
114
|
pedestal_file = Path(class_instance.args[1]).resolve()
|
|
116
115
|
timecalibration_file = Path(class_instance.args[2]).resolve()
|
|
@@ -133,10 +132,16 @@ def parse_variables(class_instance):
|
|
|
133
132
|
class_instance.InterleavedPedestalEventsFile = None
|
|
134
133
|
if class_instance.args[6] is not None:
|
|
135
134
|
class_instance.InterleavedPedestalEventsFile = str(Path(class_instance.args[6]))
|
|
135
|
+
|
|
136
|
+
if class_instance.__name__ == "catB_calibration":
|
|
137
|
+
class_instance.ObservationRun = class_instance.args[0].split(".")[0]
|
|
136
138
|
|
|
137
139
|
if class_instance.__name__ == "dl1ab":
|
|
138
140
|
# run_str [0] 02006.0000
|
|
141
|
+
# dl1b_config [1]
|
|
142
|
+
# dl1_prod_id [2]
|
|
139
143
|
|
|
144
|
+
outdir_dl1 = dl1_dir / flat_date / options.prod_id / class_instance.args[2]
|
|
140
145
|
class_instance.Analysisconfigfile_dl1 = str(Path(configfile_dl1b))
|
|
141
146
|
class_instance.ObservationRun = class_instance.args[0].split(".")[0]
|
|
142
147
|
class_instance.StoreImage = cfg.getboolean("lstchain", "store_image_dl1ab")
|
|
@@ -146,9 +151,12 @@ def parse_variables(class_instance):
|
|
|
146
151
|
|
|
147
152
|
if class_instance.__name__ == "dl1_datacheck":
|
|
148
153
|
# run_str [0] 02006.0000
|
|
154
|
+
# dl1b_prod_id [1]
|
|
155
|
+
|
|
149
156
|
run_subrun = class_instance.args[0]
|
|
150
157
|
run = run_subrun.split(".")[0]
|
|
151
158
|
|
|
159
|
+
outdir_dl1 = dl1_dir / flat_date / options.prod_id / class_instance.args[1]
|
|
152
160
|
class_instance.ObservationRun = run
|
|
153
161
|
class_instance.DL1SubrunDataset = str(
|
|
154
162
|
(outdir_dl1 / f"dl1_LST-1.Run{run_subrun}.h5").resolve()
|
|
@@ -168,9 +176,16 @@ def parse_variables(class_instance):
|
|
|
168
176
|
|
|
169
177
|
if class_instance.__name__ == "dl1_to_dl2":
|
|
170
178
|
# run_str [0] 02006.0000
|
|
179
|
+
# rf_model_path [1]
|
|
180
|
+
# dl1_prod_id [2]
|
|
181
|
+
# dl2_prod_id [3]
|
|
182
|
+
|
|
171
183
|
run_subrun = class_instance.args[0]
|
|
172
184
|
run = run_subrun.split(".")[0]
|
|
173
185
|
|
|
186
|
+
outdir_dl1 = dl1_dir / flat_date / options.prod_id / class_instance.args[2]
|
|
187
|
+
outdir_dl2 = dl2_dir / flat_date / options.prod_id / class_instance.args[3]
|
|
188
|
+
|
|
174
189
|
class_instance.Analysisconfigfile_dl2 = configfile_dl2
|
|
175
190
|
class_instance.ObservationRun = run
|
|
176
191
|
class_instance.RFModelEnergyFile = str((rf_models_directory / "reg_energy.sav").resolve())
|
osa/scripts/autocloser.py
CHANGED
|
@@ -266,21 +266,11 @@ class Sequence:
|
|
|
266
266
|
|
|
267
267
|
def is_100(self, no_dl2: bool):
|
|
268
268
|
"""Check that all analysis products are 100% complete."""
|
|
269
|
-
if (
|
|
270
|
-
no_dl2
|
|
271
|
-
and self.dict_sequence["Tel"] != "ST"
|
|
272
|
-
and self.dict_sequence["DL1%"] == "100"
|
|
273
|
-
and self.dict_sequence["DL1AB%"] == "100"
|
|
274
|
-
and self.dict_sequence["MUONS%"] == "100"
|
|
275
|
-
):
|
|
276
|
-
return True
|
|
277
|
-
|
|
278
269
|
if (
|
|
279
270
|
self.dict_sequence["Tel"] != "ST"
|
|
280
271
|
and self.dict_sequence["DL1%"] == "100"
|
|
281
272
|
and self.dict_sequence["DL1AB%"] == "100"
|
|
282
273
|
and self.dict_sequence["MUONS%"] == "100"
|
|
283
|
-
and self.dict_sequence["DL2%"] == "100"
|
|
284
274
|
):
|
|
285
275
|
return True
|
|
286
276
|
|
|
@@ -44,23 +44,27 @@ def is_calibration_produced(drs4_pedestal_run_id: int, pedcal_run_id: int) -> bo
|
|
|
44
44
|
def drs4_pedestal_command(drs4_pedestal_run_id: int) -> list:
|
|
45
45
|
"""Build the create_drs4_pedestal command."""
|
|
46
46
|
base_dir = Path(cfg.get("LST1", "BASE")).resolve()
|
|
47
|
+
r0_dir = Path(cfg.get("LST1", "R0_DIR")).resolve()
|
|
47
48
|
command = cfg.get("lstchain", "drs4_baseline")
|
|
48
49
|
return [
|
|
49
50
|
command,
|
|
50
51
|
"-r", str(drs4_pedestal_run_id),
|
|
51
52
|
"-b", base_dir,
|
|
53
|
+
f"--r0-dir={r0_dir}",
|
|
52
54
|
"--no-progress",
|
|
53
55
|
]
|
|
54
56
|
|
|
55
57
|
def calibration_file_command(drs4_pedestal_run_id: int, pedcal_run_id: int) -> list:
|
|
56
58
|
"""Build the create_calibration_file command."""
|
|
57
59
|
base_dir = Path(cfg.get("LST1", "BASE")).resolve()
|
|
60
|
+
r0_dir = Path(cfg.get("LST1", "R0_DIR")).resolve()
|
|
58
61
|
command = cfg.get("lstchain", "charge_calibration")
|
|
59
62
|
cmd = [
|
|
60
63
|
command,
|
|
61
64
|
"-p", str(drs4_pedestal_run_id),
|
|
62
65
|
"-r", str(pedcal_run_id),
|
|
63
66
|
"-b", base_dir,
|
|
67
|
+
f"--r0-dir={r0_dir}",
|
|
64
68
|
]
|
|
65
69
|
# In case of problems with trigger tagging:
|
|
66
70
|
if cfg.getboolean("lstchain", "use_ff_heuristic_id"):
|
osa/scripts/closer.py
CHANGED
|
@@ -169,14 +169,14 @@ def post_process(seq_tuple):
|
|
|
169
169
|
# Extract the provenance info
|
|
170
170
|
extract_provenance(seq_list)
|
|
171
171
|
|
|
172
|
-
# Merge DL1b files run-wise
|
|
173
|
-
merge_files(seq_list, data_level="DL1AB")
|
|
174
|
-
|
|
175
172
|
merge_muon_files(seq_list)
|
|
176
173
|
|
|
177
|
-
# Merge
|
|
178
|
-
|
|
179
|
-
merge_files(
|
|
174
|
+
# Merge DL1b files run-wise
|
|
175
|
+
for sequence in seq_list:
|
|
176
|
+
dl1_merge_job_id = merge_files(sequence, data_level="DL1AB")
|
|
177
|
+
# Produce DL2 files run-wise
|
|
178
|
+
if not options.no_dl2 and sequence.type=="DATA":
|
|
179
|
+
dl1_to_dl2(sequence, dl1_merge_job_id)
|
|
180
180
|
|
|
181
181
|
# Merge DL1 datacheck files and produce PDFs. It also produces
|
|
182
182
|
# the daily datacheck report using the longterm script, and updates
|
|
@@ -215,6 +215,66 @@ def post_process(seq_tuple):
|
|
|
215
215
|
return False
|
|
216
216
|
|
|
217
217
|
|
|
218
|
+
def dl1_to_dl2(sequence, dl1_merge_job_id) -> int:
|
|
219
|
+
"""
|
|
220
|
+
It prepares and execute the dl1 to dl2 lstchain scripts that applies
|
|
221
|
+
the already trained RFs models to DL1 files. It identifies the
|
|
222
|
+
primary particle, reconstructs its energy and direction.
|
|
223
|
+
|
|
224
|
+
Parameters
|
|
225
|
+
----------
|
|
226
|
+
run_str: str
|
|
227
|
+
|
|
228
|
+
Returns
|
|
229
|
+
-------
|
|
230
|
+
rc: int
|
|
231
|
+
"""
|
|
232
|
+
nightdir = date_to_dir(options.date)
|
|
233
|
+
dl2_dir = Path(cfg.get("LST1", "DL2_DIR"))
|
|
234
|
+
dl2_subdirectory = dl2_dir / nightdir / options.prod_id / sequence.dl2_prod_id
|
|
235
|
+
dl2_file = dl2_subdirectory / f"dl2_LST-1.Run{sequence.run_str[:5]}.h5"
|
|
236
|
+
dl2_config = Path(cfg.get("lstchain", "dl2_config"))
|
|
237
|
+
dl1ab_subdirectory = Path(cfg.get("LST1", "DL1AB_DIR"))
|
|
238
|
+
dl1_file = dl1ab_subdirectory / nightdir / options.prod_id / sequence.dl1_prod_id / f"dl1_LST-1.Run{sequence.run_str[:5]}.h5"
|
|
239
|
+
log_dir = Path(options.directory) / "log"
|
|
240
|
+
slurm_account = cfg.get("SLURM", "ACCOUNT")
|
|
241
|
+
|
|
242
|
+
if dl2_file.exists():
|
|
243
|
+
log.debug(f"The dl2 file {dl2_file} already exists.")
|
|
244
|
+
return 0
|
|
245
|
+
|
|
246
|
+
command = cfg.get("lstchain", "dl1_to_dl2")
|
|
247
|
+
cmd = [
|
|
248
|
+
"sbatch",
|
|
249
|
+
"--parsable",
|
|
250
|
+
"--mem-per-cpu=60GB",
|
|
251
|
+
f"--account={slurm_account}",
|
|
252
|
+
"-o", f"{log_dir}/Run{sequence.run_str[:5]}_dl2_%j.out",
|
|
253
|
+
"-e", f"{log_dir}/Run{sequence.run_str[:5]}_dl2_%j.err",
|
|
254
|
+
f"--dependency=afterok:{dl1_merge_job_id}",
|
|
255
|
+
command,
|
|
256
|
+
f"--input-file={dl1_file}",
|
|
257
|
+
f"--output-dir={dl2_subdirectory}",
|
|
258
|
+
f"--path-models={sequence.rf_model}",
|
|
259
|
+
f"--config={dl2_config}",
|
|
260
|
+
]
|
|
261
|
+
log.info(f"executing {cmd}")
|
|
262
|
+
|
|
263
|
+
if options.simulate:
|
|
264
|
+
return 0
|
|
265
|
+
|
|
266
|
+
if not options.test and shutil.which("sbatch") is not None:
|
|
267
|
+
job = subprocess.run(
|
|
268
|
+
cmd,
|
|
269
|
+
encoding="utf-8",
|
|
270
|
+
capture_output=True,
|
|
271
|
+
text=True,
|
|
272
|
+
check=True,
|
|
273
|
+
)
|
|
274
|
+
job_id = job.stdout.strip()
|
|
275
|
+
return job_id
|
|
276
|
+
|
|
277
|
+
|
|
218
278
|
def post_process_files(seq_list: list):
|
|
219
279
|
"""
|
|
220
280
|
Identify the different types of files, try to close the sequences
|
|
@@ -226,9 +286,7 @@ def post_process_files(seq_list: list):
|
|
|
226
286
|
list of sequences
|
|
227
287
|
"""
|
|
228
288
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
DL1AB_RE = re.compile(rf"{options.dl1_prod_id}/dl1.*.(?:h5|hdf5|hdf)")
|
|
289
|
+
DL1AB_RE = re.compile(r"tailcut.*/dl1.*.(?:h5|hdf5|hdf)")
|
|
232
290
|
MUONS_RE = re.compile(r"muons.*.fits")
|
|
233
291
|
DATACHECK_RE = re.compile(r"datacheck_dl1.*.(?:h5|hdf5|hdf)")
|
|
234
292
|
INTERLEAVED_RE = re.compile(r"interleaved.*.(?:h5|hdf5|hdf)")
|
|
@@ -243,27 +301,36 @@ def post_process_files(seq_list: list):
|
|
|
243
301
|
)
|
|
244
302
|
|
|
245
303
|
if not options.no_dl2:
|
|
246
|
-
DL2_RE = re.compile(
|
|
304
|
+
DL2_RE = re.compile("tailcut.*/nsb_tuning_.*/dl2.*.(?:h5|hdf5|hdf)")
|
|
247
305
|
pattern_files["DL2"] = DL2_RE
|
|
248
306
|
|
|
249
307
|
for concept, pattern_re in pattern_files.items():
|
|
250
|
-
|
|
308
|
+
for sequence in seq_list:
|
|
309
|
+
output_files_set = set(Path(options.directory).rglob(f"*Run{sequence.run:05d}*"))
|
|
310
|
+
log.info(f"Post processing {concept} files, {len(output_files_set)} files left")
|
|
311
|
+
|
|
312
|
+
if sequence.type=="DATA":
|
|
313
|
+
dst_path = destination_dir(
|
|
314
|
+
concept,
|
|
315
|
+
create_dir=True,
|
|
316
|
+
dl1_prod_id=sequence.dl1_prod_id,
|
|
317
|
+
dl2_prod_id=sequence.dl2_prod_id
|
|
318
|
+
)
|
|
251
319
|
|
|
252
|
-
|
|
320
|
+
log.debug(f"Checking if {concept} files need to be moved to {dst_path}")
|
|
253
321
|
|
|
254
|
-
|
|
322
|
+
for file_path in output_files_set.copy():
|
|
255
323
|
|
|
256
|
-
|
|
324
|
+
file = str(file_path)
|
|
257
325
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
continue
|
|
326
|
+
# If seqtoclose is set, we only want to close that sequence
|
|
327
|
+
if options.seqtoclose is not None and options.seqtoclose not in file:
|
|
328
|
+
continue
|
|
262
329
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
330
|
+
if pattern_found := pattern_re.search(file):
|
|
331
|
+
log.debug(f"Pattern {concept} found, {pattern_found} in {file}")
|
|
332
|
+
registered_file = register_found_pattern(file_path, seq_list, concept, dst_path)
|
|
333
|
+
output_files_set.remove(registered_file)
|
|
267
334
|
|
|
268
335
|
|
|
269
336
|
def set_closed_with_file():
|
|
@@ -335,13 +402,13 @@ def merge_dl1_datacheck(seq_list) -> List[str]:
|
|
|
335
402
|
log.debug("Merging dl1 datacheck files and producing PDFs")
|
|
336
403
|
|
|
337
404
|
muons_dir = destination_dir("MUON", create_dir=False)
|
|
338
|
-
datacheck_dir = destination_dir("DATACHECK", create_dir=False)
|
|
339
405
|
slurm_account = cfg.get("SLURM", "ACCOUNT")
|
|
340
406
|
|
|
341
407
|
list_job_id = []
|
|
342
408
|
|
|
343
409
|
for sequence in seq_list:
|
|
344
410
|
if sequence.type == "DATA":
|
|
411
|
+
datacheck_dir = destination_dir("DATACHECK", create_dir=False, dl1_prod_id=sequence.dl1_prod_id)
|
|
345
412
|
cmd = [
|
|
346
413
|
"sbatch",
|
|
347
414
|
"--parsable",
|
|
@@ -387,7 +454,7 @@ def extract_provenance(seq_list):
|
|
|
387
454
|
"""
|
|
388
455
|
log.info("Extract provenance run wise")
|
|
389
456
|
|
|
390
|
-
nightdir =
|
|
457
|
+
nightdir = date_to_iso(options.date)
|
|
391
458
|
slurm_account = cfg.get("SLURM", "ACCOUNT")
|
|
392
459
|
|
|
393
460
|
for sequence in seq_list:
|
|
@@ -431,40 +498,52 @@ def get_pattern(data_level) -> Tuple[str, str]:
|
|
|
431
498
|
raise ValueError(f"Unknown data level {data_level}")
|
|
432
499
|
|
|
433
500
|
|
|
434
|
-
def merge_files(
|
|
501
|
+
def merge_files(sequence, data_level="DL2"):
|
|
435
502
|
"""Merge DL1b or DL2 h5 files run-wise."""
|
|
436
503
|
log.info(f"Looping over the sequences and merging the {data_level} files")
|
|
437
|
-
|
|
438
|
-
data_dir = destination_dir(data_level, create_dir=False)
|
|
439
504
|
pattern, prefix = get_pattern(data_level)
|
|
440
505
|
slurm_account = cfg.get("SLURM", "ACCOUNT")
|
|
506
|
+
|
|
507
|
+
if sequence.type == "DATA":
|
|
508
|
+
data_dir = destination_dir(
|
|
509
|
+
data_level,
|
|
510
|
+
create_dir=False,
|
|
511
|
+
dl1_prod_id=sequence.dl1_prod_id,
|
|
512
|
+
dl2_prod_id=sequence.dl2_prod_id
|
|
513
|
+
)
|
|
514
|
+
merged_file = Path(data_dir) / f"{prefix}_LST-1.Run{sequence.run:05d}.h5"
|
|
441
515
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
f"--run-number={sequence.run}",
|
|
459
|
-
f"--pattern={pattern}",
|
|
460
|
-
]
|
|
516
|
+
cmd = [
|
|
517
|
+
"sbatch",
|
|
518
|
+
"--parsable",
|
|
519
|
+
f"--account={slurm_account}",
|
|
520
|
+
"-D",
|
|
521
|
+
options.directory,
|
|
522
|
+
"-o",
|
|
523
|
+
f"log/merge_{prefix}_{sequence.run:05d}_%j.log",
|
|
524
|
+
"lstchain_merge_hdf5_files",
|
|
525
|
+
f"--input-dir={data_dir}",
|
|
526
|
+
f"--output-file={merged_file}",
|
|
527
|
+
"--no-image",
|
|
528
|
+
"--no-progress",
|
|
529
|
+
f"--run-number={sequence.run}",
|
|
530
|
+
f"--pattern={pattern}",
|
|
531
|
+
]
|
|
461
532
|
|
|
462
|
-
|
|
533
|
+
log.debug(f"Executing {stringify(cmd)}")
|
|
463
534
|
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
535
|
+
if not options.simulate and not options.test and shutil.which("sbatch") is not None:
|
|
536
|
+
job = subprocess.run(
|
|
537
|
+
cmd,
|
|
538
|
+
encoding="utf-8",
|
|
539
|
+
capture_output=True,
|
|
540
|
+
text=True,
|
|
541
|
+
check=True,
|
|
542
|
+
)
|
|
543
|
+
job_id = job.stdout.strip()
|
|
544
|
+
return job_id
|
|
545
|
+
else:
|
|
546
|
+
log.debug("Simulate launching scripts")
|
|
468
547
|
|
|
469
548
|
|
|
470
549
|
def merge_muon_files(sequence_list):
|
|
@@ -503,7 +582,7 @@ def merge_muon_files(sequence_list):
|
|
|
503
582
|
def daily_longterm_cmd(parent_job_ids: List[str]) -> List[str]:
|
|
504
583
|
"""Build the daily longterm command."""
|
|
505
584
|
nightdir = date_to_dir(options.date)
|
|
506
|
-
datacheck_dir = destination_dir("DATACHECK", create_dir=False)
|
|
585
|
+
datacheck_dir = destination_dir("DATACHECK", create_dir=False, dl1_prod_id="tailcut84")
|
|
507
586
|
muons_dir = destination_dir("MUON", create_dir=False)
|
|
508
587
|
longterm_dir = Path(cfg.get("LST1", "LONGTERM_DIR")) / options.prod_id / nightdir
|
|
509
588
|
longterm_output_file = longterm_dir / f"DL1_datacheck_{nightdir}.h5"
|
|
@@ -548,7 +627,7 @@ def daily_datacheck(cmd: List[str]):
|
|
|
548
627
|
def cherenkov_transparency_cmd(longterm_job_id: str) -> List[str]:
|
|
549
628
|
"""Build the cherenkov transparency command."""
|
|
550
629
|
nightdir = date_to_dir(options.date)
|
|
551
|
-
datacheck_dir = destination_dir("DATACHECK", create_dir=False)
|
|
630
|
+
datacheck_dir = destination_dir("DATACHECK", create_dir=False, dl1_prod_id="tailcut84")
|
|
552
631
|
longterm_dir = Path(cfg.get("LST1", "LONGTERM_DIR")) / options.prod_id / nightdir
|
|
553
632
|
longterm_datacheck_file = longterm_dir / f"DL1_datacheck_{nightdir}.h5"
|
|
554
633
|
slurm_account = cfg.get("SLURM", "ACCOUNT")
|
osa/scripts/copy_datacheck.py
CHANGED
|
@@ -5,12 +5,13 @@ directories whenever they are needed.
|
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import logging
|
|
8
|
+
from pathlib import Path
|
|
8
9
|
|
|
9
10
|
from osa.configs import options
|
|
11
|
+
from osa.configs.config import cfg
|
|
10
12
|
from osa.paths import (
|
|
11
13
|
datacheck_directory,
|
|
12
14
|
get_datacheck_files,
|
|
13
|
-
destination_dir,
|
|
14
15
|
)
|
|
15
16
|
from osa.utils.cliopts import copy_datacheck_parsing
|
|
16
17
|
from osa.utils.logging import myLogger
|
|
@@ -90,8 +91,9 @@ def get_number_of_runs():
|
|
|
90
91
|
Get the run sequence processed list for the given date by globbing the
|
|
91
92
|
run-wise DL1 files.
|
|
92
93
|
"""
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
nightdir = date_to_dir(options.date)
|
|
95
|
+
dl1_directory = Path(cfg.get("LST1", "DL1_DIR")) / nightdir / options.prod_id
|
|
96
|
+
list_files = list(dl1_directory.glob("tailcut*/dl1_LST-1.Run?????.h5"))
|
|
95
97
|
return len(list_files)
|
|
96
98
|
|
|
97
99
|
|