Belle II Software  release-06-02-00
lowMulti.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 """ Skim list building functions for the low multiplicity physics working group """
13 
14 import modularAnalysis as ma
15 from skim import BaseSkim, fancy_skim_header
16 from stdCharged import stdE, stdPi
17 from stdPhotons import stdPhotons
18 from variables import variables as vm
19 
20 _VALIDATION_SAMPLE = "mdst14.root"
21 
22 
23 @fancy_skim_header
25  """
26  **Physics channel**: :math:`e^{+}e^{-} \\to e^{+}e^{-}` and :math:`e^{+}e^{-} \\to \\mu^{+}\\mu^{-}`
27  """
28  __authors__ = "Xing-Yu Zhou"
29  __description__ = "Skim list for two track lepton (e+e- to e+e- and e+e- to mu+mu-) events for luminosity measurements."
30  __contact__ = "Xing-Yu Zhou <xing-yu.zhou@desy.de>"
31  __category__ = "physics, low multiplicity"
32 
33  TestSampleProcess = "mumu"
34  ApplyHLTHadronCut = False
35 
36  def __init__(self, prescale=1, **kwargs):
37  """
38  Parameters:
39  prescale (int): the prescale for this skim
40  **kwargs: Passed to the constructor of `BaseSkim`
41  """
42  # Redefine __init__ to allow for additional optional arguments
43  super().__init__(**kwargs)
44  self.prescaleprescale = prescale
45 
46  def build_lists(self, path):
47  # Skim label
48  skim_label = 'TwoTrackLeptonsForLuminosity'
49  # Skim label for the case of two tracks
50  skim_label_2 = 'TwoTrackLeptonsForLuminosity2'
51  # Skim label for the case of one track plus one cluster
52  skim_label_1 = 'TwoTrackLeptonsForLuminosity1'
53 
54  # Tracks from IP
55  IP_cut = '[abs(dz) < 5.0] and [abs(dr) < 2.0]'
56  # Tracks or clusters of momenta greater than 2 GeV in the CMS frame
57  p_cut = '[useCMSFrame(p) > 2.0]'
58  # Tracks pointing to or clusters locating in the barrel ECL + 10 degrees
59  theta_cut = '[0.561 < theta < 2.247]'
60 
61  single_track_cut = IP_cut + ' and ' + p_cut + ' and ' + theta_cut
62  single_cluster_cut = p_cut + ' and ' + theta_cut
63 
64  # Exactly 2 tracks
65  nTracks_cut_2 = '[nCleanedTracks(' + single_track_cut + ') == 2 or nCleanedTracks(' + single_track_cut + ') == 3]'
66  # Exactly 1 track
67  nTracks_cut_1 = '[nCleanedTracks(' + single_track_cut + ') == 1]'
68  # Acollinearity angle in the theta dimension less than 10 degrees in the CMS frame
69  # candidates are : vpho -> e+ e- or vpho -> e gamma
70  # daughter indices are: 0 1 0 1
71  deltaTheta_cut = (
72  '[abs(formula(daughter(0, useCMSFrame(theta)) + daughter(1, useCMSFrame(theta)) - 3.1415927)) < 0.17453293]'
73  )
74 
75  # convert the prescale from trigger convention
76  prescale = str(float(1.0 / self.prescaleprescale))
77  prescale_logic = 'eventRandom <= ' + prescale
78 
79  two_track_cut = nTracks_cut_2 + ' and ' + deltaTheta_cut + ' and ' + prescale_logic
80  track_cluster_cut = nTracks_cut_1 + ' and ' + deltaTheta_cut + ' and ' + prescale_logic
81 
82  # Reconstruct the event candidates with two tracks
83  ma.fillParticleList('e+:' + skim_label_2, single_track_cut + ' and ' + nTracks_cut_2, path=path)
84  ma.reconstructDecay('vpho:' + skim_label_2 + ' -> e+:' + skim_label_2 + ' e-:' + skim_label_2, two_track_cut, path=path)
85 
86  # Reconstruct the event candidates with one track plus one cluster
87  ma.fillParticleList('e+:' + skim_label_1, single_track_cut + ' and ' + nTracks_cut_1, path=path)
88  ma.fillParticleList('gamma:' + skim_label_1, single_cluster_cut + ' and ' + nTracks_cut_1, path=path,
89  loadPhotonBeamBackgroundMVA=False)
90  ma.reconstructDecay(
91  'vpho:' +
92  skim_label_1 +
93  ' -> e+:' +
94  skim_label_1 +
95  ' gamma:' +
96  skim_label_1,
97  track_cluster_cut,
98  allowChargeViolation=True,
99  path=path)
100 
101  ma.copyLists('vpho:' + skim_label, ['vpho:' + skim_label_2, 'vpho:' + skim_label_1], path=path)
102  return ['vpho:' + skim_label]
103 
104 
105 @fancy_skim_header
107  """
108  **Physics channel**: :math:`e^{+}e^{-} \\to \\gamma h_{1}^{+}h_{2}^{-} X`
109 
110  .. Warning::
111  This skim includes the golden mode :math:`e^{+}e^{-} \\to \\gamma \\pi^{+}\\pi^{-}`
112 
113  .. Note::
114  The :math:`h_{1}^{+}` and :math:`h_{2}^{+}` here mean a positive particle
115  and a negative particle that could be either conjugate or non-conjugate. The
116  :math:`X` means arbitrary final state particles.
117 
118  **Decay Modes**
119 
120  1. :math:`e^{+}e^{-} \\to \\gamma \\pi^{+} \\pi^{-} X`,
121  2. :math:`e^{+}e^{-} \\to \\gamma K^{+} K^{-} X`,
122  3. :math:`e^{+}e^{-} \\to \\gamma K^{+} \\pi^{-} X`,
123  4. :math:`e^{+}e^{-} \\to \\gamma p \\overline{p} X`,
124  5. :math:`e^{+}e^{-} \\to \\gamma p \\pi^{-} X`,
125  6. :math:`e^{+}e^{-} \\to \\gamma p K^{-} X`,
126  """
127  __authors__ = ["Xing-Yu Zhou", "Guanda Gong"]
128  __description__ = "Skim list for low mass events with at least two tracks and one hard photon" \
129  " in final state."
130  __contact__ = "Xing-Yu Zhou <xing-yu.zhou@desy.de>"
131  __category__ = "physics, low multiplicity"
132 
133  TestSampleProcess = "mumu"
134  validation_sample = _VALIDATION_SAMPLE
135  ApplyHLTHadronCut = False
136 
137  def build_lists(self, path):
138  label = "LowMassTwoTrack"
139 
140  # Momenta of tracks greater than 0.5 GeV in the Lab frame
141  pCut = "p > 0.5"
142  # Energy of hard ISR gamma greater than 2 GeV in the CMS frame
143  ISRECut = "useCMSFrame(E) > 2"
144  # Invariant mass of h+h- system less than 3.5 GeV
145  hhMassWindow = "daughterInvM(1,2) < 3.5"
146 
147  # Event based cut
148  # Number of tracks passing the selection criteria, should be greater than or equal to to 2
149  nTracksCut = f"nCleanedTracks({pCut}) >= 2"
150  # Require at least one hard photon
151  nHardISRPhotonCut = f"nCleanedECLClusters({ISRECut}) > 0"
152 
153  # Apply event based cuts
154  path = self.skim_event_cutsskim_event_cuts(f"{nTracksCut} and {nHardISRPhotonCut}", path=path)
155 
156  # Reconstruct candidates
157  ma.fillParticleList(f"pi+:{label}", pCut, path=path)
158  ma.fillParticleList(f"K+:{label}", pCut, path=path)
159  ma.fillParticleList(f"p+:{label}", pCut, path=path)
160  ma.fillParticleList(f"gamma:{label}_ISR", ISRECut, path=path, loadPhotonBeamBackgroundMVA=False)
161 
162  # the mass hypothesis is different for p+, pi+ and K+ lists, so it is good to write them separately.
163  ModesAndCuts = [
164  (f"vpho:{label}_pipi", f" -> gamma:{label}_ISR pi+:{label} pi-:{label}", hhMassWindow),
165  (f"vpho:{label}_KK", f" -> gamma:{label}_ISR K+:{label} K-:{label}", hhMassWindow),
166  # Might be useful when one wants to reconstruct ISR K pi and missing other final state particles
167  (f"vpho:{label}_Kpi", f" -> gamma:{label}_ISR K+:{label} pi-:{label}", hhMassWindow),
168  (f"vpho:{label}_pp", f" -> gamma:{label}_ISR p+:{label} anti-p-:{label}", hhMassWindow),
169  # Useful for analyses for processes like ISR Lambda Lambda-bar (Sigma Sigma-bar) , especially when
170  # one wants to reconstruct the hard ISR photon and one of the Lambda (Sigma), missing anthoer
171  # Lambda (Sigma)
172  (f"vpho:{label}_ppi", f" -> gamma:{label}_ISR p+:{label} pi-:{label}", hhMassWindow),
173  # Might be useful when one wants to reconstruct ISR p K and missing other final state particles
174  (f"vpho:{label}_pK", f" -> gamma:{label}_ISR p+:{label} K-:{label}", hhMassWindow),
175  ]
176 
177  ParticleLists = []
178  for dmID, (mode, decayString, cut) in enumerate(ModesAndCuts):
179  ma.reconstructDecay(mode + decayString, cut, dmID=dmID, path=path)
180  ParticleLists.append(mode)
181  return ParticleLists
182 
183  def validation_histograms(self, path):
184  vm.addAlias('pip_p_cms', 'daughter(0, useCMSFrame(p))')
185  vm.addAlias('pim_p_cms', 'daughter(1, useCMSFrame(p))')
186  vm.addAlias('gamma_E_cms', 'daughter(2, useCMSFrame(E))')
187  vm.addAlias('pip_theta_lab', 'formula(daughter(0, theta)*180/3.1415927)')
188  vm.addAlias('pim_theta_lab', 'formula(daughter(1, theta)*180/3.1415927)')
189  vm.addAlias('gamma_theta_lab', 'formula(daughter(2, theta)*180/3.1415927)')
190  vm.addAlias('Mpipi', 'daughterInvM(0,1)')
191 
192  ma.copyLists('vpho:LowMassTwoTrack', self.SkimListsSkimListsSkimLists, path=path)
193 
194  variablesHist = [
195  ('pip_p_cms', 60, 0, 6),
196  ('pim_p_cms', 60, 0, 6),
197  ('gamma_E_cms', 60, 0, 6),
198  ('pip_theta_lab', 90, 0, 180),
199  ('pim_theta_lab', 90, 0, 180),
200  ('gamma_theta_lab', 90, 0, 180),
201  ('Mpipi', 80, 0., 4.),
202  ('M', 60, 6., 12.)
203  ]
204 
205  # Output the variables to histograms
206  ma.variablesToHistogram(
207  'vpho:LowMassTwoTrack',
208  variablesHist,
209  filename=f'{self}_Validation.root',
210  path=path)
211 
212 
213 @fancy_skim_header
215  """
216  **Physics channel**: :math:`e^{+}e^{-} \\to e^{\\pm} (e^{\\mp}) \\pi^{0}/\\eta/\\eta^{\\prime}`
217 
218  **Decay Modes**
219 
220  1. :math:`\\pi^{0}\\to \\gamma \\gamma`,
221  2. :math:`\\eta \\to \\gamma\\gamma`,
222  3. :math:`\\eta \\to \\pi^{+}\\pi^{-}\\pi^{0}`,
223  4. :math:`\\eta \\to \\pi^{+}\\pi^{-}\\gamma`,
224  5. :math:`\\eta^{\\prime} \\to \\pi^{+}\\pi^{-}\\eta(\\to \\gamma\\gamma)`,
225  6. :math:`\\eta^{\\prime} \\to \\pi^{+}\\pi^{-}\\gamma`
226  """
227 
228  __authors__ = ["Hisaki Hayashii"]
229  __contact__ = "Hisaki Hayashii <hisaki.hayashii@desy.de>"
230  __description__ = "A skim script to select events with one high-energy electron and one or more pi0/eta/eta mesons."
231  __category__ = "physics, low multiplicity"
232  ApplyHLTHadronCut = False
233 
234  def load_standard_lists(self, path):
235  stdE("all", path=path)
236  stdPi("all", path=path)
237  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
238 
239  def build_lists(self, path):
240 
241  label = "PseudoScalarSkim"
242  TrackCuts = "abs(dz) < 2.0 and dr < 0.5 and pt > 0.15"
243 
244  ma.fillParticleList(f"e+:{label}", f"{TrackCuts} and E > 1.5 and clusterEoP > 0.7", path=path)
245  ma.fillParticleList(f"pi+:{label}", f"{TrackCuts} and [isInList(e+:{label})==0]", path=path)
246  ma.fillParticleList(f"gamma:{label}", "clusterE > 0.1", path=path, loadPhotonBeamBackgroundMVA=False)
247 
248  pi0MassWindow = "0.06 < InvM < 0.18"
249  etaMassWindow = "0.50 < InvM < 0.60"
250  etapMassWindow = "0.91 < InvM < 1.10"
251  ModesAndCuts = [
252  (f"pi0:{label}_loose -> gamma:{label} gamma:{label}", pi0MassWindow),
253  (f"eta:gg -> gamma:{label} gamma:{label}", etaMassWindow),
254  (f"eta:pipipi0 -> pi+:{label} pi-:{label} pi0:{label}_loose", etaMassWindow),
255  (f"eta:pipig -> pi+:{label} pi-:{label} gamma:{label}", etaMassWindow),
256  (f"eta':pipieta_gg -> pi+:{label} pi-:{label} eta:gg", etapMassWindow),
257  (f"eta':pipig -> pi+:{label} pi-:{label} gamma:{label}", etapMassWindow),
258  ]
259  for dmID, (mode, cut) in enumerate(ModesAndCuts):
260  ma.reconstructDecay(mode, cut, dmID=dmID, path=path)
261 
262  ma.cutAndCopyList(f"pi0:{label}_highE", f"pi0:{label}_loose", "E > 0.5", path=path)
263 
264  particles = [
265  f"pi0:{label}_highE",
266  "eta:gg",
267  "eta:pipipi0",
268  "eta:pipig",
269  "eta':pipieta_gg",
270  "eta':pipig"
271  ]
272  ModeSum = " + ".join(f"nParticlesInList({particle})" for particle in particles)
273  presel = f"nParticlesInList(e+:{label}) == 1 and nParticlesInList(pi+:{label}) <= 2"
274  EventCuts = f"{presel} and formula({ModeSum}) >= 1"
275 
276  # Although a condition of "mode_sum >= 1" looks like very loose,
277  # the reduction rate of this SingleTagPseudoScalar skim is very large, i.e. 1/50,
278  # since the requirements, one high-energy electron and <=2 other charged
279  # tracks, are quite stringent.
280  path = self.skim_event_cutsskim_event_cuts(EventCuts, path=path)
281 
282  return [f"e+:{label}"]
def build_lists(self, path)
Definition: lowMulti.py:137
def validation_histograms(self, path)
Definition: lowMulti.py:183
def build_lists(self, path)
Definition: lowMulti.py:239
def load_standard_lists(self, path)
Definition: lowMulti.py:234
def __init__(self, prescale=1, **kwargs)
Definition: lowMulti.py:36
list SkimLists
Definition: core.py:260
def skim_event_cuts(self, cut, *path)
Definition: core.py:272