Using Container classes¶
ctapipe.core.Container
is the base class for all event-wise data classes in ctapipe. It works like a object-relational mapper, in that it defines a set of Fields
along with their metadata (description, unit, default), which can be later translated automatially into an output table using a ctapipe.io.TableWriter
.
[1]:
from ctapipe.core import Container, Field, Map
import numpy as np
from astropy import units as u
Let’s define a few example containers with some dummy fields in them:
[2]:
class SubContainer(Container):
junk = Field("nothing","Some junk")
value = Field(0.0, "some value", unit=u.deg)
class EventContainer(Container):
event_id = Field(-1,"event id number")
tels_with_data = Field([], "list of telescopes with data")
sub = Field(SubContainer(), "stuff") # a sub-container in the hierarchy
# for dicts of sub-containers, use Map instead
# of a dict() as the default value to support serialization
tel = Field(Map(), "telescopes")
Basic features¶
[3]:
ev = EventContainer()
Check that default values are automatically filled in
[4]:
print(ev.event_id)
print(ev.tel.keys())
print(ev.tel)
-1
dict_keys([])
Map(None, {})
print the json representation
[5]:
print(ev)
{'event_id': -1,
'sub': {'junk': 'nothing', 'value': 0.0},
'tel': {},
'tels_with_data': []}
values can be set as normal for a class:
[6]:
ev.event_id = 100
ev.event_id
[6]:
100
[7]:
ev.as_dict() # by default only shows the bare items, not sub-containers (See later)
[7]:
{'event_id': 100,
'tels_with_data': [],
'sub': __main__.SubContainer:
junk: Some junk
value: some value [deg],
'tel': Map(None, {})}
[8]:
ev.as_dict(recursive=True)
[8]:
{'event_id': 100,
'tels_with_data': [],
'sub': {'junk': 'nothing', 'value': 0.0},
'tel': {}}
Now, let’s define a sub-container that we can add per telescope:
[9]:
class TelContainer(Container):
tel_id = Field(-1, "telescope ID number")
image = Field(np.zeros(10), "camera pixel data")
and we can add a few of these to the parent container inside the tel dict:
[10]:
ev.tel[10] = TelContainer()
ev.tel[5] = TelContainer()
ev.tel[42] = TelContainer()
[11]:
ev.tel
[11]:
Map(None,
{10: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data,
5: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data,
42: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data})
Converion to dictionaries¶
[12]:
ev.as_dict()
[12]:
{'event_id': 100,
'tels_with_data': [],
'sub': __main__.SubContainer:
junk: Some junk
value: some value [deg],
'tel': Map(None,
{10: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data,
5: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data,
42: __main__.TelContainer:
tel_id: telescope ID number
image: camera pixel data})}
[13]:
ev.as_dict(recursive=True, flatten=False)
[13]:
{'event_id': 100,
'tels_with_data': [],
'sub': {'junk': 'nothing', 'value': 0.0},
'tel': {10: {'tel_id': -1,
'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])},
5: {'tel_id': -1, 'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])},
42: {'tel_id': -1,
'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])}}}
for serialization to a table, we can even flatten the output into a single set of columns
[14]:
ev.as_dict(recursive=True, flatten=True)
[14]:
{'event_id': 100,
'tels_with_data': [],
'sub_junk': 'nothing',
'sub_value': 0.0,
'tel_10': {'tel_id': -1,
'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])},
'tel_5': {'tel_id': -1,
'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])},
'tel_42': {'tel_id': -1,
'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])}}
Setting and clearing values¶
[15]:
ev.tel[5].image[:] = 9
print(ev)
{'event_id': 100,
'sub': {'junk': 'nothing', 'value': 0.0},
'tel': {5: {'image': array([9., 9., 9., 9., 9., 9., 9., 9., 9., 9.]),
'tel_id': -1},
10: {'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
'tel_id': -1},
42: {'image': array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]),
'tel_id': -1}},
'tels_with_data': []}
[16]:
ev.reset()
ev.as_dict(recursive=True, flatten=True)
[16]:
{'event_id': -1, 'tels_with_data': [], 'sub_junk': 'nothing', 'sub_value': 0.0}
look at a pre-defined Container¶
[17]:
from ctapipe.containers import SimulatedShowerContainer
[18]:
shower = SimulatedShowerContainer()
[19]:
shower
[19]:
ctapipe.containers.SimulatedShowerContainer:
energy: Simulated Energy [TeV]
alt: Simulated altitude [deg]
az: Simulated azimuth [deg]
core_x: Simulated core position (x) [m]
core_y: Simulated core position (y) [m]
h_first_int: Height of first interaction [m]
x_max: Simulated Xmax value [g / cm2]
shower_primary_id: Simulated shower primary ID 0 (gamma),
1(e-),2(mu-), 100*A+Z for nucleons and
nuclei,negative for antimatter.