How can we distinguish between a and a ? This is not
as simple as the case where we have a vs a and can just
consider the charge of the particles. Yet determining this “flavor” of the
meson is crucial to look into CP violation.
This is where the Flavor Tagger comes in. Used in an analysis, where we have
reconstructed a signal , the Flavor Tagger looks at the ROE
of the (i.e. at the ) and applies
machine learning techniques (also called multivariate analysis) to determine the
flavor of the .
In this lesson we will not train this model ourselves, but rather use
pre-computed weights. So where do we get them?
Luckily, such sets of weights are contained in the conditions database.
That means that we only need to use a specific global tag (if you forgot what
that is, consult this page again: Conditions Database Overview).
Add the the
flavorTagger.flavor_tagging variable collection to your output
variables
Solution
b_vars+=ft.flavor_tagging# [S43|E43]
Exercise
Run your steering file!
Solution
This is the full steering file at this point:
#!/usr/bin/env python3importsysimportbasf2asb2importmodularAnalysisasmaimportstdV0simportflavorTaggerasft# [S23|E23]fromvariablesimportvariablesasvm# shorthand for VariableManagerimportvariables.collectionsasvcimportvariables.utilsasvu# get input file number from the command linefilenumber=sys.argv[1]# set analysis global tag (needed for flavor tagging) [S10]b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())# [E10]# create pathmain=b2.Path()# load input data from mdst/udst filema.inputMdstList(filelist=[b2.find_file(f"starterkit/2021/1111540100_eph3_BGx0_{filenumber}.root","examples")],path=main,)# fill final state particle listsma.fillParticleList("e+:uncorrected","electronID > 0.1 and dr < 0.5 and abs(dz) < 2 and thetaInCDCAcceptance",path=main,)stdV0s.stdKshorts(path=main)# apply Bremsstrahlung correction to electronsvm.addAlias("goodFWDGamma","passesCut(clusterReg == 1 and clusterE > 0.075)")vm.addAlias("goodBRLGamma","passesCut(clusterReg == 2 and clusterE > 0.05)")vm.addAlias("goodBWDGamma","passesCut(clusterReg == 3 and clusterE > 0.1)")vm.addAlias("goodGamma","passesCut(goodFWDGamma or goodBRLGamma or goodBWDGamma)")ma.fillParticleList("gamma:brems","goodGamma",path=main)ma.correctBrems("e+:corrected","e+:uncorrected","gamma:brems",path=main)vm.addAlias("isBremsCorrected","extraInfo(bremsCorrected)")# combine final state particles to form composite particlesma.reconstructDecay("J/psi:ee -> e+:corrected e-:corrected ?addbrems",cut="abs(dM) < 0.11",path=main,)# combine J/psi and KS candidates to form B0 candidatesma.reconstructDecay("B0 -> J/psi:ee K_S0:merged",cut="Mbc > 5.2 and abs(deltaE) < 0.15",path=main,)# match reconstructed with MC particlesma.matchMCTruth("B0",path=main)# build the rest of the eventma.buildRestOfEvent("B0",fillWithMostLikely=True,path=main)track_based_cuts="thetaInCDCAcceptance and pt > 0.075 and dr < 5 and abs(dz) < 10"ecl_based_cuts="thetaInCDCAcceptance and E > 0.05"roe_mask=("my_mask",track_based_cuts,ecl_based_cuts)ma.appendROEMasks("B0",[roe_mask],path=main)# call flavor tagging [S20]ft.flavorTagger(["B0"],path=main)# [E20]# perform best candidate selectionb2.set_random_seed("Belle II StarterKit")ma.rankByHighest("B0",variable="random",numBest=1,path=main)# Create list of variables to save into the output fileb_vars=[]standard_vars=vc.kinematics+vc.mc_kinematics+vc.mc_truthb_vars+=vc.deltae_mbcb_vars+=standard_vars# ROE variablesroe_kinematics=["roeE()","roeM()","roeP()","roeMbc()","roeDeltae()"]roe_multiplicities=["nROE_Charged()","nROE_Photons()","nROE_NeutralHadrons()",]b_vars+=roe_kinematics+roe_multiplicities# Let's also add a version of the ROE variables that includes the mask:forroe_variableinroe_kinematics+roe_multiplicities:# e.g. instead of 'roeE()' (no mask) we want 'roeE(my_mask)'roe_variable_with_mask=roe_variable.replace("()","(my_mask)")b_vars.append(roe_variable_with_mask)b_vars+=ft.flavor_tagging# [S43|E43]# Variables for final states (electrons, positrons, pions)fs_vars=vc.pid+vc.track+vc.track_hits+standard_varsb_vars+=vu.create_aliases_for_selected(fs_vars+["isBremsCorrected"],"B0 -> [J/psi -> ^e+ ^e-] K_S0",prefix=["ep","em"],)b_vars+=vu.create_aliases_for_selected(fs_vars,"B0 -> J/psi [K_S0 -> ^pi+ ^pi-]",prefix=["pip","pim"])# Variables for J/Psi, KSjpsi_ks_vars=vc.inv_mass+standard_varsb_vars+=vu.create_aliases_for_selected(jpsi_ks_vars,"B0 -> ^J/psi ^K_S0")# Add the J/Psi mass calculated with uncorrected electrons:vm.addAlias("Jpsi_M_uncorrected","daughter(0, daughterCombination(M,0:0,1:0))")b_vars+=["Jpsi_M_uncorrected"]# Also add kinematic variables boosted to the center of mass frame (CMS)# for all particlescmskinematics=vu.create_aliases(vc.kinematics,"useCMSFrame({variable})","CMS")b_vars+=vu.create_aliases_for_selected(cmskinematics,"^B0 -> [^J/psi -> ^e+ ^e-] [^K_S0 -> ^pi+ ^pi-]")vm.addAlias("withBremsCorrection","passesCut(passesCut(ep_isBremsCorrected == 1) or passesCut(em_isBremsCorrected == 1))",)b_vars+=["withBremsCorrection"]# Save variables to an output file (ntuple)ma.variablesToNtuple("B0",variables=b_vars,filename="Bd2JpsiKS.root",treename="tree",path=main,)# Start the event loop (actually start processing things)b2.process(main)# print out the summaryprint(b2.statistics)
Good!
Now let’s talk about the output of the flavor tagger. This is the value
, where corresponds to a and
to . is called the dilution factor. It’s 0
if the algorighm can’t decide between both options for and 1 if the
algorithm is certain about it’s decision.
The variable FBDT_qrCombined is the result of one of the
models of the Flavor Tagger (a fast boosted decision tree).
It can also be NaN to signal that not a single charged
track in the ROE was found, so that the algorithm can’t work.
Note
In releases before release-05, a value of was used instead of
NaN.
Part of the variables you just added was also qrMC for the “true” (MC level) flavor of the .
It can take the numbers
, as well as 0 (no flavor defined in the MC) and
(some problems with MC matching in the ROE).
That means that we can check how well our flavor tagger performed by comparing
it to FBDT_qrCombined!
Exercise (optional)
Plot a histogram of FBDT_qrCombined and qrMC.
Exercise
Only consider candidates with clearly defined MC level flavor tag.
Compare the output of qrMC to that of FBDT_qrCombined.
Hint
For clearly defined MC flavor tag, you only have to distinguish between
qrMC==0 and qrMC==1. For these two cases you can then plot
the distribution of FBDT_qrCombined.
Solution
########################################################################### 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. ###########################################################################importmatplotlib.pyplotaspltimportuprootplt.style.use("belle2")df=uproot.open("Bd2JpsiKS.root:tree").arrays(['qrMC','FBDT_qrCombined'],library='pd')fig,ax=plt.subplots()ax.hist(df.query("qrMC == -1.")["FBDT_qrCombined"],histtype="step",linewidth=1.5,label=r"True $\bar B^0$",bins=30,)ax.hist(df.query("qrMC == 1.")["FBDT_qrCombined"],histtype="step",linewidth=1.5,label=r"True $B^0$",bins=30,)ax.set_xlabel("FBDT_qrCombined")ax.legend()fig.savefig("flavor_tags.svg")
You can clearly see that the flavor tagger is by no means perfect, but
definitely allows to do better than just guessing!
Key points
The flavor tagger is used to discriminate between and
The output is of the form times the confidence between 0 and
1.
Stuck? We can help!
If you get stuck or have any questions to the online book material, the
#starterkit-workshop channel
in our chat is full of nice people who will provide fast help.
Refer to Collaborative Tools. for other
places to get help if you have specific or detailed questions about your
own analysis.
Improving things!
If you know how to do it, we recommend you to report bugs and other requests
with GitLab.
Make sure to use the documentation-training label of the basf2 project.
If you just want to give very quick feedback, use the last box
“Quick feedback”.
Please make sure to be as precise as possible to make it easier for us
to fix things! So for example:
typos (where?)
missing bits of information (what?)
bugs (what did you do? what goes wrong?)
too hard exercises (which one?)
etc.
If you are familiar with git and want to create your first merge request
for the software, take a look at How to contribute.
We’d be happy to have you on the team!