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"
Example #2
0
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
Example #3
0
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
Example #10
0
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")
Example #11
0
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)
Example #12
0
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
Example #13
0
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
Example #14
0
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+"."
Example #21
0
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)