Source code for vibe.analysis_validation_modes.physics.tau1x1or3

#################################################################################
# Basf2 (Belle II Analysis Software Framework)                                  #
# Author: The Belle II Collaboration                                            #
#                                                                               #
# See git log for contributors and copyright holders.                           #
# This file is licensed under LGPL-3.0, see LICENSE.md.                         #
# e-e+ -> tau+(sig)  (mode 303) tau-(tag)  (mode = 1 or 2 or 303 or 163 or 112) #
#         |                     |                                               #
#         |                     +-> e- nu_tau                                   #
#         |                     +-> mu- nu_tau                                  #
#         |                     +-> pi- nu_tau                                  #
#         |                     +-> pi- pi0 nu_tau                              #
#         |                     +-> pi- pi+ pi- nu_tau                          #
#         |                                                                     #
#         +-> pi+ nu_tau                                                        #
#################################################################################
from typing import List, Dict
import pandas as pd

import basf2 as b2
import modularAnalysis as ma
import variables.collections as vc
import variables.utils as vu
from variables import variables as va

from vibe.core.utils.misc import fancy_validation_mode_header
from vibe.core.validation_mode import ValidationModeBaseClass
from vibe.core.helper.histogram_tools import HistVariable, Histogram, HistComponent
from vibe.core.helper.root_helper import makeROOTCompatible


# Show warnings and above (ignore info/debug)
b2.logging.log_level = b2.LogLevel.INFO
# Only show each maximum 3 times
b2.logging.max_repetitions = 3

__all__ = [
    "Tau1x1or3",
]


[docs] @fancy_validation_mode_header class Tau1x1or3(ValidationModeBaseClass): name = "Tau1x1or3" latex_str = r"$\tau^+ \rightarrow \pi^+ \nu, \tau^- \rightarrow e^-/\mu^-/\pi^-/K^-/(\pi^+ \pi^- \pi^+) \nu$" # Define trigger categories and their corresponding names L1_TRIGGER_CATEGORIES: Dict[str, List[str]] = { "ECL triggers": [ "hie", "c4", "eclmumu", "ecltaub2b", "lml0", "lml1", "lml2", "lml4", "lml6", "lml7", "lml8", "lml9", "lml10", "lml12", "lml13", ], "CDC triggers": ["fff", "ffo", "ffy", "fyo", "stt", "ff30", "fy30", "fso", "sso"], "KLM triggers": [ "klm2", "eklm2", "eklmhit", "beklm", "seklm1", "seklm2", "fwd_seklm", "bwd_seklm", "ieklm1", "mu_pair", "mu_eb2b", ], "Combined triggers": ["cdcklm1", "cdcklm2", "cdcecl3", "cdcecl4", "ecleklm1"], "Other triggers": ["bhabha", "lml16"], } HLT_TRIGGER_CATEGORIES: Dict[str, List[str]] = {"HLT triggers": ["hlt_filter", "hlt_all", "hlt_skim", "hlt_tautau"]}
[docs] def create_basf2_path(self, include_HLT=True, include_L1=True): mypath = b2.Path() ma.labelTauPairMC(path=mypath) # Fill all 'good' tracks trackCuts = "dr <= 0.5 and -2.0 <= dz <= 2.0 and thetaInCDCAcceptance and useCMSFrame(p) < 5.29" ma.fillParticleList("pi+:track", trackCuts, path=mypath) ########################################### # Photons selections ########################################### ma.fillParticleList("gamma:all", "", path=mypath) gammaAndPi0Cuts = "-0.8660 < cosTheta < 0.9563" ########################################### # pi0s selections and reconstruction ########################################### gammaForPi0Cuts = gammaAndPi0Cuts + " and E > 0.1" ma.cutAndCopyLists("gamma:looseForPi0", "gamma:all", gammaForPi0Cuts, path=mypath) ma.reconstructDecay( "pi0:fromLooseGammas -> gamma:looseForPi0 gamma:looseForPi0", "[0.115 < M < 0.155]", path=mypath ) ma.rankByLowest(particleList="pi0:fromLooseGammas", variable="abs(dM)", path=mypath) ########################################### # gammas list with/without pi0 daughters ########################################### gammaCuts = gammaAndPi0Cuts + " and E > 0.2 and isDescendantOfList(pi0:fromLooseGammas,1) == 0" ma.cutAndCopyLists("gamma:notPi0", "gamma:all", gammaCuts, path=mypath) ma.cutAndCopyLists("gamma:pi0", "gamma:looseForPi0", "isDescendantOfList(pi0:fromLooseGammas) == 1", path=mypath) ########################################### # Event Shape/Kinematics ########################################### listforEvent = ["pi+:track", "pi0:fromLooseGammas", "gamma:notPi0"] ma.buildEventShape( listforEvent, foxWolfram=True, cleoCones=True, jets=False, harmonicMoments=False, allMoments=False, collisionAxis=False, sphericity=False, thrust=True, path=mypath, ) ma.buildEventKinematics(listforEvent, path=mypath) ########################################### # Event variables and selection cuts ########################################### eventVariables = [ "tauPlusMCMode", "tauMinusMCMode", "tauMinusMCProng", "tauPlusMCProng", "thrust", "visibleEnergyOfEventCMS", "missingMomentumOfEventCMS", "missingMomentumOfEvent", "missingMomentumOfEvent_theta", "missingMomentumOfEventCMS_theta", "totalPhotonsEnergyOfEvent", "missingMass2OfEvent", ] va.addAlias("totalChargeInEvent", "sumValueInList(pi+:track, charge)") ## First event cuts for total charge = 0 ma.applyEventCuts("totalChargeInEvent==0", path=mypath) ########################################### # Pi0 topology and variables ########################################### va.addAlias("Npi0", "countInList(pi0:fromLooseGammas)") va.addAlias("pi0InParaThrust", "countInList(pi0:fromLooseGammas, cosToThrustOfEvent>0)") va.addAlias("pi0InAntiThrust", "countInList(pi0:fromLooseGammas, cosToThrustOfEvent<0)") va.addAlias("pi0Topology", "formula(10*pi0InParaThrust + pi0InAntiThrust)") eventVariables += ["Npi0", "pi0InParaThrust", "pi0InAntiThrust", "pi0Topology"] ########################################### # Photon topology and variables ########################################### ma.copyLists("gamma:good", ["gamma:notPi0", "gamma:pi0"], path=mypath) va.addAlias("nAllPhotons", "countInList(gamma:good)") # Number of all gammas used in the event va.addAlias("nPhotons", "countInList(gamma:notPi0)") # Number of gammas used in the event that are not pi0s va.addAlias("gammaInParaThrust", "countInList(gamma:good, cosToThrustOfEvent>0)") va.addAlias("gammaInAntiThrust", "countInList(gamma:good, cosToThrustOfEvent<0)") va.addAlias("gammaTopology", "formula(10*gammaInParaThrust + gammaInAntiThrust)") eventVariables += ["nAllPhotons", "nPhotons", "gammaInParaThrust", "gammaInAntiThrust", "gammaTopology"] ########################################### # Track topology and variables ########################################### va.addAlias("nAllTracks", "countInList(pi+:track)") # Number of all tracks used in the event ma.cutAndCopyLists("pi+:parathrust", "pi+:track", "cosToThrustOfEvent>0", path=mypath) ma.cutAndCopyLists("pi+:antithrust", "pi+:track", "cosToThrustOfEvent<0", path=mypath) va.addAlias("trackInParaThrust", "countInList(pi+:parathrust)") va.addAlias("trackInAntiThrust", "countInList(pi+:antithrust)") va.addAlias("trackTopology", "formula(10*trackInParaThrust + trackInAntiThrust)") eventVariables += ["nAllTracks", "trackTopology"] ########################################### # 1prong and 3prong tracks separation ########################################### ma.cutAndCopyLists("pi+:1prongpara", "pi+:parathrust", "trackInParaThrust==1", path=mypath) ma.cutAndCopyLists("pi+:1pronganti", "pi+:antithrust", "trackInAntiThrust==1", path=mypath) ma.copyLists("pi+:1prong", ["pi+:1prongpara", "pi+:1pronganti"], path=mypath) ma.cutAndCopyLists("pi+:3prongpara", "pi+:parathrust", "trackInParaThrust==3", path=mypath) ma.cutAndCopyLists("pi+:3pronganti", "pi+:antithrust", "trackInAntiThrust==3", path=mypath) ma.copyLists("pi+:3prong", ["pi+:3prongpara", "pi+:3pronganti"], path=mypath) ## Second event cut for track topology ma.applyEventCuts("trackTopology==11 or trackTopology==13 or trackTopology==31", path=mypath) # Rank the 3prong tracks by momentum ( helpful for 3prong to get the slow/fast pi+ and pi- combination ) ma.rankByLowest(particleList="pi+:3prong", variable="p", outputVariable="p_rank", path=mypath) va.addAlias("p_rank", "extraInfo(p_rank)") # variable to store the rank of the 3prong tracks ########################################### # Reconstruction ########################################### reconstruction_condition_1 = "[[trackTopology==13] or [trackTopology==31]] and [Npi0==0]" # 1 vs 3 prong reconstruction_condition_2 = "[trackTopology==11] and [Npi0==0]" # 1 vs 1 prong reconstruction_condition_3 = "[trackTopology==11] and [Npi0>0]" # 1 vs 1 prong + pi0 ma.reconstructDecay( "tau-:tag3prong -> pi-:3prong pi+:3prong pi-:3prong ?nu", reconstruction_condition_1, path=mypath, dmID=1 ) ma.reconstructDecay("tau-:tag1prong -> pi-:1prong ?nu", reconstruction_condition_2, path=mypath, dmID=2) ma.reconstructDecay( "tau-:tag1prongPi0 -> pi-:1prong pi0:fromLooseGammas ?nu", reconstruction_condition_3, path=mypath, dmID=3 ) ma.reconstructDecay("tau+:sig -> pi+:1prong ?nu", "", path=mypath) ma.copyLists("tau-:tag", ["tau-:tag3prong", "tau-:tag1prong", "tau-:tag1prongPi0"], path=mypath) # Select vpho candidates with signal and tag in opposite sides of the event. va.addAlias( "prod1", "formula(daughter(0, daughter(0, cosToThrustOfEvent))*daughter(1, daughter(0,cosToThrustOfEvent)))" ) va.addAlias( "prod2", "formula(daughter(0, daughter(0, cosToThrustOfEvent))*daughter(1, daughter(1,cosToThrustOfEvent)))" ) va.addAlias( "prod3", "formula(daughter(0, daughter(0, cosToThrustOfEvent))*daughter(1, daughter(2,cosToThrustOfEvent)))" ) ma.reconstructDecay("vpho:1X3 -> tau+:sig tau-:tag3prong", "prod1 < 0 and prod2 < 0 and prod3 < 0", path=mypath) ma.reconstructDecay("vpho:1X1 -> tau+:sig tau-:tag1prong", "prod1 < 0", path=mypath) ma.reconstructDecay("vpho:1X1Pi0 -> tau+:sig tau-:tag1prongPi0", "prod1 < 0 and prod2 < 0", path=mypath) va.addAlias("dmIDTag", "daughter(1, extraInfo(decayModeID))") # Select vpho candidates with signal and tag in opposite sides of the event. ma.copyLists("vpho:tau", ["vpho:1X3", "vpho:1X1", "vpho:1X1Pi0"], path=mypath) ############################################################################################ # Match MC Truth ############################################################################################ # ma.matchMCTruth('vpho:tau', path=mypath) ma.matchMCTruth("tau+:sig", path=mypath) ma.matchMCTruth("tau-:tag", path=mypath) va.addAlias("isSignal_tau1", "daughter(0, isSignal)") va.addAlias("isSignal_tau2", "daughter(1, isSignal)") eventVariables += ["dmIDTag", "isSignal_tau1", "isSignal_tau2"] ############################################################################################ # number of photons and pi0 on signal and tag side of thrust axis ############################################################################################ # Check in which hemisphere is the tag tau located va.addAlias("tagInPosThrust", "countInList(vpho:tau, daughter(1, daughter(0, cosToThrustOfEvent)) > 0)") va.addAlias("tagInNegThrust", "countInList(vpho:tau, daughter(1, daughter(0, cosToThrustOfEvent)) < 0)") eventVariables += ["tagInPosThrust", "tagInNegThrust"] ########################################### # Variables for vpho:tau decay ########################################### # select the tracks that enter the reconstruction of tau+ and tau- and vpho ma.cutAndCopyList("tau+:sigFromVpho", "tau+:sig", "isDescendantOfList(vpho:tau, 1)==1", path=mypath) ma.cutAndCopyList("tau-:tagFromVpho", "tau-:tag", "isDescendantOfList(vpho:tau, 1)==1", path=mypath) # energy of signal tau va.addAlias("ECMS_sig", "useCMSFrame(totalEnergyOfParticlesInList(tau+:sigFromVpho))") # energy of tagged tau va.addAlias("ECMS_tag", "useCMSFrame(totalEnergyOfParticlesInList(tau-:tagFromVpho))") eventVariables += ["ECMS_sig", "ECMS_tag"] va.addAlias("Nvpho", "countInList(vpho:tau)") va.addAlias("missingPtCMS", "formula(missingMomentumOfEventCMS*sin(missingMomentumOfEventCMS_theta))") va.addAlias( "tau3prongPseudoMass_Mmin", "formula( [ daughter(1,M) * daughter(1,M) + 2. * \ ( 5.29 - useCMSFrame(daughter(1,E)) ) * \ ( useCMSFrame(daughter(1,E)) - useCMSFrame(daughter(1,p)) ) \ ]^0.5 )", ) eventVariables += ["Nvpho", "missingPtCMS", "tau3prongPseudoMass_Mmin"] ########################################### # Variables for L1 and HLT triggers ########################################### if include_L1: # Combine all trigger lists trigger_bits = [] for trigger_category in self.L1_TRIGGER_CATEGORIES: trigger_bits += self.L1_TRIGGER_CATEGORIES[trigger_category] for trigger_name in trigger_bits: # https://software.belle2.org/development/sphinx/analysis/doc/Variables.html#variable-L1FTDLBit:~:text=of%20trigger%20variables%3A-,L1FTDL,-(name) va.addAlias("ftdl_" + trigger_name, "L1FTDL(" + trigger_name + ")") eventVariables.append("ftdl_" + trigger_name) if include_HLT: va.addAlias("hlt_filter", "SoftwareTriggerResult(software_trigger_cut&filter&total_result)") va.addAlias("hlt_all", "SoftwareTriggerResult(software_trigger_cut&all&total_result)") va.addAlias("hlt_skim", "SoftwareTriggerResult(software_trigger_cut&skim&total_result)") va.addAlias("hlt_tautau", "SoftwareTriggerResult(software_trigger_cut&skim&accept_tau_tau)") eventVariables += ["hlt_filter", "hlt_skim", "hlt_all", "hlt_tautau"] ########################################### # Variables for reconstructed particles ########################################### # Rho mass (pi + pi0) variable will be NaN for non-rho decays va.addAlias("rho_mass", "daughterInvM(0, 1)") va.addAlias("rho_asym", "formula((daughter(0, E) - daughter(1, E))/(daughter(0, E) + daughter(1, E)))") # pi0 mass va.addAlias("pi0_mass", "daughter(1, daughterInvM(0, 1))") va.addAlias("pi0_0_clusterHasNPhotons", "daughter(1, daughter(0, clusterHasNPhotons))") va.addAlias("pi0_1_clusterHasNPhotons", "daughter(1, daughter(1, clusterHasNPhotons))") va.addAlias("pi0_0_clusterNHits", "daughter(1, daughter(0, clusterNHits))") va.addAlias("pi0_1_clusterNHits", "daughter(1, daughter(1, clusterNHits))") va.addAlias("pi0_0_minC2TDist", "daughter(1, daughter(0, minC2TDist))") va.addAlias("pi0_1_minC2TDist", "daughter(1, daughter(1, minC2TDist))") va.addAlias("pi0_0_clusterTiming", "daughter(1, daughter(0, clusterTiming))") va.addAlias("pi0_1_clusterTiming", "daughter(1, daughter(1, clusterTiming))") pi0_variables = [ "rho_mass", "rho_asym", "pi0_mass", "pi0_0_clusterHasNPhotons", "pi0_1_clusterHasNPhotons", "pi0_0_clusterNHits", "pi0_1_clusterNHits", "pi0_0_minC2TDist", "pi0_1_minC2TDist", "pi0_0_clusterTiming", "pi0_1_clusterTiming", ] # Tau 3prong mass variables va.addAlias("M_12", "daughterInvM(0, 1)") # slow pi- pi+ same charge combination va.addAlias("M_23", "daughterInvM(1, 2)") # fast pi+ pi- combination va.addAlias("M_31", "daughterInvM(0, 2)") # same pi- pi- combination va.addAlias("M_123", "daughterInvM(0, 1, 2)") # pi- pi+ pi- combination prong3_mass_variables = ["M_12", "M_23", "M_31", "M_123"] # Kinematics variables using alias creation kinematics = vu.create_aliases(vc.kinematics, "useCMSFrame({variable})", "km") mc_kinematics = vu.create_aliases(vc.mc_kinematics, "useCMSFrame({variable})", "mc_km") track_hits = vu.create_aliases(vc.track_hits, "{variable}", "track_hits") va.addAlias("EoverP", "formula( ifNANgiveX( clusterE, -1 )/p )") va.addAlias("hasAncTau", "hasAncestor(15)") track_variables = ["charge", "p_rank", "pValue", "mcPDG", "EoverP", "hasAncTau"] # PID pid = vu.create_aliases(vc.pid, "{variable}", "pid") pid += ["muonID_noSVD", "electronID_noSVD_noTOP"] variableList = ( vu.create_aliases_for_selected(list_of_variables=eventVariables, decay_string="^vpho") + vu.create_aliases_for_selected( list_of_variables=kinematics + mc_kinematics + track_hits + track_variables + pid, decay_string="vpho -> [tau+ -> ^pi+] [tau- -> ^pi- ^pi- ^pi+]", ) + vu.create_aliases_for_selected( list_of_variables=prong3_mass_variables + pi0_variables, decay_string="vpho -> tau+ ^tau- ", prefix=["tau"], ) ) self.variables_to_validation_ntuple( decay_str="vpho:tau", variables=variableList, path=mypath, ) return mypath
@property def plotting_strategies(self): return ["per_dataset_comparison", "overlay_datasets_per_component_comparison", "tau_trg_eff"] @property def analysis_validation_histograms(self) -> List[Histogram]: return [ Histogram( name="Thrust", title="Thrust", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="thrust"), label="Thrust", unit="", bins=60, scope=(0.7, 1.0), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), Histogram( name="Visible_Energy", title=r"$E_{vis}$", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="visibleEnergyOfEventCMS"), label=r"$E_{visible}$ in CMS", unit="GeV", bins=50, scope=(0.0, 12.0), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), Histogram( name="M_12", title=r"$M_{12} & M_{23}$ of 3Prong", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="tau_M_12"), label=r"$M_{\pi^{\pm} \pi^{\mp}}$", unit=r"GeV/$c^2$", bins=80, scope=(0.0, 1.8), merged_variable=makeROOTCompatible(variable="tau_M_23"), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), Histogram( name="M_31", title=r"$M_{31}$ of 3Prong", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="tau_M_31"), label=r"$M_{\pi^{\pm}\pi^{\pm}}$", unit=r"GeV/$c^2$", bins=80, scope=(0.0, 1.8), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), Histogram( name="M_123", title=r"$M_{123}$ of 3Prong", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="tau_M_123"), label=r"$M_{2\pi^{\mp}\pi^{\pm}}$", unit=r"GeV/$c^2$", bins=80, scope=(0.0, 1.8), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), Histogram( name="Momentum_1prong", title=r"$P_{tag-track}$", hist_variable=HistVariable( df_label=makeROOTCompatible(variable="tau_0_pi_km_p"), label=r"$Momentum_{tag-track}$", unit="GeV", bins=80, scope=(0.0, 8.0), ), hist_components=[ HistComponent( label="e3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 11) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="mu3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 13) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), HistComponent( label="pi3pi", additional_cut_str="(dmIDTag == 1) and (abs(tau_0_pi_mcPDG) == 211) and (isSignal_tau2 == 1) and (nAllPhotons == 0)", ), ], ), ]
[docs] def get_number_of_signal_for_efficiency(self, df: pd.DataFrame) -> float: return ((abs(df["tau_0_pi_mcPDG"]) == 211) & (df["nAllPhotons"] == 0)).sum()