Skip to content

Commit

Permalink
Merge pull request #140 from cta-observatory/no_object_order_expectation
Browse files Browse the repository at this point in the history
Do not expect much order in the objects of the file
  • Loading branch information
Dominik Neise authored Feb 8, 2019
2 parents 2709cf9 + dcb073c commit 6050c17
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 171 deletions.
16 changes: 8 additions & 8 deletions eventio/simtel/objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,7 @@ def __repr__(self):
)

def parse(self):
assert_exact_version(self, supported_version=0)
assert_version_in(self, supported_versions=(0, 1))
self.seek(0)
byte_stream = BytesIO(self.read())

Expand All @@ -1251,13 +1251,13 @@ def parse(self):
moni_time = read_time(byte_stream)

# Dimensions of various things
# version 0
ns, np, nd, ng = read_from(byte_stream, '<hhhh')
# in version 1 this uses crazy 32bit ints
# ns = read_utf8_like_signed_int(byte_stream)
# np = read_utf8_like_signed_int(byte_stream)
# nd = read_utf8_like_signed_int(byte_stream)
# ng = read_utf8_like_signed_int(byte_stream)
if self.header.version == 0:
ns, np, nd, ng = read_from(byte_stream, '<hhhh')
else: # version == 1
ns = read_utf8_like_signed_int(byte_stream)
np = read_utf8_like_signed_int(byte_stream)
nd = read_utf8_like_signed_int(byte_stream)
ng = read_utf8_like_signed_int(byte_stream)

result = {
'telescope_id': self.telescope_id,
Expand Down
286 changes: 124 additions & 162 deletions eventio/simtel/simtelfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from copy import copy
from collections import defaultdict
import logging
import warnings
from ..base import EventIOFile
from ..exceptions import check_type
from .. import iact
Expand Down Expand Up @@ -65,194 +64,157 @@ def __init__(self, path, allowed_telescopes=None):
self.histograms = None

self.history = []
o = next(self)
while isinstance(o, History):
self.history.append(o)
o = next(self)

check_type(o, RunHeader)
self.header = o.parse()
self.n_telescopes = self.header['n_telescopes']

o = next(self)
self.mc_run_headers = []
while isinstance(o, MCRunHeader):
self.mc_run_headers.append(o.parse())
o = next(self)

self.corsika_inputcards = []
while isinstance(o, iact.InputCard):
self.corsika_inputcards.append(o.parse())
o = next(self)
self.header = None
self.n_telescopes = 0
self.telescope_descriptions = defaultdict(dict)
self.camera_monitorings = defaultdict(dict)
self.laser_calibrations = defaultdict(dict)
self.current_mc_shower = None
self.current_mc_event = None
self.current_photoelectron_sum = None
self.current_photoelectrons = {}
self.current_array_event = None

# read the header:
# assumption: the header is done when
# self.current_mc_shower is not None anymore
while self.current_mc_shower is None:
self.next_low_level()

expected_structure = [
self._first_event_byte = self.tell()

def __iter__(self):
return self.iter_array_events()

def next_low_level(self):
telescope_descriptions_types = (
CameraSettings,
CameraOrganization,
PixelSettings,
DisabledPixels,
CameraSoftwareSettings,
DriveSettings,
PointingCorrection,
]
)
o = next(self)

# in some files, the telescope description block
# strangely contains already MCRunHeader, MCEvent, MCShower
# we save them here for later use
self.init_mc_showers = []
self.init_mc_events = []
if isinstance(o, History):
self.history.append(o)
elif isinstance(o, RunHeader):
self.header = o.parse()
self.n_telescopes = self.header['n_telescopes']
elif isinstance(o, MCRunHeader):
self.mc_run_headers.append(o.parse())
elif isinstance(o, iact.InputCard):
self.corsika_inputcards.append(o.parse())
o = next(self)
elif isinstance(o, telescope_descriptions_types):
key = camel_to_snake(o.__class__.__name__)
self.telescope_descriptions[o.telescope_id][key] = o.parse()

self.telescope_descriptions = defaultdict(dict)
first = True
for _ in range(self.n_telescopes):
for eventio_type in expected_structure:
if not first:
o = next(self)
first = False

while not isinstance(o, eventio_type):
if isinstance(o, MCRunHeader):
self.mc_run_headers.append(o.parse())
msg = 'Unexpectd MCRunHeader in telescope description block'

elif isinstance(o, MCShower):
self.init_mc_showers.append(o.parse())
msg = 'Unexpectd MCShower in telescope description block'

elif isinstance(o, MCEvent):
self.init_mc_events.append(
(o.header.id, o.parse(), self.init_mc_showers[-1])
)
msg = 'Unexpectd MCEvent in telescope description block'
else:
msg = 'Skipping unexpected object of type {}'.format(
o.__class__.__name__
)
warnings.warn(msg)
log.warning(msg)

o = next(self)

key = camel_to_snake(o.__class__.__name__)
self.telescope_descriptions[o.telescope_id][key] = o.parse()
elif isinstance(o, MCShower):
self.current_mc_shower = o.parse()

self._first_event_byte = self.tell()
elif isinstance(o, MCEvent):
self.current_mc_event = o.parse()
self.current_mc_event_id = o.header.id

def __iter__(self):
return self.iter_array_events()
elif isinstance(o, iact.TelescopeData):
self.current_photoelectrons = parse_photoelectrons(o)

def iter_mc_events(self):
self._next_header_pos = self._first_event_byte
elif isinstance(o, MCPhotoelectronSum):
self.current_photoelectron_sum = o.parse()

for event_id, event, shower in self.init_mc_events:
yield {'event_id': event_id, 'event': event, 'shower': shower}
elif isinstance(o, ArrayEvent):
self.current_array_event = parse_array_event(
o,
self.allowed_telescopes
)

current_mc_shower = None
if len(self.init_mc_showers) > 0:
current_mc_shower = self.init_mc_showers[-1]
elif isinstance(o, CameraMonitoring):
self.camera_monitorings[o.telescope_id].update(o.parse())

o = next(self)
elif isinstance(o, LaserCalibration):
self.laser_calibrations[o.telescope_id].update(o.parse())

elif isinstance(o, Histograms):
self.histograms = o.parse()
else:
raise Exception(
'object type encountered, which is no handled'
'at the moment: {}'.format(o)
)

def iter_mc_events(self):
self._next_header_pos = self._first_event_byte
while True:
if isinstance(o, MCShower):
current_mc_shower = o.parse()

elif isinstance(o, MCEvent):
yield {
'event_id': o.header.id,
'mc_shower': current_mc_shower,
'mc_event': o.parse()
}

elif isinstance(o, Histograms):
self.histograms = o.parse()
try:
next_event = self.try_build_mc_event()
except StopIteration:
break

o = next(self)
if next_event is not None:
yield next_event

def try_build_mc_event(self):
self.next_low_level()
if self.current_mc_event:
event_data = {
'event_id': self.current_mc_event_id,
'mc_shower': self.current_mc_shower,
'mc_event': self.current_mc_event,
}
self.current_mc_event = None
return event_data

def iter_array_events(self):
self._next_header_pos = self._first_event_byte

current_mc_shower = None
current_mc_event = None
current_mc_event_id = None

# check if some showers or events were already read in __init__
if len(self.init_mc_showers) > 0:
current_mc_shower = self.init_mc_showers[-1]

if len(self.init_mc_events) > 0:
current_mc_event_id, current_mc_event, _ = self.init_mc_events[-1]

current_photoelectron_sum = None
current_photoelectrons = {}
camera_monitorings = defaultdict(dict)
laser_calibrations = defaultdict(dict)

try:
o = next(self)
except StopIteration:
return

while True:
if isinstance(o, MCShower):
current_mc_shower = o.parse()

elif isinstance(o, MCEvent):
current_mc_event = o.parse()
current_mc_event_id = o.header.id

elif isinstance(o, iact.TelescopeData):
current_photoelectrons = parse_photoelectrons(o)

elif isinstance(o, MCPhotoelectronSum):
current_photoelectron_sum = o.parse()

elif isinstance(o, ArrayEvent):
array_event = parse_array_event(o, self.allowed_telescopes)

# with allowed_telescopes set, it might happen there
# are no telescopes left
if self.allowed_telescopes and len(array_event['telescope_events']) == 0:
try:
o = next(self)
except StopIteration:
return
continue

event_data = {
'event_id': current_mc_event_id,
'mc_shower': current_mc_shower,
'mc_event': current_mc_event,
'telescope_events': array_event['telescope_events'],
'tracking_positions': array_event['tracking_positions'],
'trigger_information': array_event['trigger_information'],
'photoelectron_sums': current_photoelectron_sum,
'photoelectrons': current_photoelectrons,
}
event_data['camera_monitorings'] = {
telescope_id: copy(camera_monitorings[telescope_id])
for telescope_id in array_event['telescope_events'].keys()
}
event_data['laser_calibrations'] = {
telescope_id: copy(laser_calibrations[telescope_id])
for telescope_id in array_event['telescope_events'].keys()
}
yield event_data

elif isinstance(o, CameraMonitoring):
camera_monitorings[o.telescope_id].update(o.parse())

elif isinstance(o, LaserCalibration):
laser_calibrations[o.telescope_id].update(o.parse())

elif isinstance(o, Histograms):
self.histograms = o.parse()
break

try:
o = next(self)
next_event = self.try_build_event()
except StopIteration:
return
break
if next_event is not None:
yield next_event

def try_build_event(self):
'''check if all necessary info for an event was found,
then make an event and invalidate old data
'''
self.next_low_level()

if self.current_array_event:
if (
self.allowed_telescopes
and not self.current_array_event['telescope_events']
):
self.current_array_event = None
return None

event_data = {
'event_id': self.current_mc_event_id,
'mc_shower': self.current_mc_shower,
'mc_event': self.current_mc_event,
'telescope_events': self.current_array_event['telescope_events'],
'tracking_positions': self.current_array_event['tracking_positions'],
'trigger_information': self.current_array_event['trigger_information'],
'photoelectron_sums': self.current_photoelectron_sum,
'photoelectrons': self.current_photoelectrons,
}

event_data['camera_monitorings'] = {
telescope_id: copy(self.camera_monitorings[telescope_id])
for telescope_id in self.current_array_event['telescope_events'].keys()
}
event_data['laser_calibrations'] = {
telescope_id: copy(self.laser_calibrations[telescope_id])
for telescope_id in self.current_array_event['telescope_events'].keys()
}

self.current_array_event = None

return event_data


def parse_array_event(array_event, allowed_telescopes=None):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def finalize_options(self):

setup(
name='eventio',
version='0.15.0',
version='0.16.0',
description='Python read-only implementation of the EventIO file format',
long_description=long_description,
url='https://github.com/fact-project/pyeventio',
Expand Down

0 comments on commit 6050c17

Please sign in to comment.