def calculateCenterOfSensorPerBatch(pulse_amplitude, tracking): # Calculate position for largest batches if md.getBatchNumber() not in [101, 207, 306, 401, 507, 601, 707]: return 0 global canvas_center canvas_center = ROOT.TCanvas("c1", "c1") print "\nCalculating CENTER POSITIONS for batch", md.getBatchNumber(), "\n" position_temp = np.zeros(1, dtype=dm.getDTYPETrackingPosition()) for chan in pulse_amplitude.dtype.names: md.setChannelName(chan) print md.getSensor() position_temp[chan][0] = getCenterOfSensor(pulse_amplitude[chan], np.copy(tracking)) dm.exportImportROOTData("tracking", "position", position_temp) print "\nDONE producing CENTER POSITIONS for batch", md.getBatchNumber( ), "\n"
def createSinglePadGraphs(numpy_arrays, max_sample): [pulse_amplitude, gain, rise_time, time_difference_peak, time_difference_cfd, tracking] = [i for i in numpy_arrays] # Convert pulse amplitude values from [-V] to [+mV], charge from [C] to gain, rise time from [ns] to [ps] dm.changeIndexNumpyArray(pulse_amplitude, -1000.0) dm.changeIndexNumpyArray(max_sample, -1000.0) dm.changeIndexNumpyArray(gain, 1./(md.getChargeWithoutGainLayer()*10**-15)) dm.changeIndexNumpyArray(rise_time, 1000.0) # Produce single pad plots for chan in pulse_amplitude.dtype.names: md.setChannelName(chan) if (md.getSensor() != md.sensor and md.sensor != "") or md.getSensor() == "SiPM-AFP": continue print "Single pad", md.getSensor(), "\n" # This function requires a ROOT file which have the center positions for each pad tracking_chan = t_calc.changeCenterPositionSensor(np.copy(tracking)) produceTProfilePlots([np.copy(pulse_amplitude[chan]), gain[chan], rise_time[chan], time_difference_peak[chan], time_difference_cfd[chan]], max_sample[chan], tracking_chan, distance_x, distance_y) produceEfficiencyPlot(pulse_amplitude[chan], tracking_chan, distance_x, distance_y)
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 sensorIsAnArrayPad(): bool = True if md.sensor != "": md.setChannelName(getArrayPadChannels()[0]) if md.sensor != md.getSensor(): bool = False return bool
def importAndAddHistogram(TH2_object, index): chan = getArrayPadChannels()[index] md.setChannelName(chan) category = TH2_object.GetTitle() subcategory = "" if category.find("timing") != -1: category, subcategory = category.split("_") THistogram = dm.exportImportROOTHistogram("tracking", category, subcategory) TH2_object.Add(THistogram)
def createArrayPadGraphs(distance_x, distance_y): if not t_calc.sensorIsAnArrayPad(): return 0 distance_x *= 2 distance_y *= 2 xbin = int(2*distance_x/bin_size) ybin = int(2*distance_y/bin_size) # The binning for the timing resolution is decreased xbin_timing = int(xbin/bin_timing_decrease) ybin_timing = int(ybin/bin_timing_decrease) arrayPadChannels = t_calc.getArrayPadChannels() md.setChannelName(arrayPadChannels[0]) th_name = "_"+str(md.getBatchNumber())+"_"+md.chan_name pulse_amplitude_TH2D = ROOT.TProfile2D("pulse_amplitude"+th_name, "pulse_amplitude", xbin, -distance_x, distance_x, ybin, -distance_y, distance_y) gain_TH2D = ROOT.TProfile2D("gain"+th_name, "gain", xbin, -distance_x, distance_x, ybin, -distance_y, distance_y) rise_time_TH2D = ROOT.TProfile2D("rise_time"+th_name, "rise_time", xbin, -distance_x, distance_x, ybin, -distance_y, distance_y) timing_peak_TH2D = ROOT.TH2D("timing_peak"+th_name, "timing_peak", xbin_timing, -distance_x, distance_x, ybin_timing, -distance_y, distance_y) timing_cfd_TH2D = ROOT.TH2D("timing_cfd"+th_name, "timing_cfd", xbin_timing, -distance_x, distance_x, ybin_timing, -distance_y, distance_y) efficiency_TH2D = ROOT.TH2D("efficiency"+th_name, "efficiency", xbin,-distance_x,distance_x,ybin,-distance_y,distance_y) inefficiency_TH2D = ROOT.TH2D("inefficiency"+th_name, "inefficiency", xbin,-distance_x,distance_x,ybin,-distance_y,distance_y) TH2D_objects_list = [pulse_amplitude_TH2D, gain_TH2D, rise_time_TH2D, timing_peak_TH2D, timing_cfd_TH2D, efficiency_TH2D, inefficiency_TH2D] print "\nArray-pad", md.getSensor(), "\n" for TH2D_object in TH2D_objects_list: for index in range(0, len(arrayPadChannels)): # Omit the lower-left pad for timing resolution only for W4-S215 B207 Sep TB17 if TH2D_object.GetName().find("timing") != -1 and arrayPadChannels[index] == "chan3": continue t_calc.importAndAddHistogram(TH2D_object, index) # Change the file names of the exported files (array) t_calc.setArrayPadExportBool(True) setPlotLimitsAndPrint(TH2D_objects_list)
def getTimeDifferencePerRun(time_location): time_difference = np.zeros(len(time_location), dtype = time_location.dtype) SiPM_chan = md.getChannelNameForSensor("SiPM-AFP") for chan in time_location.dtype.names: md.setChannelName(chan) if md.getSensor() == "SiPM-AFP": continue for event in range (0, len(time_location)): if time_location[SiPM_chan][event] != 0 and time_location[chan][event] != 0: time_difference[chan][event] = (time_location[chan][event] - time_location[SiPM_chan][event])*1000 return time_difference
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 produceResults(): global canvas global processed_sensor global bias_voltage_max bias_voltage_max = 350 categories = [ "noise", "pulse_amplitude", "charge", "rise_time", "normal_peak", "system_peak", "normal_cfd", "system_cfd", "normal_peak_gain", "system_peak_gain", "normal_cfd_gain", "system_cfd_gain", "normal_peak_gain_zoom", "system_peak_gain_zoom", "normal_cfd_gain_zoom", "system_cfd_gain_zoom" ] canvas = ROOT.TCanvas("Results", "Results") sensorNames = md.getAvailableSensors() sensorNames.remove("SiPM-AFP") sensorNames.sort() if md.sensor != "": sensorNames = [md.sensor] resultsDict = dict() resultGraphs = dict() legend = dict() print "\nStart RESULTS" zoom = False # loop through each category for category in categories: print "\n", category, "\n" if category.endswith("zoom"): zoom = True category = category[:-5] category_graph = ROOT.TMultiGraph() legend_graph = ROOT.TLegend(0.7, 0.9, 0.9, 0.6) graph = dict() doOnce = True for processed_sensor in sensorNames: print processed_sensor md.defineRunInfo( md.getRowForRunNumber( md.getRunsWithSensor(processed_sensor)[0])) md.setChannelName(md.getChannelNameForSensor(processed_sensor)) graph[processed_sensor] = dict() sensor_data = dict() # Create TGraphErrors for each sensor, temperature and position (in the case of array pads) for temperature in md.getAvailableTemperatures(): graph[processed_sensor][temperature] = dict() sensor_data[temperature] = dict() if processed_sensor == "W4-S204_6e14" and doOnce: graph["W4-S204_6e14"]["22"] = dict() graph["W4-S204_6e14"]["22"]["7_0"] = ROOT.TGraphErrors() r_plot.setMarkerType(graph["W4-S204_6e14"]["22"]["7_0"], DUT_pos, temperature) doOnce = False for DUT_pos in availableDUTPositions(processed_sensor): graph[processed_sensor][temperature][ DUT_pos] = ROOT.TGraphErrors() sensor_data[temperature][DUT_pos] = [] # Change each marker type and color r_plot.setMarkerType( graph[processed_sensor][temperature][DUT_pos], DUT_pos, temperature) importResultsValues(sensor_data, category) r_plot.addValuesToGraph( [sensor_data, category, legend_graph, graph, category_graph]) r_plot.drawAndExportResults(category, category_graph, legend_graph, zoom)
def producePulsePlots(numpy_variables): [ noise, pedestal, pulse_amplitude, rise_time, charge, cfd, peak_time, points, max_sample ] = [i for i in numpy_variables] dm.changeIndexNumpyArray(noise, 1000.0) dm.changeIndexNumpyArray(pedestal, -1000.0) dm.changeIndexNumpyArray(pulse_amplitude, -1000.0) dm.changeIndexNumpyArray(rise_time, 1000.0) dm.changeIndexNumpyArray(charge, 10**15) dm.changeIndexNumpyArray(max_sample, -1000.0) print "\nBATCH", md.getBatchNumber(), "\n" for chan in pulse_amplitude.dtype.names: md.setChannelName(chan) if md.sensor != "" and md.getSensor() != md.sensor: continue # if a fit fails, slightly change the bin number pulse_amplitude_bins = 150 point_count_limit = 50 charge_pulse_bins = 110 rise_time_bins = 150 charge_max = 150 # This is a limit for the point count, to increase it if md.getSensor() == "SiPM-AFP" or md.getSensor() == "W4-RD01": point_count_limit = 100 charge_max = 500 print md.getSensor(), "\n" noise_avg_std = [np.average(noise[chan]), np.std(noise[chan])] pedestal_avg_std = [np.average(pedestal[chan]), np.std(pedestal[chan])] noise_ranges, pedestal_ranges = getRanges(noise_avg_std, pedestal_avg_std, 6) # Create TH objects with properties to be analyzed th_name = "_" + str(md.getBatchNumber()) + "_" + md.chan_name noise_TH1F = ROOT.TH1F("noise" + th_name, "noise", 300, noise_ranges[0], noise_ranges[1]) pedestal_TH1F = ROOT.TH1F("pedestal" + th_name, "pedestal", 300, pedestal_ranges[0], pedestal_ranges[1]) pulse_amplitude_TH1F = ROOT.TH1F("pulse_amplitude" + th_name, "pulse_amplitude", pulse_amplitude_bins, 0, 500) rise_time_TH1F = ROOT.TH1F("rise_time" + th_name, "rise_time", rise_time_bins, 0, 4000) charge_TH1F = ROOT.TH1F("charge" + th_name, "charge", charge_pulse_bins, 0, charge_max) # Additional TH objects for inspection of signals peak_time_TH1F = ROOT.TH1F("peak_time" + th_name, "peak_time", 100, 0, 100) cfd_TH1F = ROOT.TH1F("cfd" + th_name, "cfd", 100, 0, 100) point_count_TH1F = ROOT.TH1F("point_count" + th_name, "point_count", point_count_limit, 0, point_count_limit) max_sample_TH1F = ROOT.TH1F("max_sample" + th_name, "max_sample", 100, 0, 400) max_sample_vs_points_threshold_TH2F = ROOT.TH2F( "max_sample_vs_point_count" + th_name, "max_sample_vs_point_count", point_count_limit, 0, point_count_limit, 100, 0, 400) TH1_objects = [ noise_TH1F, pedestal_TH1F, pulse_amplitude_TH1F, rise_time_TH1F, charge_TH1F, peak_time_TH1F, cfd_TH1F, point_count_TH1F, max_sample_TH1F ] # Fill TH1 objects for index in range(0, len(TH1_objects)): for entry in range(0, len(numpy_variables[index][chan])): if numpy_variables[index][chan][entry] != 0: TH1_objects[index].Fill( numpy_variables[index][chan][entry]) # Fill TH2 object for entry in range(0, len(pulse_amplitude[chan])): if max_sample[chan][entry] != 0 and points[chan][entry] != 0: max_sample_vs_points_threshold_TH2F.Fill( points[entry][chan], max_sample[entry][chan]) # Redefine ranges for noise and pedestal fits ranges_noise_pedestal = getRanges(noise_avg_std, pedestal_avg_std, 3) # Create fits noise_fit, pedestal_fit = makeNoisePedestalFits( noise_TH1F, pedestal_TH1F, ranges_noise_pedestal) pulse_amplitude_langaufit = makeLandauGausFit(pulse_amplitude_TH1F, signal_limit) rise_time_fit = makeRiseTimeFit(rise_time_TH1F) charge_langaufit = makeLandauGausFit(charge_TH1F) # Export plots for TH_obj in TH1_objects: exportHistogram(TH_obj) exportHistogram(max_sample_vs_points_threshold_TH2F)
def printWaveform(runNumber, sensor, event = 0): # Define global variables md.defineRunInfo(md.getRowForRunNumber(runNumber)) dm.defineDataFolderPath() chan = md.getChannelNameForSensor(sensor) md.setChannelName(chan) # Create TMultigraph and define underlying graphs multi_graph = ROOT.TMultiGraph() canvas = ROOT.TCanvas("Waveforms","Waveforms") legend = ROOT.TLegend(0.65, 0.9, 0.9, 0.6) # Import the event from the oscilloscope file data_import = dm.getOscilloscopeData(event, event+1) data = -data_import[chan][0] # Set find noise and pedestal and define the threshold of finding signals timeScope = 0.1 N = 4.27 noise, pedestal = p_calc.calculateNoiseAndPedestal(data) threshold = N * noise + pedestal # Define point difference for second degree fit and maximum signal limit (saturated signals) signal_limit_DUT = 0.3547959 point_difference = 2 # Calculate pulse characteristics (based on the methods from pulse_calculations.py) peak_value, peak_time, poly_fit = p_calc.calculatePulseAmplitude(data, pedestal, signal_limit_DUT, True) rise_time, cfd, linear_fit, linear_fit_indices = p_calc.calculateRiseTime(data, pedestal, True) charge = p_calc.calculateCharge(data, pedestal) point_count = p_calc.calculatePoints(data, threshold) max_sample = np.amax(data) - pedestal # Define ROOT objects for each type of graph graph_waveform = ROOT.TGraph(len(data)) graph_threshold = ROOT.TGraph(2) graph_pulse_amplitude = ROOT.TGraph(2) graph_max_sample = ROOT.TGraph(2) graph_cfd = ROOT.TGraph(2) graph_peak_time = ROOT.TGraph(2) graph_10 = ROOT.TGraph(2) graph_90 = ROOT.TGraph(2) graph_pedestal = ROOT.TGraph(2) graph_noise = ROOT.TGraph(2) graph_linear_fit = ROOT.TGraph(len(linear_fit_indices)) graph_2nd_deg_fit = ROOT.TGraph(point_difference*2+1) # Find points to draw the shade showing the charge pedestal_points = p_calc.getConsecutiveSeries(data, np.argwhere(data > pedestal).flatten()) n = len(pedestal_points)+1 charge_fill = ROOT.TGraph(2*n) fillOnce = True # Draw the waveform and the charge fill for index in range(0, len(data)): graph_waveform.SetPoint(index, index*0.1, data[index]*1000) if index > pedestal_points[0]-1 and fillOnce: for i in range(0, n): charge_fill.SetPoint(i, 0.1 * (i+index), data[i+index] * 1000) charge_fill.SetPoint(n+i, 0.1 * (n-i+index-1), pedestal * 1000) fillOnce = False # Draw the second degree fit first_index = np.argmax(data) - point_difference last_index = np.argmax(data) + point_difference poly_fit_range = np.arange(first_index, last_index, 0.1) i = 0 for index in range(0, len(poly_fit_range)): time = poly_fit_range[index]*timeScope value = poly_fit[0] * np.power(time, 2) + poly_fit[1] * time + poly_fit[2] + pedestal graph_2nd_deg_fit.SetPoint(i, time, value*1000) i += 1 # Draw the linear fit i = 0 for index in range(0, len(linear_fit_indices)): time = linear_fit_indices[index]*timeScope value = linear_fit[0]*time + linear_fit[1] graph_linear_fit.SetPoint(i, time, value*1000) i+=1 # Draw lines (by setting two points at the beginning and the end) graph_threshold.SetPoint(0,0, threshold*1000) graph_threshold.SetPoint(1,1002, threshold*1000) graph_noise.SetPoint(0,0, (noise+pedestal)*1000) graph_noise.SetPoint(1,1002, (noise+pedestal)*1000) graph_pedestal.SetPoint(0,0, pedestal*1000) graph_pedestal.SetPoint(1,1002, pedestal*1000) graph_pulse_amplitude.SetPoint(0,0, peak_value*1000) graph_pulse_amplitude.SetPoint(1,1002, peak_value*1000) graph_max_sample.SetPoint(0,0, max_sample*1000) graph_max_sample.SetPoint(1,1002, max_sample*1000) graph_cfd.SetPoint(0, cfd, -30) graph_cfd.SetPoint(1, cfd, 500) graph_peak_time.SetPoint(0, peak_time, -30) graph_peak_time.SetPoint(1, peak_time, 500) graph_10.SetPoint(0,0, peak_value*0.1*1000) graph_10.SetPoint(1,1002, peak_value*0.1*1000) graph_90.SetPoint(0,0, peak_value*0.9*1000) graph_90.SetPoint(1,1002, peak_value*0.9*1000) # Define line and marker attributes graph_waveform.SetLineWidth(2) graph_waveform.SetMarkerStyle(6) graph_waveform.SetLineColor(2) graph_linear_fit.SetLineWidth(3) graph_linear_fit.SetLineColorAlpha(1, 0.75) graph_linear_fit.SetMarkerColorAlpha(1, 0.0) graph_2nd_deg_fit.SetLineWidth(3) graph_2nd_deg_fit.SetLineColorAlpha(3, 0.75) graph_2nd_deg_fit.SetMarkerColorAlpha(1, 0.0) graph_cfd.SetLineStyle(7) graph_cfd.SetLineColor(8) graph_pulse_amplitude.SetLineColor(4) graph_peak_time.SetLineColor(8) graph_pedestal.SetLineColor(6) graph_noise.SetLineColor(7) graph_threshold.SetLineColor(1) graph_max_sample.SetLineColor(2) graph_10.SetLineColor(7) graph_90.SetLineColor(7) charge_fill.SetFillStyle(3013) charge_fill.SetFillColor(4) # Add the graphs to multigraph multi_graph.Add(graph_waveform) multi_graph.Add(graph_noise) multi_graph.Add(graph_threshold) multi_graph.Add(graph_2nd_deg_fit) multi_graph.Add(graph_linear_fit) multi_graph.Add(graph_pulse_amplitude) multi_graph.Add(graph_max_sample) multi_graph.Add(graph_10) multi_graph.Add(graph_90) multi_graph.Add(graph_cfd) multi_graph.Add(graph_peak_time) multi_graph.Add(graph_pedestal) multi_graph.Add(charge_fill, "f") # Add the information to a legend box legend.AddEntry(graph_waveform, "Waveform " + md.getSensor(), "l") legend.AddEntry(graph_noise, "Noise: "+str(noise*1000)[:4]+" mV", "l") legend.AddEntry(graph_pedestal, "Pedestal: "+str(pedestal*1000)[:4]+" mV", "l") legend.AddEntry(graph_threshold, "Threshold: "+str(threshold*1000)[:5]+" mV", "l") legend.AddEntry(graph_max_sample, "Max sample: "+str(max_sample*1000)[:5]+" mV", "l") legend.AddEntry(graph_waveform, "Points above threshold: "+str(point_count), "l") legend.AddEntry(graph_pulse_amplitude, "Pulse amplitude: "+str(peak_value[0]*1000)[:5]+" mV", "l") legend.AddEntry(graph_peak_time, "Time at peak: " + str(peak_time[0])[0:5] + " ns", "l") legend.AddEntry(graph_linear_fit, "Rise time: "+str(rise_time*1000)[:5]+" ps", "l") legend.AddEntry(graph_90, "10% and 90% limit", "l") legend.AddEntry(graph_cfd, "CFD 0.5: " + str(cfd)[0:5] + " ns", "l") legend.AddEntry(charge_fill, "Charge: "+str(charge*10**15)[:5]+" fC", "f") # Define the titles and draw the graph xAxisTitle = "Time [ns]" yAxisTitle = "Voltage [mV]" headTitle = "Waveform " + md.getSensor() multi_graph.Draw("ALP") legend.Draw() multi_graph.SetTitle(headTitle) multi_graph.GetXaxis().SetTitle(xAxisTitle) multi_graph.GetYaxis().SetTitle(yAxisTitle) # Set ranges on axes multi_graph.GetYaxis().SetRangeUser(-30,350) multi_graph.GetXaxis().SetRangeUser(cfd-5,cfd+5) # Export the PDF file fileName = dm.getPlotsSourceFolder()+"/waveforms/waveform"+"_"+str(md.getBatchNumber())+"_"+str(runNumber)+"_event_"+str(event)+"_"+str(sensor)+".pdf" canvas.Print(fileName) print "PDF produced at", fileName+"."
def printTHPlot(graphList, entries=0): canvas.cd() if entries == 0: entries = graphList.GetEntries() # Get file name and title for graph category = graphList.GetTitle() headTitle = getTitles(category) # Move the margins to fit the Z axis canvas.SetRightMargin(0.14) canvas.SetLeftMargin(0.11) # Draw graph to move and recreate the stats box graphList.SetStats(1) graphList.Draw("COLZ0") canvas.Update() stats_box = graphList.GetListOfFunctions().FindObject("stats") stats_box.SetX1NDC(.11) stats_box.SetX2NDC(.25) stats_box.SetY1NDC(.93) stats_box.SetY2NDC(.83) stats_box.SetOptStat(1000000010) graphList.SetEntries(entries) # Draw again to update the canvas graphList.SetTitle(headTitle) graphList.Draw("COLZ0") # Draw lines which selects the projection limits efficiency_bool = True if category.find("efficiency") != -1 and category.find("inefficiency") == -1 else False if efficiency_bool: # This prints the selections for the efficiency bulk calculations if md.checkIfArrayPad() and t_calc.array_pad_export: channels = t_calc.getArrayPadChannels() else: channels = [md.chan_name] lines = dict() efficiency_text = dict() for chan_2 in channels: md.setChannelName(chan_2) lines[chan_2] = t_calc.definelines(0) # the argument extends the lines in [um] limits, center_position = t_calc.findSelectionRange() efficiency_bulk = dm.exportImportROOTData("tracking", "efficiency") efficiency_text[chan_2] = ROOT.TLatex(center_position[0]-250,center_position[1], "Eff = " + str(efficiency_bulk[chan_2][0])[0:5] + " \pm " + str(efficiency_bulk[chan_2][1])[0:4] + " %") efficiency_text[chan_2].SetNDC(False) if md.checkIfArrayPad(): efficiency_text[chan_2].SetTextSize(0.02) else: efficiency_text[chan_2].SetTextSize(0.04) efficiency_text[chan_2].Draw() # Draw lines which marks the bulk # Y-lines lines[chan_2][0].Draw() lines[chan_2][1].Draw() # X-lines lines[chan_2][2].Draw() lines[chan_2][3].Draw() canvas.Update() # Export PDF and Histogram canvas.Update() subcategory = "" if category.find("timing") != -1: category, subcategory = category.split("_") fileName = dm.getFileNameForHistogram("tracking", category, subcategory) canvas.Print(fileName) dm.exportImportROOTHistogram("tracking", category, subcategory, "", graphList) canvas.Clear()
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)