diff --git a/ctapipe/io/datawriter.py b/ctapipe/io/datawriter.py index 0b0631d5af9..daca2aa3f55 100644 --- a/ctapipe/io/datawriter.py +++ b/ctapipe/io/datawriter.py @@ -298,15 +298,11 @@ def __call__(self, event: ArrayEventContainer): Write a single event to the output file. """ self._at_least_one_event = True + self.log.debug("WRITING EVENT %s", event.index) - # Write subarray event data self._write_subarray_pointing(event, writer=self._writer) + self._write_trigger(self._writer, event) - self.log.debug("WRITING EVENT %s", event.index) - self._writer.write( - table_name="dl1/event/subarray/trigger", - containers=[event.index, event.trigger], - ) if event.simulation is not None and event.simulation.shower is not None: self._writer.write( table_name="simulation/event/subarray/shower", @@ -415,8 +411,9 @@ def _setup_output_path(self): self.write_muon_parameters, ] if not any(writable_things): - raise ToolConfigurationError( - "DataWriter configured to write no information" + self.log.warning( + "No processing results were selected for writing" + ", only writing trigger and simulation information" ) def _setup_writer(self): @@ -616,6 +613,20 @@ def table_name(self, tel_id): """construct dataset table names depending on chosen split method""" return f"tel_{tel_id:03d}" + def _write_trigger(self, writer: TableWriter, event: ArrayEventContainer): + """ + Write trigger information + """ + self._writer.write( + table_name="dl1/event/subarray/trigger", + containers=[event.index, event.trigger], + ) + + for tel_id, trigger in event.trigger.tel.items(): + writer.write( + "dl1/event/telescope/trigger", (_get_tel_index(event, tel_id), trigger) + ) + def _write_r1_telescope_events( self, writer: TableWriter, event: ArrayEventContainer ): @@ -646,13 +657,6 @@ def _write_dl1_telescope_events( event """ - # write the telescope tables - # trigger info - for tel_id, trigger in event.trigger.tel.items(): - writer.write( - "dl1/event/telescope/trigger", [_get_tel_index(event, tel_id), trigger] - ) - # pointing info for tel_id, pnt in event.pointing.tel.items(): current_pointing = (pnt.azimuth, pnt.altitude) diff --git a/ctapipe/tools/dump_triggers.py b/ctapipe/tools/dump_triggers.py deleted file mode 100644 index af03cdd87a9..00000000000 --- a/ctapipe/tools/dump_triggers.py +++ /dev/null @@ -1,142 +0,0 @@ -""" -dump a table of the event times and trigger patterns from a -simtelarray input file. -""" - -import numpy as np -from astropy import units as u -from astropy.table import Table - -from ..core import Provenance, Tool -from ..core.traits import Dict, Path, Unicode -from ..io import EventSource - -MAX_TELS = 1000 - - -class DumpTriggersTool(Tool): - description = Unicode(__doc__) - name = "ctapipe-dump-triggers" - - # ============================================= - # configuration parameters: - # ============================================= - - input_path = Path( - exists=True, directory_ok=False, help="input simtelarray file", allow_none=False - ).tag(config=True) - - output_path = Path( - default_value="triggers.fits", - directory_ok=False, - help="output filename (*.fits, *.h5)", - ).tag(config=True) - - # ============================================= - # map low-level options to high-level command-line options - # ============================================= - - aliases = Dict( - { - "input": "DumpTriggersTool.input_path", - "output": "DumpTriggersTool.output_path", - } - ) - - examples = ( - "ctapipe-dump-triggers --input gamma.simtel.gz " - "--output trig.fits --overwrite" - "\n\n" - "If you want to see more output, use --log_level=DEBUG" - ) - - # ============================================= - # The methods of the Tool (initialize, start, finish): - # ============================================= - - def add_event_to_table(self, event): - """ - add the current hessio event to a row in the `self.events` table - """ - time = event.trigger.time - - if self._prev_time is None: - self._prev_time = time - - if self._current_starttime is None: - self._current_starttime = time - - relative_time = time - self._current_starttime - delta_t = time - self._prev_time - self._prev_time = time - - # build the trigger pattern as a fixed-length array - # (better for storage in FITS format) - # trigtels = event.get_telescope_with_data_list() - trigtels = event.dl0.tel.keys() - self._current_trigpattern[:] = 0 # zero the trigger pattern - self._current_trigpattern[list(trigtels)] = 1 # set the triggered tels - # to 1 - - # insert the row into the table - self.events.add_row( - ( - event.index.event_id, - relative_time.sec, - delta_t.sec, - len(trigtels), - self._current_trigpattern, - ) - ) - - def setup(self): - """setup function, called before `start()`""" - - self.check_output(self.output_path) - self.events = Table( - names=["EVENT_ID", "T_REL", "DELTA_T", "N_TRIG", "TRIGGERED_TELS"], - dtype=[np.int64, np.float64, np.float64, np.int32, np.uint8], - ) - - self.events["TRIGGERED_TELS"].shape = (0, MAX_TELS) - self.events["T_REL"].unit = u.s - self.events["T_REL"].description = "Time relative to first event" - self.events["DELTA_T"].unit = u.s - self.events.meta["INPUT"] = str(self.input_path) - - self._current_trigpattern = np.zeros(MAX_TELS) - self._current_starttime = None - self._prev_time = None - - def start(self): - """main event loop""" - with EventSource(self.input_path) as source: - for event in source: - self.add_event_to_table(event) - - def finish(self): - """ - finish up and write out results (called automatically after - `start()`) - """ - # write out the final table - try: - if ".fits" in self.output_path.suffixes: - self.events.write(self.output_path, overwrite=self.overwrite) - elif self.output_path.suffix in (".hdf5", ".h5", ".hdf"): - self.events.write( - self.output_path, path="/events", overwrite=self.overwrite - ) - else: - self.events.write(self.output_path) - - Provenance().add_output_file(self.output_path) - except IOError as err: - self.log.warning("Couldn't write output (%s)", err) - - self.log.info("\n %s", self.events) - - -def main(): - tool = DumpTriggersTool() - tool.run() diff --git a/ctapipe/tools/process.py b/ctapipe/tools/process.py index b7dc27de185..32848532623 100644 --- a/ctapipe/tools/process.py +++ b/ctapipe/tools/process.py @@ -282,6 +282,7 @@ def start(self): """ Process events """ + self.log.info("applying calibration: %s", self.should_calibrate) self.log.info("(re)compute DL1: %s", self.should_compute_dl1) self.log.info("(re)compute DL2: %s", self.should_compute_dl2) self.log.info( diff --git a/ctapipe/tools/tests/test_process.py b/ctapipe/tools/tests/test_process.py index 04a87079cb1..01039649265 100644 --- a/ctapipe/tools/tests/test_process.py +++ b/ctapipe/tools/tests/test_process.py @@ -478,3 +478,25 @@ def test_plugin_help(capsys): assert ( "PluginReconstructor.foo" in captured.out ), "Tool help is missing plugin classes, did you run `pip install -e ./test_plugin`?" + + +def test_only_trigger_and_simulation(tmp_path): + output = tmp_path / "only_trigger_and_simulation.h5" + + run_tool( + ProcessorTool(), + argv=[ + "--input=dataset://gamma_prod5.simtel.zst", + f"--output={output}", + "--no-write-parameters", + "--overwrite", + ], + cwd=tmp_path, + raises=True, + ) + + with TableLoader(output, load_simulated=True) as loader: + events = loader.read_subarray_events() + assert len(events) == 7 + assert "tels_with_trigger" in events.colnames + assert "true_energy" in events.colnames diff --git a/ctapipe/tools/tests/test_tools.py b/ctapipe/tools/tests/test_tools.py index c31c7736456..c7e2dd46cf9 100644 --- a/ctapipe/tools/tests/test_tools.py +++ b/ctapipe/tools/tests/test_tools.py @@ -81,19 +81,6 @@ def test_fileinfo(tmp_path, dl1_image_file): assert "CTA ACTIVITY ID" in header[str(dl1_image_file)] -def test_dump_triggers(tmp_path): - from ctapipe.tools.dump_triggers import DumpTriggersTool - - sys.argv = ["dump_triggers"] - output_path = tmp_path / "triggers.fits" - tool = DumpTriggersTool(input_path=PROD5B_PATH, output_path=str(output_path)) - - assert run_tool(tool, cwd=tmp_path) == 0 - - assert output_path.exists() - assert run_tool(tool, ["--help-all"]) == 0 - - def test_dump_instrument(tmp_path): from ctapipe.tools.dump_instrument import DumpInstrumentTool diff --git a/docs/changes/2375.api.rst b/docs/changes/2375.api.rst new file mode 100644 index 00000000000..36ab27b3d7e --- /dev/null +++ b/docs/changes/2375.api.rst @@ -0,0 +1,5 @@ +The ``ctapipe-dump-triggers`` tool was removed, since it wrote a custom data format +not compatble with e.g. the output of the ``DataWriter`` and ``ctapipe-process``. +If you only want to store trigger and simulation information from simulated / DL0 +input files into the ctapipe format HDF5 files, you can now use +``ctapipe-process -i -o --no-write-parameters``. diff --git a/setup.cfg b/setup.cfg index 5092c8e6292..6386aa80308 100644 --- a/setup.cfg +++ b/setup.cfg @@ -88,7 +88,6 @@ exclude = [options.entry_points] console_scripts = ctapipe-info = ctapipe.tools.info:main - ctapipe-dump-triggers = ctapipe.tools.dump_triggers:main ctapipe-dump-instrument=ctapipe.tools.dump_instrument:main ctapipe-display-dl1 = ctapipe.tools.display_dl1:main ctapipe-process = ctapipe.tools.process:main