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 getFileNameForHistogram(group, category, subcategory="", chan2=""): # pulse all types fileName = getPlotsSourceFolder() + "/" + md.getSensor( ) + "/" + group + "/" + category + "/" + group + "_" + category + "_" + str( md.getBatchNumber()) + "_" + md.chan_name ending = "_" + md.getSensor() + ".pdf" # timing resolution if subcategory != "": if md.checkIfSameOscAsSiPM(): getOscText = "same_osc" else: getOscText = "diff_osc" # timing normal fileName = fileName.replace(category + "/", category + "/" + subcategory + "/", 1) ending = "_" + md.getSensor( ) + "_" + getOscText + "_" + subcategory + ".pdf" # sys eq if chan2 != "": ending = "_" + md.getSensor() + "_and_" + md.getSensor( chan2) + "_" + subcategory + ".pdf" # For array-pad tracking plots if group == "tracking" and t_calc.array_pad_export: ending = ending.replace(".pdf", "_array.pdf") return fileName + ending
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 getDistanceFromCenterArrayPad(position): arrayPadChannels = getArrayPadChannels() numberOfPads = len(position[arrayPadChannels][0]) if md.getSensor() == "W4-S204_6e14": numberOfPads += 1 pos_pad = np.zeros((numberOfPads, 2)) for pad_no in range(0, numberOfPads): for dim in range(0, 2): # This is a hand-waving fix for the missing pad, to temporarily calculate the average value to find the center of the three (four) pads if md.getSensor() == "W4-S204_6e14" and pad_no == 3: pos_pad[pad_no][0] = position[arrayPadChannels][0][1][0] pos_pad[pad_no][1] = position[arrayPadChannels][0][0][1] else: pos_pad[pad_no][dim] = position[arrayPadChannels][0][pad_no][ dim] centerArrayPadPosition = np.average(pos_pad, axis=0) newArrayPositions = np.zeros(1, dtype=dm.getDTYPETrackingPosition()) if md.getSensor() == "W4-S204_6e14": numberOfPads -= 1 for index in range(0, numberOfPads): chan2 = arrayPadChannels[index] newArrayPositions[chan2][0] = pos_pad[index] - centerArrayPadPosition return newArrayPositions
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 getPlotAttributes(chan2=""): # Set titles and export file headTitle = "Time difference "+md.getSensor()+" and SiPM, T = "+str(md.getTemperature()) + " \circ"+"C, " + "U = "+str(md.getBiasVoltage()) + " V" xAxisTitle = "\Deltat [ps]" yAxisTitle = "Entries" if chan2 != "": # Create titles and print the graph headTitle = "Time difference "+md.getSensor()+" and "+md.getSensor(chan2)+", T = "+str(md.getTemperature()) + " \circ"+"C, " + "U = "+str(md.getBiasVoltage()) + " V" return [headTitle, xAxisTitle, yAxisTitle]
def getThresholdSamples(chan): sensor = md.getSensor(chan) if sensor == "50D-GBGR2" or sensor == "W9-LGA35": samples_limit = 5 elif sensor == "SiPM-AFP": samples_limit = 36 elif sensor == "W4-RD01": samples_limit = 32 elif sensor == "W4-S1022": samples_limit = 7 elif sensor == "W4-LG12" or sensor == "W4-S1061" or sensor == "W4-S203" or sensor == "W4-S215": samples_limit = 8 elif sensor == "W4-S204_6e14": samples_limit = 3 else: samples_limit = 0 return samples_limit
def exportTHPlot(graphList, sigma, category, chan2 = ""): canvas.Clear() titles = getPlotAttributes(chan2) graphList.SetLineColor(1) graphList.SetTitle(titles[0]) graphList.GetXaxis().SetTitle(titles[1]) graphList.GetYaxis().SetTitle(titles[2]) ROOT.gStyle.SetOptFit(0012) ROOT.gStyle.SetOptStat("ne") graphList.Draw() canvas.Update() # Remove text from stats box statsBox = canvas.GetPrimitive("stats") statsBox.SetName("Mystats") graphList.SetStats(0) statsBox.AddText("\sigma_{"+md.getSensor()+"} = "+str(sigma[0])[0:6] + " \pm " + str(sigma[1])[0:4]) canvas.Modified() canvas.Update() category, subcategory = getCategorySubcategory(category) fileName = dm.getFileNameForHistogram("timing", category, subcategory, chan2) canvas.Print(fileName) dm.exportImportROOTHistogram("timing", category, subcategory, chan2, graphList)
def sensorIsAnArrayPad(): bool = True if md.sensor != "": md.setChannelName(getArrayPadChannels()[0]) if md.sensor != md.getSensor(): bool = False return bool
def exportImportROOTData(group, category, data=np.empty(0)): dataPath = getDataSourceFolder() + "/" + group + "/" # This line is for exporting data and adapting correct dtype for the numpy array if group != "timing" and group != "results" and category != "position" and data.size != 0: data = data.astype(getDTYPE()) if group == "tracking": if category == "tracking": fileName = category + str(md.getTimeStamp()) elif category == "efficiency": fileName = category + "_" + str(md.getBatchNumber( )) + "_" + md.getSensor() + "_" + md.chan_name # Position else: fileName = category + "_" + str(md.getBatchNumber() / 100) dataPath += category + "/" + fileName + ".root" elif group == "timing": category, subcategory = category.split("_") fileName = group + "_" + category + "_" + subcategory + "_" + str( md.getRunNumber()) dataPath += category + "/" + subcategory + "/" + fileName + ".root" else: fileName = group + "_" + category + "_" + str(md.getRunNumber()) dataPath += category + "/" + fileName + ".root" # read the file if data.size == 0: try: return rnm.root2array(dataPath) except IOError as e: print "\nFile", fileName + ".root", "not found!\n" return np.zeros(1) # export the file else: rnm.array2root(data, dataPath, mode="recreate")
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 getTitles(objectTitle): headTitle = "empty" if objectTitle.find("pulse_amplitude") != -1: graphTitle = "Pulse amplitude mean value" dimension_z = "V [mV]" elif objectTitle.find("gain") != -1: graphTitle = "Gain mean value" dimension_z = "Gain" elif objectTitle.find("rise_time") != -1: graphTitle = "Rise time mean value" dimension_z = "t [ps]" elif objectTitle.find("peak") != -1: graphTitle = "Timing resolution (peak)" dimension_z = "\sigma_{t} [ps]" elif objectTitle.find("cfd") != -1: graphTitle = "Timing resolution (CFD)" dimension_z = "\sigma_{t} [ps]" elif objectTitle.find("efficiency") != -1: graphTitle = "Efficiency" dimension_z = "Efficiency (%)" elif objectTitle.find("inefficiency") != -1: graphTitle = "Inefficiency" dimension_z = "Inefficiency (%)" headTitle = graphTitle + " - " + md.getSensor() + ", T = " + str(md.getTemperature()) + " \circ C, U = " + str(md.getBiasVoltage()) + " V; X [\mum] ; Y [\mum] ; " + dimension_z return headTitle
def produceProjectionPlots(projectionX_th1d, projectionY_th1d): distance_projection, center_positions = t_calc.findSelectionRange() headTitle = "Projection on X-axis of efficiency 2D plot - "+md.getSensor()+", T = "+str(md.getTemperature()) + " \circ"+"C, " + "U = "+str(md.getBiasVoltage()) + " V"+ "; X [\mum] ; Efficiency (%)" category = projectionX_th1d.GetTitle() fileName = dm.getFileNameForHistogram("tracking", category) sigmas_x = createProjectionFit(projectionX_th1d, center_positions[0]) printProjectionPlot(projectionX_th1d, headTitle, fileName, sigmas_x) headTitle = headTitle.replace("X-axis", "Y-axis") headTitle = headTitle.replace("X [\mum]", "Y [\mum]") category = projectionY_th1d.GetTitle() fileName = dm.getFileNameForHistogram("tracking", category) sigmas_y = createProjectionFit(projectionY_th1d, center_positions[1]) printProjectionPlot(projectionY_th1d, headTitle, fileName, sigmas_y)
def findSelectionRange(): position = dm.exportImportROOTData("tracking", "position") # In case of array pads, refer to the center of the pad if md.checkIfArrayPad(): center_position = ( getDistanceFromCenterArrayPad(position)[md.chan_name])[0] # otherwise, refer from origo with predefined structured array else: center_position = (np.zeros( 1, dtype=dm.getDTYPETrackingPosition())[md.chan_name])[0] # Distance from the center of the pad and 350 um from the center projection_cut = 350 # This is a manual correction for the irradiated array pad to center the selection of the bulk for efficiency calculation if md.getSensor() == "W4-S204_6e14": if md.chan_name == "chan5": center_position = [-540, -550] elif md.chan_name == "chan6": center_position = [530, 530] elif md.chan_name == "chan7": center_position = [-540, 530] x1 = center_position[0] - projection_cut x2 = center_position[0] + projection_cut y1 = center_position[1] - projection_cut y2 = center_position[1] + projection_cut return np.array([[x1, x2], [y1, y2]]), center_position
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 getPlotAttributes(category): yAxisTitle = "Entries" if category == "max_sample_vs_point_count": head_title_type = "Maximum sample value vs number of samples above the threshold" xAxisTitle = "Number of samples [N]" yAxisTitle = "Maximum sample value [mV]" elif category == "noise": head_title_type = "Noise" xAxisTitle = "Standard deviation [mV]" elif category == "pedestal": head_title_type = "Pedestal" xAxisTitle = "Average value [mV]" elif category == "pulse_amplitude": head_title_type = "Pulse amplitude" xAxisTitle = "Pulse amplitude [mV]" elif category == "cfd": head_title_type = "CFD time location" xAxisTitle = "Time location [ns]" elif category == "peak_time": head_title_type = "Peak time location" xAxisTitle = "Time location [ns]" elif category == "charge": head_title_type = "Charge distribution" xAxisTitle = "Charge [fC]" elif category == "max_sample": head_title_type = "Maximum sample value above the threshold" xAxisTitle = "Maximum sample value [mV]" elif category == "rise_time": head_title_type = "Rise time" xAxisTitle = "Rise time [ps]" elif category == "point_count": head_title_type = "Samples above the threshold" xAxisTitle = "Number of samples [N]" headTitle = head_title_type + " - " + md.getSensor() + ", T = " + str( md.getTemperature()) + " \circ" + "C, " + "U = " + str( md.getBiasVoltage()) + " V" fileName = dm.getFileNameForHistogram("pulse", category) return [headTitle, xAxisTitle, yAxisTitle, fileName]
def rotateSensor(tracking): theta = 0.0 sensor = md.getSensor() batchGroup = md.getBatchNumber() / 100 # Rotation for W4-S204_6e14 if sensor == "W9-LGA35" and batchGroup == 2: theta = 0.1 elif sensor == "W4-LG12" and batchGroup == 2: theta = 0 elif sensor == "W4-RD01": theta = 0.0 if batchGroup == 3: theta = 0.2 elif sensor == "W4-S1022": theta = 0.2 if batchGroup == 5: theta = 0.1 elif batchGroup == 7: theta = 0.1 elif sensor == "W4-S1061": theta = 0.2 if batchGroup == 5: theta = 0.15 elif batchGroup == 7: theta = 0.15 elif sensor == "W4-S203": theta = -0.1 if batchGroup == 5: theta = -0.2 elif batchGroup == 7: theta = -0.5 elif sensor == "W4-S204_6e14": theta = 4.45 elif sensor == "W4-S215": theta = 0.55 if batchGroup == 5: theta = 0.8 elif batchGroup == 7: theta = 0.8 if theta != 0.0: # Use the rotation matrix around z theta *= np.pi / 180.0 tracking["X"] = np.multiply(tracking["X"], np.cos(theta)) - np.multiply( tracking["Y"], np.sin(theta)) tracking["Y"] = np.multiply(tracking["X"], np.sin(theta)) + np.multiply( tracking["Y"], np.cos(theta))
def getCenterOfSensor(pulse_amplitude, tracking): bin_size = 18.4 # This has to be changed to match the MIMOSA! These are ranges in um. [xmin, xmax, ymin, ymax, minEntries] = [-7000, 8000, 9000, 16000, 5] # Choose ranges to center the DUTs depending on batch (the SiPM is larger, therefore the mean values of it are not exact) if md.getBatchNumber() == 101 or md.getBatchNumber() == 207: [xmin, xmax, ymin, ymax, minEntries] = [-1500, 1500, 11500, 14500, 8] elif md.getBatchNumber() == 306: [xmin, xmax, ymin, ymax, minEntries] = [-4500, -1000, 11000, 14000, 20] elif md.getBatchNumber() == 401: [xmin, xmax, ymin, ymax, minEntries] = [-4000, -1200, 10500, 13500, 5] elif md.getBatchNumber() == 507 or md.getBatchNumber() == 707: [xmin, xmax, ymin, ymax, minEntries] = [-3000, 800, 10000, 13500, 5] if md.getBatchNumber() == 707: bin_size *= 2 elif md.getBatchNumber() == 601: [xmin, xmax, ymin, ymax, minEntries] = [-1500, 500, 10000, 13000, 20] xbin = int((xmax - xmin) / bin_size) ybin = int((ymax - ymin) / bin_size) passed_th2d = ROOT.TH2D("passed", "passed", xbin, xmin, xmax, ybin, ymin, ymax) total_th2d = ROOT.TH2D("total", "total", xbin, xmin, xmax, ybin, ymin, ymax) # Fill the events for event in range(0, len(tracking)): if tracking['X'][event] > -9990 and tracking['Y'][event] > -9990: total_th2d.Fill(tracking['X'][event], tracking['Y'][event], 1) if pulse_amplitude[event] != 0: passed_th2d.Fill(tracking['X'][event], tracking['Y'][event], 1) # Remove bins with less than some entries for i in range(1, xbin + 1): for j in range(1, ybin + 1): bin = passed_th2d.GetBin(i, j) num_passed = float(passed_th2d.GetBinContent(bin)) num_total = float(total_th2d.GetBinContent(bin)) if num_total != 0.0: if 0.8 > (num_passed / num_total): passed_th2d.SetBinContent(bin, 0) total_th2d.SetBinContent(bin, 0) if num_total < minEntries: passed_th2d.SetBinContent(bin, 0) total_th2d.SetBinContent(bin, 0) passed_th2d.ResetStats() total_th2d.ResetStats() efficiency = ROOT.TEfficiency(passed_th2d, total_th2d) efficiency.Draw("COLZ") canvas_center.Update() efficiency_th2d = efficiency.GetPaintedHistogram() # Calculate the mean value for each DUT position_temp = np.array( [efficiency_th2d.GetMean(), efficiency_th2d.GetMean(2)]) line_distance = 700 line_x = ROOT.TLine(position_temp[0], position_temp[1] - line_distance, position_temp[0], position_temp[1] + line_distance) line_y = ROOT.TLine(position_temp[0] - line_distance, position_temp[1], position_temp[0] + line_distance, position_temp[1]) # Print the graph to estimate if the positions are correct canvas_center.Clear() efficiency_th2d.SetStats(1) efficiency_th2d efficiency_th2d.Draw("COLZ") line_x.Draw("same") line_y.Draw("same") canvas_center.Update() filePath = dm.getSourceFolderPath() + "/position_" + str(md.getBatchNumber( )) + "_" + md.getSensor() + "_" + md.chan_name + ".pdf" # If one wants to see where the center is, comment this line and change filePath #canvas_center.Print(filePath) canvas_center.Clear() return position_temp
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 getLimitsForEachSensorAndBatch(): if md.getSensor() == "50D-GBGR2" and md.getBatchNumber() == 108: # not used gain_limits = [20, 70] pulse_amplitude_limits = [100, 160] rise_time_limits = [400, 460] timing_res_cfd_limits = [22, 40] timing_res_peak_limits = [27, 47] elif md.getSensor() == "50D-GBGR2" and md.getBatchNumber() == 207: gain_limits = [20, 55] pulse_amplitude_limits = [80, 150] rise_time_limits = [415, 450] # not used timing_res_cfd_limits = [22, 60] timing_res_peak_limits = [25, 65] elif md.getSensor() == "W4-LG12" and md.getBatchNumber() == 108: # not used gain_limits = [20, 90] pulse_amplitude_limits = [60, 160] rise_time_limits = [520, 560] timing_res_cfd_limits = [18, 32] timing_res_peak_limits = [30, 46] elif md.getSensor() == "W4-LG12" and md.getBatchNumber() == 207: gain_limits = [30, 90] pulse_amplitude_limits = [60, 155] rise_time_limits = [520, 565] # not used timing_res_cfd_limits = [20, 70] timing_res_peak_limits = [25, 75] elif md.getSensor() == "W4-RD01" and md.getBatchNumber() == 306: gain_limits = [340, 500] pulse_amplitude_limits = [160, 310] rise_time_limits = [820, 870] timing_res_peak_limits = [58, 80] timing_res_cfd_limits = [20, 40] elif md.getSensor() == "W4-RD01" and md.getBatchNumber() == 601: gain_limits = [330, 440] pulse_amplitude_limits = [140, 240] rise_time_limits = [1220, 1300] timing_res_peak_limits = [70, 130] timing_res_cfd_limits = [30, 75] elif md.getSensor() == "W4-S1022" and md.getBatchNumber() == 306: gain_limits = [20, 38] pulse_amplitude_limits = [35, 75] rise_time_limits = [600, 680] timing_res_peak_limits = [90, 160] timing_res_cfd_limits = [30, 80] elif md.getSensor() == "W4-S1022" and md.getBatchNumber() == 507: gain_limits = [40, 85] pulse_amplitude_limits = [80, 160] rise_time_limits = [505, 545] timing_res_peak_limits = [90, 160] timing_res_cfd_limits = [30, 80] elif md.getSensor() == "W4-S1022" and md.getBatchNumber() == 707: gain_limits = [35, 85] pulse_amplitude_limits = [80, 160] rise_time_limits = [525, 565] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S1061" and md.getBatchNumber() == 306: gain_limits = [35, 65] pulse_amplitude_limits = [85, 145] rise_time_limits = [490, 530] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S1061" and md.getBatchNumber() == 507: gain_limits = [35, 65] pulse_amplitude_limits = [80, 140] rise_time_limits = [535, 570] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S1061" and md.getBatchNumber() == 707: gain_limits = [30, 80] pulse_amplitude_limits = [70, 150] rise_time_limits = [540, 575] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S203" and md.getBatchNumber() == 306: gain_limits = [40, 70] pulse_amplitude_limits = [90, 150] rise_time_limits = [515, 540] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S203" and md.getBatchNumber() == 507: gain_limits = [35, 70] pulse_amplitude_limits = [50, 100] rise_time_limits = [680, 780] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S203" and md.getBatchNumber() == 707: gain_limits = [40, 80] pulse_amplitude_limits = [50, 110] rise_time_limits = [725, 830] timing_res_cfd_limits = [30, 80] timing_res_peak_limits = [90, 160] elif md.getSensor() == "W4-S204_6e14" and md.getBatchNumber() == 507: gain_limits = [5, 26] pulse_amplitude_limits = [40, 80] rise_time_limits = [315, 390] timing_res_cfd_limits = [36, 60] timing_res_peak_limits = [44, 65] elif md.getSensor() == "W4-S204_6e14" and md.getBatchNumber() == 707: gain_limits = [8, 28] pulse_amplitude_limits = [40, 90] rise_time_limits = [315, 390] timing_res_cfd_limits = [34, 48] timing_res_peak_limits = [40, 60] elif md.getSensor() == "W4-S215" and md.getBatchNumber() == 207: gain_limits = [70, 130] pulse_amplitude_limits = [160, 260] rise_time_limits = [490, 540] timing_res_cfd_limits = [25, 45] timing_res_peak_limits = [25, 50] elif md.getSensor() == "W4-S215" and md.getBatchNumber() == 507: gain_limits = [110, 210] pulse_amplitude_limits = [200, 330] rise_time_limits = [520, 590] # not used timing_res_cfd_limits = [20, 70] timing_res_peak_limits = [30, 80] elif md.getSensor() == "W4-S215" and md.getBatchNumber() == 707: gain_limits = [100, 190] pulse_amplitude_limits = [140, 240] rise_time_limits = [700, 825] # not used timing_res_cfd_limits = [20, 70] timing_res_peak_limits = [30, 80] elif md.getSensor() == "W9-LGA35" and md.getBatchNumber() == 108: # not used gain_limits = [30, 50] pulse_amplitude_limits = [60, 110] rise_time_limits = [415, 455] timing_res_cfd_limits = [20, 40] timing_res_peak_limits = [24, 46] elif md.getSensor() == "W9-LGA35" and md.getBatchNumber() == 207: gain_limits = [20, 45] pulse_amplitude_limits = [50, 115] rise_time_limits = [415, 455] # not used timing_res_cfd_limits = [20, 70] timing_res_peak_limits = [25, 75] # This method is modular to adapt for other batches else: th_name = "_"+str(md.getBatchNumber())+"_"+md.chan_name pulse_amplitude_mean = dm.exportImportROOTHistogram("pulse", "pulse_amplitude").GetFunction("Fitfcn_pulse_amplitude"+th_name).GetParameter(1) gain_mean = dm.exportImportROOTHistogram("pulse", "charge").GetFunction("Fitfcn_charge"+th_name).GetParameter(1)/md.getChargeWithoutGainLayer() rise_time_mean = dm.exportImportROOTHistogram("pulse", "rise_time").GetFunction("gaus").GetParameter(1) timing_res_peak_mean = np.sqrt(np.power(dm.exportImportROOTHistogram("timing", "normal", "peak").GetFunction("gaus").GetParameter(2),2) - np.power(md.getSigmaSiPM(),2)) timing_res_cfd_mean = np.sqrt(np.power(dm.exportImportROOTHistogram("timing", "normal", "cfd").GetFunction("gaus").GetParameter(2), 2) - np.power(md.getSigmaSiPM(), 2)) [pulse_amplitude_limits, gain_limits, rise_time_limits, timing_res_peak_limits, timing_res_cfd_limits] = [[max(pulse_amplitude_mean-50,0), pulse_amplitude_mean+50], [max(gain_mean-30, 0), gain_mean+30], [max(rise_time_mean-30, 0), rise_time_mean+30], [max(timing_res_peak_mean-50, 0), timing_res_peak_mean+50], [max(timing_res_cfd_mean-50, 0), timing_res_cfd_mean+50]] limits_graph = [pulse_amplitude_limits, gain_limits, rise_time_limits, timing_res_peak_limits, timing_res_cfd_limits] return limits_graph
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)