import json
import time
import datetime as dt
from pathlib import Path
import numpy as np
from listmode import misc
import listmode.exceptions as ex
"""
Utils is a repository for generally useful functions and classes for thing such as reading and writing.
"""
[docs]def delete_channel_data(data_path, base_name, cfg):
"""
Used to delete channel files after parsing.
:param data_path:
:param base_name:
:param cfg:
:return:
"""
timenames, enames, tnames, xnames = find_data_files(data_path, base_name, cfg, mode='channel')
for fname in timenames + enames + tnames:
if fname.exists():
fname.unlink()
for chnamelist in xnames:
for fname in chnamelist:
if fname is not None:
if fname.exists():
fname.unlink()
[docs]def find_data_files(data_path, base_name, cfg, mode):
"""
Generate filenames (paths) of all the data produced by config. You can select either 'event' or 'channel'
type names.
:param data_path:
:param base_name:
:param cfg:
:param mode:
:return: timing, energy, time and extras filenames as lists. For event data the first (and only) item in the list
is the relevant filename. For channel data there is a single name for every channel. Extra names are given
as a list of names, one per extra (for each channel as in other data).
"""
extras = cfg.det['extras']
if mode == 'event':
timenames = [data_path / (base_name + '_timestamps.dat')]
enames = [data_path / (base_name + '_events.dat')]
tnames = [data_path / (base_name + '_timing.dat')]
xnames = []
try:
for extra in extras:
xnames.append([data_path / (base_name + '_{}.dat'.format(extra['name']))])
except TypeError:
pass
elif mode == 'channel':
timenames = [data_path / (base_name + '_timestamps_ch{}.dat'.format(x)) for x in cfg.det['ch_list']]
enames = [data_path / (base_name + '_events_ch{}.dat'.format(x)) for x in cfg.det['ch_list']]
tnames = [data_path / (base_name + '_timing_ch{}.dat'.format(x)) for x in cfg.det['ch_list']]
xnames = []
if extras is not None:
for extra in extras:
xnames.append([]) # all extras get a position in list
if extra['name'] not in ['latency', 'multihit']:
# this extra is present in channel data and will be combined either as
# a bit in a bitmask or a column in a matrix.
try: # channel mask default is ones
ch_mask = np.array(extra['ch_mask'], dtype='bool')
except:
ch_mask = np.ones((len(cfg.det['ch_cfg']),), dtype='bool')
# Extra names are retrieved by channel index. In cases there is no extra defined
# for a channel we need to put something in to the list. Let it be None then.
for ch_idx in range(len(cfg.det['ch_cfg'])):
xnames[-1].append(None)
if ch_mask[ch_idx]:
efil = data_path / (base_name + '_{}_ch{}.dat'.format(extra['name'], ch_idx))
#assert(efil.exists())
xnames[-1][-1] = efil
return timenames, enames, tnames, xnames
def write_channel_metadata(data_path, base_name, channel, metadata):
datetime_type = ['start', 'stop']
# make sure data_path is a pathlib Path
data_path = Path(data_path)
try:
temp_for_file = {key: value for key, value in metadata[channel].items() if key not in datetime_type}
except:
print(metadata[channel].items())
raise ex.ListModeException('write metadata')
if 'notes' in metadata[channel]:
temp_for_file['notes'] = metadata[channel]['notes']
pass
else:
temp_for_file['notes'] = 'Metadata defined by raw files.'
if metadata[channel]['start'] is None:
temp_for_file['notes'] += 'Missing start time substituted by compile time.'
metadata[channel]['start'] = dt.datetime.fromtimestamp(time.time())
if metadata[channel]['stop'] is None:
temp_for_file['notes'] += 'Missing stop time calculated from timestamps.'
metadata[channel]['stop'] = metadata[channel]['start'] + \
dt.timedelta(seconds=int(metadata[channel]['total_time']*1e-9))
for key in datetime_type:
temp_for_file[key] = metadata[channel][key].isoformat()
with (data_path / (base_name + '_metadata_ch{:02d}.json'.format(channel))).open('w') as fil:
json.dump(temp_for_file, fil, indent=0, default=int)
def read_channel_metadata(data_path, base_name, channel):
with (data_path / (base_name + '_metadata_ch{:02d}.json'.format(channel))).open('r') as fil:
temp_from_file = json.load(fil)
datetime_type = ['start', 'stop']
metadata = {key: value for key, value in temp_from_file.items() if key not in datetime_type}
for key in datetime_type:
#metadata[key] = dt.datetime.fromisoformat(temp_from_file[key]) # not present in python 3.6
metadata[key] = misc.fromisoformat(temp_from_file[key])
return metadata
def find_path(config, name, suffix):
home = Path(config.path['home'])
filename = Path(name + suffix) #name of the file
if (home / filename).exists():
loadpath = home / filename
print('Found local file', loadpath)
else:
loadpath = Path(config.path['cfg_dir']) / suffix.split('.')[0][1:] / filename
return loadpath
[docs]def load_plot_config(config, plot_name):
"""
Loads a plot configuration from a json file.
:param config: Config data that has the config.path in it
:param plot_name_list: plot config name without the _plotcfg.json postfix or a list of names
:return: a plot config dictionary
"""
try:
with find_path(config, plot_name, '_plotcfg.json').open('r') as fil:
plot_config = json.load(fil)
except FileNotFoundError:
raise ex.ListModeConfigurationError('Could not find plot configuration.')
return plot_config
[docs]def load_style_sheet(config, style_name_list):
"""
Loads a matplotlib style sheet written as a json file.
:param config: Detector configuration object
:param style_name_list: style path or a list of paths
:return: list of style dictionaries
"""
style_cfg_list = []
if isinstance(style_name_list, str): # single stylesheet if string is given
style_name_list = [style_name_list]
while len(style_name_list) > 0:
style_name = style_name_list.pop(0)
try:
with find_path(config, style_name, '_stylecfg.json').open('r') as fil:
style_config = json.load(fil)
style_cfg_list.append(style_config)
except FileNotFoundError:
raise ex.ListModeConfigurationError('Could not find stylesheet.')
return style_cfg_list
def load_effcal(config, eff=None):
if eff is None:
eff = config.det['effcal']
with find_path(config, eff, '_effcal.json').open('r') as fil:
temp = json.load(fil)
effcal = temp
return effcal
[docs]def load_strip_cal(config):
"""
Load strip calibration files if they exist.
"""
strips = []
for ch in config.det['ch_cfg']:
try:
if ch['cal_array'] is not None:
strips.append(ch['cal_array'])
except KeyError:
pass
if strips:
strip_cal = []
try:
for cal in strips:
with find_path(config, cal, '_stripcal.json').open('r') as fil:
strip_cal.append(json.load(fil)['calibration'])
except FileNotFoundError:
print('Strip calibration file not found!')
raise ex.ListModeConfigurationError('Strip calibration file not found!')
out = np.stack(strip_cal, axis=0)
return out
return None