Belle II Software  light-2403-persian
graFEI_ntupliser_B.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 from stdCharged import stdK, stdPi
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 # Random seeds
27 b2.set_random_seed(42)
28 random.seed(42)
29 
30 
31 def _parse_args():
32  parser = argparse.ArgumentParser()
33  parser.add_argument(
34  "-c", "--config", type=str, default=None, help="graFEI config file"
35  )
36  parser.add_argument(
37  "-w", "--weight", type=str, default=None, help="graFEI weight file"
38  )
39  parser.add_argument(
40  "-g",
41  "--globaltag",
42  type=str,
43  default="analysis_tools_light-2403-persian",
44  help="Globaltag containing graFEI model",
45  )
46  return parser.parse_args()
47 
48 
49 if __name__ == "__main__":
50  args = _parse_args()
51 
52  store_mc_truth = True
53 
54  path = b2.create_path()
55  ma.inputMdst(filename=b2.find_file('mdst14.root', 'validation', False), path=path)
56 
57  b2.conditions.prepend_globaltag(ma.getAnalysisGlobaltag())
58  if args.globaltag:
59  b2.conditions.prepend_globaltag(args.globaltag)
60 
61  # These priors were obtained by counting truth-matched tracks in BB mixed MC
62  # It could be modified by the user if needed
63  priors = [0.068, 0.050, 0.7326, 0.1315, 0.0183, 0.00006]
64 
65  cut_charged_graFEI = [
66  f"pidIsMostLikely({','.join(str(p) for p in priors)})>0",
67  "nCDCHits>20",
68  "thetaInCDCAcceptance",
69  "abs(dz)<1.0",
70  "dr<0.5",
71  "p<5",
72  "pt>0.2",
73  ]
74  cut_photons_graFEI = [
75  "beamBackgroundSuppression>0.4",
76  "fakePhotonSuppression>0.3",
77  "abs(clusterTiming)<100",
78  "abs(formula(clusterTiming/clusterErrorTiming))<2.0",
79  "[[clusterReg==1 and E>0.09] or [clusterReg==2 and E>0.09] or [clusterReg==3 and E>0.14]]",
80  ]
81  cut_KL0_graFEI = "PDG==0" # Reject KL0 for the moment
82 
83  # Additional cuts from stdPhotons tight list are added
84  cut_photons_graFEI += [
85  "inCDCAcceptance",
86  "[clusterErrorTiming < 1e6 and [clusterE1E9 > 0.4 or E > 0.075]]",
87  ]
88 
89  # How to deal with multiple signal side candidates
90  ranking_variable = "abs(cosThetaBetweenParticleAndNominalB)" # "random" # abs(cosThetaBetweenParticleAndNominalB)
91  # ----------------------------------------------------------------------------------------------------------
92 
93  cut_charged_graFEI = " and ".join(cut_charged_graFEI)
94  cut_photons_graFEI = " and ".join(cut_photons_graFEI)
95 
96  # Load MVA's for all gamma
97  ma.fillParticleList(
98  "gamma:all",
99  "",
100  path=path,
101  )
102  ma.getBeamBackgroundProbability("gamma:all", "MC15ri", path=path)
103  ma.getFakePhotonProbability("gamma:all", "MC15ri", path=path)
104 
105  # -----------------------Signal side reconstruction------------------------------------
106 
107  stdK("loose", path=path)
108  stdPi("loose", path=path)
109 
110  ma.reconstructDecay(
111  "D-:Kpipi -> K+:loose pi-:loose pi-:loose", "1.85 < M < 1.88", path=path
112  )
113  ma.fillParticleList(
114  "mu+:tight",
115  "abs(dz)<2 and dr<0.5 and nCDCHits>20 and thetaInCDCAcceptance and pt>0.6 and E<5.5 and muonID_noSVD>0.9",
116  path=path,
117  )
118  ma.reconstructDecay(
119  "B0:sig -> D-:Kpipi mu+:tight",
120  "abs(cosThetaBetweenParticleAndNominalB)<2",
121  path=path,
122  )
123 
124  # ROE
125  ma.buildRestOfEvent("B0:sig", path=path)
126 
127  # Basic cuts to evaluate truth-matching on ROE
128  basicMask = (
129  "basicMask",
130  f"pidIsMostLikely({','.join(str(p) for p in priors)})>0 and nCDCHits>0 and thetaInCDCAcceptance and abs(dz)<4 and dr<2",
131  "inCDCAcceptance and clusterErrorTiming<1e6 and [clusterE1E9>0.4 or E>0.075] and [[clusterReg == 1 and E > 0.05]"
132  " or [clusterReg == 2 and E > 0.05] or [clusterReg == 3 and E > 0.075]]",
133  )
134 
135  # Continuum suppression
136  csMask = (
137  "csMask",
138  "nCDCHits > 0 and useCMSFrame(p)<=3.2",
139  "p >= 0.05 and useCMSFrame(p)<=3.2",
140  )
141 
142  ma.appendROEMasks(list_name="B0:sig", mask_tuples=[csMask, basicMask], path=path)
143 
144  ma.buildContinuumSuppression(list_name="B0:sig", roe_mask="csMask", path=path)
145 
146  ma.applyCuts("B0:sig", "cosTBz<0.9", path=path)
147 
148  # Reject events with no signal candidates
149  skimfilter = b2.register_module("SkimFilter")
150  skimfilter.param("particleLists", ["B0:sig"])
151  empty_path = b2.create_path()
152  skimfilter.if_value("=0", empty_path, b2.AfterConditionPath.END)
153  path.add_module(skimfilter)
154 
155  if store_mc_truth:
156  ma.matchMCTruth("B0:sig", path=path)
157 
158  # perform random candidate selection
159  ma.rankByLowest(
160  "B0:sig",
161  variable=ranking_variable,
162  outputVariable="Rank",
163  numBest=1,
164  path=path,
165  )
166 
167  if store_mc_truth:
168  # Fill particle list from ROE B0:sig to evaluate truth-matching condition
169  ma.fillParticleListFromROE(
170  "B0:forTM",
171  "",
172  maskName="basicMask",
173  sourceParticleListName="B0:sig",
174  path=path,
175  )
176 
177  ma.matchMCTruth("B0:forTM", path=path)
178 
179  ma.variablesToEventExtraInfo("B0:forTM", {"mcPDG": "Bsig_ROE_mcPDG"}, path=path)
180 
181  # -----------------------------------------Tag side reconstruction----------------------------------------
182 
183  if store_mc_truth:
184  # Fill particle list of true B0 from ground-truth information
185  ma.fillParticleListFromMC("B0:MC", "", path=path)
186 
187  graFEI_roe_mask = (
188  "graFEIROE",
189  cut_charged_graFEI,
190  cut_photons_graFEI,
191  cut_KL0_graFEI,
192  )
193 
194  ma.appendROEMasks("B0:sig", [graFEI_roe_mask], path=path)
195 
196  # Fill particle list from ROE B0:sig
197  ma.fillParticleListFromROE(
198  "B0:tag",
199  "",
200  maskName="graFEIROE",
201  sourceParticleListName="B0:sig",
202  path=path,
203  )
204 
205  if store_mc_truth:
206  # Associate parent B to each FSP
207  ma.matchMCTruth("B0:tag", path=path)
208 
209  # Run graFEI
210  graFEI(
211  "B0:tag",
212  cfg_path=args.config,
213  param_file=args.weight,
214  payload_config_name="graFEIConfigFile_Breco_example", # If you default payload name just remove this argument
215  payload_model_name="graFEIModelFile_Breco_example", # If you use default payload name just remove this argument
216  path=path,
217  )
218 
219  # Reconstructin Upsilon(4S)
220  ma.reconstructDecay("Upsilon(4S):graFEI -> B0:tag B0:sig", "", path=path)
221 
222  # ---------------- Write information to file ---------------------------
223 
224  # Variables
225  default_vars = [
226  "M",
227  "Mbc",
228  "deltaE",
229  "p",
230  "px",
231  "py",
232  "pz",
233  "pt",
234  "E",
235  "phi",
236  "theta",
237  "cosTBz",
238  "cosThetaBetweenParticleAndNominalB",
239  ]
240  if store_mc_truth:
241  default_vars.extend(
242  [
243  "isSignal",
244  "isSignalAcceptMissingNeutrino",
245  "mcErrors",
246  "genMotherPDG",
247  "mcPDG",
248  ]
249  )
250 
251  # graFEI variables
252  graFEI_vars = [
253  "graFEI_probEdgeProd",
254  "graFEI_probEdgeMean",
255  "graFEI_probEdgeGeom",
256  "graFEI_validTree",
257  "graFEI_goodEvent",
258  "graFEI_nFSP",
259  "graFEI_nCharged_preFit",
260  "graFEI_nElectrons_preFit",
261  "graFEI_nMuons_preFit",
262  "graFEI_nPions_preFit",
263  "graFEI_nKaons_preFit",
264  "graFEI_nProtons_preFit",
265  "graFEI_nLeptons_preFit",
266  "graFEI_nPhotons_preFit",
267  "graFEI_nOthers_preFit",
268  "graFEI_nCharged_postFit",
269  "graFEI_nElectrons_postFit",
270  "graFEI_nMuons_postFit",
271  "graFEI_nPions_postFit",
272  "graFEI_nKaons_postFit",
273  "graFEI_nProtons_postFit",
274  "graFEI_nLeptons_postFit",
275  "graFEI_nPhotons_postFit",
276  "graFEI_nOthers_postFit",
277  "graFEI_nPredictedUnmatched",
278  "graFEI_nPredictedUnmatched_noPhotons",
279  ]
280  if store_mc_truth:
281  graFEI_vars.extend(
282  [
283  "graFEI_truth_perfectLCA",
284  "graFEI_truth_perfectMasses",
285  "graFEI_truth_perfectEvent",
286  "graFEI_truth_isSemileptonic",
287  "graFEI_truth_nFSP",
288  "graFEI_truth_nPhotons",
289  "graFEI_truth_nElectrons",
290  "graFEI_truth_nMuons",
291  "graFEI_truth_nPions",
292  "graFEI_truth_nKaons",
293  "graFEI_truth_nProtons",
294  "graFEI_truth_nOthers",
295  ]
296  )
297 
298  # Aliases
299  vm.addAlias("Bsig_Rank", "daughter(1, extraInfo(Rank))")
300 
301  for var in default_vars:
302  vm.addAlias(f"Btag_{var}", f"daughter(0, {var})")
303  vm.addAlias(f"Bsig_{var}", f"daughter(1, {var})")
304  for var in graFEI_vars:
305  vm.addAlias(f"Btag_{var}", f"daughter(0, extraInfo({var}))")
306 
307  graFEI_vars = [f"Btag_{var}" for var in graFEI_vars + default_vars] + \
308  ["Btag_Mbc", "Bsig_Rank"] + [f"Bsig_{var}" for var in default_vars]
309 
310  vm.addAlias("Bsig_D_M", "daughter(1, daughter(0, M))")
311  vm.addAlias("Bsig_D_E", "daughter(1, daughter(0, E))")
312  vm.addAlias("Bsig_D_pt", "daughter(1, daughter(0, pt))")
313  vm.addAlias("Bsig_D_p", "daughter(1, daughter(0, p))")
314 
315  graFEI_vars.extend(["Bsig_D_M", "Bsig_D_E", "Bsig_D_pt", "Bsig_D_p"])
316 
317  ma.variablesToNtuple(
318  "Upsilon(4S):graFEI",
319  sorted(graFEI_vars),
320  filename="graFEI_BReco_example.root",
321  treename="tree",
322  path=path,
323  )
324 
325  # Process
326  b2.process(path)
327 
328  # print out the summary
329  print(b2.statistics)