Exemple #1
0
def calc_angles( lv_top, lv_lep, Qlep = 1 ):
    '''Calculated the cosine of the angle of the lepton in the (leptonic) top rest frame
    relative to three axes: the beam axis, the helicity axis, and the transverse axis'''
    lv_lep_tag = LV.new( lv_lep )
    lv_lep_tag.Boost( -lv_top.BoostVector() )
    lv_beam = R.TVector3( 980 * Qlep, 0, 0 ) # haven't checked the sign. Maybe other way around.
    lv_perp = lv_beam.Cross( lv_top.Vect() )
    beam_angle = lv_beam.Angle(  lv_lep_tag.Vect() )
    helicity_angle = lv_top.Vect().Angle( lv_lep_tag.Vect() )
    transverse_angle = lv_perp.Angle( lv_lep_tag.Vect() )
    return cos( beam_angle ), cos( helicity_angle ), cos( transverse_angle )
Exemple #2
0
    def fit( self, p4_lep, p4_jets, mex, mey, parton_matches, cheat = False, nofit = False,
             btag_indices = [], btag_vals = [], known_problem_with_lepton = False ):
        '''Does a kinematic fit given 4-vectors (the p4 inputs), lepton charge, met and btags.

           The "cheat" option means to consider only the correct assignment (if any).
           The parton_matches and known_problem_with_lepton are used to cheat and to create debug information

           If hard_btags constraints are used, only the btag_indices are needed,
           but the btag_vals may be useful to study tag_prob s.
           Otherwise, only the full btag_vals are needed.
           '''
        fit_allowed = not nofit
        Nj = len( p4_jets )
        assert Nj >= 4
        for ib in btag_indices:
            assert ib >= 0 and ib < Nj
        if not self.hard_btags:
            assert len( btag_vals ) == Nj
        if self.dbg > 50:
            print 'rocfit_algebraic.fit inputs are reasonable.'
        mex = R.TMath.Range( -300, 300, mex )
        mey = R.TMath.Range( -300, 300, mey )
        raw_nu = LV.new()
        raw_nu.SetXYZM( mex, mey, 0., 0. )
        met = raw_nu.Pt()

        lv_lep = LV.new()
        lv_lep.SetXYZM( p4_lep.X(), p4_lep.Y(), p4_lep.Z(), max( 0, p4_lep.M() ) )
        for tries in range( 14 ):
            if lv_lep.M() >= 0:
                break
            lv_lep.SetXYZM( p4_lep.X(), p4_lep.Y(), p4_lep.Z(), max( 1E-3 * 2**tries, p4_lep.M() ) )
            if tries > 9:
                print 'Warning: lepton mass insists on being negative. # of tries:',tries

        Ntag = len( btag_indices )
        self.matchable = all_matched( parton_matches ) and not known_problem_with_lepton
        true_Bs = parton_matches[ 0:2 ]
        true_Qs = parton_matches[ 2:4 ]

        j_as_q_ITF = [ self.ITF( p4, False ) for p4 in p4_jets ]
        j_as_b_ITF = [ self.ITF( p4, True ) for p4 in p4_jets ]
        j_as_q_eff = [ self.eff( p4, False ) for p4 in p4_jets ]
        j_as_b_eff = [ self.eff( p4, True ) for p4 in p4_jets ]

        recos = []
        indices = range( 4 )

        hf = self.hadFit
        lf = self.lepFit

        for iPQH in itertools.permutations(indices,3) : # loop over possible hadronic assignments
            if iPQH[0]>iPQH[1] : continue               # the two equivalent W->qq assignments
            iL = 6 - reduce( operator.add, iPQH )
            if self.hard_btags:
                if Ntag >= 2:
                    if iPQH[2] not in btag_indices or iL not in btag_indices: continue   # b-quarks can only be assigned to tagged jets
                if Ntag <= 2:
                    if iPQH[0] in btag_indices or iPQH[1] in btag_indices : continue # no spare tag to waste on W->q jets

            self.perm_is_correct = iPQH[0] in true_Qs  and  iPQH[1] in true_Qs  and \
                                   iPQH[2] == parton_matches[1]  and  iL == parton_matches[0]
            if cheat and not self.perm_is_correct:
                continue

            tag_prob = self.calc_tag_prob( iPQH, btag_vals )
            if self.dbg > 28:
                print 'DBG29 iPQH:',iPQH,'-> perm_is_correct:',self.perm_is_correct,' tag_prob:',tag_prob
            self.fill_hist_vars( 'h_tagprob', [ math.log(tag_prob) ] )

            lv_W_had = p4_jets[ iPQH[0] ] + p4_jets[ iPQH[1] ]
            lv_t_had = p4_jets[ iPQH[2] ] + lv_W_had
            m_W_had = lv_W_had.M()
            m_t_had = lv_t_had.M()
            
            self.fill_hist_vars( 'h_premass', [ m_t_had, m_W_had ] )

            this_reco = {'iPQH': iPQH, 'Ptags' : tag_prob }

            if fit_allowed:

                if self.dbg > 49:
                    print 'DBG50 will fit iPQH:',iPQH,'jITF0:',j_as_q_ITF[ iPQH[ 0 ] ]
                                                              
                perm_jets = [ p4_jets[ i ] for i in iPQH ]
                perm_ITFs = [ j_as_q_ITF[ iPQH[ 0 ] ], j_as_q_ITF[ iPQH[ 1 ] ], j_as_b_ITF[ iPQH[ 2 ] ] ]
                perm_effs = [ j_as_q_eff[ iPQH[ 0 ] ], j_as_q_eff[ iPQH[ 1 ] ], j_as_b_eff[ iPQH[ 2 ] ] ]

                corr_b_jet = LV.new( perm_jets[2] )
                self.correct_b_mass( corr_b_jet, perm_ITFs[ 2 ] )
                if self.dbg > 9:
                    print 'DBG10 TMP',lv_as_PM_4tup( perm_jets[2] ),' --> ',lv_as_PM_4tup( corr_b_jet )

                hf.fit( perm_jets[ 0 ], perm_jets[ 1 ], corr_b_jet, 
                        perm_ITFs[ 0 ], perm_ITFs[ 1 ], perm_ITFs[ 2 ],
                        perm_effs[ 0 ], perm_effs[ 1 ], perm_effs[ 2 ] )

                ratios = [ hf.ratio( ii ) for ii in range( 3 ) ]
                    
                delta_HT = reduce( operator.add, map( lambda p4,rat: p4*(self.fcorr * (rat-1)), perm_jets, ratios ))
                self.fill_hist_vars( 'h_funcalls', [hf.n_calls()] )

                if self.dbg > 12:
                    print 'DBG13',iPQH,'perm_jets:',[lv_as_3tup(lv) for lv in perm_jets],\
                          'had MLL:',hf.MLL(),'ratios:',ratios,' -->',delta_HT.Print()

                this_reco.update( { 'hadR' : ratios, 'hadMLL' : hf.MLL(), 'dHT' : delta_HT, 'hadNC' : hf.n_calls(),
                                    'hadW' : hf.fitW(), 'hadT' : hf.fitT(), 'hadConv' : hf._converged } )

                if self.prob_track_level > 0:
                    self.fill_hist_vars( 'h_Peff', [hf._effs[0]] )
                    self.fill_hist_vars( 'h_Qeff', [hf._effs[1]] )
                    self.fill_hist_vars( 'h_Heff', [hf._effs[2]] )
                    self.fill_hist_vars( 'h_Pprob', [hf._probs[0]] )
                    self.fill_hist_vars( 'h_Qprob', [hf._probs[1]] )
                    self.fill_hist_vars( 'h_Hprob', [hf._probs[2]] )
                    self.fill_hist_vars( 'h_Pcum', [hf._cums[0]] )
                    self.fill_hist_vars( 'h_Qcum', [hf._cums[1]] )
                    self.fill_hist_vars( 'h_Hcum', [hf._cums[2]] )
                    self.fill_hist_vars( 'h_WH', [hf._res[0]] )
                    self.fill_hist_vars( 'h_TH', [hf._res[1]] )
                    if self.prob_track_level > 2:
                        this_reco.update( { 'hadEffs' : hf._effs, 'hadProbs' : hf._probs, 'hadRes' : hf._res,
                                            'hadCums' : hf._cums } )

                corr_lep_b_jet = LV.new( p4_jets[iL] )
                self.correct_b_mass( corr_lep_b_jet,  j_as_b_ITF[iL] )
                if self.dbg > 9:
                    print 'DBG10 TMP leptonic ',lv_as_PM_4tup( p4_jets[ iL ] ),' --> ',lv_as_PM_4tup( corr_lep_b_jet )
                OK = lf.fit( corr_lep_b_jet, j_as_b_ITF[iL], j_as_b_eff[iL],
                             lv_lep,
                             mex, mey, self.dnu_PDF( met ) )
                if not OK:
                    print 'ERROR! leptonic_fitter_algebraic failed!?'
                    return

                fitNu = lf.fitNu()

                if self.dbg > 39:
                    print 'DBG40 lepFit has MLL:',lf.MLL(),'.nu:',lv_as_PM_4tup(fitNu),\
                          '.W:',lv_as_PM_4tup(lf.fitW()),'.T:',lv_as_PM_4tup(lf.fitT()),' --> penalty: ',lf.penalty()
                elif self.dbg>19:
                    print 'DBG20 lepFit return W & top masses of:',lf.fitW().M(),'&',lf.fitT().M(),' --> penalty: ',lf.penalty()
                assert lf.penalty() > 0 or lv_lep.P() > 2*self.top_mass or \
                       (abs( lf.fitW().M() - self.W_mass ) < 1. and abs( lf.fitT().M() - self.top_mass ) < 1.)

                MLL = kinMLL = hf.MLL() + lf.MLL()
                if not self.hard_btags:
                    MLL -= math.log( tag_prob )

                self.fill_hist_vars( 'h_lep_funcalls', [lf.n_calls()] )

                dnu = raw_nu - fitNu

                this_reco.update( {'nu'     : fitNu,               'lepW' : lf.fitW(),       'lepT' : lf.fitT(),
                                   'lepB'   : lf.param_value(),    'lepL' : 1.0,
                                   'lepX'   : dnu.X(),             'lepY' : dnu.Y(), 
                                   'lepMLL' : lf.MLL(),          'kinMLL' : kinMLL,           'MLL' : MLL,
                                   'lepNC'  : lf.n_calls(),     'penalty' : lf.penalty(), 
                                   'lepConv': lf._converged,       'swap' : lf._swapped
                                   } )
                if self.prob_track_level > 0:
                    self.fill_hist_vars( "h_Beff", [lf._eff] )
                    self.fill_hist_vars( "h_Bprob", [lf._probs[0]] )
                    self.fill_hist_vars( "h_Bcum", [lf._cums[0]] )
                    self.fill_hist_vars( "h_Xprob", [lf._probs[1]] )
                    self.fill_hist_vars( "h_Yprob", [lf._probs[2]] )
                    self.fill_hist_vars( "h_Xcum", [lf._cums[1]] )
                    self.fill_hist_vars( "h_Ycum", [lf._cums[2]] )
                    if self.prob_track_level > 2:
                        this_reco.update( { 'lepEffs' : [lf._eff], 'lepProbs' : lf._probs, 'lepCums' : lf._cums } )
            recos.append( this_reco )
            if self.dbg>89:
                print 'DBG90 this_reco:',sorted(this_reco.items())

        if fit_allowed:
            if self.hard_btags:
                recos.sort( key=lambda assignment: assignment[ 'kinMLL' ] )
            else:
                recos.sort( key=lambda assignment: assignment[ 'MLL' ] )
        
        return recos
Exemple #3
0
    def reco( self, lv_lep, lv_jets, mex, mey, true_leptonic_b_index, cheat = False,
              btag_vals = [], known_problem_with_lepton = False ):
        '''Does a partial reconstruction of ttbar --> ( b (l nu) ) [ b q ... ].

           The "cheat" option means to consider only the correct assignment (if any).
           The parton_matches and known_problem_with_lepton are used to cheat and to create debug information
           '''
        Nj = len( lv_jets )
        assert Nj == 3
        assert (not cheat) or true_leptonic_b_index in range(3)
        dbg = self.dbg
        if dbg > 50:
            print 'partial_reco inputs are reasonable.'

        self.matchable = true_leptonic_b_index >= 0 and not known_problem_with_lepton

        # prepare the observables (one per reco-assignments)
        SR = self.SR
        OK = SR.reco_nu( mex, mey, lv_lep )
        if not OK:
            raise Exception( 'Unexpected failure of simple_reco::reco_nu' )
        if SR.two_solutions() :
            lv_nu_pzs = [ SR.nu(), SR.alt_nu() ]
        if dbg > 29:
            print 'DBG30PR SR(',mex,',',mey,',',U.lv_as_EPM_str(lv_lep),') -> '\
                  '2?',self.SR.two_solutions(),'nu:',U.lv_as_3tup(SR.nu()),'/',U.lv_as_3tup(SR.alt_nu())
        lv_nu_iLs = []
        lv_tl_iLs = []
        mtls = []
        mps = []
        lv_3j = sum( lv_jets, LV.new() )
        for iL in range( 3 ) :
            self.perm_is_correct = iL == true_leptonic_b_index
            lv_LBL = lv_jets[ iL ] + lv_lep
            if self.SR.two_solutions() :
                leptonic_tops = [ lv_LBL + lv for lv in lv_nu_pzs ] 
                dmtops = [ abs( self.top_mass - lv.M() ) for lv in leptonic_tops ] 
                iNu = 0 if dmtops[0] < dmtops[1] else 1
                if dbg > 18:
                    print 'DBG19PR iL:',iL,'-> perm_is_correct:',self.perm_is_correct,', dmtops:',dmtops,'-> iNu:',iNu
                lv_nu_iLs.append( lv_nu_pzs[ iNu ] )
                lv_tl = leptonic_tops[ iNu ]
            else:
                lv_nu = self.SR.nu()
                lv_nu_iLs.append( lv_nu )
                lv_tl = lv_LBL + lv_nu
            if dbg > 14:
                print 'DBG15PR iL:',iL,'-> nu:',U.lv_as_EPM_str(lv_nu_iLs[-1]),', tl:',U.lv_as_EPM_str(lv_tl)
            lv_tl_iLs.append( lv_tl )
            mtls.append( lv_tl.M() )
            mps.append( ( lv_3j - lv_jets[ iL ] ).M() )

        # loop over statistics-assignments
        recos = []
        for iL in range( 3 ) :
            self.perm_is_correct = iL == true_leptonic_b_index
            if cheat and not self.matchable and self.perm_is_correct:
                continue
 
            lv_nu = lv_nu_iLs[ iL ]
            lv_tl = lv_tl_iLs[ iL ]

            for iH in range( 3 ) : # loop over hadronic b assignment
                if iH == iL :
                    continue
                iW = 3 - iL - iH
                iLHW = [iL,iH,iW]
                
                # probabilities for hypothesis H: lhq   (i.e. a jet from the hadronic W is missing)
                prob_t_H = U.hist_value_at( self.h_mt_l, mtls[ iL ] ) * \
                           U.hist_value_at( self.h_mt_h, mtls[ iH ] ) * \
                           U.hist_value_at( self.h_mt_q, mtls[ iW ] )
                prob_p_H = U.hist_value_at( self.h_mp_hq, mps[ iL ] ) * \
                           U.hist_value_at( self.h_mp_lq, mps[ iH ] ) * \
                           U.hist_value_at( self.h_mp_hl, mps[ iW ] )
                prob_tag_H = self.calc_tag_prob_H( iW, btag_vals )
                prob_H = prob_t_H * prob_p_H * prob_tag_H

                # probabilities for hypothesis Q: lqq   (i.e. the hadronic b is missing)
                prob_t_Q = U.hist_value_at( self.h_mt_l, mtls[ iL ] ) * \
                           U.hist_value_at( self.h_mt_q, mtls[ iH ] ) * \
                           U.hist_value_at( self.h_mt_q, mtls[ iW ] )
                prob_p_Q = U.hist_value_at( self.h_mp_qq, mps[ iL ] ) * \
                           U.hist_value_at( self.h_mp_lq, mps[ iH ] ) * \
                           U.hist_value_at( self.h_mp_lq, mps[ iW ] )
                prob_tag_Q = self.calc_tag_prob_Q( iL, btag_vals )
                prob_Q = prob_t_Q * prob_p_Q * prob_tag_Q

                if dbg > 28:
                    print 'DBG29PR iLHW:',iLHW,'-> prob_tag_H:',prob_tag_H,'prob_tag_Q:',prob_tag_Q

                prob = self.fQ * prob_Q + ( 1-self.fQ ) * prob_H
                postriori_fQ = ( self.fQ * prob_Q / prob ) if prob > 0 else -1

                lv_th_proxy = lv_jets[ iH ] + lv_jets[ iW ]

                xx = R.TMath.Range( 0.2, 1.2, 100 / lv_th_proxy.E() )
                alpha = 0.6500 + 0.7076 * xx
                lv_ttbar = lv_tl + lv_th_proxy * alpha                

                recos.append( { 'iLHW' : iLHW, 'tagH' : prob_tag_H, 'tagQ' : prob_tag_Q,
                                'tl' : lv_tl, 'proxy' : lv_th_proxy, 'nu' : lv_nu,
                                'alpha' : alpha, 'ttbar' : lv_ttbar, 'cmtt' : ( lv_ttbar.M() - 64.44 ) / 0.7596,
                                'fQ' : postriori_fQ, 'prob' : prob } )
                if dbg > 34:
                    print 'DBG35PR alpha:',alpha,'prob_Q:',prob_Q,'prob_H',prob_H
         
        recos.sort( key=lambda reco: - reco[ 'prob' ] )

        denom = sum( [ reco['prob'] for reco in recos ] )
        if denom <= 0:
            if dbg :
                print 'Information (DBG1): partial reco probs are non-positive:',\
                      [ reco['prob'] for reco in recos ],'(legitimate as long as it is rare)'
            averages = self.averagables_array( recos[0], lv_lep ) [1:]
        else:
            cands = [ self.averagables_array( reco, lv_lep ) for reco in recos ]
            if dbg > 21 :
                print 'DBG22 partial_reco\'s cands for averaging are',cands
            weighted_cands = [ [cand[0]*obs for obs in cand[1:] ]  for cand in cands ]
            averages = [ sum(column)/denom for column in zip( *weighted_cands ) ]
            if dbg > 14 :
                print 'DBG15 partial_reco\'s averages:',averages

        return recos, { 'mtt': averages[ 0 ], 'yl': averages[ 1 ], 'yh': averages[ 2 ],
                        'cb' : averages[ 3 ], 'ch': averages[ 4 ], 'ct': averages[ 5 ] }