Skip to content

Commit 8b5da7a

Browse files
authored
Merge pull request #2901 from cta-observatory/mondata_merger
attach monitoring data to hdf5 file
2 parents bdce21d + 3a71ef0 commit 8b5da7a

16 files changed

+315
-194
lines changed

docs/changes/2901.bugfix.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Fix trigger tables when merging monitoring data of same event types.

docs/changes/2901.feature.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Add merge_strategy option to HDF5Merger component to control merging behavior. The HDF5Merger component supports the following strategies:
2+
- 'events-multiple-obs': allows merging event files (with and without monitoring data) from different observation blocks.
3+
- 'events-single-ob': for merging events in consecutive chunks of the same observation block.
4+
- 'monitoring-only': attaches horizontally monitoring data from the same observation block (requires monitoring=True).
5+
Allow monitoring data to be merged to the HDF5 event data files.

src/ctapipe/conftest.py

Lines changed: 66 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import importlib.util
77
import json
88
import shutil
9+
import warnings
910
from copy import deepcopy
1011
from pathlib import Path
1112

@@ -19,12 +20,11 @@
1920

2021
from ctapipe.core import run_tool
2122
from ctapipe.instrument import CameraGeometry, FromNameWarning, SubarrayDescription
22-
from ctapipe.io import SimTelEventSource
23+
from ctapipe.io import SimTelEventSource, read_table, write_table
2324
from ctapipe.io.hdf5dataformat import (
2425
DL0_TEL_POINTING_GROUP,
2526
DL1_CAMERA_COEFFICIENTS_GROUP,
26-
DL1_CAMERA_MONITORING_GROUP,
27-
FIXED_POINTING_GROUP,
27+
DL1_GROUP,
2828
SIMULATION_GROUP,
2929
)
3030
from ctapipe.utils import get_dataset_path
@@ -198,18 +198,18 @@ def proton_dl2_train_small_h5():
198198

199199

200200
@pytest.fixture(scope="session")
201-
def calibpipe_camcalib_single_chunk():
202-
return get_dataset_path("calibpipe_camcalib_single_chunk_i0.1.0.dl1.h5")
201+
def calibpipe_camcalib_sims_single_chunk():
202+
return get_dataset_path("calibpipe_camcalib_sims_single_chunk_i0.2.0.dl1.h5")
203203

204204

205205
@pytest.fixture(scope="session")
206-
def calibpipe_camcalib_same_chunks():
207-
return get_dataset_path("calibpipe_camcalib_same_chunks_i0.1.0.dl1.h5")
206+
def calibpipe_camcalib_obslike_same_chunks():
207+
return get_dataset_path("calibpipe_camcalib_obslike_same_chunks_i0.2.0.dl1.h5")
208208

209209

210210
@pytest.fixture(scope="session")
211-
def calibpipe_camcalib_different_chunks():
212-
return get_dataset_path("calibpipe_camcalib_different_chunks_i0.1.0.dl1.h5")
211+
def calibpipe_camcalib_obslike_different_chunks():
212+
return get_dataset_path("calibpipe_camcalib_obslike_different_chunks_i0.2.0.dl1.h5")
213213

214214

215215
@pytest.fixture(scope="session")
@@ -496,6 +496,30 @@ def dl1_parameters_file(dl1_tmp_path, prod5_gamma_simtel_path):
496496
return output
497497

498498

499+
@pytest.fixture(scope="session")
500+
def dl1_tel1_file(dl1_tmp_path, prod6_gamma_simtel_path):
501+
"""
502+
DL1 file containing only data for telescope 1 from a gamma simulation set.
503+
"""
504+
from ctapipe.tools.process import ProcessorTool
505+
506+
output = dl1_tmp_path / "gamma_tel1.dl1.h5"
507+
508+
# prevent running process multiple times in case of parallel tests
509+
with FileLock(_lock_file(output)):
510+
if output.is_file():
511+
return output
512+
tel_id = 1
513+
argv = [
514+
f"--input={prod6_gamma_simtel_path}",
515+
f"--output={output}",
516+
f"--EventSource.allowed_tels={tel_id}",
517+
"--write-images",
518+
]
519+
assert run_tool(ProcessorTool(), argv=argv, cwd=dl1_tmp_path) == 0
520+
return output
521+
522+
499523
@pytest.fixture(scope="session")
500524
def dl1_muon_file(dl1_tmp_path):
501525
"""
@@ -708,16 +732,17 @@ def reference_location():
708732

709733

710734
@pytest.fixture(scope="session")
711-
def dl1_mon_pointing_file(calibpipe_camcalib_same_chunks, dl1_tmp_path):
712-
from ctapipe.io import read_table, write_table
713-
735+
def dl1_mon_pointing_file(calibpipe_camcalib_sims_single_chunk, dl1_tmp_path):
714736
path = dl1_tmp_path / "dl1_mon_pointing.dl1.h5"
715-
shutil.copy(calibpipe_camcalib_same_chunks, path)
737+
shutil.copy(calibpipe_camcalib_sims_single_chunk, path)
716738

717739
tel_id = 1
718740
# create some dummy monitoring data
719-
time = read_table(path, f"{DL1_CAMERA_COEFFICIENTS_GROUP}/tel_{tel_id:03d}")["time"]
720-
start, stop = time[[0, -1]]
741+
start = read_table(
742+
calibpipe_camcalib_sims_single_chunk,
743+
f"{DL1_CAMERA_COEFFICIENTS_GROUP}/tel_{tel_id:03d}",
744+
)["time"][0]
745+
stop = start + 10 * u.s
721746
duration = (stop - start).to_value(u.s)
722747

723748
# start a bit before, go a bit longer
@@ -732,86 +757,53 @@ def dl1_mon_pointing_file(calibpipe_camcalib_same_chunks, dl1_tmp_path):
732757

733758
# remove static pointing table
734759
with tables.open_file(path, "r+") as f:
735-
# Remove the constant pointing
736-
f.remove_node(FIXED_POINTING_GROUP, recursive=True)
737-
# Remove camera-related monitoring data
738-
f.remove_node(DL1_CAMERA_MONITORING_GROUP, recursive=True)
760+
# Remove the DL1 table
761+
if DL1_GROUP in f.root:
762+
f.remove_node(DL1_GROUP, recursive=True)
739763

740764
return path
741765

742766

743767
@pytest.fixture(scope="session")
744768
def dl1_mon_pointing_file_obs(dl1_mon_pointing_file, dl1_tmp_path):
745-
path = dl1_tmp_path / "dl1_mon_pointingobs.dl1.h5"
769+
path = dl1_tmp_path / "dl1_mon_pointing_obs.dl1.h5"
746770
shutil.copy(dl1_mon_pointing_file, path)
747771

748772
# Remove the simulation to mimic a real observation file
749773
with tables.open_file(path, "r+") as f:
750-
f.remove_node(SIMULATION_GROUP, recursive=True)
751-
752-
return path
753-
754-
755-
@pytest.fixture(scope="session")
756-
def calibpipe_camcalib_single_chunk_obs(calibpipe_camcalib_single_chunk, dl1_tmp_path):
757-
path = dl1_tmp_path / "calibpipe_camcalib_single_chunk_obs.dl1.h5"
758-
shutil.copy(calibpipe_camcalib_single_chunk, path)
759-
760-
# Remove the simulation to mimic a real observation file
761-
with tables.open_file(path, "r+") as f:
762-
f.remove_node(SIMULATION_GROUP, recursive=True)
763-
764-
return path
765-
766-
767-
@pytest.fixture(scope="session")
768-
def calibpipe_camcalib_same_chunks_obs(calibpipe_camcalib_same_chunks, dl1_tmp_path):
769-
path = dl1_tmp_path / "calibpipe_camcalib_same_chunks_obs.dl1.h5"
770-
shutil.copy(calibpipe_camcalib_same_chunks, path)
771-
772-
# Remove the simulation to mimic a real observation file
773-
with tables.open_file(path, "r+") as f:
774-
f.remove_node(SIMULATION_GROUP, recursive=True)
775-
776-
return path
777-
778-
779-
@pytest.fixture(scope="session")
780-
def calibpipe_camcalib_different_chunks_obs(
781-
calibpipe_camcalib_different_chunks, dl1_tmp_path
782-
):
783-
path = dl1_tmp_path / "calibpipe_camcalib_different_chunks_obs.dl1.h5"
784-
shutil.copy(calibpipe_camcalib_different_chunks, path)
785-
786-
# Remove the simulation to mimic a real observation file
787-
with tables.open_file(path, "r+") as f:
788-
f.remove_node(SIMULATION_GROUP, recursive=True)
789-
774+
data_category = "CTA PRODUCT DATA CATEGORY"
775+
if SIMULATION_GROUP in f.root:
776+
f.remove_node(SIMULATION_GROUP, recursive=True)
777+
if data_category in f.root._v_attrs and f.root._v_attrs[data_category] == "Sim":
778+
with warnings.catch_warnings():
779+
warnings.simplefilter("ignore", tables.NaturalNameWarning)
780+
f.root._v_attrs[data_category] = "Other"
790781
return path
791782

792783

793784
@pytest.fixture(scope="session")
794785
def dl1_merged_monitoring_file(
795-
dl1_tmp_path, dl1_mon_pointing_file, calibpipe_camcalib_different_chunks
786+
dl1_tmp_path,
787+
dl1_tel1_file,
788+
dl1_mon_pointing_file,
789+
calibpipe_camcalib_sims_single_chunk,
796790
):
797791
"""
798792
File containing both camera and pointing monitoring data.
799793
"""
800794
from ctapipe.tools.merge import MergeTool
801795

802796
output = dl1_tmp_path / "dl1_merged_monitoring_file.h5"
797+
shutil.copy(dl1_tel1_file, output)
803798

804799
# prevent running process multiple times in case of parallel tests
805800
with FileLock(output.with_suffix(output.suffix + ".lock")):
806-
if output.is_file():
807-
return output
808-
809801
argv = [
810802
f"--output={output}",
811803
str(dl1_mon_pointing_file),
812-
str(calibpipe_camcalib_different_chunks),
813-
"--monitoring",
814-
"--single-ob",
804+
str(calibpipe_camcalib_sims_single_chunk),
805+
"--append",
806+
"--merge-strategy=monitoring-only",
815807
]
816808
assert run_tool(MergeTool(), argv=argv, cwd=dl1_tmp_path) == 0
817809
return output
@@ -824,8 +816,13 @@ def dl1_merged_monitoring_file_obs(dl1_merged_monitoring_file, dl1_tmp_path):
824816

825817
# Remove the simulation to mimic a real observation file
826818
with tables.open_file(path, "r+") as f:
827-
f.remove_node(SIMULATION_GROUP, recursive=True)
828-
819+
data_category = "CTA PRODUCT DATA CATEGORY"
820+
if SIMULATION_GROUP in f.root:
821+
f.remove_node(SIMULATION_GROUP, recursive=True)
822+
if data_category in f.root._v_attrs and f.root._v_attrs[data_category] == "Sim":
823+
with warnings.catch_warnings():
824+
warnings.simplefilter("ignore", tables.NaturalNameWarning)
825+
f.root._v_attrs[data_category] = "Other"
829826
return path
830827

831828

src/ctapipe/io/datawriter.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,10 @@ def _get_tel_index(event, tel_id):
4444
# (meaning readers need to update scripts)
4545
# - increase the minor number if new columns or datasets are added
4646
# - increase the patch number if there is a small bugfix to the model.
47-
DATA_MODEL_VERSION = "v7.2.0"
47+
DATA_MODEL_VERSION = "v7.3.0"
4848
DATA_MODEL_CHANGE_HISTORY = """
49+
- v7.3.0: - Add possibility to attach monitoring data to the event HDF5 file.
50+
- Add the event type to the telescope trigger container.
4951
- v7.2.0: - Added new monitoring groups: DL1_TEL_[OPTICAL_PSF, MUON_THROUGHPUT, ILLUMINATOR_THROUGHPUT]_GROUP
5052
and DL2_SUBARRAY_[MONITORING, INTER_CALIBRATION, CROSS_CALIBRATION]_GROUP
5153
- Change field name in ``ReconstructedContainer`` from 'classification' to 'particle_type'.

src/ctapipe/io/hdf5eventsource.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@
105105
"v7.0.0",
106106
"v7.1.0",
107107
"v7.2.0",
108+
"v7.3.0",
108109
]
109110

110111

0 commit comments

Comments
 (0)