def pulseAnalysis(): defineNameOfProperties() startTime = dm.getTime() print "\nStart PULSE analysis, batches:", md.batchNumbers for batchNumber in md.batchNumbers: runNumbers = md.getAllRunNumbers(batchNumber) startTimeBatch = dm.getTime() print "Batch:", batchNumber, len(runNumbers), "run files.\n" for runNumber in runNumbers: md.defineRunInfo(md.getRowForRunNumber(runNumber)) if not dm.checkIfFileAvailable("pulse"): continue pulseAnalysisPerRun() print "Done with batch", batchNumber, "Time analysing: "+str(dm.getTime()-startTimeBatch)+"\n" print "Done with PULSE analysis. Time analysing: "+str(dm.getTime()-startTime)+"\n"
def pulsePlots(): print "\nStart producing PULSE plots, batches:", md.batchNumbers, "\n" for batchNumber in md.batchNumbers: p_main.defineNameOfProperties() runNumbers = md.getAllRunNumbers(batchNumber) numpy_arrays = [ np.empty(0, dtype=dm.getDTYPE(batchNumber)) for _ in range(len(p_main.var_names)) ] for runNumber in runNumbers: md.defineRunInfo(md.getRowForRunNumber(runNumber)) if runNumber not in md.getRunsWithSensor(): continue for index in range(0, len(p_main.var_names)): numpy_arrays[index] = np.concatenate( (numpy_arrays[index], dm.exportImportROOTData("pulse", p_main.var_names[index])), axis=0) if len(numpy_arrays[0]) != 0: producePulsePlots(numpy_arrays) print "Done with producing PULSE plots.\n"
def produceTimingDistributionPlots(time_difference, category): group = "timing" if category.find("system") != -1: # Comment this function if you want to omit producing plots for system of equations produceTimingDistributionPlotsSysEq(time_difference, category) return 0 text = "\nSiPM-DUT TIME DIFFERENCE (PEAK) BATCH " + str(md.getBatchNumber()) + "\n" if category.find("cfd") != -1: text = text.replace("PEAK", "CFD") print text time_diff_TH1F = dict() for chan in time_difference.dtype.names: md.setChannelName(chan) # Omit sensors which are not analyzed (defined in main.py) if (md.getSensor() != md.sensor and md.sensor != "") or md.getSensor() == "SiPM-AFP": continue print md.getSensor(), "\n" th_name = "_" + str(md.getBatchNumber()) + "_" + md.chan_name time_diff_TH1F[chan] = ROOT.TH1F(category + th_name, category, xbins, -fill_range, fill_range) # Fill the objects for entry in range(0, len(time_difference[chan])): if time_difference[chan][entry] != 0: time_diff_TH1F[chan].Fill(time_difference[chan][entry]) # Get fit function with width of the distributions # Check if the filled object have at a minimum of required entries if time_diff_TH1F[chan].GetEntries() < min_entries_per_run * len(md.getAllRunNumbers(md.getBatchNumber())): if category.find("cfd") != -1: type = "cfd reference" else: type = "peak reference" print "Omitting sensor", md.getSensor(), "for", type, "due to low statistics. \n" continue sigma_DUT, sigma_fit_error = t_calc.getSigmasFromFit(time_diff_TH1F[chan], window_range, percentage) exportTHPlot(time_diff_TH1F[chan], [sigma_DUT, sigma_fit_error], category)
def timingPlots(): print "\nStart producing TIMING RESOLUTION plots, batches:", md.batchNumbers, "\n" global var_names for batchNumber in md.batchNumbers: runNumbers = md.getAllRunNumbers(batchNumber) # Create numpy arrays for linear time difference (one element per "channel") numpy_arrays = [np.empty(0, dtype = dm.getDTYPE(batchNumber)) for _ in range(2)] # Create numpy arrays for system of equations (three elements per "channel") numpy_arrays.append(np.empty(0, dtype = dm.getDTYPESysEq())) numpy_arrays.append(np.empty(0, dtype = dm.getDTYPESysEq())) var_names = ["normal_peak", "normal_cfd", "system_peak", "system_cfd"] for runNumber in runNumbers: md.defineRunInfo(md.getRowForRunNumber(runNumber)) if not dm.checkIfROOTDataFileExists("timing", "normal_peak"): t_calc.createTimingFiles(batchNumber) # Skip runs which are not in synch if runNumber not in md.getRunsWithSensor() or runNumber in [3697, 3698, 3701]: continue for index in range(0, len(var_names)): # omit batch 60X for solving system of equations if var_names[index].find("system") != -1 and md.getBatchNumber()/100 == 6: continue else: numpy_arrays[index] = np.concatenate((numpy_arrays[index], dm.exportImportROOTData("timing", var_names[index])), axis = 0) if numpy_arrays[0].size != 0: for index in range(0, len(var_names)): # omit batch 60X for solving system of equations if var_names[index].find("system") != -1 and md.getBatchNumber()/100 == 6: continue else: produceTimingDistributionPlots(numpy_arrays[index], var_names[index]) print "\nDone with producing TIMING RESOLUTION plots.\n"
def createTimingFiles(batchNumber): runNumbers = md.getAllRunNumbers(batchNumber) startTimeBatch = dm.getTime() print "\nBatch:", batchNumber, len(runNumbers), "run files.\n" for runNumber in runNumbers: md.defineRunInfo(md.getRowForRunNumber(runNumber)) if not dm.checkIfFileAvailable("timing"): continue print "Run", runNumber, "\n" # Import files per run peak_time = dm.exportImportROOTData("pulse", "peak_time") cfd = dm.exportImportROOTData("pulse", "cfd") # Perform linear calculations time_diff_peak = getTimeDifferencePerRun(peak_time) time_diff_cfd = getTimeDifferencePerRun(cfd) # Export per run number linear dm.exportImportROOTData("timing", "normal_peak", time_diff_peak) dm.exportImportROOTData("timing", "normal_cfd", time_diff_cfd) if batchNumber/100 != 6: # Perform calculations sys eq time_diff_peak_sys_eq = getTimeDifferencePerRunSysEq(peak_time) time_diff_cfd_sys_eq = getTimeDifferencePerRunSysEq(cfd) # Export per run number sys eq dm.exportImportROOTData("timing", "system_peak", time_diff_peak_sys_eq) dm.exportImportROOTData("timing", "system_cfd", time_diff_cfd_sys_eq) print "Done with run", runNumber, "\n" print "Done with batch", batchNumber, "Time analysing: "+str(dm.getTime()-startTimeBatch)+"\n"
def importResultsValues(sensor_data, category_subcategory): global oneSensorInLegend if category_subcategory.endswith('gain'): category_subcategory = category_subcategory[:-5] gain_category = True else: gain_category = False # here are imported all files, that is for each pad, temperature and bias voltage for batchNumber in md.getAllBatchNumbers(): for chan in md.getAllChannelsForSensor(batchNumber, processed_sensor): if batchNumber not in md.getAllBatchNumberForSensor( processed_sensor) or omitBadData(batchNumber, category_subcategory): continue md.defineRunInfo( md.getRowForRunNumber(md.getAllRunNumbers(batchNumber)[0])) md.setChannelName(chan) if md.getDUTPos() in ["3_0", "3_1", "3_3", "8_1", "7_2", "7_3"]: continue # Define the name for the histogram, depending on type if category_subcategory.find( "pulse_amplitude") == -1 and category_subcategory.find( "charge") == -1: group = "timing" chan2 = "" parameter_number = 2 if category_subcategory.find("system") != -1: if md.chan_name not in [ "chan0", "chan1", "chan2", "chan3" ]: continue category = "system" chan2 = "chan" + str((int(md.chan_name[-1]) + 1) % 4) else: category = "normal" if category_subcategory.endswith('cfd'): subcategory = "cfd" else: subcategory = "peak" if category_subcategory.find( "rise_time") != -1 or category_subcategory.find( "noise") != -1: group = "pulse" category = category_subcategory subcategory = "" chan2 = "" # Here, import the histogram which contain the results histogram = dm.exportImportROOTHistogram( group, category, subcategory, chan2) if histogram: fit_function = histogram.GetFunction("gaus") if category_subcategory.find( "noise") != -1 or category_subcategory.find( "rise_time") != -1: parameter_number = 1 results = [ fit_function.GetParameter(parameter_number), fit_function.GetParError(parameter_number) ] else: continue # pulse and gain else: histogram = dm.exportImportROOTHistogram( "pulse", category_subcategory) if histogram: th_name = "_" + str( md.getBatchNumber()) + "_" + md.chan_name function_name = "Fitfcn_" + category_subcategory + th_name fit_function = histogram.GetFunction(function_name) try: fit_function.GetTitle() except: continue results = [ fit_function.GetParameter(1), fit_function.GetParError(1) ] else: continue if category_subcategory.find( "normal") != -1 or category_subcategory.find( "system") != -1: results[0] = np.sqrt( np.power(results[0], 2) - np.power(md.getSigmaSiPM(), 2)) value_error = [results[0], results[1]] voltage = md.getBiasVoltage() # For the timing resolution vs gain, replace the bias voltage with gain if (category_subcategory.find("system") != -1 or category_subcategory.find("normal") != -1 ) and gain_category: histogram = dm.exportImportROOTHistogram("pulse", "charge") th_name = "_" + str(md.getBatchNumber()) + "_" + md.chan_name function_name = "Fitfcn_" + "charge" + th_name fit_function = histogram.GetFunction(function_name) gain = fit_function.GetParameter( 1) / md.getChargeWithoutGainLayer() voltage = int( gain ) # this takes the even number of gain (to select better values) temperature = str(md.getTemperature()) DUT_pos = md.getDUTPos() omitRun = False # Among the all batches, choose one with smallest error. for index in range(0, len(sensor_data[temperature][DUT_pos])): sensor_results = sensor_data[temperature][DUT_pos][index] # Check if there is an earlier filled bias voltage, otherwise fill if voltage == sensor_results[0]: omitRun = True # For the same voltage, choose the one with smallest error. if value_error[1] < sensor_results[1][1]: sensor_data[temperature][DUT_pos][index] = [ voltage, value_error ] if not omitRun: sensor_data[temperature][DUT_pos].append( [voltage, value_error]) oneSensorInLegend = True
def trackingPlots(): global var_names startTime = dm.getTime() print "\nStart TRACKING analysis, batches:", md.batchNumbers, "\n" for batchNumber in md.batchNumbers: startTimeBatch = dm.getTime() runNumbers = md.getAllRunNumbers(batchNumber) # Omit batches with less than 3 runs if len(runNumbers) < 3: print "Batch", batchNumber, "omitted, < 3 runs.\n" continue print "BATCH", batchNumber, "\n" var_names = [["pulse", "pulse_amplitude"], ["pulse", "charge"], ["pulse", "rise_time"], ["timing", "normal_peak"], ["timing", "normal_cfd"]] numpy_arrays = [np.empty(0, dtype = dm.getDTYPE(batchNumber)) for _ in range(len(var_names))] numpy_arrays.append(np.empty(0, dtype = dm.getDTYPETracking())) max_sample = np.empty(0, dtype = dm.getDTYPE(batchNumber)) for runNumber in runNumbers: md.defineRunInfo(md.getRowForRunNumber(runNumber)) # Produce timing resolution files if they not exist if not dm.checkIfROOTDataFileExists("timing", "normal_peak"): t_calc.createTimingFiles(batchNumber) if not dm.checkIfFileAvailable("tracking"): continue tracking_run = dm.exportImportROOTData("tracking", "tracking") # This strips the event number to match the ones with the tracking. It assumes that the tracking have fewer number of events than the oscilloscope events. for index in range(0, len(var_names)): numpy_arrays[index] = np.concatenate((numpy_arrays[index], np.take(dm.exportImportROOTData(var_names[index][0], var_names[index][1]), np.arange(0, len(tracking_run)))), axis=0) max_sample = np.concatenate((max_sample, np.take(dm.exportImportROOTData("pulse", "max_sample"), np.arange(0, len(tracking_run)))), axis=0) # Concatenate tracking arrays numpy_arrays[-1] = np.concatenate((numpy_arrays[-1], tracking_run), axis=0) [pulse_amplitude, gain, rise_time, time_difference_peak, time_difference_cfd, tracking] = [i for i in numpy_arrays] # This checks if the position file exists, otherwise it will create it if not dm.checkIfROOTDataFileExists("tracking", "position"): t_calc.calculateCenterOfSensorPerBatch(pulse_amplitude, tracking) declareTCanvas() defineBinSizes() if md.getBatchNumber()/100 == 7: updateBinSize(1.5) t_calc.setArrayPadExportBool(False) createSinglePadGraphs(numpy_arrays, max_sample) createArrayPadGraphs(distance_x, distance_y) print "\nDone with batch", batchNumber, "Time analysing: "+str(md.dm.getTime()-startTimeBatch)+"\n" print "\nDone with TRACKING analysis. Time analysing: "+str(md.dm.getTime()-startTime)+"\n"
def produceTimingDistributionPlotsSysEq(time_difference, category): text = "\nSYS. OF. EQS. TIME DIFFERENCE (PEAK) BATCH " + str(md.getBatchNumber()) + "\n" if category.find("cfd") != -1: text = text.replace("PEAK", "CFD") print text # TH1 objects time_diff_TH1F = dict() omit_batch = False channels_1st_oscilloscope = ["chan0", "chan1", "chan2", "chan3"] sigma_convoluted = np.zeros((4,4)) sigma_convoluted_error = np.zeros((4,4)) # First loop, calculate the sigmas for each combination of time differences for chan in channels_1st_oscilloscope: md.setChannelName(chan) print md.getSensor(), "\n" # Do not consider the same channel when comparing two chan2_list = list(channels_1st_oscilloscope) chan2_list.remove(chan) # Create TH1 object time_diff_TH1F[chan] = dict() for chan2 in chan2_list: th_name = "_" + str(md.getBatchNumber()) + "_" + md.chan_name + "_" + chan2 time_diff_TH1F[chan][chan2] = ROOT.TH1F(category + th_name, category, xbins, -fill_range, fill_range) # Fill TH1 object between channels in first oscilloscope for entry in range(0, len(time_difference[chan])): for index in range(0, len(chan2_list)): chan2 = chan2_list[index] if time_difference[chan][entry][0][index] != 0: time_diff_TH1F[chan][chan2].Fill(time_difference[chan][entry][0][index]) # Get sigma and adapt distribution curve for chan2 in chan2_list: # Find the maximal value MPV_bin = time_diff_TH1F[chan][chan2].GetMaximumBin() MPV_time_diff = int(time_diff_TH1F[chan][chan2].GetXaxis().GetBinCenter(MPV_bin)) xMin = MPV_time_diff - window_range xMax = MPV_time_diff + window_range time_diff_TH1F[chan][chan2].SetAxisRange(xMin, xMax) # Redefine range for the fit sigma_window = time_diff_TH1F[chan][chan2].GetStdDev() mean_window = time_diff_TH1F[chan][chan2].GetMean() xMin = mean_window - width_selection * sigma_window xMax = mean_window + width_selection * sigma_window # Obtain the parameters time_diff_TH1F[chan][chan2].Fit("gaus", "Q", "", xMin, xMax) fit_function = time_diff_TH1F[chan][chan2].GetFunction("gaus") i = int(chan[-1]) % 4 j = int(chan2[-1]) % 4 try: # Get sigma between two channels sigma_convoluted[i][j] = fit_function.GetParameter(2) sigma_convoluted_error[i][j] = fit_function.GetParError(2) except: sigma_convoluted[i][j] = sigma_convoluted[j][i] = 0 sigma_convoluted_error[i][j] = sigma_convoluted_error[j][i] = 0 # Second loop, check if all combined plots have at least 1000 entries for chan in channels_1st_oscilloscope: md.setChannelName(chan) # Do not consider the same channel when comparing two chan2_list = list(channels_1st_oscilloscope) chan2_list.remove(chan) for chan2 in chan2_list: if time_diff_TH1F[chan][chan2].GetEntries() < min_entries_per_run * len(md.getAllRunNumbers(md.getBatchNumber())): type = "peak reference" if category.find("cfd") != -1: type = "cfd reference" print "Omitting batch", md.getBatchNumber(), "for", type, "time difference system plot for", md.getSensor(), "and", md.getSensor(chan2), "due to low statistics \n" omit_batch = True break if omit_batch: break # Solve the system in case the condition of requiring at least 1000 entries for each plots is not fulfilled if not omit_batch: sigmas_chan, sigmas_error = t_calc.solveSystemOfEqs(sigma_convoluted, sigma_convoluted_error) # Third loop, print the graphs together with the solutions for chan in channels_1st_oscilloscope: md.setChannelName(chan) chan2_list = list(channels_1st_oscilloscope) chan2_list.remove(chan) # Loop through the combinations for chan2 in chan2_list: index = int(chan[-1]) % 4 sigma_DUT = sigmas_chan[index] sigma_DUT_error = sigmas_error[index] exportTHPlot(time_diff_TH1F[chan][chan2], [sigma_DUT, sigma_DUT_error], category, chan2)