Belle II Software  release-06-02-00
dark.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 """ Skim list building functions for the dark sector physics working group """
13 
14 import basf2 as b2
15 import modularAnalysis as ma
16 import pdg
17 from skim import BaseSkim, fancy_skim_header
18 from stdCharged import stdE, stdMu, stdPi, stdK
19 from stdPhotons import stdPhotons
20 import vertex as vertex
21 
22 __liaison__ = "Sascha Dreyer <sascha.dreyer@desy.de>"
23 _VALIDATION_SAMPLE = "mdst14.root"
24 
25 
26 @fancy_skim_header
28  """
29  **Physics channel**: ee → A'γ; A' → invisible
30 
31  Skim list contains single photon candidates for the dark photon to invisible final
32  state analysis.
33  """
34  __authors__ = ["Sam Cunliffe", "Chris Hearty"]
35  __contact__ = __liaison__
36  __description__ = "Single photon skim list for the dark photon analysis."
37  __category__ = "physics, dark sector"
38  ApplyHLTHadronCut = False
39 
40  def load_standard_lists(self, path):
41  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
42 
43  def build_lists(self, path):
44 
45  # start with all photons with E* above 500 MeV in the tracking acceptance
46  in_tracking_acceptance = "0.296706 < theta < 2.61799" # rad = [17, 150] degrees
47  ma.cutAndCopyList(
48  "gamma:singlePhoton", "gamma:all",
49  f"useCMSFrame(E) > 0.5 and {in_tracking_acceptance}", path=path)
50 
51  # require a region-dependent minimum energy of the candidate, we have
52  # a new 0.5 GeV trigger in the inner barrel: [44.2, 94.8] degrees @ L1
53  region_dependent = " [clusterTheta < 1.65457213 and clusterTheta > 0.77143553] or "
54  region_dependent += "[clusterReg == 2 and useCMSFrame(E) > 1.0] or " # barrel
55  region_dependent += "[clusterReg == 1 and useCMSFrame(E) > 2.0] or " # fwd
56  region_dependent += "[clusterReg == 3 and useCMSFrame(E) > 2.0] or " # bwd
57  region_dependent += "[clusterReg == 11 and useCMSFrame(E) > 2.0] or " # between fwd and barrel
58  region_dependent += "[clusterReg == 13 and useCMSFrame(E) > 2.0]" # between bwd and barrel
59  ma.applyCuts("gamma:singlePhoton", region_dependent, path=path)
60 
61  # require only one single photon candidate and no good tracks in the event
62  good_tracks = 'abs(dz) < 2.0 and abs(dr) < 0.5 and pt > 0.15' # cm, cm, GeV/c
63  path = self.skim_event_cutsskim_event_cuts(
64  f"nParticlesInList(gamma:singlePhoton) == 1 and nCleanedTracks({good_tracks}) == 0",
65  path=path
66  )
67 
68  # check the second-most-energetic photon (above 550 MeV is unlikely to
69  # be beam-induced background) and veto if it's in time with our signal
70  # candidate -- do after the event cuts since it uses a ParticleCombiner
71  # and should not be done for all events (save event-processing time)
72  not_in_signal_list = "isInList(gamma:singlePhoton) < 1"
73  in_time = "maxWeightedDistanceFromAverageECLTime < 1"
74  ma.cutAndCopyList("gamma:to_veto", "gamma:all",
75  f"E > 0.55 and {not_in_signal_list}", path=path)
76  ma.rankByHighest("gamma:to_veto", "E", numBest=1, path=path)
77  ma.reconstructDecay("vpho:veto -> gamma:singlePhoton gamma:to_veto",
78  in_time, path=path)
79  veto_additional_in_time_cluster = 'nParticlesInList(vpho:veto) < 1'
80 
81  # final signal selection must pass the 'in-time' veto on the
82  # second-most-energetic cluster -- this is also an event cut, but apply
83  # to the list (which is anyway a maximum one candidate per event) since
84  # we can only call skim_event_cuts once
85  ma.applyCuts("gamma:singlePhoton", veto_additional_in_time_cluster, path=path)
86  return ["gamma:singlePhoton"]
87 
88 
89 @fancy_skim_header
91  __authors__ = ["Michael De Nuccio"]
92  __description__ = (
93  "Neutral dark sector skim list for the ALP 3-photon analysis: "
94  ":math:`ee\\to a(\\to\\gamma\\gamma)\\gamma`"
95  )
96  __contact__ = __liaison__
97  __category__ = "physics, dark sector"
98  ApplyHLTHadronCut = False
99 
100  def addALPToPDG(self):
101  """Adds the ALP codes to the basf2 pdg instance """
102  pdg.add_particle('beam', 55, 999., 999., 0, 0)
103  pdg.add_particle('ALP', 9000006, 999., 999., 0, 0)
104 
105  def initialALP(self, path):
106  """
107  An list builder function for the ALP decays. Part of the `ALP3Gamma` skim.
108 
109  Parameters:
110  path (basf2.Path): the path to add the skim
111 
112  Returns:
113  list name of the ALP decays candidates
114  """
115  # no cuts applied on ALP
116  ALPcuts = ''
117 
118  # applying a lab frame energy cut to the daughter photons
119  ma.fillParticleList(
120  'gamma:cdcAndMinimumEnergy',
121  'E >= 0.1 and theta >= 0.297 and theta <= 2.618',
122  True, path=path, loadPhotonBeamBackgroundMVA=False
123  )
124 
125  # defining the decay string
126  ALPchannels = ['gamma:cdcAndMinimumEnergy gamma:cdcAndMinimumEnergy']
127  ALPList = []
128 
129  # creating an ALP from the daughter photons
130  for chID, channel in enumerate(ALPchannels):
131  mode = 'ALP:' + str(chID) + ' -> ' + channel
132  print(mode)
133  ma.reconstructDecay(mode, ALPcuts, chID, path=path)
134 
135  ALPList.append('ALP:' + str(chID))
136 
137  Lists = ALPList
138  return Lists
139 
140  def additional_setup(self, path):
141  self.addALPToPDGaddALPToPDG()
142 
143  def build_lists(self, path):
144  # applying invariant mass cut on the beam particle
145  beamcuts = "InvM >= formula(0.8 * Ecms) and InvM <= formula(1.05 * Ecms) and maxWeightedDistanceFromAverageECLTime <= 2"
146 
147  ALPList = self.initialALPinitialALP(path=path)
148 
149  # applying a lab frame energy cut to the recoil photon
150  ma.fillParticleList("gamma:minimumEnergy", "E >= 0.1", True, path=path)
151  beamList = []
152 
153  # reconstructing decay using the reconstructed ALP
154  # from previous function and adding the recoil photon
155  for chID, channel in enumerate(ALPList):
156  mode = "beam:" + str(chID) + " -> gamma:minimumEnergy " + channel
157  print(mode)
158  ma.reconstructDecay(mode, beamcuts, chID, path=path)
159  beamList.append("beam:" + str(chID))
160  return beamList
161 
162 
163 @fancy_skim_header
165  """
166  **Physics channel**: :math:`e^{+}e^{-} \\to \\mu^{+}\\mu^{-} \\, +` missing energy.
167  """
168  __authors__ = ["Giacomo De Pietro"]
169  __description__ = (
170  "Dimuon + missing energy skim, needed for :math:`e^{+}e^{-} \\to \\mu^{+}\\mu^{-}"
171  "Z^{\\prime}; \\, Z^{\\prime} \\to \\mathrm{invisible}` and other searches."
172  )
173  __contact__ = __liaison__
174  __category__ = "physics, dark sector"
175  ApplyHLTHadronCut = False
176 
177  def load_standard_lists(self, path):
178  stdMu("all", path=path)
179 
180  def build_lists(self, path):
181  dimuon_list = []
182  skim_label = "forDimuonMissingEnergySkim"
183  dimuon_name = "Z0:" + skim_label
184 
185  # Define some cuts
186  fromIP_cut = "[abs(dz) < 5.0] and [abs(dr) < 2.0]"
187  muonID_cut = "[muonID > 0.3]"
188  # We want exactly 2 tracks from IP
189  dimuon_cut = "[nCleanedTracks(" + fromIP_cut + ") < 4]"
190  # And the pair must have pt > 200 MeV in CMS frame
191  dimuon_cut += " and [useCMSFrame(pt) > 0.2]"
192 
193  # Reconstruct the dimuon candidate
194  ma.cutAndCopyList("mu+:" + skim_label, "mu+:all", fromIP_cut + " and " + muonID_cut, path=path)
195  ma.reconstructDecay(dimuon_name + " -> mu+:" + skim_label + " mu-:" + skim_label, dimuon_cut, path=path)
196 
197  # And return the dimuon list
198  dimuon_list.append(dimuon_name)
199  return dimuon_list
200 
201 
202 @fancy_skim_header
204  __authors__ = ["Giacomo De Pietro"]
205  __description__ = (
206  "Electron-muon pair + missing energy skim, needed for :math:`e^{+}e^{-} \\to "
207  "e^{\\pm}\\mu^{\\mp} Z^{\\prime}; \\, Z^{\\prime} \\to \\mathrm{invisible}` and other "
208  "searches."
209  )
210  __contact__ = __liaison__
211  __category__ = "physics, dark sector"
212  ApplyHLTHadronCut = False
213 
214  def load_standard_lists(self, path):
215  stdE("all", path=path)
216  stdMu("all", path=path)
217 
218  def build_lists(self, path):
219  """
220  **Physics channel**: :math:`e^{+}e^{-} \\to e^{\\pm}\\mu^{\\mp} \\, +` missing energy
221  """
222  emu_list = []
223  skim_label = "forElectronMuonMissingEnergySkim"
224  emu_name = "Z0:" + skim_label
225 
226  # Define some basic cuts
227  fromIP_cut = "[abs(dz) < 5.0] and [abs(dr) < 2.0]"
228  electronID_cut = "[electronID > 0.3]"
229  muonID_cut = "[muonID > 0.3]"
230  # We require that the electron points to the barrel ECL + 10 degrees
231  theta_cut = "[0.387 < theta < 2.421]"
232  # We want exactly 2 tracks from IP
233  emu_cut = "[nCleanedTracks(" + fromIP_cut + ") < 4]"
234  # And the pair must have pt > 200 MeV in CMS frame
235  emu_cut += " and [useCMSFrame(pt) > 0.2]"
236 
237  # Reconstruct the dimuon candidate
238  ma.cutAndCopyList("e+:" + skim_label, "e+:all", fromIP_cut + " and " + electronID_cut + " and " + theta_cut, path=path)
239  ma.cutAndCopyList("mu+:" + skim_label, "mu+:all", fromIP_cut + " and " + muonID_cut, path=path)
240  ma.reconstructDecay(emu_name + " -> e+:" + skim_label + " mu-:" + skim_label, emu_cut, path=path)
241 
242  # And return the dimuon list
243  emu_list.append(emu_name)
244  return emu_list
245 
246 
247 @fancy_skim_header
249  __authors__ = ["Ilya Komarov"]
250  __description__ = "Lepton flavour violating Z' skim, Z' to visible FS."
251  __contact__ = __liaison__
252  __category__ = "physics, dark sector"
253  ApplyHLTHadronCut = False
254 
255  def load_standard_lists(self, path):
256  stdE("all", path=path)
257  stdE("loose", path=path)
258 
259  def build_lists(self, path):
260  """
261  **Physics channel**: ee --> e mu Z'; Z' --> e mu
262  """
263  lfvzp_list = []
264 
265  # Here we just want four gpood tracks to be reconstructed
266  track_cuts = "abs(dz) < 2.0 and abs(dr) < 0.5"
267  Event_cuts_vis = "nCleanedTracks(abs(dz) < 2.0 and abs(dr) < 0.5) == 4"
268 
269  ma.cutAndCopyList("e+:lfvzp", "e+:all", track_cuts, path=path)
270 
271  # Z' to lfv: fully reconstructed
272  LFVZpVisChannel = "e+:lfvzp e+:lfvzp e-:lfvzp e-:lfvzp"
273 
274  ma.reconstructDecay("vpho:vislfvzp -> " + LFVZpVisChannel, Event_cuts_vis, path=path)
275 
276  lfvzp_list.append("vpho:vislfvzp")
277 
278  # Z' to lfv: part reco
279  LFVZpVisChannel = "e+:lfvzp e+:lfvzp e-:lfvzp"
280  Event_cuts_vis = "nCleanedTracks(abs(dz) < 2.0 and abs(dr) < 0.5) == 3"
281 
282  ma.reconstructDecay("vpho:3tr_vislfvzp -> " + LFVZpVisChannel, Event_cuts_vis, path=path, allowChargeViolation=True)
283 
284  lfvzp_list.append("vpho:3tr_vislfvzp")
285 
286  # Z' to lfv: two same-sign tracks
287  LFVZpVisChannel = "e+:lfvzp e+:lfvzp"
288  Event_cuts_vis = "nCleanedTracks(abs(dz) < 2.0 and abs(dr) < 0.5) == 2"
289  ma.reconstructDecay("vpho:2tr_vislfvzp -> " + LFVZpVisChannel, Event_cuts_vis, path=path, allowChargeViolation=True)
290 
291  lfvzp_list.append("vpho:2tr_vislfvzp")
292 
293  return lfvzp_list
294 
295 
296 @fancy_skim_header
298  """
299  **Physics channel**: ee → eγ
300  """
301 
302  __authors__ = ["Sam Cunliffe", "Torben Ferber"]
303  __description__ = (
304  "Electron-gamma skim list for study of the ee backgrounds at high dark "
305  "photon mass, as part of the dark photon analysis"
306  )
307  __contact__ = __liaison__
308  __category__ = "physics, dark sector, control-channel"
309  ApplyHLTHadronCut = False
310 
311  def load_standard_lists(self, path):
312  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
313  stdE("all", path=path)
314 
315  def build_lists(self, path):
316 
317  # long-winded names for the particle lists to avoid clash
318  internal_skim_label = "forEGammaSkim"
319  skim_output_label = "EGammaControl"
320 
321  # want exactly 1 good quality track in the event
322  # (not one good electron, one good anything)
323  phys_perf_good_track = 'abs(dr) < 1 and abs(dz) < 3 and pt > 0.15' # cm, cm, GeV/c
324  one_good_track = f'[nCleanedTracks({phys_perf_good_track}) == 1]'
325 
326  # exactly 1 good photon in the event
327  photon_energy_cut = '0.45'
328  good_photon = 'theta > 0.296706 and theta < 2.61799' +\
329  f' and useCMSFrame(E) > {photon_energy_cut}'
330  ma.cutAndCopyList(f'gamma:{internal_skim_label}', 'gamma:all', good_photon, path=path)
331  one_good_photon = f'[eventCached(nParticlesInList(gamma:{internal_skim_label})) == 1]'
332 
333  # apply the event-level cuts
334  event_cuts = f'{one_good_photon} and {one_good_track}'
335  path = self.skim_event_cutsskim_event_cuts(event_cuts, path=path)
336 
337  # fill electron lists (tighter than previous selection)
338  good_track_w_hie_cluster_match = '%s and clusterE > 2.0' % phys_perf_good_track
339  ma.cutAndCopyList(f'e+:{internal_skim_label}', 'e+:all', good_track_w_hie_cluster_match, path=path)
340 
341  # reconstruct decay
342  ma.reconstructDecay(
343  f'vpho:{skim_output_label} -> e+:{internal_skim_label} gamma:{internal_skim_label}',
344  '', 1, allowChargeViolation=True, path=path)
345  return [f"vpho:{skim_output_label}"]
346 
347 
348 @fancy_skim_header
350  """
351  **Physics channel**: ee → γγ
352 
353  .. Note::
354  This skim can retain a lot of γγ events.
355  In case this becomes unacceptable, we provide prescale parameters.
356  Prescales are given in standard trigger convention (reciprocal),
357  so prescale of 100 is 1% of events kept, etc.
358 
359  .. Tip::
360  To prescale the higher-energy probe photons by 10%:
361 
362  >>> from skim.WGs.dark import GammaGammaControlKLMDark
363  >>> Skim = GammaGammaControlKLMDark(prescale_high=10)
364  >>> Skim(path) # Add list-building function and uDST output module to path
365  >>> b2.process(path)
366  """
367 
368  __authors__ = ["Sam Cunliffe", "Miho Wakai"]
369  __description__ = (
370  "Gamma gamma skim list for study of the KLM efficiency as part of "
371  "the dark photon analysis"
372  )
373  __contact__ = __liaison__
374  __category__ = "physics, dark sector, control-channel"
375  ApplyHLTHadronCut = False
376 
377  def load_standard_lists(self, path):
378  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
379 
380  TestSampleProcess = "gg"
381 
382  def __init__(self, prescale_high=1, prescale_low=1, **kwargs):
383  """
384  Parameters:
385  prescale_high (int): the prescale for more energetic probe photon
386  prescale_low (int): the prescale for a less energetic probe photon
387  **kwargs: Passed to constructor of `BaseSkim`.
388  """
389  # Redefine __init__ to allow for additional optional arguments
390  super().__init__(**kwargs)
391  self.prescale_highprescale_high = prescale_high
392  self.prescale_lowprescale_low = prescale_low
393 
394  def build_lists(self, path):
395  # unpack prescales and convert from trigger convention to a number we can
396  # compare with a float
397  prescale_high, prescale_low = self.prescale_highprescale_high, self.prescale_lowprescale_low
398  if (prescale_high, prescale_low) != (1, 1):
399  b2.B2INFO(
400  "GammaGammaControlKLMDarkList is prescaled. "
401  f"prescale_high={prescale_high}, prescale_low={prescale_low}"
402  )
403  prescale_high = str(float(1.0 / prescale_high))
404  prescale_low = str(float(1.0 / prescale_low))
405 
406  # no good (IP-originating) tracks in the event
407  good_tracks = "abs(dz) < 2.0 and abs(dr) < 0.5 and pt > 0.2" # cm, cm, GeV/c
408  no_good_tracks = f"nCleanedTracks({good_tracks}) < 1"
409 
410  # get two most energetic photons in the event (must be at least 100 MeV
411  # and not more than 7 GeV)
412  ma.cutAndCopyList(
413  "gamma:controlKLM", "gamma:all", "0.1 < useCMSFrame(clusterE) < 7", path=path)
414  ma.rankByHighest("gamma:controlKLM", "useCMSFrame(clusterE)", numBest=2, path=path)
415 
416  # will build pairwise candidates from the gamma:controlKLM list:
417  # vpho -> gamma gamma
418 
419  # the more energetic must be at least 4.5 GeV
420  tag_daughter = "daughterHighest(useCMSFrame(clusterE)) > 4.5"
421  # note that sometimes the probe will also fulfill this criteria, but the
422  # candidate list will *not* be double-counted: these extra candidates need
423  # to be added back offline
424 
425  # apply prescales to the less energetic daughter: compare to the eventwise random number
426  probe_high = f"[daughterLowest(useCMSFrame(clusterE)) > 4.5] and [eventRandom < {prescale_high}]"
427  probe_low = f"[daughterLowest(useCMSFrame(clusterE)) < 4.5] and [eventRandom < {prescale_low}]"
428  prescale = f"[ {probe_high} ] or [ {probe_low} ]"
429 
430  # ~back-to-back in phi in the CMS (3.1066... radians = 178 degrees)
431  delta_phi_cut = "abs(daughterDiffOfPhiCMS(0, 1)) > 3.1066860685499065"
432 
433  # sum theta in the cms 178 --> 182 degrees
434  sum_th = "daughterSumOf(useCMSFrame(theta))"
435  sum_th_cut = f"3.1066860685499065 < {sum_th} < 3.1764992386296798"
436 
437  # now build and return the candidates passing the AND of our cuts
438  cuts = [no_good_tracks, tag_daughter, prescale, delta_phi_cut, sum_th_cut]
439  cuts = " and ".join([f"[ {cut} ]" for cut in cuts])
440 
441  ma.reconstructDecay(
442  "vpho:singlePhotonControlKLM -> gamma:controlKLM gamma:controlKLM",
443  cuts, path=path)
444  return ["vpho:singlePhotonControlKLM"]
445 
446 
447 @fancy_skim_header
449  """
450  **Physics channel**: :math:`e^{+}e^{-} \\to e^{+}e^{-}`
451 
452  Warning:
453  This skim is currently deactivated, since the retention rate is too high.
454  """
455 
456  __authors__ = "Giacomo De Pietro"
457  __description__ = (
458  "Dielectron skim, needed for :math:`e^{+}e^{-} \\to A^{\\prime} h^{\\prime};`"
459  ":math:`A^{\\prime} \\to e^{+}e^{-}; \\, h^{\\prime} \\to \\mathrm{invisible}` and other searches."
460  )
461  __contact__ = __liaison__
462  __category__ = "physics, dark sector"
463  ApplyHLTHadronCut = False
464 
465  def load_standard_lists(self, path):
466  stdE("all", path=path)
467 
468  TestSampleProcess = "mumu"
469 
470  def build_lists(self, path):
471  dielectron_list = []
472  skim_label = "forDielectronMissingEnergySkim"
473  dielectron_name = f"Z0:{skim_label}"
474 
475  # Define some basic cuts
476  fromIP_cut = "[abs(dz) < 5.0] and [abs(dr) < 2.0]"
477  electronID_cut = "[electronID > 0.2]"
478  # We require that the electron points to the barrel ECL + 10 degrees
479  theta_cut = "[0.387 < theta < 2.421]"
480  # We want exactly 2 tracks from IP
481  dielectron_cut = f"[nCleanedTracks({fromIP_cut}) == 2]"
482  # And the pair must have pt > 200 MeV in CMS frame
483  dielectron_cut += " and [useCMSFrame(pt) > 0.2]"
484 
485  # Reconstruct the dielectron candidate
486  electron_cuts = " and ".join([fromIP_cut, electronID_cut, theta_cut])
487  ma.cutAndCopyList(f"e+:{skim_label}", "e+:all", electron_cuts, path=path)
488  ma.reconstructDecay(f"{dielectron_name} -> e+:{skim_label} e-:{skim_label}", dielectron_cut, path=path)
489 
490  # And return the dielectron list
491  dielectron_list.append(dielectron_name)
492  return dielectron_list
493 
494 
495 @fancy_skim_header
497 
498  """
499  Control sample: :math:`e^{+}e^{-} \\to e^{+}e^{-}V^{0};`"
500  """
501 
502  __authors__ = "Savino Longo"
503  __description__ = (
504  "iDM control sample skim. :math:`e^{+}e^{-} \\to e^{+}e^{-}V^{0};`"
505  )
506  __contact__ = __liaison__
507  __category__ = "physics, dark sector"
508  ApplyHLTHadronCut = False
509 
510  def load_standard_lists(self, path):
511  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
512  stdE("all", path=path)
513 
514  def build_lists(self, path):
515 
516  # require Bhabha tracks are high p and E/p is consitent with e+/e-
517  BhabhaTrackCuts = 'abs(dr)<0.5 and abs(dz)<2 and pt>0.2 and 0.8<clusterEoP<1.2 and p>1.0 and clusterReg==2 and nCDCHits>4'
518  BhabhaSystemCuts = '4<M<10 and 0.5<pRecoilTheta<2.25'
519  V0TrackCuts = 'nCDCHits>4 and p<3.0'
520  V0Cuts = 'dr>0.5'
521  PhotonVetoCuts = 'p>1.0' # event should have no high E photons
522 
523  ma.cutAndCopyList("gamma:HighEGammaVeto", "gamma:all", PhotonVetoCuts, path=path)
524  ma.cutAndCopyList("e+:BhabhaTrack", "e+:all", BhabhaTrackCuts, path=path)
525  ma.cutAndCopyList("e+:V0Track", "e+:all", V0TrackCuts, path=path)
526 
527  ma.reconstructDecay("vpho:BhabhaSysyem -> e+:BhabhaTrack e-:BhabhaTrack", BhabhaSystemCuts, path=path)
528 
529  ma.reconstructDecay("vpho:V0System -> e+:V0Track e-:V0Track", '', path=path)
530  vertex.treeFit('vpho:V0System', conf_level=0.0, path=path)
531  ma.applyCuts('vpho:V0System', V0Cuts, path=path)
532 
533  ma.reconstructDecay('vpho:Total -> vpho:BhabhaSysyem vpho:V0System', '', path=path)
534 
535  eventCuts = ('nParticlesInList(gamma:HighEGammaVeto)<1 and '
536  'nParticlesInList(vpho:Total)>0')
537 
538  path = self.skim_event_cutsskim_event_cuts(eventCuts, path=path)
539 
540  return ["vpho:Total"]
541 
542 
543 @fancy_skim_header
545  """
546  Skim list contains events with no tracks from IP, no high E tracks and only one high E photon.
547  """
548  __authors__ = ["Savino Longo"]
549  __contact__ = __liaison__
550  __description__ = "iDM list for the iDM analysis."
551  __category__ = "physics, dark sector"
552  ApplyHLTHadronCut = False
553 
554  def load_standard_lists(self, path):
555  stdPhotons("all", path=path, loadPhotonBeamBackgroundMVA=False)
556  stdE("all", path=path)
557 
558  def build_lists(self, path):
559 
560  IPtrack = 'abs(dr) < 0.05' # cm
561  HighEtrack = 'useCMSFrame(p)>3.0' # GeV
562  ma.cutAndCopyList("e+:TrackFromIP", "e+:all", IPtrack, path=path)
563  ma.cutAndCopyList("e+:HighEnergyTrack", "e+:all", HighEtrack, path=path)
564 
565  signalPhoton = "[clusterReg==2 and useCMSFrame(E) > 1.0] or "
566  signalPhoton += "[clusterReg == 1 and useCMSFrame(E) > 2.0] or " # fwd
567  signalPhoton += "[clusterReg == 3 and useCMSFrame(E) > 2.0] or " # bwd
568  signalPhoton += "[clusterReg == 11 and useCMSFrame(E) > 2.0] or " # between fwd and barrel
569  signalPhoton += "[clusterReg == 13 and useCMSFrame(E) > 2.0] " # between bwd and barrel
570 
571  photonVetoHE1 = 'useCMSFrame(p) > 0.6'
572  photonVetoHE3 = 'p>0.5'
573 
574  ma.cutAndCopyList("gamma:ISR", "gamma:all", signalPhoton, path=path)
575  ma.cutAndCopyList("gamma:HighEnergyPhotons", "gamma:all", photonVetoHE1, path=path)
576  ma.cutAndCopyList("gamma:MediumEnergyPhotons", "gamma:all", photonVetoHE3, path=path)
577 
578  idmEventCuts = ('nParticlesInList(gamma:ISR)==1 and '
579  'nParticlesInList(e+:TrackFromIP)==0 and '
580  'nParticlesInList(e+:HighEnergyTrack) == 0 and '
581  'nParticlesInList(gamma:HighEnergyPhotons) == 1 and '
582  'nParticlesInList(gamma:MediumEnergyPhotons) < 4 and '
583  'HighLevelTrigger == 1')
584 
585  path = self.skim_event_cutsskim_event_cuts(idmEventCuts, path=path)
586 
587  return ['gamma:ISR']
588 
589 
590 @fancy_skim_header
592  """
593  Skim to select B+ decays to a K+ from the IP and a LLP with a vertex displaced from the IR decaying to two charged tracks.
594  """
595  __authors__ = ["Sascha Dreyer"]
596  __contact__ = __liaison__
597  __description__ = (
598  "B+ to K+ LLP analysis skim :math:`e^{+}e^{-} \\to \\Upsilon(4s) \\to [B^{+} \\to K^{+} LLP]B^{-}`"
599  )
600  __category__ = "physics, dark sector"
601  ApplyHLTHadronCut = False
602 
603  def load_standard_lists(self, path):
604  stdPi("all", path=path)
605  stdK("all", path=path)
606  stdE("all", path=path)
607  stdMu("all", path=path)
608 
609  def build_lists(self, path):
610 
611  btoksLbl = '_btoks'
612 
613  minDisplacementCut = "[dr > 0.05]"
614 
615  ma.reconstructDecay("vpho:LLP_e" + btoksLbl + " -> e+:all e-:all", "", path=path)
616  ma.reconstructDecay("vpho:LLP_mu" + btoksLbl + " -> mu+:all mu-:all", "", path=path)
617  ma.reconstructDecay("vpho:LLP_pi" + btoksLbl + " -> pi+:all pi-:all", "", path=path)
618  ma.reconstructDecay("vpho:LLP_K" + btoksLbl + " -> K+:all K-:all", "", path=path)
619 
620  ma.copyLists(outputListName="vpho:LLP" + btoksLbl,
621  inputListNames=["vpho:LLP_e" + btoksLbl, "vpho:LLP_mu" + btoksLbl,
622  "vpho:LLP_pi" + btoksLbl, "vpho:LLP_K" + btoksLbl],
623  path=path)
624 
625  vertex.treeFit("vpho:LLP" + btoksLbl, conf_level=0, updateAllDaughters=True, path=path)
626 
627  ma.applyCuts("vpho:LLP" + btoksLbl, minDisplacementCut, path=path)
628 
629  ipKaon = "[pt > 0.1] and [abs(dr) < 0.5] and [abs(dz) < 2.0]"
630  ma.cutAndCopyList("K+:TrackFromIP" + btoksLbl, "K+:all", ipKaon, path=path)
631 
632  kinematicCuts = "[Mbc > 5.20] and [abs(deltaE) < 0.25]"
633  ma.reconstructDecay("B+:b" + btoksLbl + " -> K+:TrackFromIP" + btoksLbl + " vpho:LLP" + btoksLbl,
634  kinematicCuts, path=path)
635 
636  return ["B+:b" + btoksLbl]
637 
638 
639 @fancy_skim_header
641  """
642  Skim list contains events with at least one displaced vertex and no additional unused tracks from the IP.
643  """
644  __authors__ = ["Patrick Ecker"]
645  __contact__ = __liaison__
646  __description__ = "Skim for the inelastic Dark Matter with a Dark Higgs analysis."
647  __category__ = "physics, dark sector"
648  ApplyHLTHadronCut = False
649 
650  def addParticlesToPDG(self):
651  """Adds the particle codes to the basf2 pdg instance """
652  pdg.add_particle("chi2", 52, 0, 0, 0, 0)
653  # Abstract beam object to combine the two vertices (particles)
654  pdg.add_particle("beam", 9999, 0, 0, 0, 0)
655 
656  def load_standard_lists(self, path):
657  stdE("all", path=path)
658  stdMu("all", path=path)
659 
660  def additional_setup(self, path):
661  self.addParticlesToPDGaddParticlesToPDG()
662 
663  def build_lists(self, path):
664  skim_str = "InelasticDarkMatterWithDarkHiggs"
665  n_track_event_cut = "[nCleanedTracks([nCDCHits > 20] and [thetaInCDCAcceptance] and [dr < 0.5] and [abs(dz) < 2]) < 5]"
666 
667  track_requirements = "[formula(nPXDHits + nSVDHits + nCDCHits) > 20]"
668 
669  ma.cutAndCopyList(
670  f"e+:{skim_str}",
671  "e+:all",
672  f"[{track_requirements} and {n_track_event_cut}]",
673  path=path)
674  ma.cutAndCopyList(
675  f"mu+:{skim_str}",
676  "mu+:all",
677  f"[{track_requirements} and {n_track_event_cut}]",
678  path=path)
679 
680  ma.reconstructDecay(
681  decayString=f"A0:{skim_str} -> mu+:{skim_str} mu-:{skim_str}",
682  cut="pt > 0.1",
683  path=path
684  )
686  list_name=f"A0:{skim_str}",
687  conf_level=0,
688  updateAllDaughters=True,
689  path=path,
690  )
691  ma.applyCuts(
692  list_name=f"A0:{skim_str}",
693  cut="chiProb > 0",
694  path=path,
695  )
696 
697  ma.reconstructDecay(
698  decayString=f"chi2:{skim_str} -> e+:{skim_str} e-:{skim_str}",
699  cut="pt > 0.1",
700  path=path
701  )
703  list_name=f"chi2:{skim_str}",
704  conf_level=0,
705  updateAllDaughters=True,
706  path=path,
707  )
708  ma.applyCuts(
709  list_name=f"chi2:{skim_str}",
710  cut="chiProb > 0",
711  path=path,
712  )
713 
714  dr_cut = "[daughter(0, dr) > 0.05] or [daughter(1, dr) > 0.05]"
715  vertex_fit_cut = "[daughter(0, chiProb) > 0.1] or [daughter(1, chiProb) > 0.1]"
716  ma.reconstructDecay(
717  decayString=f"beam:{skim_str} -> A0:{skim_str} chi2:{skim_str}",
718  cut=f"[{dr_cut} and {vertex_fit_cut}]",
719  path=path,
720  )
721  ma.buildRestOfEvent(
722  target_list_name=f"beam:{skim_str}",
723  fillWithMostLikely=True,
724  path=path,
725  )
726  ma.appendROEMasks(
727  list_name=f"beam:{skim_str}",
728  mask_tuples=[
729  ("std_roe",
730  "thetaInCDCAcceptance and nCDCHits>20 and dr < 0.5 and abs(dz) < 2",
731  "thetaInCDCAcceptance and E > 0.05")],
732  path=path,
733  )
734 
735  ma.applyCuts(
736  list_name=f"beam:{skim_str}",
737  cut="roeNeextra(std_roe) < 2.0",
738  path=path,
739  )
740 
741  return [f"beam:{skim_str}"]
742 
743 
744 @fancy_skim_header
746  """
747  Searching the dark sector through U(1) kinetic mixing.
748 
749  Reconstructed 2 muons to a dark photon.
750  """
751  __description__ = ":math:`ee\\to A^{\\prime}A^{\\prime}\\nu_{D}\\nu_{D}\\to \\mu\\mu\\mu\\mu`"
752  __category__ = "physics, dark sector"
753  __authors__ = ["Chanyoung LEE"]
754  __contact__ = __liaison__
755 
756  ApplyHLTHadronCut = False
757 
758  def load_standard_lists(self, path):
759  stdMu("all", path=path)
760 
761  def build_lists(self, path):
762  muon_cuts = """[0.8 < muonID_noSVD]
763  and [inKLMAcceptance == 1]
764  and [inCDCAcceptance == 1] and [4 < nCDCHits]"""
765 
766  track_cuts = "[dr < 0.5] and [abs(dz) < 2]"
767 
768  path = self.skim_event_cutsskim_event_cuts(f"4 <= nCleanedTracks({track_cuts}) <= 6", path=path)
769 
770  ma.cutAndCopyList("mu+:accepted", "mu+:all", muon_cuts, path=path)
771  ma.reconstructDecay(decayString="vpho:rec -> mu+:accepted mu-:accepted", cut="", path=path)
772  ma.reconstructDecay(decayString="Upsilon(4S):rec -> vpho:rec vpho:rec", cut="", path=path)
773 
774  return ["Upsilon(4S):rec"]
def build_lists(self, path)
Definition: dark.py:761
def load_standard_lists(self, path)
Definition: dark.py:758
def additional_setup(self, path)
Definition: dark.py:140
def addALPToPDG(self)
Definition: dark.py:100
def initialALP(self, path)
Definition: dark.py:105
def build_lists(self, path)
Definition: dark.py:143
def build_lists(self, path)
Definition: dark.py:609
def load_standard_lists(self, path)
Definition: dark.py:603
def build_lists(self, path)
Definition: dark.py:470
def load_standard_lists(self, path)
Definition: dark.py:465
def build_lists(self, path)
Definition: dark.py:180
def load_standard_lists(self, path)
Definition: dark.py:177
def build_lists(self, path)
Definition: dark.py:315
def load_standard_lists(self, path)
Definition: dark.py:311
def build_lists(self, path)
Definition: dark.py:218
def load_standard_lists(self, path)
Definition: dark.py:214
def build_lists(self, path)
Definition: dark.py:394
def __init__(self, prescale_high=1, prescale_low=1, **kwargs)
Definition: dark.py:382
def load_standard_lists(self, path)
Definition: dark.py:377
def additional_setup(self, path)
Definition: dark.py:660
def load_standard_lists(self, path)
Definition: dark.py:656
def build_lists(self, path)
Definition: dark.py:558
def load_standard_lists(self, path)
Definition: dark.py:554
def build_lists(self, path)
Definition: dark.py:259
def load_standard_lists(self, path)
Definition: dark.py:255
def build_lists(self, path)
Definition: dark.py:514
def load_standard_lists(self, path)
Definition: dark.py:510
def build_lists(self, path)
Definition: dark.py:43
def load_standard_lists(self, path)
Definition: dark.py:40
def skim_event_cuts(self, cut, *path)
Definition: core.py:272
def add_particle(name, pdgCode, mass, width, charge, spin, max_width=None, lifetime=0, pythiaID=0)
Definition: pdg.py:133
def treeFit(list_name, conf_level=0.001, massConstraint=[], ipConstraint=False, updateAllDaughters=False, customOriginConstraint=False, customOriginVertex=[0.001, 0, 0.0116], customOriginCovariance=[0.0048, 0, 0, 0, 0.003567, 0, 0, 0, 0.0400], path=None)
Definition: vertex.py:223