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