def calc_tag_prob_Q( self, iL, btag_vals ): '''Returns the 3 jet b-tagging probability assuming bqq and iL, the index of the jet from the leptonic b''' assert iL >= 0 and iL <= 2 prob = U.hist_value_at( self.b_tag_hist, btag_vals[ iL ] ) for iW in range( 3 ): if iW != iL : prob *= U.hist_value_at( self.w_tag_hist, btag_vals[ iW ] ) return prob
def calc_tag_prob_H( self, iW, btag_vals ): '''Returns the 3 jet b-tagging probability assuming bbq and iW, the index of the jet from the hadronic W decay''' assert iW >= 0 and iW <= 2 prob = U.hist_value_at( self.w_tag_hist, btag_vals[ iW ] ) for iB in range( 3 ): if iB != iW : prob *= U.hist_value_at( self.b_tag_hist, btag_vals[ iB ] ) return prob
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 ] }