from typing import List
import pandas as pd
import basf2
import modularAnalysis as ma
import vertex as vx
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
__all__ = [
"BtoPiLNuValidationMode",
]
[docs]
@fancy_validation_mode_header
class BtoPiLNuValidationMode(ValidationModeBaseClass):
name = "BtoPiLNu"
latex_str = r"$B \rightarrow \pi \ell \nu$"
[docs]
def create_basf2_path(self):
main_path = basf2.Path()
# fsp selection
electrons = ("e-:rec", "useCMSFrame(p) > 1.0 and electronID > 0.8")
muons = ("mu-:rec", "useCMSFrame(p) > 1.0 and muonID > 0.8")
pions = ("pi-:rec", "pionID > 0.8")
ma.fillParticleLists(
decayStringsWithCuts=[electrons, muons, pions],
path=main_path,
)
# B meson reconstruction
ma.reconstructDecay(
decayString="B0:e_rec -> pi-:rec e+:rec",
cut="",
dmID=1,
path=main_path,
)
ma.reconstructDecay(
decayString="B0:mu_rec -> pi-:rec mu+:rec",
cut="",
dmID=2,
path=main_path,
)
# merge + MC matching + vertex
ma.copyLists(
outputListName="B0:sl_rec",
inputListNames=["B0:e_rec", "B0:mu_rec"],
path=main_path,
)
ma.matchMCTruth(
"B0:sl_rec",
path=main_path,
)
vx.treeFit("B0:sl_rec", 0.001, ipConstraint=True, path=main_path)
# select signal + build ROE
ma.buildRestOfEvent("B0:sl_rec", path=main_path)
# define ROE mask cuts
tracksCut = "abs(d0) < 10.0 and abs(z0) < 20.0"
clustersCut1 = "[clusterE1E9 > 0.4 or E > 0.075] and [[E > 0.062 and abs(clusterTiming)<18 and clusterReg==1] or "
clustersCut2 = (
"[E > 0.060 and abs(clusterTiming)<20 and clusterReg==2]"
" or [E > 0.056 and abs(clusterTiming)<44 and clusterReg==3]]"
)
clustersCut = clustersCut1 + clustersCut2
# define and append ROE masks
m0 = ("m0", "", "")
m1 = ("m1", tracksCut, clustersCut)
ma.appendROEMasks("B0:sl_rec", [m0, m1], path=main_path)
ma.buildEventShape(inputListNames=["B0:sl_rec"], path=main_path)
ma.buildEventKinematics(inputListNames=["B0:sl_rec"], path=main_path)
self.variables_to_validation_ntuple(
decay_str="B0:sl_rec",
variables=list(
{
"cosThetaBetweenParticleAndNominalB",
"isSignalAcceptMissingNeutrino",
"decayTypeRecoil",
"roeCharge(m0)",
"roeCharge(m1)",
"roeMC_MissFlags(m0)",
"roeMC_MissFlags(m1)",
"weDeltae(m0, 0)",
"weDeltae(m1, 0)",
"weMbc(m0, 0)",
"weMbc(m1, 0)",
"weMissM2OverMissE(m0)",
"weMissM2OverMissE(m1)",
"weMissM2(m0,0)",
"weMissM2(m1,0)",
}
),
path=main_path,
)
return main_path
@property
def analysis_validation_histograms(self) -> List[Histogram]:
return [
Histogram(
name="Mbc",
title="",
hist_variable=HistVariable(
df_label=makeROOTCompatible(variable="weMbc(m1, 0)"),
label=r"$M_{bc}$ (cleaned ROE)",
unit=r"GeV/$c^2$",
bins=50,
scope=(5.24, 5.29),
),
hist_components=[
HistComponent(
label="All",
),
],
)
]
[docs]
def offline_df_manipulation(self, df: pd.DataFrame) -> pd.DataFrame:
df = df.sample(frac=1.0).groupby(by=["__event__"]).head(1) # Applying rand BCS offline
return df
[docs]
def get_number_of_signal_for_efficiency(self, df: pd.DataFrame) -> float:
return df["isSignalAcceptMissingNeutrino"].sum()