Newer
Older
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Code for computeing 2D/3D/4D histograms ("images") based on the event_hits hit pattern of the file_to_hits.py output"""
import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
#import line_profiler # call with kernprof -l -v file.py args
#from memory_profiler import profile
from mpl_toolkits.axes_grid1 import make_axes_locatable
def get_time_parameters(event_hits, mode=('trigger_cluster', 'all'), t_start_margin=0.15, t_end_margin=0.15):
"""
Gets the fundamental time parameters in one place for cutting a time residual.
Later on these parameters cut out a certain time span of events specified by t_start and t_end.
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
:param ndarray(ndim=2) event_hits: 2D array that contains the hits (_xyzt) data for a certain eventID. [positions_xyz, time, triggered]
:param tuple(str, str) mode: type of time cut that is used. Currently available: timeslice_relative and first_triggered.
:param float t_start_margin: Used in timeslice_relative mode. Defines the start time of the selected timespan with t_mean - t_start * t_diff.
:param float t_end_margin: Used in timeslice_relative mode. Defines the end time of the selected timespan with t_mean + t_start * t_diff.
:return: float t_start, t_end: absolute start and end time that will be used for the later timespan cut.
Events in this timespan are accepted, others are rejected.
"""
t = event_hits[:, 3:4]
if mode[0] == 'trigger_cluster':
triggered = event_hits[:, 4:5]
t = t[triggered == 1]
t_mean = np.mean(t, dtype=np.float64)
if mode[1] == 'tight_1':
# make a tighter cut, 12.5ns / bin
t_start = t_mean - 250 # trigger-cluster - 350ns
t_end = t_mean + 500 # trigger-cluster + 850ns
elif mode[1] == 'tight_2':
# make an even tighter cut, 5.8ns / bin
t_start = t_mean - 150 # trigger-cluster - 150ns
t_end = t_mean + 200 # trigger-cluster + 200ns
else:
assert mode[1] == 'all'
# include nearly all mc_hits from muon-CC and elec-CC, 20ns / bin
t_start = t_mean - 350 # trigger-cluster - 350ns
t_end = t_mean + 850 # trigger-cluster + 850ns
elif mode[0] == 'timeslice_relative':
t_min = np.amin(t)
t_max = np.amax(t)
t_diff = t_max - t_min
t_mean = t_min + 0.5 * t_diff
t_start = t_mean - t_start_margin * t_diff
t_end = t_mean + t_end_margin * t_diff
else: raise ValueError('Time cut modes other than "first_triggered" or "timeslice_relative" are currently not supported.')
return t_start, t_end
def compute_4d_to_2d_histograms(event_hits, x_bin_edges, y_bin_edges, z_bin_edges, n_bins, all_4d_to_2d_hists, timecut, event_track, do2d_pdf, pdf_2d_plots):
"""
Computes 2D numpy histogram 'images' from the 4D data.
:param ndarray(ndim=2) event_hits: 2D array that contains the hits (_xyzt) data for a certain eventID. [positions_xyz, time, triggered]
:param ndarray(ndim=1) x_bin_edges: bin edges for the X-direction.
:param ndarray(ndim=1) y_bin_edges: bin edges for the Y-direction.
:param ndarray(ndim=1) z_bin_edges: bin edges for the Z-direction.
:param tuple n_bins: Contains the number of bins that should be used for each dimension (x,y,z,t).
:param list all_4d_to_2d_hists: contains all 2D histogram projections.
:param (str, str/None) timecut: Tuple that defines what timecut should be used in hits_to_histograms.py.
:param ndarray(ndim=2) event_track: contains the relevant mc_track info for the event in order to get a nice title for the pdf histos.
:param bool do2d_pdf: if True, generate 2D matplotlib pdf histograms.
:param PdfPages/None pdf_2d_plots: either a mpl PdfPages instance or None.
:return: appends the 2D histograms to the all_4d_to_2d_hists list.
"""
x, y, z, t = event_hits[:, 0], event_hits[:, 1], event_hits[:, 2], event_hits[:, 3]
# analyze time
t_start, t_end = get_time_parameters(event_hits, mode=timecut)
# create histograms for this event
hist_xy = np.histogram2d(x, y, bins=(x_bin_edges, y_bin_edges)) # hist[0] = H, hist[1] = xedges, hist[2] = yedges
hist_xz = np.histogram2d(x, z, bins=(x_bin_edges, z_bin_edges))
hist_yz = np.histogram2d(y, z, bins=(y_bin_edges, z_bin_edges))
hist_xt = np.histogram2d(x, t, bins=(x_bin_edges, n_bins[3]), range=((min(x_bin_edges), max(x_bin_edges)), (t_start, t_end)))
hist_yt = np.histogram2d(y, t, bins=(y_bin_edges, n_bins[3]), range=((min(y_bin_edges), max(y_bin_edges)), (t_start, t_end)))
hist_zt = np.histogram2d(z, t, bins=(z_bin_edges, n_bins[3]), range=((min(z_bin_edges), max(z_bin_edges)), (t_start, t_end)))
# Format in classical numpy convention: x along first dim (vertical), y along second dim (horizontal)
all_4d_to_2d_hists.append((np.array(hist_xy[0], dtype=np.uint8),
np.array(hist_xz[0], dtype=np.uint8),
np.array(hist_yz[0], dtype=np.uint8),
np.array(hist_xt[0], dtype=np.uint8),
np.array(hist_yt[0], dtype=np.uint8),
np.array(hist_zt[0], dtype=np.uint8)))
if do2d_pdf:
# Format in classical numpy convention: x along first dim (vertical), y along second dim (horizontal)
# Need to take that into account in convert_2d_numpy_hists_to_pdf_image()
# transpose to get typical cartesian convention: y along first dim (vertical), x along second dim (horizontal)
hists = [hist_xy, hist_xz, hist_yz, hist_xt, hist_yt, hist_zt]
convert_2d_numpy_hists_to_pdf_image(hists, t_start, t_end, pdf_2d_plots, event_track=event_track) # slow! takes about 1s per event
def convert_2d_numpy_hists_to_pdf_image(hists, t_start, t_end, pdf_2d_plots, event_track=None):
"""
Creates matplotlib 2D histos based on the numpy histogram2D objects and saves them to a pdf file.
:param list(ndarray(ndim=2)) hists: Contains np.histogram2d objects of all projections [xy, xz, yz, xt, yt, zt].
:param float t_start: absolute start time of the timespan cut.
:param float t_end: absolute end time of the timespan cut.
:param PdfPages/None pdf_2d_plots: either a mpl PdfPages instance or None.
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
:param ndarray(ndim=2) event_track: contains the relevant mc_track info for the event in order to get a nice title for the pdf histos.
[event_id, particle_type, energy, isCC, bjorkeny, dir_x/y/z]
"""
fig = plt.figure(figsize=(10, 13))
if event_track is not None:
particle_type = {16: 'Tau', -16: 'Anti-Tau', 14: 'Muon', -14: 'Anti-Muon', 12: 'Electron', -12: 'Anti-Electron', 'isCC': ['NC', 'CC']}
event_info = {'event_id': str(int(event_track[0])), 'energy': str(event_track[2]),
'particle_type': particle_type[int(event_track[1])], 'interaction_type': particle_type['isCC'][int(event_track[3])]}
title = event_info['particle_type'] + '-' + event_info['interaction_type'] + ', Event ID: ' + event_info['event_id'] + ', Energy: ' + event_info['energy'] + ' GeV'
props = dict(boxstyle='round', facecolor='wheat', alpha=0.5)
fig.suptitle(title, usetex=False, horizontalalignment='center', size='xx-large', bbox=props)
t_diff = t_end - t_start
axes_xy = plt.subplot2grid((3, 2), (0, 0), title='XY - projection', xlabel='X Position [m]', ylabel='Y Position [m]', aspect='equal', xlim=(-175, 175), ylim=(-175, 175))
axes_xz = plt.subplot2grid((3, 2), (0, 1), title='XZ - projection', xlabel='X Position [m]', ylabel='Z Position [m]', aspect='equal', xlim=(-175, 175), ylim=(-57.8, 400))
axes_yz = plt.subplot2grid((3, 2), (1, 0), title='YZ - projection', xlabel='Y Position [m]', ylabel='Z Position [m]', aspect='equal', xlim=(-175, 175), ylim=(-57.8, 292.2))
axes_xt = plt.subplot2grid((3, 2), (1, 1), title='XT - projection', xlabel='X Position [m]', ylabel='Time [ns]', aspect='auto',
xlim=(-175, 175), ylim=(t_start - 0.1*t_diff, t_end + 0.1*t_diff))
axes_yt = plt.subplot2grid((3, 2), (2, 0), title='YT - projection', xlabel='Y Position [m]', ylabel='Time [ns]', aspect='auto',
xlim=(-175, 175), ylim=(t_start - 0.1*t_diff, t_end + 0.1*t_diff))
axes_zt = plt.subplot2grid((3, 2), (2, 1), title='ZT - projection', xlabel='Z Position [m]', ylabel='Time [ns]', aspect='auto',
xlim=(-57.8, 292.2), ylim=(t_start - 0.1*t_diff, t_end + 0.1*t_diff))
def fill_subplot(hist_ab, axes_ab):
# Mask hist_ab
h_ab_masked = np.ma.masked_where(hist_ab[0] == 0, hist_ab[0])
a, b = np.meshgrid(hist_ab[1], hist_ab[2]) #2,1
plot_ab = axes_ab.pcolormesh(a, b, h_ab_masked.T)
the_divider = make_axes_locatable(axes_ab)
color_axis = the_divider.append_axes("right", size="5%", pad=0.1)
# add color bar
cbar_ab = plt.colorbar(plot_ab, cax=color_axis, ax=axes_ab)
cbar_ab.ax.set_ylabel('Hits [#]')
return plot_ab
plot_xy = fill_subplot(hists[0], axes_xy)
plot_xz = fill_subplot(hists[1], axes_xz)
plot_yz = fill_subplot(hists[2], axes_yz)
plot_xt = fill_subplot(hists[3], axes_xt)
plot_yt = fill_subplot(hists[4], axes_yt)
plot_zt = fill_subplot(hists[5], axes_zt)
fig.tight_layout(rect=[0, 0.02, 1, 0.95])
pdf_2d_plots.savefig(fig)
plt.close()
def compute_4d_to_3d_histograms(event_hits, x_bin_edges, y_bin_edges, z_bin_edges, n_bins, all_4d_to_3d_hists, timecut):
"""
Computes 3D numpy histogram 'images' from the 4D data.
Careful: Currently, appending to all_4d_to_3d_hists takes quite a lot of memory (about 200MB for 3500 events).
In the future, the list should be changed to a numpy ndarray.
(Which unfortunately would make the code less readable, since an array is needed for each projection...)
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
:param ndarray(ndim=2) event_hits: 2D array that contains the hits (_xyzt) data for a certain eventID. [positions_xyz, time, triggered]
:param ndarray(ndim=1) x_bin_edges: bin edges for the X-direction.
:param ndarray(ndim=1) y_bin_edges: bin edges for the Y-direction.
:param ndarray(ndim=1) z_bin_edges: bin edges for the Z-direction.
:param tuple n_bins: Declares the number of bins that should be used for each dimension (x,y,z,t).
:param list all_4d_to_3d_hists: contains all 3D histogram projections.
:param (str, str/None) timecut: Tuple that defines what timecut should be used in hits_to_histograms.py.
:return: appends the 3D histograms to the all_4d_to_3d_hists list. [xyz, xyt, xzt, yzt, rzt]
"""
x, y, z, t = event_hits[:, 0:1], event_hits[:, 1:2], event_hits[:, 2:3], event_hits[:, 3:4]
t_start, t_end = get_time_parameters(event_hits, mode=timecut)
hist_xyz = np.histogramdd(event_hits[:, 0:3], bins=(x_bin_edges, y_bin_edges, z_bin_edges))
hist_xyt = np.histogramdd(np.concatenate([x, y, t], axis=1), bins=(x_bin_edges, y_bin_edges, n_bins[3]),
range=((min(x_bin_edges), max(x_bin_edges)), (min(y_bin_edges), max(y_bin_edges)), (t_start, t_end)))
hist_xzt = np.histogramdd(np.concatenate([x, z, t], axis=1), bins=(x_bin_edges, z_bin_edges, n_bins[3]),
range=((min(x_bin_edges), max(x_bin_edges)), (min(z_bin_edges), max(z_bin_edges)), (t_start, t_end)))
hist_yzt = np.histogramdd(event_hits[:, 1:4], bins=(y_bin_edges, z_bin_edges, n_bins[3]),
range=((min(y_bin_edges), max(y_bin_edges)), (min(z_bin_edges), max(z_bin_edges)), (t_start, t_end)))
# add a rotation-symmetric 3d hist
r = np.sqrt(x * x + y * y)
rzt = np.concatenate([r, z, t], axis=1)
hist_rzt = np.histogramdd(rzt, bins=(n_bins[0], n_bins[2], n_bins[3]), range=((np.amin(r), np.amax(r)), (np.amin(z), np.amax(z)), (t_start, t_end)))
all_4d_to_3d_hists.append((np.array(hist_xyz[0], dtype=np.uint8),
np.array(hist_xyt[0], dtype=np.uint8),
np.array(hist_xzt[0], dtype=np.uint8),
np.array(hist_yzt[0], dtype=np.uint8),
np.array(hist_rzt[0], dtype=np.uint8)))
def compute_4d_to_4d_histograms(event_hits, x_bin_edges, y_bin_edges, z_bin_edges, n_bins, all_4d_to_4d_hists, timecut, do4d):
"""
Computes 4D numpy histogram 'images' from the 4D data.
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
:param ndarray(ndim=2) event_hits: 2D array that contains the hits (_xyzt) data for a certain eventID. [positions_xyz, time, triggered, (channel_id)]
:param ndarray(ndim=1) x_bin_edges: bin edges for the X-direction.
:param ndarray(ndim=1) y_bin_edges: bin edges for the Y-direction.
:param ndarray(ndim=1) z_bin_edges: bin edges for the Z-direction.
:param tuple n_bins: Declares the number of bins that should be used for each dimension (x,y,z,t).
:param list all_4d_to_4d_hists: contains all 4D histogram projections.
:param (str, str/None) timecut: Tuple that defines what timecut should be used in hits_to_histograms.py.
:param (bool, str) do4d: Tuple, where [1] declares what should be used as 4th dimension after xyz.
Currently, only 'time' and 'channel_id' are available.
:return: appends the 4D histogram to the all_4d_to_4d_hists list. [xyzt]
"""
t_start, t_end = get_time_parameters(event_hits, mode=timecut)
if do4d[1] == 'time':
hist_4d = np.histogramdd(event_hits[:, 0:4], bins=(x_bin_edges, y_bin_edges, z_bin_edges, n_bins[3]),
range=((min(x_bin_edges),max(x_bin_edges)),(min(y_bin_edges),max(y_bin_edges)),
(min(z_bin_edges),max(z_bin_edges)),(t_start, t_end)))
elif do4d[1] == 'channel_id':
time = event_hits[:, 3]
event_hits = event_hits[np.logical_and(time >= t_start, time <= t_end)]
channel_id = event_hits[:, 5:6]
hist_4d = np.histogramdd(np.concatenate([event_hits[:, 0:3], channel_id], axis=1), bins=(x_bin_edges, y_bin_edges, z_bin_edges, 31),
range=((min(x_bin_edges),max(x_bin_edges)),(min(y_bin_edges),max(y_bin_edges)),
(min(z_bin_edges),max(z_bin_edges)),(np.amin(channel_id), np.amax(channel_id))))
else:
raise ValueError('The parameter in do4d[1] ' + str(do4d[1]) + ' is not available. Currently, only time and channel_id are supported.')
all_4d_to_4d_hists.append(np.array(hist_4d[0], dtype=np.uint8))