Belle II Software  release-05-02-19
modularAnalysis.py
1 # !/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 """
5 This module defines wrapper functions around the analysis modules.
6 """
7 
8 from basf2 import register_module, create_path
9 from basf2 import B2INFO, B2WARNING, B2ERROR, B2FATAL
10 
11 import basf2
12 import subprocess
13 
14 
15 def setAnalysisConfigParams(configParametersAndValues, path):
16  """
17  Sets analysis configuration parameters.
18 
19  These are:
20 
21  - 'tupleStyle': 'Default' (default) or 'Laconic'
22  o) defines the style of the branch name in the ntuple
23 
24  - 'mcMatchingVersion': Specifies what version of mc matching algorithm is going to be used:
25 
26  - 'MC5' - analysis of BelleII MC5
27  - 'Belle' - analysis of Belle MC
28  - 'BelleII' (default) - all other cases
29 
30  @param configParametersAndValues dictionary of parameters and their values of the form {param1: value, param2: value, ...)
31  @param modules are added to this path
32  """
33 
34  conf = register_module('AnalysisConfiguration')
35 
36  allParameters = ['tupleStyle', 'mcMatchingVersion']
37 
38  keys = configParametersAndValues.keys()
39  for key in keys:
40  if key not in allParameters:
41  allParametersString = ', '.join(allParameters)
42  B2ERROR('Invalid analysis configuration parameter: ' + key + '.\n'
43  'Please use one of the following: ' + allParametersString)
44 
45  for param in allParameters:
46  if param in configParametersAndValues:
47  conf.param(param, configParametersAndValues.get(param))
48 
49  path.add_module(conf)
50 
51 
52 def inputMdst(environmentType, filename, path, skipNEvents=0, entrySequence=None, *, parentLevel=0):
53  """
54  Loads the specified ROOT (DST/mDST/muDST) file with the RootInput module.
55 
56  The correct environment (e.g. magnetic field settings) are determined from
57  the specified environment type. For the possible values please see
58  `inputMdstList()`
59 
60  Parameters:
61  environmentType (str): type of the environment to be loaded
62  filename (str): the name of the file to be loaded
63  path (basf2.Path): modules are added to this path
64  skipNEvents (int): N events of the input file are skipped
65  entrySequence (str): The number sequences (e.g. 23:42,101) defining the entries which are processed.
66  parentLevel (int): Number of generations of parent files (files used as input when creating a file) to be read
67  """
68  if entrySequence is not None:
69  entrySequence = [entrySequence]
70 
71  inputMdstList(environmentType, [filename], path, skipNEvents, entrySequence, parentLevel=parentLevel)
72 
73 
74 def inputMdstList(environmentType, filelist, path, skipNEvents=0, entrySequences=None, *, parentLevel=0):
75  """
76  Loads the specified ROOT (DST/mDST/muDST) files with the RootInput module.
77 
78  The correct environment (e.g. magnetic field settings) are determined from the specified environment type.
79  The currently available environments are:
80 
81  - 'MC5': for analysis of Belle II MC samples produced with releases prior to build-2016-05-01.
82  This environment sets the constant magnetic field (B = 1.5 T)
83  - 'MC6': for analysis of Belle II MC samples produced with build-2016-05-01 or newer but prior to release-00-08-00
84  - 'MC7': for analysis of Belle II MC samples produced with build-2016-05-01 or newer but prior to release-00-08-00
85  - 'MC8', for analysis of Belle II MC samples produced with release-00-08-00 or newer but prior to release-02-00-00
86  - 'MC9', for analysis of Belle II MC samples produced with release-00-08-00 or newer but prior to release-02-00-00
87  - 'MC10', for analysis of Belle II MC samples produced with release-00-08-00 or newer but prior to release-02-00-00
88  - 'default': for analysis of Belle II MC samples produced with releases with release-02-00-00 or newer.
89  This environment sets the default magnetic field (see geometry settings)
90  - 'Belle': for analysis of converted (or during of conversion of) Belle MC/DATA samples
91  - 'None': for analysis of generator level information or during simulation/reconstruction of
92  previously generated events
93 
94  Note that there is no difference between MC6 and MC7. Both are given for sake of completion.
95  The same is true for MC8, MC9 and MC10
96 
97  Parameters:
98  environmentType (str): type of the environment to be loaded
99  filelist (list(str)): the filename list of files to be loaded
100  path (basf2.Path): modules are added to this path
101  skipNEvents (int): N events of the input files are skipped
102  entrySequences (list(str)): The number sequences (e.g. 23:42,101) defining
103  the entries which are processed for each inputFileName.
104  parentLevel (int): Number of generations of parent files (files used as input when creating a file) to be read
105  """
106 
107  roinput = register_module('RootInput')
108  roinput.param('inputFileNames', filelist)
109  roinput.param('skipNEvents', skipNEvents)
110  if entrySequences is not None:
111  roinput.param('entrySequences', entrySequences)
112  roinput.param('parentLevel', parentLevel)
113 
114  path.add_module(roinput)
115  progress = register_module('ProgressBar')
116  path.add_module(progress)
117 
118  # None means don't create custom magnetic field, use whatever comes from the
119  # DB
120  environToMagneticField = {'MC5': 'MagneticFieldConstant',
121  'MC6': None,
122  'MC7': None,
123  'default': None,
124  'Belle': 'MagneticFieldConstantBelle'}
125 
126  fixECLClusters = {'MC5': True,
127  'MC6': True,
128  'MC7': True,
129  'default': False,
130  'Belle': False}
131 
132  if environmentType in environToMagneticField:
133  fieldType = environToMagneticField[environmentType]
134  if fieldType is not None:
135  from ROOT import Belle2 # reduced scope of potentially-misbehaving import
136  field = Belle2.MagneticField()
137  field.addComponent(Belle2.MagneticFieldComponentConstant(Belle2.B2Vector3D(0, 0, 1.5 * Belle2.Unit.T)))
138  Belle2.DBStore.Instance().addConstantOverride("MagneticField", field, False)
139  elif environmentType in ["MC8", "MC9", "MC10"]:
140  # make sure the last database setup is the magnetic field for MC8-10
141  from basf2 import conditions
142  conditions.globaltags += ["Legacy_MagneticField_MC8_MC9_MC10"]
143  elif environmentType is 'None':
144  B2INFO('No magnetic field is loaded. This is OK, if generator level information only is studied.')
145  else:
146  environments = ' '.join(list(environToMagneticField.keys()) + ["MC8", "MC9", "MC10"])
147  B2FATAL('Incorrect environment type provided: ' + environmentType + '! Please use one of the following:' + environments)
148 
149  # set the correct MCMatching algorithm for MC5 and Belle MC
150  if environmentType is 'Belle':
151  setAnalysisConfigParams({'mcMatchingVersion': 'Belle'}, path)
152  import b2bii
154  if environmentType is 'MC5':
155  setAnalysisConfigParams({'mcMatchingVersion': 'MC5'}, path)
156 
157  # fixECLCluster for MC5/MC6/MC7
158  if fixECLClusters.get(environmentType) is True:
159  fixECL = register_module('FixECLClusters')
160  path.add_module(fixECL)
161 
162 
163 def outputMdst(filename, path):
164  """
165  Saves mDST (mini-Data Summary Tables) to the output root file.
166  """
167 
168  import mdst
169  mdst.add_mdst_output(path, mc=True, filename=filename)
170 
171 
172 def outputUdst(filename, particleLists=[], includeArrays=[], path=None, dataDescription=None):
173  """
174  Save uDST (micro-Data Summary Tables) = MDST + Particles + ParticleLists
175  The charge-conjugate lists of those given in particleLists are also stored.
176  Additional Store Arrays and Relations to be stored can be specified via includeArrays
177  list argument.
178 
179  Note that this does not reduce the amount of Particle objects saved,
180  see skimOutputUdst() for a function that does.
181  """
182 
183  import mdst
184  import pdg
185  # also add anti-particle lists
186  plSet = set(particleLists)
187  for List in particleLists:
188  name, label = List.split(':')
189  plSet.add(pdg.conjugate(name) + ':' + label)
190 
191  partBranches = ['Particles', 'ParticlesToMCParticles',
192  'ParticlesToPIDLikelihoods', 'ParticleExtraInfoMap',
193  'EventExtraInfo'] + includeArrays + list(plSet)
194 
195  # set dataDescription: dictionary is mutable and thus not a good
196  # default argument.
197  if dataDescription is None:
198  dataDescription = {}
199 
200  dataDescription.update(dataLevel="udst")
201 
202  return mdst.add_mdst_output(path, mc=True, filename=filename,
203  additionalBranches=partBranches,
204  dataDescription=dataDescription)
205 
206 
207 def skimOutputUdst(skimDecayMode, skimParticleLists=[], outputParticleLists=[],
208  includeArrays=[], path=None, *,
209  outputFile=None, dataDescription=None):
210  """
211  Create a new path for events that contain a non-empty particle list specified via skimParticleLists.
212  Write the accepted events as a udst file, saving only particles from skimParticleLists
213  and from outputParticleLists.
214  Additional Store Arrays and Relations to be stored can be specified via includeArrays
215  list argument.
216 
217  :param str skimDecayMode: Name of the skim. If no outputFile is given this is
218  also the name of the output filename. This name will be added to the
219  FileMetaData as an extra data description "skimDecayMode"
220  :param list(str) skimParticleLists: Names of the particle lists to skim for.
221  An event will be accepted if at least one of the particle lists is not empty
222  :param list(str) outputParticleLists: Names of the particle lists to store in
223  the output in addition to the ones in skimParticleLists
224  :param list(str) includeArrays: datastore arrays/objects to write to the output
225  file in addition to mdst and particle information
226  :param basf2.Path path: Path to add the skim output to. Defaults to the default analysis path
227  :param str outputFile: Name of the output file if different from the skim name
228  :param dict dataDescription: Additional data descriptions to add to the output file. For example {"mcEventType":"mixed"}
229  """
230 
231  from basf2 import AfterConditionPath
232  # if no outputfile is specified, set it to the skim name
233  if outputFile is None:
234  outputFile = skimDecayMode
235 
236  # make sure the output filename has the correct extension
237  if not outputFile.endswith(".udst.root"):
238  outputFile += ".udst.root"
239 
240  skimfilter = register_module('SkimFilter')
241  skimfilter.set_name('SkimFilter_' + skimDecayMode)
242  skimfilter.param('particleLists', skimParticleLists)
243  path.add_module(skimfilter)
244  filter_path = create_path()
245  skimfilter.if_value('=1', filter_path, AfterConditionPath.CONTINUE)
246 
247  # add_independent_path() is rather expensive, only do this for skimmed events
248  skim_path = create_path()
249  saveParticleLists = skimParticleLists + outputParticleLists
250  removeParticlesNotInLists(saveParticleLists, path=skim_path)
251 
252  # set dataDescription: dictionary is mutable and thus not a good
253  # default argument.
254  if dataDescription is None:
255  dataDescription = {}
256 
257  dataDescription.setdefault("skimDecayMode", skimDecayMode)
258  outputUdst(outputFile, saveParticleLists, includeArrays, path=skim_path,
259  dataDescription=dataDescription)
260  filter_path.add_independent_path(skim_path, "skim_" + skimDecayMode)
261 
262 
263 def outputIndex(filename, path, includeArrays=[], keepParents=False, mc=True):
264  """
265  Write out all particle lists as an index file to be reprocessed using parentLevel flag.
266  Additional branches necessary for file to be read are automatically included.
267  Additional Store Arrays and Relations to be stored can be specified via includeArrays
268  list argument.
269 
270  @param str filename the name of the output index file
271  @param str path modules are added to this path
272  @param list(str) includeArrays: datastore arrays/objects to write to the output
273  file in addition to particle lists and related information
274  @param bool keepParents whether the parents of the input event will be saved as the parents of the same event
275  in the output index file. Useful if you are only adding more information to another index file
276  @param bool mc whether the input data is MC or not
277  """
278 
279  # Module to mark all branches to not be saved except particle lists
280  onlyPLists = register_module('OnlyWriteOutParticleLists')
281  path.add_module(onlyPLists)
282 
283  # Set up list of all other branches we need to make index file complete
284  partBranches = [
285  'Particles',
286  'ParticlesToMCParticles',
287  'ParticlesToPIDLikelihoods',
288  'ParticleExtraInfoMap',
289  'EventExtraInfo'
290  ]
291  branches = ['EventMetaData']
292  persistentBranches = ['FileMetaData']
293  if mc:
294  branches += []
295  # persistentBranches += ['BackgroundInfos']
296  branches += partBranches
297  branches += includeArrays
298 
299  r1 = register_module('RootOutput')
300  r1.param('outputFileName', filename)
301  r1.param('additionalBranchNames', branches)
302  r1.param('branchNamesPersistent', persistentBranches)
303  r1.param('keepParents', keepParents)
304  path.add_module(r1)
305 
306 
307 def setupEventInfo(noEvents, path):
308  """
309  Prepare to generate events. This function sets up the EventInfoSetter.
310  You should call this before adding a generator from generators.
311  The experiment and run numbers are set to 0 (run independent generic MC in phase 3).
312  https://confluence.desy.de/display/BI/Experiment+numbering
313 
314  Parameters:
315  noEvents (int): number of events to be generated
316  path (basf2.Path): modules are added to this path
317  """
318  evtnumbers = register_module('EventInfoSetter')
319  evtnumbers.param('evtNumList', [noEvents])
320  evtnumbers.param('runList', [0])
321  evtnumbers.param('expList', [0])
322  path.add_module(evtnumbers)
323 
324 
325 def loadGearbox(path, silence_warning=False):
326  """
327  Loads Gearbox module to the path.
328 
329  Warning:
330  Should be used in a job with *cosmic event generation only*
331 
332  Needed for scripts which only generate cosmic events in order to
333  load the geometry.
334 
335  @param path modules are added to this path
336  @param silence_warning stops a verbose warning message if you know you want to use this function
337  """
338 
339  if not silence_warning:
340  B2WARNING("""You are overwriting the geometry from the database with Gearbox.
341  This is fine if you're generating cosmic events. But in most other cases you probably don't want this.
342 
343  If you're really sure you know what you're doing you can suppress this message with:
344 
345  >>> loadGearbox(silence_warning=True)
346 
347  """)
348 
349  paramloader = register_module('Gearbox')
350  path.add_module(paramloader)
351 
352 
353 def printPrimaryMCParticles(path):
354  """
355  Prints all primary MCParticles.
356  """
357 
358  mcparticleprinter = register_module('PrintMCParticles')
359  path.add_module(mcparticleprinter)
360 
361 
362 def printMCParticles(onlyPrimaries=False, maxLevel=-1, path=None):
363  """
364  Prints all MCParticles or just primary MCParticles up to specified level. -1 means no limit.
365  """
366 
367  mcparticleprinter = register_module('PrintMCParticles')
368  mcparticleprinter.param('onlyPrimaries', onlyPrimaries)
369  mcparticleprinter.param('maxLevel', maxLevel)
370  path.add_module(mcparticleprinter)
371 
372 
373 def correctBrems(outputList,
374  inputList,
375  gammaList,
376  maximumAcceptance=3.0,
377  multiplePhotons=False,
378  usePhotonOnlyOnce=True,
379  writeOut=False,
380  path=None):
381  """
382  For each particle in the given ``inputList``, copies it to the ``outputList`` and adds the
383  4-vector of the photon(s) in the ``gammaList`` which has(have) a weighted named relation to
384  the particle's track, set by the ``ECLTrackBremFinder`` module during reconstruction.
385 
386  Warning:
387  This can only work if the mdst file contains the *Bremsstrahlung* named relation. Official MC samples
388  up to and including MC12 and proc9 **do not** contain this. Newer production campaigns (from proc10 and MC13) will.
389 
390  Information:
391  Please note that a new particle is always generated, with the old particle and -if found- one or more
392  photons as daughters.
393 
394  The ``inputList`` should contain particles with associated tracks. Otherwise the module will exit with an error.
395 
396  The ``gammaList`` should contain photons. Otherwise the module will exit with an error.
397 
398  @param outputList The output particle list name containing the corrected particles
399  @param inputList The initial particle list name containing the particles to correct. *It should already exist.*
400  @param gammaList The photon list containing possibly bremsstrahlung photons; *It should already exist.*
401  @param maximumAcceptance Maximum value of the relation weight. Should be a number between [0,3)
402  @param multiplePhotons Whether to use only one photon (the one with the smallest acceptance) or as many as possible
403  @param usePhotonOnlyOnce If true, each brems candidate is used to correct only the track with the smallest relation weight
404  @param writeOut Whether `RootOutput` module should save the created ``outputList``
405  @param path The module is added to this path
406  """
407 
408  bremscorrector = register_module('BremsFinder')
409  bremscorrector.set_name('bremsCorrector_' + outputList)
410  bremscorrector.param('inputList', inputList)
411  bremscorrector.param('outputList', outputList)
412  bremscorrector.param('gammaList', gammaList)
413  bremscorrector.param('maximumAcceptance', maximumAcceptance)
414  bremscorrector.param('multiplePhotons', multiplePhotons)
415  bremscorrector.param('usePhotonOnlyOnce', usePhotonOnlyOnce)
416  bremscorrector.param('writeOut', writeOut)
417  path.add_module(bremscorrector)
418 
419 
420 def copyList(outputListName, inputListName, writeOut=False, path=None):
421  """
422  Copy all Particle indices from input ParticleList to the output ParticleList.
423  Note that the Particles themselves are not copied. The original and copied
424  ParticleLists will point to the same Particles.
425 
426  @param ouputListName copied ParticleList
427  @param inputListName original ParticleList to be copied
428  @param writeOut whether RootOutput module should save the created ParticleList
429  @param path modules are added to this path
430  """
431 
432  copyLists(outputListName, [inputListName], writeOut, path)
433 
434 
435 def correctBremsBelle(outputListName,
436  inputListName,
437  gammaListName,
438  multiplePhotons=True,
439  minimumEnergy=0.05,
440  angleThreshold=0.05,
441  writeOut=False,
442  path=None):
443  """
444  Run the Belle - like brems finding on the ``inputListName`` of charged particles.
445  Adds all photons in ``gammaListName`` to a copy of the charged particle that are within
446  ``angleThreshold`` and above ``minimumEnergy``.
447 
448  Parameters:
449  outputListName (str): The output charged particle list containing the corrected charged particles
450  inputListName (str): The initial charged particle list containing the charged particles to correct.
451  gammaListName (str): The gammas list containing possibly radiative gammas, should already exist.
452  multiplePhotons (bool): How many photons should be added to the charged particle? nearest one -> False,
453  add all the photons within the cone -> True
454  angleThreshold (float): The maximum angle in radians between the charged particle and the (radiative)
455  gamma to be accepted.
456  minimumEnergy (float): The minimum energy in GeV of the (radiative) gamma to be accepted.
457  writeOut (bool): whether RootOutput module should save the created ParticleList
458  path (basf2.Path): modules are added to this path
459  """
460 
461  fsrcorrector = register_module('BelleBremRecovery')
462  fsrcorrector.set_name('BelleFSRCorrection_' + outputListName)
463  fsrcorrector.param('inputListName', inputListName)
464  fsrcorrector.param('outputListName', outputListName)
465  fsrcorrector.param('gammaListName', gammaListName)
466  fsrcorrector.param('multiplePhotons', multiplePhotons)
467  fsrcorrector.param('angleThreshold', angleThreshold)
468  fsrcorrector.param('minimumEnergy', minimumEnergy)
469  fsrcorrector.param('writeOut', writeOut)
470  path.add_module(fsrcorrector)
471 
472 
473 def copyLists(outputListName, inputListNames, writeOut=False, path=None):
474  """
475  Copy all Particle indices from all input ParticleLists to the
476  single output ParticleList.
477  Note that the Particles themselves are not copied.
478  The original and copied ParticleLists will point to the same Particles.
479 
480  Duplicates are removed based on the first-come, first-served principle.
481  Therefore, the order of the input ParticleLists matters.
482 
483  .. seealso::
484  If you want to select the best duplicate based on another criterion, have
485  a look at the function `mergeListsWithBestDuplicate`.
486 
487  .. note::
488  Two particles that differ only by the order of their daughters are
489  considered duplicates and one of them will be removed.
490 
491  @param ouputListName copied ParticleList
492  @param inputListName vector of original ParticleLists to be copied
493  @param writeOut whether RootOutput module should save the created ParticleList
494  @param path modules are added to this path
495  """
496 
497  pmanipulate = register_module('ParticleListManipulator')
498  pmanipulate.set_name('PListCopy_' + outputListName)
499  pmanipulate.param('outputListName', outputListName)
500  pmanipulate.param('inputListNames', inputListNames)
501  pmanipulate.param('writeOut', writeOut)
502  path.add_module(pmanipulate)
503 
504 
505 def copyParticles(outputListName, inputListName, writeOut=False, path=None):
506  """
507  Create copies of Particles given in the input ParticleList and add them to the output ParticleList.
508 
509  The existing relations of the original Particle (or it's (grand-)^n-daughters)
510  are copied as well. Note that only the relation is copied and that the related
511  object is not. Copied particles are therefore related to the *same* object as
512  the original ones.
513 
514  @param ouputListName new ParticleList filled with copied Particles
515  @param inputListName input ParticleList with original Particles
516  @param writeOut whether RootOutput module should save the created ParticleList
517  @param path modules are added to this path
518  """
519 
520  # first copy original particles to the new ParticleList
521  pmanipulate = register_module('ParticleListManipulator')
522  pmanipulate.set_name('PListCopy_' + outputListName)
523  pmanipulate.param('outputListName', outputListName)
524  pmanipulate.param('inputListNames', [inputListName])
525  pmanipulate.param('writeOut', writeOut)
526  path.add_module(pmanipulate)
527 
528  # now replace original particles with their copies
529  pcopy = register_module('ParticleCopier')
530  pcopy.param('inputListNames', [outputListName])
531  path.add_module(pcopy)
532 
533 
534 def cutAndCopyLists(outputListName, inputListNames, cut, writeOut=False, path=None):
535  """
536  Copy candidates from all lists in ``inputListNames`` to
537  ``outputListName`` if they pass ``cut`` (given selection criteria).
538 
539  Note:
540  Note that the Particles themselves are not copied.
541  The original and copied ParticleLists will point to the same Particles.
542 
543  Example:
544  Require energetic pions safely inside the cdc
545 
546  >>> cutAndCopyLists("pi+:energeticPions", ["pi+:good", "pi+:loose"], "[E > 2] and [0.3 < theta < 2.6]", path=mypath)
547 
548  Warning:
549  You must use square braces ``[`` and ``]`` for conditional statements.
550 
551  Parameters:
552  outputListName (str): the new ParticleList name
553  inputListName (list(str)): list of input ParticleList names
554  cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
555  writeOut (bool): whether RootOutput module should save the created ParticleList
556  path (basf2.Path): modules are added to this path
557  """
558  pmanipulate = register_module('ParticleListManipulator')
559  pmanipulate.set_name('PListCutAndCopy_' + outputListName)
560  pmanipulate.param('outputListName', outputListName)
561  pmanipulate.param('inputListNames', inputListNames)
562  pmanipulate.param('cut', cut)
563  pmanipulate.param('writeOut', writeOut)
564  path.add_module(pmanipulate)
565 
566 
567 def cutAndCopyList(outputListName, inputListName, cut, writeOut=False, path=None):
568  """
569  Copy candidates from ``inputListName`` to ``outputListName`` if they pass
570  ``cut`` (given selection criteria).
571 
572  Note:
573  Note the Particles themselves are not copied.
574  The original and copied ParticleLists will point to the same Particles.
575 
576  Example:
577  require energetic pions safely inside the cdc
578 
579  >>> cutAndCopyLists("pi+:energeticPions", "pi+:loose", "[E > 2] and [0.3 < theta < 2.6]", path=mypath)
580 
581  Warning:
582  You must use square braces ``[`` and ``]`` for conditional statements.
583 
584  Parameters:
585  outputListName (str): the new ParticleList name
586  inputListName (str): input ParticleList name
587  cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
588  writeOut (bool): whether RootOutput module should save the created ParticleList
589  path (basf2.Path): modules are added to this path
590  """
591  cutAndCopyLists(outputListName, [inputListName], cut, writeOut, path)
592 
593 
594 def trackingEfficiency(inputListNames, fraction, path=None):
595  """
596  Randomly remove tracks from the provided particle lists to estimate the tracking efficiency.
597  Takes care of the duplicates, if any.
598 
599  Parameters:
600  inputListNames (list(str)): input particle list names
601  fraction (float): fraction of particles to be removed randomly
602  path (basf2.Path): module is added to this path
603  """
604 
605  trackingefficiency = register_module('TrackingEfficiency')
606  trackingefficiency.param('particleLists', inputListNames)
607  trackingefficiency.param('frac', fraction)
608  path.add_module(trackingefficiency)
609 
610 
611 def trackingMomentum(inputListNames, scale, path=None):
612  """
613  Scale momenta of the particles (based on charged tracks) according to the scaling factor scale.
614 
615  Parameters:
616  inputListNames (list(str)): input particle list names
617  scale (float): scaling factor (1.0 -- no scaling)
618  path (basf2.Path): module is added to this path
619  """
620 
621  trackingmomentum = register_module('TrackingMomentum')
622  trackingmomentum.param('particleLists', inputListNames)
623  trackingmomentum.param('scale', scale)
624  path.add_module(trackingmomentum)
625 
626 
627 def mergeListsWithBestDuplicate(outputListName,
628  inputListNames,
629  variable,
630  preferLowest=True,
631  writeOut=False,
632  path=None):
633  """
634  Merge input ParticleLists into one output ParticleList. Only the best
635  among duplicates is kept. The lowest or highest value (configurable via
636  preferLowest) of the provided variable determines which duplicate is the
637  best.
638 
639  @param ouputListName name of merged ParticleList
640  @param inputListName vector of original ParticleLists to be merged
641  @param variable variable to determine best duplicate
642  @param preferLowest whether lowest or highest value of variable should be preferred
643  @param writeOut whether RootOutput module should save the created ParticleList
644  @param path modules are added to this path
645  """
646 
647  pmanipulate = register_module('ParticleListManipulator')
648  pmanipulate.set_name('PListMerger_' + outputListName)
649  pmanipulate.param('outputListName', outputListName)
650  pmanipulate.param('inputListNames', inputListNames)
651  pmanipulate.param('variable', variable)
652  pmanipulate.param('preferLowest', preferLowest)
653  pmanipulate.param('writeOut', writeOut)
654  path.add_module(pmanipulate)
655 
656 
657 def fillSignalSideParticleList(outputListName, decayString, path):
658  """
659  This function should only be used in the ROE path, that is a path
660  that is executed for each ROE object in the DataStore.
661 
662  Example: fillSignalSideParticleList('gamma:sig','B0 -> K*0 ^gamma', roe_path)
663 
664  Function will create a ParticleList with name 'gamma:sig' which will be filled
665  with the existing photon Particle, being the second daughter of the B0 candidate
666  to which the ROE object has to be related.
667 
668  @param ouputListName name of the created ParticleList
669  @param decayString specify Particle to be added to the ParticleList
670  """
671 
672  pload = register_module('SignalSideParticleListCreator')
673  pload.set_name('SSParticleList_' + outputListName)
674  pload.param('particleListName', outputListName)
675  pload.param('decayString', decayString)
676  path.add_module(pload)
677 
678 
679 def fillParticleLists(decayStringsWithCuts, writeOut=False, path=None, enforceFitHypothesis=False):
680  """
681  Creates Particles of the desired types from the corresponding ``mdst`` dataobjects,
682  loads them to the ``StoreArray<Particle>`` and fills the ParticleLists.
683 
684  The multiple ParticleLists with their own selection criteria are specified
685  via list tuples (decayString, cut), for example
686 
687  .. code-block:: python
688 
689  kaons = ('K+:mykaons', 'kaonID>0.1')
690  pions = ('pi+:mypions','pionID>0.1')
691  fillParticleLists([kaons, pions])
692 
693  If you are unsure what selection you want, you might like to see the
694  :doc:`StandardParticles` functions.
695 
696  The type of the particles to be loaded is specified via the decayString module parameter.
697  The type of the ``mdst`` dataobject that is used as an input is determined from the type of
698  the particle. The following types of the particles can be loaded:
699 
700  * charged final state particles (input ``mdst`` type = Tracks)
701  - e+, mu+, pi+, K+, p, deuteron (and charge conjugated particles)
702 
703  * neutral final state particles
704  - "gamma" (input ``mdst`` type = ECLCluster)
705  - "K_S0", "Lambda0" (input ``mdst`` type = V0)
706  - "K_L0" (input ``mdst`` type = KLMCluster or ECLCluster)
707 
708  Note:
709  For "K_S0" and "Lambda0" you must specify the daughter ordering.
710 
711  For example, to load V0s as :math:`\\Lambda^0\\to p^+\\pi^-` decays from V0s:
712 
713  .. code-block:: python
714 
715  v0lambdas = ('Lambda0 -> p+ pi-', '0.9 < M < 1.3')
716  fillParticleLists([kaons, pions, v0lambdas], path=mypath)
717 
718  Tip:
719  For "K_L0" it is now possible to load from ECLClusters, to revert to
720  the old (Belle) behavior, you can require ``'isFromKLM > 0'``.
721 
722  .. code-block:: python
723 
724  klongs = ('K_L0', 'isFromKLM > 0')
725  fillParticleLists([kaons, pions, klongs], path=mypath)
726 
727 
728  Parameters:
729  decayStringsWithCuts (list): A list of python ntuples of (decayString, cut).
730  The decay string determines the type of Particle
731  and determines the of the ParticleList.
732  If the input MDST type is V0 the whole
733  decay chain needs to be specified, so that
734  the user decides and controls the daughters
735  ' order (e.g. ``K_S0 -> pi+ pi-``)
736  The cut is the selection criteria
737  to be added to the ParticleList. It can be an empty string.
738  writeOut (bool): whether RootOutput module should save the created ParticleList
739  path (basf2.Path): modules are added to this path
740  enforceFitHypothesis (bool): If true, Particles will be created only for the tracks which have been fitted
741  using a mass hypothesis of the exact type passed to fillParticleLists().
742  If enforceFitHypothesis is False (the default) the next closest fit hypothesis
743  in terms of mass difference will be used if the fit using exact particle
744  type is not available.
745  """
746 
747  pload = register_module('ParticleLoader')
748  pload.set_name('ParticleLoader_' + 'PLists')
749  pload.param('decayStringsWithCuts', decayStringsWithCuts)
750  pload.param('writeOut', writeOut)
751  pload.param("enforceFitHypothesis", enforceFitHypothesis)
752  path.add_module(pload)
753 
754 
755 def fillParticleList(decayString, cut, writeOut=False, path=None, enforceFitHypothesis=False):
756  """
757  Creates Particles of the desired type from the corresponding ``mdst`` dataobjects,
758  loads them to the StoreArray<Particle> and fills the ParticleList.
759 
760  See also:
761  the :doc:`StandardParticles` functions.
762 
763  The type of the particles to be loaded is specified via the decayString module parameter.
764  The type of the ``mdst`` dataobject that is used as an input is determined from the type of
765  the particle. The following types of the particles can be loaded:
766 
767  * charged final state particles (input ``mdst`` type = Tracks)
768  - e+, mu+, pi+, K+, p, deuteron (and charge conjugated particles)
769 
770  * neutral final state particles
771  - "gamma" (input ``mdst`` type = ECLCluster)
772  - "K_S0", "Lambda0" (input ``mdst`` type = V0)
773  - "K_L0" (input ``mdst`` type = KLMCluster or ECLCluster)
774 
775  Note:
776  For "K_S0" and "Lambda0" you must specify the daughter ordering.
777 
778  For example, to load V0s as :math:`\\Lambda^0\\to p^+\\pi^-` decays from V0ss:
779 
780  .. code-block:: python
781 
782  fillParticleList('Lambda0 -> p+ pi-', '0.9 < M < 1.3', path=mypath)
783 
784  Tip:
785  For "K_L0" it is now possible to load from ECLClusters, to revert to
786  the old (Belle) behavior, you can require ``'isFromKLM > 0'``.
787 
788  .. code-block:: python
789 
790  fillParticleList('K_L0', 'isFromKLM > 0', path=mypath)
791 
792  Parameters:
793  decayString (str): Type of Particle and determines the name of the ParticleList.
794  If the input MDST type is V0 the whole decay chain needs to be specified, so that
795  the user decides and controls the daughters' order (e.g. ``K_S0 -> pi+ pi-``)
796  cut (str): Particles need to pass these selection criteria to be added to the ParticleList
797  writeOut (bool): whether RootOutput module should save the created ParticleList
798  path (basf2.Path): modules are added to this path
799  enforceFitHypothesis (bool): If true, Particles will be created only for the tracks which have been fitted
800  using a mass hypothesis of the exact type passed to fillParticleLists().
801  If enforceFitHypothesis is False (the default) the next closest fit hypothesis
802  in terms of mass difference will be used if the fit using exact particle
803  type is not available.
804  """
805 
806  pload = register_module('ParticleLoader')
807  pload.set_name('ParticleLoader_' + decayString)
808  pload.param('decayStringsWithCuts', [(decayString, cut)])
809  pload.param('writeOut', writeOut)
810  pload.param("enforceFitHypothesis", enforceFitHypothesis)
811  path.add_module(pload)
812 
813 
814 def fillParticleListWithTrackHypothesis(decayString,
815  cut,
816  hypothesis,
817  writeOut=False,
818  enforceFitHypothesis=False,
819  path=None):
820  """
821  As fillParticleList, but if used for a charged FSP, loads the particle with the requested hypothesis if available
822 
823  @param decayString specifies type of Particles and determines the name of the ParticleList
824  @param cut Particles need to pass these selection criteria to be added to the ParticleList
825  @param hypothesis the PDG code of the desired track hypothesis
826  @param writeOut whether RootOutput module should save the created ParticleList
827  @param enforceFitHypothesis If true, Particles will be created only for the tracks which have been fitted
828  using a mass hypothesis of the exact type passed to fillParticleLists().
829  If enforceFitHypothesis is False (the default) the next closest fit hypothesis
830  in terms of mass difference will be used if the fit using exact particle
831  type is not available.
832  @param path modules are added to this path
833  """
834 
835  pload = register_module('ParticleLoader')
836  pload.set_name('ParticleLoader_' + decayString)
837  pload.param('decayStringsWithCuts', [(decayString, cut)])
838  pload.param('trackHypothesis', hypothesis)
839  pload.param('writeOut', writeOut)
840  pload.param("enforceFitHypothesis", enforceFitHypothesis)
841  path.add_module(pload)
842 
843 
844 def fillConvertedPhotonsList(decayString, cut, writeOut=False, path=None):
845  """
846  Creates photon Particle object for each e+e- combination in the V0 StoreArray.
847 
848  Note:
849  You must specify the daughter ordering.
850 
851  .. code-block:: python
852 
853  fillConvertedPhotonsList('gamma:converted -> e+ e-', '')
854 
855  Parameters:
856  decayString (str): Must be gamma to an e+e- pair. You muse specify the daughter ordering.
857  Will also determine the name of the particleList.
858  cut (str): Particles need to pass these selection criteria to be added to the ParticleList
859  writeOut (bool): whether RootOutput module should save the created ParticleList
860  path (basf2.Path): modules are added to this path
861 
862  """
863  pload = register_module('ParticleLoader')
864  pload.set_name('ParticleLoader_' + decayString)
865  pload.param('decayStringsWithCuts', [(decayString, cut)])
866  pload.param('addDaughters', True)
867  pload.param('writeOut', writeOut)
868  path.add_module(pload)
869 
870 
871 def fillParticleListFromROE(decayString,
872  cut,
873  maskName='',
874  sourceParticleListName='',
875  useMissing=False,
876  writeOut=False,
877  path=None):
878  """
879  Creates Particle object for each ROE of the desired type found in the
880  StoreArray<RestOfEvent>, loads them to the StoreArray<Particle>
881  and fills the ParticleList. If useMissing is True, then the missing
882  momentum is used instead of ROE.
883 
884  The type of the particles to be loaded is specified via the decayString module parameter.
885 
886  @param decayString specifies type of Particles and determines the name of the ParticleList.
887  Source ROEs can be taken as a daughter list, for example:
888  'B0:tagFromROE -> B0:signal'
889  @param cut Particles need to pass these selection criteria to be added to the ParticleList
890  @param maskName Name of the ROE mask to use
891  @param sourceParticleListName Use related ROEs to this particle list as a source
892  @param useMissing Use missing momentum instead of ROE momentum
893  @param writeOut whether RootOutput module should save the created ParticleList
894  @param path modules are added to this path
895  """
896 
897  pload = register_module('ParticleLoader')
898  pload.set_name('ParticleLoader_' + decayString)
899  pload.param('decayStringsWithCuts', [(decayString, cut)])
900  pload.param('writeOut', writeOut)
901  pload.param('roeMaskName', maskName)
902  pload.param('useMissing', useMissing)
903  pload.param('sourceParticleListName', sourceParticleListName)
904  pload.param('useROEs', True)
905  path.add_module(pload)
906 
907 
908 def fillParticleListFromMC(decayString,
909  cut,
910  addDaughters=False,
911  skipNonPrimaryDaughters=False,
912  writeOut=False,
913  path=None):
914  """
915  Creates Particle object for each MCParticle of the desired type found in the StoreArray<MCParticle>,
916  loads them to the StoreArray<Particle> and fills the ParticleList.
917 
918  The type of the particles to be loaded is specified via the decayString module parameter.
919 
920  @param decayString specifies type of Particles and determines the name of the ParticleList
921  @param cut Particles need to pass these selection criteria to be added to the ParticleList
922  @param addDaughters adds the bottom part of the decay chain of the particle to the datastore and
923  sets mother-daughter relations
924  @param skipNonPrimaryDaughters if true, skip non primary daughters, useful to study final state daughter particles
925  @param writeOut whether RootOutput module should save the created ParticleList
926  @param path modules are added to this path
927  """
928 
929  pload = register_module('ParticleLoader')
930  pload.set_name('ParticleLoader_' + decayString)
931  pload.param('decayStringsWithCuts', [(decayString, cut)])
932  pload.param('addDaughters', addDaughters)
933  pload.param('skipNonPrimaryDaughters', skipNonPrimaryDaughters)
934  pload.param('writeOut', writeOut)
935  pload.param('useMCParticles', True)
936  path.add_module(pload)
937 
938 
939 def fillParticleListsFromMC(decayStringsWithCuts,
940  addDaughters=False,
941  skipNonPrimaryDaughters=False,
942  writeOut=False,
943  path=None):
944  """
945  Creates Particle object for each MCParticle of the desired type found in the StoreArray<MCParticle>,
946  loads them to the StoreArray<Particle> and fills the ParticleLists.
947 
948  The types of the particles to be loaded are specified via the (decayString, cut) tuples given in a list.
949  For example:
950  kaons = ('K+:gen', '')
951  pions = ('pi+:gen', 'pionID>0.1')
952  fillParticleListsFromMC([kaons, pions])
953 
954  @param decayString specifies type of Particles and determines the name of the ParticleList
955  @param cut Particles need to pass these selection criteria to be added to the ParticleList
956  @param addDaughters adds the bottom part of the decay chain of the particle to the datastore and
957  sets mother-daughter relations
958  @param skipNonPrimaryDaughters if true, skip non primary daughters, useful to study final state daughter particles
959  @param writeOut whether RootOutput module should save the created ParticleList
960  @param path modules are added to this path
961  """
962 
963  pload = register_module('ParticleLoader')
964  pload.set_name('ParticleLoader_' + 'PLists')
965  pload.param('decayStringsWithCuts', decayStringsWithCuts)
966  pload.param('addDaughters', addDaughters)
967  pload.param('skipNonPrimaryDaughters', skipNonPrimaryDaughters)
968  pload.param('writeOut', writeOut)
969  pload.param('useMCParticles', True)
970  path.add_module(pload)
971 
972 
973 def applyCuts(list_name, cut, path):
974  """
975  Removes particle candidates from ``list_name`` that do not pass ``cut``
976  (given selection criteria).
977 
978  Example:
979  require energetic pions safely inside the cdc
980 
981  >>> applyCuts("pi+:mypions", "[E > 2] and [0.3 < theta < 2.6]", path=mypath)
982 
983  Warning:
984  You must use square braces ``[`` and ``]`` for conditional statements.
985 
986  Parameters:
987  list_name (str): input ParticleList name
988  cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
989  path (basf2.Path): modules are added to this path
990  """
991 
992  pselect = register_module('ParticleSelector')
993  pselect.set_name('ParticleSelector_applyCuts_' + list_name)
994  pselect.param('decayString', list_name)
995  pselect.param('cut', cut)
996  path.add_module(pselect)
997 
998 
999 def applyEventCuts(cut, path):
1000  """
1001  Removes events that do not pass the ``cut`` (given selection criteria).
1002 
1003  Example:
1004  continuum events (in mc only) with more than 5 tracks
1005 
1006  >>> applyEventCuts("[nTracks > 5] and [isContinuumEvent], path=mypath)
1007 
1008  Warning:
1009  You must use square braces ``[`` and ``]`` for conditional statements.
1010 
1011  Parameters:
1012  cut (str): Events that do not pass these selection criteria are skipped
1013  path (basf2.Path): modules are added to this path
1014  """
1015 
1016  eselect = register_module('VariableToReturnValue')
1017  eselect.param('variable', 'passesEventCut(' + cut + ')')
1018  path.add_module(eselect)
1019  empty_path = create_path()
1020  eselect.if_value('<1', empty_path)
1021 
1022 
1023 def reconstructDecay(decayString,
1024  cut,
1025  dmID=0,
1026  writeOut=False,
1027  path=None,
1028  candidate_limit=None,
1029  ignoreIfTooManyCandidates=True,
1030  chargeConjugation=True,
1031  allowChargeViolation=False):
1032  r"""
1033  Creates new Particles by making combinations of existing Particles - it reconstructs unstable particles via their specified
1034  decay mode, e.g. in form of a :ref:`DecayString`: :code:`D0 -> K- pi+` or :code:`B+ -> anti-D0 pi+`, ... All possible
1035  combinations are created (particles are used only once per candidate) and combinations that pass the specified selection
1036  criteria are saved to a newly created (mother) ParticleList. By default the charge conjugated decay is reconstructed as well
1037  (meaning that the charge conjugated mother list is created as well) but this can be deactivated.
1038 
1039  One can use an ``@``-sign to mark a particle as unspecified, e.g. in form of a DecayString: :code:`\@Xsd -> K+ pi-`. If the
1040  particle is marked as unspecified, its identity will not be checked when doing :ref:`MCMatching`. Any particle which decays into
1041  the correct daughters will be flagged as correct. For example the DecayString :code:`\@Xsd -> K+ pi-` would match all particles
1042  which decay into a kaon and a pion, for example :math:`K^*`, :math:`B^0`, :math:`D^0`. Still the daughters need to be stated
1043  correctly so this can be used for "sum of exclusive" decays.
1044 
1045  .. warning::
1046  The input ParticleLists are typically ordered according to the upstream reconstruction algorithm.
1047  Therefore, if you combine two or more identical particles in the decay chain you should not expect to see the same
1048  distribution for the daughter kinematics as they may be sorted by geometry, momentum etc.
1049 
1050  For example, in the decay :code:`D0 -> pi0 pi0` the momentum distributions of the two ``pi0`` s are not identical.
1051  This can be solved by manually randomising the lists before combining.
1052 
1053  See Also:
1054 
1055  * `Particle combiner how does it work? <https://questions.belle2.org/question/4318/particle-combiner-how-does-it-work/>`_
1056  * `Identical particles in decay chain <https://questions.belle2.org/question/5724/identical-particles-in-decay-chain/>`_
1057 
1058  @param decayString :ref:`DecayString` specifying what kind of the decay should be reconstructed
1059  (from the DecayString the mother and daughter ParticleLists are determined)
1060  @param cut created (mother) Particles are added to the mother ParticleList if they
1061  pass give cuts (in VariableManager style) and rejected otherwise
1062  @param dmID user specified decay mode identifier
1063  @param writeOut whether RootOutput module should save the created ParticleList
1064  @param path modules are added to this path
1065  @param candidate_limit Maximum amount of candidates to be reconstructed. If
1066  the number of candidates is exceeded a Warning will be
1067  printed.
1068  By default, all these candidates will be removed and event will be ignored.
1069  This behaviour can be changed by \'ignoreIfTooManyCandidates\' flag.
1070  If no value is given the amount is limited to a sensible
1071  default. A value <=0 will disable this limit and can
1072  cause huge memory amounts so be careful.
1073  @param ignoreIfTooManyCandidates whether event should be ignored or not if number of reconstructed
1074  candidates reaches limit. If event is ignored, no candidates are reconstructed,
1075  otherwise, number of candidates in candidate_limit is reconstructed.
1076  @param chargeConjugation boolean to decide whether charge conjugated mode should be reconstructed as well (on by default)
1077  @param allowChargeViolation whether the decay string needs to conserve the electric charge
1078  """
1079 
1080  pmake = register_module('ParticleCombiner')
1081  pmake.set_name('ParticleCombiner_' + decayString)
1082  pmake.param('decayString', decayString)
1083  pmake.param('cut', cut)
1084  pmake.param('decayMode', dmID)
1085  pmake.param('writeOut', writeOut)
1086  if candidate_limit is not None:
1087  pmake.param("maximumNumberOfCandidates", candidate_limit)
1088  pmake.param("ignoreIfTooManyCandidates", ignoreIfTooManyCandidates)
1089  pmake.param('chargeConjugation', chargeConjugation)
1090  pmake.param("allowChargeViolation", allowChargeViolation)
1091  path.add_module(pmake)
1092 
1093 
1094 def combineAllParticles(inputParticleLists, outputList, cut='', writeOut=False, path=None):
1095  """
1096  Creates a new Particle as the combination of all Particles from all
1097  provided inputParticleLists. However, each particle is used only once
1098  (even if duplicates are provided) and the combination has to pass the
1099  specified selection criteria to be saved in the newly created (mother)
1100  ParticleList.
1101 
1102  @param inputParticleLists List of input particle lists which are combined to the new Particle
1103  @param outputList Name of the particle combination created with this module
1104  @param cut created (mother) Particle is added to the mother ParticleList if it passes
1105  these given cuts (in VariableManager style) and is rejected otherwise
1106  @param writeOut whether RootOutput module should save the created ParticleList
1107  @param path module is added to this path
1108  """
1109 
1110  pmake = register_module('AllParticleCombiner')
1111  pmake.set_name('AllParticleCombiner_' + outputList)
1112  pmake.param('inputListNames', inputParticleLists)
1113  pmake.param('outputListName', outputList)
1114  pmake.param('cut', cut)
1115  pmake.param('writeOut', writeOut)
1116  path.add_module(pmake)
1117 
1118 
1119 def reconstructMissingKlongDecayExpert(decayString,
1120  cut,
1121  dmID=0,
1122  writeOut=False,
1123  path=None,
1124  recoList="_reco"):
1125  """
1126  Creates a list of K_L0's with their momentum determined from kinematic constraints of B->K_L0 + something else.
1127 
1128  @param decayString DecayString specifying what kind of the decay should be reconstructed
1129  (from the DecayString the mother and daughter ParticleLists are determined)
1130  @param cut Particles are added to the K_L0 ParticleList if they
1131  pass the given cuts (in VariableManager style) and rejected otherwise
1132  @param dmID user specified decay mode identifier
1133  @param writeOut whether RootOutput module should save the created ParticleList
1134  @param path modules are added to this path
1135  @param recoList suffix appended to original K_L0 ParticleList that identifies the newly created K_L0 list
1136  """
1137 
1138  pcalc = register_module('KlongMomentumCalculatorExpert')
1139  pcalc.set_name('KlongMomentumCalculatorExpert_' + decayString)
1140  pcalc.param('decayString', decayString)
1141  pcalc.param('cut', cut)
1142  pcalc.param('decayMode', dmID)
1143  pcalc.param('writeOut', writeOut)
1144  pcalc.param('recoList', recoList)
1145  path.add_module(pcalc)
1146 
1147  rmake = register_module('KlongDecayReconstructorExpert')
1148  rmake.set_name('KlongDecayReconstructorExpert_' + decayString)
1149  rmake.param('decayString', decayString)
1150  rmake.param('cut', cut)
1151  rmake.param('decayMode', dmID)
1152  rmake.param('writeOut', writeOut)
1153  rmake.param('recoList', recoList)
1154  path.add_module(rmake)
1155 
1156 
1157 def replaceMass(replacerName, particleLists=[], pdgCode=22, path=None):
1158  """
1159  replaces the mass of the particles inside the given particleLists
1160  with the invariant mass of the particle corresponding to the given pdgCode.
1161 
1162  @param particleLists new ParticleList filled with copied Particles
1163  @param pdgCode PDG code for mass reference
1164  @param path modules are added to this path
1165  """
1166 
1167  # first copy original particles to the new ParticleList
1168  pmassupdater = register_module('ParticleMassUpdater')
1169  pmassupdater.set_name('ParticleMassUpdater_' + replacerName)
1170  pmassupdater.param('particleLists', particleLists)
1171  pmassupdater.param('pdgCode', pdgCode)
1172  path.add_module(pmassupdater)
1173 
1174 
1175 def reconstructRecoil(decayString,
1176  cut,
1177  dmID=0,
1178  writeOut=False,
1179  path=None,
1180  candidate_limit=None,
1181  allowChargeViolation=False):
1182  """
1183  Creates new Particles that recoil against the input particles.
1184 
1185  For example the decay string M -> D1 D2 D3 will:
1186  - create mother Particle M for each unique combination of D1, D2, D3 Particles
1187  - Particles D1, D2, D3 will be appended as daughters to M
1188  - the 4-momentum of the mother Particle M is given by
1189  p(M) = p(HER) + p(LER) - Sum_i p(Di)
1190 
1191  @param decayString DecayString specifying what kind of the decay should be reconstructed
1192  (from the DecayString the mother and daughter ParticleLists are determined)
1193  @param cut created (mother) Particles are added to the mother ParticleList if they
1194  pass give cuts (in VariableManager style) and rejected otherwise
1195  @param dmID user specified decay mode identifier
1196  @param writeOut whether RootOutput module should save the created ParticleList
1197  @param path modules are added to this path
1198  @param candidate_limit Maximum amount of candidates to be reconstructed. If
1199  the number of candidates is exceeded no candidate will be
1200  reconstructed for that event and a Warning will be
1201  printed.
1202  If no value is given the amount is limited to a sensible
1203  default. A value <=0 will disable this limit and can
1204  cause huge memory amounts so be careful.
1205  @param allowChargeViolation whether the decay string needs to conserve the electric charge
1206  """
1207 
1208  pmake = register_module('ParticleCombiner')
1209  pmake.set_name('ParticleCombiner_' + decayString)
1210  pmake.param('decayString', decayString)
1211  pmake.param('cut', cut)
1212  pmake.param('decayMode', dmID)
1213  pmake.param('writeOut', writeOut)
1214  pmake.param('recoilParticleType', 1)
1215  if candidate_limit is not None:
1216  pmake.param("maximumNumberOfCandidates", candidate_limit)
1217  pmake.param('allowChargeViolation', allowChargeViolation)
1218  path.add_module(pmake)
1219 
1220 
1221 def reconstructRecoilDaughter(decayString,
1222  cut,
1223  dmID=0,
1224  writeOut=False,
1225  path=None,
1226  candidate_limit=None,
1227  allowChargeViolation=False):
1228  """
1229  Creates new Particles that are daughters of the particle reconstructed in the recoil (always assumed to be the first daughter).
1230 
1231  For example the decay string M -> D1 D2 D3 will:
1232  - create mother Particle M for each unique combination of D1, D2, D3 Particles
1233  - Particles D1, D2, D3 will be appended as daughters to M
1234  - the 4-momentum of the mother Particle M is given by
1235  p(M) = p(D1) - Sum_i p(Di), where i>1
1236 
1237  @param decayString DecayString specifying what kind of the decay should be reconstructed
1238  (from the DecayString the mother and daughter ParticleLists are determined)
1239  @param cut created (mother) Particles are added to the mother ParticleList if they
1240  pass give cuts (in VariableManager style) and rejected otherwise
1241  @param dmID user specified decay mode identifier
1242  @param writeOut whether RootOutput module should save the created ParticleList
1243  @param path modules are added to this path
1244  @param candidate_limit Maximum amount of candidates to be reconstructed. If
1245  the number of candidates is exceeded no candidate will be
1246  reconstructed for that event and a Warning will be
1247  printed.
1248  If no value is given the amount is limited to a sensible
1249  default. A value <=0 will disable this limit and can
1250  cause huge memory amounts so be careful.
1251  @param allowChargeViolation whether the decay string needs to conserve the electric charge taking into account that the first
1252  daughter is actually the mother
1253  """
1254 
1255  pmake = register_module('ParticleCombiner')
1256  pmake.set_name('ParticleCombiner_' + decayString)
1257  pmake.param('decayString', decayString)
1258  pmake.param('cut', cut)
1259  pmake.param('decayMode', dmID)
1260  pmake.param('writeOut', writeOut)
1261  pmake.param('recoilParticleType', 2)
1262  if candidate_limit is not None:
1263  pmake.param("maximumNumberOfCandidates", candidate_limit)
1264  pmake.param('allowChargeViolation', allowChargeViolation)
1265  path.add_module(pmake)
1266 
1267 
1268 def rankByHighest(particleList,
1269  variable,
1270  numBest=0,
1271  outputVariable='',
1272  allowMultiRank=False,
1273  cut='',
1274  path=None):
1275  """
1276  Ranks particles in the input list by the given variable (highest to lowest), and stores an integer rank for each Particle
1277  in an :b2:var:`extraInfo` field ``${variable}_rank`` starting at 1 (best).
1278  The list is also sorted from best to worst candidate
1279  (each charge, e.g. B+/B-, separately).
1280  This can be used to perform a best candidate selection by cutting on the corresponding rank value, or by specifying
1281  a non-zero value for 'numBest'.
1282 
1283  .. tip::
1284  Extra-info fields can be accessed by the :b2:var:`extraInfo` metavariable.
1285  These variable names can become clunky, so it's probably a good idea to set an alias.
1286  For example if you rank your B candidates by momentum,
1287 
1288  .. code:: python
1289 
1290  rankByHighest("B0:myCandidates", "p", path=mypath)
1291  vm.addAlias("momentumRank", "extraInfo(p_rank)")
1292 
1293 
1294  @param particleList The input ParticleList
1295  @param variable Variable to order Particles by.
1296  @param numBest If not zero, only the $numBest Particles in particleList with rank <= numBest are kept.
1297  @param outputVariable Name for the variable that will be created which contains the rank, Default is '${variable}_rank'.
1298  @param allowMultiRank If true, candidates with the same value will get the same rank.
1299  @param cut Only candidates passing the cut will be ranked. The others will have rank -1
1300  @param path modules are added to this path
1301  """
1302 
1303  bcs = register_module('BestCandidateSelection')
1304  bcs.set_name('BestCandidateSelection_' + particleList + '_' + variable)
1305  bcs.param('particleList', particleList)
1306  bcs.param('variable', variable)
1307  bcs.param('numBest', numBest)
1308  bcs.param('outputVariable', outputVariable)
1309  bcs.param('allowMultiRank', allowMultiRank)
1310  bcs.param('cut', cut)
1311  path.add_module(bcs)
1312 
1313 
1314 def rankByLowest(particleList,
1315  variable,
1316  numBest=0,
1317  outputVariable='',
1318  allowMultiRank=False,
1319  cut='',
1320  path=None):
1321  """
1322  Ranks particles in the input list by the given variable (lowest to highest), and stores an integer rank for each Particle
1323  in an :b2:var:`extraInfo` field ``${variable}_rank`` starting at 1 (best).
1324  The list is also sorted from best to worst candidate
1325  (each charge, e.g. B+/B-, separately).
1326  This can be used to perform a best candidate selection by cutting on the corresponding rank value, or by specifying
1327  a non-zero value for 'numBest'.
1328 
1329  .. tip::
1330  Extra-info fields can be accessed by the :b2:var:`extraInfo` metavariable.
1331  These variable names can become clunky, so it's probably a good idea to set an alias.
1332  For example if you rank your B candidates by :b2:var:`dM`,
1333 
1334  .. code:: python
1335 
1336  rankByLowest("B0:myCandidates", "dM", path=mypath)
1337  vm.addAlias("massDifferenceRank", "extraInfo(dM_rank)")
1338 
1339 
1340  @param particleList The input ParticleList
1341  @param variable Variable to order Particles by.
1342  @param numBest If not zero, only the $numBest Particles in particleList with rank <= numBest are kept.
1343  @param outputVariable Name for the variable that will be created which contains the rank, Default is '${variable}_rank'.
1344  @param allowMultiRank If true, candidates with the same value will get the same rank.
1345  @param cut Only candidates passing the cut will be ranked. The others will have rank -1
1346  @param path modules are added to this path
1347  """
1348 
1349  bcs = register_module('BestCandidateSelection')
1350  bcs.set_name('BestCandidateSelection_' + particleList + '_' + variable)
1351  bcs.param('particleList', particleList)
1352  bcs.param('variable', variable)
1353  bcs.param('numBest', numBest)
1354  bcs.param('selectLowest', True)
1355  bcs.param('allowMultiRank', allowMultiRank)
1356  bcs.param('outputVariable', outputVariable)
1357  bcs.param('cut', cut)
1358  path.add_module(bcs)
1359 
1360 
1361 def applyRandomCandidateSelection(particleList, path=None):
1362  """
1363  If there are multiple candidates in the provided particleList, all but one of them are removed randomly.
1364  This is done on a event-by-event basis.
1365 
1366  @param particleList ParticleList for which the random candidate selection should be applied
1367  @param path module is added to this path
1368  """
1369 
1370  rcs = register_module('BestCandidateSelection')
1371  rcs.set_name('RandomCandidateSelection_' + particleList)
1372  rcs.param('particleList', particleList)
1373  rcs.param('variable', 'random')
1374  rcs.param('selectLowest', False)
1375  rcs.param('allowMultiRank', False)
1376  rcs.param('numBest', 1)
1377  rcs.param('cut', '')
1378  rcs.param('outputVariable', '')
1379  path.add_module(rcs)
1380 
1381 
1382 def printDataStore(eventNumber=-1, path=None):
1383  """
1384  Prints the contents of DataStore in the first event (or a specific event number or all events).
1385  Will list all objects and arrays (including size).
1386 
1387  See also:
1388  The command line tool: ``b2file-size``.
1389 
1390  Parameters:
1391  eventNumber (int): Print the datastore only for this event. The default
1392  (-1) prints only the first event, 0 means print for all events (can produce large output)
1393  path (basf2.Path): the PrintCollections module is added to this path
1394 
1395  Warning:
1396  This will print a lot of output if you print it for all events and process many events.
1397 
1398  """
1399 
1400  printDS = register_module('PrintCollections')
1401  printDS.param('printForEvent', eventNumber)
1402  path.add_module(printDS)
1403 
1404 
1405 def printVariableValues(list_name, var_names, path):
1406  """
1407  Prints out values of specified variables of all Particles included in given ParticleList. For debugging purposes.
1408 
1409  @param list_name input ParticleList name
1410  @param var_names vector of variable names to be printed
1411  @param path modules are added to this path
1412  """
1413 
1414  prlist = register_module('ParticlePrinter')
1415  prlist.set_name('ParticlePrinter_' + list_name)
1416  prlist.param('listName', list_name)
1417  prlist.param('fullPrint', False)
1418  prlist.param('variables', var_names)
1419  path.add_module(prlist)
1420 
1421 
1422 def printList(list_name, full, path):
1423  """
1424  Prints the size and executes Particle->print() (if full=True)
1425  method for all Particles in given ParticleList. For debugging purposes.
1426 
1427  @param list_name input ParticleList name
1428  @param full execute Particle->print() method for all Particles
1429  @param path modules are added to this path
1430  """
1431 
1432  prlist = register_module('ParticlePrinter')
1433  prlist.set_name('ParticlePrinter_' + list_name)
1434  prlist.param('listName', list_name)
1435  prlist.param('fullPrint', full)
1436  path.add_module(prlist)
1437 
1438 
1439 def variablesToNtuple(decayString, variables, treename='variables', filename='ntuple.root', path=None):
1440  """
1441  Creates and fills a flat ntuple with the specified variables from the VariableManager.
1442  If a decayString is provided, then there will be one entry per candidate (for particle in list of candidates).
1443  If an empty decayString is provided, there will be one entry per event (useful for trigger studies, etc).
1444 
1445  Parameters:
1446  decayString (str): specifies type of Particles and determines the name of the ParticleList
1447  variables (list(str)): the list of variables (which must be registered in the VariableManager)
1448  treename (str): name of the ntuple tree
1449  filename (str): which is used to store the variables
1450  path (basf2.Path): the basf2 path where the analysis is processed
1451  """
1452 
1453  output = register_module('VariablesToNtuple')
1454  output.set_name('VariablesToNtuple_' + decayString)
1455  output.param('particleList', decayString)
1456  output.param('variables', variables)
1457  output.param('fileName', filename)
1458  output.param('treeName', treename)
1459  path.add_module(output)
1460 
1461 
1462 def variablesToHistogram(decayString,
1463  variables,
1464  variables_2d=[],
1465  filename='ntuple.root',
1466  path=None, *,
1467  directory=None,
1468  prefixDecayString=False):
1469  """
1470  Creates and fills a flat ntuple with the specified variables from the VariableManager
1471 
1472  Parameters:
1473  decayString (str): specifies type of Particles and determines the name of the ParticleList
1474  variables (list(tuple))): variables + binning which must be registered in the VariableManager
1475  variables_2d (list(tuple)): pair of variables + binning for each which must be registered in the VariableManager
1476  filename (str): which is used to store the variables
1477  path (basf2.Path): the basf2 path where the analysis is processed
1478  directory (str): directory inside the output file where the histograms should be saved.
1479  Useful if you want to have different histograms in the same file to separate them.
1480  prefixDecayString (bool): If True the decayString will be prepended to the directory name to allow for more
1481  programmatic naming of the structure in the file.
1482  """
1483 
1484  output = register_module('VariablesToHistogram')
1485  output.set_name('VariablesToHistogram_' + decayString)
1486  output.param('particleList', decayString)
1487  output.param('variables', variables)
1488  output.param('variables_2d', variables_2d)
1489  output.param('fileName', filename)
1490  if directory is not None or prefixDecayString:
1491  if directory is None:
1492  directory = ""
1493  if prefixDecayString:
1494  directory = decayString + "_" + directory
1495  output.param("directory", directory)
1496  path.add_module(output)
1497 
1498 
1499 def variablesToExtraInfo(particleList, variables, option=0, path=None):
1500  """
1501  For each particle in the input list the selected variables are saved in an extra-info field with the given name.
1502  Can be used when wanting to save variables before modifying them, e.g. when performing vertex fits.
1503 
1504  An existing extra info with the same name will be overwritten if the new
1505  value is lower / will never be overwritten / will be overwritten if the
1506  new value is higher / will always be overwritten (-1/0/1/2).
1507 
1508  @param particleList The input ParticleList
1509  @param variables Dictionary of Variables and extraInfo names.
1510  @param path modules are added to this path
1511  """
1512 
1513  mod = register_module('VariablesToExtraInfo')
1514  mod.set_name('VariablesToExtraInfo_' + particleList)
1515  mod.param('particleList', particleList)
1516  mod.param('variables', variables)
1517  mod.param('overwrite', option)
1518  path.add_module(mod)
1519 
1520 
1521 def variablesToDaughterExtraInfo(particleList, decayString, variables, option=0, path=None):
1522  """
1523  For each daughter particle specified via decay string the selected variables (estimated for the mother particle)
1524  are saved in an extra-info field with the given name. In other words, the property of mother is saved as extra-info
1525  to specified daughter particle.
1526 
1527  An existing extra info with the same name will be overwritten if the new
1528  value is lower / will never be overwritten / will be overwritten if the
1529  new value is higher / will always be overwritten (-1/0/1/2).
1530 
1531  @param particleList The input ParticleList
1532  @param decayString Decay string that specifies to which daughter the extra info should be appended
1533  @param variables Dictionary of Variables and extraInfo names.
1534  @param option Various options for overwriting
1535  @param path modules are added to this path
1536  """
1537 
1538  mod = register_module('VariablesToExtraInfo')
1539  mod.set_name('VariablesToDaughterExtraInfo_' + particleList)
1540  mod.param('particleList', particleList)
1541  mod.param('decayString', decayString)
1542  mod.param('variables', variables)
1543  mod.param('overwrite', option)
1544  path.add_module(mod)
1545 
1546 
1547 def variablesToEventExtraInfo(particleList, variables, option=0, path=None):
1548  """
1549  For each particle in the input list the selected variables are saved in an event-extra-info field with the given name,
1550  Can be used to save MC truth information, for example, in a ntuple of reconstructed particles.
1551 
1552  An existing extra info with the same name will be overwritten if the new
1553  value is lower / will never be overwritten / will be overwritten if the
1554  new value is higher / will always be overwritten (-1/0/1/2).
1555 
1556  @param particleList The input ParticleList
1557  @param variables Dictionary of Variables and extraInfo names.
1558  @param path modules are added to this path
1559  """
1560 
1561  mod = register_module('VariablesToEventExtraInfo')
1562  mod.set_name('VariablesToEventExtraInfo_' + particleList)
1563  mod.param('particleList', particleList)
1564  mod.param('variables', variables)
1565  mod.param('overwrite', option)
1566  path.add_module(mod)
1567 
1568 
1569 def variableToSignalSideExtraInfo(particleList, varToExtraInfo, path):
1570  """
1571  Write the value of specified variables estimated for the single particle in the input list (has to contain exactly 1
1572  particle) as an extra info to the particle related to current ROE.
1573  Should be used only in the for_each roe path.
1574 
1575  @param particleList The input ParticleList
1576  @param varToExtraInfo Dictionary of Variables and extraInfo names.
1577  @param path modules are added to this path
1578  """
1579  mod = register_module('SignalSideVariablesToExtraInfo')
1580  mod.set_name('SigSideVarToExtraInfo_' + particleList)
1581  mod.param('particleListName', particleList)
1582  mod.param('variableToExtraInfo', varToExtraInfo)
1583  path.add_module(mod)
1584 
1585 
1586 def signalRegion(particleList, cut, path=None, name="isSignalRegion", blind_data=True):
1587  """
1588  Define and blind a signal region.
1589  Per default, the defined signal region is cut out if ran on data.
1590  This function will provide a new variable 'isSignalRegion' as default, which is either 0 or 1 depending on the cut
1591  provided.
1592 
1593  Example:
1594  >>> ma.reconstructDecay("B+:sig -> D+ pi0", "Mbc>5.2", path=path)
1595  >>> ma.signalRegion("B+:sig",
1596  >>> "Mbc>5.27 and abs(deltaE)<0.2",
1597  >>> blind_data=True,
1598  >>> path=path)
1599  >>> ma.variablesToNtuples("B+:sig", ["isSignalRegion"], path=path)
1600 
1601  Parameters:
1602  particleList (str): The input ParticleList
1603  cut (str): Cut string describing the signal region
1604  path (basf2.Path):: Modules are added to this path
1605  name (str): Name of the Signal region in the variable manager
1606  blind_data (bool): Automatically exclude signal region from data
1607 
1608  """
1609 
1610  from variables import variables
1611  mod = register_module('VariablesToExtraInfo')
1612  mod.set_name(f'{name}_' + particleList)
1613  mod.param('particleList', particleList)
1614  mod.param('variables', {f"passesCut({cut})": name})
1615  variables.addAlias(name, f"extraInfo({name})")
1616  path.add_module(mod)
1617 
1618  # Check if we run on Data
1619  if blind_data:
1620  applyCuts(particleList, f"{name}==0 or isMC==1", path=path)
1621 
1622 
1623 def removeExtraInfo(particleLists=[], removeEventExtraInfo=False, path=None):
1624  """
1625  Removes the ExtraInfo of the given particleLists. If specified (removeEventExtraInfo = True) also the EventExtraInfo is removed.
1626  """
1627 
1628  mod = register_module('ExtraInfoRemover')
1629  mod.param('particleLists', particleLists)
1630  mod.param('removeEventExtraInfo', removeEventExtraInfo)
1631  path.add_module(mod)
1632 
1633 
1634 def signalSideParticleFilter(particleList, selection, roe_path, deadEndPath):
1635  """
1636  Checks if the current ROE object in the for_each roe path (argument roe_path) is related
1637  to the particle from the input ParticleList. Additional selection criteria can be applied.
1638  If ROE is not related to any of the Particles from ParticleList or the Particle doesn't
1639  meet the selection criteria the execution of deadEndPath is started. This path, as the name
1640  suggests should be empty and its purpose is to end the execution of for_each roe path for
1641  the current ROE object.
1642 
1643  @param particleList The input ParticleList
1644  @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
1645  @param for_each roe path in which this filter is executed
1646  @param deadEndPath empty path that ends execution of or_each roe path for the current ROE object.
1647  """
1648  mod = register_module('SignalSideParticleFilter')
1649  mod.set_name('SigSideParticleFilter_' + particleList)
1650  mod.param('particleLists', [particleList])
1651  mod.param('selection', selection)
1652  roe_path.add_module(mod)
1653  mod.if_false(deadEndPath)
1654 
1655 
1656 def signalSideParticleListsFilter(particleLists, selection, roe_path, deadEndPath):
1657  """
1658  Checks if the current ROE object in the for_each roe path (argument roe_path) is related
1659  to the particle from the input ParticleList. Additional selection criteria can be applied.
1660  If ROE is not related to any of the Particles from ParticleList or the Particle doesn't
1661  meet the selection criteria the execution of deadEndPath is started. This path, as the name
1662  suggests should be empty and its purpose is to end the execution of for_each roe path for
1663  the current ROE object.
1664 
1665  @param particleLists The input ParticleLists
1666  @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
1667  @param for_each roe path in which this filter is executed
1668  @param deadEndPath empty path that ends execution of or_each roe path for the current ROE object.
1669  """
1670  mod = register_module('SignalSideParticleFilter')
1671  mod.set_name('SigSideParticleFilter_' + particleLists[0])
1672  mod.param('particleLists', particleLists)
1673  mod.param('selection', selection)
1674  roe_path.add_module(mod)
1675  mod.if_false(deadEndPath)
1676 
1677 
1678 def reconstructMCDecay(
1679  decayString,
1680  cut,
1681  dmID=0,
1682  writeOut=False,
1683  path=None,
1684  chargeConjugation=True,
1685 ):
1686  r"""
1687  Finds and creates a ``ParticleList`` from given decay string.
1688  ``ParticleList`` of daughters with sub-decay is created.
1689 
1690  Only signal particle, which means :b2:var:`isSignal` is equal to 1, is stored. One can use the decay string grammar
1691  to change the behavior of :b2:var:`isSignal`. One can find detailed information in :ref:`DecayString`.
1692 
1693  .. tip::
1694  If one uses same sub-decay twice, same particles are registered to a ``ParticleList``. For example,
1695  ``K_S0:pi0pi0 =direct=> [pi0:gg =direct=> gamma:MC gamma:MC] [pi0:gg =direct=> gamma:MC gamma:MC]``.
1696  One can skip the second sub-decay, ``K_S0:pi0pi0 =direct=> [pi0:gg =direct=> gamma:MC gamma:MC] pi0:gg``.
1697 
1698 
1699  @param decayString :ref:`DecayString` specifying what kind of the decay should be reconstructed
1700  (from the DecayString the mother and daughter ParticleLists are determined)
1701  @param cut created (mother) Particles are added to the mother ParticleList if they
1702  pass given cuts (in VariableManager style) and rejected otherwise
1703  isSignal==1 is always required by default.
1704  @param dmID user specified decay mode identifier
1705  @param writeOut whether RootOutput module should save the created ParticleList
1706  @param path modules are added to this path
1707  @param chargeConjugation boolean to decide whether charge conjugated mode should be reconstructed as well (on by default)
1708  """
1709 
1710  pmake = register_module('ParticleCombinerFromMC')
1711  pmake.set_name('ParticleCombinerFromMC_' + decayString)
1712  pmake.param('decayString', decayString)
1713  pmake.param('cut', cut)
1714  pmake.param('decayMode', dmID)
1715  pmake.param('writeOut', writeOut)
1716  pmake.param('chargeConjugation', chargeConjugation)
1717  path.add_module(pmake)
1718 
1719 
1720 def findMCDecay(
1721  list_name,
1722  decay,
1723  writeOut=False,
1724  path=None,
1725 ):
1726  """
1727  .. warning::
1728  This function is not fully tested and maintained.
1729  Please consider to use reconstructMCDecay() instead.
1730 
1731  Finds and creates a ``ParticleList`` for all ``MCParticle`` decays matching a given :ref:`DecayString`.
1732  The decay string is required to describe correctly what you want.
1733  In the case of inclusive decays, you can use :ref:`Grammar_for_custom_MCMatching`
1734 
1735  @param list_name The output particle list name
1736  @param decay The decay string which you want
1737  @param writeOut Whether `RootOutput` module should save the created ``outputList``
1738  @param path modules are added to this path
1739  """
1740  B2WARNING("This function is not fully tested and maintained."
1741  "Please consider to use reconstructMCDecay() instead.")
1742 
1743  decayfinder = register_module('MCDecayFinder')
1744  decayfinder.set_name('MCDecayFinder_' + list_name)
1745  decayfinder.param('listName', list_name)
1746  decayfinder.param('decayString', decay)
1747  decayfinder.param('writeOut', writeOut)
1748  path.add_module(decayfinder)
1749 
1750 
1751 def summaryOfLists(particleLists, path):
1752  """
1753  Prints out Particle statistics at the end of the job: number of events with at
1754  least one candidate, average number of candidates per event, etc.
1755 
1756  @param particleLists list of input ParticleLists
1757  """
1758 
1759  particleStats = register_module('ParticleStats')
1760  particleStats.param('particleLists', particleLists)
1761  path.add_module(particleStats)
1762 
1763 
1764 def matchMCTruth(list_name, path):
1765  """
1766  Performs MC matching (sets relation Particle->MCParticle) for
1767  all particles (and its (grand)^N-daughter particles) in the specified
1768  ParticleList.
1769 
1770  @param list_name name of the input ParticleList
1771  @param path modules are added to this path
1772  """
1773 
1774  mcMatch = register_module('MCMatcherParticles')
1775  mcMatch.set_name('MCMatch_' + list_name)
1776  mcMatch.param('listName', list_name)
1777  path.add_module(mcMatch)
1778 
1779 
1780 def looseMCTruth(list_name, path):
1781  """
1782  Performs loose MC matching for all particles in the specified
1783  ParticleList.
1784  The difference between loose and normal mc matching algorithm is that
1785  the loose algorithm will find the common mother of the majority of daughter
1786  particles while the normal algorithm finds the common mother of all daughters.
1787  The results of loose mc matching algorithm are stored to the following extraInfo
1788  items:
1789 
1790  - looseMCMotherPDG: PDG code of most common mother
1791  - looseMCMotherIndex: 1-based StoreArray<MCParticle> index of most common mother
1792  - looseMCWrongDaughterN: number of daughters that don't originate from the most
1793  common mother
1794  - looseMCWrongDaughterPDG: PDG code of the daughter that doesn't originate from
1795  the most common mother
1796  (only if looseMCWrongDaughterN = 1)
1797  - looseMCWrongDaughterBiB: 1 if the wrong daughter is Beam Induced Background
1798  Particle
1799 
1800  @param list_name name of the input ParticleList
1801  @param path modules are added to this path
1802  """
1803 
1804  mcMatch = register_module('MCMatcherParticles')
1805  mcMatch.set_name('LooseMCMatch_' + list_name)
1806  mcMatch.param('listName', list_name)
1807  mcMatch.param('looseMCMatching', True)
1808  path.add_module(mcMatch)
1809 
1810 
1811 def buildRestOfEvent(target_list_name, inputParticlelists=None,
1812  fillWithMostLikely=False,
1813  chargedPIDPriors=None, path=None):
1814  """
1815  Creates for each Particle in the given ParticleList a RestOfEvent
1816  dataobject and makes BASF2 relation between them. User can provide additional
1817  particle lists with a different particle hypotheses like ['K+:good, e+:good'], etc.
1818 
1819  @param target_list_name name of the input ParticleList
1820  @param inputParticlelists list of input particle list names, which serve
1821  as a source of particles to build ROE, the FSP particles from
1822  target_list_name are excluded from ROE object
1823  @param fillWithMostLikely if True, the module uses particle mass hypothesis for charged particles
1824  according to PID likelihood and the inputParticlelists
1825  option will be ignored.
1826  @param chargedPIDPriors The prior PID fractions, that are used to regulate
1827  amount of certain charged particle species, should be a list of
1828  six floats if not None. The order of particle types is
1829  the following: [e-, mu-, pi-, K-, p+, d+]
1830  @param path modules are added to this path
1831  """
1832  if inputParticlelists is None:
1833  inputParticlelists = []
1834  fillParticleList('pi+:roe_default', '', path=path)
1835  if fillWithMostLikely:
1836  from stdCharged import stdMostLikely
1837  stdMostLikely(chargedPIDPriors, '_roe', path=path)
1838  inputParticlelists = ['%s:mostlikely_roe' % ptype for ptype in ['K+', 'p+', 'e+', 'mu+']]
1839  import b2bii
1840  if not b2bii.isB2BII():
1841  fillParticleList('gamma:roe_default', '', path=path)
1842  fillParticleList('K_L0:roe_default', 'isFromKLM > 0', path=path)
1843  inputParticlelists += ['pi+:roe_default', 'gamma:roe_default', 'K_L0:roe_default']
1844  else:
1845  inputParticlelists += ['pi+:roe_default', 'gamma:mdst']
1846  roeBuilder = register_module('RestOfEventBuilder')
1847  roeBuilder.set_name('ROEBuilder_' + target_list_name)
1848  roeBuilder.param('particleList', target_list_name)
1849  roeBuilder.param('particleListsInput', inputParticlelists)
1850  path.add_module(roeBuilder)
1851 
1852 
1853 def buildNestedRestOfEvent(target_list_name, maskName='', path=None):
1854  """
1855  Creates for each Particle in the given ParticleList a RestOfEvent
1856  @param target_list_name name of the input ParticleList
1857  @param mask_name name of the ROEMask to be used
1858  @param path modules are added to this path
1859  """
1860  roeBuilder = register_module('RestOfEventBuilder')
1861  roeBuilder.set_name('NestedROEBuilder_' + target_list_name)
1862  roeBuilder.param('particleList', target_list_name)
1863  roeBuilder.param('nestedROEMask', maskName)
1864  roeBuilder.param('createNestedROE', True)
1865  path.add_module(roeBuilder)
1866 
1867 
1868 def buildRestOfEventFromMC(target_list_name, inputParticlelists=[], path=None):
1869  """
1870  Creates for each Particle in the given ParticleList a RestOfEvent
1871  @param target_list_name name of the input ParticleList
1872  @param inputParticlelists list of input particle list names, which serve
1873  as a source of particles to build ROE, the FSP particles from
1874  target_list_name are excluded from ROE object
1875  @param path modules are added to this path
1876  """
1877  if (len(inputParticlelists) == 0):
1878  # Type of particles to use for ROEBuilder
1879  # K_S0 and Lambda0 are added here because some of them have interacted
1880  # with the detector material
1881  types = ['gamma', 'e+', 'mu+', 'pi+', 'K+', 'p+', 'K_L0',
1882  'n0', 'nu_e', 'nu_mu', 'nu_tau',
1883  'K_S0', 'Lambda0']
1884  for t in types:
1885  fillParticleListFromMC("%s:roe_default_gen" % t, 'mcPrimary > 0 and nDaughters == 0',
1886  True, True, path=path)
1887  inputParticlelists += ["%s:roe_default_gen" % t]
1888  roeBuilder = register_module('RestOfEventBuilder')
1889  roeBuilder.set_name('MCROEBuilder_' + target_list_name)
1890  roeBuilder.param('particleList', target_list_name)
1891  roeBuilder.param('particleListsInput', inputParticlelists)
1892  roeBuilder.param('fromMC', True)
1893  path.add_module(roeBuilder)
1894 
1895 
1896 def appendROEMask(list_name,
1897  mask_name,
1898  trackSelection,
1899  eclClusterSelection,
1900  path=None):
1901  """
1902  Loads the ROE object of a particle and creates a ROE mask with a specific name. It applies
1903  selection criteria for tracks and eclClusters which will be used by variables in ROEVariables.cc.
1904 
1905  - append a ROE mask with all tracks in ROE coming from the IP region
1906 
1907  >>> appendROEMask('B+:sig', 'IPtracks', 'abs(d0) < 0.05 and abs(z0) < 0.1', '')
1908 
1909  - append a ROE mask with only ECLClusters that pass as good photon candidates
1910 
1911  >>> good_photons = 'theta > 0.296706 and theta < 2.61799 and clusterErrorTiming < 1e6 and [clusterE1E9 > 0.4 or E > 0.075]'
1912  >>> appendROEMask('B+:sig', 'goodROEGamma', '', good_photons)
1913 
1914 
1915  @param list_name name of the input ParticleList
1916  @param mask_name name of the appended ROEMask
1917  @param trackSelection decay string for the tracks in ROE
1918  @param eclClusterSelection decay string for the tracks in ROE
1919  @param path modules are added to this path
1920  """
1921 
1922  roeMask = register_module('RestOfEventInterpreter')
1923  roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + mask_name)
1924  roeMask.param('particleList', list_name)
1925  roeMask.param('ROEMasks', [(mask_name, trackSelection, eclClusterSelection)])
1926  path.add_module(roeMask)
1927 
1928 
1929 def appendROEMasks(list_name, mask_tuples, path=None):
1930  """
1931  Loads the ROE object of a particle and creates a ROE mask with a specific name. It applies
1932  selection criteria for tracks and eclClusters which will be used by variables in ROEVariables.cc.
1933 
1934  The multiple ROE masks with their own selection criteria are specified
1935  via list of tuples (mask_name, trackSelection, eclClusterSelection) or
1936  (mask_name, trackSelection, eclClusterSelection) in case with fractions.
1937 
1938  - Example for two tuples, one with and one without fractions
1939 
1940  >>> ipTracks = ('IPtracks', 'abs(d0) < 0.05 and abs(z0) < 0.1', '')
1941  >>> good_photons = 'theta > 0.296706 and theta < 2.61799 and clusterErrorTiming < 1e6 and [clusterE1E9 > 0.4 or E > 0.075]'
1942  >>> goodROEGamma = ('ROESel', 'abs(d0) < 0.05 and abs(z0) < 0.1', good_photons)
1943  >>> appendROEMasks('B+:sig', [ipTracks, goodROEGamma])
1944 
1945  @param list_name name of the input ParticleList
1946  @param mask_tuples array of ROEMask list tuples to be appended
1947  @param path modules are added to this path
1948  """
1949 
1950  roeMask = register_module('RestOfEventInterpreter')
1951  roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + 'MaskList')
1952  roeMask.param('particleList', list_name)
1953  roeMask.param('ROEMasks', mask_tuples)
1954  path.add_module(roeMask)
1955 
1956 
1957 def updateROEMask(list_name,
1958  mask_name,
1959  trackSelection,
1960  eclClusterSelection='',
1961  path=None):
1962  """
1963  Update an existing ROE mask by applying additional selection cuts for
1964  tracks and/or clusters.
1965 
1966  See function `appendROEMask`!
1967 
1968  @param list_name name of the input ParticleList
1969  @param mask_name name of the ROEMask to update
1970  @param trackSelection decay string for the tracks in ROE
1971  @param eclClusterSelection decay string for the tracks in ROE
1972  @param path modules are added to this path
1973  """
1974 
1975  roeMask = register_module('RestOfEventInterpreter')
1976  roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + mask_name)
1977  roeMask.param('particleList', list_name)
1978  roeMask.param('ROEMasks', [(mask_name, trackSelection, eclClusterSelection)])
1979  roeMask.param('update', True)
1980  path.add_module(roeMask)
1981 
1982 
1983 def updateROEMasks(list_name, mask_tuples, path):
1984  """
1985  Update existing ROE masks by applying additional selection cuts for tracks
1986  and/or clusters.
1987 
1988  The multiple ROE masks with their own selection criteria are specified
1989  via list tuples (mask_name, trackSelection, eclClusterSelection)
1990 
1991  See function `appendROEMasks`!
1992 
1993  @param list_name name of the input ParticleList
1994  @param mask_tuples array of ROEMask list tuples to be appended
1995  @param path modules are added to this path
1996  """
1997 
1998  roeMask = register_module('RestOfEventInterpreter')
1999  roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + 'MaskList')
2000  roeMask.param('particleList', list_name)
2001  roeMask.param('ROEMasks', mask_tuples)
2002  roeMask.param('update', True)
2003  path.add_module(roeMask)
2004 
2005 
2006 def keepInROEMasks(list_name, mask_names, cut_string, path=None):
2007  """
2008  This function is used to apply particle list specific cuts on one or more ROE masks (track or eclCluster).
2009  With this function one can KEEP the tracks/eclclusters used in particles from provided particle list.
2010  This function should be executed only in the for_each roe path for the current ROE object.
2011 
2012  To avoid unnecessary computation, the input particle list should only contain particles from ROE
2013  (use cut 'isInRestOfEvent == 1'). To update the ECLCluster masks, the input particle list should be a photon
2014  particle list (e.g. 'gamma:someLabel'). To update the Track masks, the input particle list should be a charged
2015  pion particle list (e.g. 'pi+:someLabel').
2016 
2017  Updating a non-existing mask will create a new one.
2018 
2019  - keep only those tracks that were used in provided particle list
2020 
2021  >>> keepInROEMasks('pi+:goodTracks', 'mask', '')
2022 
2023  - keep only those clusters that were used in provided particle list and pass a cut, apply to several masks
2024 
2025  >>> keepInROEMasks('gamma:goodClusters', ['mask1', 'mask2'], 'E > 0.1')
2026 
2027 
2028  @param list_name name of the input ParticleList
2029  @param mask_names array of ROEMasks to be updated
2030  @param cut_string decay string with which the mask will be updated
2031  @param path modules are added to this path
2032  """
2033 
2034  updateMask = register_module('RestOfEventUpdater')
2035  updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2036  updateMask.param('particleList', list_name)
2037  updateMask.param('updateMasks', mask_names)
2038  updateMask.param('cutString', cut_string)
2039  updateMask.param('discard', False)
2040  path.add_module(updateMask)
2041 
2042 
2043 def discardFromROEMasks(list_name, mask_names, cut_string, path=None):
2044  """
2045  This function is used to apply particle list specific cuts on one or more ROE masks (track or eclCluster).
2046  With this function one can DISCARD the tracks/eclclusters used in particles from provided particle list.
2047  This function should be executed only in the for_each roe path for the current ROE object.
2048 
2049  To avoid unnecessary computation, the input particle list should only contain particles from ROE
2050  (use cut 'isInRestOfEvent == 1'). To update the ECLCluster masks, the input particle list should be a photon
2051  particle list (e.g. 'gamma:someLabel'). To update the Track masks, the input particle list should be a charged
2052  pion particle list (e.g. 'pi+:someLabel').
2053 
2054  Updating a non-existing mask will create a new one.
2055 
2056  - discard tracks that were used in provided particle list
2057 
2058  >>> discardFromROEMasks('pi+:badTracks', 'mask', '')
2059 
2060  - discard clusters that were used in provided particle list and pass a cut, apply to several masks
2061 
2062  >>> discardFromROEMasks('gamma:badClusters', ['mask1', 'mask2'], 'E < 0.1')
2063 
2064 
2065  @param list_name name of the input ParticleList
2066  @param mask_names array of ROEMasks to be updated
2067  @param cut_string decay string with which the mask will be updated
2068  @param path modules are added to this path
2069  """
2070 
2071  updateMask = register_module('RestOfEventUpdater')
2072  updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2073  updateMask.param('particleList', list_name)
2074  updateMask.param('updateMasks', mask_names)
2075  updateMask.param('cutString', cut_string)
2076  updateMask.param('discard', True)
2077  path.add_module(updateMask)
2078 
2079 
2080 def optimizeROEWithV0(list_name, mask_names, cut_string, path=None):
2081  """
2082  This function is used to apply particle list specific cuts on one or more ROE masks for Tracks.
2083  It is possible to optimize the ROE selection by treating tracks from V0's separately, meaning,
2084  taking V0's 4-momentum into account instead of 4-momenta of tracks. A cut for only specific V0's
2085  passing it can be applied.
2086 
2087  The input particle list should be a V0 particle list: K_S0 ('K_S0:someLabel', ''),
2088  Lambda ('Lambda:someLabel', '') or converted photons ('gamma:someLabel').
2089 
2090  Updating a non-existing mask will create a new one.
2091 
2092  - treat tracks from K_S0 inside mass window separately, replace track momenta with K_S0 momentum
2093 
2094  >>> optimizeROEWithV0('K_S0:opt', 'mask', '0.450 < M < 0.550')
2095 
2096  @param list_name name of the input ParticleList
2097  @param mask_names array of ROEMasks to be updated
2098  @param cut_string decay string with which the mask will be updated
2099  @param path modules are added to this path
2100  """
2101 
2102  updateMask = register_module('RestOfEventUpdater')
2103  updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2104  updateMask.param('particleList', list_name)
2105  updateMask.param('updateMasks', mask_names)
2106  updateMask.param('cutString', cut_string)
2107  path.add_module(updateMask)
2108 
2109 
2110 def printROEInfo(mask_names=[], which_mask='both', full_print=False, path=None):
2111  """
2112  This function prints out the information for the current ROE, so it should only be used in the for_each path.
2113  It prints out basic ROE object info.
2114 
2115  If mask names are provided, specific information for those masks will be printed out. By default, basic
2116  ECLCluster and Track mask info will be printed out, but it is possible to do this only for one, if needed.
2117 
2118  It is also possible to print out the specific mask values for each Track and ECLCluster by setting the 'full_print'
2119  option to True.
2120 
2121  @param mask_names array of ROEMask names for printing out info
2122  @param which_mask print out info for Tracks ('track'), ECLClusters ('cluster') or ('both')
2123  @param full_print print out mask values for each Track/ECLCLuster in mask
2124  @param path modules are added to this path
2125  """
2126  if not isinstance(path, Path):
2127  B2FATAL("Error from printROEInfo, please add this to the for_each path")
2128 
2129  printMask = register_module('RestOfEventPrinter')
2130  printMask.set_name('RestOfEventPrinter')
2131  printMask.param('maskNames', mask_names)
2132  printMask.param('whichMask', which_mask)
2133  printMask.param('fullPrint', full_print)
2134  path.add_module(printMask)
2135 
2136 
2137 def buildContinuumSuppression(list_name, roe_mask, path):
2138  """
2139  Creates for each Particle in the given ParticleList a ContinuumSuppression
2140  dataobject and makes BASF2 relation between them.
2141 
2142  @param list_name name of the input ParticleList
2143  @param path modules are added to this path
2144  """
2145 
2146  qqBuilder = register_module('ContinuumSuppressionBuilder')
2147  qqBuilder.set_name('QQBuilder_' + list_name)
2148  qqBuilder.param('particleList', list_name)
2149  qqBuilder.param('ROEMask', roe_mask)
2150  path.add_module(qqBuilder)
2151 
2152 
2153 def removeParticlesNotInLists(lists_to_keep, path):
2154  """
2155  Removes all Particles that are not in a given list of ParticleLists (or daughters of those).
2156  All relations from/to Particles, daughter indices, and other ParticleLists are fixed.
2157 
2158  @param lists_to_keep Keep the Particles and their daughters in these ParticleLists.
2159  @param path modules are added to this path
2160  """
2161 
2162  mod = register_module('RemoveParticlesNotInLists')
2163  mod.param('particleLists', lists_to_keep)
2164  path.add_module(mod)
2165 
2166 
2167 def inclusiveBtagReconstruction(upsilon_list_name, bsig_list_name, btag_list_name, input_lists_names, path):
2168  """
2169  Reconstructs Btag from particles in given ParticleLists which do not share any final state particles (mdstSource) with Bsig.
2170 
2171  @param upsilon_list_name Name of the ParticleList to be filled with 'Upsilon(4S) -> B:sig anti-B:tag'
2172  @param bsig_list_name Name of the Bsig ParticleList
2173  @param btag_list_name Name of the Bsig ParticleList
2174  @param input_lists_names List of names of the ParticleLists which are used to reconstruct Btag from
2175  """
2176  btag = register_module('InclusiveBtagReconstruction')
2177  btag.set_name('InclusiveBtagReconstruction_' + bsig_list_name)
2178  btag.param('upsilonListName', upsilon_list_name)
2179  btag.param('bsigListName', bsig_list_name)
2180  btag.param('btagListName', btag_list_name)
2181  btag.param('inputListsNames', input_lists_names)
2182  path.add_module(btag)
2183 
2184 
2185 def selectDaughters(particle_list_name, decay_string, path):
2186  """
2187  Redefine the Daughters of a particle: select from decayString
2188 
2189  @param particle_list_name input particle list
2190  @param decay_string for selecting the Daughters to be preserved
2191  """
2192  seld = register_module('SelectDaughters')
2193  seld.set_name('SelectDaughters_' + particle_list_name)
2194  seld.param('listName', particle_list_name)
2195  seld.param('decayString', decay_string)
2196  path.add_module(seld)
2197 
2198 
2199 def markDuplicate(particleList, prioritiseV0, path):
2200  """
2201  Call DuplicateVertexMarker to find duplicate particles in a list and
2202  flag the ones that should be kept
2203 
2204  @param particleList input particle list
2205  @param prioritiseV0 if true, give V0s a higher priority
2206  """
2207  markdup = register_module('DuplicateVertexMarker')
2208  markdup.param('particleList', particleList)
2209  markdup.param('prioritiseV0', prioritiseV0)
2210  path.add_module(markdup)
2211 
2212 
2213 PI0ETAVETO_COUNTER = 0
2214 
2215 
2216 def oldwritePi0EtaVeto(
2217  particleList,
2218  decayString,
2219  workingDirectory='.',
2220  pi0vetoname='Pi0_Prob',
2221  etavetoname='Eta_Prob',
2222  downloadFlag=True,
2223  selection='',
2224  path=None
2225 ):
2226  """
2227  Give pi0/eta probability for hard photon.
2228 
2229  In the default weight files a value of 1.4 GeV is set as the lower limit for the hard photon energy in the CMS frame.
2230 
2231  The current default weight files are optimised using MC9.
2232  The input variables are as below. Aliases are set to some variables during training.
2233 
2234  * M: pi0/eta candidates Invariant mass
2235  * lowE: soft photon energy in lab frame
2236  * cTheta: soft photon ECL cluster's polar angle
2237  * Zmva: soft photon output of MVA using Zernike moments of the cluster
2238  * minC2Hdist: soft photon distance from eclCluster to nearest point on nearest Helix at the ECL cylindrical radius
2239 
2240  If you don't have weight files in your workingDirectory,
2241  these files are downloaded from database to your workingDirectory automatically.
2242  Please refer to analysis/examples/tutorials/B2A306-B02RhoGamma-withPi0EtaVeto.py
2243  about how to use this function.
2244 
2245  NOTE:
2246  Please don't use following ParticleList names elsewhere:
2247 
2248  ``gamma:HARDPHOTON``, ``pi0:PI0VETO``, ``eta:ETAVETO``,
2249  ``gamma:PI0SOFT + str(PI0ETAVETO_COUNTER)``, ``gamma:ETASOFT + str(PI0ETAVETO_COUNTER)``
2250 
2251  Please don't use ``lowE``, ``cTheta``, ``Zmva``, ``minC2Hdist`` as alias elsewhere.
2252 
2253  @param particleList The input ParticleList
2254  @param decayString specify Particle to be added to the ParticleList
2255  @param workingDirectory The weight file directory
2256  @param downloadFlag whether download default weight files or not
2257  @param pi0vetoname extraInfo name of pi0 probability
2258  @param etavetoname extraInfo name of eta probability
2259  @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
2260  @param path modules are added to this path
2261  """
2262 
2263  import os
2264  import basf2_mva
2265 
2266  global PI0ETAVETO_COUNTER
2267 
2268  if PI0ETAVETO_COUNTER == 0:
2269  from variables import variables
2270  variables.addAlias('lowE', 'daughter(1,E)')
2271  variables.addAlias('cTheta', 'daughter(1,clusterTheta)')
2272  variables.addAlias('Zmva', 'daughter(1,clusterZernikeMVA)')
2273  variables.addAlias('minC2Tdist', 'daughter(1,minC2TDist)')
2274  variables.addAlias('cluNHits', 'daughter(1,clusterNHits)')
2275  variables.addAlias('E9E21', 'daughter(1,clusterE9E21)')
2276 
2277  PI0ETAVETO_COUNTER = PI0ETAVETO_COUNTER + 1
2278 
2279  roe_path = create_path()
2280 
2281  deadEndPath = create_path()
2282 
2283  signalSideParticleFilter(particleList, selection, roe_path, deadEndPath)
2284 
2285  fillSignalSideParticleList('gamma:HARDPHOTON', decayString, path=roe_path)
2286 
2287  pi0softname = 'gamma:PI0SOFT'
2288  etasoftname = 'gamma:ETASOFT'
2289  softphoton1 = pi0softname + str(PI0ETAVETO_COUNTER)
2290  softphoton2 = etasoftname + str(PI0ETAVETO_COUNTER)
2291 
2292  fillParticleList(
2293  softphoton1,
2294  '[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]',
2295  path=roe_path)
2296  applyCuts(softphoton1, 'abs(clusterTiming)<120', path=roe_path)
2297  fillParticleList(
2298  softphoton2,
2299  '[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]',
2300  path=roe_path)
2301  applyCuts(softphoton2, 'abs(clusterTiming)<120', path=roe_path)
2302 
2303  reconstructDecay('pi0:PI0VETO -> gamma:HARDPHOTON ' + softphoton1, '', path=roe_path)
2304  reconstructDecay('eta:ETAVETO -> gamma:HARDPHOTON ' + softphoton2, '', path=roe_path)
2305 
2306  if not os.path.isdir(workingDirectory):
2307  os.mkdir(workingDirectory)
2308  B2INFO('oldwritePi0EtaVeto: ' + workingDirectory + ' has been created as workingDirectory.')
2309 
2310  if not os.path.isfile(workingDirectory + '/pi0veto.root'):
2311  if downloadFlag:
2312  basf2_mva.download('Pi0VetoIdentifier', workingDirectory + '/pi0veto.root')
2313  B2INFO('oldwritePi0EtaVeto: pi0veto.root has been downloaded from database to workingDirectory.')
2314 
2315  if not os.path.isfile(workingDirectory + '/etaveto.root'):
2316  if downloadFlag:
2317  basf2_mva.download('EtaVetoIdentifier', workingDirectory + '/etaveto.root')
2318  B2INFO('oldwritePi0EtaVeto: etaveto.root has been downloaded from database to workingDirectory.')
2319 
2320  roe_path.add_module('MVAExpert', listNames=['pi0:PI0VETO'], extraInfoName='Pi0Veto',
2321  identifier=workingDirectory + '/pi0veto.root')
2322  roe_path.add_module('MVAExpert', listNames=['eta:ETAVETO'], extraInfoName='EtaVeto',
2323  identifier=workingDirectory + '/etaveto.root')
2324 
2325  rankByHighest('pi0:PI0VETO', 'extraInfo(Pi0Veto)', numBest=1, path=roe_path)
2326  rankByHighest('eta:ETAVETO', 'extraInfo(EtaVeto)', numBest=1, path=roe_path)
2327 
2328  variableToSignalSideExtraInfo('pi0:PI0VETO', {'extraInfo(Pi0Veto)': pi0vetoname}, path=roe_path)
2329  variableToSignalSideExtraInfo('eta:ETAVETO', {'extraInfo(EtaVeto)': etavetoname}, path=roe_path)
2330 
2331  path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
2332 
2333 
2334 def writePi0EtaVeto(
2335  particleList,
2336  decayString,
2337  workingDirectory='.',
2338  mode='standard',
2339  downloadFlag=True,
2340  selection='',
2341  path=None
2342 ):
2343  """
2344  Give pi0/eta probability for hard photon.
2345 
2346  In the default weight files a value of 1.4 GeV is set as the lower limit for the hard photon energy in the CMS frame.
2347 
2348  The current default weight files are optimised using MC12.
2349 
2350  The input variables of the mva training are:
2351 
2352  * M: pi0/eta candidates Invariant mass
2353  * daughter(1,E): soft photon energy in lab frame
2354  * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
2355  * daughter(1,minC2TDist): soft photon distance from eclCluster to nearest point on nearest Helix at the ECL cylindrical radius
2356  * daughter(1,clusterZernikeMVA): soft photon output of MVA using Zernike moments of the cluster
2357  * daughter(1,clusterNHits): soft photon total crystal weights sum(w_i) with w_i<=1
2358  * daughter(1,clusterE9E21): soft photon ratio of energies in inner 3x3 crystals and 5x5 crystals without corners
2359  * cosHelicityAngleMomentum: pi0/eta candidates cosHelicityAngleMomentum
2360 
2361  If you don't have weight files in your workingDirectory,
2362  these files are downloaded from the database to your workingDirectory automatically.
2363 
2364  The following strings are available for mode:
2365 
2366  * standard: loose energy cut and no clusterNHits cut are applied to soft photon
2367  * tight: tight energy cut and no clusterNHits cut are applied to soft photon
2368  * cluster: loose energy cut and clusterNHits cut are applied to soft photon
2369  * both: tight energy cut and clusterNHits cut are applied to soft photon
2370 
2371  One can obtain the result of pi0/eta veto from `pi0Prob`/`etaProb`
2372 
2373  NOTE:
2374  Please don't use following ParticleList names elsewhere:
2375 
2376  ``gamma:HardPhoton``,
2377  ``gamma:Pi0Soft + ListName + '_' + particleList.replace(':', '_')``,
2378  ``gamma:EtaSoft + ListName + '_' + particleList.replace(':', '_')``,
2379  ``pi0:EtaVeto + ListName``,
2380  ``eta:EtaVeto + ListName``
2381 
2382  @param particleList the input ParticleList
2383  @param decayString specify Particle to be added to the ParticleList
2384  @param workingDirectory the weight file directory
2385  @param mode choose one mode out of 'standard', 'tight', 'cluster' and 'both'
2386  @param downloadFlag whether download default weight files or not
2387  @param selection selection criteria that Particle needs meet in order for for_each ROE path to continue
2388  @param path modules are added to this path
2389  """
2390 
2391  import os
2392  import basf2_mva
2393 
2394  roe_path = create_path()
2395  deadEndPath = create_path()
2396  signalSideParticleFilter(particleList, selection, roe_path, deadEndPath)
2397  fillSignalSideParticleList('gamma:HardPhoton', decayString, path=roe_path)
2398  if not os.path.isdir(workingDirectory):
2399  os.mkdir(workingDirectory)
2400  B2INFO('writePi0EtaVeto: ' + workingDirectory + ' has been created as workingDirectory.')
2401 
2402  dictListName = {'standard': 'Origin',
2403  'tight': 'TightEnergyThreshold',
2404  'cluster': 'LargeClusterSize',
2405  'both': 'TightEnrgyThresholdAndLargeClusterSize'}
2406 
2407  dictPi0EnergyCut = {'standard': '[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]',
2408  'tight': '[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]',
2409  'cluster': '[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]',
2410  'both': '[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]'}
2411 
2412  dictEtaEnergyCut = {'standard': '[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]',
2413  'tight': '[clusterReg==1 and E>0.06] or [clusterReg==2 and E>0.06] or [clusterReg==3 and E>0.06]',
2414  'cluster': '[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]',
2415  'both': '[clusterReg==1 and E>0.06] or [clusterReg==2 and E>0.06] or [clusterReg==3 and E>0.06]'}
2416 
2417  dictTimingAndNHitsCut = {'standard': 'abs(clusterTiming)<clusterErrorTiming',
2418  'tight': 'abs(clusterTiming)<clusterErrorTiming',
2419  'cluster': 'abs(clusterTiming)<clusterErrorTiming and clusterNHits >= 2',
2420  'both': 'abs(clusterTiming)<clusterErrorTiming and clusterNHits >= 2'}
2421 
2422  dictPi0WeightFileName = {'standard': 'pi0veto_origin.root',
2423  'tight': 'pi0veto_tight.root',
2424  'cluster': 'pi0veto_cluster.root',
2425  'both': 'pi0veto_both.root'}
2426 
2427  dictEtaWeightFileName = {'standard': 'etaveto_origin.root',
2428  'tight': 'etaveto_tight.root',
2429  'cluster': 'etaveto_cluster.root',
2430  'both': 'etaveto_both.root'}
2431 
2432  dictPi0PayloadName = {'standard': 'Pi0VetoIdentifierStandard',
2433  'tight': 'Pi0VetoIdentifierWithHigherEnergyThreshold',
2434  'cluster': 'Pi0VetoIdentifierWithLargerClusterSize',
2435  'both': 'Pi0VetoIdentifierWithHigherEnergyThresholdAndLargerClusterSize'}
2436 
2437  dictEtaPayloadName = {'standard': 'EtaVetoIdentifierStandard',
2438  'tight': 'EtaVetoIdentifierWithHigherEnergyThreshold',
2439  'cluster': 'EtaVetoIdentifierWithLargerClusterSize',
2440  'both': 'EtaVetoIdentifierWithHigherEnergyThresholdAndLargerClusterSize'}
2441 
2442  dictPi0ExtraInfoName = {'standard': 'Pi0VetoOrigin',
2443  'tight': 'Pi0VetoTightEnergyThreshold',
2444  'cluster': 'Pi0VetoLargeClusterSize',
2445  'both': 'Pi0VetoTightEnergyThresholdAndLargeClusterSize'}
2446 
2447  dictEtaExtraInfoName = {'standard': 'EtaVetoOrigin',
2448  'tight': 'EtaVetoTightEnergyThreshold',
2449  'cluster': 'EtaVetoLargeClusterSize',
2450  'both': 'EtaVetoTightEnergyThresholdAndLargeClusterSize'}
2451 
2452  dictPi0ExtraInfoRename = {'standard': 'Pi0ProbOrigin',
2453  'tight': 'Pi0ProbTightEnergyThreshold',
2454  'cluster': 'Pi0ProbLargeClusterSize',
2455  'both': 'Pi0ProbTightEnergyThresholdAndLargeClusterSize'}
2456 
2457  dictEtaExtraInfoRename = {'standard': 'EtaProbOrigin',
2458  'tight': 'EtaProbTightEnergyThreshold',
2459  'cluster': 'EtaProbLargeClusterSize',
2460  'both': 'EtaProbTightEnergyThresholdAndLargeClusterSize'}
2461 
2462  ListName = dictListName[mode]
2463  Pi0EnergyCut = dictPi0EnergyCut[mode]
2464  EtaEnergyCut = dictEtaEnergyCut[mode]
2465  TimingAndNHitsCut = dictTimingAndNHitsCut[mode]
2466  Pi0WeightFileName = dictPi0WeightFileName[mode]
2467  EtaWeightFileName = dictEtaWeightFileName[mode]
2468  Pi0PayloadName = dictPi0PayloadName[mode]
2469  EtaPayloadName = dictEtaPayloadName[mode]
2470  Pi0ExtraInfoName = dictPi0ExtraInfoName[mode]
2471  EtaExtraInfoName = dictEtaExtraInfoName[mode]
2472  Pi0ExtraInfoRename = dictPi0ExtraInfoRename[mode]
2473  EtaExtraInfoRename = dictEtaExtraInfoRename[mode]
2474 
2475  """
2476  pi0 veto
2477  """
2478  # define the particleList name for soft photon
2479  pi0soft = 'gamma:Pi0Soft' + ListName + '_' + particleList.replace(':', '_')
2480  # fill the particleList for soft photon with energy cut
2481  fillParticleList(pi0soft, Pi0EnergyCut, path=roe_path)
2482  # apply an additional cut for soft photon
2483  applyCuts(pi0soft, TimingAndNHitsCut, path=roe_path)
2484  # reconstruct pi0
2485  reconstructDecay('pi0:Pi0Veto' + ListName + ' -> gamma:HardPhoton ' + pi0soft, '', path=roe_path)
2486  # if you don't have wight files in your workingDirectory,
2487  # these files are downloaded from database to your workingDirectory automatically.
2488  if not os.path.isfile(workingDirectory + '/' + Pi0WeightFileName):
2489  if downloadFlag:
2490  basf2_mva.download(Pi0PayloadName, workingDirectory + '/' + Pi0WeightFileName)
2491  B2INFO('writePi0EtaVeto: ' + Pi0WeightFileName + ' has been downloaded from database to workingDirectory.')
2492  # MVA training is conducted.
2493  roe_path.add_module('MVAExpert', listNames=['pi0:Pi0Veto' + ListName],
2494  extraInfoName=Pi0ExtraInfoName, identifier=workingDirectory + '/' + Pi0WeightFileName)
2495  # Pick up only one pi0/eta candidate with the highest pi0/eta probability.
2496  rankByHighest('pi0:Pi0Veto' + ListName, 'extraInfo(' + Pi0ExtraInfoName + ')', numBest=1, path=roe_path)
2497  # 'extraInfo(Pi0Veto)' is labeled 'Pi0_Prob'
2498  variableToSignalSideExtraInfo('pi0:Pi0Veto' + ListName,
2499  {'extraInfo(' + Pi0ExtraInfoName + ')': Pi0ExtraInfoRename}, path=roe_path)
2500 
2501  """
2502  eta veto
2503  """
2504  etasoft = 'gamma:EtaSoft' + ListName + '_' + particleList.replace(':', '_')
2505  fillParticleList(etasoft, EtaEnergyCut, path=roe_path)
2506  applyCuts(etasoft, TimingAndNHitsCut, path=roe_path)
2507  reconstructDecay('eta:EtaVeto' + ListName + ' -> gamma:HardPhoton ' + etasoft, '', path=roe_path)
2508  if not os.path.isfile(workingDirectory + '/' + EtaWeightFileName):
2509  if downloadFlag:
2510  basf2_mva.download(EtaPayloadName, workingDirectory + '/' + EtaWeightFileName)
2511  B2INFO('writePi0EtaVeto: ' + EtaWeightFileName + 'has been downloaded from database to workingDirectory.')
2512  roe_path.add_module('MVAExpert', listNames=['eta:EtaVeto' + ListName],
2513  extraInfoName=EtaExtraInfoName, identifier=workingDirectory + '/' + EtaWeightFileName)
2514  rankByHighest('eta:EtaVeto' + ListName, 'extraInfo(' + EtaExtraInfoName + ')', numBest=1, path=roe_path)
2515  variableToSignalSideExtraInfo('eta:EtaVeto' + ListName,
2516  {'extraInfo(' + EtaExtraInfoName + ')': EtaExtraInfoRename}, path=roe_path)
2517 
2518  path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
2519 
2520 
2521 def buildEventKinematics(inputListNames=[], default_cleanup=True,
2522  chargedPIDPriors=None, fillWithMostLikely=False, path=None):
2523  """
2524  Calculates the global kinematics of the event (visible energy, missing momentum, missing mass...)
2525  using ParticleLists provided. If no ParticleList is provided, default ParticleLists are used
2526  (all track and all hits in ECL without associated track).
2527 
2528  The visible energy missing values are
2529  stored in a EventKinematics dataobject.
2530 
2531  @param inputListNames list of ParticleLists used to calculate the global event kinematics.
2532  If the list is empty, default ParticleLists pi+:evtkin and gamma:evtkin are filled.
2533  @param fillWithMostLikely if True, the module uses the most likely particle mass hypothesis for charged particles
2534  according to the PID likelihood and the option inputListNames will be ignored.
2535  @param chargedPIDPriors The prior PID fractions, that are used to regulate
2536  amount of certain charged particle species, should be a list of
2537  six floats if not None. The order of particle types is
2538  the following: [e-, mu-, pi-, K-, p+, d+]
2539  @param default_cleanup if True and either inputListNames empty or fillWithMostLikely True, default clean up cuts are applied
2540  @param path modules are added to this path
2541  """
2542  trackCuts = 'pt > 0.1'
2543  trackCuts += ' and thetaInCDCAcceptance'
2544  trackCuts += ' and abs(dz) < 3'
2545  trackCuts += ' and dr < 0.5'
2546 
2547  gammaCuts = 'E > 0.05'
2548  gammaCuts += ' and thetaInCDCAcceptance'
2549 
2550  if fillWithMostLikely:
2551  from stdCharged import stdMostLikely
2552  stdMostLikely(chargedPIDPriors, '_evtkin', path=path)
2553  inputListNames = ['%s:mostlikely_evtkin' % ptype for ptype in ['K+', 'p+', 'e+', 'mu+', 'pi+']]
2554  fillParticleList('gamma:evtkin', '', path=path)
2555  inputListNames += ['gamma:evtkin']
2556  if default_cleanup:
2557  B2INFO("Using default cleanup in EventKinematics module.")
2558  for ptype in ['K+', 'p+', 'e+', 'mu+', 'pi+']:
2559  applyCuts(f'{ptype}:mostlikely_evtkin', trackCuts, path=path)
2560  applyCuts('gamma:evtkin', gammaCuts, path=path)
2561  else:
2562  B2INFO("No cleanup in EventKinematics module.")
2563  if not inputListNames:
2564  B2INFO("Creating particle lists pi+:evtkin and gamma:evtkin to get the global kinematics of the event.")
2565  fillParticleList('pi+:evtkin', '', path=path)
2566  fillParticleList('gamma:evtkin', '', path=path)
2567  particleLists = ['pi+:evtkin', 'gamma:evtkin']
2568  if default_cleanup:
2569  B2INFO("Using default cleanup in EventKinematics module.")
2570  applyCuts('pi+:evtkin', trackCuts, path=path)
2571  applyCuts('gamma:evtkin', gammaCuts, path=path)
2572  else:
2573  B2INFO("No cleanup in EventKinematics module.")
2574  else:
2575  particleLists = inputListNames
2576 
2577  eventKinematicsModule = register_module('EventKinematics')
2578  eventKinematicsModule.set_name('EventKinematics_')
2579  eventKinematicsModule.param('particleLists', particleLists)
2580  path.add_module(eventKinematicsModule)
2581 
2582 
2583 def buildEventShape(inputListNames=[],
2584  default_cleanup=True,
2585  allMoments=False,
2586  cleoCones=True,
2587  collisionAxis=True,
2588  foxWolfram=True,
2589  harmonicMoments=True,
2590  jets=True,
2591  sphericity=True,
2592  thrust=True,
2593  checkForDuplicates=False,
2594  path=None):
2595  """
2596  Calculates the event-level shape quantities (thrust, sphericity, Fox-Wolfram moments...)
2597  using the particles in the lists provided by the user. If no particle list is provided,
2598  the function will internally create a list of good tracks and a list of good photons
2599  with (optionally) minimal quality cuts.
2600 
2601 
2602  The results of the calculation are then stored into the EventShapeContainer dataobject,
2603  and are accessible using the variables of the EventShape group.
2604 
2605  The user can switch the calculation of certain quantities on or off to save computing
2606  time. By default the calculation of the high-order moments (5-8) is turned off.
2607  Switching off an option will make the corresponding variables not available.
2608 
2609  Warning:
2610  The user can provide as many particle lists
2611  as needed, using also combined particles, but the function will always assume that
2612  the lists are independent.
2613  If the lists provided by the user contain several times the same track (either with
2614  different mass hypothesis, or once as an independent particle and once as daughter of a
2615  combined particle) the results won't be reliable.
2616  A basic check for duplicates is available setting the checkForDuplicate flags,
2617  but is usually quite time consuming.
2618 
2619 
2620  @param inputListNames List of ParticleLists used to calculate the
2621  event shape variables. If the list is empty the default
2622  particleLists pi+:evtshape and gamma:evtshape are filled.
2623  @param default_cleanup If True, applies standard cuts on pt and cosTheta when
2624  defining the internal lists. This option is ignored if the
2625  particleLists are provided by the user.
2626  @param path Path to append the eventShape modules to.
2627  @param thrust Enables the calculation of thrust-related quantities (CLEO
2628  cones, Harmonic moments, jets).
2629  @param collisionAxis Enables the calculation of the quantities related to the
2630  collision axis .
2631  @param foxWolfram Enables the calculation of the Fox-Wolfram moments.
2632  @param harmonicMoments Enables the calculation of the Harmonic moments with respect
2633  to both the thrust axis and, if collisionAxis = True, the collision axis.
2634  @param allMoments If True, calculates also the FW and harmonic moments from order
2635  5 to 8 instead of the low-order ones only.
2636  @param cleoCones Enables the calculation of the CLEO cones with respect to both the thrust
2637  axis and, if collisionAxis = True, the collision axis.
2638  @param jets Enables the calculation of the hemisphere momenta and masses.
2639  Requires thrust = True.
2640  @param sphericity Enables the calculation of the sphericity-related quantities.
2641  @param checkForDuplicates Perform a check for duplicate particles before adding them. This option
2642  is quite time consuming, instead of using it consider sanitizing
2643  the lists you are passing to the function.
2644  """
2645  if not inputListNames:
2646  B2INFO("Creating particle lists pi+:evtshape and gamma:evtshape to get the event shape variables.")
2647  fillParticleList('pi+:evtshape', '', path=path)
2648  fillParticleList('gamma:evtshape', '', path=path)
2649  particleLists = ['pi+:evtshape', 'gamma:evtshape']
2650 
2651  if default_cleanup:
2652  B2INFO("Applying standard cuts")
2653  trackCuts = 'pt > 0.1'
2654  trackCuts += ' and thetaInCDCAcceptance'
2655  trackCuts += ' and abs(dz) < 3.0'
2656  trackCuts += ' and dr < 0.5'
2657  applyCuts('pi+:evtshape', trackCuts, path=path)
2658 
2659  gammaCuts = 'E > 0.05'
2660  gammaCuts += ' and thetaInCDCAcceptance'
2661  applyCuts('gamma:evtshape', gammaCuts, path=path)
2662  else:
2663  B2WARNING("Creating the default lists with no cleanup.")
2664  else:
2665  particleLists = inputListNames
2666 
2667  eventShapeModule = register_module('EventShapeCalculator')
2668  eventShapeModule.set_name('EventShape')
2669  eventShapeModule.param('particleListNames', particleLists)
2670  eventShapeModule.param('enableAllMoments', allMoments)
2671  eventShapeModule.param('enableCleoCones', cleoCones)
2672  eventShapeModule.param('enableCollisionAxis', collisionAxis)
2673  eventShapeModule.param('enableFoxWolfram', foxWolfram)
2674  eventShapeModule.param('enableJets', jets)
2675  eventShapeModule.param('enableHarmonicMoments', harmonicMoments)
2676  eventShapeModule.param('enableSphericity', sphericity)
2677  eventShapeModule.param('enableThrust', thrust)
2678  eventShapeModule.param('checkForDuplicates', checkForDuplicates)
2679 
2680  path.add_module(eventShapeModule)
2681 
2682 
2683 def labelTauPairMC(printDecayInfo=False, path=None):
2684  """
2685  Search tau leptons into the MC information of the event. If confirms it's a generated tau pair decay,
2686  labels the decay generated of the positive and negative leptons using the ID of KKMC tau decay table.
2687 
2688  @param printDecayInfo: If true, prints ID and prong of each tau lepton in the event.
2689  @param path: module is added to this path
2690  """
2691  tauDecayMarker = register_module('TauDecayMarker')
2692  tauDecayMarker.set_name('TauDecayMarker_')
2693 
2694  path.add_module(tauDecayMarker, printDecayInfo=printDecayInfo)
2695 
2696 
2697 def tagCurlTracks(particleLists,
2698  mcTruth=False,
2699  responseCut=0.324,
2700  selectorType='cUt',
2701  ptCut=0.6,
2702  train=False,
2703  path=None):
2704  """
2705  Warning:
2706  The cut selector is not calibrated with Belle II data and should not be used without extensive study.
2707 
2708  Identifies curl tracks and tags them with extraInfo(isCurl=1) for later removal.
2709  For Belle data with a `b2bii` analysis the available cut based selection is described in `BN1079`_.
2710 
2711  .. _BN1079: https://belle.kek.jp/secured/belle_note/gn1079/bn1079.pdf
2712 
2713 
2714  The module loops over all particles in a given list that meet the preselection **ptCut** and assigns them to
2715  bundles based on the response of the chosen **selector** and the required minimum response set by the
2716  **responseCut**. Once all particles are assigned they are ranked by 25dr^2+dz^2. All but the lowest are tagged
2717  with extraInfo(isCurl=1) to allow for later removal by cutting the list or removing these from ROE as
2718  applicable.
2719 
2720 
2721  @param particleLists: list of particle lists to check for curls.
2722  @param mcTruth: bool flag to additionally assign particles with extraInfo(isTruthCurl) and
2723  extraInfo(truthBundleSize). To calculate these particles are assigned to bundles by their
2724  genParticleIndex then ranked and tagged as normal.
2725  @param responseCut: float min classifier response that considers two tracks to come from the same particle.
2726  Note 'cut' selector is binary 0/1.
2727  @param selectorType: string name of selector to use. The available options are 'cut' and 'mva'.
2728  It is strongly recommended to used the 'mva' selection. The 'cut' selection
2729  is based on BN1079 and is only calibrated for Belle data.
2730  @param ptCut: pre-selection cut on transverse momentum.
2731  @param train: flag to set training mode if selector has a training mode (mva).
2732  @param path: module is added to this path.
2733  """
2734 
2735  import b2bii
2736  belle = b2bii.isB2BII()
2737 
2738  if (not isinstance(particleLists, list)):
2739  particleLists = [particleLists] # in case user inputs a particle list as string
2740 
2741  curlTagger = register_module('CurlTagger')
2742  curlTagger.set_name('CurlTagger_')
2743  curlTagger.param('particleLists', particleLists)
2744  curlTagger.param('belle', belle)
2745  curlTagger.param('mcTruth', mcTruth)
2746  curlTagger.param('responseCut', responseCut)
2747  curlTagger.param('selectorType', selectorType)
2748  curlTagger.param('ptCut', ptCut)
2749  curlTagger.param('train', train)
2750 
2751  path.add_module(curlTagger)
2752 
2753 
2754 def applyChargedPidMVA(particleLists, path, trainingMode, binaryHypoPDGCodes=(0, 0)):
2755  """
2756  Use an MVA to perform particle identification for charged stable particles, using the `ChargedPidMVA` module.
2757 
2758  The module decorates Particle objects in the input ParticleList(s) with variables
2759  containing the appropriate MVA score, which can be used to select candidates by placing a cut on it.
2760 
2761  Note:
2762  The MVA algorithm used is a gradient boosted decision tree (**TMVA 4.2.1**, **ROOT 6.14/06**).
2763 
2764  The module can perform either 'binary' PID between input S, B particle mass hypotheses according to the following scheme:
2765 
2766  - e (11) vs. pi (211)
2767  - mu (13) vs. pi (211)
2768  - pi (211) vs. K (321)
2769  - K (321) vs. pi (211)
2770  - p (2212) vs. pi (211)
2771  - d (1000010020) vs pi (211)
2772 
2773  , or 'global' PID, namely "one-vs-others" separation. The latter makes use of an MVA algorithm trained in multi-class mode,
2774  and it's the default behaviour.
2775 
2776  Note:
2777  Currently the MVA is charge-agnostic, i.e. the training is not done independently for +/- charged particles.
2778 
2779  Parameters:
2780  particleLists (list(str)): list of names of ParticleList objects for charged stable particles.
2781  The charge-conjugate ParticleLists will be also processed automatically.
2782  path (basf2.Path): the module is added to this path.
2783  trainingMode (``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``): enum identifier of the training mode.
2784  Needed to pick up the correct payload from the DB. Available choices:
2785 
2786  * c_Classification=0
2787  * c_Multiclass=1
2788  * c_ECL_Classification=2
2789  * c_ECL_Multiclass=3
2790  * c_PSD_Classification=4
2791  * c_PSD_Multiclass=5
2792  * c_ECL_PSD_Classification=6
2793  * c_ECL_PSD_Multiclass=7
2794 
2795  binaryHypoPDGCodes (tuple(int, int), ``optional``): the pdgIds of the signal, background mass hypothesis.
2796  Required only for binary PID mode.
2797  """
2798 
2799  from ROOT import Belle2
2800 
2801  plSet = set(particleLists)
2802 
2803  # Map the training mode enum value to the actual name of the payload in the GT.
2804  payloadNames = {
2805  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_Classification:
2806  {"mode": "Classification", "detector": "ALL"},
2807  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_Multiclass:
2808  {"mode": "Multiclass", "detector": "ALL"},
2809  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_ECL_Classification:
2810  {"mode": "ECL_Classification", "detector": "ECL"},
2811  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_ECL_Multiclass:
2812  {"mode": "ECL_Multiclass", "detector": "ECL"},
2813  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_PSD_Classification:
2814  {"mode": "PSD_Classification", "detector": "ALL"},
2815  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_PSD_Multiclass:
2816  {"mode": "PSD_Multiclass", "detector": "ALL"},
2817  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_ECL_PSD_Classification:
2818  {"mode": "ECL_PSD_Classification", "detector": "ECL"},
2819  Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode.c_ECL_PSD_Multiclass:
2820  {"mode": "ECL_PSD_Multiclass", "detector": "ECL"},
2821  }
2822 
2823  if payloadNames.get(trainingMode) is None:
2824  B2FATAL("The chosen training mode integer identifier:\n", trainingMode,
2825  "\nis not supported. Please choose among the following:\n",
2826  "\n".join(f"{key}:{val.get('mode')}" for key, val in sorted(payloadNames.items())))
2827 
2828  mode = payloadNames.get(trainingMode).get("mode")
2829  detector = payloadNames.get(trainingMode).get("detector")
2830 
2831  payloadName = f"ChargedPidMVAWeights_{mode}"
2832 
2833  if binaryHypoPDGCodes == (0, 0):
2834  # MULTI-CLASS training mode.
2835 
2836  chargedpid = register_module("ChargedPidMVAMulticlass")
2837  chargedpid.set_name(f"ChargedPidMVAMulticlass_{mode}")
2838 
2839  else:
2840  # BINARY training mode.
2841 
2842  # In binary mode, enforce check on input S, B hypotheses compatibility.
2843  binaryOpts = [
2844  (Belle2.Const.electron.getPDGCode(), Belle2.Const.pion.getPDGCode()),
2845  (Belle2.Const.muon.getPDGCode(), Belle2.Const.pion.getPDGCode()),
2846  (Belle2.Const.pion.getPDGCode(), Belle2.Const.kaon.getPDGCode()),
2847  (Belle2.Const.kaon.getPDGCode(), Belle2.Const.pion.getPDGCode()),
2848  (Belle2.Const.proton.getPDGCode(), Belle2.Const.pion.getPDGCode()),
2849  (Belle2.Const.deuteron.getPDGCode(), Belle2.Const.pion.getPDGCode())
2850  ]
2851 
2852  if binaryHypoPDGCodes not in binaryOpts:
2853  B2FATAL("No charged pid MVA was trained to separate ", binaryHypoPDGCodes[0], " vs. ", binaryHypoPDGCodes[1],
2854  ". Please choose among the following pairs:\n",
2855  "\n".join(f"{opt[0]} vs. {opt[1]}" for opt in binaryOpts))
2856 
2857  chargedpid = register_module("ChargedPidMVA")
2858  chargedpid.set_name(f"ChargedPidMVA_{binaryHypoPDGCodes[0]}_vs_{binaryHypoPDGCodes[1]}_{mode}")
2859  chargedpid.param("sigHypoPDGCode", binaryHypoPDGCodes[0])
2860  chargedpid.param("bkgHypoPDGCode", binaryHypoPDGCodes[1])
2861 
2862  chargedpid.param("particleLists", list(plSet))
2863 
2864  chargedpid.param("payloadName", payloadName)
2865 
2866  # Ensure the module knows whether we are using ECL-only training mode.
2867  if detector == "ECL":
2868  chargedpid.param("useECLOnlyTraining", True)
2869 
2870  path.add_module(chargedpid)
2871 
2872 
2873 def calculateDistance(list_name, decay_string, mode='vertextrack', path=None):
2874  """
2875  Calculates distance between two vertices, distance of closest approach between a vertex and a track,\
2876  distance of closest approach between a vertex and btube. For track, this calculation ignores track curvature,\
2877  it's negligible for small distances.The user should use extraInfo(CalculatedDistance)\
2878  to get it. A full example steering file is at analysis/tests/test_DistanceCalculator.py
2879 
2880  Example:
2881  >>> from modularAnalysis import calculateDistance
2882  >>>calculateDistance('list_name', 'decay_string', "mode", path=user_path)
2883 
2884  @param list_name name of the input ParticleList
2885  @param decay_string select particles between the distance of closest approch will be calculated
2886  @param mode Specifies how the distance is calculated
2887  vertextrack: calculate the distance of closest appreach between a track and a\
2888  vertex, taking the first candidate as vertex, default
2889  trackvertex: calculate the distance of closest appreach between a track and a\
2890  vertex, taking the first candidate as track
2891  2tracks: calculates the distance of closest appreach between two tracks
2892  2vertices: calculates the distance between two vertices
2893  vertexbtube: calculates the distance of closest appreach between a vertex and btube
2894  trackbtube: calculates the distance of closest appreach between a track and btube
2895  @param path modules are added to this path
2896 
2897  """
2898 
2899  dist_mod = register_module('DistanceCalculator')
2900 
2901  dist_mod.set_name('DistanceCalculator_' + list_name)
2902  dist_mod.param('listName', list_name)
2903  dist_mod.param('decayString', decay_string)
2904  dist_mod.param('mode', mode)
2905  path.add_module(dist_mod)
2906 
2907 
2908 def addInclusiveDstarReconstruction(inputPionList, outputDstarList, slowPionCut, path):
2909  """
2910  Adds the InclusiveDstarReconstruction module to the given path.
2911  This module creates a D* particle list by estimating the D* four momenta
2912  from slow pions, specified by a given cut. The D* energy is approximated
2913  as E(D*) = m(D*)/(m(D*) - m(D)) * E(pi). The absolute value of the D*
2914  momentum is calculated using the D* PDG mass and the direction is collinear
2915  to the slow pion direction. The charge of the given pion list has to be consistent
2916  with the D* charge
2917 
2918  @param inputPionList Name of the input pion particle list
2919  @param outputDstarList Name of the output D* particle list
2920  @param slowPionCut Cut applied to the pion list to identify slow pions
2921  @param path the module is added to this path
2922  """
2923  incl_dstar = register_module("InclusiveDstarReconstruction")
2924  incl_dstar.param("pionListName", inputPionList)
2925  incl_dstar.param("DstarListName", outputDstarList)
2926  incl_dstar.param("slowPionCut", slowPionCut)
2927  path.add_module(incl_dstar)
2928 
2929 
2930 def getAnalysisGlobaltag():
2931  """
2932  Returns a string containing the name of the latest and recommended analysis globaltag.
2933  """
2934  tags = subprocess.check_output(['b2conditionsdb-recommend', '--oneline']).decode('UTF-8').rstrip().split(' ')
2935  analysis_tag = ''
2936  for tag in tags:
2937  if tag.startswith('analysis_tools'):
2938  analysis_tag = tag
2939  return analysis_tag
2940 
2941 
2942 if __name__ == '__main__':
2943  from basf2.utils import pretty_print_module
2944  pretty_print_module(__name__, "modularAnalysis")
b2bii.isB2BII
def isB2BII()
Definition: b2bii.py:6
reconstructMCDecay
Definition: reconstructMCDecay.py:1
printDataStore
Definition: printDataStore.py:1
basf2.utils
Definition: utils.py:1
variablesToNtuple
Definition: variablesToNtuple.py:1
mdst.add_mdst_output
def add_mdst_output(path, mc=True, filename='mdst.root', additionalBranches=[], dataDescription=None)
Definition: mdst.py:24
removeParticlesNotInLists
Definition: removeParticlesNotInLists.py:1
variablesToExtraInfo
Definition: variablesToExtraInfo.py:1
Belle2::B2Vector3< double >
variablesToHistogram
Definition: variablesToHistogram.py:1
Belle2::DBStore::Instance
static DBStore & Instance()
Instance of a singleton DBStore.
Definition: DBStore.cc:36
b2bii.setB2BII
def setB2BII()
Definition: b2bii.py:13
Belle2::MagneticField
Magnetic field map.
Definition: MagneticField.h:43
Belle2::MagneticFieldComponentConstant
Describe one component of the Geometry.
Definition: MagneticFieldComponentConstant.h:29
pdg.conjugate
def conjugate(name)
Definition: pdg.py:98