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