Belle II Software light-2405-quaxo
graFEI_ntupliser_Upsilon.py
1#!/usr/bin/env python3
2
3
10
11
12import basf2 as b2
13import modularAnalysis as ma
14from variables import variables as vm
15import stdPhotons
16
17# Necessary to run argparse
18from ROOT import PyConfig
19PyConfig.IgnoreCommandLineOptions = True # noqa
20
21import random
22import argparse
23
24from grafei import graFEI
25
26
27# Random seeds
28b2.set_random_seed(42)
29random.seed(42)
30
31
32def _parse_args():
33 parser = argparse.ArgumentParser()
34 parser.add_argument(
35 "-c", "--config", type=str, default=None, help="graFEI config file"
36 )
37 parser.add_argument(
38 "-w", "--weight", type=str, default=None, help="graFEI weight file"
39 )
40 parser.add_argument(
41 "-g",
42 "--globaltag",
43 type=str,
44 default="analysis_tools_light-2403-persian",
45 help="Globaltag containing graFEI model",
46 )
47 parser.add_argument(
48 "-l",
49 "--lcas",
50 type=list,
51 default=[[0]],
52 help="Choose LCAS matrix of signal side",
53 )
54 parser.add_argument(
55 "-m",
56 "--masses",
57 type=list,
58 default=["k"],
59 help="Choose mass hypotheses of signal side",
60 )
61 parser.add_argument(
62 "-n",
63 "--no_mc_truth",
64 action="store_true",
65 help="Choose not to store MC-truth information",
66 )
67 return parser.parse_args()
68
69
70if __name__ == "__main__":
71 args = _parse_args()
72
73 store_mc_truth = not args.no_mc_truth
74
75 path = b2.create_path()
76 ma.inputMdst(filename=b2.find_file('mdst14.root', 'validation', False), path=path)
77
78 b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())
79 if args.globaltag:
80 b2.conditions.prepend_globaltag(args.globaltag)
81
82
87
88 # 1) Cuts on charged tracks and photons
89 # These priors were obtained by counting truth-matched tracks in BB mixed MC
90 # It could be modified by the user if needed
91 priors = [0.068, 0.050, 0.7326, 0.1315, 0.0183, 0.00006]
92
93 charged_cuts = [
94 f"pidIsMostLikely({','.join(str(p) for p in priors)})>0",
95 "nCDCHits>20",
96 "thetaInCDCAcceptance",
97 "abs(dz)<1.0",
98 "dr<0.5",
99 "p<5",
100 "pt>0.2",
101 ]
102
103 photon_cuts = [
104 "beamBackgroundSuppression>0.4",
105 "fakePhotonSuppression>0.3",
106 "abs(clusterTiming)<100",
107 "abs(formula(clusterTiming/clusterErrorTiming))<2.0",
108 "[[clusterReg==1 and E>0.09] or [clusterReg==2 and E>0.09] or [clusterReg==3 and E>0.14]]",
109 ]
110
111 evt_cut = "n_gamma_in_evt<20 and n_charged_in_evt<20"
112
113 # Fill particle list with optimized cuts
114 charged_lists = [f"{c}:final" for c in ["p+", "e+", "pi+", "mu+", "K+"]]
115
116 ma.fillParticleLists(
117 [(c, " and ".join(charged_cuts)) for c in charged_lists],
118 writeOut=True,
119 path=path,
120 )
121
123 listtype="tight",
124 beamBackgroundMVAWeight="MC15ri",
125 fakePhotonMVAWeight="MC15ri",
126 path=path,
127 )
128
129 ma.cutAndCopyList(
130 "gamma:final",
131 "gamma:tight",
132 " and ".join(photon_cuts),
133 writeOut=True,
134 path=path,
135 )
136
137 # Add requirements on total number of photons and charged in event
138 vm.addAlias("n_gamma_in_evt", "nParticlesInList(gamma:final)")
139 vm.addAlias("n_p_in_evt", "nParticlesInList(p+:final)")
140 vm.addAlias("n_e_in_evt", "nParticlesInList(e+:final)")
141 vm.addAlias("n_mu_in_evt", "nParticlesInList(mu+:final)")
142 vm.addAlias("n_pi_in_evt", "nParticlesInList(pi+:final)")
143 vm.addAlias("n_K_in_evt", "nParticlesInList(K+:final)")
144 vm.addAlias(
145 "n_charged_in_evt",
146 "formula(n_p_in_evt+n_e_in_evt+n_mu_in_evt+n_pi_in_evt+n_K_in_evt)",
147 )
148
149 ma.applyEventCuts(evt_cut, path=path)
150
151 particle_lists = charged_lists + ["gamma:final"]
152 particle_types = [x.split(":")[0] for x in particle_lists]
153 charged_types = [x.split(":")[0] for x in charged_lists]
154
155 ma.combineAllParticles(
156 [f"{part}:final" for part in particle_types], "Upsilon(4S):final", path=path
157 )
158
159 if store_mc_truth:
160 ma.fillParticleListFromMC("Upsilon(4S):MC", "", path=path)
161
162 # Flag each particle according to the B meson and decay it came from
163 for i, particle_list in enumerate(particle_lists):
164 # Match MC particles for all lists
165 ma.matchMCTruth(particle_list, path=path)
166
167 graFEI(
168 "Upsilon(4S):final",
169 cfg_path=args.config,
170 param_file=args.weight,
171 sig_side_lcas=args.lcas,
172 sig_side_masses=args.masses,
173 payload_config_name="graFEIConfigFile_Upsreco_example", # If you use default payload name just remove this argument
174 payload_model_name="graFEIModelFile_Upsreco_example", # If you use default payload name just remove this argument
175 path=path,
176 )
177
178 # Define signal-side B
179 # Here we consider all charged basf2 mass hypotheses
180 # since not necessarily they coincide with those
181 # predicted by graFEI which we require to be kaons
182 for part in charged_types:
183 ma.reconstructDecay(
184 f"B+:{part[:-1]} -> {part}:final",
185 "daughter(0, extraInfo(graFEI_sigSide)) == 1",
186 path=path,
187 )
188 ma.copyLists("B+:Bsgn", [f"B+:{part[:-1]}" for part in charged_types], path=path)
189
190 # Define tag-side B
191 for part in particle_types:
192 ma.cutAndCopyList(
193 f"{part}:Btag",
194 f"{part}:final",
195 cut="extraInfo(graFEI_sigSide) == 0",
196 writeOut=True,
197 path=path,
198 )
199 ma.combineAllParticles([f"{part}:Btag" for part in particle_types], "B+:Btag", path=path)
200
201 # 3) Keep only "good events" i.e. validTree + signal side matches chosen LCAS and mass hypotheses
202 ma.reconstructDecay(
203 "Upsilon(4S):neutral -> B+:Bsgn B-:Btag", "", path=path
204 )
205 ma.reconstructDecay(
206 "Upsilon(4S):charged -> B+:Bsgn B+:Btag", "", allowChargeViolation=True, path=path
207 )
208
209 ma.copyLists(
210 "Upsilon(4S):graFEI",
211 [
212 "Upsilon(4S):neutral",
213 "Upsilon(4S):charged",
214 ],
215 path=path,
216 )
217
218 # Reject events with no signal candidates
219 skimfilter = b2.register_module("SkimFilter")
220 skimfilter.param("particleLists", ["Upsilon(4S):graFEI"])
221 empty_path = b2.create_path()
222 skimfilter.if_value("=0", empty_path, b2.AfterConditionPath.END)
223 path.add_module(skimfilter)
224
225 if store_mc_truth:
226 ma.matchMCTruth("B+:Bsgn", path=path)
227 ma.matchMCTruth("B-:Btag", path=path)
228 ma.matchMCTruth("Upsilon(4S):graFEI", path=path)
229
230
233
234 # Variables
235 momentum_vars = [
236 "p",
237 "px",
238 "py",
239 "pz",
240 "pt",
241 "E",
242 ]
243 default_vars = [
244 "PDG",
245 "M",
246 "Mbc",
247 "deltaE",
248 ]
249 default_vars += momentum_vars
250
251 tm_vars = [
252 "mcErrors",
253 "genMotherPDG",
254 "mcPDG",
255 ] if store_mc_truth else []
256
257 # graFEI variables
258 graFEI_vars = [
259 "graFEI_probEdgeProd",
260 "graFEI_probEdgeMean",
261 "graFEI_probEdgeGeom",
262 # "graFEI_validTree", # Always 1 in this case
263 # "graFEI_goodEvent", # Always 1 in this case
264 "graFEI_nFSP",
265 "graFEI_nCharged_preFit",
266 "graFEI_nElectrons_preFit",
267 "graFEI_nMuons_preFit",
268 "graFEI_nPions_preFit",
269 "graFEI_nKaons_preFit",
270 "graFEI_nProtons_preFit",
271 "graFEI_nLeptons_preFit",
272 "graFEI_nPhotons_preFit",
273 "graFEI_nOthers_preFit",
274 "graFEI_nCharged_postFit",
275 "graFEI_nElectrons_postFit",
276 "graFEI_nMuons_postFit",
277 "graFEI_nPions_postFit",
278 "graFEI_nKaons_postFit",
279 "graFEI_nProtons_postFit",
280 "graFEI_nLeptons_postFit",
281 "graFEI_nPhotons_postFit",
282 "graFEI_nOthers_postFit",
283 "graFEI_nPredictedUnmatched",
284 "graFEI_nPredictedUnmatched_noPhotons",
285 ]
286
287 graFEI_tm_vars = [
288 "graFEI_truth_perfectLCA",
289 "graFEI_truth_perfectMasses",
290 "graFEI_truth_perfectEvent",
291 "graFEI_truth_nFSP",
292 "graFEI_truth_nPhotons",
293 "graFEI_truth_nElectrons",
294 "graFEI_truth_nMuons",
295 "graFEI_truth_nPions",
296 "graFEI_truth_nKaons",
297 "graFEI_truth_nProtons",
298 "graFEI_truth_nOthers",
299 ] if store_mc_truth else []
300
301 default_vars += tm_vars
302 graFEI_vars += graFEI_tm_vars
303
304 ma.variablesToEventExtraInfo(
305 "Upsilon(4S):final",
306 dict((f"extraInfo({var})", var) for var in graFEI_vars),
307 path=path,
308 )
309
310 # Aliases
311 for var in default_vars:
312 vm.addAlias(f"Bsgn_{var}", f"daughter(0, {var})")
313 vm.addAlias(f"Bsgn_d0_{var}", f"daughter(0, daughter(0, {var}))")
314 vm.addAlias(f"Btag_{var}", f"daughter(1, {var})")
315 vm.addAlias(f"Ups_{var}", var)
316 for var in momentum_vars:
317 vm.addAlias(
318 f"Bsgn_d0_CMS_{var}", f"useCMSFrame(daughter(0, daughter(0, {var})))"
319 )
320 vm.addAlias(f"Btag_CMS_{var}", f"useCMSFrame(daughter(1, {var}))")
321 for var in graFEI_vars:
322 vm.addAlias(var, f"eventExtraInfo({var})")
323
324 all_vars = (
325 graFEI_vars
326 + [f"Bsgn_{var}" for var in tm_vars]
327 + [f"Bsgn_d0_{var}" for var in default_vars]
328 + [f"Bsgn_d0_CMS_{var}" for var in momentum_vars]
329 + [f"Btag_{var}" for var in default_vars]
330 + [f"Btag_CMS_{var}" for var in momentum_vars]
331 + [f"Ups_{var}" for var in default_vars]
332 )
333
334 ma.variablesToNtuple(
335 "Upsilon(4S):graFEI",
336 sorted(all_vars),
337 filename="graFEI_UpsReco_example.root",
338 treename="tree",
339 path=path,
340 )
341
342 # Process
343 b2.process(path)
344
345 # print out the summary
346 print(b2.statistics)
def stdPhotons(listtype='loose', path=None, beamBackgroundMVAWeight="", fakePhotonMVAWeight="", biasCorrectionTable="")
Definition: stdPhotons.py:19