#This file is a part of xboa # #xboa is free software: you can redistribute it and/or modify #it under the terms of the GNU General Public License as published by #the Free Software Foundation, either version 3 of the License, or #(at your option) any later version. # #xboa is distributed in the hope that it will be useful, #but WITHOUT ANY WARRANTY; without even the implied warranty of #MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #GNU General Public License for more details. # #You should have received a copy of the GNU General Public License #along with xboa in the doc folder. If not, see #. # """ Common contains some global physics and other parameters, plus some useful functions - icool_pid_to_pdg = dict from icool particle identification indices to pdg indices - pdg_pid_to_icool = inverse of above - constants = string to number dict of physical constants - units = string to number dict of some units Functions documented below """ import operator import copy import bisect import sys import string import math import os import pydoc import bisect import atexit import time import signal import ctypes xboa_version = '0.15.2' float_tolerance = 1.e-9 kill_subprocesses_at_exit = True __has_maus = False try: import libMausCpp __has_maus = True except: print 'Maus library not detected' __has_root = True try: import ROOT ROOT.gStyle.SetPalette(1) except ImportError: print 'PyRoot not detected - ROOT graphing functions will not be available' __has_root = False __has_numpy = True try: import numpy from numpy import linalg from numpy import matrix except ImportError: print 'NumPy not detected - many analysis functions will not be available' __has_numpy = False __has_matplot = True try: import matplotlib import pylab from matplotlib import pyplot except ImportError: print 'MatPlotLib not detected - matplotlib graphing functions will not be available' __has_matplot = False __has_multiprocessing = True try: import multiprocessing except ImportError: print 'multiprocessing library not detected - subprocess library will not be available' __has_multiprocessing = False __has_json = True try: import json except ImportError: print 'json library not detected - maus import will not be available' __has_json = False pdg_pid_to_muon1 = {-13:'mu+'} muon1_pid_to_pdg = {} for k,v in pdg_pid_to_muon1.iteritems(): muon1_pid_to_pdg[v] = k 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}; pdg_pid_to_icool = {}; for k,v in icool_pid_to_pdg.iteritems(): pdg_pid_to_icool[v] = k; #* MARS PARTICLE ID (JJ): #* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #* p n pi+ pi- K+ K- mu+ mu- g e- e+ pbar pi0 d t He3 He4 num nuam nue nuae #* 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #* K0L K0S K0 AK0 LAM ALAM SIG+ SIG0 SIG- nbar KSI0 KSI- OM- sib- si0b sib+ AKSI0 ksib+ omb+ 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}; pdg_pid_to_mars = {} for k,v in mars_pid_to_pdg.iteritems(): pdg_pid_to_mars[v] = k; 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} 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', -11:'e+', -12:'electron antineutrino', -13:'mu+', -14:'muon antineutrino', -211:'pi-', -321:'K-', -2112:'antineutron', -2212:'antiproton', -3122:'antilambda'} 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} constants = {'c_light':299.792458, 'pi':3.14159265,'echarge':1} units = {'':1., 'mum':1.e-3, 'mm':1., 'cm':10., 'm':1.e3, 'km':1.e6, 'ns':1., 'mus':1.e3, 'ms':1e6, 's':1.e9, 'eV':1e-6, 'keV':1e-3, 'MeV':1., 'GeV':1.e3, 'TeV':1e6, 'eV/c':1e-6, 'keV/c':1e-3, 'MeV/c':1., 'GeV/c':1.e3, 'TeV/c':1e6, 'eV/c2':1e-6, 'keV/c2':1e-3, 'MeV/c2':1., 'GeV/c2':1.e3, 'TeV/c2':1e6, 'Gauss':1.e-7, 'mT':1.e-6, 'T':1.e-3, 'kT':1., 'V':1.e-6, 'kV':1.e-3, 'MV':1., 'GV':1.e3, 'kHz':1.e-6, 'MHz':1.e-3, 'GHz':1., 'GV/m':1.,'GV/mm':1.e3, 'kW':6.24150974e6, 'MW':6.24150974e9, 'GW':6.24150974e12, 'degrees':2.*constants['pi']/360., 'radians':1., 'deg':2.*constants['pi']/360., 'rad':1., 'degree':2.*constants['pi']/360., 'radian':1., 'echarge':1., 'Coulomb':6.24150974*10.**18. } ### root globals #line_color_int=1, line_style_int=1, line_width_int=2, fill_color_int=None, stats_bool=False, hist_title_string='' class rg: """ Container to hold some details of root global style information """ canvas_border_mode = 0 canvas_highlight_color = 2 canvas_fill_color = 10 hist_fill_color = canvas_fill_color line_width = 2 line_color = 1 line_style = 1 fill_color = 0 graph_fill_color = 10 stats = False histo_margin = 0.0 #margin around edge of a histogram inside axes graph_margin = 0.1 #margin around edge of a graph inside axes fit_color = 4 fit_style = 2 ## privates _canvas_persistent = [] _hist_persistent = [] _graph_persistent = [] _legend_persistent = [] _function_persistent = [] ## pyplot globals _figure_index = 1 def substitute(file_name_in, file_name_out, switch_dict): """ 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... - file_name_in = string name of the input file - file_name_out = string name of the output file - switch_dict = dict of values to be swapped to the values they will be swapped for e.g. Common.substitute('file.in', 'file.out', {'energy':'momentum'}) """ fin = open(file_name_in, 'r') fout = open(file_name_out, 'w') for line in fin: for key, value in switch_dict.iteritems(): line = line.replace(str(key), str(value)) fout.write(line) fin.close() fout.close() wrapped_y_function = None def __y_function_wrapper(x_list_of_lists): global wrapped_y_function out = [] for value in x_list_of_lists: out.append(wrapped_y_function(value)) return out 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): """ Root finding in an arbitrary dimensional system. Returns x-value for y(x) = 0; caveat is dimension of y must equal dimension of x. If you use this, you might find more and better root finding functions in SciPy module - 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 - y_tolerances_list is a list of tolerances; the iteration will stop when abs(value) < value_tolerance _ x_start_values_list is a list of the values I will try to start with - x_deltas_list is a list of the initial estimates of the error on x - max_iteration is the maximum number of iterations allowed - x_upper_limits is for future development - x_lower_limits is for future development 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]. some_function would be called like some_function([x_0, x_1]) and should return a list like [y_0,y_1] """ global wrapped_y_function wrapped_y_function = y_function 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) 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): """ 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 Optimisation for when y_function can be made faster by running several jobs at once... - 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 - y_tolerances_list is a list of tolerances; the iteration will stop when abs(value) < value_tolerance - x_start_values_list is a list of the values I will try to start with - x_deltas_list is a list of the initial estimates of the error on x - max_iteration is the maximum number of iterations allowed - x_upper_limits is for future development - x_lower_limits is for future development 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]. 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]] """ has_numpy() done = False x_list = [] for key in range( len(x_start_values_list)+1 ): x_list.append( copy.deepcopy(x_start_values_list) ) delta_x = numpy.matrix( str(x_deltas_list) ) count = copy.deepcopy(max_iteration) limits = type(x_upper_limits) == type([]) and type(x_lower_limits) == type([]) if limits: delta_limit = [] for i in range(len(x_start_values_list)): delta_limit.append(x_upper_limits[i]-x_lower_limits[i]) while not done and count > 0: count -= 1 jacobian = numpy.matrix( numpy.zeros( (len(x_start_values_list), len(x_start_values_list)) ) ) for i in range( len(x_start_values_list) ): if limits: #make the function periodic with the limits x_list[0][i] -= delta_limit[i]*math.floor(x_list[0][i]/delta_limit[i]-x_lower_limits[i]); for i in range( len(x_start_values_list) ): x_list[i+1] = copy.deepcopy(x_list[0]) x_list[i+1][i] += delta_x[0,i] y_x = y_function(x_list) try: y_x0 = numpy.matrix( str(y_x[0]) ) except: raise RuntimeError('Newton-Raphson failed to evaluate function with input '+str(x_list)+' and output '+str(y_x)) # check that I move closer to the root... should stabilise the function I hope y_magnitude_squared = numpy.vdot(y_x0, y_x0) for i in range( len(x_start_values_list) ): if abs(delta_x[0,i]) < float_tolerance: print 'Warning - returned without convergence; delta_x',str(delta_x),'is below float tolerance' return x_list[0] for j in range( len(y_tolerances_list) ): jacobian[j,i] = (y_x[i+1][j] - y_x[0][j])/delta_x[0,i] x_list[i+1][i] -= delta_x[0,i] try: delta_x = -(linalg.inv(jacobian)*y_x0.transpose()).transpose() except: print 'Newton-Raphson failed with (singular?) jacobian\n',jacobian,'\nbailing out' print 'x:',x_list[0],' y(x):',y_x[0],' dx:',delta_x return x_list[0] done = True if verbose: print 'x:',x_list[0],' y(x):',y_x[0],' dx:',delta_x for i in range( len(x_start_values_list) ): if abs(y_x[0][i]) > y_tolerances_list[i]: done = False x_list[0][i] += delta_x[0,i] y_x0 = y_function([x_list[0]]) return x_list[0] def has_multiprocessing(): """Raise an exception if multiprocessing libraries have not been imported properly""" if not __has_multiprocessing: raise ImportError("Attempt to use multiprocessing when library has not been imported - multiprocessing requires matplot >= 2.6") return True def has_maus(): """Raise an exception if MAUS tracking library has not been imported properly""" if(not __has_maus): raise ImportError("Attempt to use maus when library has not been imported - check your maus installation") return True def has_root(): """Raise an exception if ROOT graphics libraries have not been imported properly""" if(not __has_root): raise ImportError("Attempt to use root when library has not been imported - check your root installation") return True def has_numpy(): """Raise an exception if NumPy numerical algebra libraries have not been imported properly""" if(not __has_numpy): raise ImportError("Attempt to use numpy when library has not been imported - check your numpy installation") return True def has_matplot(): """Raise an exception if NumPy numerical algebra libraries have not been imported properly""" if(not __has_matplot): raise ImportError("Attempt to use matplotlib when library has not been imported - check your matplotlib installation") return True def has_json(): """Raise an exception if json data libraries have not been imported properly""" if(not __has_json): raise ImportError("Attempt to use json when library has not been imported - check your json installation (python >= 2.6)") return True def min_max(x_float_list, weight_list=[], margin = 0.1, xmin=None, xmax=None): """ Return minimum and maximum of a list (i) discarding values with ~0 weight and (ii) adding a margin. For making histograms. - x_float_list = return minimum and maximum of this list of floats - weight_list = ignore items in x_float_list if weight is 0. Ignored if weight_list is not same length as x_float_list - margin = add a margin given by (x_max-x_min)*margin - xmin = if set, will override the xmin value - xmax = if set, will override the xmax value 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] """ new_floats = x_float_list if len(weight_list) == len(x_float_list): new_floats = [] for ind in range(len(weight_list)): if weight_list[ind] > 1e-6: new_floats.append(x_float_list[ind]) if len(new_floats) == 0: x = [0.,0.] else: x = [min(new_floats), max(new_floats)] delta = (x[1]-x[0])*margin x[0] -= delta x[1] += delta if(x[1] - x[0] < 1e-9): x[0] -= 1. x[1] += 1. if xmin!=None: x[0] = xmin if xmax!=None: x[1] = xmax return x def multisort(list_of_lists): """Sort a list of lists by the first list""" #first convert from vertical lists to horizontal lists horizontal_list = [] for i in range( len(list_of_lists[0]) ): horizontal_list.append([]) for j in range( len(list_of_lists) ): horizontal_list[-1].append(list_of_lists[j][i]) #now sort by first element getitem = operator.itemgetter(0) horizontal_list = sorted(horizontal_list, key=getitem) #now convert back to list_of_lists for i in range( len(list_of_lists) ): for j in range( len(list_of_lists[0]) ): list_of_lists[i][j] = horizontal_list[j][i] return list_of_lists def n_bins(n_points, nx_bins=None, ny_bins=None, nz_bins=None, n_dimensions=1): """ Dynamically decide a number of bins depending on the number of points in the histogram - n_points = number of data points in the histogram - nx_bins = set to an integer to override the automatic selection for number of x bins - ny_bins = set to an integer to override the automatic selection for number of y bins - n_dimensions = set to number of dimensions in the histogram Return value is a tuple (nx_bins, ny_bins, nz_bins), setting 0 to values that are out of the dimension range """ out = [nx_bins, ny_bins, nz_bins] num_events = float(n_points) if nx_bins==None and n_dimensions==1: out[0] = int(num_events/10.)+1 if nx_bins==None and n_dimensions==2: out[0] = int(num_events**0.7/10.)+1 if ny_bins==None and n_dimensions==2: out[1] = int(num_events**0.7/10.)+1 if nx_bins==None and n_dimensions==3: out[0] = int(num_events**0.5/10.)+1 if ny_bins==None and n_dimensions==3: out[1] = int(num_events**0.5/10.)+1 if nz_bins==None and n_dimensions==3: out[2] = int(num_events**0.5/10.)+1 for i in range(3): if out[i] == None: out[i]= 0 #set out to 0 for values that are out of the dimension range return tuple(out) def histogram(x_values, x_bins, y_values=None, y_bins=None, weights=None): """ Get a 1d or 2d list of bin weights from a set of data, weights and bin edges - x_values = list of x values to be binned - x_bins = list of x bin edges - y_values = list of y values to be binned. Set to None to make a 1d binning - y_bins = list of y bin edges of same length as x_values - weights = list of statistical weights of same length as x_values. Set to None to make all weights default to 1. Return value is a tuple of (bin_weights, x_bins, y_bins) """ has_numpy() is_1d = False if weights == None: weights = [1.]*len(x_values) if y_values == None: is_1d = True y_values = [0.]*len(x_values) y_bins = [-1.,1.] contents = numpy.zeros((len(x_bins)-1, len(y_bins)-1), ) for i,x in enumerate(x_values): p = bisect.bisect_right(x_bins,x_values[i])-1 q = bisect.bisect_right(y_bins,y_values[i])-1 if p>=0 and p=0 and q where is a unique integer starting from 0 - 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. - title_string = Title of the canvas (as displayed in canvas window title bar); if set to None will use the name_string. - bg_color = Fill color of the canvas. - highlight_color = When a canvas is selected, ROOT draws a border with a particular color to indicate that the canvas is selected. - border_mode = When a canvas is selected, ROOT draws a border if border mode is not set to 0. - frame_fill_color = Fill color of frames drawn on the canvas (e.g. histograms). """ has_root() canvas_name_string = name_string+"-"+str(len(_canvas_persistent)) if title_string == None: title_string = name_string canvas = ROOT.TCanvas(canvas_name_string, title_string) canvas.SetHighLightColor(highlight_color) canvas.SetFillColor(bg_color) canvas.SetBorderMode(border_mode) canvas.SetFrameFillColor(frame_fill_color) _canvas_persistent.append(canvas) return canvas 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, 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=''): """ Make a root histogram with data taken from float lists and axes named after the axis strings. - name_string = name given to the histogram - x_float_list = list of x-data - x_axis_string = string used to label the x-axis - n_x_bins = number of bins in x direction - 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 - y_axis_string = string used to label the y-axis - n_y_bins = number of y bins - weight_list = if present, each item will be filled with weight taken from this list - xmin = float that overrides auto-detection of minimum x-axis value - xmax = float that overrides auto-detection of maximum x-axis value - ymin = float that overrides auto-detection of minimum y-axis value - ymax = float that overrides auto-detection of maximum y-axis value - line_color = int that sets the line colour of the histogram - line_style = int that sets the line style of the histogram - line_width = int that sets the line width of the histogram - fill_color = int that sets the fill color of the histogram - stats = set to True to plot a stats box on the histogram - hist_title_string = specify the string that will appear as a title on the canvas Return value is the histogram """ has_root() name_string += " "+str(len(_hist_persistent)) if len(y_float_list) == len(x_float_list): x_min_max = min_max(x_float_list, weight_list, margin=rg.histo_margin, xmin=xmin, xmax=xmax) y_min_max = min_max(y_float_list, weight_list, margin=rg.histo_margin, xmin=ymin, xmax=ymax) 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]) if(len(weight_list) == len(x_float_list)): for i in range( len(x_float_list) ): hist.Fill(x_float_list[i], y_float_list[i], weight_list[i]) else: for i in range( len(x_float_list) ): hist.Fill(x_float_list[i], y_float_list[i]) else: x_min_max = min_max(x_float_list, weight_list, margin=rg.histo_margin, xmin=xmin, xmax=xmax) hist = ROOT.TH1D(name_string, hist_title_string+';'+x_axis_string, n_x_bins, x_min_max[0], x_min_max[1]) if(len(weight_list) == len(x_float_list)): for i in range( len(x_float_list) ): hist.Fill(x_float_list[i], weight_list[i]) else: for i in range( len(x_float_list) ): hist.Fill(x_float_list[i]) _hist_persistent.append(hist) hist.SetLineColor(line_color) hist.SetLineStyle(line_style) if fill_color!=None: hist.SetFillColor(fill_color) hist.SetStats(stats) return hist def make_root_legend(canvas, root_item_list): """ Build a legend for the canvas """ has_root() if len(root_item_list) == 0: raise KeyError("No items for ROOT legend") canvas.cd() leg = ROOT.TLegend() for index, leg in enumerate(root_item_list): leg_min = 0.89-0.08*len(root_item_list) if leg_min < 0.1: leg_min = 0.1 leg = ROOT.TLegend(0.0, leg_min, 0.4, 0.90) # pylint: disable=E1101 leg.SetEntrySeparation(0.6) for i, hist in enumerate(root_item_list): leg.AddEntry(hist, root_item_list[i].GetName()) leg.SetFillColor(10) leg.SetBorderSize(0) leg.Draw() canvas.Update() _legend_persistent.append(leg) return leg #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, # line_color=rg.line_color, line_style=rg.line_style, line_width=rg.line_width, fill_color=rg.graph_fill_color, hist_title_string=''): # """ # 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) # name_string = name given to the histogram # x_float_list = list of x-data # x_axis_string = string used to label the x-axis # y_float_list = list of y-data # y_axis_string = string used to label the y-axis # z_float_list = list of z-data # z_axis_string = string used to label the z-axis # sort = boolean - set to true to automatically sort input data # xmin = float that overrides auto-detection of minimum x-axis value # xmax = float that overrides auto-detection of maximum x-axis value # ymin = float that overrides auto-detection of minimum y-axis value # ymax = float that overrides auto-detection of maximum y-axis value # zmin = float that overrides auto-detection of minimum z-axis value # zmax = float that overrides auto-detection of maximum z-axis value # line_color = int that sets the line colour of the graph # line_style = int that sets the line style of the graph # line_width = int that sets the line width of the graph # fill_color = graphs dont usually get a fill, but sometimes the fill colour turns up in e.g. legend drawing # hist_title_string = specify the string that will appear as a title # 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 # """ # has_root() # if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list) or len(x_float_list) != len(z_float_list)): # raise IndexError('Attempt to draw graph with no x-points, or different number of x to y to z points.') # multilist = [x_float_list, y_float_list, z_float_list] # if sort: multisort(multilist) # x_min_max = min_max(multilist[0], margin=rg.graph_margin, xmin=xmin, xmax=xmax) # y_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=ymin, xmax=ymax) # z_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=zmin, xmax=zmax) # 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], # line_color=rg.line_color, line_style=rg.line_style, line_width=0, fill_color=None, stats=False, hist_title_string=hist_title_string) # hist.SetMinimum(z_min_max[0]) # hist.SetMaximum(z_min_max[1]) # graph = ROOT.TGraph2D(len(x_float_list)) # graph.SetTitle(name_string) # graph.SetName(name_string) # graph.SetHistogram(hist) # graph_2 = ROOT.TGraph(len(x_float_list)) # for i in range(len(x_float_list)): # graph.SetPoint(i, x_float_list[i], y_float_list[i], z_float_list[i]) # graph_2.SetPoint(i, x_float_list[i], y_float_list[i]) # _graph_persistent.append(graph) # _graph_persistent.append(graph_2) # return (hist, graph, graph_2) 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=[]): """ 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) matplot can format using tex expressions - use '$some math expression$' to include math text in your labels - x_float_list = list of x-data - x_axis_string = string used to label the x-axis - y_float_list = list of y-data - y_axis_string = string used to label the y-axis After building the graph, use matplotlib.pyplot.show() to show something on the screen """ has_matplot() has_numpy() global _figure_index pyplot.figure(_figure_index) _figure_index += 1 if(len(x_float_list) == 0): raise IndexError('Attempt to draw histogram with no x-points') if not len(y_float_list) == len(x_float_list): # x_min_max = min_max(x_float_list, weight_list, margin=histo_margin) # my_bins = range(int(n_x_bins)) # 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)) if(weight_list == []): (n, my_bins) = numpy.histogram(a=x_float_list, bins=n_x_bins) else: (n, my_bins) = numpy.histogram(a=x_float_list, bins=n_x_bins, weights=weight_list) new_bins = [] new_n = [] index = 0 while(abs(n[index])<1.e-9 and index < len(n)): index += 1 for i in range(index, len(n)-1): new_bins.append((my_bins[i] + my_bins[i+1])/2.) new_n.append(n[i]) hist = pylab.plot(new_bins, new_n) pylab.xlabel(x_axis_string) else: 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)) else: hist = pyplot.hexbin(x_float_list, y_float_list, gridsize=(n_x_bins,n_y_bins)) pylab.xlabel(x_axis_string) pylab.ylabel(y_axis_string) return hist 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, line_color=rg.line_color, line_style=rg.line_style, line_width=rg.line_width, fill_color=rg.graph_fill_color, hist_title_string=''): """ 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) - name_string = name given to the histogram - x_float_list = list of x-data - x_axis_string = string used to label the x-axis - y_float_list = list of y-data - y_axis_string = string used to label the y-axis - sort = boolean - set to true to automatically sort input data - xmin = float that overrides auto-detection of minimum x-axis value - xmax = float that overrides auto-detection of maximum x-axis value - ymin = float that overrides auto-detection of minimum y-axis value - ymax = float that overrides auto-detection of maximum y-axis value - line_color = int that sets the line colour of the graph - line_style = int that sets the line style of the graph - line_width = int that sets the line width of the graph - fill_color = graphs dont usually get a fill, but sometimes the fill colour turns up in e.g. legend drawing - hist_title_string = specify the string that will appear as a title Return value is a tuple of (histogram, graph) """ has_root() x_float_list = copy.deepcopy(x_float_list) y_float_list = copy.deepcopy(y_float_list) if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list)): raise IndexError('Attempt to draw graph with no x-points, or different number of x to y points') # name_string += " "+str(len(_hist_persistent)) multilist = [x_float_list, y_float_list] if sort: multisort(multilist) x_min_max = min_max(multilist[0], margin=rg.graph_margin, xmin=xmin, xmax=xmax) y_min_max = min_max(multilist[1], margin=rg.graph_margin, xmin=ymin, xmax=ymax) 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], line_color=rg.line_color, line_style=rg.line_style, line_width=0, fill_color=None, stats=False, hist_title_string=hist_title_string) graph = ROOT.TGraph(len(x_float_list)) graph.SetTitle(name_string) graph.SetName(name_string) graph.SetLineColor(line_color) graph.SetLineStyle(line_style) graph.SetLineWidth(line_width) graph.SetFillColor(fill_color) for i in range(len(x_float_list)): graph.SetPoint(i, x_float_list[i], y_float_list[i]) _graph_persistent.append(graph) return (hist, graph) def make_matplot_graph(x_float_list, x_axis_string, y_float_list, y_axis_string, sort=True): """ 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) matplot can format using tex expressions - use '$some math expression$' to include math text in your labels - x_float_list = list of x-data - x_axis_string = string used to label the x-axis - y_float_list = list of y-data - y_axis_string = string used to label the y-axis - sort = boolean - set to true to automatically sort input data After building the graph, use matplotlib.pyplot.show() to show something on the screen """ has_matplot() global _figure_index pyplot.figure(_figure_index) _figure_index += 1 if(len(x_float_list) == 0 or len(x_float_list) != len(y_float_list)): raise IndexError('Attempt to draw graph with no x-points, or different number of x to y points') multilist = [x_float_list, y_float_list] multisort(multilist) x_min_max = min_max(multilist[0], margin=rg.graph_margin) y_min_max = min_max(multilist[1], margin=rg.graph_margin) myplot = pylab.plot(x_float_list, y_float_list) pylab.xlabel(x_axis_string) pylab.ylabel(y_axis_string) pylab.xlim (x_min_max[0], x_min_max[1]) pylab.ylim (y_min_max[0], y_min_max[1]) matplotlib.pyplot.draw() def make_root_multigraph(name_string, x_float_list_of_lists, x_axis_string, y_float_list_of_lists, y_axis_string): """ Print several different graphs on the same canvas. Some default colour scheme is applied, but it may not be the best... - name_string = name that will be given to the axes (histogram) - x_float_list_of_lists = list of lists. Each list will be used as the x-axis for a graph - x_axis_string = string that will be used to label the x_axis - y_float_list_of_lists = list of lists. Each list will be used as the y-axis for a graph - y_axis_string = string that will be used to label the y_axis 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 """ has_root() name_string += " "+str(len(_hist_persistent)) total_x_list = [] total_y_list = [] for a_list in x_float_list_of_lists: total_x_list += a_list for a_list in y_float_list_of_lists: total_y_list += a_list x_min_max = min_max(total_x_list, margin=rg.graph_margin) y_min_max = min_max(total_y_list, margin=rg.graph_margin) 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]) graphs = [] for index in range( len(x_float_list_of_lists) ): graphs.append(ROOT.TGraph( len(x_float_list_of_lists[index]) )) ROOT.gStyle.SetPalette(int(len(x_float_list_of_lists)*1.25)) graphs[-1].SetLineColor(ROOT.gStyle.GetColorPalette(index + int(len(x_float_list_of_lists)*0.25) )) #funny algebra to darken the colors slightly for i in range(len(x_float_list_of_lists[index])): graphs[-1].SetPoint(i, x_float_list_of_lists[index][i], y_float_list_of_lists[index][i]) _graph_persistent.append(graphs[-1]) _hist_persistent.append(hist) hist.SetStats(False) return (hist, graphs) def make_matplot_multigraph(x_float_list_of_lists, x_axis_string, y_float_list_of_lists, y_axis_string): """ Print several different graphs on the same axes. Some default colour scheme is applied, but it may not be the best... - x_float_list_of_lists = list of lists. Each list will be used as the x-axis for a graph - x_axis_string = string that will be used to label the x_axis - y_float_list_of_lists = list of lists. Each list will be used as the y-axis for a graph - y_axis_string = string that will be used to label the y_axis 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 """ has_matplot() global _figure_index pyplot.figure(_figure_index) _figure_index += 1 total_x_list = [] total_y_list = [] for a_list in x_float_list_of_lists: total_x_list += a_list for a_list in y_float_list_of_lists: total_y_list += a_list x_min_max = min_max(total_x_list, margin=rg.graph_margin) y_min_max = min_max(total_y_list, margin=rg.graph_margin) for index in range( len(x_float_list_of_lists) ): multilist = [x_float_list_of_lists[index], y_float_list_of_lists[index]] multisort(multilist) myplot = pylab.plot(multilist[0], multilist[1]) pylab.xlabel(x_axis_string) pylab.ylabel(y_axis_string) pylab.xlim (x_min_max[0], x_min_max[1]) pylab.ylim (y_min_max[0], y_min_max[1]) matplotlib.pyplot.draw() def make_matplot_scatter(x_float_list, x_axis_string, y_float_list, y_axis_string): """ Make a matplot scatter graph with data taken from float lists and axes naemd after the axis strings. matplot can format using tex expressions - use '$some math expression$' to include math text in your labels - x_float_list = list of x-data - x_axis_string = string used to label the x-axis - y_float_list = list of y-data - y_axis_string = string used to label the y-axis After building the graph, use matplotlib.pyplot.show() to show something on the screen """ has_matplot() global _figure_index pyplot.figure(_figure_index) _figure_index += 1 if(len(x_float_list) == 0): raise IndexError('Attempt to draw histogram with no x-points') hist = pyplot.scatter(x_float_list, y_float_list, s=1) pylab.xlabel(x_axis_string) pylab.ylabel(x_axis_string) return hist def wait_for_root(): """ Force python to halt processing until ROOT windows are closed """ print 'Close ROOT windows to continue' root_done = False while not root_done: root_done = True for canvas in _canvas_persistent: if not canvas == None: root_done = False def clear_root(): """ Close root plots (and free memory) """ for canv in _canvas_persistent: del(canv) for hist in _hist_persistent: del(hist) for graph in _graph_persistent: del(graph) def wait_for_matplot(): """Show any plots made using matplotlib on the screen""" has_matplot() matplotlib.pyplot.show() def matplot_show_and_continue(): """Show matplotlib plots and return to the script""" has_matplot() try: subprocess( matplotlib.pyplot.show, () ) except: raise ImportError('matplot_show_and_continue is only available is (i) ROOT is not installed or (ii) you have python > 2.6') def make_doc(source_folder=os.getcwd(), target_folder=os.getcwd(), test=True): import xboa.Hit import xboa.Bunch import xboa.Common import pydoc import shutil file_list = ['Hit.html','Common.html','Bunch.html'] for docfile in file_list: try: os.remove(source_folder+'/xboa.'+docfile) except: pass pydoc.writedoc(xboa.Hit) pydoc.writedoc(xboa.Common) pydoc.writedoc(xboa.Bunch) for docfile in file_list: try: os.remove(target_folder+'/doc/'+docfile) except: pass shutil.move(source_folder+'/xboa.'+docfile, target_folder+'/doc/'+docfile) def make_dist_preliminaries(source_folder=os.getcwd(), target_folder=os.getcwd(), test=True): """ Preliminaries to making a distribution: - run test scripts - generate documentation """ import Boa_Test if test==True: (passes, fails, warns) = Boa_Test.test_all() print 'TEST RESULTS:\nPasses:',passes,'\nFails:',fails,'\nWarns:',warns,'\n' if fails > 0: raise Exception('Some tests failed - aborting release') __mp_subprocesses = [] __fk_subprocesses = [] def subprocess(function, args): """ Make a function call in a subprocess; return the subprocess pid - This uses the multiprocessing libary in the first instance - if multiprocessing is not available the function tries to use os.fork(); however, os.fork is not compatible with ROOT. If - multiprocessing is not available and ROOT is installed, this routine will throw an ImportError """ try: has_multiprocessing() __mp_subprocesses.append( multiprocessing.Process(target=function, args=args) ) __mp_subprocesses[-1].start() return __mp_subprocesses[-1] except: try: has_root() raise ImportError('Error - attempt to make a subprocess using fork when ROOT library is active') except ImportError: pid = os.fork() if pid == 0: try: function(*args) except: os._exit(1) os._exit(0) else: __fk_subprocesses.append(pid) return pid def kill_all_subprocesses(): """ Kill all subprocesses generated by Common.subprocess call; automatically called at exit unless kill_subprocesses_at_exit is set to False Note that this makes a call to the os, which may take a bit of time to respond. """ for ps in __mp_subprocesses: ps.terminate() time.sleep(0.01) #dont ask me why this is necessary... for pid in __fk_subprocesses: os.kill(pid, signal.SIGKILL) def kolmogorov_smirnov_test(list_1, list_2): """ Convenience wrapper for ROOT kolmogorov-smirnov test. - list_1 = list of floats that are sampled from some parent probability distribution - list_2 = list of floats that are sampled from other some parent distribution Returns double between 0. and 1. giving probability that list_1 and list_2 have the same parent distribution. """ list_1 = sorted(list_1) list_2 = sorted(list_2) c_array_1 = ctypes.c_double*len(list_1) c_array_2 = ctypes.c_double*len(list_2) ca1 = c_array_1() ca2 = c_array_2() for i in range(len(list_1)): ca1[i]=ctypes.c_double(list_1[i]) for i in range(len(list_2)): ca2[i]=ctypes.c_double(list_2[i]) ks = ROOT.TMath.KolmogorovTest(len(list_1), ca1, len(list_2), ca2, '') return ks def __atexit(): """Calls some functions automatically at exit""" if(kill_subprocesses_at_exit): kill_all_subprocesses() atexit.register(__atexit) def make_grid(n_dimensions, n_per_dimension): """ Make a rectangular n_dimensional grid of points evenly spaced between [-1,1] in each dimension - n_dimensions = number of dimensions - n_per_dimension = number of points in each dimension; total number of points will be n_per_dimension^n_dimensions Return value is a list of numpy.matrices with shape (n_dimensions, 1) """ has_numpy() pos_vector = [numpy.matrix( [-1.]*n_dimensions )] for i in range(n_dimensions): pos_vector_copy = copy.deepcopy(pos_vector) for j in range(1, n_per_dimension): pos = (2.*j)/float(n_per_dimension-1)-1. for vec in pos_vector_copy: vec = copy.deepcopy(vec) vec[0,i] = pos pos_vector.append(vec) return pos_vector def make_shell(n_per_dimension, ellipse): """ Make a shell of points that sit on a hyper-ellipsoid defined by ellipse matrix - n_per_dimension = number of points in each dimension; total number of points will be n_per_dimension^n_dimensions if n_per_dimension is even or n_per_dimension^n_dimensions-1 if n_per_dimension is odd - ellipse = matrix that defines the ellipse on which vector sits; should be a numpy.matrix with shape (vec_length, vec_length) Points are defined on a (hyper-)cuboidal grid and then compressed so that lengths are all 1. Doesn't necessarily mean points are evenly spaced. Return value is a list of numpy.matrices with shape (n_dimensions, 1) """ has_numpy() n_dimensions = numpy.shape(ellipse)[0] grid = make_grid(n_dimensions, n_per_dimension) shell = [] ellipse_inv = numpy.linalg.inv(ellipse) for vector in grid: if numpy.vdot(vector,vector) > float_tolerance: shell.append(normalise_vector(vector, ellipse_inv)) return shell def normalise_vector(vector, matrix_inverse): """ Normalise vector so that vector.T() * matrix.inverse * vector = 1 - vector = the vector to normalise; should be a numpy.matrix with shape (vec_length,1) - matrix_inverse = inverse of matrix that defines the ellipse on which vector sits; should be a numpy.matrix with shape (vec_length, vec_length) Return value is a list of numpy.matrices with shape (vec_length,1) """ has_numpy() scale = 1./(vector * matrix_inverse * vector.transpose())[0,0]**0.5 if scale != scale: raise(ValueError("Bad input - vector or matrix magnitude 0")) #isnan and isinf not available in 2.5 vector = vector*scale return vector def __function_with_queue(args): """ Wrapper function to put multiprocessing output into a queue - args tuple of (function_call, queue, function_arg) where - function_call is a reference to the function that we want to wrap\n - queue is the queue that will hold return values from the function\n - function_arg is the argument to function_call\n - index is an index indicating function_arg's position in the inputs tuple of (index, Output) is placed into queue; if function_call throws an exception, the exception is placed on the queue instead """ (function_call, queue, function_arg, index) = args try: queue.put((index, function_call(*function_arg))) except: queue.put((index, sys.exc_info()[1])) def process_list(function_call, list_of_args, max_n_processes): """ Run multiprocessing on a list of arguments - function_call multiprocess this function call - list_of_args list of tuples of arguments for function_call - max_n_processes maximum number of concurrent processes to use Returns list of return values, one for each function call. List is always sorted into same order as input. e.g. process_list(time.sleep, [(3, ), (6, ), (2, )], 2) will multiprocess the time.sleep function with inputs 3, 6 and 2 across 2 cores and return list like [None, None, None]. """ manager = multiprocessing.Manager() queue = manager.Queue() # queue stores return values pool = multiprocessing.Pool(max_n_processes) # pool runs multiprocess # each process needs: # reference to queue to put returns values in # index (to sort return values) # plus function call and argument new_list_of_args = [(function_call, queue, x, i) for i,x in enumerate(list_of_args)] # run the processes pool.map(__function_with_queue, new_list_of_args) # sort output into a list out_list = [] while not queue.empty(): out_list.append(queue.get()) out_list.sort() for i,item in enumerate(out_list): out_list[i] = item[1] # cleanup pool.close() return out_list def _sum_weight(weights, in_cut): n_points = len(in_cut) return sum([weights[i] for i in range(n_points) if in_cut[i]]) def _get_fit_ellipse_covariance_matrix(points, weights, in_cut): n_points = len(points) sum_weight = _sum_weight(weights, in_cut) dimension = len(points[0]) mean = numpy.zeros(dimension) cov = numpy.zeros([dimension, dimension]) for i in range(n_points): if in_cut[i]: a_point = points[i] for j in range(dimension): mean[j] += a_point[j]/sum_weight*weights[i] for k in range(dimension): cov[j, k] += a_point[j]*a_point[k]/sum_weight*weights[i] for i in range(dimension): for j in range(dimension): cov[i, j] -= mean[i]*mean[j] return mean, cov def _update_fit_ellipse_cut(points, cov_inv, mean, eps_cut): n_points = len(points) n_dim = 2 in_cut = [True for i in range(n_points)] sqrt_det_inv = numpy.linalg.det(cov_inv)**(1./float(n_dim)) for i in range(n_points): vec = numpy.array(points[i])-mean vec_t = numpy.transpose(vec) eps = numpy.dot(vec_t, numpy.dot(cov_inv, vec))/sqrt_det_inv in_cut[i] = eps < eps_cut return in_cut def fit_ellipse(points, eps_cut, weights=None, max_number_of_iterations = 10, verbose = True): """ Fit an ellipse of arbitrary dimension n to a set of points - points iterable of points; each point should be an iterable of floats of length n - eps_cut float cut value; only particles with eps = x^T*V^{-1}*x*|V|**{1/n} < eps_{cut} are considered. The cut value is a normalised chi-squared. It should be positive. - weights list of floats, one for each point; use statistically weighted particles for the fit. Set to None to ignore weights. - max_number_of_iterations integer number of iterations; the ellipse fitting procedure is repeated several times to attempt to improve the fit of the ellipse. This sets a maximum number of repetitions. - verbose set to True to provide some verbose output during fitting Returns ellipse centre vector and defining matrix V. The ensemble of points on the ellipse is given by (x-)^T*V^{-1}*(x-) where x is a particle vector and V is the defining matrix. """ n_points = len(points) old_det = 1. mean, cov = numpy.ones([2]), numpy.ones([2, 2]) in_cut = [True for i in range(n_points)] if weights == None: weights = [1.,]*n_points iterations = 0 try: while abs(numpy.linalg.det(cov)-old_det) > float_tolerance and \ iterations < max_number_of_iterations: iterations += 1 my_sum = 0 for i in in_cut: if i == True: my_sum += 1 old_det = numpy.linalg.det(cov) mean, cov = _get_fit_ellipse_covariance_matrix(points, weights, in_cut) mat_inv = numpy.linalg.inv(cov) in_cut = _update_fit_ellipse_cut(points, mat_inv, mean, eps_cut) except Exception: if verbose: print "xboa.Common.fit_ellipse(...) failed to converge; final iteration as follows." sys.excepthook(*sys.exc_info()) if verbose: print 'Weight in cut:\n', _sum_weight(weights, in_cut) print "Means:\n", mean print "Ellipse:\n", cov print "Number of iterations:\n", iterations return mean, cov def make_root_ellipse_function(mean, cov, contours=None, xmin=-1e3, xmax=1e3, ymin=-1e3, ymax=1e3): """ Make a ROOT TFunction for a given beam ellipse - mean sequence type of length>=2 (must allow reference by integer [index]) - cov ellipse >= 2x2 symmetric numpy.array (must allow referencing by integer indices like [a, b]) - contours iterable; draw contours at values of x^T V^{-1} x given by elements. Uses ROOT default if equal to None. Returns a ROOT TFunction. Note ROOT TFunction does not like drawing small ellipses if the ellipse matrix is highly correlated (i.e. determinant near zero). A workaround is to make a TGraph with appropriate points (using e.g. formula given by this function). May be possible to fix by increasing the number of points in plot (func.SetNpy). """ has_root() mat_inv = numpy.linalg.inv(cov) fit_func_str = "[2]*(x-[0])**2+[4]*(y-[1])**2+2*[3]*(x-[0])*(y-[1])" fit_func = ROOT.TF2("ellipse", fit_func_str) if contours != None: fit_func.SetContour(len(contours)) for i, contour in enumerate(contours): fit_func.SetContourLevel(i, contour) if xmax == None or xmin == None or ymax == None or ymin == None: xmin = -1e3+mean[0] xmax = 1e3+mean[0] ymin = -1e3+mean[1] ymax = 1e3+mean[1] fit_func.SetRange(xmin, ymin, xmax, ymax) fit_func.SetParameter(0, mean[0]) fit_func.SetParameter(1, mean[1]) fit_func.SetParameter(2, mat_inv[0, 0]) fit_func.SetParameter(3, mat_inv[0, 1]) fit_func.SetParameter(4, mat_inv[1, 1]) fit_func.SetLineColor(rg.fit_color) fit_func.SetLineStyle(rg.fit_style) _function_persistent.append(fit_func) return fit_func def common_overview_doc(verbose = False): """Creates some summary documentation for the Common module. If verbose is True then will also print any functions or data not included in summary""" common_doc = '\nCommon module contains a number of useful interfaces, defaults, data and ancillary functions that support the rest of the XBOA package.\n' name_list = ['math', 'root', 'matplot', 'data', 'defaults', 'other_stuff'] function_list = { 'math' : ['min_max', 'multisort', 'nd_newton_raphson1', 'nd_newton_raphson2'], 'root' : ['has_root','make_root_graph', 'make_root_histogram', 'make_root_multigraph', 'clear_root', 'wait_for_root', 'make_root_canvas'], 'matplot' : ['has_matplot', 'make_matplot_graph', 'make_matplot_histogram', 'make_matplot_multigraph', 'make_matplot_scatter', 'wait_for_matplot', 'matplot_show_and_continue'], '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'], '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'], 'defaults' : ['canvas_fill_color', 'canvas_highlight_color', 'default_margin', 'float_tolerance', 'graph_margin', 'histo_margin', 'python_version', 'xboa_version', 'kill_subprocesses_at_exit'] } function_doc = { 'math' : 'Maths functions:', 'root' : 'Interfaces to ROOT plotting library:', 'matplot' : 'Interfaces to matplotlib plotting library:', 'other_stuff' : 'Some other useful functions:', 'data' : 'Physics data:', 'defaults' : 'Defaults for e.g. root canvases, etc:', } import Common dir_common = dir(Common) if verbose: print 'The following functions and data are in Common but not in common_overview_doc:' for func in dir_common: found = False for func_sublist in function_list.values(): if func in func_sublist: found = True if not found: print func, print '\n' print 'The following functions and data are in common_overview_doc but not in Common:' for func_sublist in function_list.values(): for func in func_sublist: if func not in dir_common: print func, print doc = common_doc for key in name_list: doc = doc + function_doc[key]+'\n' for item in function_list[key]: doc = doc+' '+item+'\n' return doc __doc__ = common_overview_doc()