xboa
Common.py
Go to the documentation of this file.
1 #This file is a part of xboa
2 #
3 #xboa is free software: you can redistribute it and/or modify
4 #it under the terms of the GNU General Public License as published by
5 #the Free Software Foundation, either version 3 of the License, or
6 #(at your option) any later version.
7 #
8 #xboa is distributed in the hope that it will be useful,
9 #but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 #GNU General Public License for more details.
12 #
13 #You should have received a copy of the GNU General Public License
14 #along with xboa in the doc folder. If not, see
15 #<http://www.gnu.org/licenses/>.
16 #
17 
18 """
19 Common contains some global physics and other parameters, plus some useful functions
20 - icool_pid_to_pdg = dict from icool particle identification indices to pdg indices
21 - pdg_pid_to_icool = inverse of above
22 - constants = string to number dict of physical constants
23 - units = string to number dict of some units
24 Functions documented below
25 """
26 
27 import operator
28 import copy
29 import bisect
30 import sys
31 import string
32 import math
33 import os
34 import pydoc
35 import bisect
36 import atexit
37 import time
38 import signal
39 import ctypes
40 
41 xboa_version = '0.15.1'
42 float_tolerance = 1.e-9
43 kill_subprocesses_at_exit = True
44 
45 __has_maus = False
46 try:
47  import libMausCpp
48  __has_maus = True
49 except:
50  print 'Maus library not detected'
51 
52 __has_root = True
53 try:
54  import ROOT
55  ROOT.gStyle.SetPalette(1)
56 except ImportError:
57  print 'PyRoot not detected - ROOT graphing functions will not be available'
58  __has_root = False
59 
60 __has_numpy = True
61 try:
62  import numpy
63  from numpy import linalg
64  from numpy import matrix
65 except ImportError:
66  print 'NumPy not detected - many analysis functions will not be available'
67  __has_numpy = False
68 
69 __has_matplot = True
70 try:
71  import matplotlib
72  import pylab
73  from matplotlib import pyplot
74 except ImportError:
75  print 'MatPlotLib not detected - matplotlib graphing functions will not be available'
76  __has_matplot = False
77 
78 __has_multiprocessing = True
79 try:
80  import multiprocessing
81 except ImportError:
82  print 'multiprocessing library not detected - subprocess library will not be available'
83  __has_multiprocessing = False
84 
85 __has_json = True
86 try:
87  import json
88 except ImportError:
89  print 'json library not detected - maus import will not be available'
90  __has_json = False
91 
92 pdg_pid_to_muon1 = {-13:'mu+'}
93 muon1_pid_to_pdg = {}
94 for k,v in pdg_pid_to_muon1.iteritems():
95  muon1_pid_to_pdg[v] = k
96 
97 icool_pid_to_pdg = {0:0, 1:-11, 2:-13, 3:211, 4:321, 5:2212, -1:11, -2:13, -3:-211, -4:-321, -5:-2212};
98 pdg_pid_to_icool = {};
99 for k,v in icool_pid_to_pdg.iteritems():
100  pdg_pid_to_icool[v] = k;
101 
102 #* MARS PARTICLE ID (JJ):
103 #* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
104 #* p n pi+ pi- K+ K- mu+ mu- g e- e+ pbar pi0 d t He3 He4 num nuam nue nuae
105 #* 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
106 #* K0L K0S K0 AK0 LAM ALAM SIG+ SIG0 SIG- nbar KSI0 KSI- OM- sib- si0b sib+ AKSI0 ksib+ omb+
107 
108 mars_pid_to_pdg = {1:2212, 2:2112, 3:211, 4:-211, 5:321, 6:-321, 7:-13, 8:13, 9:22, 10:11, 11:-11, 12:-2212, 13:111, 14:1000010020, 15:1000010030, 16:1000020030, 17:1000020040, 18:14, 19:-14, 20:12, 21:-12, 22:130, 23:310, 24:311, 25:-311,26:3122,27:-3122,28:0,29:0,30:0,31:-2112,32:0,33:0,34:0,35:0,36:0,38:0,39:0,40:0};
109 pdg_pid_to_mars = {}
110 for k,v in mars_pid_to_pdg.iteritems():
111  pdg_pid_to_mars[v] = k;
112 
113 pdg_pid_to_mass = {0:0, 11:0.510998910, 12:0., 13:105.6583668, 14:0., 22:0., 111:134.9766, 211:139.57018, 321:493.667, 2112:939.56536, 2212:938.271996, 1000010020:1876.1239, 1000010030:2809.432, 1000020030:2809.41346, 1000020040: 3728.4001, 130:497.614, 310:497.614, 311:497.614, 3122:1115.683}
114 pdg_pid_to_name = {0:'none', 11:'e-', 12:'electron neutrino', 13:'mu-', 14:'muon neutrino', 22:'photon', 111:'pi0', 211:'pi+', 321:'K+', 2112:'neutron', 2212:'proton', 1000010020:'deuterium', 1000010030:'tritium', 1000020030:'3He', 1000020040:'4He', 130:'K0L', 310:'K0S', 311:'K0', 3122:'lambda',
115 -11:'e+', -12:'electron antineutrino', -13:'mu+', -14:'muon antineutrino', -211:'pi-', -321:'K-', -2112:'antineutron', -2212:'antiproton', -3122:'antilambda'}
116 
117 pdg_pid_to_charge = {0:0, 11:-1, 12:0, 13:-1, 14:0, 22:0, 111:0, 211:+1, 321:+1, 2112:0, 2212:+1, 1000010020:0, 1000010030:0, 1000020030:0, 1000020040:0, 130:0, 310:0, 311:0, 3122:0, -11:+1, -12:0, -13:+1, -14:0, -211:-1, -321:-1, -2112:0, -2212:-1, -3122:0}
119 
120 constants = {'c_light':299.792458, 'pi':3.14159265,'echarge':1}
121 units = {'':1.,
122  'mum':1.e-3, 'mm':1., 'cm':10., 'm':1.e3, 'km':1.e6,
123  'ns':1., 'mus':1.e3, 'ms':1e6, 's':1.e9,
124  'eV':1e-6, 'keV':1e-3, 'MeV':1., 'GeV':1.e3, 'TeV':1e6,
125  'eV/c':1e-6, 'keV/c':1e-3, 'MeV/c':1., 'GeV/c':1.e3, 'TeV/c':1e6,
126  'eV/c2':1e-6, 'keV/c2':1e-3, 'MeV/c2':1., 'GeV/c2':1.e3, 'TeV/c2':1e6,
127  'Gauss':1.e-7, 'mT':1.e-6, 'T':1.e-3, 'kT':1.,
128  'V':1.e-6, 'kV':1.e-3, 'MV':1., 'GV':1.e3,
129  'kHz':1.e-6, 'MHz':1.e-3, 'GHz':1.,
130  'GV/m':1.,'GV/mm':1.e3,
131  'kW':6.24150974e6, 'MW':6.24150974e9, 'GW':6.24150974e12,
132  'degrees':2.*constants['pi']/360., 'radians':1., 'deg':2.*constants['pi']/360., 'rad':1., 'degree':2.*constants['pi']/360., 'radian':1.,
133  'echarge':1., 'Coulomb':6.24150974*10.**18.
134 }
135 
136 ### root globals
137 #line_color_int=1, line_style_int=1, line_width_int=2, fill_color_int=None, stats_bool=False, hist_title_string=''
138 class rg:
139  """
140  Container to hold some details of root global style information
141  """
142  canvas_border_mode = 0
143  canvas_highlight_color = 2
144  canvas_fill_color = 10
145  hist_fill_color = canvas_fill_color
146  line_width = 2
147  line_color = 1
148  line_style = 1
149  fill_color = 0
150  graph_fill_color = 10
151  stats = False
152  histo_margin = 0.0 #margin around edge of a histogram inside axes
153  graph_margin = 0.1 #margin around edge of a graph inside axes
155 ## privates
156 _canvas_persistent = []
157 _hist_persistent = []
158 _graph_persistent = []
159 _legend_persistent = []
161 ## pyplot globals
162 _figure_index = 1
163 
164 def substitute(file_name_in, file_name_out, switch_dict):
165  """
166  Read in file_name_in and write to file_name_out, replacing key with value in switch_dict. Must be a built in function somewhere to do same...
167 
168  - file_name_in = string name of the input file
169  - file_name_out = string name of the output file
170  - switch_dict = dict of values to be swapped to the values they will be swapped for
171 
172  e.g. Common.substitute('file.in', 'file.out', {'energy':'momentum'})
173  """
174  fin = open(file_name_in, 'r')
175  fout = open(file_name_out, 'w')
176  for line in fin:
177  for key, value in switch_dict.iteritems():
178  line = line.replace(str(key), str(value))
179  fout.write(line)
180  fin.close()
181  fout.close()
182 
183 wrapped_y_function = None
184 def __y_function_wrapper(x_list_of_lists):
185  global wrapped_y_function
186  out = []
187  for value in x_list_of_lists: out.append(wrapped_y_function(value))
188  return out
189 
190 def nd_newton_raphson1(y_function, y_tolerances_list, x_start_values_list, x_deltas_list, max_iteration=10, x_upper_limits=None, x_lower_limits = None, verbose=True):
191  """
192  Root finding in an arbitrary dimensional system. Returns x-value for y(x) = 0; caveat is dimension of y must equal dimension of x.
193  If you use this, you might find more and better root finding functions in SciPy module
194 
195  - y_function is a reference to the function to be minimised i.e. y(x); it takes a list of x-values; and returns a list of y-values
196  - y_tolerances_list is a list of tolerances; the iteration will stop when abs(value) < value_tolerance
197  _ x_start_values_list is a list of the values I will try to start with
198  - x_deltas_list is a list of the initial estimates of the error on x
199  - max_iteration is the maximum number of iterations allowed
200  - x_upper_limits is for future development
201  - x_lower_limits is for future development
202 
203  e.g. nd_newton_raphson(some_function, [0.1, 0.1], [0,0], [1,1]) will find root to y(x) < (0.1,0.1); starting at x=(0,0); initial error estimated to be [1,1].
204  some_function would be called like some_function([x_0, x_1]) and should return a list like [y_0,y_1]
205  """
206  global wrapped_y_function
207  wrapped_y_function = y_function
208  return nd_newton_raphson2( __y_function_wrapper, y_tolerances_list, x_start_values_list, x_deltas_list, max_iteration, x_upper_limits, x_lower_limits, verbose)
210 def nd_newton_raphson2(y_function, y_tolerances_list, x_start_values_list, x_deltas_list, max_iteration=10, x_upper_limits=None, x_lower_limits = None, verbose=True):
211  """
212  Alternative version of nd_newton_raphson1. Here y_function takes a list of lists of x_values, of length dimension+1 and returns a list of lists of y_values
213  Optimisation for when y_function can be made faster by running several jobs at once...
214 
215  - y_function is a reference to the function to be minimised i.e. y(x); it takes a list of lists of x-values; and returns a list of lists y-values
216  - y_tolerances_list is a list of tolerances; the iteration will stop when abs(value) < value_tolerance
217  - x_start_values_list is a list of the values I will try to start with
218  - x_deltas_list is a list of the initial estimates of the error on x
219  - max_iteration is the maximum number of iterations allowed
220  - x_upper_limits is for future development
221  - x_lower_limits is for future development
222 
223  e.g. nd_newton_raphson(some_function, [0.1, 0.1], [0,0], [1,1]) will find root to y(x) < (0.1,0.1); starting at x=(0,0); initial error estimated to be [1,1].
224  some_function would be called like some_function([[x_00, x_01] ,[x_10, x_11], [x_20, x_21]) and should return a list like [[y_00,y_01],[y_10,y_11],[y_20,y_21]]
225  """
226  has_numpy()
227  done = False
228  x_list = []
229  for key in range( len(x_start_values_list)+1 ):
230  x_list.append( copy.deepcopy(x_start_values_list) )
231  delta_x = numpy.matrix( str(x_deltas_list) )
232  count = copy.deepcopy(max_iteration)
233  limits = type(x_upper_limits) == type([]) and type(x_lower_limits) == type([])
234  if limits:
235  delta_limit = []
236  for i in range(len(x_start_values_list)): delta_limit.append(x_upper_limits[i]-x_lower_limits[i])
237  while not done and count > 0:
238  count -= 1
239  jacobian = numpy.matrix( numpy.zeros( (len(x_start_values_list), len(x_start_values_list)) ) )
240  for i in range( len(x_start_values_list) ):
241  if limits: #make the function periodic with the limits
242  x_list[0][i] -= delta_limit[i]*math.floor(x_list[0][i]/delta_limit[i]-x_lower_limits[i]);
243  for i in range( len(x_start_values_list) ):
244  x_list[i+1] = copy.deepcopy(x_list[0])
245  x_list[i+1][i] += delta_x[0,i]
246  y_x = y_function(x_list)
247  try:
248  y_x0 = numpy.matrix( str(y_x[0]) )
249  except:
250  raise RuntimeError('Newton-Raphson failed to evaluate function with input '+str(x_list)+' and output '+str(y_x))
251  # check that I move closer to the root... should stabilise the function I hope
252  y_magnitude_squared = numpy.vdot(y_x0, y_x0)
253  for i in range( len(x_start_values_list) ):
254  if abs(delta_x[0,i]) < float_tolerance:
255  print 'Warning - returned without convergence; delta_x',str(delta_x),'is below float tolerance'
256  return x_list[0]
257  for j in range( len(y_tolerances_list) ):
258  jacobian[j,i] = (y_x[i+1][j] - y_x[0][j])/delta_x[0,i]
259  x_list[i+1][i] -= delta_x[0,i]
260  try:
261  delta_x = -(linalg.inv(jacobian)*y_x0.transpose()).transpose()
262  except:
263  print 'Newton-Raphson failed with (singular?) jacobian\n',jacobian,'\nbailing out'
264  print 'x:',x_list[0],' y(x):',y_x[0],' dx:',delta_x
265  return x_list[0]
266  done = True
267  if verbose: print 'x:',x_list[0],' y(x):',y_x[0],' dx:',delta_x
268  for i in range( len(x_start_values_list) ):
269  if abs(y_x[0][i]) > y_tolerances_list[i]:
270  done = False
271  x_list[0][i] += delta_x[0,i]
272  y_x0 = y_function([x_list[0]])
273  return x_list[0]
274 
275 def has_multiprocessing():
276  """Raise an exception if multiprocessing libraries have not been imported properly"""
277  if not __has_multiprocessing:
278  raise ImportError("Attempt to use multiprocessing when library has not been imported - multiprocessing requires matplot >= 2.6")
279  return True
280 
281 def has_maus():
282  """Raise an exception if MAUS tracking library has not been imported properly"""
283  if(not __has_maus):
284  raise ImportError("Attempt to use maus when library has not been imported - check your maus installation")
285  return True
286 
287 def has_root():
288  """Raise an exception if ROOT graphics libraries have not been imported properly"""
289  if(not __has_root):
290  raise ImportError("Attempt to use root when library has not been imported - check your root installation")
291  return True
292 
293 def has_numpy():
294  """Raise an exception if NumPy numerical algebra libraries have not been imported properly"""
295  if(not __has_numpy):
296  raise ImportError("Attempt to use numpy when library has not been imported - check your numpy installation")
297  return True
298 
299 def has_matplot():
300  """Raise an exception if NumPy numerical algebra libraries have not been imported properly"""
301  if(not __has_matplot):
302  raise ImportError("Attempt to use matplotlib when library has not been imported - check your matplotlib installation")
303  return True
304 
305 def has_json():
306  """Raise an exception if json data libraries have not been imported properly"""
307  if(not __has_json):
308  raise ImportError("Attempt to use json when library has not been imported - check your json installation (python >= 2.6)")
309  return True
311 
312 def min_max(x_float_list, weight_list=[], margin = 0.1, xmin=None, xmax=None):
313  """
314  Return minimum and maximum of a list (i) discarding values with ~0 weight and (ii) adding a margin. For making histograms.
315 
316  - x_float_list = return minimum and maximum of this list of floats
317  - weight_list = ignore items in x_float_list if weight is 0. Ignored if weight_list is not same length as x_float_list
318  - margin = add a margin given by (x_max-x_min)*margin
319  - xmin = if set, will override the xmin value
320  - xmax = if set, will override the xmax value
321 
322  e.g. Common.min_max([0.1,0.2,0.3,0.4], [0,1,1,1], 0.2) will return [0.16,0.44]
323  """
324  new_floats = x_float_list
325  if len(weight_list) == len(x_float_list):
326  new_floats = []
327  for ind in range(len(weight_list)):
328  if weight_list[ind] > 1e-6:
329  new_floats.append(x_float_list[ind])
330  if len(new_floats) == 0: x = [0.,0.]
331  else: x = [min(new_floats), max(new_floats)]
332  delta = (x[1]-x[0])*margin
333  x[0] -= delta
334  x[1] += delta
335  if(x[1] - x[0] < 1e-9):
336  x[0] -= 1.
337  x[1] += 1.
338  if xmin!=None: x[0] = xmin
339  if xmax!=None: x[1] = xmax
340  return x
341 
342 def multisort(list_of_lists):
343  """Sort a list of lists by the first list"""
344  #first convert from vertical lists to horizontal lists
345  horizontal_list = []
346  for i in range( len(list_of_lists[0]) ):
347  horizontal_list.append([])
348  for j in range( len(list_of_lists) ):
349  horizontal_list[-1].append(list_of_lists[j][i])
350  #now sort by first element
351  getitem = operator.itemgetter(0)
352  horizontal_list = sorted(horizontal_list, key=getitem)
353  #now convert back to list_of_lists
354  for i in range( len(list_of_lists) ):
355  for j in range( len(list_of_lists[0]) ):
356  list_of_lists[i][j] = horizontal_list[j][i]
357  return list_of_lists
358 
359 def n_bins(n_points, nx_bins=None, ny_bins=None, nz_bins=None, n_dimensions=1):
360  """
361  Dynamically decide a number of bins depending on the number of points in the histogram
362 
363  - n_points = number of data points in the histogram
364  - nx_bins = set to an integer to override the automatic selection for number of x bins
365  - ny_bins = set to an integer to override the automatic selection for number of y bins
366  - n_dimensions = set to number of dimensions in the histogram
367 
368  Return value is a tuple (nx_bins, ny_bins, nz_bins), setting 0 to values that are out of the dimension range
369  """
370  out = [nx_bins, ny_bins, nz_bins]
371  num_events = float(n_points)
372  if nx_bins==None and n_dimensions==1: out[0] = int(num_events/10.)+1
373  if nx_bins==None and n_dimensions==2: out[0] = int(num_events**0.7/10.)+1
374  if ny_bins==None and n_dimensions==2: out[1] = int(num_events**0.7/10.)+1
375  if nx_bins==None and n_dimensions==3: out[0] = int(num_events**0.5/10.)+1
376  if ny_bins==None and n_dimensions==3: out[1] = int(num_events**0.5/10.)+1
377  if nz_bins==None and n_dimensions==3: out[2] = int(num_events**0.5/10.)+1
378  for i in range(3):
379  if out[i] == None: out[i]= 0 #set out to 0 for values that are out of the dimension range
380  return tuple(out)
381 
382 def histogram(x_values, x_bins, y_values=None, y_bins=None, weights=None):
383  """
384  Get a 1d or 2d list of bin weights from a set of data, weights and bin edges
385 
386  - x_values = list of x values to be binned
387  - x_bins = list of x bin edges
388  - y_values = list of y values to be binned. Set to None to make a 1d binning
389  - y_bins = list of y bin edges of same length as x_values
390  - weights = list of statistical weights of same length as x_values. Set to None to make all weights default to 1.
391 
392  Return value is a tuple of (bin_weights, x_bins, y_bins)
393  """
394  has_numpy()
395  is_1d = False
396  if weights == None:
397  weights = [1.]*len(x_values)
398  if y_values == None:
399  is_1d = True
400  y_values = [0.]*len(x_values)
401  y_bins = [-1.,1.]
402  contents = numpy.zeros((len(x_bins)-1, len(y_bins)-1), )
403  for i,x in enumerate(x_values):
404  p = bisect.bisect_right(x_bins,x_values[i])-1
405  q = bisect.bisect_right(y_bins,y_values[i])-1
406  if p>=0 and p<len(x_bins)-1 and q>=0 and q<len(y_bins)-1:
407  contents[p,q] += weights[i]
408  out = (contents, x_bins, y_bins)
409  if is_1d: return (contents, x_bins, [])
410  else: return (contents, x_bins, y_bins)
411 
412 def get_bin_edges(list_of_variables, number_of_bins, xmin=None, xmax=None):
413  """
414  Get a sorted list of equally spaced bin edges from a list of floats
415 
416  - list_of_variables = list of floats to be binned
417  - number_of_bins = number of bins to make; note that there will be number_of_bins+1 edges
418  - xmin = lower edge of all the bins (set to None to auto-detect)
419  - xmax = upper edge of all the bins (set to None to auto-detect)
420  """
421  my_bins = []
422  mm = min_max(list_of_variables, margin=0.0)
423  if xmin!=None: mm[0]=xmin
424  if xmax!=None: mm[1]=xmax
425  delta = (mm[1] - mm[0])/float(number_of_bins)
426  for i in range(number_of_bins+1):
427  my_bins.append(mm[0]+delta*i)
428  return my_bins
429 
430 def make_root_canvas(name_string, title_string=None, bg_color=rg.canvas_fill_color, highlight_color=rg.canvas_highlight_color,
431  border_mode=rg.canvas_border_mode, frame_fill_color=rg.hist_fill_color):
432  """
433  Make a root canvas with name canvas_name_string-<index> where <index> is a unique integer starting from 0
434 
435  - name_string = Name of the canvas. Due to a ROOT bug, xboa adds a unique identifier to the name_string to ensure the canvas is drawn.
436  - title_string = Title of the canvas (as displayed in canvas window title bar); if set to None will use the name_string.
437  - bg_color = Fill color of the canvas.
438  - highlight_color = When a canvas is selected, ROOT draws a border with a particular color to indicate that the canvas is selected.
439  - border_mode = When a canvas is selected, ROOT draws a border if border mode is not set to 0.
440  - frame_fill_color = Fill color of frames drawn on the canvas (e.g. histograms).
441  """
442  has_root()
443  canvas_name_string = name_string+"-"+str(len(_canvas_persistent))
444  if title_string == None: title_string = name_string
445  canvas = ROOT.TCanvas(canvas_name_string, title_string)
446  canvas.SetHighLightColor(highlight_color)
447  canvas.SetFillColor(bg_color)
448  canvas.SetBorderMode(border_mode)
449  canvas.SetFrameFillColor(frame_fill_color)
450  _canvas_persistent.append(canvas)
451  return canvas
452 
453 def make_root_histogram(name_string, x_float_list, x_axis_string, n_x_bins, y_float_list=[], y_axis_string='', n_y_bins=0, weight_list=[], xmin=None, xmax=None, ymin=None, ymax=None,
454  line_color=rg.line_color, line_style=rg.line_style, line_width=rg.line_width, fill_color=rg.fill_color, stats=rg.stats, hist_title_string=''):
455  """
456  Make a root histogram with data taken from float lists and axes named after the axis strings.
457 
458  - name_string = name given to the histogram
459  - x_float_list = list of x-data
460  - x_axis_string = string used to label the x-axis
461  - n_x_bins = number of bins in x direction
462  - y_float_list = list of y-data. If number of items in y list not equal to number in x list, will build 1d histogram
463  - y_axis_string = string used to label the y-axis
464  - n_y_bins = number of y bins
465  - weight_list = if present, each item will be filled with weight taken from this list
466  - xmin = float that overrides auto-detection of minimum x-axis value
467  - xmax = float that overrides auto-detection of maximum x-axis value
468  - ymin = float that overrides auto-detection of minimum y-axis value
469  - ymax = float that overrides auto-detection of maximum y-axis value
470  - line_color = int that sets the line colour of the histogram
471  - line_style = int that sets the line style of the histogram
472  - line_width = int that sets the line width of the histogram
473  - fill_color = int that sets the fill color of the histogram
474  - stats = set to True to plot a stats box on the histogram
475  - hist_title_string = specify the string that will appear as a title on the canvas
476 
477  Return value is the histogram
478  """
479  has_root()
480  name_string += " "+str(len(_hist_persistent))
481  if len(y_float_list) == len(x_float_list):
482  x_min_max = min_max(x_float_list, weight_list, margin=rg.histo_margin, xmin=xmin, xmax=xmax)
483  y_min_max = min_max(y_float_list, weight_list, margin=rg.histo_margin, xmin=ymin, xmax=ymax)
484  hist = ROOT.TH2D(name_string, hist_title_string+';'+x_axis_string+';'+y_axis_string, n_x_bins, x_min_max[0], x_min_max[1], n_y_bins, y_min_max[0], y_min_max[1])
485  if(len(weight_list) == len(x_float_list)):
486  for i in range( len(x_float_list) ):
487  hist.Fill(x_float_list[i], y_float_list[i], weight_list[i])
488  else:
489  for i in range( len(x_float_list) ):
490  hist.Fill(x_float_list[i], y_float_list[i])
491  else:
492  x_min_max = min_max(x_float_list, weight_list, margin=rg.histo_margin, xmin=xmin, xmax=xmax)
493  hist = ROOT.TH1D(name_string, hist_title_string+';'+x_axis_string, n_x_bins, x_min_max[0], x_min_max[1])
494  if(len(weight_list) == len(x_float_list)):
495  for i in range( len(x_float_list) ):
496  hist.Fill(x_float_list[i], weight_list[i])
497  else:
498  for i in range( len(x_float_list) ):
499  hist.Fill(x_float_list[i])
500  _hist_persistent.append(hist)
501  hist.SetLineColor(line_color)
502  hist.SetLineStyle(line_style)
503  if fill_color!=None: hist.SetFillColor(fill_color)
504  hist.SetStats(stats)
505  return hist
506 
507 def make_root_legend(canvas, root_item_list):
508  """
509  Build a legend for the canvas
510  """
511  has_root()
512  canvas.cd()
513  leg = ROOT.TLegend()
514  for index, leg in enumerate(root_item_list):
515  leg_min = 0.89-0.08*len(root_item_list)
516  if leg_min < 0.1:
517  leg_min = 0.1
518  leg = ROOT.TLegend(0.0, leg_min, 0.4, 0.90) # pylint: disable=E1101
519  leg.SetEntrySeparation(0.6)
520  for i, hist in enumerate(root_item_list):
521  leg.AddEntry(hist, root_item_list[i].GetName())
522  leg.SetFillColor(10)
523  leg.SetBorderSize(0)
524  leg.Draw()
525  canvas.Update()
526  _legend_persistent.append(leg)
527  return leg
528 
530 #def make_root_graph_2d(name_string, x_float_list, x_axis_string, y_float_list, y_axis_string, z_float_list, z_axis_string, sort=True, xmin=None, xmax=None, ymin=None, ymax=None, zmin=None, zmax=None,
531 # line_color=rg.line_color, line_style=rg.line_style, line_width=rg.line_width, fill_color=rg.graph_fill_color, hist_title_string=''):
532 # """
533 # Make a root graph with data taken from float lists and axes named after the axis strings. Return value is a tuple of (hist, graph)
534 # name_string = name given to the histogram
535 # x_float_list = list of x-data
536 # x_axis_string = string used to label the x-axis
537 # y_float_list = list of y-data
538 # y_axis_string = string used to label the y-axis
539 # z_float_list = list of z-data
540 # z_axis_string = string used to label the z-axis
541 # sort = boolean - set to true to automatically sort input data
542 # xmin = float that overrides auto-detection of minimum x-axis value
543 # xmax = float that overrides auto-detection of maximum x-axis value
544 # ymin = float that overrides auto-detection of minimum y-axis value
545 # ymax = float that overrides auto-detection of maximum y-axis value
546 # zmin = float that overrides auto-detection of minimum z-axis value
547 # zmax = float that overrides auto-detection of maximum z-axis value
548 # line_color = int that sets the line colour of the graph
549 # line_style = int that sets the line style of the graph
550 # line_width = int that sets the line width of the graph
551 # fill_color = graphs dont usually get a fill, but sometimes the fill colour turns up in e.g. legend drawing
552 # hist_title_string = specify the string that will appear as a title
553 # Return value is a tuple of (histogram, graph, graph_2) where graph is the TGraph2D and graph_2 is a graph of x vs y
554 # """
555 # has_root()
556 # if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list) or len(x_float_list) != len(z_float_list)):
557 # raise IndexError('Attempt to draw graph with no x-points, or different number of x to y to z points.')
558 # multilist = [x_float_list, y_float_list, z_float_list]
559 # if sort: multisort(multilist)
560 # x_min_max = min_max(multilist[0], margin=rg.graph_margin, xmin=xmin, xmax=xmax)
561 # y_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=ymin, xmax=ymax)
562 # z_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=zmin, xmax=zmax)
563 # hist = make_root_histogram(name_string, [], x_axis_string, 1000, [], y_axis_string, 1000, [], x_min_max[0], x_min_max[1], y_min_max[0], y_min_max[1],
564 # line_color=rg.line_color, line_style=rg.line_style, line_width=0, fill_color=None, stats=False, hist_title_string=hist_title_string)
565 # hist.SetMinimum(z_min_max[0])
566 # hist.SetMaximum(z_min_max[1])
567 # graph = ROOT.TGraph2D(len(x_float_list))
568 # graph.SetTitle(name_string)
569 # graph.SetName(name_string)
570 # graph.SetHistogram(hist)
571 # graph_2 = ROOT.TGraph(len(x_float_list))
572 # for i in range(len(x_float_list)):
573 # graph.SetPoint(i, x_float_list[i], y_float_list[i], z_float_list[i])
574 # graph_2.SetPoint(i, x_float_list[i], y_float_list[i])
575 # _graph_persistent.append(graph)
576 # _graph_persistent.append(graph_2)
577 # return (hist, graph, graph_2)
578 
579 
580 def make_matplot_histogram(x_float_list, x_axis_string, n_x_bins, y_float_list=[], y_axis_string='', n_y_bins=0, weight_list=[]):
581  """
582  Make a matplot graph with data taken from float lists and axes naemd after the axis strings. Return value is a tuple of (hist, graph)
583  matplot can format using tex expressions - use '$some math expression$' to include math text in your labels
584 
585  - x_float_list = list of x-data
586  - x_axis_string = string used to label the x-axis
587  - y_float_list = list of y-data
588  - y_axis_string = string used to label the y-axis
589 
590  After building the graph, use matplotlib.pyplot.show() to show something on the screen
591  """
592  has_matplot()
593  has_numpy()
594  global _figure_index
595  pyplot.figure(_figure_index)
596  _figure_index += 1
597  if(len(x_float_list) == 0):
598  raise IndexError('Attempt to draw histogram with no x-points')
599  if not len(y_float_list) == len(x_float_list):
600 # x_min_max = min_max(x_float_list, weight_list, margin=histo_margin)
601 # my_bins = range(int(n_x_bins))
602 # for i in range(len(my_bins)): my_bins[i] = x_min_max[0]+float(i)*(x_min_max[1]-x_min_max[0])/float(len(my_bins))
603  if(weight_list == []):
604  (n, my_bins) = numpy.histogram(a=x_float_list, bins=n_x_bins)
605  else:
606  (n, my_bins) = numpy.histogram(a=x_float_list, bins=n_x_bins, weights=weight_list)
607  new_bins = []
608  new_n = []
609  index = 0
610  while(abs(n[index])<1.e-9 and index < len(n)): index += 1
611  for i in range(index, len(n)-1):
612  new_bins.append((my_bins[i] + my_bins[i+1])/2.)
613  new_n.append(n[i])
614  hist = pylab.plot(new_bins, new_n)
615  pylab.xlabel(x_axis_string)
616  else:
617  if len(weight_list) == len(x_float_list): hist = pyplot.hexbin(x_float_list, y_float_list, weight_list, gridsize=(n_x_bins,n_y_bins))
618  else: hist = pyplot.hexbin(x_float_list, y_float_list, gridsize=(n_x_bins,n_y_bins))
619  pylab.xlabel(x_axis_string)
620  pylab.ylabel(y_axis_string)
621  return hist
622 
623 def make_root_graph(name_string, x_float_list, x_axis_string, y_float_list, y_axis_string, sort=True, xmin=None, xmax=None, ymin=None, ymax=None,
624  line_color=rg.line_color, line_style=rg.line_style, line_width=rg.line_width, fill_color=rg.graph_fill_color, hist_title_string=''):
625  """
626  Make a root graph with data taken from float lists and axes named after the axis strings. Return value is a tuple of (hist, graph)
627 
628  - name_string = name given to the histogram
629  - x_float_list = list of x-data
630  - x_axis_string = string used to label the x-axis
631  - y_float_list = list of y-data
632  - y_axis_string = string used to label the y-axis
633  - sort = boolean - set to true to automatically sort input data
634  - xmin = float that overrides auto-detection of minimum x-axis value
635  - xmax = float that overrides auto-detection of maximum x-axis value
636  - ymin = float that overrides auto-detection of minimum y-axis value
637  - ymax = float that overrides auto-detection of maximum y-axis value
638  - line_color = int that sets the line colour of the graph
639  - line_style = int that sets the line style of the graph
640  - line_width = int that sets the line width of the graph
641  - fill_color = graphs dont usually get a fill, but sometimes the fill colour turns up in e.g. legend drawing
642  - hist_title_string = specify the string that will appear as a title
643 
644  Return value is a tuple of (histogram, graph)
645  """
646  has_root()
647  x_float_list = copy.deepcopy(x_float_list)
648  y_float_list = copy.deepcopy(y_float_list)
649  if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list)):
650  raise IndexError('Attempt to draw graph with no x-points, or different number of x to y points')
651 # name_string += " "+str(len(_hist_persistent))
652  multilist = [x_float_list, y_float_list]
653  if sort: multisort(multilist)
654  x_min_max = min_max(multilist[0], margin=rg.graph_margin, xmin=xmin, xmax=xmax)
655  y_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=ymin, xmax=ymax)
656  hist = make_root_histogram(name_string, [], x_axis_string, 1000, [], y_axis_string, 1000, [], x_min_max[0], x_min_max[1], y_min_max[0], y_min_max[1],
657  line_color=rg.line_color, line_style=rg.line_style, line_width=0, fill_color=None, stats=False, hist_title_string=hist_title_string)
658  graph = ROOT.TGraph(len(x_float_list))
659  graph.SetTitle(name_string)
660  graph.SetName(name_string)
661  graph.SetLineColor(line_color)
662  graph.SetLineStyle(line_style)
663  graph.SetLineWidth(line_width)
664  graph.SetFillColor(fill_color)
665  for i in range(len(x_float_list)):
666  graph.SetPoint(i, x_float_list[i], y_float_list[i])
667  _graph_persistent.append(graph)
668  return (hist, graph)
669 
670 def make_matplot_graph(x_float_list, x_axis_string, y_float_list, y_axis_string, sort=True):
671  """
672  Make a matplot graph with data taken from float lists and axes naemd after the axis strings. Return value is a tuple of (hist, graph)
673  matplot can format using tex expressions - use '$some math expression$' to include math text in your labels
674 
675  - x_float_list = list of x-data
676  - x_axis_string = string used to label the x-axis
677  - y_float_list = list of y-data
678  - y_axis_string = string used to label the y-axis
679  - sort = boolean - set to true to automatically sort input data
680 
681  After building the graph, use matplotlib.pyplot.show() to show something on the screen
682  """
683  has_matplot()
684  global _figure_index
685  pyplot.figure(_figure_index)
686  _figure_index += 1
687  if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list)):
688  raise IndexError('Attempt to draw graph with no x-points, or different number of x to y points')
689  multilist = [x_float_list, y_float_list]
690  multisort(multilist)
691  x_min_max = min_max(multilist[0], margin=rg.graph_margin)
692  y_min_max = min_max(multilist[1], margin=rg.graph_margin)
693  myplot = pylab.plot(x_float_list, y_float_list)
694  pylab.xlabel(x_axis_string)
695  pylab.ylabel(y_axis_string)
696  pylab.xlim (x_min_max[0], x_min_max[1])
697  pylab.ylim (y_min_max[0], y_min_max[1])
698  matplotlib.pyplot.draw()
699 
700 def make_root_multigraph(name_string, x_float_list_of_lists, x_axis_string, y_float_list_of_lists, y_axis_string):
701  """
702  Print several different graphs on the same canvas. Some default colour scheme is applied, but it may not be the best...
703 
704  - name_string = name that will be given to the axes (histogram)
705  - x_float_list_of_lists = list of lists. Each list will be used as the x-axis for a graph
706  - x_axis_string = string that will be used to label the x_axis
707  - y_float_list_of_lists = list of lists. Each list will be used as the y-axis for a graph
708  - y_axis_string = string that will be used to label the y_axis
709 
710  E.g. Common.make_root_multigraph('example', [[1.,2.,3.,4.], [1.,4.,9.,16.]], 'x', [[1.,2.,3.,4.],[1.,2.,3.,4.]], 'f(x)') will make a graph of f = x and f = x^0.5
711  """
712  has_root()
713  name_string += " "+str(len(_hist_persistent))
714  total_x_list = []
715  total_y_list = []
716  for a_list in x_float_list_of_lists: total_x_list += a_list
717  for a_list in y_float_list_of_lists: total_y_list += a_list
718  x_min_max = min_max(total_x_list, margin=rg.graph_margin)
719  y_min_max = min_max(total_y_list, margin=rg.graph_margin)
720  hist = ROOT.TH2D(name_string, ';'+x_axis_string+';'+y_axis_string, 1000, x_min_max[0], x_min_max[1], 1000, y_min_max[0], y_min_max[1])
721  graphs = []
722  for index in range( len(x_float_list_of_lists) ):
723  graphs.append(ROOT.TGraph( len(x_float_list_of_lists[index]) ))
724  ROOT.gStyle.SetPalette(int(len(x_float_list_of_lists)*1.25))
725  graphs[-1].SetLineColor(ROOT.gStyle.GetColorPalette(index + int(len(x_float_list_of_lists)*0.25) )) #funny algebra to darken the colors slightly
726  for i in range(len(x_float_list_of_lists[index])):
727  graphs[-1].SetPoint(i, x_float_list_of_lists[index][i], y_float_list_of_lists[index][i])
728  _graph_persistent.append(graphs[-1])
729  _hist_persistent.append(hist)
730  hist.SetStats(False)
731  return (hist, graphs)
732 
733 def make_matplot_multigraph(x_float_list_of_lists, x_axis_string, y_float_list_of_lists, y_axis_string):
734  """
735  Print several different graphs on the same axes. Some default colour scheme is applied, but it may not be the best...
736 
737  - x_float_list_of_lists = list of lists. Each list will be used as the x-axis for a graph
738  - x_axis_string = string that will be used to label the x_axis
739  - y_float_list_of_lists = list of lists. Each list will be used as the y-axis for a graph
740  - y_axis_string = string that will be used to label the y_axis
741 
742  E.g. Common.make_matplot_multigraph('example', [[1.,2.,3.,4.], [1.,4.,9.,16.]], 'x', [[1.,2.,3.,4.],[1.,2.,3.,4.]], 'f(x)') will make a graph of f = x and f = x^0.5
743  """
744  has_matplot()
745  global _figure_index
746  pyplot.figure(_figure_index)
747  _figure_index += 1
748  total_x_list = []
749  total_y_list = []
750  for a_list in x_float_list_of_lists: total_x_list += a_list
751  for a_list in y_float_list_of_lists: total_y_list += a_list
752  x_min_max = min_max(total_x_list, margin=rg.graph_margin)
753  y_min_max = min_max(total_y_list, margin=rg.graph_margin)
754  for index in range( len(x_float_list_of_lists) ):
755  multilist = [x_float_list_of_lists[index], y_float_list_of_lists[index]]
756  multisort(multilist)
757  myplot = pylab.plot(multilist[0], multilist[1])
758  pylab.xlabel(x_axis_string)
759  pylab.ylabel(y_axis_string)
760  pylab.xlim (x_min_max[0], x_min_max[1])
761  pylab.ylim (y_min_max[0], y_min_max[1])
762  matplotlib.pyplot.draw()
763 
764 def make_matplot_scatter(x_float_list, x_axis_string, y_float_list, y_axis_string):
765  """
766  Make a matplot scatter graph with data taken from float lists and axes naemd after the axis strings.
767  matplot can format using tex expressions - use '$some math expression$' to include math text in your labels
768 
769  - x_float_list = list of x-data
770  - x_axis_string = string used to label the x-axis
771  - y_float_list = list of y-data
772  - y_axis_string = string used to label the y-axis
773 
774  After building the graph, use matplotlib.pyplot.show() to show something on the screen
775  """
776  has_matplot()
777  global _figure_index
778  pyplot.figure(_figure_index)
779  _figure_index += 1
780  if(len(x_float_list) == 0):
781  raise IndexError('Attempt to draw histogram with no x-points')
782  hist = pyplot.scatter(x_float_list, y_float_list, s=1)
783  pylab.xlabel(x_axis_string)
784  pylab.ylabel(x_axis_string)
785  return hist
786 
787 def wait_for_root():
788  """
789  Force python to halt processing until ROOT windows are closed
790  """
791  print 'Close ROOT windows to continue'
792  root_done = False
793  while not root_done:
794  root_done = True
795  for canvas in _canvas_persistent:
796  if not canvas == None:
797  root_done = False
798 
799 def clear_root():
800  """
801  Close root plots (and free memory)
802  """
803  for canv in _canvas_persistent: del(canv)
804  for hist in _hist_persistent: del(hist)
805  for graph in _graph_persistent: del(graph)
806 
807 def wait_for_matplot():
808  """Show any plots made using matplotlib on the screen"""
809  has_matplot()
810  matplotlib.pyplot.show()
811 
813  """Show matplotlib plots and return to the script"""
814  has_matplot()
815  try:
816  subprocess( matplotlib.pyplot.show, () )
817  except:
818  raise ImportError('matplot_show_and_continue is only available is (i) ROOT is not installed or (ii) you have python > 2.6')
819 
820 
821 def make_doc(source_folder=os.getcwd(), target_folder=os.getcwd(), test=True):
822  import xboa.Hit
823  import xboa.Bunch
824  import xboa.Common
825  import pydoc
826  import shutil
827  file_list = ['Hit.html','Common.html','Bunch.html']
828  for docfile in file_list:
829  try: os.remove(source_folder+'/xboa.'+docfile)
830  except: pass
831  pydoc.writedoc(xboa.Hit)
832  pydoc.writedoc(xboa.Common)
833  pydoc.writedoc(xboa.Bunch)
834  for docfile in file_list:
835  try: os.remove(target_folder+'/doc/'+docfile)
836  except: pass
837  shutil.move(source_folder+'/xboa.'+docfile, target_folder+'/doc/'+docfile)
838 
839 def make_dist_preliminaries(source_folder=os.getcwd(), target_folder=os.getcwd(), test=True):
840  """
841  Preliminaries to making a distribution:
842  - run test scripts
843  - generate documentation
844  """
845  import Boa_Test
846  if test==True:
847  (passes, fails, warns) = Boa_Test.test_all()
848  print 'TEST RESULTS:\nPasses:',passes,'\nFails:',fails,'\nWarns:',warns,'\n'
849  if fails > 0: raise Exception('Some tests failed - aborting release')
851 
852 __mp_subprocesses = []
853 __fk_subprocesses = []
854 def subprocess(function, args):
855  """
856  Make a function call in a subprocess; return the subprocess pid
857  - This uses the multiprocessing libary in the first instance - if multiprocessing is not available
858  the function tries to use os.fork(); however, os.fork is not compatible with ROOT. If
859  - multiprocessing is not available and ROOT is installed, this routine will throw an ImportError
860  """
861  try:
863  __mp_subprocesses.append( multiprocessing.Process(target=function, args=args) )
864  __mp_subprocesses[-1].start()
865  return __mp_subprocesses[-1]
866  except:
867  try:
868  has_root()
869  raise ImportError('Error - attempt to make a subprocess using fork when ROOT library is active')
870  except ImportError:
871  pid = os.fork()
872  if pid == 0:
873  try: function(*args)
874  except: os._exit(1)
875  os._exit(0)
876  else:
877  __fk_subprocesses.append(pid)
878  return pid
879 
881  """
882  Kill all subprocesses generated by Common.subprocess call; automatically called at exit unless kill_subprocesses_at_exit is set to False
883  Note that this makes a call to the os, which may take a bit of time to respond.
884  """
885  for ps in __mp_subprocesses:
886  ps.terminate()
887  time.sleep(0.01) #dont ask me why this is necessary...
888  for pid in __fk_subprocesses:
889  os.kill(pid, signal.SIGKILL)
890 
891 def kolmogorov_smirnov_test(list_1, list_2):
892  """
893  Convenience wrapper for ROOT kolmogorov-smirnov test.
894  - list_1 = list of floats that are sampled from some parent probability distribution
895  - list_2 = list of floats that are sampled from other some parent distribution
896  Returns double between 0. and 1. giving probability that list_1 and list_2 have the same parent distribution.
897  """
898  list_1 = sorted(list_1)
899  list_2 = sorted(list_2)
900  c_array_1 = ctypes.c_double*len(list_1)
901  c_array_2 = ctypes.c_double*len(list_2)
902  ca1 = c_array_1()
903  ca2 = c_array_2()
904  for i in range(len(list_1)): ca1[i]=ctypes.c_double(list_1[i])
905  for i in range(len(list_2)): ca2[i]=ctypes.c_double(list_2[i])
906  ks = ROOT.TMath.KolmogorovTest(len(list_1), ca1, len(list_2), ca2, '')
907  return ks
908 
909 def __atexit():
910  """Calls some functions automatically at exit"""
911  if(kill_subprocesses_at_exit):
913 atexit.register(__atexit)
914 
915 
916 def make_grid(n_dimensions, n_per_dimension):
917  """
918  Make a rectangular n_dimensional grid of points evenly spaced between [-1,1] in each
919  dimension
920 
921  - n_dimensions = number of dimensions
922  - n_per_dimension = number of points in each dimension; total number of points will be
923  n_per_dimension^n_dimensions
924 
925  Return value is a list of numpy.matrices with shape (n_dimensions, 1)
926  """
927  has_numpy()
928  pos_vector = [numpy.matrix( [-1.]*n_dimensions )]
929  for i in range(n_dimensions):
930  pos_vector_copy = copy.deepcopy(pos_vector)
931  for j in range(1, n_per_dimension):
932  pos = (2.*j)/float(n_per_dimension-1)-1.
933  for vec in pos_vector_copy:
934  vec = copy.deepcopy(vec)
935  vec[0,i] = pos
936  pos_vector.append(vec)
937  return pos_vector
938 
939 def make_shell(n_per_dimension, ellipse):
940  """
941  Make a shell of points that sit on a hyper-ellipsoid defined by ellipse matrix
942 
943  - n_per_dimension = number of points in each dimension; total number of points will be
944  n_per_dimension^n_dimensions if n_per_dimension is even or
945  n_per_dimension^n_dimensions-1 if n_per_dimension is odd
946  - ellipse = matrix that defines the ellipse on which vector sits; should be a
947  numpy.matrix with shape (vec_length, vec_length)
948 
949  Points are defined on a (hyper-)cuboidal grid and then compressed so that lengths are
950  all 1. Doesn't necessarily mean points are evenly spaced. Return value is a list of
951  numpy.matrices with shape (n_dimensions, 1)
952  """
953  has_numpy()
954  n_dimensions = numpy.shape(ellipse)[0]
955  grid = make_grid(n_dimensions, n_per_dimension)
956  shell = []
957  ellipse_inv = numpy.linalg.inv(ellipse)
958  for vector in grid:
959  if numpy.vdot(vector,vector) > float_tolerance:
960  shell.append(normalise_vector(vector, ellipse_inv))
961  return shell
962 
963 def normalise_vector(vector, matrix_inverse):
964  """
965  Normalise vector so that vector.T() * matrix.inverse * vector = 1
966 
967  - vector = the vector to normalise; should be a numpy.matrix with shape (vec_length,1)
968  - matrix_inverse = inverse of matrix that defines the ellipse on which vector sits; should be a
969  numpy.matrix with shape (vec_length, vec_length)
970 
971  Return value is a list of numpy.matrices with shape (vec_length,1)
972  """
973  has_numpy()
974  scale = 1./(vector * matrix_inverse * vector.transpose())[0,0]**0.5
975  if scale != scale: raise(ValueError("Bad input - vector or matrix magnitude 0")) #isnan and isinf not available in 2.5
976  vector = vector*scale
977  return vector
978 
979 def __function_with_queue(args):
980  """
981  Wrapper function to put multiprocessing output into a queue
982 
983  - args tuple of (function_call, queue, function_arg) where
984  - function_call is a reference to the function that we want to wrap\n
985  - queue is the queue that will hold return values from the function\n
986  - function_arg is the argument to function_call\n
987  - index is an index indicating function_arg's position in the inputs
989  tuple of (index, Output) is placed into queue; if function_call throws an
990  exception, the exception is placed on the queue instead
991  """
992  (function_call, queue, function_arg, index) = args
993  try:
994  queue.put((index, function_call(*function_arg)))
995  except:
996  queue.put((index, sys.exc_info()[1]))
997 
998 def process_list(function_call, list_of_args, max_n_processes):
999  """
1000  Run multiprocessing on a list of arguments
1001 
1002  - function_call multiprocess this function call
1003  - list_of_args list of tuples of arguments for function_call
1004  - max_n_processes maximum number of concurrent processes to use
1005 
1006  Returns list of return values, one for each function call. List is always
1007  sorted into same order as input.
1008 
1009  e.g. process_list(time.sleep, [(3, ), (6, ), (2, )], 2) will multiprocess
1010  the time.sleep function with inputs 3, 6 and 2 across 2 cores and return
1011  list like [None, None, None].
1012  """
1013  manager = multiprocessing.Manager()
1014  queue = manager.Queue() # queue stores return values
1015  pool = multiprocessing.Pool(max_n_processes) # pool runs multiprocess
1016  # each process needs:
1017  # reference to queue to put returns values in
1018  # index (to sort return values)
1019  # plus function call and argument
1020  new_list_of_args = [(function_call, queue, x, i) for i,x in enumerate(list_of_args)]
1021  # run the processes
1022  pool.map(__function_with_queue, new_list_of_args)
1023  # sort output into a list
1024  out_list = []
1025  while not queue.empty():
1026  out_list.append(queue.get())
1027  out_list.sort()
1028  for i,item in enumerate(out_list):
1029  out_list[i] = item[1]
1030  # cleanup
1031  pool.close()
1032  return out_list
1033 
1034 def common_overview_doc(verbose = False):
1035  """Creates some summary documentation for the Common module. If verbose is True then will also print any functions or data not included in summary"""
1036  common_doc = '\nCommon module contains a number of useful interfaces, defaults, data and ancillary functions that support the rest of the XBOA package.\n'
1037 
1038  name_list = ['math', 'root', 'matplot', 'data', 'defaults', 'other_stuff']
1039  function_list = {
1040  'math' : ['min_max', 'multisort', 'nd_newton_raphson1', 'nd_newton_raphson2'],
1041  'root' : ['has_root','make_root_graph', 'make_root_histogram', 'make_root_multigraph', 'clear_root', 'wait_for_root', 'make_root_canvas'],
1042  'matplot' : ['has_matplot', 'make_matplot_graph', 'make_matplot_histogram', 'make_matplot_multigraph', 'make_matplot_scatter', 'wait_for_matplot', 'matplot_show_and_continue'],
1043  'other_stuff' : ['get_bin_edges', 'histogram', 'substitute', 'has_numpy', 'build_installation', 'make_grid', 'make_shell', 'normalise_vector', 'kolmogorov_smirnov_test', 'kill_all_subprocesses', 'has_multiprocessing', 'has_json'],
1044  'data' : ['constants', 'pdg_pid_to_icool', 'pdg_pid_to_mars', 'pdg_pid_to_mass', 'pdg_pid_to_name', 'pdg_pid_to_charge', 'icool_pid_to_pdg', 'mars_pid_to_pdg', 'units'],
1045  'defaults' : ['canvas_fill_color', 'canvas_highlight_color', 'default_margin', 'float_tolerance', 'graph_margin', 'histo_margin', 'python_version', 'xboa_version', 'kill_subprocesses_at_exit']
1046  }
1047 
1048  function_doc = {
1049  'math' : 'Maths functions:',
1050  'root' : 'Interfaces to ROOT plotting library:',
1051  'matplot' : 'Interfaces to matplotlib plotting library:',
1052  'other_stuff' : 'Some other useful functions:',
1053  'data' : 'Physics data:',
1054  'defaults' : 'Defaults for e.g. root canvases, etc:',
1055  }
1056 
1057  import Common
1058  dir_common = dir(Common)
1059  if verbose:
1060  print 'The following functions and data are in Common but not in common_overview_doc:'
1061  for func in dir_common:
1062  found = False
1063  for func_sublist in function_list.values():
1064  if func in func_sublist: found = True
1065  if not found: print func,
1066  print '\n'
1067 
1068  print 'The following functions and data are in common_overview_doc but not in Common:'
1069  for func_sublist in function_list.values():
1070  for func in func_sublist:
1071  if func not in dir_common:
1072  print func,
1073  print
1074 
1075  doc = common_doc
1076  for key in name_list:
1077  doc = doc + function_doc[key]+'\n'
1078  for item in function_list[key]:
1079  doc = doc+' '+item+'\n'
1080  return doc
1081 
1082 __doc__ = common_overview_doc()
1083 
1084