From 97ac7b803721f9a1f287875352c9bb8d8147959a Mon Sep 17 00:00:00 2001
From: Jason Watson <jason.jw@live.co.uk>
Date: Fri, 4 May 2018 14:07:24 +0100
Subject: [PATCH] Changes to SST1MEventSource (#729)

* Add test using SST1MEventSource in the calibration pipeline

* Cleanup of SST1MEventSource

Removed functions inside containers
Corrected tels_with_data into a set
Added the channel dimension to r0.tel[i].waveform

* Corrected telid for subarray

* Added container to __all__

* Filled event_id and tels_with_data for other levels

* Moved TelescopeDescription so that it is not re-created for each event

* Add obs_id for the other data levels

* Some minor corrections

Restored relative import
Corrected comment
---
 ctapipe/io/containers.py                  | 25 +-----
 ctapipe/io/sst1meventsource.py            | 97 +++++++++++++----------
 ctapipe/io/tests/test_sst1meventsource.py | 16 +++-
 3 files changed, 70 insertions(+), 68 deletions(-)

diff --git a/ctapipe/io/containers.py b/ctapipe/io/containers.py
index 24dd7323..192b3898 100644
--- a/ctapipe/io/containers.py
+++ b/ctapipe/io/containers.py
@@ -21,6 +21,8 @@ __all__ = ['InstrumentContainer',
            'DL1CameraContainer',
            'TargetIOContainer',
            'TargetIOCameraContainer',
+           'SST1MContainer',
+           'SST1MCameraContainer',
            'MCEventContainer',
            'MCHeaderContainer',
            'MCCameraEventContainer',
@@ -32,6 +34,7 @@ __all__ = ['InstrumentContainer',
            'ParticleClassificationContainer',
            'DataContainer',
            'TargetIODataContainer',
+           'SST1MDataContainer',
            'HillasParametersContainer']
 
 
@@ -50,20 +53,6 @@ class SST1MCameraContainer(Container):
         None,
         "trigger 19 patch cluster trace (n_clusters)")
 
-    def fill_from_zfile_event(self, event, pixel_sort_ids):
-        self.pixel_flags = event.pixels_flags[pixel_sort_ids]
-        self.digicam_baseline = event.hiGain.waveforms.baselines[
-            pixel_sort_ids]
-        self.local_camera_clock = (
-            event.local_time_sec * 1E9 + event.local_time_nanosec)
-        self.gps_time = (
-            event.trig.timeSec * 1E9 + event.trig.timeNanoSec)
-        self.camera_event_type = event.event_type
-        self.array_event_type = event.eventType
-        self.trigger_input_traces = event.trigger_input_traces
-        self.trigger_output_patch7 = event.trigger_output_patch7
-        self.trigger_output_patch19 = event.trigger_output_patch19
-
 
 class SST1MContainer(Container):
     tels_with_data = Field([], "list of telescopes with data")
@@ -71,14 +60,6 @@ class SST1MContainer(Container):
         Map(SST1MCameraContainer),
         "map of tel_id to SST1MCameraContainer")
 
-    def fill_from_zfile_event(self, event, pixel_sort_ids):
-        self.tels_with_data = [event.telescopeID, ]
-        sst1m_cam_container = self.tel[event.telescopeID]
-        sst1m_cam_container.fill_from_zfile_event(
-            event,
-            pixel_sort_ids,
-        )
-
 
 # todo: change some of these Maps to be just 3D NDarrays?
 
diff --git a/ctapipe/io/sst1meventsource.py b/ctapipe/io/sst1meventsource.py
index 12bcc779..ea4c80d6 100644
--- a/ctapipe/io/sst1meventsource.py
+++ b/ctapipe/io/sst1meventsource.py
@@ -18,38 +18,76 @@ class SST1MEventSource(EventSource):
         super().__init__(config=config, tool=tool, **kwargs)
         from protozfits import SimpleFile
         self.file = SimpleFile(self.input_url)
-
+        # TODO: Correct pixel ordering
+        self._tel_desc = TelescopeDescription.from_name(
+            optics_name='SST-1M',
+            camera_name='DigiCam'
+        )
 
     def _generator(self):
-        self._pixel_sort_ids = None
+        pixel_sort_ids = None
 
         for count, event in enumerate(self.file.Events):
-            if self._pixel_sort_ids is None:
-                self._pixel_sort_ids = np.argsort(
-                    event.hiGain.waveforms.pixelsIndices)
-                self.n_pixels = len(self._pixel_sort_ids)
+            if pixel_sort_ids is None:
+                pixel_indices = event.hiGain.waveforms.pixelsIndices
+                pixel_sort_ids = np.argsort(pixel_indices)
+                self.n_pixels = len(pixel_sort_ids)
+            telid = event.telescopeID
             data = SST1MDataContainer()
             data.count = count
-            data.sst1m.fill_from_zfile_event(event, self._pixel_sort_ids)
-            self.fill_R0Container_from_zfile_event(data.r0, event)
-            data.inst.subarray.tels[0] = TelescopeDescription.from_name(
-                optics_name='SST-1M', 
-                camera_name='DigiCam'
-            )
-            yield data
 
+            data.inst.subarray.tels[telid] = self._tel_desc
+
+            # Data level Containers
+            data.r0.obs_id = -1
+            data.r0.event_id = event.eventNumber
+            data.r0.tels_with_data = {telid}
+            data.r1.obs_id = -1
+            data.r1.event_id = event.eventNumber
+            data.r1.tels_with_data = {telid}
+            data.dl0.obs_id = -1
+            data.dl0.event_id = event.eventNumber
+            data.dl0.tels_with_data = {telid}
+
+            # R0CameraContainer
+            camera_time = event.local_time_sec * 1E9 + event.local_time_nanosec
+            samples = event.hiGain.waveforms.samples.reshape(self.n_pixels, -1)
+            data.r0.tel[telid].trigger_time = camera_time
+            data.r0.tel[telid].trigger_type = event.event_type
+            data.r0.tel[telid].waveform = samples[pixel_sort_ids][None, :]
+            data.r0.tel[telid].num_samples = samples.shape[-1]
+
+            # SST1MContainer
+            data.sst1m.tels_with_data = {telid}
+
+            # SST1MCameraContainer
+            digicam_baseline = event.hiGain.waveforms.baselines[pixel_sort_ids]
+            gps_time = (event.trig.timeSec * 1E9 + event.trig.timeNanoSec)
+            container = data.sst1m.tel[telid]
+            container.pixel_flags = event.pixels_flags[pixel_sort_ids]
+            container.digicam_baseline = digicam_baseline
+            container.local_camera_clock = camera_time
+            container.gps_time = gps_time
+            container.camera_event_type = event.event_type
+            container.array_event_type = event.eventType
+            container.trigger_input_traces = event.trigger_input_traces
+            container.trigger_output_patch7 = event.trigger_output_patch7
+            container.trigger_output_patch19 = event.trigger_output_patch19
+
+            yield data
 
     @staticmethod
     def is_compatible(file_path):
         from astropy.io import fits
         try:
             h = fits.open(file_path)[1].header
-            ttypes = [
-                h[x] for x in h.keys() if 'TTYPE' in x
-            ]
+            ttypes = [h[x] for x in h.keys() if 'TTYPE' in x]
         except OSError:
             # not even a fits file
             return False
+        except IndexError:
+            # A fits file of a different format
+            return False
 
         is_protobuf_zfits_file = (
             (h['XTENSION'] == 'BINTABLE') and
@@ -61,30 +99,3 @@ class SST1MEventSource(EventSource):
         is_sst1m_file = 'trigger_input_traces' in ttypes
 
         return is_protobuf_zfits_file & is_sst1m_file
-
-    def fill_R0CameraContainer_from_zfile_event(self, container, event):
-        container.trigger_time = (
-            event.local_time_sec * 1E9 + event.local_time_nanosec)
-        container.trigger_type = event.event_type
-
-        _samples = (
-            event.hiGain.waveforms.samples
-        ).reshape(self.n_pixels, -1)
-        container.waveform = _samples[self._pixel_sort_ids]
-
-        # Why is this needed ???
-        # This is exactly the definition of waveforms.
-        container.num_samples = container.waveform.shape[1]
-
-    def fill_R0Container_from_zfile_event(self, container, event):
-        container.obs_id = -1  # I do not know what this is.
-        container.event_id = event.eventNumber
-        container.tels_with_data = [event.telescopeID, ]
-        r0_cam_container = container.tel[event.telescopeID]
-
-        self.fill_R0CameraContainer_from_zfile_event(
-            r0_cam_container,
-            event
-        )
-
-
diff --git a/ctapipe/io/tests/test_sst1meventsource.py b/ctapipe/io/tests/test_sst1meventsource.py
index aa483311..1a7fb175 100644
--- a/ctapipe/io/tests/test_sst1meventsource.py
+++ b/ctapipe/io/tests/test_sst1meventsource.py
@@ -1,8 +1,9 @@
 from pkg_resources import resource_filename
 import os
 import copy
-
 import pytest
+from ctapipe.calib.camera.calibrator import CameraCalibrator
+
 pytest.importorskip("protozfits", minversion="0.44.3")
 
 example_file_path = resource_filename(
@@ -15,7 +16,7 @@ example_file_path = resource_filename(
 )
 
 FIRST_EVENT_NUMBER_IN_FILE = 97750287
-ADC_SAMPLES_SHAPE = (1296, 50)
+ADC_SAMPLES_SHAPE = (1, 1296, 50)
 
 
 def test_loop_over_events():
@@ -28,7 +29,7 @@ def test_loop_over_events():
     )
 
     for i, event in enumerate(inputfile_reader):
-        assert event.r0.tels_with_data == [1]
+        assert event.r0.tels_with_data == {1}
         for telid in event.r0.tels_with_data:
             assert event.r0.event_id == FIRST_EVENT_NUMBER_IN_FILE + i
             assert event.r0.tel[telid].waveform.shape == ADC_SAMPLES_SHAPE
@@ -70,3 +71,12 @@ def test_factory_for_protozfits_file():
     reader = EventSourceFactory.produce(input_url=example_file_path)
     assert isinstance(reader, SST1MEventSource)
     assert reader.input_url == example_file_path
+
+
+def test_pipeline():
+    from ctapipe.io.sst1meventsource import SST1MEventSource
+    reader = SST1MEventSource(input_url=example_file_path, max_events=10)
+    calibrator = CameraCalibrator(eventsource=reader)
+    for event in reader:
+        calibrator.calibrate(event)
+        assert event.r0.tel.keys() == event.dl1.tel.keys()
-- 
GitLab