Belle II Software light-2609-luna
B2A306-B02RhoGamma-withPi0EtaVeto.py
1#!/usr/bin/env python3
2
3
10
11
33
34import basf2 as b2
35import modularAnalysis as ma
36import variables.collections as vc
37import variables.utils as vu
38from stdCharged import stdK
39
40# create path
41my_path = b2.create_path()
42
43# writePi0EtaVeto uses a payload in analysis global tag.
44b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())
45
46# load input ROOT file
47ma.inputMdst(filename=b2.find_file('B2rhogamma_rho2pipi.root', 'examples', False),
48 path=my_path)
49
50ma.fillParticleList(decayString='gamma:highE',
51 cut='E > 1.5',
52 path=my_path)
53ma.fillParticleList(decayString='pi+:loose',
54 cut='abs(d0) < 0.5 and abs(z0) < 0.5 and pionID > 0.002',
55 path=my_path)
56
57# reconstruct rho -> pi+ pi- decay
58# keep only candidates with 0.6 < M(pi+pi-) < 1.0 GeV
59ma.reconstructDecay(decayString='rho0 -> pi+:loose pi-:loose',
60 cut='0.6 < M < 1.0',
61 path=my_path)
62
63# reconstruct B0 -> rho0 gamma decay
64# keep only candidates with Mbc > 5.2 GeV
65# and -2 < Delta E < 2 GeV
66ma.reconstructDecay(decayString='B0 -> rho0 gamma:highE',
67 cut='5.2 < Mbc and abs(deltaE) < 2.0',
68 path=my_path)
69
70# perform MC matching (MC truth association)
71ma.matchMCTruth(list_name='B0',
72 path=my_path)
73
74# build RestOfEvent (ROE) object for each B0 candidate
75# ROE is required by the veto
76ma.buildRestOfEvent(target_list_name='B0',
77 path=my_path)
78
79# perform pi0/eta veto
80# particleList : Signal side particle's particleList
81# decayString : DecayString specifying a particle which is used to calculate the pi0/eta probability
82# mode : One can select the payload from 'standard'(default), 'tight', 'cluster', and 'both'.
83# Each payload is optimized for different soft-photon selection criteria.
84# If one wants to use one's own payload and soft-photon criteria, please use arguments,
85# pi0PayloadNameOverride, pi0SoftPhotonCutOverride, etaPayloadNameOverride, etaSoftPhotonCutOverride,
86modePi0EtaVeto = 'standardMC16rd'
87ma.writePi0EtaVeto(particleList='B0',
88 decayString='B0 -> rho0 ^gamma',
89 mode=modePi0EtaVeto,
90 path=my_path)
91
92# Add the pi0 veto Data/MC efficiency ratio weights for a given veto threshold.
93# They are stored in payloads named '{tableName}_{mode}{thresholdPercent}' with
94# - tableName: the version of the weight tables (here 'Pi0VetoEfficiencySystematics_Mar2022'),
95# - mode: the pi0 veto mode the weights were derived for ('standard', 'tight', 'cluster' or 'both'),
96# - thresholdPercent: the veto threshold in percent as a two-digit integer (here 30 for 0.30).
97# The weights depend on the hard photon energy. The ParticleWeighting module stores them as
98# extraInfo of the hard photon selected with the '^' in the decay string.
99pi0VetoWeightTable = 'Pi0VetoEfficiencySystematics_Mar2022_standard30'
100my_path.add_module('ParticleWeighting',
101 particleList='B0',
102 selectedDaughters='B0 -> rho0 ^gamma',
103 tableName=pi0VetoWeightTable)
104
105# Then one can obtain the pi0/eta probability by the variables pi0Prob(arg) and etaProb(arg).
106# The argument corresponds to the mode which you set in writePi0EtaVeto function.
107# In above case, one can call pi0Probe(standard) and etaProb(standard).
108# For the B0 candidates whose gamma daughter could not be combined with
109# any of the remaining photons to form pi0/eta because of soft photon selection
110# NaN will be written to the pi0Probe(standard) branch and etaProb(standard) branch.
111
112# For the validation purpose, one may want to calculate the pi0/eta probability using a particle other than a photon.
113# Example : B+ -> anti-D0 pi+. This is one of the modes to validate the pi0/eta veto tool.
114stdK('loose', path=my_path)
115ma.reconstructDecay("D0:Kpi -> K-:loose pi+:loose", "", path=my_path)
116ma.reconstructDecay("B+:Dpi -> anti-D0:Kpi pi+:loose", "useCMSFrame(daughter(1,E))>1.4", path=my_path)
117ma.matchMCTruth("B+:Dpi", path=my_path)
118ma.buildRestOfEvent("B+:Dpi", path=my_path)
119
120# hardParticle : If one wants to use non-gamma particle to calculate the pi0/eta probability,
121# you have to tell the particle name with an argument hardParticle. (default: gamma)
122ma.writePi0EtaVeto(particleList='B+:Dpi',
123 decayString='B+ -> [anti-D0 -> K+ pi-] ^pi+',
124 mode='standard',
125 hardParticle='pi+',
126 path=my_path)
127
128
129# The weight files are optimised by MC campaign 12.
130# If you train by yourself, you should refer to
131# B2A701-ContinuumSuppression_Input.py
132# B2A702-ContinuumSuppression_MVATrain.py
133
134
135# You can also do a simple veto using delta mass ranking as below.
136
137# VETO starts here
138# ----------------
139
140# Create a new path (called ROE path) which will be executed for
141# each ROE in an event.
142# Note that ROE exists for each B0 candidate, so when we loop
143# over each ROE, we effectively loop over signal B0 candidates
144
145roe_path = b2.create_path()
146
147# The ROE objects might in general be related to Particle from multiple
148# particle lists therefore we need to check if the current ROE object
149# is related to the Particle from our signal decay. If it is not
150# the execution of roe_path will be finished (by starting empty,
151# dead end path). Note that in this example this x-check is not
152# necessary, but is anyway added for sake of completeness
153deadEndPath = b2.create_path()
154
155# Note again: all actions (modules) included in roe_path will be
156# executed for each ROE in the event
157# First we check that the current ROE is related to B0 candidate
158ma.signalSideParticleFilter(particleList='B0',
159 selection='',
160 roe_path=roe_path,
161 deadEndPath=deadEndPath)
162
163# create and fill gamma ParticleList that will contain
164# all photons found in ROE (not used to reconstruct current B0 candidate)
165# The photons need to have energy above 50 MeV to be considered
166# (one can add any cut)
167ma.fillParticleList(decayString='gamma:roe',
168 cut='isInRestOfEvent == 1 and E > 0.050',
169 path=roe_path)
170
171# in order to be able to use modularAnalysis functions (reconstructDecay in particular)
172# we need a ParticleList containing the photon candidate used to reconstruct the
173# current B meson as well
174# The DecayString is used to specify the selected particle (^)
175ma.fillSignalSideParticleList(outputListName='gamma:sig',
176 decayString='B0 -> rho0 ^gamma',
177 path=roe_path)
178
179# make combinations of signal photon candidates with all photons from ROE
180# keep only combinations in given invariant mass range
181ma.reconstructDecay(decayString='pi0:veto -> gamma:sig gamma:roe',
182 cut='0.080 < M < 0.200',
183 path=roe_path)
184
185# at this point one could use all features provided by the analysis software
186# to make the veto as effective as possible. For example, one can perform truth
187# matching, training/applying TMVA classifier, save pi0 candidates with ntuple
188# maker for offline analysis/study.
189
190# in this example the variable, which is used to veto pi0 is very simple:
191# invariant mass of pi0 that is closest to the pi0's nominal mass
192# Therefore, we just simply rank pi0 candidates according to their distance
193# from nominal mass (dM variable) and keep only the best candidate
194ma.rankByLowest(particleList='pi0:veto',
195 variable='abs(dM)',
196 numBest=1,
197 path=roe_path)
198
199# write the invariant mass of the best pi0 candidate to the current B0
200# candidate as the 'pi0veto' extraInfo
201ma.variableToSignalSideExtraInfo(particleList='pi0:veto', varToExtraInfo={'M': 'pi0veto'}, path=roe_path)
202
203# execute roe_path for each RestOfEvent in the event
204my_path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
205
206# VETO ends here
207# ----------------
208
209# we're now out of the ROE path
210# at this stage the B0 candidates should have
211# extraInfo(pi0veto) value attached. For the B0
212# candidates whose gamma daughter could not be combined with
213# any of the remaining photons to form pi0 within given mass
214# range the extraInfo(pi0veto) does not exist. In these cases
215# NaN will be written to the extraInfo(pi0veto) branch
216# Select variables that we want to store to ntuple
217
218gamma_vars = vc.cluster + \
219 vc.mc_truth + \
220 vc.kinematics
221
222rho_vars = vc.cluster + \
223 vc.mc_truth + \
224 vc.kinematics + \
225 vc.inv_mass
226
227pi_vars = vc.track
228
229# pi0 veto Data/MC weights, which are stored as extraInfo of the hard photon
230pi0veto_weight_vars = [f'extraInfo({pi0VetoWeightTable}_{name})' for name in
231 ['data_MC_ratio', 'data_MC_uncertainty_stat', 'data_MC_uncertainty_sys',
232 'data_MC_uncertainty_total', 'threshold']]
233
234b_vars = vc.kinematics + \
235 vc.deltae_mbc + \
236 vc.mc_truth + \
237 vu.create_aliases_for_selected(list_of_variables=gamma_vars,
238 decay_string='B0 -> rho0 ^gamma') + \
239 vu.create_aliases_for_selected(list_of_variables=rho_vars,
240 decay_string='B0 -> ^rho0 gamma') + \
241 vu.create_aliases_for_selected(list_of_variables=pi_vars,
242 decay_string='B0 -> [rho0 -> ^pi+ ^pi-] gamma') + \
243 [f'pi0Prob({modePi0EtaVeto})', f'etaProb({modePi0EtaVeto})', 'extraInfo(pi0veto)'] + \
244 vu.create_aliases_for_selected(list_of_variables=pi0veto_weight_vars,
245 decay_string='B0 -> rho0 ^gamma')
246
247
248# Saving variables to ntuple
249rootOutputFile = "B2A306-B02RhoGamma-withPi0EtaVeto.root"
250ma.variablesToNtuple(decayString='B0',
251 variables=b_vars,
252 filename=rootOutputFile,
253 treename='b0',
254 path=my_path)
255
256# Process the events
257b2.process(my_path, calculateStatistics=True)