def makeSubEventAccumulators(chsubevents,
                             gatehalfwidth,
                             opdata,
                             pmtspe,
                             HGSLOT=5,
                             LGSLOT=6):
    """
    inputs
    ------
    chsubevents: dict of {int:list of ChannelSubEvent} for {FEM CH:subevent list}
    gatehalfwidth: int
    opdata: concrete instance of pylard.opdataplottable
    pmtspe: dict of {int:float} for {FEM CH:spe max amp mean}

    outputs
    -------
    nch_acc: numpy array
    npe_acc: numpy array
    """

    nch_acc = np.zeros(opdata.getNBeamWinSamples(),
                       dtype=np.int)  # for number of hits
    npe_acc = np.zeros(opdata.getNBeamWinSamples(),
                       dtype=np.float)  # for number of pe
    hgwfms = opdata.getData(slot=HGSLOT)
    lgwfms = opdata.getData(slot=LGSLOT)
    for ch, chsubevent in chsubevents.items():
        hgped = ped.getpedestal(hgwfms[:, ch], 10, 10)
        lgped = ped.getpedestal(lgwfms[:, ch], 10, 10)
        if ch in pmtspe:
            chspe = pmtspe[ch]
        else:
            chspe = 1.0

        gainfactor = 1.0
        chped = hgped
        #print  hgwfms[:,ch]
        if np.max(hgwfms[:, ch]) >= 4094:
            gainfactor = 10.0
            chped = lgped
        if chped is None:
            chped = 2048.0

        # fill accumulator
        for subevent in chsubevent:
            pe = gainfactor * float(subevent.maxamp - chped) / float(chspe)
            nch_acc[np.maximum(0, subevent.tstart - gatehalfwidth):np.
                    minimum(opdata.getNBeamWinSamples(), subevent.tstart +
                            gatehalfwidth)] += 1
            npe_acc[np.maximum(0, subevent.tstart - gatehalfwidth):np.
                    minimum(opdata.getNBeamWinSamples(), subevent.tstart +
                            gatehalfwidth)] += pe
    return nch_acc, npe_acc
def runSubEventDiscChannel(waveform, config, ch, retpostwfm=False):
    """
    Multiple pass strategy.
    (1) Find peaks using CFD
    (2) Pick biggest peak
    (3) Define expected signal using fast and slow fractions
    (4) Define start and end this way
    (5) Subtract off subevent
    (6) Repeat (1)-(5) until all disc. peaks are below threshold
    * Note this is time hog now *
    """

    subevents = []

    # build configuration
    config.fastconst = 20.0
    config.sigthresh = 3.0
    cdfthresh = config.threshold
    cfdconf = cfd.cfdiscConfig(config.discrname,
                               threshold=cdfthresh,
                               deadtime=config.deadtime,
                               delay=config.delay,
                               width=config.width)
    cfdconf.pedestal = ped.getpedestal(
        waveform, config.pedsamples,
        config.pedmaxvar)  # will have to calculate this at some point
    if cfdconf.pedestal is None:
        return subevents  # empty -- bad baseline!
    cfdconf.nspersample = 15.625
    #print pbin1, config.fastconst, config.slowconst

    # make our working copy of the waveform
    wfm = np.copy(waveform)

    # find subevent
    maxsubevents = 20
    nsubevents = 0
    while nsubevents < maxsubevents:
        # find subevent
        subevent = findOneSubEvent(wfm, cfdconf, config, ch)
        if subevent is not None:
            subevents.append(subevent)
        else:
            break
        # subtract waveform below subevent threshold
        for (t, fx) in subevent.expectation:

            sig = np.sqrt(fx / 20.0)  # units of pe
            thresh = fx + 3.0 * sig * 20.0  # 3 sigma times pe variance

            #if fx*config.sigthresh > wfm[t]-config.pedestal:
            if wfm[t] - cfdconf.pedestal < thresh:
                wfm[t] = cfdconf.pedestal
        nsubevents += 1
        #break

    if retpostwfm:
        return subevents, wfm
    else:
        return subevents
def runSubEventDiscChannel( waveform, config, ch, retpostwfm=False ):
    """
    Multiple pass strategy.
    (1) Find peaks using CFD
    (2) Pick biggest peak
    (3) Define expected signal using fast and slow fractions
    (4) Define start and end this way
    (5) Subtract off subevent
    (6) Repeat (1)-(5) until all disc. peaks are below threshold
    * Note this is time hog now *
    """
    

    subevents = []

    # build configuration
    config.fastconst = 20.0
    config.sigthresh = 3.0
    cdfthresh = config.threshold
    cfdconf = cfd.cfdiscConfig( config.discrname, threshold=cdfthresh, deadtime=config.deadtime, delay=config.delay, width=config.width )
    cfdconf.pedestal = ped.getpedestal( waveform, config.pedsamples, config.pedmaxvar )  # will have to calculate this at some point
    if cfdconf.pedestal is None:
        return subevents # empty -- bad baseline!
    cfdconf.nspersample = 15.625
    #print pbin1, config.fastconst, config.slowconst

    # make our working copy of the waveform
    wfm = np.copy( waveform )

    # find subevent
    maxsubevents = 20
    nsubevents = 0
    while nsubevents<maxsubevents:
        # find subevent
        subevent = findOneSubEvent( wfm, cfdconf, config, ch )
        if subevent is not None:
            subevents.append(subevent)
        else:
            break
        # subtract waveform below subevent threshold
        for (t,fx) in subevent.expectation:
            
            sig = np.sqrt( fx/20.0 ) # units of pe
            thresh =  fx + 3.0*sig*20.0 # 3 sigma times pe variance

            #if fx*config.sigthresh > wfm[t]-config.pedestal:
            if wfm[t]-cfdconf.pedestal < thresh:
                wfm[t] = cfdconf.pedestal
        nsubevents += 1
        #break

    if retpostwfm:
        return subevents, wfm
    else:
        return subevents
def makeSubEventAccumulators( chsubevents, gatehalfwidth, opdata, pmtspe, HGSLOT=5, LGSLOT=6 ):
    """
    inputs
    ------
    chsubevents: dict of {int:list of ChannelSubEvent} for {FEM CH:subevent list}
    gatehalfwidth: int
    opdata: concrete instance of pylard.opdataplottable
    pmtspe: dict of {int:float} for {FEM CH:spe max amp mean}

    outputs
    -------
    nch_acc: numpy array
    npe_acc: numpy array
    """
    
    nch_acc = np.zeros( opdata.getNBeamWinSamples(), dtype=np.int ) # for number of hits
    npe_acc = np.zeros( opdata.getNBeamWinSamples(), dtype=np.float ) # for number of pe
    hgwfms = opdata.getData( slot=HGSLOT )
    lgwfms = opdata.getData( slot=LGSLOT )
    for ch,chsubevent in chsubevents.items():
        hgped = ped.getpedestal(hgwfms[:,ch], 10, 10)
        lgped = ped.getpedestal(lgwfms[:,ch], 10, 10)
        if ch in pmtspe:
            chspe = pmtspe[ch]
        else:
            chspe = 1.0

        gainfactor = 1.0
        chped = hgped
        #print  hgwfms[:,ch]
        if np.max( hgwfms[:,ch] )>=4094:
            gainfactor = 10.0
            chped = lgped
        if chped is None:
            chped = 2048.0

        # fill accumulator
        for subevent in chsubevent:
            pe = gainfactor*float( subevent.maxamp - chped )/float(chspe)
            nch_acc[ np.maximum(0,subevent.tstart-gatehalfwidth) : np.minimum( opdata.getNBeamWinSamples(), subevent.tstart+gatehalfwidth ) ] += 1
            npe_acc[ np.maximum(0,subevent.tstart-gatehalfwidth) : np.minimum( opdata.getNBeamWinSamples(), subevent.tstart+gatehalfwidth ) ] += pe
    return nch_acc, npe_acc
def prepWaveforms( opdata, RC, fA,hgslot, lgslot, boundarysubevent=None, doit=True ):
    """ prep beam windows. we do baseline and charge corrections. """
    #RC = 80000.0 # ns
    #fA = 5.0
    #hgslot = 5
    #lgslot = 6

    wfms = {} # will store waveforms for each channel
    qs   = {} # will store charge correction  for each channel

    nlargest = 0
    for ch in range(0,48):
        wins = opdata.getBeamWindows( hgslot, ch )
        if len(wins)>0 and len(wins[0].wfm)>0:
            if len(wins[0].wfm)>nlargest:
                nlargest = len(wins[0].wfm)
            wfms[ch] = np.ones( len(wins[0].wfm), dtype=np.float )
            qs[ch]   = np.zeros( len(wins[0].wfm), dtype=np.float )

    # if boundary subevent, we need to calculate corrections to the first part of the waveforms
    boundary_corrections = {}
    if boundarysubevent is not None:
        opdata.suppressed_wfm = {}
        for flash in boundarysubevent.getFlashList():
            ch = flash.ch
            t = flash.tstart
            wfm = flash.expectation
            qcorr = [0.0]
            for i in range(1,len(wfm)):
                q = fA*wfm[i]*(15.625/RC) + qcorr[i-1]*np.exp( -1.0*15.625/RC )
                qcorr.append( q )
            # keep going until q runs out
            while qcorr[-1]>1.0:
                q = qcorr[-1]*np.exp( -1.0*15.625/RC )
                qcorr.append( q )
            print "channel ",ch," has a boundary correction of length ",len(qcorr)," starting from time=",t
            boundary_corrections[ch] = ( t, np.asarray( qcorr, dtype=np.float ) )

    # get waveforms, swap for low if needed, remove pedestal
    for ch in range(0,32):
        scale = 1.0
        beamwfm = opdata.getBeamWindows( hgslot, ch )[0]
        if np.max( beamwfm.wfm )<4090:
            wfms[ch] = beamwfm.wfm
        else:
            lgwins = opdata.getBeamWindows( lgslot, ch )
            print "swap HG ch",ch," with LG wfm",lgwins
            lgwfm = lgwins[0].wfm
            
            lgped = getpedestal( lgwfm, 20, 10.0 )
            if lgped is None:
                #print "ch ",ch," LG has bad ped"
                lgped = lgwfm[0]
            opdata.getBeamWindows( lgslot, ch )[0].wfm -= lgped
            opdata.getBeamWindows( lgslot, ch )[0].wfm *= 10.0

            wfms[ch] = opdata.getBeamWindows( lgslot, ch )[0].wfm
            #print "lg ped=",lgped," wfm[0-10]=",np.mean( wfms[0:10,ch] )
            scale = 10.0
        # charge correction due to cosmic disc windows
        tlen = 1
        if ch in boundary_corrections.keys():
            # charge correction
            tstart = boundary_corrections[ch][0]
            qcorr = boundary_corrections[ch][1]
            t1 = -tstart
            tlen = np.minimum( len(qcorr)-t1, len(wfms[ch]) )
            #print "apply boundary correction to ch ",ch,": tstart=",tstart," covering=[0,",tlen,"]"
            if tlen>0:
                wfms[ch][:tlen] += qcorr[t1:t1+tlen] # boundary charge correction
            # supress early subevent..
            if boundarysubevent.tend_sample>0 and boundarysubevent.tend_sample<len(wfms[ch]):
                ped = getpedestal( wfms[ch][boundarysubevent.tend_sample:], 20, 0.5 )
                tlen = np.minimum( boundarysubevent.tend_sample, len(wfms[ch]) )
                if ped is None:
                    ped = wfms[ch][tlen]

                print "suppress using boundary flash on channel ",ch,". tlen=",tlen
                #flash = boundarysubevent.getFlash( ch )
                #expectation = flash.expectation
                opdata.suppressed_wfm[ch] = np.copy( wfms[ch][:tlen-1] )
                
                wfms[ch][:tlen-1] = ped
                #
                #for i in range(0,tlen-1):
                #    if -flash.tstart+i>=0 and -flash.tstart+i<len(expectation):
                #        expect = expectation[ -flash.tstart +i ]
                #        if wfms[i,ch] < expect + np.sqrt( expect )*3.0:
                #            wfms[i,ch] = ped

        ped = getpedestal( wfms[ch], 20, 2.0*scale )
        if ped is not None:
            #print "ch ",ch," ped=",ped
            wfms[ch] -= ped
            if boundarysubevent is not None and ch in opdata.suppressed_wfm:
                # subtract ped off of suppressed portion
                opdata.suppressed_wfm[ch] -= ped
        else:
            #print "ch ",ch," has bad ped"
            wfms[ch] -= 0.0
        # calc undershoot and correct
        for i in range(np.maximum(1,tlen),len(qs[ch])):
            #for j in range(i+1,np.minimum(i+1+200,len(qs[:,ch])) ):
            q = fA*wfms[ch][i]*(15.625/RC) + qs[ch][i-1]*np.exp(-1.0*15.625/RC) # 10 is fudge factor!
            qs[ch][i] = q
            if doit:
                wfms[ch][i] += q # in window charge correction
        opdata.getBeamWindows( hgslot, ch )[0].wfm = wfms[ch]
    
    npwfms = np.zeros( (nlargest,48), dtype=np.float )
    for ch,wfm in wfms.items():
        npwfms[:len(wfm),ch] = wfm[:]
    return npwfms,qs
def calcEventRates( cfdsettings, inputfile, nevents, outfile, wffile=False, rawdigitfile=False, VISUALIZE=False ):
    # check that the data file type was selected
    if wffile==False and rawdigitfile==False:
        print "Select either wffile or rawdigitfile"
        return
    # load data
    if wffile==True:
        opdata = WFOpData( inputfile )
    elif rawdigitfile==True:
        opdata = RawDigitsOpData( inputfile )

    out = rt.TFile( outfile, "RECREATE" )
    # Event tree
    eventtree = rt.TTree("eventtree","PMT Rates")
    event = array('i',[0])
    samples = array('i',[0])
    nfires = array('i',[0]*NCHANS)
    echmax = array('f',[0])
    eventtree.Branch( 'event', event,'event/I' )
    eventtree.Branch( 'samples', samples, 'samples/I' )
    eventtree.Branch( 'nfires', nfires, 'nfires[%d]/I'%(NCHANS) )
    eventtree.Branch( 'chmax', echmax, 'chmax/F' )

    # Pulse Tree
    pulsetree = rt.TTree("pulsetree","PMT Rates")
    pch = array('i',[0])
    pt = array( 'f',[0] )
    pwindt = array( 'f',[0] )
    pchdt = array( 'f',[0] )
    pmaxamp = array('f',[0])
    ped = array('f',[0])
    pchmaxamp = array('f',[0])
    parea = array('f',[0])
    pulsetree.Branch( 'event', event, 'event/I' )
    pulsetree.Branch( 'ch',pch,'ch/I' )
    pulsetree.Branch( 't',pt,'t/F' )
    pulsetree.Branch( 'windt',pwindt,'windt/F' )
    pulsetree.Branch( 'chdt',pchdt,'chdt/F' )
    pulsetree.Branch( 'amp',pmaxamp,'amp/F' )
    pulsetree.Branch( 'area',parea,'area/F' )
    pulsetree.Branch( "ped",ped,"ped/F")
    pulsetree.Branch( 'chmaxamp',pchmaxamp,'chmaxamp/F' )

    first_event = opdata.first_event
    if VISUALIZE and PYQTGRAPH:
        opdisplay = OpDetDisplay( opdata )
        opdisplay.show()

    more = opdata.getEvent( first_event )
    ievent = first_event
    #for ievent in range(first_event,first_event+nevents+1):
    while more:

        if ievent%50==0:
            print "Event: ",ievent
        event[0] = ievent

        if VISUALIZE:
            more = opdisplay.gotoEvent( ievent )

        # for each event gather disc fires and sort by time
        event_times = []
        event_discs = {}
        echmax[0] = 0
        chpedestals = {}
        chmaxamps = {}
        for femch in range(0,NCHANS):
            wfm = opdata.getData(slot=5)[:,femch]            
            discs = runCFdiscriminator( wfm, cfdsettings )
            theped = getpedestal( wfm, 10, 2 )
            if theped is None:
                ped[0] = 2048.0
            else:
                ped[0] = theped
            chpedestals[femch] = ped[0]

            nfires[femch]  = len(discs)
            pch[0] = femch
            pchmaxamp[0] = np.max( wfm - ped[0] )
            chmaxamps[femch] = pchmaxamp[0]
            if echmax[0]<pchmaxamp[0]:
                echmax[0] = pchmaxamp[0]
            samples[0] = len(wfm)

            pchdt[0] = -1
            pmaxamp[0] = 0
            
            if len(discs)==0:
                print "Discriminator found zero pusles in channel=",femch," event=",event[0]
            
            for idisc,disc in enumerate(discs):
                disc.ch = femch
                t = disc.tfire + 0.01*disc.ch # the channel number is just to keep a unique tag
                event_times.append( t )
                event_discs[t] = disc

                tdisc = disc.tfire
                disc.pmaxamp = np.max( wfm[tdisc:tdisc+cfdsettings.deadtime]-ped[0] )
                pped = getpedestal( wfm[ np.maximum(0,tdisc-20):tdisc ] , 5, 2 )
                if pped is None:
                    pped = ped[0]
                disc.parea = np.sum( wfm[tdisc:tdisc+cfdsettings.deadtime] ) - cfdsettings.deadtime*pped

                if idisc>0:
                    disc.pchdt = tdisc - discs[idisc-1].tfire
                else:
                    disc.pchdt = -1
                if VISUALIZE and PYQTGRAPH:
                    discfire = pg.PlotCurveItem()
                    x = np.linspace( 15.625*(tdisc-5), 15.625*(tdisc+5+cfdsettings.deadtime), cfdsettings.deadtime+10 )
                    y = np.ones( cfdsettings.deadtime+10 )*femch
                    y[5:5+cfdsettings.deadtime] += 2
                    discfire.setData( x=x, y=y, pen=(255,0,0,255) )
                    opdisplay.addUserWaveformItem( discfire, femch )
        event_times.sort()
        for idisc,t in enumerate(event_times):
            disc = event_discs[t]
            pch[0] = disc.ch
            pt[0] = disc.tfire
            pmaxamp[0] = disc.pmaxamp
            pchdt[0] = disc.pchdt
            ped[0] = chpedestals[disc.ch]
            pchmaxamp[0] = chmaxamps[disc.ch]
            parea[0] = disc.parea
            
            if idisc==0:
                pwindt[0] = -1
            else:
                pwindt[0] = disc.tfire-event_discs[ event_times[idisc-1] ].tfire
            
            pulsetree.Fill()
            
        if VISUALIZE:
            print "please enjoy plot"
            raw_input()
        eventtree.Fill()

        more = opdata.getNextEvent()
        ievent = opdata.current_event
        if not more:
            print "no more"
            break

    eventtree.Write()
    pulsetree.Write()
def prepWaveforms(opdata,
                  RC,
                  fA,
                  hgslot,
                  lgslot,
                  boundarysubevent=None,
                  doit=True):
    """ prep beam windows. we do baseline and charge corrections. """
    #RC = 80000.0 # ns
    #fA = 5.0
    #hgslot = 5
    #lgslot = 6

    wfms = {}  # will store waveforms for each channel
    qs = {}  # will store charge correction  for each channel

    nlargest = 0
    for ch in range(0, 48):
        wins = opdata.getBeamWindows(hgslot, ch)
        if len(wins) > 0 and len(wins[0].wfm) > 0:
            if len(wins[0].wfm) > nlargest:
                nlargest = len(wins[0].wfm)
            wfms[ch] = np.ones(len(wins[0].wfm), dtype=np.float)
            qs[ch] = np.zeros(len(wins[0].wfm), dtype=np.float)

    # if boundary subevent, we need to calculate corrections to the first part of the waveforms
    boundary_corrections = {}
    if boundarysubevent is not None:
        opdata.suppressed_wfm = {}
        for flash in boundarysubevent.getFlashList():
            ch = flash.ch
            t = flash.tstart
            wfm = flash.expectation
            qcorr = [0.0]
            for i in range(1, len(wfm)):
                q = fA * wfm[i] * (15.625 / RC) + qcorr[i - 1] * np.exp(
                    -1.0 * 15.625 / RC)
                qcorr.append(q)
            # keep going until q runs out
            while qcorr[-1] > 1.0:
                q = qcorr[-1] * np.exp(-1.0 * 15.625 / RC)
                qcorr.append(q)
            print "channel ", ch, " has a boundary correction of length ", len(
                qcorr), " starting from time=", t
            boundary_corrections[ch] = (t, np.asarray(qcorr, dtype=np.float))

    # get waveforms, swap for low if needed, remove pedestal
    for ch in range(0, 32):
        scale = 1.0
        beamwfm = opdata.getBeamWindows(hgslot, ch)[0]
        if np.max(beamwfm.wfm) < 4090:
            wfms[ch] = beamwfm.wfm
        else:
            lgwins = opdata.getBeamWindows(lgslot, ch)
            print "swap HG ch", ch, " with LG wfm", lgwins
            lgwfm = lgwins[0].wfm

            lgped = getpedestal(lgwfm, 20, 10.0)
            if lgped is None:
                #print "ch ",ch," LG has bad ped"
                lgped = lgwfm[0]
            opdata.getBeamWindows(lgslot, ch)[0].wfm -= lgped
            opdata.getBeamWindows(lgslot, ch)[0].wfm *= 10.0

            wfms[ch] = opdata.getBeamWindows(lgslot, ch)[0].wfm
            #print "lg ped=",lgped," wfm[0-10]=",np.mean( wfms[0:10,ch] )
            scale = 10.0
        # charge correction due to cosmic disc windows
        tlen = 1
        if ch in boundary_corrections.keys():
            # charge correction
            tstart = boundary_corrections[ch][0]
            qcorr = boundary_corrections[ch][1]
            t1 = -tstart
            tlen = np.minimum(len(qcorr) - t1, len(wfms[ch]))
            #print "apply boundary correction to ch ",ch,": tstart=",tstart," covering=[0,",tlen,"]"
            if tlen > 0:
                wfms[ch][:tlen] += qcorr[t1:t1 +
                                         tlen]  # boundary charge correction
            # supress early subevent..
            if boundarysubevent.tend_sample > 0 and boundarysubevent.tend_sample < len(
                    wfms[ch]):
                ped = getpedestal(wfms[ch][boundarysubevent.tend_sample:], 20,
                                  0.5)
                tlen = np.minimum(boundarysubevent.tend_sample, len(wfms[ch]))
                if ped is None:
                    ped = wfms[ch][tlen]

                print "suppress using boundary flash on channel ", ch, ". tlen=", tlen
                #flash = boundarysubevent.getFlash( ch )
                #expectation = flash.expectation
                opdata.suppressed_wfm[ch] = np.copy(wfms[ch][:tlen - 1])

                wfms[ch][:tlen - 1] = ped
                #
                #for i in range(0,tlen-1):
                #    if -flash.tstart+i>=0 and -flash.tstart+i<len(expectation):
                #        expect = expectation[ -flash.tstart +i ]
                #        if wfms[i,ch] < expect + np.sqrt( expect )*3.0:
                #            wfms[i,ch] = ped

        ped = getpedestal(wfms[ch], 20, 2.0 * scale)
        if ped is not None:
            #print "ch ",ch," ped=",ped
            wfms[ch] -= ped
            if boundarysubevent is not None and ch in opdata.suppressed_wfm:
                # subtract ped off of suppressed portion
                opdata.suppressed_wfm[ch] -= ped
        else:
            #print "ch ",ch," has bad ped"
            wfms[ch] -= 0.0
        # calc undershoot and correct
        for i in range(np.maximum(1, tlen), len(qs[ch])):
            #for j in range(i+1,np.minimum(i+1+200,len(qs[:,ch])) ):
            q = fA * wfms[ch][i] * (15.625 / RC) + qs[ch][i - 1] * np.exp(
                -1.0 * 15.625 / RC)  # 10 is fudge factor!
            qs[ch][i] = q
            if doit:
                wfms[ch][i] += q  # in window charge correction
        opdata.getBeamWindows(hgslot, ch)[0].wfm = wfms[ch]

    npwfms = np.zeros((nlargest, 48), dtype=np.float)
    for ch, wfm in wfms.items():
        npwfms[:len(wfm), ch] = wfm[:]
    return npwfms, qs
Beispiel #8
0
def calcEventRates(cfdsettings,
                   inputfile,
                   nevents,
                   outfile,
                   wffile=False,
                   rawdigitfile=False,
                   VISUALIZE=False):
    # check that the data file type was selected
    if wffile == False and rawdigitfile == False:
        print "Select either wffile or rawdigitfile"
        return
    # load data
    if wffile == True:
        opdata = WFOpData(inputfile)
    elif rawdigitfile == True:
        opdata = RawDigitsOpData(inputfile)

    out = rt.TFile(outfile, "RECREATE")
    # Event tree
    eventtree = rt.TTree("eventtree", "PMT Rates")
    event = array('i', [0])
    samples = array('i', [0])
    nfires = array('i', [0] * NCHANS)
    echmax = array('f', [0])
    eventtree.Branch('event', event, 'event/I')
    eventtree.Branch('samples', samples, 'samples/I')
    eventtree.Branch('nfires', nfires, 'nfires[%d]/I' % (NCHANS))
    eventtree.Branch('chmax', echmax, 'chmax/F')

    # Pulse Tree
    pulsetree = rt.TTree("pulsetree", "PMT Rates")
    pch = array('i', [0])
    pt = array('f', [0])
    pwindt = array('f', [0])
    pchdt = array('f', [0])
    pmaxamp = array('f', [0])
    ped = array('f', [0])
    pchmaxamp = array('f', [0])
    parea = array('f', [0])
    pulsetree.Branch('event', event, 'event/I')
    pulsetree.Branch('ch', pch, 'ch/I')
    pulsetree.Branch('t', pt, 't/F')
    pulsetree.Branch('windt', pwindt, 'windt/F')
    pulsetree.Branch('chdt', pchdt, 'chdt/F')
    pulsetree.Branch('amp', pmaxamp, 'amp/F')
    pulsetree.Branch('area', parea, 'area/F')
    pulsetree.Branch("ped", ped, "ped/F")
    pulsetree.Branch('chmaxamp', pchmaxamp, 'chmaxamp/F')

    first_event = opdata.first_event
    if VISUALIZE and PYQTGRAPH:
        opdisplay = OpDetDisplay(opdata)
        opdisplay.show()

    more = opdata.getEvent(first_event)
    ievent = first_event
    #for ievent in range(first_event,first_event+nevents+1):
    while more:

        if ievent % 50 == 0:
            print "Event: ", ievent
        event[0] = ievent

        if VISUALIZE:
            more = opdisplay.gotoEvent(ievent)

        # for each event gather disc fires and sort by time
        event_times = []
        event_discs = {}
        echmax[0] = 0
        chpedestals = {}
        chmaxamps = {}
        for femch in range(0, NCHANS):
            wfm = opdata.getData(slot=5)[:, femch]
            discs = runCFdiscriminator(wfm, cfdsettings)
            theped = getpedestal(wfm, 10, 2)
            if theped is None:
                ped[0] = 2048.0
            else:
                ped[0] = theped
            chpedestals[femch] = ped[0]

            nfires[femch] = len(discs)
            pch[0] = femch
            pchmaxamp[0] = np.max(wfm - ped[0])
            chmaxamps[femch] = pchmaxamp[0]
            if echmax[0] < pchmaxamp[0]:
                echmax[0] = pchmaxamp[0]
            samples[0] = len(wfm)

            pchdt[0] = -1
            pmaxamp[0] = 0

            if len(discs) == 0:
                print "Discriminator found zero pusles in channel=", femch, " event=", event[
                    0]

            for idisc, disc in enumerate(discs):
                disc.ch = femch
                t = disc.tfire + 0.01 * disc.ch  # the channel number is just to keep a unique tag
                event_times.append(t)
                event_discs[t] = disc

                tdisc = disc.tfire
                disc.pmaxamp = np.max(wfm[tdisc:tdisc + cfdsettings.deadtime] -
                                      ped[0])
                pped = getpedestal(wfm[np.maximum(0, tdisc - 20):tdisc], 5, 2)
                if pped is None:
                    pped = ped[0]
                disc.parea = np.sum(
                    wfm[tdisc:tdisc +
                        cfdsettings.deadtime]) - cfdsettings.deadtime * pped

                if idisc > 0:
                    disc.pchdt = tdisc - discs[idisc - 1].tfire
                else:
                    disc.pchdt = -1
                if VISUALIZE and PYQTGRAPH:
                    discfire = pg.PlotCurveItem()
                    x = np.linspace(
                        15.625 * (tdisc - 5),
                        15.625 * (tdisc + 5 + cfdsettings.deadtime),
                        cfdsettings.deadtime + 10)
                    y = np.ones(cfdsettings.deadtime + 10) * femch
                    y[5:5 + cfdsettings.deadtime] += 2
                    discfire.setData(x=x, y=y, pen=(255, 0, 0, 255))
                    opdisplay.addUserWaveformItem(discfire, femch)
        event_times.sort()
        for idisc, t in enumerate(event_times):
            disc = event_discs[t]
            pch[0] = disc.ch
            pt[0] = disc.tfire
            pmaxamp[0] = disc.pmaxamp
            pchdt[0] = disc.pchdt
            ped[0] = chpedestals[disc.ch]
            pchmaxamp[0] = chmaxamps[disc.ch]
            parea[0] = disc.parea

            if idisc == 0:
                pwindt[0] = -1
            else:
                pwindt[0] = disc.tfire - event_discs[event_times[idisc -
                                                                 1]].tfire

            pulsetree.Fill()

        if VISUALIZE:
            print "please enjoy plot"
            raw_input()
        eventtree.Fill()

        more = opdata.getNextEvent()
        ievent = opdata.current_event
        if not more:
            print "no more"
            break

    eventtree.Write()
    pulsetree.Write()