Belle II Software  light-2403-persian
graFEI_ntupliser_Upsilon.py
1 #!/usr/bin/env python3
2 
3 
10 
11 
12 import basf2 as b2
13 import modularAnalysis as ma
14 from variables import variables as vm
15 import stdPhotons
16 
17 # Necessary to run argparse
18 from ROOT import PyConfig
19 PyConfig.IgnoreCommandLineOptions = True # noqa
20 
21 import random
22 import argparse
23 
24 from grafei import graFEI
25 
26 
27 # Random seeds
28 b2.set_random_seed(42)
29 random.seed(42)
30 
31 
32 def _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 
70 if __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