Belle II Software development
modularAnalysis.py
1#!/usr/bin/env python3
2
3
10
11"""
12This module defines wrapper functions around the analysis modules.
13"""
14
15import b2bii
16from basf2 import register_module, create_path
17from basf2 import B2INFO, B2WARNING, B2ERROR, B2FATAL
18import basf2
19import subprocess
20
21
22def setAnalysisConfigParams(configParametersAndValues, path):
23 """
24 Sets analysis configuration parameters.
25
26 These are:
27
28 - 'tupleStyle': 'Default' (default) or 'Laconic'
29
30 - defines the style of the branch name in the ntuple
31
32 - 'mcMatchingVersion': Specifies what version of mc matching algorithm is going to be used:
33
34 - 'Belle' - analysis of Belle MC
35 - 'BelleII' (default) - all other cases
36
37 @param configParametersAndValues dictionary of parameters and their values of the form {param1: value, param2: value, ...)
38 @param modules are added to this path
39 """
40
41 conf = register_module('AnalysisConfiguration')
42
43 allParameters = ['tupleStyle', 'mcMatchingVersion']
44
45 keys = configParametersAndValues.keys()
46 for key in keys:
47 if key not in allParameters:
48 allParametersString = ', '.join(allParameters)
49 B2ERROR('Invalid analysis configuration parameter: ' + key + '.\n'
50 'Please use one of the following: ' + allParametersString)
51
52 for param in allParameters:
53 if param in configParametersAndValues:
54 conf.param(param, configParametersAndValues.get(param))
55
56 path.add_module(conf)
57
58
59def inputMdst(filename, path, environmentType='default', skipNEvents=0, entrySequence=None, *, parentLevel=0, **kwargs):
60 """
61 Loads the specified :ref:`mDST <mdst>` (or :ref:`uDST <analysis_udstoutput>`) file with the RootInput module.
62
63 The correct environment (e.g. magnetic field settings) is determined from
64 ``environmentType``. Options are either: 'default' (for Belle II MC and
65 data: falls back to database), 'Belle': for analysis of converted Belle 1
66 data and MC.
67
68 Parameters:
69 filename (str): the name of the file to be loaded
70 path (basf2.Path): modules are added to this path
71 environmentType (str): type of the environment to be loaded (either 'default' or 'Belle')
72 skipNEvents (int): N events of the input file are skipped
73 entrySequence (str): The number sequences (e.g. 23:42,101) defining the entries which are processed.
74 parentLevel (int): Number of generations of parent files (files used as input when creating a file) to be read
75 """
76
77 if entrySequence is not None:
78 entrySequence = [entrySequence]
79
80 inputMdstList([filename], path, environmentType, skipNEvents, entrySequence, parentLevel=parentLevel, **kwargs)
81
82
83def inputMdstList(
84 filelist,
85 path,
86 environmentType='default',
87 skipNEvents=0,
88 entrySequences=None,
89 *,
90 parentLevel=0,
91 useB2BIIDBCache=True):
92 """
93 Loads the specified list of :ref:`mDST <mdst>` (or :ref:`uDST <analysis_udstoutput>`) files with the RootInput module.
94
95 The correct environment (e.g. magnetic field settings) is determined from
96 ``environmentType``. Options are either: 'default' (for Belle II MC and
97 data: falls back to database), 'Belle': for analysis of converted Belle 1
98 data and MC.
99
100 Parameters:
101 filelist (list(str)): the filename list of files to be loaded
102 path (basf2.Path): modules are added to this path
103 environmentType (str): type of the environment to be loaded (either 'default' or 'Belle')
104 skipNEvents (int): N events of the input files are skipped
105 entrySequences (list(str)): The number sequences (e.g. 23:42,101) defining
106 the entries which are processed for each inputFileName.
107 parentLevel (int): Number of generations of parent files (files used as input when creating a file) to be read
108 useB2BIIDBCache (bool): Loading of local KEKCC database (only to be deactivated in very special cases)
109 """
110
111 roinput = register_module('RootInput')
112 roinput.param('inputFileNames', filelist)
113 roinput.param('skipNEvents', skipNEvents)
114 if entrySequences is not None:
115 roinput.param('entrySequences', entrySequences)
116 roinput.param('parentLevel', parentLevel)
117
118 path.add_module(roinput)
119 path.add_module('ProgressBar')
120
121 if environmentType == 'Belle':
122 # Belle 1 constant magnetic field
123 # -------------------------------
124 # n.b. slightly unfortunate syntax: the MagneticField is a member of the
125 # Belle2 namespace but will be set to the Belle 1 values
126 from ROOT import Belle2 # reduced scope of potentially-misbehaving import
127 from ROOT.Math import XYZVector
128 belle1_field = Belle2.MagneticField()
129 belle1_field.addComponent(Belle2.MagneticFieldComponentConstant(XYZVector(0, 0, 1.5 * Belle2.Unit.T)))
130 Belle2.DBStore.Instance().addConstantOverride("MagneticField", belle1_field, False)
131 # also set the MC matching for Belle 1
132 setAnalysisConfigParams({'mcMatchingVersion': 'Belle'}, path)
134 if useB2BIIDBCache:
135 basf2.conditions.metadata_providers = ["/sw/belle/b2bii/database/conditions/b2bii.sqlite"]
136 basf2.conditions.payload_locations = ["/sw/belle/b2bii/database/conditions/"]
137
138
139def outputMdst(filename, path):
140 """
141 Saves mDST (mini-Data Summary Tables) to the output root file.
142
143 .. warning::
144
145 This function is kept for backward-compatibility.
146 Better to use `mdst.add_mdst_output` directly.
147
148 """
149
150 import mdst
151 mdst.add_mdst_output(path, mc=True, filename=filename)
152
153
154def outputUdst(filename, particleLists=None, includeArrays=None, path=None, dataDescription=None):
155 """
156 Save uDST (user-defined Data Summary Tables) = MDST + Particles + ParticleLists
157 The charge-conjugate lists of those given in particleLists are also stored.
158 Additional Store Arrays and Relations to be stored can be specified via includeArrays
159 list argument.
160
161 Note:
162 This does not reduce the amount of Particle objects saved,
163 see `udst.add_skimmed_udst_output` for a function that does.
164
165 """
166
167 import udst
169 path=path, filename=filename, particleLists=particleLists,
170 additionalBranches=includeArrays, dataDescription=dataDescription)
171
172
173def outputIndex(filename, path, includeArrays=None, keepParents=False, mc=True):
174 """
175 Write out all particle lists as an index file to be reprocessed using parentLevel flag.
176 Additional branches necessary for file to be read are automatically included.
177 Additional Store Arrays and Relations to be stored can be specified via includeArrays
178 list argument.
179
180 @param str filename the name of the output index file
181 @param str path modules are added to this path
182 @param list(str) includeArrays: datastore arrays/objects to write to the output
183 file in addition to particle lists and related information
184 @param bool keepParents whether the parents of the input event will be saved as the parents of the same event
185 in the output index file. Useful if you are only adding more information to another index file
186 @param bool mc whether the input data is MC or not
187 """
188
189 if includeArrays is None:
190 includeArrays = []
191
192 # Module to mark all branches to not be saved except particle lists
193 onlyPLists = register_module('OnlyWriteOutParticleLists')
194 path.add_module(onlyPLists)
195
196 # Set up list of all other branches we need to make index file complete
197 partBranches = [
198 'Particles',
199 'ParticlesToMCParticles',
200 'ParticlesToPIDLikelihoods',
201 'ParticleExtraInfoMap',
202 'EventExtraInfo'
203 ]
204 branches = ['EventMetaData']
205 persistentBranches = ['FileMetaData']
206 if mc:
207 branches += []
208 # persistentBranches += ['BackgroundInfos']
209 branches += partBranches
210 branches += includeArrays
211
212 r1 = register_module('RootOutput')
213 r1.param('outputFileName', filename)
214 r1.param('additionalBranchNames', branches)
215 r1.param('branchNamesPersistent', persistentBranches)
216 r1.param('keepParents', keepParents)
217 path.add_module(r1)
218
219
220def setupEventInfo(noEvents, path):
221 """
222 Prepare to generate events. This function sets up the EventInfoSetter.
223 You should call this before adding a generator from generators.
224 The experiment and run numbers are set to 0 (run independent generic MC in phase 3).
225 https://xwiki.desy.de/xwiki/rest/p/59192
226
227 Parameters:
228 noEvents (int): number of events to be generated
229 path (basf2.Path): modules are added to this path
230 """
231
232 evtnumbers = register_module('EventInfoSetter')
233 evtnumbers.param('evtNumList', [noEvents])
234 evtnumbers.param('runList', [0])
235 evtnumbers.param('expList', [0])
236 path.add_module(evtnumbers)
237
238
239def loadGearbox(path, silence_warning=False):
240 """
241 Loads Gearbox module to the path.
242
243 Warning:
244 Should be used in a job with *cosmic event generation only*
245
246 Needed for scripts which only generate cosmic events in order to
247 load the geometry.
248
249 @param path modules are added to this path
250 @param silence_warning stops a verbose warning message if you know you want to use this function
251 """
252
253 if not silence_warning:
254 B2WARNING("""You are overwriting the geometry from the database with Gearbox.
255 This is fine if you're generating cosmic events. But in most other cases you probably don't want this.
256
257 If you're really sure you know what you're doing you can suppress this message with:
258
259 >>> loadGearbox(silence_warning=True)
260
261 """)
262
263 paramloader = register_module('Gearbox')
264 path.add_module(paramloader)
265
266
267def printPrimaryMCParticles(path, **kwargs):
268 """
269 Prints all primary MCParticles, that is particles from
270 the physics generator and not particles created by the simulation
271
272 This is equivalent to `printMCParticles(onlyPrimaries=True, path=path) <printMCParticles>` and additional
273 keyword arguments are just forwarded to that function
274 """
275
276 return printMCParticles(onlyPrimaries=True, path=path, **kwargs)
277
278
279def printMCParticles(onlyPrimaries=False, maxLevel=-1, path=None, *,
280 showProperties=False, showMomenta=False, showVertices=False, showStatus=False, suppressPrint=False):
281 """
282 Prints all MCParticles or just primary MCParticles up to specified level. -1 means no limit.
283
284 By default this will print a tree of just the particle names and their pdg
285 codes in the event, for example ::
286
287 [INFO] Content of MCParticle list
288 ├── e- (11)
289 ├── e+ (-11)
290 ╰── Upsilon(4S) (300553)
291 ├── B+ (521)
292 │ ├── anti-D_0*0 (-10421)
293 │ │ ├── D- (-411)
294 │ │ │ ├── K*- (-323)
295 │ │ │ │ ├── anti-K0 (-311)
296 │ │ │ │ │ ╰── K_S0 (310)
297 │ │ │ │ │ ├── pi+ (211)
298 │ │ │ │ │ │ ╰╶╶ p+ (2212)
299 │ │ │ │ │ ╰── pi- (-211)
300 │ │ │ │ │ ├╶╶ e- (11)
301 │ │ │ │ │ ├╶╶ n0 (2112)
302 │ │ │ │ │ ├╶╶ n0 (2112)
303 │ │ │ │ │ ╰╶╶ n0 (2112)
304 │ │ │ │ ╰── pi- (-211)
305 │ │ │ │ ├╶╶ anti-nu_mu (-14)
306 │ │ │ │ ╰╶╶ mu- (13)
307 │ │ │ │ ├╶╶ nu_mu (14)
308 │ │ │ │ ├╶╶ anti-nu_e (-12)
309 │ │ │ │ ╰╶╶ e- (11)
310 │ │ │ ╰── K_S0 (310)
311 │ │ │ ├── pi0 (111)
312 │ │ │ │ ├── gamma (22)
313 │ │ │ │ ╰── gamma (22)
314 │ │ │ ╰── pi0 (111)
315 │ │ │ ├── gamma (22)
316 │ │ │ ╰── gamma (22)
317 │ │ ╰── pi+ (211)
318 │ ├── mu+ (-13)
319 │ │ ├╶╶ anti-nu_mu (-14)
320 │ │ ├╶╶ nu_e (12)
321 │ │ ╰╶╶ e+ (-11)
322 │ ├── nu_mu (14)
323 │ ╰── gamma (22)
324 ...
325
326
327 There's a distinction between primary and secondary particles. Primary
328 particles are the ones created by the physics generator while secondary
329 particles are ones generated by the simulation of the detector interaction.
330
331 Secondaries are indicated with a dashed line leading to the particle name
332 and if the output is to the terminal they will be printed in red. If
333 ``onlyPrimaries`` is True they will not be included in the tree.
334
335 On demand, extra information on all the particles can be displayed by
336 enabling any of the ``showProperties``, ``showMomenta``, ``showVertices``
337 and ``showStatus`` flags. Enabling all of them will look like
338 this::
339
340 ...
341 ╰── pi- (-211)
342 │ mass=0.14 energy=0.445 charge=-1 lifetime=6.36
343 │ p=(0.257, -0.335, 0.0238) |p|=0.423
344 │ production vertex=(0.113, -0.0531, 0.0156), time=0.00589
345 │ status flags=PrimaryParticle, StableInGenerator, StoppedInDetector
346 │ list index=48
347
348 ╰╶╶ n0 (2112)
349 mass=0.94 energy=0.94 charge=0 lifetime=5.28e+03
350 p=(-0.000238, -0.0127, 0.0116) |p|=0.0172
351 production vertex=(144, 21.9, -1.29), time=39
352 status flags=StoppedInDetector
353 creation process=HadronInelastic
354 list index=66
355
356 The first line of extra information is enabled by ``showProperties``, the
357 second line by ``showMomenta``, the third line by ``showVertices`` and the
358 last two lines by ``showStatus``. Note that all values are given in Belle II
359 standard units, that is GeV, centimeter and nanoseconds.
360
361 The depth of the tree can be limited with the ``maxLevel`` argument: If it's
362 bigger than zero it will limit the tree to the given number of generations.
363 A visual indicator will be added after each particle which would have
364 additional daughters that are skipped due to this limit. An example event
365 with ``maxLevel=3`` is given below. In this case only the tau neutrino and
366 the pion don't have additional daughters. ::
367
368 [INFO] Content of MCParticle list
369 ├── e- (11)
370 ├── e+ (-11)
371 ╰── Upsilon(4S) (300553)
372 ├── B+ (521)
373 │ ├── anti-D*0 (-423) → …
374 │ ├── tau+ (-15) → …
375 │ ╰── nu_tau (16)
376 ╰── B- (-521)
377 ├── D*0 (423) → …
378 ├── K*- (-323) → …
379 ├── K*+ (323) → …
380 ╰── pi- (-211)
381
382 The same information will be stored in the branch ``__MCDecayString__`` of
383 TTree created by `VariablesToNtuple` or `VariablesToEventBasedTree` module.
384 This branch is automatically created when `PrintMCParticles` modules is called.
385 Printing the information on the log message can be suppressed if ``suppressPrint``
386 is True, while the branch ``__MCDecayString__``. This option helps to reduce the
387 size of the log message.
388
389 Parameters:
390 onlyPrimaries (bool): If True show only primary particles, that is particles coming from
391 the generator and not created by the simulation.
392 maxLevel (int): If 0 or less print the whole tree, otherwise stop after n generations
393 showProperties (bool): If True show mass, energy and charge of the particles
394 showMomenta (bool): if True show the momenta of the particles
395 showVertices (bool): if True show production vertex and production time of all particles
396 showStatus (bool): if True show some status information on the particles.
397 For secondary particles this includes creation process.
398 suppressPrint (bool): if True printing the information on the log message is suppressed.
399 Even if True, the branch ``__MCDecayString__`` is created.
400 """
401
402 return path.add_module(
403 "PrintMCParticles",
404 onlyPrimaries=onlyPrimaries,
405 maxLevel=maxLevel,
406 showProperties=showProperties,
407 showMomenta=showMomenta,
408 showVertices=showVertices,
409 showStatus=showStatus,
410 suppressPrint=suppressPrint,
411 )
412
413
414def correctBrems(outputList,
415 inputList,
416 gammaList,
417 maximumAcceptance=3.0,
418 multiplePhotons=False,
419 usePhotonOnlyOnce=True,
420 writeOut=False,
421 path=None):
422 """
423 For each particle in the given ``inputList``, copies it to the ``outputList`` and adds the
424 4-vector of the photon(s) in the ``gammaList`` which has(have) a weighted named relation to
425 the particle's track, set by the ``ECLTrackBremFinder`` module during reconstruction.
426
427 Tip:
428 Since release-08 (proc16 and MC16), `correctBrems` is the recommended way of performing the Bremsstrahlung
429 recovery and is preferred over `correctBremsBelle`. The cuts applied by the ``ECLTrackBremFinder`` module
430 used to be too tight, which is why the Belle-like approach was recommended in the past; they were loosened
431 for proc16 and MC16.
432
433 For the recommended selection of the Bremsstrahlung photons, the recommended values of the parameters
434 below and the studies these are based on, refer to :ref:`b2help-recommendation`
435 (web version: `Performance Recommendations <https://belle2.pages.desy.de/performance/recommendations/>`_),
436 which is kept up to date with the current data-taking and processing campaign.
437
438 Information:
439 A detailed description of how the weights are set can be found directly at the documentation of the
440 `BremsFinder` module.
441
442 Please note that a new particle is always generated, with the old particle and -if found- one or more
443 photons as daughters.
444
445 The ``inputList`` should contain particles with associated tracks. Otherwise, the module will exit with an error.
446
447 The ``gammaList`` should contain photons. Otherwise, the module will exit with an error.
448
449 @param outputList The output particle list name containing the corrected particles
450 @param inputList The initial particle list name containing the particles to correct. *It should already exist.*
451 @param gammaList The photon list containing possibly bremsstrahlung photons; *It should already exist.*
452 @param maximumAcceptance Maximum value of the relation weight. Should be a number between [0,3)
453 @param multiplePhotons Whether to use only one photon (the one with the smallest acceptance) or as many as possible
454 @param usePhotonOnlyOnce If true, each brems candidate is used to correct only the track with the smallest relation weight
455 @param writeOut Whether `RootOutput` module should save the created ``outputList``
456 @param path The module is added to this path
457 """
458
459 import b2bii
460 if b2bii.isB2BII():
461 B2ERROR("The BremsFinder can only be run over Belle II data.")
462
463 bremscorrector = register_module('BremsFinder')
464 bremscorrector.set_name('bremsCorrector_' + outputList)
465 bremscorrector.param('inputList', inputList)
466 bremscorrector.param('outputList', outputList)
467 bremscorrector.param('gammaList', gammaList)
468 bremscorrector.param('maximumAcceptance', maximumAcceptance)
469 bremscorrector.param('multiplePhotons', multiplePhotons)
470 bremscorrector.param('usePhotonOnlyOnce', usePhotonOnlyOnce)
471 bremscorrector.param('writeOut', writeOut)
472 path.add_module(bremscorrector)
473
474
475def copyList(outputListName, inputListName, writeOut=False, path=None):
476 """
477 Copy all Particle indices from input ParticleList to the output ParticleList.
478 Note that the Particles themselves are not copied. The original and copied
479 ParticleLists will point to the same Particles.
480
481 @param ouputListName copied ParticleList
482 @param inputListName original ParticleList to be copied
483 @param writeOut whether RootOutput module should save the created ParticleList
484 @param path modules are added to this path
485 """
486
487 copyLists(outputListName, [inputListName], writeOut, path)
488
489
490def correctBremsBelle(outputListName,
491 inputListName,
492 gammaListName,
493 multiplePhotons=True,
494 angleThreshold=0.05,
495 usePhotonOnlyOnce=False,
496 writeOut=False,
497 path=None):
498 """
499 Run the Belle - like brems finding on the ``inputListName`` of charged particles.
500 Adds all photons in ``gammaListName`` to a copy of the charged particle that are within
501 ``angleThreshold``.
502
503 Warning:
504 Since release-08 (proc16 and MC16), `correctBrems` is preferred over this function. Refer to
505 :ref:`b2help-recommendation`
506 (web version: `Performance Recommendations <https://belle2.pages.desy.de/performance/recommendations/>`_)
507 for the current recommendation on the Bremsstrahlung recovery.
508
509 Tip:
510 If you still use this function, studies by the tau WG show that using a rather wide opening angle (up to
511 0.2 rad) and rather low energetic photons results in good correction.
512 However, this should only serve as a starting point for your own studies
513 because the optimal criteria are likely mode-dependent
514
515 Parameters:
516 outputListName (str): The output charged particle list containing the corrected charged particles
517 inputListName (str): The initial charged particle list containing the charged particles to correct.
518 gammaListName (str): The gammas list containing possibly radiative gammas, should already exist.
519 multiplePhotons (bool): How many photons should be added to the charged particle? nearest one -> False,
520 add all the photons within the cone -> True
521 angleThreshold (float): The maximum angle in radians between the charged particle and the (radiative)
522 gamma to be accepted.
523 writeOut (bool): whether RootOutput module should save the created ParticleList
524 usePhotonOnlyOnce (bool): If true, a photon is used for correction of the closest charged particle in the inputList.
525 If false, a photon is allowed to be used for correction multiple times (Default).
526
527 Warning:
528 One cannot use a photon twice to reconstruct a composite particle. Thus, for example, if ``e+`` and ``e-`` are corrected
529 with a ``gamma``, the pair of ``e+`` and ``e-`` cannot form a ``J/psi -> e+ e-`` candidate.
530
531 path (basf2.Path): modules are added to this path
532 """
533
534 fsrcorrector = register_module('BelleBremRecovery')
535 fsrcorrector.set_name('BelleFSRCorrection_' + outputListName)
536 fsrcorrector.param('inputListName', inputListName)
537 fsrcorrector.param('outputListName', outputListName)
538 fsrcorrector.param('gammaListName', gammaListName)
539 fsrcorrector.param('multiplePhotons', multiplePhotons)
540 fsrcorrector.param('angleThreshold', angleThreshold)
541 fsrcorrector.param('usePhotonOnlyOnce', usePhotonOnlyOnce)
542 fsrcorrector.param('writeOut', writeOut)
543 path.add_module(fsrcorrector)
544
545
546def copyLists(outputListName, inputListNames, writeOut=False, path=None):
547 """
548 Copy all Particle indices from all input ParticleLists to the
549 single output ParticleList.
550 Note that the Particles themselves are not copied.
551 The original and copied ParticleLists will point to the same Particles.
552
553 Duplicates are removed based on the first-come, first-served principle.
554 Therefore, the order of the input ParticleLists matters.
555
556 .. seealso::
557 If you want to select the best duplicate based on another criterion, have
558 a look at the function `mergeListsWithBestDuplicate`.
559
560 .. note::
561 Two particles that differ only by the order of their daughters are
562 considered duplicates and one of them will be removed.
563
564 @param ouputListName copied ParticleList
565 @param inputListName vector of original ParticleLists to be copied
566 @param writeOut whether RootOutput module should save the created ParticleList
567 @param path modules are added to this path
568 """
569
570 pmanipulate = register_module('ParticleListManipulator')
571 pmanipulate.set_name('PListCopy_' + outputListName)
572 pmanipulate.param('outputListName', outputListName)
573 pmanipulate.param('inputListNames', inputListNames)
574 pmanipulate.param('writeOut', writeOut)
575 path.add_module(pmanipulate)
576
577
578def copyParticles(outputListName, inputListName, writeOut=False, path=None):
579 """
580 Create copies of Particles given in the input ParticleList and add them to the output ParticleList.
581
582 The existing relations of the original Particle (or it's (grand-)^n-daughters)
583 are copied as well. Note that only the relation is copied and that the related
584 object is not. Copied particles are therefore related to the *same* object as
585 the original ones.
586
587 @param ouputListName new ParticleList filled with copied Particles
588 @param inputListName input ParticleList with original Particles
589 @param writeOut whether RootOutput module should save the created ParticleList
590 @param path modules are added to this path
591 """
592
593 # first copy original particles to the new ParticleList
594 pmanipulate = register_module('ParticleListManipulator')
595 pmanipulate.set_name('PListCopy_' + outputListName)
596 pmanipulate.param('outputListName', outputListName)
597 pmanipulate.param('inputListNames', [inputListName])
598 pmanipulate.param('writeOut', writeOut)
599 path.add_module(pmanipulate)
600
601 # now replace original particles with their copies
602 pcopy = register_module('ParticleCopier')
603 pcopy.param('inputListNames', [outputListName])
604 path.add_module(pcopy)
605
606
607def cutAndCopyLists(outputListName, inputListNames, cut, writeOut=False, path=None):
608 """
609 Copy candidates from all lists in ``inputListNames`` to
610 ``outputListName`` if they pass ``cut`` (given selection criteria).
611
612 Note:
613 Note that the Particles themselves are not copied.
614 The original and copied ParticleLists will point to the same Particles.
615
616 Example:
617 Require energetic pions safely inside the cdc
618
619 .. code-block:: python
620
621 cutAndCopyLists("pi+:energeticPions", ["pi+:good", "pi+:loose"], "[E > 2] and thetaInCDCAcceptance", path=mypath)
622
623 Warning:
624 You must use square braces ``[`` and ``]`` for conditional statements.
625
626 Parameters:
627 outputListName (str): the new ParticleList name
628 inputListName (list(str)): list of input ParticleList names
629 cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
630 writeOut (bool): whether RootOutput module should save the created ParticleList
631 path (basf2.Path): modules are added to this path
632 """
633
634 pmanipulate = register_module('ParticleListManipulator')
635 pmanipulate.set_name('PListCutAndCopy_' + outputListName)
636 pmanipulate.param('outputListName', outputListName)
637 pmanipulate.param('inputListNames', inputListNames)
638 pmanipulate.param('cut', cut)
639 pmanipulate.param('writeOut', writeOut)
640 path.add_module(pmanipulate)
641
642
643def cutAndCopyList(outputListName, inputListName, cut, writeOut=False, path=None):
644 """
645 Copy candidates from ``inputListName`` to ``outputListName`` if they pass
646 ``cut`` (given selection criteria).
647
648 Note:
649 Note the Particles themselves are not copied.
650 The original and copied ParticleLists will point to the same Particles.
651
652 Example:
653 require energetic pions safely inside the cdc
654
655 .. code-block:: python
656
657 cutAndCopyList("pi+:energeticPions", "pi+:loose", "[E > 2] and thetaInCDCAcceptance", path=mypath)
658
659 Warning:
660 You must use square braces ``[`` and ``]`` for conditional statements.
661
662 Parameters:
663 outputListName (str): the new ParticleList name
664 inputListName (str): input ParticleList name
665 cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
666 writeOut (bool): whether RootOutput module should save the created ParticleList
667 path (basf2.Path): modules are added to this path
668 """
669
670 cutAndCopyLists(outputListName, [inputListName], cut, writeOut, path)
671
672
673def removeTracksForTrackingEfficiencyCalculation(inputListNames, fraction, path=None):
674 """
675 Randomly remove tracks from the provided particle lists to estimate the tracking efficiency.
676 Takes care of the duplicates, if any.
677
678 Parameters:
679 inputListNames (list(str)): input particle list names
680 fraction (float): fraction of particles to be removed randomly
681 path (basf2.Path): module is added to this path
682 """
683
684 trackingefficiency = register_module('TrackingEfficiency')
685 trackingefficiency.param('particleLists', inputListNames)
686 trackingefficiency.param('frac', fraction)
687 path.add_module(trackingefficiency)
688
689
690def scaleTrackMomenta(inputListNames, scale=float('nan'), payloadName="tracking_MomentumScaling", scalingFactorName="central",
691 path=None):
692 """
693 Scale momenta of the particles according to a scaling factor scale.
694 This scaling factor can either be given as constant number or as the name of the payload which contains
695 the variable scale factors.
696 If the particle list contains composite particles, the momenta of the track-based daughters are scaled.
697 Subsequently, the momentum of the mother particle is updated as well.
698
699 Parameters:
700 inputListNames (list(str)): input particle list names
701 scale (float): scaling factor (1.0 -- no scaling). If a valid value is given, it takes precedence over
702 ``payloadName`` and the latter is ignored.
703 payloadName (str): base name of the payload which contains the phase-space dependent scaling factors.
704 The suffix ``_data`` or ``_MC`` is appended automatically depending on whether the module runs on data or MC.
705 Defaults to the standard ``tracking_MomentumScaling`` payload.
706 scalingFactorName (str): name of scaling factor variable in the payload.
707 path (basf2.Path): module is added to this path
708 """
709
710 import b2bii
711 if b2bii.isB2BII():
712 B2ERROR("The tracking momentum scaler can only be run over Belle II data.")
713
714 import math
715 # A valid constant scale takes precedence over the default payload (the two options are mutually exclusive).
716 if not math.isnan(scale) and payloadName == "tracking_MomentumScaling":
717 B2WARNING(f"A constant scale value ({scale}) was provided to scaleTrackMomenta: the default "
718 "'tracking_MomentumScaling' payload from the global tag will NOT be used. "
719 "The constant scale is applied instead.")
720 payloadName = ""
721
722 TrackingMomentumScaleFactors = register_module('TrackingMomentumScaleFactors')
723 TrackingMomentumScaleFactors.param('particleLists', inputListNames)
724 TrackingMomentumScaleFactors.param('scale', scale)
725 TrackingMomentumScaleFactors.param('payloadName', payloadName)
726 TrackingMomentumScaleFactors.param('scalingFactorName', scalingFactorName)
727
728 path.add_module(TrackingMomentumScaleFactors)
729
730
731def correctTrackEnergy(inputListNames, correction=float('nan'), payloadName="tracking_EnergyLoss", correctionName="central",
732 path=None):
733 """
734 Correct the energy loss of tracks according to a 'correction' value.
735 This correction can either be given as constant number or as the name of the payload which contains
736 the variable corrections.
737 If the particle list contains composite particles, the momenta of the track-based daughters are corrected.
738 Subsequently, the momentum of the mother particle is updated as well.
739
740 Parameters:
741 inputListNames (list(str)): input particle list names
742 correction (float): correction value to be subtracted to the particle energy (0.0 -- no correction).
743 If a valid value is given, it takes precedence over ``payloadName`` and the latter is ignored.
744 payloadName (str): base name of the payload which contains the phase-space dependent corrections.
745 The suffix ``_data`` or ``_MC`` is appended automatically depending on whether the module runs on data or MC.
746 Defaults to the standard ``tracking_EnergyLoss`` payload.
747 correctionName (str): name of correction variable in the payload.
748 path (basf2.Path): module is added to this path
749 """
750
751 import b2bii
752 if b2bii.isB2BII():
753 B2ERROR("The tracking energy correction can only be run over Belle II data.")
754
755 import math
756 # A valid constant correction takes precedence over the default payload (the two options are mutually exclusive).
757 if not math.isnan(correction) and payloadName == "tracking_EnergyLoss":
758 B2WARNING(f"A constant correction value ({correction}) was provided to correctTrackEnergy: the default "
759 "'tracking_EnergyLoss' payload from the global tag will NOT be used. "
760 "The constant correction is applied instead.")
761 payloadName = ""
762
763 TrackingEnergyLossCorrection = register_module('TrackingEnergyLossCorrection')
764 TrackingEnergyLossCorrection.param('particleLists', inputListNames)
765 TrackingEnergyLossCorrection.param('correction', correction)
766 TrackingEnergyLossCorrection.param('payloadName', payloadName)
767 TrackingEnergyLossCorrection.param('correctionName', correctionName)
768
769 path.add_module(TrackingEnergyLossCorrection)
770
771
772def smearTrackMomenta(inputListNames, payloadName="", smearingFactorName="smear", path=None):
773 """
774 Smear the momenta of the particles according the values read from the given payload.
775 If the particle list contains composite particles, the momenta of the track-based daughters are smeared.
776 Subsequently, the momentum of the mother particle is updated as well.
777
778 Parameters:
779 inputListNames (list(str)): input particle list names
780 payloadName (str): name of the payload which contains the smearing values
781 smearingFactorName (str): name of smearing factor variable in the payload.
782 path (basf2.Path): module is added to this path
783 """
784
785 TrackingMomentumScaleFactors = register_module('TrackingMomentumScaleFactors')
786 TrackingMomentumScaleFactors.param('particleLists', inputListNames)
787 TrackingMomentumScaleFactors.param('payloadName', payloadName)
788 TrackingMomentumScaleFactors.param('smearingFactorName', smearingFactorName)
789
790 path.add_module(TrackingMomentumScaleFactors)
791
792
793def mergeListsWithBestDuplicate(outputListName,
794 inputListNames,
795 variable,
796 preferLowest=True,
797 writeOut=False,
798 ignoreMotherFlavor=False,
799 path=None):
800 """
801 Merge input ParticleLists into one output ParticleList. Only the best
802 among duplicates is kept. The lowest or highest value (configurable via
803 preferLowest) of the provided variable determines which duplicate is the
804 best.
805
806 @param ouputListName name of merged ParticleList
807 @param inputListName vector of original ParticleLists to be merged
808 @param variable variable to determine best duplicate
809 @param preferLowest whether lowest or highest value of variable should be preferred
810 @param writeOut whether RootOutput module should save the created ParticleList
811 @param ignoreMotherFlavor whether the flavor of the mother particle is ignored when trying to find duplicates
812 @param path modules are added to this path
813 """
814
815 pmanipulate = register_module('ParticleListManipulator')
816 pmanipulate.set_name('PListMerger_' + outputListName)
817 pmanipulate.param('outputListName', outputListName)
818 pmanipulate.param('inputListNames', inputListNames)
819 pmanipulate.param('variable', variable)
820 pmanipulate.param('preferLowest', preferLowest)
821 pmanipulate.param('writeOut', writeOut)
822 pmanipulate.param('ignoreMotherFlavor', ignoreMotherFlavor)
823 path.add_module(pmanipulate)
824
825
826def fillSignalSideParticleList(outputListName, decayString, path):
827 """
828 This function should only be used in the ROE path, that is a path
829 that is executed for each ROE object in the DataStore.
830
831 Example: fillSignalSideParticleList('gamma:sig','B0 -> K*0 ^gamma', roe_path)
832
833 Function will create a ParticleList with name 'gamma:sig' which will be filled
834 with the existing photon Particle, being the second daughter of the B0 candidate
835 to which the ROE object has to be related.
836
837 @param ouputListName name of the created ParticleList
838 @param decayString specify Particle to be added to the ParticleList
839 """
840
841 pload = register_module('SignalSideParticleListCreator')
842 pload.set_name('SSParticleList_' + outputListName)
843 pload.param('particleListName', outputListName)
844 pload.param('decayString', decayString)
845 path.add_module(pload)
846
847
848def fillParticleLists(decayStringsWithCuts, writeOut=False, path=None, enforceFitHypothesis=False,
849 loadPhotonsFromKLM=False):
850 """
851 Creates Particles of the desired types from the corresponding ``mdst`` dataobjects,
852 loads them to the ``StoreArray<Particle>`` and fills the ParticleLists.
853
854 The multiple ParticleLists with their own selection criteria are specified
855 via list tuples (decayString, cut), for example
856
857 .. code-block:: python
858
859 kaons = ('K+:mykaons', 'kaonID>0.1')
860 pions = ('pi+:mypions','pionID>0.1')
861 fillParticleLists([kaons, pions], path=mypath)
862
863 If you are unsure what selection you want, you might like to see the
864 :doc:`StandardParticles` functions.
865
866 The type of the particles to be loaded is specified via the decayString module parameter.
867 The type of the ``mdst`` dataobject that is used as an input is determined from the type of
868 the particle. The following types of the particles can be loaded:
869
870 * charged final state particles (input ``mdst`` type = Tracks)
871 - e+, mu+, pi+, K+, p, deuteron (and charge conjugated particles)
872
873 * neutral final state particles
874 - "gamma" (input ``mdst`` type = ECLCluster)
875 - "K_S0", "Lambda0" (input ``mdst`` type = V0)
876 - "K_L0", "n0" (input ``mdst`` type = KLMCluster or ECLCluster)
877
878 Note:
879 For "K_S0" and "Lambda0" you must specify the daughter ordering.
880
881 For example, to load V0s as :math:`\\Lambda^0\\to p^+\\pi^-` decays from V0s:
882
883 .. code-block:: python
884
885 v0lambdas = ('Lambda0 -> p+ pi-', '0.9 < M < 1.3')
886 fillParticleLists([kaons, pions, v0lambdas], path=mypath)
887
888 Tip:
889 Gammas can also be loaded from KLMClusters by explicitly setting the
890 parameter ``loadPhotonsFromKLM`` to True. However, this should only be
891 done in selected use-cases and the effect should be studied carefully.
892
893 Tip:
894 For "K_L0" it is now possible to load from ECLClusters, to revert to
895 the old (Belle) behavior, you can require ``'isFromKLM > 0'``.
896
897 .. code-block:: python
898
899 klongs = ('K_L0', 'isFromKLM > 0')
900 fillParticleLists([kaons, pions, klongs], path=mypath)
901
902 * Charged kinks final state particles (input ``mdst`` type = Kink)
903
904 Note:
905 To reconstruct charged particle kink you must specify the daughter.
906
907 For example, to load Kinks as :math:`K^- \\to \\pi^-\\pi^0` decays from Kinks:
908
909 .. code-block:: python
910
911 kinkKaons = ('K- -> pi-', yourCut)
912 fillParticleLists([kaons, pions, v0lambdas, kinkKaons], path=mypath)
913
914
915 Parameters:
916 decayStringsWithCuts (list): A list of python ntuples of (decayString, cut).
917 The decay string determines the type of Particle
918 and the name of the ParticleList.
919 If the input MDST type is V0 the whole
920 decay chain needs to be specified, so that
921 the user decides and controls the daughters
922 ' order (e.g. ``K_S0 -> pi+ pi-``).
923 If the input MDST type is Kink the decay chain needs to be specified
924 with only one daughter (e.g. ``K- -> pi-``).
925 The cut is the selection criteria
926 to be added to the ParticleList. It can be an empty string.
927 writeOut (bool): whether RootOutput module should save the created ParticleList
928 path (basf2.Path): modules are added to this path
929 enforceFitHypothesis (bool): If true, Particles will be created only for the tracks which have been fitted
930 using a mass hypothesis of the exact type passed to fillParticleLists().
931 If enforceFitHypothesis is False (the default) the next closest fit hypothesis
932 in terms of mass difference will be used if the fit using exact particle
933 type is not available.
934 loadPhotonsFromKLM (bool): If true, photon candidates will be created from KLMClusters as well.
935 """
936
937 pload = register_module('ParticleLoader')
938 pload.set_name('ParticleLoader_' + 'PLists')
939 pload.param('decayStrings', [decayString for decayString, cut in decayStringsWithCuts])
940 pload.param('writeOut', writeOut)
941 pload.param("enforceFitHypothesis", enforceFitHypothesis)
942 path.add_module(pload)
943
944 from ROOT import Belle2
945 decayDescriptor = Belle2.DecayDescriptor()
946 for decayString, cut in decayStringsWithCuts:
947 if not decayDescriptor.init(decayString):
948 raise ValueError("Invalid decay string")
949 # need to check some logic to unpack possible scenarios
950 if decayDescriptor.getNDaughters() > 0:
951 # ... then we have an actual decay in the decay string which must be a V0 (if more than 1 daughter)
952 # or a kink (if 1 daughter)
953 # the particle loader automatically calls this "V0" or "kink", respectively, so we have to copy over
954 # the list to name/format that user wants
955 if (decayDescriptor.getNDaughters() == 1) and (decayDescriptor.getMother().getLabel() != 'kink'):
956 copyList(decayDescriptor.getMother().getFullName(), decayDescriptor.getMother().getName() + ':kink',
957 writeOut, path)
958 if (decayDescriptor.getNDaughters() > 1) and (decayDescriptor.getMother().getLabel() != 'V0'):
959 copyList(decayDescriptor.getMother().getFullName(), decayDescriptor.getMother().getName() + ':V0', writeOut, path)
960 elif (decayDescriptor.getMother().getLabel() != 'all' and
961 abs(decayDescriptor.getMother().getPDGCode()) != Belle2.Const.neutron.getPDGCode()):
962 # then we have a non-V0/kink particle which the particle loader automatically calls "all"
963 # as with the special V0 and kink cases we have to copy over the list to the name/format requested
964 copyList(decayString, decayDescriptor.getMother().getName() + ':all', writeOut, path)
965
966 # optionally apply a cut
967 if cut != "":
968 applyCuts(decayDescriptor.getMother().getFullName(), cut, path)
969
970 if decayString.startswith("gamma"):
971 # keep KLM-source photons as a experts-only for now: they are loaded by the particle loader,
972 # but the user has to explicitly request them.
973 if not loadPhotonsFromKLM:
974 applyCuts(decayString, 'isFromECL', path)
975
976
977def fillParticleList(decayString, cut, writeOut=False, path=None, enforceFitHypothesis=False,
978 loadPhotonsFromKLM=False):
979 """
980 Creates Particles of the desired type from the corresponding ``mdst`` dataobjects,
981 loads them to the StoreArray<Particle> and fills the ParticleList.
982
983 See also:
984 the :doc:`StandardParticles` functions.
985
986 The type of the particles to be loaded is specified via the decayString module parameter.
987 The type of the ``mdst`` dataobject that is used as an input is determined from the type of
988 the particle. The following types of the particles can be loaded:
989
990 * charged final state particles (input ``mdst`` type = Tracks)
991 - e+, mu+, pi+, K+, p, deuteron (and charge conjugated particles)
992
993 * neutral final state particles
994 - "gamma" (input ``mdst`` type = ECLCluster)
995 - "K_S0", "Lambda0" (input ``mdst`` type = V0)
996 - "K_L0", "n0" (input ``mdst`` type = KLMCluster or ECLCluster)
997
998 Note:
999 For "K_S0" and "Lambda0" you must specify the daughter ordering.
1000
1001 For example, to load V0s as :math:`\\Lambda^0\\to p^+\\pi^-` decays from V0s:
1002
1003 .. code-block:: python
1004
1005 fillParticleList('Lambda0 -> p+ pi-', '0.9 < M < 1.3', path=mypath)
1006
1007 Tip:
1008 Gammas can also be loaded from KLMClusters by explicitly setting the
1009 parameter ``loadPhotonsFromKLM`` to True. However, this should only be
1010 done in selected use-cases and the effect should be studied carefully.
1011
1012 Tip:
1013 For "K_L0" it is now possible to load from ECLClusters, to revert to
1014 the old (Belle) behavior, you can require ``'isFromKLM > 0'``.
1015
1016 .. code-block:: python
1017
1018 fillParticleList('K_L0', 'isFromKLM > 0', path=mypath)
1019
1020 * Charged kinks final state particles (input ``mdst`` type = Kink)
1021
1022 .. note::
1023 To reconstruct charged particle kink you must specify the daughter.
1024
1025 For example, to load Kinks as :math:`K^- \\to \\pi^-\\pi^0` decays from Kinks:
1026
1027 .. code-block:: python
1028
1029 fillParticleList('K- -> pi-', yourCut, path=mypath)
1030
1031
1032 Parameters:
1033 decayString (str): Type of Particle and determines the name of the ParticleList.
1034 If the input MDST type is V0 the whole decay chain needs to be specified, so that
1035 the user decides and controls the daughters' order (e.g. ``K_S0 -> pi+ pi-``).
1036 If the input MDST type is Kink the decay chain needs to be specified
1037 with only one daughter (e.g. ``K- -> pi-``).
1038 cut (str): Particles need to pass these selection criteria to be added to the ParticleList
1039 writeOut (bool): whether RootOutput module should save the created ParticleList
1040 path (basf2.Path): modules are added to this path
1041 enforceFitHypothesis (bool): If true, Particles will be created only for the tracks which have been fitted
1042 using a mass hypothesis of the exact type passed to fillParticleLists().
1043 If enforceFitHypothesis is False (the default) the next closest fit hypothesis
1044 in terms of mass difference will be used if the fit using exact particle
1045 type is not available.
1046 loadPhotonsFromKLM (bool): If true, photon candidates will be created from KLMClusters as well.
1047 """
1048
1049 pload = register_module('ParticleLoader')
1050 pload.set_name('ParticleLoader_' + decayString)
1051 pload.param('decayStrings', [decayString])
1052 pload.param('writeOut', writeOut)
1053 pload.param("enforceFitHypothesis", enforceFitHypothesis)
1054 path.add_module(pload)
1055
1056 # need to check some logic to unpack possible scenarios
1057 from ROOT import Belle2
1058 decayDescriptor = Belle2.DecayDescriptor()
1059 if not decayDescriptor.init(decayString):
1060 raise ValueError("Invalid decay string")
1061 if decayDescriptor.getNDaughters() > 0:
1062 # ... then we have an actual decay in the decay string which must be a V0 (if more than 1 daughter)
1063 # or a kink (if 1 daughter)
1064 # the particle loader automatically calls this "V0" or "kink", respectively, so we have to copy over
1065 # the list to name/format that user wants
1066 if (decayDescriptor.getNDaughters() == 1) and (decayDescriptor.getMother().getLabel() != 'kink'):
1067 copyList(decayDescriptor.getMother().getFullName(), decayDescriptor.getMother().getName() + ':kink',
1068 writeOut, path)
1069 if (decayDescriptor.getNDaughters() > 1) and (decayDescriptor.getMother().getLabel() != 'V0'):
1070 copyList(decayDescriptor.getMother().getFullName(), decayDescriptor.getMother().getName() + ':V0', writeOut,
1071 path)
1072 elif (decayDescriptor.getMother().getLabel() != 'all' and
1073 abs(decayDescriptor.getMother().getPDGCode()) != Belle2.Const.neutron.getPDGCode()):
1074 # then we have a non-V0/kink particle which the particle loader automatically calls "all"
1075 # as with the special V0 and kink cases we have to copy over the list to the name/format requested
1076 copyList(decayString, decayDescriptor.getMother().getName() + ':all', writeOut, path)
1077
1078 # optionally apply a cut
1079 if cut != "":
1080 applyCuts(decayDescriptor.getMother().getFullName(), cut, path)
1081
1082 if decayString.startswith("gamma"):
1083 # keep KLM-source photons as a experts-only for now: they are loaded by the particle loader,
1084 # but the user has to explicitly request them.
1085 if not loadPhotonsFromKLM:
1086 applyCuts(decayString, 'isFromECL', path)
1087
1088
1089def fillParticleListWithTrackHypothesis(decayString,
1090 cut,
1091 hypothesis,
1092 writeOut=False,
1093 enforceFitHypothesis=False,
1094 path=None):
1095 """
1096 As fillParticleList, but if used for a charged FSP, loads the particle with the requested hypothesis if available
1097
1098 @param decayString specifies type of Particles and determines the name of the ParticleList
1099 @param cut Particles need to pass these selection criteria to be added to the ParticleList
1100 @param hypothesis the PDG code of the desired track hypothesis
1101 @param writeOut whether RootOutput module should save the created ParticleList
1102 @param enforceFitHypothesis If true, Particles will be created only for the tracks which have been fitted
1103 using a mass hypothesis of the exact type passed to fillParticleLists().
1104 If enforceFitHypothesis is False (the default) the next closest fit hypothesis
1105 in terms of mass difference will be used if the fit using exact particle
1106 type is not available.
1107 @param path modules are added to this path
1108 """
1109
1110 pload = register_module('ParticleLoader')
1111 pload.set_name('ParticleLoader_' + decayString)
1112 pload.param('decayStrings', [decayString])
1113 pload.param('trackHypothesis', hypothesis)
1114 pload.param('writeOut', writeOut)
1115 pload.param("enforceFitHypothesis", enforceFitHypothesis)
1116 path.add_module(pload)
1117
1118 from ROOT import Belle2
1119 decayDescriptor = Belle2.DecayDescriptor()
1120 if not decayDescriptor.init(decayString):
1121 raise ValueError("Invalid decay string")
1122 if decayDescriptor.getMother().getLabel() != 'all':
1123 # the particle loader automatically calls particle lists of charged FSPs "all"
1124 # so we have to copy over the list to the name/format requested
1125 copyList(decayString, decayDescriptor.getMother().getName() + ':all', writeOut, path)
1126
1127 # apply a cut if a non-empty cut string is provided
1128 if cut != "":
1129 applyCuts(decayString, cut, path)
1130
1131
1132def fillConvertedPhotonsList(decayString, cut, writeOut=False, path=None):
1133 """
1134 Creates photon Particle object for each e+e- combination in the V0 StoreArray.
1135
1136 Note:
1137 You must specify the daughter ordering.
1138
1139 .. code-block:: python
1140
1141 fillConvertedPhotonsList('gamma:converted -> e+ e-', '', path=mypath)
1142
1143 Parameters:
1144 decayString (str): Must be gamma to an e+e- pair. You must specify the daughter ordering.
1145 Will also determine the name of the particleList.
1146 cut (str): Particles need to pass these selection criteria to be added to the ParticleList
1147 writeOut (bool): whether RootOutput module should save the created ParticleList
1148 path (basf2.Path): modules are added to this path
1149
1150 """
1151
1152 import b2bii
1153 if b2bii.isB2BII():
1154 B2ERROR('For Belle converted photons are available in the pre-defined list "gamma:v0mdst".')
1155
1156 pload = register_module('ParticleLoader')
1157 pload.set_name('ParticleLoader_' + decayString)
1158 pload.param('decayStrings', [decayString])
1159 pload.param('addDaughters', True)
1160 pload.param('writeOut', writeOut)
1161 path.add_module(pload)
1162
1163 from ROOT import Belle2
1164 decayDescriptor = Belle2.DecayDescriptor()
1165 if not decayDescriptor.init(decayString):
1166 raise ValueError("Invalid decay string")
1167 if decayDescriptor.getMother().getLabel() != 'V0':
1168 # the particle loader automatically calls converted photons "V0" so we have to copy over
1169 # the list to name/format that user wants
1170 copyList(decayDescriptor.getMother().getFullName(), decayDescriptor.getMother().getName() + ':V0', writeOut, path)
1171
1172 # apply a cut if a non-empty cut string is provided
1173 if cut != "":
1174 applyCuts(decayDescriptor.getMother().getFullName(), cut, path)
1175
1176
1177def fillParticleListFromROE(decayString,
1178 cut,
1179 maskName='all',
1180 sourceParticleListName='',
1181 useMissing=False,
1182 writeOut=False,
1183 path=None):
1184 """
1185 Creates Particle object for each ROE of the desired type found in the
1186 StoreArray<RestOfEvent>, loads them to the StoreArray<Particle>
1187 and fills the ParticleList. If useMissing is True, then the missing
1188 momentum is used instead of ROE.
1189
1190 The type of the particles to be loaded is specified via the decayString module parameter.
1191
1192 @param decayString specifies type of Particles and determines the name of the ParticleList.
1193 Source ROEs can be taken as a daughter list, for example:
1194 'B0:tagFromROE -> B0:signal'
1195 @param cut Particles need to pass these selection criteria to be added to the ParticleList
1196 @param maskName Name of the ROE mask to use
1197 @param sourceParticleListName Use related ROEs to this particle list as a source
1198 @param useMissing Use missing momentum instead of ROE momentum
1199 @param writeOut whether RootOutput module should save the created ParticleList
1200 @param path modules are added to this path
1201 """
1202
1203 pload = register_module('ParticleLoader')
1204 pload.set_name('ParticleLoader_' + decayString)
1205 pload.param('decayStrings', [decayString])
1206 pload.param('writeOut', writeOut)
1207 pload.param('roeMaskName', maskName)
1208 pload.param('useMissing', useMissing)
1209 pload.param('sourceParticleListName', sourceParticleListName)
1210 pload.param('useROEs', True)
1211 path.add_module(pload)
1212
1213 from ROOT import Belle2
1214 decayDescriptor = Belle2.DecayDescriptor()
1215 if not decayDescriptor.init(decayString):
1216 raise ValueError("Invalid decay string")
1217
1218 # apply a cut if a non-empty cut string is provided
1219 if cut != "":
1220 applyCuts(decayDescriptor.getMother().getFullName(), cut, path)
1221
1222
1223def fillParticleListFromDummy(decayString,
1224 mdstIndex=0,
1225 covMatrix=10000.,
1226 treatAsInvisible=True,
1227 writeOut=False,
1228 path=None):
1229 """
1230 Creates a ParticleList and fills it with dummy Particles. For self-conjugated Particles one dummy
1231 Particle is created, for Particles that are not self-conjugated one Particle and one anti-Particle is
1232 created. The four-momentum is set to zero.
1233
1234 The type of the particles to be loaded is specified via the decayString module parameter.
1235
1236 @param decayString specifies type of Particles and determines the name of the ParticleList
1237 @param mdstIndex sets the mdst index of Particles
1238 @param covMatrix sets the value of the diagonal covariance matrix of Particles
1239 @param treatAsInvisible whether treeFitter should treat the Particles as invisible
1240 @param writeOut whether RootOutput module should save the created ParticleList
1241 @param path modules are added to this path
1242 """
1243
1244 pload = register_module('ParticleLoader')
1245 pload.set_name('ParticleLoader_' + decayString)
1246 pload.param('decayStrings', [decayString])
1247 pload.param('useDummy', True)
1248 pload.param('dummyMDSTIndex', mdstIndex)
1249 pload.param('dummyCovMatrix', covMatrix)
1250 pload.param('dummyTreatAsInvisible', treatAsInvisible)
1251 pload.param('writeOut', writeOut)
1252 path.add_module(pload)
1253
1254
1255def fillParticleListFromMC(decayString,
1256 cut,
1257 addDaughters=False,
1258 skipNonPrimaryDaughters=False,
1259 writeOut=False,
1260 path=None,
1261 skipNonPrimary=False,
1262 skipInitial=True):
1263 """
1264 Creates Particle object for each MCParticle of the desired type found in the StoreArray<MCParticle>,
1265 loads them to the StoreArray<Particle> and fills the ParticleList.
1266
1267 The type of the particles to be loaded is specified via the decayString module parameter.
1268
1269 @param decayString specifies type of Particles and determines the name of the ParticleList
1270 @param cut Particles need to pass these selection criteria to be added to the ParticleList
1271 @param addDaughters adds the bottom part of the decay chain of the particle to the datastore and
1272 sets mother-daughter relations
1273 @param skipNonPrimaryDaughters if true, skip non primary daughters, useful to study final state daughter particles
1274 @param writeOut whether RootOutput module should save the created ParticleList
1275 @param path modules are added to this path
1276 @param skipNonPrimary if true, skip non primary particle
1277 @param skipInitial if true, skip initial particles
1278 """
1279
1280 pload = register_module('ParticleLoader')
1281 pload.set_name('ParticleLoader_' + decayString)
1282 pload.param('decayStrings', [decayString])
1283 pload.param('addDaughters', addDaughters)
1284 pload.param('skipNonPrimaryDaughters', skipNonPrimaryDaughters)
1285 pload.param('writeOut', writeOut)
1286 pload.param('useMCParticles', True)
1287 pload.param('skipNonPrimary', skipNonPrimary)
1288 pload.param('skipInitial', skipInitial)
1289 path.add_module(pload)
1290
1291 from ROOT import Belle2
1292 decayDescriptor = Belle2.DecayDescriptor()
1293 if not decayDescriptor.init(decayString):
1294 raise ValueError("Invalid decay string")
1295
1296 # apply a cut if a non-empty cut string is provided
1297 if cut != "":
1298 applyCuts(decayString, cut, path)
1299
1300
1301def fillParticleListsFromMC(decayStringsWithCuts,
1302 addDaughters=False,
1303 skipNonPrimaryDaughters=False,
1304 writeOut=False,
1305 path=None,
1306 skipNonPrimary=False,
1307 skipInitial=True):
1308 """
1309 Creates Particle object for each MCParticle of the desired type found in the StoreArray<MCParticle>,
1310 loads them to the StoreArray<Particle> and fills the ParticleLists.
1311
1312 The types of the particles to be loaded are specified via the (decayString, cut) tuples given in a list.
1313 For example:
1314
1315 .. code-block:: python
1316
1317 kaons = ('K+:gen', '')
1318 pions = ('pi+:gen', 'pionID>0.1')
1319 fillParticleListsFromMC([kaons, pions], path=mypath)
1320
1321 .. tip::
1322 Daughters of ``Lambda0`` are not primary, but ``Lambda0`` is not final state particle.
1323 Thus, when one reconstructs a particle from ``Lambda0``, that is created with
1324 ``addDaughters=True`` and ``skipNonPrimaryDaughters=True``, the particle always has ``isSignal==0``.
1325 Please set options for ``Lambda0`` to use MC-matching variables properly as follows,
1326 ``addDaughters=True`` and ``skipNonPrimaryDaughters=False``.
1327
1328 @param decayString specifies type of Particles and determines the name of the ParticleList
1329 @param cut Particles need to pass these selection criteria to be added to the ParticleList
1330 @param addDaughters adds the bottom part of the decay chain of the particle to the datastore and
1331 sets mother-daughter relations
1332 @param skipNonPrimaryDaughters if true, skip non primary daughters, useful to study final state daughter particles
1333 @param writeOut whether RootOutput module should save the created ParticleList
1334 @param path modules are added to this path
1335 @param skipNonPrimary if true, skip non primary particle
1336 @param skipInitial if true, skip initial particles
1337 """
1338
1339 pload = register_module('ParticleLoader')
1340 pload.set_name('ParticleLoader_' + 'PLists')
1341 pload.param('decayStrings', [decayString for decayString, cut in decayStringsWithCuts])
1342 pload.param('addDaughters', addDaughters)
1343 pload.param('skipNonPrimaryDaughters', skipNonPrimaryDaughters)
1344 pload.param('writeOut', writeOut)
1345 pload.param('useMCParticles', True)
1346 pload.param('skipNonPrimary', skipNonPrimary)
1347 pload.param('skipInitial', skipInitial)
1348 path.add_module(pload)
1349
1350 from ROOT import Belle2
1351 decayDescriptor = Belle2.DecayDescriptor()
1352 for decayString, cut in decayStringsWithCuts:
1353 if not decayDescriptor.init(decayString):
1354 raise ValueError("Invalid decay string")
1355
1356 # apply a cut if a non-empty cut string is provided
1357 if cut != "":
1358 applyCuts(decayString, cut, path)
1359
1360
1361def fillParticleListFromChargedCluster(outputParticleList,
1362 inputParticleList,
1363 cut,
1364 useOnlyMostEnergeticECLCluster=True,
1365 writeOut=False,
1366 path=None):
1367 """
1368 Creates the Particle object from ECLCluster and KLMCluster that are being matched with the Track of inputParticleList.
1369
1370 @param outputParticleList The output ParticleList. Only neutral final state particles are supported.
1371 @param inputParticleList The input ParticleList that is required to have the relation to the Track object.
1372 @param cut Particles need to pass these selection criteria to be added to the ParticleList
1373 @param useOnlyMostEnergeticECLCluster If True, only the most energetic ECLCluster among ones that are matched with the Track is
1374 used. If False, all matched ECLClusters are loaded. The default is True. Regardless of
1375 this option, the KLMCluster is loaded.
1376 @param writeOut whether RootOutput module should save the created ParticleList
1377 @param path modules are added to this path
1378 """
1379
1380 pload = register_module('ParticleLoader')
1381 pload.set_name('ParticleLoader_' + outputParticleList)
1382
1383 pload.param('decayStrings', [outputParticleList])
1384 pload.param('sourceParticleListName', inputParticleList)
1385 pload.param('writeOut', writeOut)
1386 pload.param('loadChargedCluster', True)
1387 pload.param('useOnlyMostEnergeticECLCluster', useOnlyMostEnergeticECLCluster)
1388 path.add_module(pload)
1389
1390 # apply a cut if a non-empty cut string is provided
1391 if cut != "":
1392 applyCuts(outputParticleList, cut, path)
1393
1394
1395def extractParticlesFromROE(particleLists,
1396 signalSideParticleList=None,
1397 maskName='all',
1398 writeOut=False,
1399 path=None):
1400 """
1401 Extract Particle objects that belong to the Rest-Of-Events and fill them into the ParticleLists.
1402 The types of the particles other than those specified by ``particleLists`` are not stored.
1403 If one creates a ROE with ``fillWithMostLikely=True`` via `buildRestOfEvent`, for example,
1404 one should create particleLists for not only ``pi+``, ``gamma``, ``K_L0`` but also other charged final state particles.
1405
1406 When one calls the function in the main path, one has to set the argument ``signalSideParticleList`` and the signal side
1407 ParticleList must have only one candidate.
1408
1409 .. code-block:: python
1410
1411 buildRestOfEvent('B0:sig', fillWithMostLikely=True, path=mypath)
1412
1413 roe_path = create_path()
1414 deadEndPath = create_path()
1415 signalSideParticleFilter('B0:sig', '', roe_path, deadEndPath)
1416
1417 plists = ['%s:in_roe' % ptype for ptype in ['pi+', 'gamma', 'K_L0', 'K+', 'p+', 'e+', 'mu+']]
1418 extractParticlesFromROE(plists, maskName='all', path=roe_path)
1419
1420 # one can analyze these ParticleLists in the roe_path
1421
1422 mypath.for_each('RestOfEvent', 'RestOfEvents', roe_path)
1423
1424 rankByLowest('B0:sig', 'deltaE', numBest=1, path=mypath)
1425 extractParticlesFromROE(plists, signalSideParticleList='B0:sig', maskName='all', path=mypath)
1426
1427 # one can analyze these ParticleLists in the main path
1428
1429
1430 @param particleLists (str or list(str)) Name of output ParticleLists
1431 @param signalSideParticleList (str) Name of signal side ParticleList
1432 @param maskName (str) Name of the ROE mask to be applied on Particles
1433 @param writeOut (bool) whether RootOutput module should save the created ParticleList
1434 @param path (basf2.Path) modules are added to this path
1435 """
1436
1437 if isinstance(particleLists, str):
1438 particleLists = [particleLists]
1439
1440 pext = register_module('ParticleExtractorFromROE')
1441 pext.set_name('ParticleExtractorFromROE_' + '_'.join(particleLists))
1442 pext.param('outputListNames', particleLists)
1443 if signalSideParticleList is not None:
1444 pext.param('signalSideParticleListName', signalSideParticleList)
1445 pext.param('maskName', maskName)
1446 pext.param('writeOut', writeOut)
1447 path.add_module(pext)
1448
1449
1450def applyCuts(list_name, cut, path):
1451 """
1452 Removes particle candidates from ``list_name`` that do not pass ``cut``
1453 (given selection criteria).
1454
1455 Example:
1456 require energetic pions safely inside the cdc
1457
1458 .. code-block:: python
1459
1460 applyCuts("pi+:mypions", "[E > 2] and thetaInCDCAcceptance", path=mypath)
1461
1462 Warning:
1463 You must use square braces ``[`` and ``]`` for conditional statements.
1464
1465 Parameters:
1466 list_name (str): input ParticleList name
1467 cut (str): Candidates that do not pass these selection criteria are removed from the ParticleList
1468 path (basf2.Path): modules are added to this path
1469 """
1470
1471 pselect = register_module('ParticleSelector')
1472 pselect.set_name('ParticleSelector_applyCuts_' + list_name)
1473 pselect.param('decayString', list_name)
1474 pselect.param('cut', cut)
1475 path.add_module(pselect)
1476
1477
1478def applyEventCuts(cut, path, metavariables=None):
1479 """
1480 Removes events that do not pass the ``cut`` (given selection criteria).
1481
1482 Example:
1483 continuum events (in mc only) with more than 5 tracks
1484
1485 .. code-block:: python
1486
1487 applyEventCuts("[nTracks > 5] and [isContinuumEvent], path=mypath)
1488
1489 .. warning::
1490 Only event-based variables are allowed in this function
1491 and only square brackets ``[`` and ``]`` for conditional statements.
1492
1493 Parameters:
1494 cut (str): Events that do not pass these selection criteria are skipped
1495 path (basf2.Path): modules are added to this path
1496 metavariables (list(str)): List of meta variables to be considered in decomposition of cut
1497 """
1498
1499 import b2parser
1500 from variables import variables
1501
1502 def find_vars(t: tuple, var_list: list, meta_list: list) -> None:
1503 """ Recursive helper function to find variable names """
1504 if not isinstance(t, tuple):
1505 return
1506 if t[0] == b2parser.B2ExpressionParser.node_types['IdentifierNode']:
1507 var_list += [t[1]]
1508 return
1509 if t[0] == b2parser.B2ExpressionParser.node_types['FunctionNode']:
1510 meta_list.append(list(t[1:]))
1511 return
1512 for i in t:
1513 if isinstance(i, tuple):
1514 find_vars(i, var_list, meta_list)
1515
1516 def check_variable(var_list: list, metavar_ids: list) -> None:
1517 for var_string in var_list:
1518 # Check if the var_string is alias
1519 orig_name = variables.resolveAlias(var_string)
1520 if orig_name != var_string:
1521 var_list_temp = []
1522 meta_list_temp = []
1523 find_vars(b2parser.parse(orig_name), var_list_temp, meta_list_temp)
1524
1525 check_variable(var_list_temp, metavar_ids)
1526 check_meta(meta_list_temp, metavar_ids)
1527 else:
1528 # Get the variable
1529 var = variables.getVariable(var_string)
1530 if event_var_id not in var.description:
1531 B2ERROR(f'Variable {var_string} is not an event-based variable! "\
1532 "Please check your inputs to the applyEventCuts method!')
1533
1534 def check_meta(meta_list: list, metavar_ids: list) -> None:
1535 for meta_string_list in meta_list:
1536 var_list_temp = []
1537 while meta_string_list[0] in metavar_ids:
1538 # remove special meta variable
1539 meta_string_list.pop(0)
1540 for meta_string in meta_string_list[0].split(","):
1541 find_vars(b2parser.parse(meta_string), var_list_temp, meta_string_list)
1542 if len(meta_string_list) > 0:
1543 meta_string_list.pop(0)
1544 if len(meta_string_list) == 0:
1545 break
1546 if len(meta_string_list) > 1:
1547 meta_list += meta_string_list[1:]
1548 if isinstance(meta_string_list[0], list):
1549 meta_string_list = [element for element in meta_string_list[0]]
1550
1551 check_variable(var_list_temp, metavar_ids)
1552
1553 if len(meta_string_list) == 0:
1554 continue
1555 elif len(meta_string_list) == 1:
1556 var = variables.getVariable(meta_string_list[0])
1557 else:
1558 var = variables.getVariable(meta_string_list[0], meta_string_list[1].split(","))
1559 # Check if the variable's description contains event-based marker
1560 if event_var_id in var.description:
1561 continue
1562 # Throw an error message if non event-based variable is used
1563 B2ERROR(f'Variable {var.name} is not an event-based variable! Please check your inputs to the applyEventCuts method!')
1564
1565 event_var_id = '[Eventbased]'
1566 metavar_ids = ['formula', 'abs',
1567 'cos', 'acos',
1568 'tan', 'atan',
1569 'sin', 'asin',
1570 'exp', 'log', 'log10',
1571 'min', 'max',
1572 'isNAN', 'ifNANgiveX']
1573 if metavariables:
1574 metavar_ids += metavariables
1575
1576 var_list = []
1577 meta_list = []
1578 find_vars(b2parser.parse(cut), var_list=var_list, meta_list=meta_list)
1579
1580 if len(var_list) == 0 and len(meta_list) == 0:
1581 B2WARNING(f'Cut string "{cut}" has no variables for applyEventCuts helper function!')
1582
1583 check_variable(var_list, metavar_ids)
1584 check_meta(meta_list, metavar_ids)
1585
1586 eselect = register_module('VariableToReturnValue')
1587 eselect.param('variable', 'passesEventCut(' + cut + ')')
1588 path.add_module(eselect)
1589 empty_path = create_path()
1590 eselect.if_value('<1', empty_path)
1591
1592
1593def reconstructDecay(decayString,
1594 cut,
1595 dmID=0,
1596 writeOut=False,
1597 path=None,
1598 candidate_limit=None,
1599 ignoreIfTooManyCandidates=True,
1600 chargeConjugation=True,
1601 allowChargeViolation=False):
1602 r"""
1603 Creates new Particles by making combinations of existing Particles - it reconstructs unstable particles via their specified
1604 decay mode, e.g. in form of a :ref:`DecayString`: :code:`D0 -> K- pi+` or :code:`B+ -> anti-D0 pi+`, ... All possible
1605 combinations are created (particles are used only once per candidate) and combinations that pass the specified selection
1606 criteria are saved to a newly created (mother) ParticleList. By default the charge conjugated decay is reconstructed as well
1607 (meaning that the charge conjugated mother list is created as well) but this can be deactivated.
1608
1609 One can use an ``@``-sign to mark a particle as unspecified for inclusive analyses,
1610 e.g. in a DecayString: :code:`'@Xsd -> K+ pi-'`.
1611
1612 .. seealso:: :ref:`Marker_of_unspecified_particle`
1613
1614 .. warning::
1615 The input ParticleLists are typically ordered according to the upstream reconstruction algorithm.
1616 Therefore, if you combine two or more identical particles in the decay chain you should not expect to see the same
1617 distribution for the daughter kinematics as they may be sorted by geometry, momentum etc.
1618
1619 For example, in the decay :code:`D0 -> pi0 pi0` the momentum distributions of the two ``pi0`` s are not identical.
1620 This can be solved by manually randomising the lists before combining.
1621
1622 See Also:
1623
1624 * `Particle combiner how does it work? <https://questions.belle2.org/question/4318/particle-combiner-how-does-it-work/>`_
1625 * `Identical particles in decay chain <https://questions.belle2.org/question/5724/identical-particles-in-decay-chain/>`_
1626
1627 @param decayString :ref:`DecayString` specifying what kind of the decay should be reconstructed
1628 (from the DecayString the mother and daughter ParticleLists are determined)
1629 @param cut created (mother) Particles are added to the mother ParticleList if they
1630 pass give cuts (in VariableManager style) and rejected otherwise
1631 @param dmID user specified decay mode identifier
1632 @param writeOut whether RootOutput module should save the created ParticleList
1633 @param path modules are added to this path
1634 @param candidate_limit Maximum amount of candidates to be reconstructed. If
1635 the number of candidates is exceeded a Warning will be
1636 printed.
1637 By default, all these candidates will be removed and event will be ignored.
1638 This behaviour can be changed by \'ignoreIfTooManyCandidates\' flag.
1639 If no value is given the amount is limited to a sensible
1640 default. A value <=0 will disable this limit and can
1641 cause huge memory amounts so be careful.
1642 @param ignoreIfTooManyCandidates whether event should be ignored or not if number of reconstructed
1643 candidates reaches limit. If event is ignored, no candidates are reconstructed,
1644 otherwise, number of candidates in candidate_limit is reconstructed.
1645 @param chargeConjugation boolean to decide whether charge conjugated mode should be reconstructed as well (on by default)
1646 @param allowChargeViolation whether the decay string needs to conserve the electric charge
1647 """
1648
1649 pmake = register_module('ParticleCombiner')
1650 pmake.set_name('ParticleCombiner_' + decayString)
1651 pmake.param('decayString', decayString)
1652 pmake.param('cut', cut)
1653 pmake.param('decayMode', dmID)
1654 pmake.param('writeOut', writeOut)
1655 if candidate_limit is not None:
1656 pmake.param("maximumNumberOfCandidates", candidate_limit)
1657 pmake.param("ignoreIfTooManyCandidates", ignoreIfTooManyCandidates)
1658 pmake.param('chargeConjugation', chargeConjugation)
1659 pmake.param("allowChargeViolation", allowChargeViolation)
1660 path.add_module(pmake)
1661
1662
1663def combineAllParticles(inputParticleLists, outputList, cut='', writeOut=False, path=None):
1664 """
1665 Creates a new Particle as the combination of all Particles from all
1666 provided inputParticleLists. However, each particle is used only once
1667 (even if duplicates are provided) and the combination has to pass the
1668 specified selection criteria to be saved in the newly created (mother)
1669 ParticleList.
1670
1671 @param inputParticleLists List of input particle lists which are combined to the new Particle
1672 @param outputList Name of the particle combination created with this module
1673 @param cut created (mother) Particle is added to the mother ParticleList if it passes
1674 these given cuts (in VariableManager style) and is rejected otherwise
1675 @param writeOut whether RootOutput module should save the created ParticleList
1676 @param path module is added to this path
1677 """
1678
1679 pmake = register_module('AllParticleCombiner')
1680 pmake.set_name('AllParticleCombiner_' + outputList)
1681 pmake.param('inputListNames', inputParticleLists)
1682 pmake.param('outputListName', outputList)
1683 pmake.param('cut', cut)
1684 pmake.param('writeOut', writeOut)
1685 path.add_module(pmake)
1686
1687
1688def reconstructMissingKlongDecayExpert(decayString,
1689 cut,
1690 dmID=0,
1691 writeOut=False,
1692 path=None,
1693 recoList="_reco"):
1694 """
1695 Creates a list of K_L0's and of B -> K_L0 + X, with X being a fully-reconstructed state.
1696 The K_L0 momentum is determined from kinematic constraints of the two-body B decay into K_L0 and X
1697
1698 @param decayString DecayString specifying what kind of the decay should be reconstructed
1699 (from the DecayString the mother and daughter ParticleLists are determined)
1700 @param cut Particles are added to the K_L0 and B ParticleList if the B candidates
1701 pass the given cuts (in VariableManager style) and rejected otherwise
1702 @param dmID user specified decay mode identifier
1703 @param writeOut whether RootOutput module should save the created ParticleList
1704 @param path modules are added to this path
1705 @param recoList suffix appended to original K_L0 and B ParticleList that identify the newly created K_L0 and B lists
1706 """
1707
1708 pcalc = register_module('KlongMomentumCalculatorExpert')
1709 pcalc.set_name('KlongMomentumCalculatorExpert_' + decayString)
1710 pcalc.param('decayString', decayString)
1711 pcalc.param('writeOut', writeOut)
1712 pcalc.param('recoList', recoList)
1713 path.add_module(pcalc)
1714
1715 rmake = register_module('KlongDecayReconstructorExpert')
1716 rmake.set_name('KlongDecayReconstructorExpert_' + decayString)
1717 rmake.param('decayString', decayString)
1718 rmake.param('cut', cut)
1719 rmake.param('decayMode', dmID)
1720 rmake.param('writeOut', writeOut)
1721 rmake.param('recoList', recoList)
1722 path.add_module(rmake)
1723
1724
1725def setBeamConstrainedMomentum(particleList, decayStringTarget, decayStringDaughters, path=None):
1726 """
1727 Replace the four-momentum of the target Particle by p(beam) - p(selected daughters).
1728 The momentum of the mother Particle will not be changed.
1729
1730 @param particleList mother Particlelist
1731 @param decayStringTarget DecayString specifying the target particle whose momentum
1732 will be updated
1733 @param decayStringDaughters DecayString specifying the daughter particles used to replace
1734 the momentum of the target particle by p(beam)-p(daughters)
1735 """
1736
1737 mod = register_module('ParticleMomentumUpdater')
1738 mod.set_name('ParticleMomentumUpdater' + particleList)
1739 mod.param('particleList', particleList)
1740 mod.param('decayStringTarget', decayStringTarget)
1741 mod.param('decayStringDaughters', decayStringDaughters)
1742 path.add_module(mod)
1743
1744
1745def updateKlongKinematicsExpert(particleList,
1746 writeOut=False,
1747 path=None):
1748 """
1749 Calculates and updates the kinematics of B->K_L0 + something else with same method as
1750 `reconstructMissingKlongDecayExpert`. This helps to revert the kinematics after the vertex fitting.
1751
1752 @param particleList input ParticleList of B meson that decays to K_L0 + X
1753 @param writeOut whether RootOutput module should save the ParticleList
1754 @param path modules are added to this path
1755 """
1756
1757 mod = register_module('KlongMomentumUpdaterExpert')
1758 mod.set_name('KlongMomentumUpdaterExpert_' + particleList)
1759 mod.param('listName', particleList)
1760 mod.param('writeOut', writeOut)
1761 path.add_module(mod)
1762
1763
1764def replaceMass(replacerName, particleLists=None, pdgCode=22, path=None):
1765 """
1766 replaces the mass of the particles inside the given particleLists
1767 with the invariant mass of the particle corresponding to the given pdgCode.
1768
1769 @param particleLists new ParticleList filled with copied Particles
1770 @param pdgCode PDG code for mass reference
1771 @param path modules are added to this path
1772 """
1773
1774 if particleLists is None:
1775 particleLists = []
1776
1777 # first copy original particles to the new ParticleList
1778 pmassupdater = register_module('ParticleMassUpdater')
1779 pmassupdater.set_name('ParticleMassUpdater_' + replacerName)
1780 pmassupdater.param('particleLists', particleLists)
1781 pmassupdater.param('pdgCode', pdgCode)
1782 path.add_module(pmassupdater)
1783
1784
1785def reconstructRecoil(decayString,
1786 cut,
1787 dmID=0,
1788 writeOut=False,
1789 path=None,
1790 candidate_limit=None,
1791 allowChargeViolation=False):
1792 """
1793 Creates new Particles that recoil against the input particles.
1794
1795 For example the decay string M -> D1 D2 D3 will:
1796
1797 - create mother Particle M for each unique combination of D1, D2, D3 Particles
1798 - Particles D1, D2, D3 will be appended as daughters to M
1799 - the 4-momentum of the mother Particle M is given by
1800 p(M) = p(HER) + p(LER) - Sum_i p(Di)
1801
1802 @param decayString DecayString specifying what kind of the decay should be reconstructed
1803 (from the DecayString the mother and daughter ParticleLists are determined)
1804 @param cut created (mother) Particles are added to the mother ParticleList if they
1805 pass give cuts (in VariableManager style) and rejected otherwise
1806 @param dmID user specified decay mode identifier
1807 @param writeOut whether RootOutput module should save the created ParticleList
1808 @param path modules are added to this path
1809 @param candidate_limit Maximum amount of candidates to be reconstructed. If
1810 the number of candidates is exceeded no candidate will be
1811 reconstructed for that event and a Warning will be
1812 printed.
1813 If no value is given the amount is limited to a sensible
1814 default. A value <=0 will disable this limit and can
1815 cause huge memory amounts so be careful.
1816 @param allowChargeViolation whether the decay string needs to conserve the electric charge
1817 """
1818
1819 pmake = register_module('ParticleCombiner')
1820 pmake.set_name('ParticleCombiner_' + decayString)
1821 pmake.param('decayString', decayString)
1822 pmake.param('cut', cut)
1823 pmake.param('decayMode', dmID)
1824 pmake.param('writeOut', writeOut)
1825 pmake.param('recoilParticleType', 1)
1826 if candidate_limit is not None:
1827 pmake.param("maximumNumberOfCandidates", candidate_limit)
1828 pmake.param('allowChargeViolation', allowChargeViolation)
1829 path.add_module(pmake)
1830
1831
1832def reconstructRecoilDaughter(decayString,
1833 cut,
1834 dmID=0,
1835 writeOut=False,
1836 path=None,
1837 candidate_limit=None,
1838 allowChargeViolation=False):
1839 """
1840 Creates new Particles that are daughters of the particle reconstructed in the recoil (always assumed to be the first daughter).
1841
1842 For example the decay string M -> D1 D2 D3 will:
1843
1844 - create mother Particle M for each unique combination of D1, D2, D3 Particles
1845 - Particles D1, D2, D3 will be appended as daughters to M
1846 - the 4-momentum of the mother Particle M is given by
1847 p(M) = p(D1) - Sum_i p(Di), where i>1
1848
1849 @param decayString DecayString specifying what kind of the decay should be reconstructed
1850 (from the DecayString the mother and daughter ParticleLists are determined)
1851 @param cut created (mother) Particles are added to the mother ParticleList if they
1852 pass give cuts (in VariableManager style) and rejected otherwise
1853 @param dmID user specified decay mode identifier
1854 @param writeOut whether RootOutput module should save the created ParticleList
1855 @param path modules are added to this path
1856 @param candidate_limit Maximum amount of candidates to be reconstructed. If
1857 the number of candidates is exceeded no candidate will be
1858 reconstructed for that event and a Warning will be
1859 printed.
1860 If no value is given the amount is limited to a sensible
1861 default. A value <=0 will disable this limit and can
1862 cause huge memory amounts so be careful.
1863 @param allowChargeViolation whether the decay string needs to conserve the electric charge taking into account that the first
1864 daughter is actually the mother
1865 """
1866
1867 pmake = register_module('ParticleCombiner')
1868 pmake.set_name('ParticleCombiner_' + decayString)
1869 pmake.param('decayString', decayString)
1870 pmake.param('cut', cut)
1871 pmake.param('decayMode', dmID)
1872 pmake.param('writeOut', writeOut)
1873 pmake.param('recoilParticleType', 2)
1874 if candidate_limit is not None:
1875 pmake.param("maximumNumberOfCandidates", candidate_limit)
1876 pmake.param('allowChargeViolation', allowChargeViolation)
1877 path.add_module(pmake)
1878
1879
1880def rankByHighest(particleList,
1881 variable,
1882 numBest=0,
1883 outputVariable='',
1884 allowMultiRank=False,
1885 cut='',
1886 overwriteRank=False,
1887 path=None):
1888 """
1889 Ranks particles in the input list by the given variable (highest to lowest), and stores an integer rank for each Particle
1890 in an :b2:var:`extraInfo` field ``${variable}_rank`` starting at 1 (best).
1891 The list is also sorted from best to worst candidate.
1892 All particles are ranked together regardless of particle type.
1893 This can be used to perform a best candidate selection by cutting on the corresponding rank value, or by specifying
1894 a non-zero value for 'numBest'.
1895
1896 .. tip::
1897 Extra-info fields can be accessed by the :b2:var:`extraInfo` metavariable.
1898 These variable names can become clunky, so it's probably a good idea to set an alias.
1899 For example if you rank your B candidates by momentum,
1900
1901 .. code:: python
1902
1903 rankByHighest("B0:myCandidates", "p", path=mypath)
1904 vm.addAlias("momentumRank", "extraInfo(p_rank)")
1905
1906
1907 @param particleList The input ParticleList
1908 @param variable Variable to order Particles by.
1909 @param numBest If not zero, only the $numBest Particles in particleList with rank <= numBest are kept.
1910 @param outputVariable Name for the variable that will be created which contains the rank, Default is '${variable}_rank'.
1911 @param allowMultiRank If true, candidates with the same value will get the same rank.
1912 @param cut Only candidates passing the cut will be ranked. The others will have rank -1
1913 @param overwriteRank If true, the extraInfo of rank is overwritten when the particle has already the extraInfo.
1914 @param path modules are added to this path
1915 """
1916
1917 bcs = register_module('BestCandidateSelection')
1918 bcs.set_name('BestCandidateSelection_' + particleList + '_' + variable)
1919 bcs.param('particleList', particleList)
1920 bcs.param('variable', variable)
1921 bcs.param('numBest', numBest)
1922 bcs.param('outputVariable', outputVariable)
1923 bcs.param('allowMultiRank', allowMultiRank)
1924 bcs.param('cut', cut)
1925 bcs.param('overwriteRank', overwriteRank)
1926 path.add_module(bcs)
1927
1928
1929def rankByLowest(particleList,
1930 variable,
1931 numBest=0,
1932 outputVariable='',
1933 allowMultiRank=False,
1934 cut='',
1935 overwriteRank=False,
1936 path=None):
1937 """
1938 Ranks particles in the input list by the given variable (lowest to highest), and stores an integer rank for each Particle
1939 in an :b2:var:`extraInfo` field ``${variable}_rank`` starting at 1 (best).
1940 The list is also sorted from best to worst candidate.
1941 All particles are ranked together regardless of particle type.
1942 This can be used to perform a best candidate selection by cutting on the corresponding rank value, or by specifying
1943 a non-zero value for 'numBest'.
1944
1945 .. tip::
1946 Extra-info fields can be accessed by the :b2:var:`extraInfo` metavariable.
1947 These variable names can become clunky, so it's probably a good idea to set an alias.
1948 For example if you rank your B candidates by :b2:var:`dM`,
1949
1950 .. code:: python
1951
1952 rankByLowest("B0:myCandidates", "dM", path=mypath)
1953 vm.addAlias("massDifferenceRank", "extraInfo(dM_rank)")
1954
1955
1956 @param particleList The input ParticleList
1957 @param variable Variable to order Particles by.
1958 @param numBest If not zero, only the $numBest Particles in particleList with rank <= numBest are kept.
1959 @param outputVariable Name for the variable that will be created which contains the rank, Default is '${variable}_rank'.
1960 @param allowMultiRank If true, candidates with the same value will get the same rank.
1961 @param cut Only candidates passing the cut will be ranked. The others will have rank -1
1962 @param overwriteRank If true, the extraInfo of rank is overwritten when the particle has already the extraInfo.
1963 @param path modules are added to this path
1964 """
1965
1966 bcs = register_module('BestCandidateSelection')
1967 bcs.set_name('BestCandidateSelection_' + particleList + '_' + variable)
1968 bcs.param('particleList', particleList)
1969 bcs.param('variable', variable)
1970 bcs.param('numBest', numBest)
1971 bcs.param('selectLowest', True)
1972 bcs.param('allowMultiRank', allowMultiRank)
1973 bcs.param('outputVariable', outputVariable)
1974 bcs.param('cut', cut)
1975 bcs.param('overwriteRank', overwriteRank)
1976 path.add_module(bcs)
1977
1978
1979def applyRandomCandidateSelection(particleList, path=None):
1980 """
1981 If there are multiple candidates in the provided particleList, all but one of them are removed randomly.
1982 This is done on a event-by-event basis.
1983
1984 @param particleList ParticleList for which the random candidate selection should be applied
1985 @param path module is added to this path
1986 """
1987
1988 rcs = register_module('BestCandidateSelection')
1989 rcs.set_name('RandomCandidateSelection_' + particleList)
1990 rcs.param('particleList', particleList)
1991 rcs.param('variable', 'random')
1992 rcs.param('selectLowest', False)
1993 rcs.param('allowMultiRank', False)
1994 rcs.param('numBest', 1)
1995 rcs.param('cut', '')
1996 rcs.param('outputVariable', '')
1997 path.add_module(rcs)
1998
1999
2000def printDataStore(eventNumber=-1, path=None):
2001 """
2002 Prints the contents of DataStore in the first event (or a specific event number or all events).
2003 Will list all objects and arrays (including size).
2004
2005 See also:
2006 The command line tool: ``b2file-size``.
2007
2008 Parameters:
2009 eventNumber (int): Print the datastore only for this event. The default
2010 (-1) prints only the first event, 0 means print for all events (can produce large output)
2011 path (basf2.Path): the PrintCollections module is added to this path
2012
2013 Warning:
2014 This will print a lot of output if you print it for all events and process many events.
2015
2016 """
2017
2018 printDS = register_module('PrintCollections')
2019 printDS.param('printForEvent', eventNumber)
2020 path.add_module(printDS)
2021
2022
2023def printVariableValues(list_name, var_names, path):
2024 """
2025 Prints out values of specified variables of all Particles included in given ParticleList. For debugging purposes.
2026
2027 @param list_name input ParticleList name
2028 @param var_names vector of variable names to be printed
2029 @param path modules are added to this path
2030 """
2031
2032 prlist = register_module('ParticlePrinter')
2033 prlist.set_name('ParticlePrinter_' + list_name)
2034 prlist.param('listName', list_name)
2035 prlist.param('fullPrint', False)
2036 prlist.param('variables', var_names)
2037 path.add_module(prlist)
2038
2039
2040def printList(list_name, full, path):
2041 """
2042 Prints the size and executes Particle->print() (if full=True)
2043 method for all Particles in given ParticleList. For debugging purposes.
2044
2045 @param list_name input ParticleList name
2046 @param full execute Particle->print() method for all Particles
2047 @param path modules are added to this path
2048 """
2049
2050 prlist = register_module('ParticlePrinter')
2051 prlist.set_name('ParticlePrinter_' + list_name)
2052 prlist.param('listName', list_name)
2053 prlist.param('fullPrint', full)
2054 path.add_module(prlist)
2055
2056
2057def variablesToNtuple(decayString, variables, treename='variables', filename='ntuple.root', path=None, basketsize=1600,
2058 signalSideParticleList="", filenameSuffix="", useFloat=False, storeEventType=True,
2059 ignoreCommandLineOverride=False):
2060 """
2061 Creates and fills a flat ntuple with the specified variables from the VariableManager.
2062 If a decayString is provided, then there will be one entry per candidate (for particle in list of candidates).
2063 If an empty decayString is provided, there will be one entry per event (useful for trigger studies, etc).
2064
2065 Parameters:
2066 decayString (str): specifies type of Particles and determines the name of the ParticleList
2067 variables (list(str)): the list of variables (which must be registered in the VariableManager)
2068 treename (str): name of the ntuple tree
2069 filename (str): which is used to store the variables
2070 path (basf2.Path): the basf2 path where the analysis is processed
2071 basketsize (int): size of baskets in the output ntuple in bytes
2072 signalSideParticleList (str): The name of the signal-side ParticleList.
2073 Only valid if the module is called in a for_each loop over the RestOfEvent.
2074 filenameSuffix (str): suffix to be appended to the filename before ``.root``.
2075 useFloat (bool): Use single precision (float) instead of double precision (double)
2076 for floating-point numbers.
2077 storeEventType (bool) : if true, the branch __eventType__ is added for the MC event type information.
2078 The information is available from MC16 on.
2079 ignoreCommandLineOverride (bool) : if true, ignore override of file name via command line argument ``-o``.
2080
2081 .. tip:: The output filename can be overridden using the ``-o`` argument of basf2.
2082 """
2083
2084 output = register_module('VariablesToNtuple')
2085 output.set_name('VariablesToNtuple_' + decayString)
2086 output.param('particleList', decayString)
2087 output.param('variables', variables)
2088 output.param('fileName', filename)
2089 output.param('treeName', treename)
2090 output.param('basketSize', basketsize)
2091 output.param('signalSideParticleList', signalSideParticleList)
2092 output.param('fileNameSuffix', filenameSuffix)
2093 output.param('useFloat', useFloat)
2094 output.param('storeEventType', storeEventType)
2095 output.param('ignoreCommandLineOverride', ignoreCommandLineOverride)
2096 path.add_module(output)
2097
2098
2099def variablesToHistogram(decayString,
2100 variables,
2101 variables_2d=None,
2102 filename='ntuple.root',
2103 path=None, *,
2104 directory=None,
2105 prefixDecayString=False,
2106 filenameSuffix="",
2107 ignoreCommandLineOverride=False):
2108 """
2109 Creates and fills a flat ntuple with the specified variables from the VariableManager
2110
2111 Parameters:
2112 decayString (str): specifies type of Particles and determines the name of the ParticleList
2113 variables (list(tuple))): variables + binning which must be registered in the VariableManager
2114 variables_2d (list(tuple)): pair of variables + binning for each which must be registered in the VariableManager
2115 filename (str): which is used to store the variables
2116 path (basf2.Path): the basf2 path where the analysis is processed
2117 directory (str): directory inside the output file where the histograms should be saved.
2118 Useful if you want to have different histograms in the same file to separate them.
2119 prefixDecayString (bool): If True the decayString will be prepended to the directory name to allow for more
2120 programmatic naming of the structure in the file.
2121 filenameSuffix (str): suffix to be appended to the filename before ``.root``.
2122 ignoreCommandLineOverride (bool) : if true, ignore override of file name via command line argument ``-o``.
2123
2124 .. tip:: The output filename can be overridden using the ``-o`` argument of basf2.
2125 """
2126
2127 if variables_2d is None:
2128 variables_2d = []
2129 output = register_module('VariablesToHistogram')
2130 output.set_name('VariablesToHistogram_' + decayString)
2131 output.param('particleList', decayString)
2132 output.param('variables', variables)
2133 output.param('variables_2d', variables_2d)
2134 output.param('fileName', filename)
2135 output.param('fileNameSuffix', filenameSuffix)
2136 output.param('ignoreCommandLineOverride', ignoreCommandLineOverride)
2137 if directory is not None or prefixDecayString:
2138 if directory is None:
2139 directory = ""
2140 if prefixDecayString:
2141 directory = decayString + "_" + directory
2142 output.param("directory", directory)
2143 path.add_module(output)
2144
2145
2146def variablesToExtraInfo(particleList, variables, option=0, path=None):
2147 """
2148 For each particle in the input list the selected variables are saved in an extra-info field with the given name.
2149 Can be used when wanting to save variables before modifying them, e.g. when performing vertex fits.
2150
2151 Parameters:
2152 particleList (str): The input ParticleList
2153 variables (dict[str,str]): Dictionary of Variables (key) and extraInfo names (value).
2154 option (int): Option to overwrite an existing extraInfo. Choose among -1, 0, 1, 2.
2155 An existing extra info with the same name will be overwritten if the new
2156 value is lower / will never be overwritten / will be overwritten if the
2157 new value is higher / will always be overwritten (option = -1/0/1/2).
2158 path (basf2.Path): modules are added to this path
2159 """
2160
2161 mod = register_module('VariablesToExtraInfo')
2162 mod.set_name('VariablesToExtraInfo_' + particleList)
2163 mod.param('particleList', particleList)
2164 mod.param('variables', variables)
2165 mod.param('overwrite', option)
2166 path.add_module(mod)
2167
2168
2169def variablesToDaughterExtraInfo(particleList, decayString, variables, option=0, path=None):
2170 """
2171 For each daughter particle specified via decay string the selected variables (estimated for the mother particle)
2172 are saved in an extra-info field with the given name. In other words, the property of mother is saved as extra-info
2173 to specified daughter particle.
2174
2175 Parameters:
2176 particleList (str): The input ParticleList
2177 decayString (str): Decay string that specifies to which daughter the extra info should be appended
2178 variables (dict[str,str]): Dictionary of Variables (key) and extraInfo names (value).
2179 option (int): Option to overwrite an existing extraInfo. Choose among -1, 0, 1, 2.
2180 An existing extra info with the same name will be overwritten if the new
2181 value is lower / will never be overwritten / will be overwritten if the
2182 new value is higher / will always be overwritten (option = -1/0/1/2).
2183 path (basf2.Path): modules are added to this path
2184 """
2185
2186 mod = register_module('VariablesToExtraInfo')
2187 mod.set_name('VariablesToDaughterExtraInfo_' + particleList)
2188 mod.param('particleList', particleList)
2189 mod.param('decayString', decayString)
2190 mod.param('variables', variables)
2191 mod.param('overwrite', option)
2192 path.add_module(mod)
2193
2194
2195def variablesToEventExtraInfo(particleList, variables, option=0, path=None):
2196 """
2197 For each particle in the input list the selected variables are saved in an event-extra-info field with the given name,
2198 Can be used to save MC truth information, for example, in a ntuple of reconstructed particles.
2199
2200 .. tip::
2201 When the function is called first time not in the main path but in a sub-path e.g. ``roe_path``,
2202 the eventExtraInfo cannot be accessed from the main path because of the shorter lifetime of the event-extra-info field.
2203 If one wants to call the function in a sub-path, one has to call the function in the main path beforehand.
2204
2205 Parameters:
2206 particleList (str): The input ParticleList
2207 variables (dict[str,str]): Dictionary of Variables (key) and extraInfo names (value).
2208 option (int): Option to overwrite an existing extraInfo. Choose among -1, 0, 1, 2.
2209 An existing extra info with the same name will be overwritten if the new
2210 value is lower / will never be overwritten / will be overwritten if the
2211 new value is higher / will always be overwritten (option = -1/0/1/2).
2212 path (basf2.Path): modules are added to this path
2213 """
2214
2215 mod = register_module('VariablesToEventExtraInfo')
2216 mod.set_name('VariablesToEventExtraInfo_' + particleList)
2217 mod.param('particleList', particleList)
2218 mod.param('variables', variables)
2219 mod.param('overwrite', option)
2220 path.add_module(mod)
2221
2222
2223def variableToSignalSideExtraInfo(particleList, varToExtraInfo, path):
2224 """
2225 Write the value of specified variables estimated for the single particle in the input list (has to contain exactly 1
2226 particle) as an extra info to the particle related to current ROE.
2227 Should be used only in the for_each roe path.
2228
2229 Parameters:
2230 particleList (str): The input ParticleList
2231 varToExtraInfo (dict[str,str]): Dictionary of Variables (key) and extraInfo names (value).
2232 path (basf2.Path): modules are added to this path
2233 """
2234
2235 mod = register_module('SignalSideVariablesToExtraInfo')
2236 mod.set_name('SigSideVarToExtraInfo_' + particleList)
2237 mod.param('particleListName', particleList)
2238 mod.param('variableToExtraInfo', varToExtraInfo)
2239 path.add_module(mod)
2240
2241
2242def signalRegion(particleList, cut, path=None, name="isSignalRegion", blind_data=True):
2243 """
2244 Define and blind a signal region.
2245 Per default, the defined signal region is cut out if ran on data.
2246 This function will provide a new variable 'isSignalRegion' as default, which is either 0 or 1 depending on the cut
2247 provided.
2248
2249 Example:
2250 .. code-block:: python
2251
2252 ma.reconstructDecay("B+:sig -> D+ pi0", "Mbc>5.2", path=path)
2253 ma.signalRegion("B+:sig",
2254 "Mbc>5.27 and abs(deltaE)<0.2",
2255 blind_data=True,
2256 path=path)
2257 ma.variablesToNtuples("B+:sig", ["isSignalRegion"], path=path)
2258
2259 Parameters:
2260 particleList (str): The input ParticleList
2261 cut (str): Cut string describing the signal region
2262 path (basf2.Path):: Modules are added to this path
2263 name (str): Name of the Signal region in the variable manager
2264 blind_data (bool): Automatically exclude signal region from data
2265
2266 """
2267
2268 from variables import variables
2269 mod = register_module('VariablesToExtraInfo')
2270 mod.set_name(f'{name}_' + particleList)
2271 mod.param('particleList', particleList)
2272 mod.param('variables', {f"passesCut({cut})": name})
2273 variables.addAlias(name, f"extraInfo({name})")
2274 path.add_module(mod)
2275
2276 # Check if we run on Data
2277 if blind_data:
2278 applyCuts(particleList, f"{name}==0 or isMC==1", path=path)
2279
2280
2281def removeExtraInfo(particleLists=None, removeEventExtraInfo=False, path=None):
2282 """
2283 Removes the ExtraInfo of the given particleLists. If specified (removeEventExtraInfo = True) also the EventExtraInfo is removed.
2284 """
2285
2286 if particleLists is None:
2287 particleLists = []
2288 mod = register_module('ExtraInfoRemover')
2289 mod.param('particleLists', particleLists)
2290 mod.param('removeEventExtraInfo', removeEventExtraInfo)
2291 path.add_module(mod)
2292
2293
2294def signalSideParticleFilter(particleList, selection, roe_path, deadEndPath):
2295 """
2296 Checks if the current ROE object in the for_each roe path (argument roe_path) is related
2297 to the particle from the input ParticleList. Additional selection criteria can be applied.
2298 If ROE is not related to any of the Particles from ParticleList or the Particle doesn't
2299 meet the selection criteria the execution of deadEndPath is started. This path, as the name
2300 suggests should be empty and its purpose is to end the execution of for_each roe path for
2301 the current ROE object.
2302
2303 @param particleList The input ParticleList
2304 @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
2305 @param for_each roe path in which this filter is executed
2306 @param deadEndPath empty path that ends execution of or_each roe path for the current ROE object.
2307 """
2308
2309 mod = register_module('SignalSideParticleFilter')
2310 mod.set_name('SigSideParticleFilter_' + particleList)
2311 mod.param('particleLists', [particleList])
2312 mod.param('selection', selection)
2313 roe_path.add_module(mod)
2314 mod.if_false(deadEndPath)
2315
2316
2317def signalSideParticleListsFilter(particleLists, selection, roe_path, deadEndPath):
2318 """
2319 Checks if the current ROE object in the for_each roe path (argument roe_path) is related
2320 to the particle from the input ParticleList. Additional selection criteria can be applied.
2321 If ROE is not related to any of the Particles from ParticleList or the Particle doesn't
2322 meet the selection criteria the execution of deadEndPath is started. This path, as the name
2323 suggests should be empty and its purpose is to end the execution of for_each roe path for
2324 the current ROE object.
2325
2326 @param particleLists The input ParticleLists
2327 @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
2328 @param for_each roe path in which this filter is executed
2329 @param deadEndPath empty path that ends execution of or_each roe path for the current ROE object.
2330 """
2331
2332 mod = register_module('SignalSideParticleFilter')
2333 mod.set_name('SigSideParticleFilter_' + particleLists[0])
2334 mod.param('particleLists', particleLists)
2335 mod.param('selection', selection)
2336 roe_path.add_module(mod)
2337 mod.if_false(deadEndPath)
2338
2339
2341 decayString,
2342 cut,
2343 dmID=0,
2344 writeOut=False,
2345 path=None,
2346 chargeConjugation=True,
2347):
2348 r"""
2349 Finds and creates a ``ParticleList`` from given decay string.
2350 ``ParticleList`` of daughters with sub-decay is created.
2351
2352 Only the particles made from MCParticle, which can be loaded by `fillParticleListFromMC`, are accepted as daughters.
2353
2354 Only signal particle, which means :b2:var:`isSignal` is equal to 1, is stored. One can use the decay string grammar
2355 to change the behavior of :b2:var:`isSignal`. One can find detailed information in :ref:`DecayString`.
2356
2357 .. tip::
2358 If one uses same sub-decay twice, same particles are registered to a ``ParticleList``. For example,
2359 ``K_S0:pi0pi0 =direct=> [pi0:gg =direct=> gamma:MC gamma:MC] [pi0:gg =direct=> gamma:MC gamma:MC]``.
2360 One can skip the second sub-decay, ``K_S0:pi0pi0 =direct=> [pi0:gg =direct=> gamma:MC gamma:MC] pi0:gg``.
2361
2362 .. tip::
2363 It is recommended to use only primary particles as daughter particles unless you want to explicitly study the secondary
2364 particles. The behavior of MC-matching for secondary particles from a stable particle decay is not guaranteed.
2365 Please consider to use `fillParticleListFromMC` with ``skipNonPrimary=True`` to load daughter particles.
2366 Moreover, it is recommended to load ``K_S0`` and ``Lambda0`` directly from MCParticle by `fillParticleListFromMC` rather
2367 than reconstructing from two pions or a proton-pion pair, because their direct daughters can be the secondary particle.
2368
2369
2370 @param decayString :ref:`DecayString` specifying what kind of the decay should be reconstructed
2371 (from the DecayString the mother and daughter ParticleLists are determined)
2372 @param cut created (mother) Particles are added to the mother ParticleList if they
2373 pass given cuts (in VariableManager style) and rejected otherwise
2374 isSignal==1 is always required by default.
2375 @param dmID user specified decay mode identifier
2376 @param writeOut whether RootOutput module should save the created ParticleList
2377 @param path modules are added to this path
2378 @param chargeConjugation boolean to decide whether charge conjugated mode should be reconstructed as well (on by default)
2379 """
2380
2381 pmake = register_module('ParticleCombinerFromMC')
2382 pmake.set_name('ParticleCombinerFromMC_' + decayString)
2383 pmake.param('decayString', decayString)
2384 pmake.param('cut', cut)
2385 pmake.param('decayMode', dmID)
2386 pmake.param('writeOut', writeOut)
2387 pmake.param('chargeConjugation', chargeConjugation)
2388 path.add_module(pmake)
2389
2390
2391def findMCDecay(
2392 list_name,
2393 decay,
2394 writeOut=False,
2395 appendAllDaughters=False,
2396 skipNonPrimaryDaughters=True,
2397 path=None,
2398):
2399 """
2400 Finds and creates a ``ParticleList`` for all ``MCParticle`` decays matching a given :ref:`DecayString`.
2401 The decay string is required to describe correctly what you want.
2402 In the case of inclusive decays, you can use :ref:`Grammar_for_custom_MCMatching`
2403
2404 The output particles has only the daughter particles written in the given decay string, if
2405 ``appendAllDaughters=False`` (default). If ``appendAllDaughters=True``, all daughters of the matched MCParticle are
2406 appended in the order defined at the MCParticle level. For example,
2407
2408 .. code-block:: python
2409
2410 findMCDecay('B0:Xee', 'B0 -> e+ e- ... ?gamma', appendAllDaughters=False, path=mypath)
2411
2412 The output ParticleList ``B0:Xee`` will match the inclusive ``B0 -> e+ e-`` decays (but neutrinos are not included),
2413 in both cases of ``appendAllDaughters`` is false and true.
2414 If the ``appendAllDaughters=False`` as above example, the ``B0:Xee`` has only two electrons as daughters.
2415 While, if ``appendAllDaughters=True``, all daughters of the matched MCParticles are appended. When the truth decay mode of
2416 the MCParticle is ``B0 -> [K*0 -> K+ pi-] [J/psi -> e+ e-]``, the first daughter of ``B0:Xee`` is ``K*0`` and ``e+``
2417 will be the first daughter of second daughter of ``B0:Xee``.
2418
2419 The option ``skipNonPrimaryDaughters`` only has an effect if ``appendAllDaughters=True``. If ``skipNonPrimaryDaughters=True``,
2420 all primary daughters are appended but the secondary particles are not.
2421
2422 .. tip::
2423 Daughters of ``Lambda0`` are not primary, but ``Lambda0`` is not a final state particle.
2424 In order for the MCMatching to work properly, the daughters of ``Lambda0`` are appended to
2425 ``Lambda0`` regardless of the value of the option ``skipNonPrimaryDaughters``.
2426
2427
2428 @param list_name The output particle list name
2429 @param decay The decay string which you want
2430 @param writeOut Whether `RootOutput` module should save the created ``outputList``
2431 @param skipNonPrimaryDaughters if true, skip non primary daughters, useful to study final state daughter particles
2432 @param appendAllDaughters if true, not only the daughters described in the decay string but all daughters are appended
2433 @param path modules are added to this path
2434 """
2435
2436 decayfinder = register_module('MCDecayFinder')
2437 decayfinder.set_name('MCDecayFinder_' + list_name)
2438 decayfinder.param('listName', list_name)
2439 decayfinder.param('decayString', decay)
2440 decayfinder.param('appendAllDaughters', appendAllDaughters)
2441 decayfinder.param('skipNonPrimaryDaughters', skipNonPrimaryDaughters)
2442 decayfinder.param('writeOut', writeOut)
2443 path.add_module(decayfinder)
2444
2445
2446def summaryOfLists(particleLists, outputFile=None, path=None):
2447 """
2448 Prints out Particle statistics at the end of the job: number of events with at
2449 least one candidate, average number of candidates per event, etc.
2450 If an output file name is provided the statistics is also dumped into a json file with that name.
2451
2452 @param particleLists list of input ParticleLists
2453 @param outputFile output file name (not created by default)
2454 """
2455
2456 particleStats = register_module('ParticleStats')
2457 particleStats.param('particleLists', particleLists)
2458 if outputFile is not None:
2459 particleStats.param('outputFile', outputFile)
2460 path.add_module(particleStats)
2461
2462
2463def matchMCTruth(list_name, path):
2464 """
2465 Performs MC matching (sets relation Particle->MCParticle) for
2466 all particles (and its (grand)^N-daughter particles) in the specified
2467 ParticleList.
2468
2469 @param list_name name of the input ParticleList
2470 @param path modules are added to this path
2471 """
2472
2473 mcMatch = register_module('MCMatcherParticles')
2474 mcMatch.set_name('MCMatch_' + list_name)
2475 mcMatch.param('listName', list_name)
2476 path.add_module(mcMatch)
2477
2478
2479def looseMCTruth(list_name, path):
2480 """
2481 Performs loose MC matching for all particles in the specified
2482 ParticleList.
2483 The difference between loose and normal mc matching algorithm is that
2484 the loose algorithm will find the common mother of the majority of daughter
2485 particles while the normal algorithm finds the common mother of all daughters.
2486 The results of loose mc matching algorithm are stored to the following extraInfo
2487 items:
2488
2489 - looseMCMotherPDG: PDG code of most common mother
2490 - looseMCMotherIndex: 1-based StoreArray<MCParticle> index of most common mother
2491 - looseMCWrongDaughterN: number of daughters that don't originate from the most common mother
2492 - looseMCWrongDaughterPDG: PDG code of the daughter that doesn't originate from the most common mother (only if
2493 looseMCWrongDaughterN = 1)
2494 - looseMCWrongDaughterBiB: 1 if the wrong daughter is Beam Induced Background Particle
2495
2496 @param list_name name of the input ParticleList
2497 @param path modules are added to this path
2498 """
2499
2500 mcMatch = register_module('MCMatcherParticles')
2501 mcMatch.set_name('LooseMCMatch_' + list_name)
2502 mcMatch.param('listName', list_name)
2503 mcMatch.param('looseMCMatching', True)
2504 path.add_module(mcMatch)
2505
2506
2507def matchTagTruth(list_name, path):
2508 """
2509 Performs tag matching for all particles in the specified ParticleList.
2510 The difference between tag and normal mc matching algorithm is that
2511 a (ccbar) tag (usually defined by ccbarFEI) does not correspond to an actual MC particle.
2512 Instead the tag is meant to capture everything except the signal particle.
2513 Requires that normal MC matching has already been performed and set relations.
2514 Also note that low energy photons with energy < 0.1 GeV and ISR are ignored.
2515 The results of (ccbar) tag matching algorithm are stored to the following extraInfo items:
2516 - ccbarTagSignal: 1st digit is status of signal particle, 2nd digit is Nleft-1, 3rd digit is NextraFSP.
2517 - ccbarTagMCpdg: PDG code of (charm) hadron outside tag (signal side).
2518 - ccbarTagMCpdgMother: PDG code of the mother of the (charm) hadron outside tag (signal side).
2519 - ccbarTagNleft: number of particles (composites have priority) left outisde tag.
2520 - ccbarTagNextraFSP: number of extra FSP particles attached to the tag.
2521 - ccbarTagSignalStatus: status of the targeted signal side particle.
2522 - ccbarTagNwoMC: number of daughters without MC match.
2523 - ccbarTagNwoMCMother: number of daughters without MC mother.
2524 - ccbarTagNnoAllMother: number of daughters without common allmother.
2525 - ccbarTagNmissGamma: number of daughters with missing gamma mc error.
2526 - ccbarTagNmissNeutrino: number of daughters with missing neutrino mc error.
2527 - ccbarTagNdecayInFlight: number of daughters with decay in flight mc error.
2528 - ccbarTagNsevereMCError: number of daughters with severe mc error.
2529 - ccbarTagNmissRecoDaughters: number of daughters with any mc error.
2530 - ccbarTagNleft2ndPDG: PDG of one particle left additionally to the signal particle.
2531 - ccbarTagAllMotherPDG: PDG code of the allmother (Z0 or virtual photon).
2532
2533 @param list_name name of the input ParticleList
2534 @param path modules are added to this path
2535 """
2536
2537 mcMatch = register_module('MCMatcherParticles')
2538 mcMatch.set_name('ccbarTagMatch_' + list_name)
2539 mcMatch.param('listName', list_name)
2540 mcMatch.param('ccbarTagMatching', True)
2541 path.add_module(mcMatch)
2542
2543
2544def buildRestOfEvent(target_list_name, inputParticlelists=None,
2545 fillWithMostLikely=True,
2546 chargedPIDPriors=None, path=None):
2547 """
2548 Creates for each Particle in the given ParticleList a RestOfEvent
2549 dataobject and makes basf2 relation between them. User can provide additional
2550 particle lists with a different particle hypothesis like ['K+:good, e+:good'], etc.
2551
2552 @param target_list_name name of the input ParticleList
2553 @param inputParticlelists list of user-defined input particle list names, which serve
2554 as source of particles to build the ROE, the FSP particles from
2555 target_list_name are automatically excluded from the ROE object
2556 @param fillWithMostLikely By default the module uses the most likely particle mass hypothesis for charged particles
2557 based on the PID likelihood. Turn this behavior off if you want to configure your own
2558 input particle lists.
2559 @param chargedPIDPriors The prior PID fractions, that are used to regulate the
2560 amount of certain charged particle species, should be a list of
2561 six floats if not None. The order of particle types is
2562 the following: [e-, mu-, pi-, K-, p+, d+]
2563 @param path modules are added to this path
2564 """
2565
2566 if inputParticlelists is None:
2567 inputParticlelists = []
2568 fillParticleList('pi+:all', '', path=path)
2569 if fillWithMostLikely:
2570 from stdCharged import stdMostLikely
2571 stdMostLikely(chargedPIDPriors, '_roe', path=path)
2572 inputParticlelists = [f'{ptype}:mostlikely_roe' for ptype in ['K+', 'p+', 'e+', 'mu+']]
2573 import b2bii
2574 if not b2bii.isB2BII():
2575 fillParticleList('gamma:all', '', path=path)
2576 fillParticleList('K_L0:roe_default', 'isFromKLM > 0', path=path)
2577 inputParticlelists += ['pi+:all', 'gamma:all', 'K_L0:roe_default']
2578 else:
2579 inputParticlelists += ['pi+:all', 'gamma:mdst']
2580 roeBuilder = register_module('RestOfEventBuilder')
2581 roeBuilder.set_name('ROEBuilder_' + target_list_name)
2582 roeBuilder.param('particleList', target_list_name)
2583 roeBuilder.param('particleListsInput', inputParticlelists)
2584 roeBuilder.param('mostLikely', fillWithMostLikely)
2585 path.add_module(roeBuilder)
2586
2587
2588def buildNestedRestOfEvent(target_list_name, maskName='all', path=None):
2589 """
2590 Creates for each Particle in the given ParticleList a RestOfEvent
2591 @param target_list_name name of the input ParticleList
2592 @param mask_name name of the ROEMask to be used
2593 @param path modules are added to this path
2594 """
2595
2596 roeBuilder = register_module('RestOfEventBuilder')
2597 roeBuilder.set_name('NestedROEBuilder_' + target_list_name)
2598 roeBuilder.param('particleList', target_list_name)
2599 roeBuilder.param('nestedROEMask', maskName)
2600 roeBuilder.param('createNestedROE', True)
2601 path.add_module(roeBuilder)
2602
2603
2604def buildRestOfEventFromMC(target_list_name, inputParticlelists=None, path=None):
2605 """
2606 Creates for each Particle in the given ParticleList a RestOfEvent
2607 @param target_list_name name of the input ParticleList
2608 @param inputParticlelists list of input particle list names, which serve
2609 as a source of particles to build ROE, the FSP particles from
2610 target_list_name are excluded from ROE object
2611 @param path modules are added to this path
2612 """
2613
2614 if inputParticlelists is None:
2615 inputParticlelists = []
2616 if (len(inputParticlelists) == 0):
2617 # Type of particles to use for ROEBuilder
2618 # K_S0 and Lambda0 are added here because some of them have interacted
2619 # with the detector material
2620 types = ['gamma', 'e+', 'mu+', 'pi+', 'K+', 'p+', 'K_L0',
2621 'n0', 'nu_e', 'nu_mu', 'nu_tau',
2622 'K_S0', 'Lambda0']
2623 for t in types:
2624 fillParticleListFromMC(f"{t}:roe_default_gen", 'mcPrimary > 0 and nDaughters == 0',
2625 True, True, path=path)
2626 inputParticlelists += [f"{t}:roe_default_gen"]
2627 roeBuilder = register_module('RestOfEventBuilder')
2628 roeBuilder.set_name('MCROEBuilder_' + target_list_name)
2629 roeBuilder.param('particleList', target_list_name)
2630 roeBuilder.param('particleListsInput', inputParticlelists)
2631 roeBuilder.param('fromMC', True)
2632 path.add_module(roeBuilder)
2633
2634
2635def appendROEMask(list_name,
2636 mask_name,
2637 trackSelection,
2638 eclClusterSelection,
2639 klmClusterSelection='',
2640 path=None):
2641 """
2642 Loads the ROE object of a particle and creates a ROE mask with a specific name. It applies
2643 selection criteria for tracks and eclClusters which will be used by variables in ROEVariables.cc.
2644
2645 - append a ROE mask with all tracks in ROE coming from the IP region
2646
2647 .. code-block:: python
2648
2649 appendROEMask('B+:sig', 'IPtracks', '[dr < 2] and [abs(dz) < 5]', path=mypath)
2650
2651 - append a ROE mask with only ECL-based particles that pass as good photon candidates
2652
2653 .. code-block:: python
2654
2655 goodPhotons = 'inCDCAcceptance and clusterErrorTiming < 1e6 and [clusterE1E9 > 0.4 or E > 0.075]'
2656 appendROEMask('B+:sig', 'goodROEGamma', '', goodPhotons, path=mypath)
2657
2658
2659 @param list_name name of the input ParticleList
2660 @param mask_name name of the appended ROEMask
2661 @param trackSelection decay string for the track-based particles in ROE
2662 @param eclClusterSelection decay string for the ECL-based particles in ROE
2663 @param klmClusterSelection decay string for the KLM-based particles in ROE
2664 @param path modules are added to this path
2665 """
2666
2667 roeMask = register_module('RestOfEventInterpreter')
2668 roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + mask_name)
2669 roeMask.param('particleList', list_name)
2670 roeMask.param('ROEMasks', [(mask_name, trackSelection, eclClusterSelection, klmClusterSelection)])
2671 path.add_module(roeMask)
2672
2673
2674def appendROEMasks(list_name, mask_tuples, path=None):
2675 """
2676 Loads the ROE object of a particle and creates a ROE mask with a specific name. It applies
2677 selection criteria for track-, ECL- and KLM-based particles which will be used by ROE variables.
2678
2679 The multiple ROE masks with their own selection criteria are specified
2680 via list of tuples (mask_name, trackParticleSelection, eclParticleSelection, klmParticleSelection) or
2681 (mask_name, trackSelection, eclClusterSelection) in case with fractions.
2682
2683 - Example for two tuples, one with and one without fractions
2684
2685 .. code-block:: python
2686
2687 ipTracks = ('IPtracks', '[dr < 2] and [abs(dz) < 5]', '', '')
2688 goodPhotons = 'inCDCAcceptance and [clusterErrorTiming < 1e6] and [clusterE1E9 > 0.4 or E > 0.075]'
2689 goodROEGamma = ('ROESel', '[dr < 2] and [abs(dz) < 5]', goodPhotons, '')
2690 goodROEKLM = ('IPtracks', '[dr < 2] and [abs(dz) < 5]', '', 'nKLMClusterTrackMatches == 0')
2691 appendROEMasks('B+:sig', [ipTracks, goodROEGamma, goodROEKLM], path=mypath)
2692
2693 @param list_name name of the input ParticleList
2694 @param mask_tuples array of ROEMask list tuples to be appended
2695 @param path modules are added to this path
2696 """
2697
2698 compatible_masks = []
2699 for mask in mask_tuples:
2700 # add empty KLM-based selection if it's absent:
2701 if len(mask) == 3:
2702 compatible_masks += [(*mask, '')]
2703 else:
2704 compatible_masks += [mask]
2705 roeMask = register_module('RestOfEventInterpreter')
2706 roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + 'MaskList')
2707 roeMask.param('particleList', list_name)
2708 roeMask.param('ROEMasks', compatible_masks)
2709 path.add_module(roeMask)
2710
2711
2712def updateROEMask(list_name,
2713 mask_name,
2714 trackSelection,
2715 eclClusterSelection='',
2716 klmClusterSelection='',
2717 path=None):
2718 """
2719 Update an existing ROE mask by applying additional selection cuts for
2720 tracks and/or clusters.
2721
2722 See function `appendROEMask`!
2723
2724 @param list_name name of the input ParticleList
2725 @param mask_name name of the ROEMask to update
2726 @param trackSelection decay string for the track-based particles in ROE
2727 @param eclClusterSelection decay string for the ECL-based particles in ROE
2728 @param klmClusterSelection decay string for the KLM-based particles in ROE
2729 @param path modules are added to this path
2730 """
2731
2732 roeMask = register_module('RestOfEventInterpreter')
2733 roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + mask_name)
2734 roeMask.param('particleList', list_name)
2735 roeMask.param('ROEMasks', [(mask_name, trackSelection, eclClusterSelection, klmClusterSelection)])
2736 roeMask.param('update', True)
2737 path.add_module(roeMask)
2738
2739
2740def updateROEMasks(list_name, mask_tuples, path):
2741 """
2742 Update existing ROE masks by applying additional selection cuts for tracks
2743 and/or clusters.
2744
2745 The multiple ROE masks with their own selection criteria are specified
2746 via list tuples (mask_name, trackSelection, eclClusterSelection, klmClusterSelection)
2747
2748 See function `appendROEMasks`!
2749
2750 @param list_name name of the input ParticleList
2751 @param mask_tuples array of ROEMask list tuples to be appended
2752 @param path modules are added to this path
2753 """
2754
2755 compatible_masks = []
2756 for mask in mask_tuples:
2757 # add empty KLM-based selection if it's absent:
2758 if len(mask) == 3:
2759 compatible_masks += [(*mask, '')]
2760 else:
2761 compatible_masks += [mask]
2762
2763 roeMask = register_module('RestOfEventInterpreter')
2764 roeMask.set_name('RestOfEventInterpreter_' + list_name + '_' + 'MaskList')
2765 roeMask.param('particleList', list_name)
2766 roeMask.param('ROEMasks', compatible_masks)
2767 roeMask.param('update', True)
2768 path.add_module(roeMask)
2769
2770
2771def keepInROEMasks(list_name, mask_names, cut_string, path=None):
2772 """
2773 This function is used to apply particle list specific cuts on one or more ROE masks (track or eclCluster).
2774 With this function one can KEEP the tracks/eclclusters used in particles from provided particle list.
2775 This function should be executed only in the for_each roe path for the current ROE object.
2776
2777 To avoid unnecessary computation, the input particle list should only contain particles from ROE
2778 (use cut 'isInRestOfEvent == 1'). To update the ECLCluster masks, the input particle list should be a photon
2779 particle list (e.g. 'gamma:someLabel'). To update the Track masks, the input particle list should be a charged
2780 pion particle list (e.g. 'pi+:someLabel').
2781
2782 Updating a non-existing mask will create a new one.
2783
2784 - keep only those tracks that were used in provided particle list
2785
2786 .. code-block:: python
2787
2788 keepInROEMasks('pi+:goodTracks', 'mask', '', path=mypath)
2789
2790 - keep only those clusters that were used in provided particle list and pass a cut, apply to several masks
2791
2792 .. code-block:: python
2793
2794 keepInROEMasks('gamma:goodClusters', ['mask1', 'mask2'], 'E > 0.1', path=mypath)
2795
2796
2797 @param list_name name of the input ParticleList
2798 @param mask_names array of ROEMasks to be updated
2799 @param cut_string decay string with which the mask will be updated
2800 @param path modules are added to this path
2801 """
2802
2803 updateMask = register_module('RestOfEventUpdater')
2804 updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2805 updateMask.param('particleList', list_name)
2806 updateMask.param('updateMasks', mask_names)
2807 updateMask.param('cutString', cut_string)
2808 updateMask.param('discard', False)
2809 path.add_module(updateMask)
2810
2811
2812def discardFromROEMasks(list_name, mask_names, cut_string, path=None):
2813 """
2814 This function is used to apply particle list specific cuts on one or more ROE masks (track or eclCluster).
2815 With this function one can DISCARD the tracks/eclclusters used in particles from provided particle list.
2816 This function should be executed only in the for_each roe path for the current ROE object.
2817
2818 To avoid unnecessary computation, the input particle list should only contain particles from ROE
2819 (use cut 'isInRestOfEvent == 1'). To update the ECLCluster masks, the input particle list should be a photon
2820 particle list (e.g. 'gamma:someLabel'). To update the Track masks, the input particle list should be a charged
2821 pion particle list (e.g. 'pi+:someLabel').
2822
2823 Updating a non-existing mask will create a new one.
2824
2825 - discard tracks that were used in provided particle list
2826
2827 .. code-block:: python
2828
2829 discardFromROEMasks('pi+:badTracks', 'mask', '', path=mypath)
2830
2831 - discard clusters that were used in provided particle list and pass a cut, apply to several masks
2832
2833 .. code-block:: python
2834
2835 discardFromROEMasks('gamma:badClusters', ['mask1', 'mask2'], 'E < 0.1', path=mypath)
2836
2837
2838 @param list_name name of the input ParticleList
2839 @param mask_names array of ROEMasks to be updated
2840 @param cut_string decay string with which the mask will be updated
2841 @param path modules are added to this path
2842 """
2843
2844 updateMask = register_module('RestOfEventUpdater')
2845 updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2846 updateMask.param('particleList', list_name)
2847 updateMask.param('updateMasks', mask_names)
2848 updateMask.param('cutString', cut_string)
2849 updateMask.param('discard', True)
2850 path.add_module(updateMask)
2851
2852
2853def optimizeROEWithV0(list_name, mask_names, cut_string, path=None):
2854 """
2855 This function is used to apply particle list specific cuts on one or more ROE masks for Tracks.
2856 It is possible to optimize the ROE selection by treating tracks from V0's separately, meaning,
2857 taking V0's 4-momentum into account instead of 4-momenta of tracks. A cut for only specific V0's
2858 passing it can be applied.
2859
2860 The input particle list should be a V0 particle list: K_S0 ('K_S0:someLabel', ''),
2861 Lambda ('Lambda:someLabel', '') or converted photons ('gamma:someLabel').
2862
2863 Updating a non-existing mask will create a new one.
2864
2865 - treat tracks from K_S0 inside mass window separately, replace track momenta with K_S0 momentum
2866
2867 .. code-block:: python
2868
2869 optimizeROEWithV0('K_S0:opt', 'mask', '0.450 < M < 0.550', path=mypath)
2870
2871 @param list_name name of the input ParticleList
2872 @param mask_names array of ROEMasks to be updated
2873 @param cut_string decay string with which the mask will be updated
2874 @param path modules are added to this path
2875 """
2876
2877 updateMask = register_module('RestOfEventUpdater')
2878 updateMask.set_name('RestOfEventUpdater_' + list_name + '_masks')
2879 updateMask.param('particleList', list_name)
2880 updateMask.param('updateMasks', mask_names)
2881 updateMask.param('cutString', cut_string)
2882 path.add_module(updateMask)
2883
2884
2885def updateROEUsingV0Lists(target_particle_list, mask_names, default_cleanup=True, selection_cuts=None,
2886 apply_mass_fit=False, fitter='treefit', path=None):
2887 """
2888 This function creates V0 particle lists (photons, :math:`K^0_S` and :math:`\\Lambda^0`)
2889 and it uses V0 candidates to update the Rest Of Event, which is associated to the target particle list.
2890 It is possible to apply a standard or customized selection and mass fit to the V0 candidates.
2891
2892
2893 @param target_particle_list name of the input ParticleList
2894 @param mask_names array of ROE masks to be applied
2895 @param default_cleanup if True, predefined cuts will be applied on the V0 lists
2896 @param selection_cuts a single string of selection cuts or tuple of three strings (photon_cuts, K_S0_cuts, Lambda0_cuts),
2897 which will be applied to the V0 lists. These cuts will have a priority over the default ones.
2898 @param apply_mass_fit if True, a mass fit will be applied to the V0 particles
2899 @param fitter string, that represent a fitter choice: "treefit" for TreeFitter and "kfit" for KFit
2900 @param path modules are added to this path
2901 """
2902
2903 roe_path = create_path()
2904 deadEndPath = create_path()
2905 signalSideParticleFilter(target_particle_list, '', roe_path, deadEndPath)
2906
2907 if (default_cleanup and selection_cuts is None):
2908 B2INFO("Using default cleanup in updateROEUsingV0Lists.")
2909 selection_cuts = 'abs(dM) < 0.1 '
2910 selection_cuts += 'and daughter(0,particleID) > 0.2 and daughter(1,particleID) > 0.2 '
2911 selection_cuts += 'and daughter(0,thetaInCDCAcceptance) and daughter(1,thetaInCDCAcceptance)'
2912 if (selection_cuts is None or selection_cuts == ''):
2913 B2INFO("No cleanup in updateROEUsingV0Lists.")
2914 selection_cuts = ('True', 'True', 'True')
2915 if (isinstance(selection_cuts, str)):
2916 selection_cuts = (selection_cuts, selection_cuts, selection_cuts)
2917 # The isInRestOfEvent variable will be applied on FSPs of composite particles automatically:
2918 roe_cuts = 'isInRestOfEvent > 0'
2919 fillConvertedPhotonsList('gamma:v0_roe -> e+ e-', f'{selection_cuts[0]} and {roe_cuts}',
2920 path=roe_path)
2921 fillParticleList('K_S0:v0_roe -> pi+ pi-', f'{selection_cuts[1]} and {roe_cuts}',
2922 path=roe_path)
2923 fillParticleList('Lambda0:v0_roe -> p+ pi-', f'{selection_cuts[2]} and {roe_cuts}',
2924 path=roe_path)
2925 fitter = fitter.lower()
2926 if (fitter != 'treefit' and fitter != 'kfit'):
2927 B2WARNING('Argument "fitter" in updateROEUsingV0Lists has only "treefit" and "kfit" options, '
2928 f'but "{fitter}" was provided! TreeFitter will be used instead.')
2929 fitter = 'treefit'
2930 from vertex import kFit, treeFit
2931 for v0 in ['gamma:v0_roe', 'K_S0:v0_roe', 'Lambda0:v0_roe']:
2932 if (apply_mass_fit and fitter == 'kfit'):
2933 kFit(v0, conf_level=0.0, fit_type='massvertex', path=roe_path)
2934 if (apply_mass_fit and fitter == 'treefit'):
2935 treeFit(v0, conf_level=0.0, massConstraint=[v0.split(':')[0]], path=roe_path)
2936 optimizeROEWithV0(v0, mask_names, '', path=roe_path)
2937 path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
2938
2939
2940def printROEInfo(mask_names=None, full_print=False,
2941 unpackComposites=True, path=None):
2942 """
2943 This function prints out the information for the current ROE, so it should only be used in the for_each path.
2944 It prints out basic ROE object info.
2945
2946 If mask names are provided, specific information for those masks will be printed out.
2947
2948 It is also possible to print out all particles in a given mask if the
2949 'full_print' is set to True.
2950
2951 @param mask_names array of ROEMask names for printing out info
2952 @param unpackComposites if true, replace composite particles by their daughters
2953 @param full_print print out particles in mask
2954 @param path modules are added to this path
2955 """
2956
2957 if mask_names is None:
2958 mask_names = []
2959 printMask = register_module('RestOfEventPrinter')
2960 printMask.set_name('RestOfEventPrinter')
2961 printMask.param('maskNames', mask_names)
2962 printMask.param('fullPrint', full_print)
2963 printMask.param('unpackComposites', unpackComposites)
2964 path.add_module(printMask)
2965
2966
2967def buildContinuumSuppression(list_name, roe_mask, ipprofile_fit=False, path=None):
2968 """
2969 Creates for each Particle in the given ParticleList a ContinuumSuppression
2970 dataobject and makes basf2 relation between them.
2971
2972 :param list_name: name of the input ParticleList
2973 :param roe_mask: name of the ROE mask
2974 :param ipprofile_fit: turn on vertex fit of input tracks with IP profile constraint
2975 :param path: modules are added to this path
2976 """
2977
2978 qqBuilder = register_module('ContinuumSuppressionBuilder')
2979 qqBuilder.set_name('QQBuilder_' + list_name)
2980 qqBuilder.param('particleList', list_name)
2981 qqBuilder.param('ROEMask', roe_mask)
2982 qqBuilder.param('performIPProfileFit', ipprofile_fit)
2983 path.add_module(qqBuilder)
2984
2985
2986def removeParticlesNotInLists(lists_to_keep, path):
2987 """
2988 Removes all Particles that are not in a given list of ParticleLists (or daughters of those).
2989 All relations from/to Particles, daughter indices, and other ParticleLists are fixed.
2990
2991 @param lists_to_keep Keep the Particles and their daughters in these ParticleLists.
2992 @param path modules are added to this path
2993 """
2994
2995 mod = register_module('RemoveParticlesNotInLists')
2996 mod.param('particleLists', lists_to_keep)
2997 path.add_module(mod)
2998
2999
3000def inclusiveBtagReconstruction(upsilon_list_name, bsig_list_name, btag_list_name, input_lists_names, path):
3001 """
3002 Reconstructs Btag from particles in given ParticleLists which do not share any final state particles (mdstSource) with Bsig.
3003
3004 @param upsilon_list_name Name of the ParticleList to be filled with 'Upsilon(4S) -> B:sig anti-B:tag'
3005 @param bsig_list_name Name of the Bsig ParticleList
3006 @param btag_list_name Name of the Bsig ParticleList
3007 @param input_lists_names List of names of the ParticleLists which are used to reconstruct Btag from
3008 """
3009
3010 btag = register_module('InclusiveBtagReconstruction')
3011 btag.set_name('InclusiveBtagReconstruction_' + bsig_list_name)
3012 btag.param('upsilonListName', upsilon_list_name)
3013 btag.param('bsigListName', bsig_list_name)
3014 btag.param('btagListName', btag_list_name)
3015 btag.param('inputListsNames', input_lists_names)
3016 path.add_module(btag)
3017
3018
3019def selectDaughters(particle_list_name, decay_string, path):
3020 """
3021 Redefine the Daughters of a particle: select from decayString
3022
3023 @param particle_list_name input particle list
3024 @param decay_string for selecting the Daughters to be preserved
3025 """
3026
3027 seld = register_module('SelectDaughters')
3028 seld.set_name('SelectDaughters_' + particle_list_name)
3029 seld.param('listName', particle_list_name)
3030 seld.param('decayString', decay_string)
3031 path.add_module(seld)
3032
3033
3034def markDuplicate(particleList, prioritiseV0, path):
3035 """
3036 Call DuplicateVertexMarker to find duplicate particles in a list and
3037 flag the ones that should be kept
3038
3039 @param particleList input particle list
3040 @param prioritiseV0 if true, give V0s a higher priority
3041 """
3042
3043 markdup = register_module('DuplicateVertexMarker')
3044 markdup.param('particleList', particleList)
3045 markdup.param('prioritiseV0', prioritiseV0)
3046 path.add_module(markdup)
3047
3048
3049PI0ETAVETO_COUNTER = 0
3050
3051
3052def oldwritePi0EtaVeto(
3053 particleList,
3054 decayString,
3055 workingDirectory='.',
3056 pi0vetoname='Pi0_Prob',
3057 etavetoname='Eta_Prob',
3058 downloadFlag=True,
3059 selection='',
3060 path=None
3061):
3062 """
3063 Give pi0/eta probability for hard photon.
3064
3065 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.
3066
3067 The current default weight files are optimised using MC9.
3068 The input variables are as below. Aliases are set to some variables during training.
3069
3070 * M: pi0/eta candidates Invariant mass
3071 * lowE: soft photon energy in lab frame
3072 * cTheta: soft photon ECL cluster's polar angle
3073 * Zmva: soft photon output of MVA using Zernike moments of the cluster
3074 * minC2Hdist: soft photon distance from eclCluster to nearest point on nearest Helix at the ECL cylindrical radius
3075
3076 If you don't have weight files in your workingDirectory,
3077 these files are downloaded from database to your workingDirectory automatically.
3078 Please refer to analysis/examples/tutorials/B2A306-B02RhoGamma-withPi0EtaVeto.py
3079 about how to use this function.
3080
3081 NOTE:
3082 Please don't use following ParticleList names elsewhere:
3083
3084 ``gamma:HARDPHOTON``, ``pi0:PI0VETO``, ``eta:ETAVETO``,
3085 ``gamma:PI0SOFT + str(PI0ETAVETO_COUNTER)``, ``gamma:ETASOFT + str(PI0ETAVETO_COUNTER)``
3086
3087 Please don't use ``lowE``, ``cTheta``, ``Zmva``, ``minC2Hdist`` as alias elsewhere.
3088
3089 @param particleList The input ParticleList
3090 @param decayString specify Particle to be added to the ParticleList
3091 @param workingDirectory The weight file directory
3092 @param downloadFlag whether download default weight files or not
3093 @param pi0vetoname extraInfo name of pi0 probability
3094 @param etavetoname extraInfo name of eta probability
3095 @param selection Selection criteria that Particle needs meet in order for for_each ROE path to continue
3096 @param path modules are added to this path
3097 """
3098
3099 import b2bii
3100 if b2bii.isB2BII():
3101 B2ERROR("The old pi0 / eta veto is not suitable for Belle analyses.")
3102
3103 import os
3104 import basf2_mva
3105
3106 global PI0ETAVETO_COUNTER
3107
3108 if PI0ETAVETO_COUNTER == 0:
3109 from variables import variables
3110 variables.addAlias('lowE', 'daughter(1,E)')
3111 variables.addAlias('cTheta', 'daughter(1,clusterTheta)')
3112 variables.addAlias('Zmva', 'daughter(1,clusterZernikeMVA)')
3113 variables.addAlias('minC2Tdist', 'daughter(1,minC2TDist)')
3114 variables.addAlias('cluNHits', 'daughter(1,clusterNHits)')
3115 variables.addAlias('E9E21', 'daughter(1,clusterE9E21)')
3116
3117 PI0ETAVETO_COUNTER = PI0ETAVETO_COUNTER + 1
3118
3119 roe_path = create_path()
3120
3121 deadEndPath = create_path()
3122
3123 signalSideParticleFilter(particleList, selection, roe_path, deadEndPath)
3124
3125 fillSignalSideParticleList('gamma:HARDPHOTON', decayString, path=roe_path)
3126
3127 pi0softname = 'gamma:PI0SOFT'
3128 etasoftname = 'gamma:ETASOFT'
3129 softphoton1 = pi0softname + str(PI0ETAVETO_COUNTER)
3130 softphoton2 = etasoftname + str(PI0ETAVETO_COUNTER)
3131
3132 fillParticleList(
3133 softphoton1,
3134 '[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]',
3135 path=roe_path)
3136 applyCuts(softphoton1, 'abs(clusterTiming)<120', path=roe_path)
3137 fillParticleList(
3138 softphoton2,
3139 '[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]',
3140 path=roe_path)
3141 applyCuts(softphoton2, 'abs(clusterTiming)<120', path=roe_path)
3142
3143 reconstructDecay('pi0:PI0VETO -> gamma:HARDPHOTON ' + softphoton1, '', path=roe_path)
3144 reconstructDecay('eta:ETAVETO -> gamma:HARDPHOTON ' + softphoton2, '', path=roe_path)
3145
3146 if not os.path.isdir(workingDirectory):
3147 os.mkdir(workingDirectory)
3148 B2INFO('oldwritePi0EtaVeto: ' + workingDirectory + ' has been created as workingDirectory.')
3149
3150 if not os.path.isfile(workingDirectory + '/pi0veto.root'):
3151 if downloadFlag:
3152 basf2_mva.download('Pi0VetoIdentifier', workingDirectory + '/pi0veto.root')
3153 B2INFO('oldwritePi0EtaVeto: pi0veto.root has been downloaded from database to workingDirectory.')
3154
3155 if not os.path.isfile(workingDirectory + '/etaveto.root'):
3156 if downloadFlag:
3157 basf2_mva.download('EtaVetoIdentifier', workingDirectory + '/etaveto.root')
3158 B2INFO('oldwritePi0EtaVeto: etaveto.root has been downloaded from database to workingDirectory.')
3159
3160 roe_path.add_module('MVAExpert', listNames=['pi0:PI0VETO'], extraInfoName='Pi0Veto',
3161 identifier=workingDirectory + '/pi0veto.root')
3162 roe_path.add_module('MVAExpert', listNames=['eta:ETAVETO'], extraInfoName='EtaVeto',
3163 identifier=workingDirectory + '/etaveto.root')
3164
3165 rankByHighest('pi0:PI0VETO', 'extraInfo(Pi0Veto)', numBest=1, path=roe_path)
3166 rankByHighest('eta:ETAVETO', 'extraInfo(EtaVeto)', numBest=1, path=roe_path)
3167
3168 variableToSignalSideExtraInfo('pi0:PI0VETO', {'extraInfo(Pi0Veto)': pi0vetoname}, path=roe_path)
3169 variableToSignalSideExtraInfo('eta:ETAVETO', {'extraInfo(EtaVeto)': etavetoname}, path=roe_path)
3170
3171 path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
3172
3173
3174def writePi0EtaVeto(
3175 particleList,
3176 decayString,
3177 mode='standardMC16rd',
3178 selection='',
3179 path=None,
3180 suffix='',
3181 hardParticle='gamma',
3182 pi0PayloadNameOverride=None,
3183 pi0SoftPhotonCutOverride=None,
3184 etaPayloadNameOverride=None,
3185 etaSoftPhotonCutOverride=None,
3186 requireSoftPhotonIsInROE=False,
3187 pi0Selection='',
3188 etaSelection=''
3189):
3190 """
3191 Give pi0/eta probability for hard photon.
3192
3193 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.
3194 For MC15rd/MC16rd weight files, the BtoXGamma skim is applied during the MVA training.
3195
3196 The current default weight files are for MC16rd. The weight files for MC15rd/MC12 are still available.
3197
3198 The input variables of the mva training for pi0 veto using MC16rd are:
3199
3200 * M: Invariant mass of pi0 candidates
3201 * cosHelicityAngleMomentum: Cosine of angle between momentum difference of the photons in the pi0 rest frame
3202 and momentum of pi0 in lab frame
3203 * daughter(1,E): soft photon energy in lab frame
3204 * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
3205 * daughter(1,clusterLAT): soft photon lateral energy distribution
3206 * daughter(1,beamBackgroundSuppression): soft photon beam background suppression MVA output
3207 * daughter(1,fakePhotonSuppression): soft photon fake photon suppression MVA output
3208
3209 The input variables of the mva training for eta veto using MC16rd are:
3210
3211 * M: Invariant mass of eta candidates
3212 * cosHelicityAngleMomentum: Cosine of angle between momentum difference of the photons in the eta rest frame
3213 and momentum of eta in lab frame
3214 * daughter(1,E): soft photon energy in lab frame
3215 * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
3216 * daughter(1,clusterLAT): soft photon lateral energy distribution
3217 * daughter(1,clusterNHits): soft photon total crystal weights sum(w_i) with w_i<=1
3218 * daughter(1,clusterE1E9): soft photon ratio between energies of central crystal and inner 3x3 crystals
3219 * daughter(1,clusterE9E21): soft photon ratio of energies in inner 3x3 crystals and 5x5 crystals without corners
3220 * daughter(1,clusterSecondMoment): soft photon second moment
3221 * daughter(1,clusterAbsZernikeMoment40): soft photon Zernike moment 40
3222 * daughter(1,clusterAbsZernikeMoment51): soft photon Zernike moment 51
3223 * daughter(1,beamBackgroundSuppression): soft photon beam background suppression MVA output
3224 * daughter(1,fakePhotonSuppression): soft photon fake photon suppression MVA output
3225
3226
3227 The input variables of the mva training for pi0 veto using MC15rd are:
3228
3229 * M: Invariant mass of pi0 candidates
3230 * cosHelicityAngleMomentum: Cosine of angle between momentum difference of the photons in the pi0 rest frame
3231 and momentum of pi0 in lab frame
3232 * daughter(1,E): soft photon energy in lab frame
3233 * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
3234 * daughter(1,clusterLAT): soft photon lateral energy distribution
3235
3236 The input variables of the mva training for eta veto using MC15rd are:
3237
3238 * M: Invariant mass of eta candidates
3239 * cosHelicityAngleMomentum: Cosine of angle between momentum difference of the photons in the eta rest frame
3240 and momentum of eta in lab frame
3241 * daughter(1,E): soft photon energy in lab frame
3242 * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
3243 * daughter(1,clusterLAT): soft photon lateral energy distribution
3244 * daughter(1,clusterNHits): soft photon total crystal weights sum(w_i) with w_i<=1
3245 * daughter(1,clusterE1E9): soft photon ratio between energies of central crystal and inner 3x3 crystals
3246 * daughter(1,clusterE9E21): soft photon ratio of energies in inner 3x3 crystals and 5x5 crystals without corners
3247 * daughter(1,clusterSecondMoment): soft photon second moment
3248 * daughter(1,clusterAbsZernikeMoment40): soft photon Zernike moment 40
3249 * daughter(1,clusterAbsZernikeMoment51): soft photon Zernike moment 51
3250
3251 The input variables of the mva training using MC12 are:
3252
3253 * M: Invariant mass of pi0/eta candidates
3254 * daughter(1,E): soft photon energy in lab frame
3255 * daughter(1,clusterTheta): soft photon ECL cluster's polar angle
3256 * daughter(1,minC2TDist): soft photon distance from eclCluster to nearest point on nearest Helix at the ECL cylindrical radius
3257 * daughter(1,clusterZernikeMVA): soft photon output of MVA using Zernike moments of the cluster
3258 * daughter(1,clusterNHits): soft photon total crystal weights sum(w_i) with w_i<=1
3259 * daughter(1,clusterE9E21): soft photon ratio of energies in inner 3x3 crystals and 5x5 crystals without corners
3260 * cosHelicityAngleMomentum: Cosine of angle between momentum difference of the photons in the pi0/eta rest frame
3261 and momentum of pi0/eta in lab frame
3262
3263 The following strings are available for mode:
3264
3265 * standard: loose energy cut and no clusterNHits cut are applied to soft photon
3266 * tight: tight energy cut and no clusterNHits cut are applied to soft photon
3267 * cluster: loose energy cut and clusterNHits cut are applied to soft photon
3268 * both: tight energy cut and clusterNHits cut are applied to soft photon
3269 * standardMC15rd: loose energy cut is applied to soft photon and the weight files are trained using MC15rd
3270 * tightMC15rd: tight energy cut is applied to soft photon and the weight files are trained using MC15rd
3271 * standardMC16rd: loose energy cut is applied to soft photon and the weight files are trained using MC16rd
3272 * tightMC16rd: tight energy cut is applied to soft photon and the weight files are trained using MC16rd
3273
3274 The final probability of the pi0/eta veto is stored as an extraInfo. If no suffix is set it can be obtained from the variables
3275 `pi0Prob`/`etaProb`. Otherwise, it is available as '{Pi0, Eta}ProbOrigin', '{Pi0, Eta}ProbTightEnergyThreshold', '{Pi0,
3276 Eta}ProbLargeClusterSize', '{Pi0, Eta}ProbTightEnergyThresholdAndLargeClusterSize', '{Pi0, Eta}ProbOriginMC15rd', or
3277 '{Pi0, Eta}ProbTightEnergyThresholdMC15rd' for the six modes described above, with the chosen suffix appended. If one would
3278 like to call this veto twice in one script, add suffix in the second time!
3279 The second highest probability of the pi0/eta veto also is stored as an extraInfo, with a prefix of 'second' to the previous
3280 ones, e.g. secondPi0ProbOrigin{suffix}. This can be used to do validation/systematics study.
3281
3282 NOTE:
3283 Please don't use following ParticleList names elsewhere:
3284
3285 ``gamma:HardPhoton``,
3286 ``gamma:Pi0Soft + ListName + '_' + particleList.replace(':', '_')``,
3287 ``gamma:EtaSoft + ListName + '_' + particleList.replace(':', '_')``,
3288 ``pi0:EtaVeto + ListName``,
3289 ``eta:EtaVeto + ListName``
3290
3291 @param particleList the input ParticleList
3292 @param decayString specify Particle to be added to the ParticleList
3293 @param mode choose one mode out of 'standardMC16rd', 'tightMC16rd', 'standardMC15rd', 'tightMC15rd',
3294 'standard', 'tight', 'cluster' and 'both'
3295 @param selection selection criteria that Particle needs meet in order for for_each ROE path to continue
3296 @param path modules are added to this path
3297 @param suffix optional suffix to be appended to the usual extraInfo name
3298 @param hardParticle particle name which is used to calculate the pi0/eta probability (default is gamma)
3299 @param pi0PayloadNameOverride specify the payload name of pi0 veto only if one wants to use non-default one. (default is None)
3300 @param pi0SoftPhotonCutOverride specify the soft photon selection criteria of pi0 veto only if one wants to use non-default one.
3301 (default is None)
3302 @param etaPayloadNameOverride specify the payload name of eta veto only if one wants to use non-default one. (default is None)
3303 @param etaSoftPhotonCutOverride specify the soft photon selection criteria of eta veto only if one wants to use non-default one.
3304 (default is None)
3305 @param requireSoftPhotonIsInROE specify if the soft photons used to build pi0 and eta candidates have to be in the current ROE
3306 or not. Default is False, i.e. all soft photons in the event are used.
3307 @param pi0Selection Selection for the pi0 reconstruction. Default is "".
3308 @param etaSelection Selection for the eta reconstruction. Default is "".
3309 """
3310
3311 import b2bii
3312 if b2bii.isB2BII():
3313 B2ERROR("The pi0 / eta veto is not suitable for Belle analyses.")
3314
3315 if (requireSoftPhotonIsInROE):
3316 B2WARNING("Requiring the soft photon to being in the ROE was not done for the MVA training. "
3317 "Please check the results carefully.")
3318 showWarning = False
3319
3320 if (mode == 'standardMC15rd' or mode == 'tightMC15rd'):
3321 if (pi0Selection != '[0.03 < M < 0.23]' or etaSelection != '[0.25 < M < 0.75]'):
3322 showWarning = True
3323 else:
3324 if (pi0Selection != '' or etaSelection != ''):
3325 showWarning = True
3326 if showWarning:
3327 B2WARNING(
3328 "Selection criteria for the pi0 or the eta during reconstructDecay differ from those used during the MVA training. "
3329 "You may get NAN value. Please check the results carefully.")
3330
3331 renameSuffix = False
3332
3333 for module in path.modules():
3334 if module.type() == "SubEvent" and not renameSuffix:
3335 for subpath in [p.values for p in module.available_params() if p.name == "path"]:
3336 if renameSuffix:
3337 break
3338 for submodule in subpath.modules():
3339 if f'{hardParticle}:HardPhoton{suffix}' in submodule.name():
3340 suffix += '_0'
3341 B2WARNING("Same extension already used in writePi0EtaVeto, append '_0'")
3342 renameSuffix = True
3343 break
3344
3345 roe_path = create_path()
3346 deadEndPath = create_path()
3347 signalSideParticleFilter(particleList, selection, roe_path, deadEndPath)
3348 fillSignalSideParticleList(f'{hardParticle}:HardPhoton{suffix}', decayString, path=roe_path)
3349
3350 dictListName = {'standard': 'Origin',
3351 'tight': 'TightEnergyThreshold',
3352 'cluster': 'LargeClusterSize',
3353 'both': 'TightEnrgyThresholdAndLargeClusterSize',
3354 'standardMC15rd': 'OriginMC15rd',
3355 'tightMC15rd': 'TightEnergyThresholdMC15rd',
3356 'standardMC16rd': 'OriginMC16rd',
3357 'tightMC16rd': 'TightEnergyThresholdMC16rd'}
3358
3359 dictPi0EnergyCut = {
3360 'standard': '[[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3361 'tight': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]',
3362 'cluster': '[[clusterReg==1 and E>0.025] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3363 'both': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]',
3364 'standardMC15rd': '[[clusterReg==1 and E>0.0225] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3365 'tightMC15rd': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]',
3366 'standardMC16rd': '[[clusterReg==1 and E>0.0225] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3367 'tightMC16rd': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]'}
3368
3369 dictEtaEnergyCut = {
3370 'standard': '[[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]]',
3371 'tight': '[[clusterReg==1 and E>0.06] or [clusterReg==2 and E>0.06] or [clusterReg==3 and E>0.06]]',
3372 'cluster': '[[clusterReg==1 and E>0.035] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.03]]',
3373 'both': '[[clusterReg==1 and E>0.06] or [clusterReg==2 and E>0.06] or [clusterReg==3 and E>0.06]]',
3374 'standardMC15rd': '[[clusterReg==1 and E>0.0225] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3375 'tightMC15rd': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]',
3376 'standardMC16rd': '[[clusterReg==1 and E>0.0225] or [clusterReg==2 and E>0.02] or [clusterReg==3 and E>0.02]]',
3377 'tightMC16rd': '[[clusterReg==1 and E>0.03] or [clusterReg==2 and E>0.03] or [clusterReg==3 and E>0.04]]'}
3378
3379 dictNHitsTimingCut = {'standard': 'clusterNHits >= 0 and abs(clusterTiming)<clusterErrorTiming',
3380 'tight': 'clusterNHits >= 0 and abs(clusterTiming)<clusterErrorTiming',
3381 'cluster': 'clusterNHits >= 2 and abs(clusterTiming)<clusterErrorTiming',
3382 'both': 'clusterNHits >= 2 and abs(clusterTiming)<clusterErrorTiming',
3383 'standardMC15rd': 'clusterNHits > 1.5 and abs(clusterTiming) < 200',
3384 'tightMC15rd': 'clusterNHits > 1.5 and abs(clusterTiming) < 200',
3385 'standardMC16rd': 'clusterNHits > 1.5 and abs(clusterTiming) < 200',
3386 'tightMC16rd': 'clusterNHits > 1.5 and abs(clusterTiming) < 200'}
3387
3388 dictPi0PayloadName = {'standard': 'Pi0VetoIdentifierStandard',
3389 'tight': 'Pi0VetoIdentifierWithHigherEnergyThreshold',
3390 'cluster': 'Pi0VetoIdentifierWithLargerClusterSize',
3391 'both': 'Pi0VetoIdentifierWithHigherEnergyThresholdAndLargerClusterSize',
3392 'standardMC15rd': 'Pi0VetoIdentifierStandardMC15rd',
3393 'tightMC15rd': 'Pi0VetoIdentifierWithHigherEnergyThresholdMC15rd',
3394 'standardMC16rd': 'Pi0VetoIdentifierStandardMC16rd',
3395 'tightMC16rd': 'Pi0VetoIdentifierWithHigherEnergyThresholdMC16rd'}
3396
3397 dictEtaPayloadName = {'standard': 'EtaVetoIdentifierStandard',
3398 'tight': 'EtaVetoIdentifierWithHigherEnergyThreshold',
3399 'cluster': 'EtaVetoIdentifierWithLargerClusterSize',
3400 'both': 'EtaVetoIdentifierWithHigherEnergyThresholdAndLargerClusterSize',
3401 'standardMC15rd': 'EtaVetoIdentifierStandardMC15rd',
3402 'tightMC15rd': 'EtaVetoIdentifierWithHigherEnergyThresholdMC15rd',
3403 'standardMC16rd': 'EtaVetoIdentifierStandardMC16rd',
3404 'tightMC16rd': 'EtaVetoIdentifierWithHigherEnergyThresholdMC16rd'}
3405
3406 dictPi0ExtraInfoName = {'standard': 'Pi0ProbOrigin',
3407 'tight': 'Pi0ProbTightEnergyThreshold',
3408 'cluster': 'Pi0ProbLargeClusterSize',
3409 'both': 'Pi0ProbTightEnergyThresholdAndLargeClusterSize',
3410 'standardMC15rd': 'Pi0ProbOriginMC15rd',
3411 'tightMC15rd': 'Pi0ProbTightEnergyThresholdMC15rd',
3412 'standardMC16rd': 'Pi0ProbOriginMC16rd',
3413 'tightMC16rd': 'Pi0ProbTightEnergyThresholdMC16rd'}
3414
3415 dictEtaExtraInfoName = {'standard': 'EtaProbOrigin',
3416 'tight': 'EtaProbTightEnergyThreshold',
3417 'cluster': 'EtaProbLargeClusterSize',
3418 'both': 'EtaProbTightEnergyThresholdAndLargeClusterSize',
3419 'standardMC15rd': 'EtaProbOriginMC15rd',
3420 'tightMC15rd': 'EtaProbTightEnergyThresholdMC15rd',
3421 'standardMC16rd': 'EtaProbOriginMC16rd',
3422 'tightMC16rd': 'EtaProbTightEnergyThresholdMC16rd'}
3423
3424 ListName = dictListName[mode]
3425 Pi0EnergyCut = dictPi0EnergyCut[mode]
3426 EtaEnergyCut = dictEtaEnergyCut[mode]
3427 NHitsTimingCut = dictNHitsTimingCut[mode]
3428 Pi0PayloadName = dictPi0PayloadName[mode]
3429 EtaPayloadName = dictEtaPayloadName[mode]
3430 Pi0ExtraInfoName = dictPi0ExtraInfoName[mode]
3431 EtaExtraInfoName = dictEtaExtraInfoName[mode]
3432
3433 # pi0 veto
3434 if pi0PayloadNameOverride is not None:
3435 Pi0PayloadName = pi0PayloadNameOverride
3436 B2WARNING("You're using personal weight files, be careful. ")
3437 if pi0SoftPhotonCutOverride is None:
3438 Pi0SoftPhotonCut = Pi0EnergyCut + ' and ' + NHitsTimingCut
3439 else:
3440 Pi0SoftPhotonCut = pi0SoftPhotonCutOverride
3441 B2WARNING("You're applying personal cuts on the soft photon candidates, be careful. ")
3442
3443 if requireSoftPhotonIsInROE:
3444 Pi0SoftPhotonCut += ' and isInRestOfEvent==1'
3445
3446 # define the particleList name for soft photon
3447 pi0soft = f'gamma:Pi0Soft{suffix}' + ListName + '_' + particleList.replace(':', '_')
3448 # fill the particleList for soft photon with energy, timing and clusterNHits cuts
3449 fillParticleList(pi0soft, Pi0SoftPhotonCut, path=roe_path)
3450 # register beambackground MVA for MC16rd
3451 if 'MC16rd' in mode:
3452 getBeamBackgroundProbability(pi0soft, weight="MC16rd", path=roe_path)
3453 getFakePhotonProbability(pi0soft, weight="MC16rd", path=roe_path)
3454 # reconstruct pi0
3455 reconstructDecay('pi0:Pi0Veto' + ListName + suffix + f' -> {hardParticle}:HardPhoton{suffix} ' + pi0soft, pi0Selection,
3456 allowChargeViolation=True, path=roe_path)
3457 # MVA training is conducted.
3458 roe_path.add_module('MVAExpert', listNames=['pi0:Pi0Veto' + ListName + suffix],
3459 extraInfoName=Pi0ExtraInfoName, identifier=Pi0PayloadName)
3460 # Pick up the pi0/eta candidate with the highest pi0/eta probability.
3461 rankByHighest(
3462 'pi0:Pi0Veto' + ListName + suffix,
3463 'extraInfo(' + Pi0ExtraInfoName + ')',
3464 numBest=2,
3465 outputVariable="Pi0VetoRank",
3466 path=roe_path)
3467 cutAndCopyList(outputListName='pi0:Pi0VetoFirst' + ListName + suffix,
3468 inputListName='pi0:Pi0Veto' + ListName + suffix,
3469 cut='extraInfo(Pi0VetoRank)==1',
3470 path=roe_path)
3471 variableToSignalSideExtraInfo('pi0:Pi0VetoFirst' + ListName + suffix,
3472 {'extraInfo(' + Pi0ExtraInfoName + ')': Pi0ExtraInfoName + suffix}, path=roe_path)
3473 variableToSignalSideExtraInfo('pi0:Pi0VetoFirst' + ListName + suffix,
3474 {'daughter(1,E)': 'SoftGammaEinPi0' + suffix}, path=roe_path)
3475 # Pick up the pi0/eta candidate with the second highest pi0/eta probability.
3476 cutAndCopyList(outputListName='pi0:Pi0VetoSecond' + ListName + suffix,
3477 inputListName='pi0:Pi0Veto' + ListName + suffix,
3478 cut='extraInfo(Pi0VetoRank)==2',
3479 path=roe_path)
3480 variableToSignalSideExtraInfo('pi0:Pi0VetoSecond' + ListName + suffix,
3481 {'extraInfo(' + Pi0ExtraInfoName + ')': 'second' + Pi0ExtraInfoName + suffix}, path=roe_path)
3482 variableToSignalSideExtraInfo('pi0:Pi0VetoSecond' + ListName + suffix,
3483 {'daughter(1,E)': 'secondSoftGammaEinPi0' + suffix}, path=roe_path)
3484
3485 # eta veto
3486 if etaPayloadNameOverride is not None:
3487 EtaPayloadName = etaPayloadNameOverride
3488 B2WARNING("You're using personal weight files, be careful. ")
3489 if etaSoftPhotonCutOverride is None:
3490 EtaSoftPhotonCut = EtaEnergyCut + ' and ' + NHitsTimingCut
3491 else:
3492 EtaSoftPhotonCut = etaSoftPhotonCutOverride
3493 B2WARNING("You're applying personal cuts on the soft photon candidates, be careful. ")
3494
3495 if requireSoftPhotonIsInROE:
3496 EtaSoftPhotonCut += ' and isInRestOfEvent==1'
3497
3498 etasoft = f'gamma:EtaSoft{suffix}' + ListName + '_' + particleList.replace(':', '_')
3499 fillParticleList(etasoft, EtaSoftPhotonCut, path=roe_path)
3500 # register beambackground MVA for MC16rd
3501 if 'MC16rd' in mode:
3502 getBeamBackgroundProbability(etasoft, weight="MC16rd", path=roe_path)
3503 getFakePhotonProbability(etasoft, weight="MC16rd", path=roe_path)
3504 reconstructDecay('eta:EtaVeto' + ListName + suffix + f' -> {hardParticle}:HardPhoton{suffix} ' + etasoft, etaSelection,
3505 allowChargeViolation=True, path=roe_path)
3506 roe_path.add_module('MVAExpert', listNames=['eta:EtaVeto' + ListName + suffix],
3507 extraInfoName=EtaExtraInfoName, identifier=EtaPayloadName)
3508 rankByHighest(
3509 'eta:EtaVeto' + ListName + suffix,
3510 'extraInfo(' + EtaExtraInfoName + ')',
3511 numBest=2,
3512 outputVariable="EtaVetoRank",
3513 path=roe_path)
3514 cutAndCopyList(outputListName='eta:EtaVetoFirst' + ListName + suffix,
3515 inputListName='eta:EtaVeto' + ListName + suffix,
3516 cut='extraInfo(EtaVetoRank)==1',
3517 path=roe_path)
3518 variableToSignalSideExtraInfo('eta:EtaVetoFirst' + ListName + suffix,
3519 {'extraInfo(' + EtaExtraInfoName + ')': EtaExtraInfoName + suffix}, path=roe_path)
3520 variableToSignalSideExtraInfo('eta:EtaVetoFirst' + ListName + suffix,
3521 {'daughter(1,E)': 'SoftGammaEinEta' + suffix}, path=roe_path)
3522 cutAndCopyList(outputListName='eta:EtaVetoSecond' + ListName + suffix,
3523 inputListName='eta:EtaVeto' + ListName + suffix,
3524 cut='extraInfo(EtaVetoRank)==2',
3525 path=roe_path)
3526 variableToSignalSideExtraInfo('eta:EtaVetoSecond' + ListName + suffix,
3527 {'extraInfo(' + EtaExtraInfoName + ')': 'second' + EtaExtraInfoName + suffix}, path=roe_path)
3528 variableToSignalSideExtraInfo('eta:EtaVetoSecond' + ListName + suffix,
3529 {'daughter(1,E)': 'secondSoftGammaEinEta' + suffix}, path=roe_path)
3530
3531 path.for_each('RestOfEvent', 'RestOfEvents', roe_path)
3532
3533
3534def lowEnergyPi0Identification(pi0List, gammaList, payloadNameSuffix,
3535 path=None):
3536 """
3537 Calculate low-energy pi0 identification.
3538 The result is stored as ExtraInfo ``lowEnergyPi0Identification`` for
3539 the list pi0List.
3540
3541 Parameters:
3542 pi0List (str): Pi0 list.
3543
3544 gammaList (str): Gamma list. First, an energy cut E > 0.2 is applied to the photons from this list.
3545 Then, all possible combinations with a pi0 daughter photon are formed except the one
3546 corresponding to the reconstructed pi0.
3547 The maximum low-energy pi0 veto value is calculated for such photon pairs
3548 and used as one of the input variables for the identification classifier.
3549
3550 payloadNameSuffix (str): Payload name suffix. The weight payloads are stored in the analysis global
3551 tag and have the following names:\n
3552 * ``'LowEnergyPi0Veto' + payloadNameSuffix``
3553 * ``'LowEnergyPi0Identification' + payloadNameSuffix``\n
3554 The possible suffixes are:\n
3555 * ``'Belle1'`` for Belle data.
3556 * ``'Belle2Release5'`` for Belle II release 5 data (MC14, proc12, buckets 16 - 25).
3557 * ``'Belle2Release6'`` for Belle II release 6 data (MC15, proc13, buckets 26 - 36).
3558
3559 path (basf2.Path): Module path.
3560 """
3561
3562 # Select photons with higher energy for formation of veto combinations.
3563 gammaListVeto = f'{gammaList}_pi0veto'
3564 cutAndCopyList(gammaListVeto, gammaList, 'E > 0.2', path=path)
3565 import b2bii
3566 payload_name = 'LowEnergyPi0Veto' + payloadNameSuffix
3567 path.add_module('LowEnergyPi0VetoExpert', identifier=payload_name,
3568 VetoPi0Daughters=True, GammaListName=gammaListVeto,
3569 Pi0ListName=pi0List, Belle1=b2bii.isB2BII())
3570 payload_name = 'LowEnergyPi0Identification' + payloadNameSuffix
3571 path.add_module('LowEnergyPi0IdentificationExpert',
3572 identifier=payload_name, Pi0ListName=pi0List,
3573 Belle1=b2bii.isB2BII())
3574
3575
3576def getNeutralHadronGeomMatches(
3577 particleLists,
3578 addKL=True,
3579 addNeutrons=False,
3580 efficiencyCorrectionKl=0.83,
3581 efficiencyCorrectionNeutrons=1.0,
3582 path=None):
3583 """
3584 For an ECL-based list, assign the mcdistanceKL and mcdistanceNeutron variables that correspond
3585 to the distance to the closest MC KL and neutron, respectively.
3586 @param particleLists the input ParticleLists, must be ECL-based lists (e.g. photons)
3587 @param addKL (default True) add distance to MC KL
3588 @param addNeutrons (default False) add distance to MC neutrons
3589 @param efficiencyCorrectionKl (default 0.83) apply overall efficiency correction
3590 @param efficiencyCorrectionNeutrons (default 1.0) apply overall efficiency correction
3591 @param path modules are added to this path
3592 """
3593 from ROOT import Belle2
3594 Const = Belle2.Const
3595
3596 if addKL:
3597 path.add_module(
3598 "NeutralHadronMatcher",
3599 particleLists=particleLists,
3600 mcPDGcode=Const.Klong.getPDGCode(),
3601 efficiencyCorrection=efficiencyCorrectionKl)
3602 if addNeutrons:
3603 path.add_module(
3604 "NeutralHadronMatcher",
3605 particleLists=particleLists,
3606 mcPDGcode=Const.neutron.getPDGCode(),
3607 efficiencyCorrection=efficiencyCorrectionNeutrons)
3608
3609
3610def getBeamBackgroundProbability(particleList, weight, path=None):
3611 """
3612 Assign a probability to each ECL cluster as being signal like (1) compared to beam background like (0)
3613 @param particleList the input ParticleList, must be a photon list
3614 @param weight type of weight file to use
3615 @param path modules are added to this path
3616 """
3617
3618 import b2bii
3619 if b2bii.isB2BII() and weight != "Belle":
3620 B2WARNING("weight type must be 'Belle' for b2bii.")
3621
3622 path.add_module('MVAExpert',
3623 listNames=particleList,
3624 extraInfoName='beamBackgroundSuppression',
3625 identifier=f'BeamBackgroundMVA_{weight}')
3626
3627
3628def getFakePhotonProbability(particleList, weight, path=None):
3629 """
3630 Assign a probability to each ECL cluster as being signal like (1) compared to fake photon like (0)
3631 @param particleList the input ParticleList, must be a photon list
3632 @param weight type of weight file to use
3633 @param path modules are added to this path
3634 """
3635
3636 import b2bii
3637 if b2bii.isB2BII() and weight != "Belle":
3638 B2WARNING("weight type must be 'Belle' for b2bii.")
3639
3640 path.add_module('MVAExpert',
3641 listNames=particleList,
3642 extraInfoName='fakePhotonSuppression',
3643 identifier=f'FakePhotonMVA_{weight}')
3644
3645
3646def buildEventKinematics(inputListNames=None, default_cleanup=True, custom_cuts=None,
3647 chargedPIDPriors=None, fillWithMostLikely=False, path=None):
3648 """
3649 Calculates the global kinematics of the event (visible energy, missing momentum, missing mass...)
3650 using ParticleLists provided. If no ParticleList is provided, default ParticleLists are used
3651 (all track and all hits in ECL without associated track).
3652
3653 The visible energy missing values are
3654 stored in a EventKinematics dataobject.
3655
3656 @param inputListNames list of ParticleLists used to calculate the global event kinematics.
3657 If the list is empty, default ParticleLists pi+:evtkin and gamma:evtkin are filled.
3658 @param fillWithMostLikely if True, the module uses the most likely particle mass hypothesis for charged particles
3659 according to the PID likelihood and the option inputListNames will be ignored.
3660 @param chargedPIDPriors The prior PID fractions, that are used to regulate
3661 amount of certain charged particle species, should be a list of
3662 six floats if not None. The order of particle types is
3663 the following: [e-, mu-, pi-, K-, p+, d+]
3664 @param default_cleanup if True and either inputListNames empty or fillWithMostLikely True, default clean up cuts are applied
3665 @param custom_cuts tuple of selection cut strings of form (trackCuts, photonCuts), default is None,
3666 which would result in a standard predefined selection cuts
3667 @param path modules are added to this path
3668 """
3669
3670 if inputListNames is None:
3671 inputListNames = []
3672 trackCuts = 'pt > 0.1'
3673 trackCuts += ' and thetaInCDCAcceptance'
3674 trackCuts += ' and abs(dz) < 3'
3675 trackCuts += ' and dr < 0.5'
3676
3677 gammaCuts = 'E > 0.05'
3678 gammaCuts += ' and thetaInCDCAcceptance'
3679 if not b2bii.isB2BII():
3680 gammaCuts += ' and abs(clusterTiming) < 200'
3681 if (custom_cuts is not None):
3682 trackCuts, gammaCuts = custom_cuts
3683
3684 if fillWithMostLikely:
3685 from stdCharged import stdMostLikely
3686 stdMostLikely(chargedPIDPriors, '_evtkin', path=path)
3687 inputListNames = [f'{ptype}:mostlikely_evtkin' for ptype in ['K+', 'p+', 'e+', 'mu+', 'pi+']]
3688 if b2bii.isB2BII():
3689 copyList('gamma:evtkin', 'gamma:mdst', path=path)
3690 else:
3691 fillParticleList('gamma:evtkin', '', path=path)
3692 inputListNames += ['gamma:evtkin']
3693 if default_cleanup:
3694 B2INFO("Using default cleanup in EventKinematics module.")
3695 for ptype in ['K+', 'p+', 'e+', 'mu+', 'pi+']:
3696 applyCuts(f'{ptype}:mostlikely_evtkin', trackCuts, path=path)
3697 applyCuts('gamma:evtkin', gammaCuts, path=path)
3698 else:
3699 B2INFO("No cleanup in EventKinematics module.")
3700 if not inputListNames:
3701 B2INFO("Creating particle lists pi+:evtkin and gamma:evtkin to get the global kinematics of the event.")
3702 fillParticleList('pi+:evtkin', '', path=path)
3703 if b2bii.isB2BII():
3704 copyList('gamma:evtkin', 'gamma:mdst', path=path)
3705 else:
3706 fillParticleList('gamma:evtkin', '', path=path)
3707 particleLists = ['pi+:evtkin', 'gamma:evtkin']
3708 if default_cleanup:
3709 if (custom_cuts is not None):
3710 B2INFO("Using default cleanup in EventKinematics module.")
3711 applyCuts('pi+:evtkin', trackCuts, path=path)
3712 applyCuts('gamma:evtkin', gammaCuts, path=path)
3713 else:
3714 B2INFO("No cleanup in EventKinematics module.")
3715 else:
3716 particleLists = inputListNames
3717
3718 eventKinematicsModule = register_module('EventKinematics')
3719 eventKinematicsModule.set_name('EventKinematics_reco')
3720 eventKinematicsModule.param('particleLists', particleLists)
3721 path.add_module(eventKinematicsModule)
3722
3723
3724def buildEventKinematicsFromMC(inputListNames=None, selectionCut='', path=None):
3725 """
3726 Calculates the global kinematics of the event (visible energy, missing momentum, missing mass...)
3727 using generated particles. If no ParticleList is provided, default generated ParticleLists are used.
3728
3729 @param inputListNames list of ParticleLists used to calculate the global event kinematics.
3730 If the list is empty, default ParticleLists are filled.
3731 @param selectionCut optional selection cuts
3732 @param path Path to append the eventKinematics module to.
3733 """
3734
3735 if inputListNames is None:
3736 inputListNames = []
3737 if (len(inputListNames) == 0):
3738 # Type of particles to use for EventKinematics
3739 # K_S0 and Lambda0 are added here because some of them have interacted
3740 # with the detector material
3741 types = ['gamma', 'e+', 'mu+', 'pi+', 'K+', 'p+',
3742 'K_S0', 'Lambda0']
3743 for t in types:
3744 fillParticleListFromMC(f"{t}:evtkin_default_gen", 'mcPrimary > 0 and nDaughters == 0',
3745 True, True, path=path)
3746 if (selectionCut != ''):
3747 applyCuts(f"{t}:evtkin_default_gen", selectionCut, path=path)
3748 inputListNames += [f"{t}:evtkin_default_gen"]
3749
3750 eventKinematicsModule = register_module('EventKinematics')
3751 eventKinematicsModule.set_name('EventKinematics_gen')
3752 eventKinematicsModule.param('particleLists', inputListNames)
3753 eventKinematicsModule.param('usingMC', True)
3754 path.add_module(eventKinematicsModule)
3755
3756
3757def buildEventShape(inputListNames=None,
3758 default_cleanup=True,
3759 custom_cuts=None,
3760 allMoments=False,
3761 cleoCones=True,
3762 collisionAxis=True,
3763 foxWolfram=True,
3764 harmonicMoments=True,
3765 jets=True,
3766 sphericity=True,
3767 thrust=True,
3768 checkForDuplicates=False,
3769 path=None):
3770 """
3771 Calculates the event-level shape quantities (thrust, sphericity, Fox-Wolfram moments...)
3772 using the particles in the lists provided by the user. If no particle list is provided,
3773 the function will internally create a list of good tracks and a list of good photons
3774 with (optionally) minimal quality cuts.
3775
3776
3777 The results of the calculation are then stored into the EventShapeContainer dataobject,
3778 and are accessible using the variables of the EventShape group.
3779
3780 The user can switch the calculation of certain quantities on or off to save computing
3781 time. By default the calculation of the high-order moments (5-8) is turned off.
3782 Switching off an option will make the corresponding variables not available.
3783
3784 Info:
3785 The user can provide as many particle lists as needed, using also composite particles.
3786 In these cases, it is recommended to activate the checkForDuplicates flag since it
3787 will eliminate duplicates, e.g., if the same track is provided multiple times
3788 (either with different mass hypothesis or once as an independent particle and once
3789 as daughter of a composite particle). The first occurrence will be used in the
3790 calculations so the order in which the particle lists are given as well as within
3791 the particle lists matters.
3792
3793 @param inputListNames List of ParticleLists used to calculate the
3794 event shape variables. If the list is empty the default
3795 particleLists pi+:evtshape and gamma:evtshape are filled.
3796 @param default_cleanup If True, applies standard cuts on pt and cosTheta when
3797 defining the internal lists. This option is ignored if the
3798 particleLists are provided by the user.
3799 @param custom_cuts tuple of selection cut strings of form (trackCuts, photonCuts), default is None,
3800 which would result in a standard predefined selection cuts
3801 @param path Path to append the eventShape modules to.
3802 @param thrust Enables the calculation of thrust-related quantities (CLEO
3803 cones, Harmonic moments, jets).
3804 @param collisionAxis Enables the calculation of the quantities related to the
3805 collision axis .
3806 @param foxWolfram Enables the calculation of the Fox-Wolfram moments.
3807 @param harmonicMoments Enables the calculation of the Harmonic moments with respect
3808 to both the thrust axis and, if collisionAxis = True, the collision axis.
3809 @param allMoments If True, calculates also the FW and harmonic moments from order
3810 5 to 8 instead of the low-order ones only.
3811 @param cleoCones Enables the calculation of the CLEO cones with respect to both the thrust
3812 axis and, if collisionAxis = True, the collision axis.
3813 @param jets Enables the calculation of the hemisphere momenta and masses.
3814 Requires thrust = True.
3815 @param sphericity Enables the calculation of the sphericity-related quantities.
3816 @param checkForDuplicates Perform a check for duplicate particles before adding them. Regardless of the value of this option,
3817 it is recommended to consider sanitizing the lists you are passing to the function since this will
3818 speed up the processing.
3819
3820 """
3821
3822 if inputListNames is None:
3823 inputListNames = []
3824 trackCuts = 'pt > 0.1'
3825 trackCuts += ' and thetaInCDCAcceptance'
3826 trackCuts += ' and abs(dz) < 3.0'
3827 trackCuts += ' and dr < 0.5'
3828
3829 gammaCuts = 'E > 0.05'
3830 gammaCuts += ' and thetaInCDCAcceptance'
3831 if not b2bii.isB2BII():
3832 gammaCuts += ' and abs(clusterTiming) < 200'
3833 if (custom_cuts is not None):
3834 trackCuts, gammaCuts = custom_cuts
3835
3836 if not inputListNames:
3837 B2INFO("Creating particle lists pi+:evtshape and gamma:evtshape to get the event shape variables.")
3838 fillParticleList('pi+:evtshape', '', path=path)
3839 if b2bii.isB2BII():
3840 copyList('gamma:evtshape', 'gamma:mdst', path=path)
3841 else:
3842 fillParticleList(
3843 'gamma:evtshape',
3844 '',
3845 path=path)
3846 particleLists = ['pi+:evtshape', 'gamma:evtshape']
3847
3848 if default_cleanup:
3849 if (custom_cuts is not None):
3850 B2INFO("Applying standard cuts")
3851 applyCuts('pi+:evtshape', trackCuts, path=path)
3852
3853 applyCuts('gamma:evtshape', gammaCuts, path=path)
3854 else:
3855 B2WARNING("Creating the default lists with no cleanup.")
3856 else:
3857 particleLists = inputListNames
3858
3859 eventShapeModule = register_module('EventShapeCalculator')
3860 eventShapeModule.set_name('EventShape')
3861 eventShapeModule.param('particleListNames', particleLists)
3862 eventShapeModule.param('enableAllMoments', allMoments)
3863 eventShapeModule.param('enableCleoCones', cleoCones)
3864 eventShapeModule.param('enableCollisionAxis', collisionAxis)
3865 eventShapeModule.param('enableFoxWolfram', foxWolfram)
3866 eventShapeModule.param('enableJets', jets)
3867 eventShapeModule.param('enableHarmonicMoments', harmonicMoments)
3868 eventShapeModule.param('enableSphericity', sphericity)
3869 eventShapeModule.param('enableThrust', thrust)
3870 eventShapeModule.param('checkForDuplicates', checkForDuplicates)
3871
3872 path.add_module(eventShapeModule)
3873
3874
3875def labelTauPairMC(printDecayInfo=False, path=None, TauolaBelle=False, mapping_minus=None, mapping_plus=None):
3876 """
3877 Search tau leptons into the MC information of the event. If confirms it's a generated tau pair decay,
3878 labels the decay generated of the positive and negative leptons using the ID of KKMC tau decay table.
3879
3880 @param printDecayInfo: If true, prints ID and prong of each tau lepton in the event.
3881 @param path: module is added to this path
3882 @param TauolaBelle: if False, TauDecayMode is set. If True, TauDecayMarker is set.
3883 @param mapping_minus: if None, the map is the default one, else the path for the map is given by the user for tau-
3884 @param mapping_plus: if None, the map is the default one, else the path for the map is given by the user for tau+
3885 """
3886
3887 from basf2 import find_file
3888 if not TauolaBelle:
3889
3890 if printDecayInfo:
3891 m_printmode = 'all'
3892 else:
3893 m_printmode = 'default'
3894
3895 if mapping_minus is None:
3896 mp_file_minus = find_file('data/analysis/modules/TauDecayMode/map_tauminus.txt')
3897 else:
3898 mp_file_minus = mapping_minus
3899
3900 if mapping_plus is None:
3901 mp_file_plus = find_file('data/analysis/modules/TauDecayMode/map_tauplus.txt')
3902 else:
3903 mp_file_plus = mapping_plus
3904
3905 path.add_module('TauDecayMode', printmode=m_printmode, file_minus=mp_file_minus, file_plus=mp_file_plus)
3906
3907 else:
3908 tauDecayMarker = register_module('TauDecayMarker')
3909 tauDecayMarker.set_name('TauDecayMarker_')
3910
3911 path.add_module(tauDecayMarker, printDecayInfo=printDecayInfo)
3912
3913
3914def tagCurlTracks(particleLists,
3915 mcTruth=False,
3916 responseCut=-1.0,
3917 selectorType='cut',
3918 ptCut=0.5,
3919 expert_train=False,
3920 expert_filename="",
3921 path=None):
3922 """
3923 Warning:
3924 The cut selector is not calibrated with Belle II data and should not be used without extensive study.
3925
3926 Identifies curl tracks and tags them with extraInfo(isCurl=1) for later removal.
3927 For Belle data with a `b2bii` analysis the available cut based selection is described in `BN1079`_.
3928
3929 .. _BN1079: https://belle.kek.jp/secured/belle_note/gn1079/bn1079.pdf
3930
3931
3932 The module loops over all particles in a given list with a transverse momentum below the pre-selection **ptCut**
3933 and assigns them to bundles based on the response of the chosen **selector** and the required minimum response set by the
3934 **responseCut**. Once all particles are assigned they are ranked by 25dr^2+dz^2. All but the lowest are tagged
3935 with extraInfo(isCurl=1) to allow for later removal by cutting the list or removing these from ROE as
3936 applicable.
3937
3938
3939 @param particleLists: list of particle lists to check for curls.
3940 @param mcTruth: bool flag to additionally assign particles with extraInfo(isTruthCurl) and
3941 extraInfo(truthBundleSize). To calculate these particles are assigned to bundles by their
3942 genParticleIndex then ranked and tagged as normal.
3943 @param responseCut: float min classifier response that considers two tracks to come from the same particle.
3944 If set to ``-1`` a cut value optimised to maximise the accuracy on a BBbar sample is used.
3945 Note 'cut' selector is binary 0/1.
3946 @param selectorType: string name of selector to use. The available options are 'cut' and 'mva'.
3947 It is strongly recommended to used the 'mva' selection. The 'cut' selection
3948 is based on BN1079 and is only calibrated for Belle data.
3949
3950 @param ptCut: Pre-selection cut on transverse momentum. Only tracks below that are considered as curler candidates.
3951
3952 @param expert_train: flag to set training mode if selector has a training mode (mva).
3953 @param expert_filename: set file name of produced training ntuple (mva).
3954 @param path: module is added to this path.
3955 """
3956
3957 import b2bii
3958 belle = b2bii.isB2BII()
3959
3960 if (not isinstance(particleLists, list)):
3961 particleLists = [particleLists] # in case user inputs a particle list as string
3962
3963 curlTagger = register_module('CurlTagger')
3964 curlTagger.set_name('CurlTagger_')
3965 curlTagger.param('particleLists', particleLists)
3966 curlTagger.param('belle', belle)
3967 curlTagger.param('mcTruth', mcTruth)
3968 curlTagger.param('responseCut', responseCut)
3969 if abs(responseCut + 1) < 1e-9:
3970 curlTagger.param('usePayloadCut', True)
3971 else:
3972 curlTagger.param('usePayloadCut', False)
3973
3974 curlTagger.param('selectorType', selectorType)
3975 curlTagger.param('ptCut', ptCut)
3976 curlTagger.param('train', expert_train)
3977 curlTagger.param('trainFilename', expert_filename)
3978
3979 path.add_module(curlTagger)
3980
3981
3982def applyChargedPidMVA(particleLists, path, trainingMode, chargeIndependent=False, binaryHypoPDGCodes=(0, 0)):
3983 """
3984 Use an MVA to perform particle identification for charged stable particles, using the `ChargedPidMVA` module.
3985
3986 The module decorates Particle objects in the input ParticleList(s) with variables
3987 containing the appropriate MVA score, which can be used to select candidates by placing a cut on it.
3988
3989 Note:
3990 The MVA algorithm used is a gradient boosted decision tree (**TMVA 4.3.0**, **ROOT 6.20/04**).
3991
3992 The module can perform either 'binary' PID between input S, B particle mass hypotheses according to the following scheme:
3993
3994 * e (11) vs. pi (211)
3995 * mu (13) vs. pi (211)
3996 * pi (211) vs. K (321)
3997 * K (321) vs. pi (211)
3998
3999 , or 'global' PID, namely "one-vs-others" separation. The latter exploits an MVA algorithm trained in multi-class mode,
4000 and it's the default behaviour. Currently, the multi-class training separates the following standard charged hypotheses:
4001
4002 - e (11), mu (13), pi (211), K (321)
4003
4004 Warning:
4005 In order to run the `ChargedPidMVA` and ensure the most up-to-date MVA training weights are applied,
4006 it is necessary to append the latest analysis global tag (GT) to the steering script.
4007
4008 Parameters:
4009 particleLists (list(str)): the input list of DecayStrings, where each selected (^) daughter should correspond to a
4010 standard charged ParticleList, e.g. ``['Lambda0:sig -> ^p+ ^pi-', 'J/psi:sig -> ^mu+ ^mu-']``.
4011 One can also directly pass a list of standard charged ParticleLists,
4012 e.g. ``['e+:my_electrons', 'pi+:my_pions']``.
4013 Note that charge-conjugated ParticleLists will automatically be included.
4014 path (basf2.Path): the module is added to this path.
4015 trainingMode (``Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode``): enum identifier of the training mode.
4016 Needed to pick up the correct payload from the DB. Available choices:
4017
4018 * c_Classification=0
4019 * c_Multiclass=1
4020 * c_ECL_Classification=2
4021 * c_ECL_Multiclass=3
4022 * c_PSD_Classification=4
4023 * c_PSD_Multiclass=5
4024 * c_ECL_PSD_Classification=6
4025 * c_ECL_PSD_Multiclass=7
4026
4027 chargeIndependent (bool, ``optional``): use a BDT trained on a sample of inclusively charged particles.
4028 binaryHypoPDGCodes (tuple(int, int), ``optional``): the pdgIds of the signal, background mass hypothesis.
4029 Required only for binary PID mode.
4030 """
4031
4032 import b2bii
4033 if b2bii.isB2BII():
4034 B2ERROR("Charged PID via MVA is not available for Belle data.")
4035
4036 from ROOT import Belle2
4037
4038 TrainingMode = Belle2.ChargedPidMVAWeights.ChargedPidMVATrainingMode
4039 Const = Belle2.Const
4040
4041 plSet = set(particleLists)
4042
4043 # Map the training mode enum value to the actual name of the payload in the GT.
4044 payloadNames = {
4045 TrainingMode.c_Classification:
4046 {"mode": "Classification", "detector": "ALL"},
4047 TrainingMode.c_Multiclass:
4048 {"mode": "Multiclass", "detector": "ALL"},
4049 TrainingMode.c_ECL_Classification:
4050 {"mode": "ECL_Classification", "detector": "ECL"},
4051 TrainingMode.c_ECL_Multiclass:
4052 {"mode": "ECL_Multiclass", "detector": "ECL"},
4053 TrainingMode.c_PSD_Classification:
4054 {"mode": "PSD_Classification", "detector": "ALL"},
4055 TrainingMode.c_PSD_Multiclass:
4056 {"mode": "PSD_Multiclass", "detector": "ALL"},
4057 TrainingMode.c_ECL_PSD_Classification:
4058 {"mode": "ECL_PSD_Classification", "detector": "ECL"},
4059 TrainingMode.c_ECL_PSD_Multiclass:
4060 {"mode": "ECL_PSD_Multiclass", "detector": "ECL"},
4061 }
4062
4063 if payloadNames.get(trainingMode) is None:
4064 B2FATAL("The chosen training mode integer identifier:\n", trainingMode,
4065 "\nis not supported. Please choose among the following:\n",
4066 "\n".join(f"{key}:{val.get('mode')}" for key, val in sorted(payloadNames.items())))
4067
4068 mode = payloadNames.get(trainingMode).get("mode")
4069 detector = payloadNames.get(trainingMode).get("detector")
4070
4071 payloadName = f"ChargedPidMVAWeights_{mode}"
4072
4073 # Map pdgIds of std charged particles to name identifiers,
4074 # and binary bkg identifiers.
4075 stdChargedMap = {
4076 Const.electron.getPDGCode():
4077 {"pName": "e", "pFullName": "electron", "pNameBkg": "pi", "pdgIdBkg": Const.pion.getPDGCode()},
4078 Const.muon.getPDGCode():
4079 {"pName": "mu", "pFullName": "muon", "pNameBkg": "pi", "pdgIdBkg": Const.pion.getPDGCode()},
4080 Const.pion.getPDGCode():
4081 {"pName": "pi", "pFullName": "pion", "pNameBkg": "K", "pdgIdBkg": Const.kaon.getPDGCode()},
4082 Const.kaon.getPDGCode():
4083 {"pName": "K", "pFullName": "kaon", "pNameBkg": "pi", "pdgIdBkg": Const.pion.getPDGCode()},
4084 Const.proton.getPDGCode():
4085 {"pName": "p", "pFullName": "proton", "pNameBkg": "pi", "pdgIdBkg": Const.pion.getPDGCode()},
4086 Const.deuteron.getPDGCode():
4087 {"pName": "d", "pFullName": "deuteron", "pNameBkg": "pi", "pdgIdBkg": Const.pion.getPDGCode()},
4088 }
4089
4090 if binaryHypoPDGCodes == (0, 0):
4091
4092 # MULTI-CLASS training mode.
4093 chargedpid = register_module("ChargedPidMVAMulticlass")
4094 chargedpid.set_name(f"ChargedPidMVAMulticlass_{mode}")
4095
4096 else:
4097
4098 # BINARY training mode.
4099 # In binary mode, enforce check on input S, B hypotheses compatibility.
4100
4101 binaryOpts = [(pdgIdSig, info["pdgIdBkg"]) for pdgIdSig, info in stdChargedMap.items()]
4102
4103 if binaryHypoPDGCodes not in binaryOpts:
4104 B2FATAL("No charged pid MVA was trained to separate ", binaryHypoPDGCodes[0], " vs. ", binaryHypoPDGCodes[1],
4105 ". Please choose among the following pairs:\n",
4106 "\n".join(f"{opt[0]} vs. {opt[1]}" for opt in binaryOpts))
4107
4108 decayDescriptor = Belle2.DecayDescriptor()
4109 for name in plSet:
4110 if not decayDescriptor.init(name):
4111 raise ValueError(f"Invalid particle list {name} in applyChargedPidMVA!")
4112 msg = f"Input ParticleList: {name}"
4113 pdgs = [abs(decayDescriptor.getMother().getPDGCode())]
4114 daughter_pdgs = decayDescriptor.getSelectionPDGCodes()
4115 if len(daughter_pdgs) > 0:
4116 pdgs = daughter_pdgs
4117 for idaughter, pdg in enumerate(pdgs):
4118 if abs(pdg) not in binaryHypoPDGCodes:
4119 if daughter_pdgs:
4120 msg = f"Selected daughter {idaughter} in ParticleList: {name}"
4121 B2WARNING(
4122 f"{msg} (PDG={pdg}) is neither signal ({binaryHypoPDGCodes[0]}) nor background ({binaryHypoPDGCodes[1]}).")
4123
4124 chargedpid = register_module("ChargedPidMVA")
4125 chargedpid.set_name(f"ChargedPidMVA_{binaryHypoPDGCodes[0]}_vs_{binaryHypoPDGCodes[1]}_{mode}")
4126 chargedpid.param("sigHypoPDGCode", binaryHypoPDGCodes[0])
4127 chargedpid.param("bkgHypoPDGCode", binaryHypoPDGCodes[1])
4128
4129 chargedpid.param("particleLists", list(plSet))
4130 chargedpid.param("payloadName", payloadName)
4131 chargedpid.param("chargeIndependent", chargeIndependent)
4132
4133 # Ensure the module knows whether we are using ECL-only training mode.
4134 if detector == "ECL":
4135 chargedpid.param("useECLOnlyTraining", True)
4136
4137 path.add_module(chargedpid)
4138
4139
4140def calculateTrackIsolation(
4141 decay_string,
4142 path,
4143 *detectors,
4144 reference_list_name=None,
4145 vars_for_nearest_part=[],
4146 highest_prob_mass_for_ext=True,
4147 exclude_pid_det_weights=False):
4148 """
4149 Given an input decay string, compute variables that quantify track helix-based isolation of the charged
4150 stable particles in the input decay chain.
4151
4152 Note:
4153 An "isolation score" can be defined using the distance
4154 of each particle to its closest neighbour, defined as the segment connecting the two
4155 extrapolated track helices intersection points on a given cylindrical surface.
4156 The distance variables defined in the `VariableManager` is named `minET2ETDist`,
4157 the isolation scores are named `minET2ETIsoScore`, `minET2ETIsoScoreAsWeightedAvg`.
4158
4159 The definition of distance and the number of distances that are calculated per sub-detector is based on
4160 the following recipe:
4161
4162 * **CDC**: as the segmentation is very coarse along :math:`z`,
4163 the distance is defined as the cord length on the :math:`(\\rho=R, \\phi)` plane.
4164 A total of 9 distances are calculated: the cylindrical surfaces are defined at radiuses
4165 that correspond to the positions of the 9 CDC wire superlayers: :math:`R_{i}^{\\mathrm{CDC}}~(i \\in \\{0,...,8\\})`.
4166
4167 * **TOP**: as there is no segmentation along :math:`z`,
4168 the distance is defined as the cord length on the :math:`(\\rho=R, \\phi)` plane.
4169 Only one distance at the TOP entry radius :math:`R_{0}^{\\mathrm{TOP}}` is calculated.
4170
4171 * **ARICH**: as there is no segmentation along :math:`z`,
4172 the distance is defined as the distance on the :math:`(\\rho=R, \\phi)` plane at fixed :math:`z=Z`.
4173 Only one distance at the ARICH photon detector entry coordinate :math:`Z_{0}^{\\mathrm{ARICH}}` is calculated.
4174
4175 * **ECL**: the distance is defined on the :math:`(\\rho=R, \\phi, z)` surface in the barrel,
4176 on the :math:`(\\rho, \\phi, z=Z)` surface in the endcaps.
4177 Two distances are calculated: one at the ECL entry surface :math:`R_{0}^{\\mathrm{ECL}}` (barrel),
4178 :math:`Z_{0}^{\\mathrm{ECL}}` (endcaps), and one at :math:`R_{1}^{\\mathrm{ECL}}` (barrel),
4179 :math:`Z_{1}^{\\mathrm{ECL}}` (endcaps), corresponding roughly to the mid-point
4180 of the longitudinal size of the crystals.
4181
4182 * **KLM**: the distance is defined on the :math:`(\\rho=R, \\phi, z)` surface in the barrel,
4183 on the :math:`(\\rho, \\phi, z=Z)` surface in the endcaps.
4184 Only one distance at the KLM first strip entry surface :math:`R_{0}^{\\mathrm{KLM}}` (barrel),
4185 :math:`Z_{0}^{\\mathrm{KLM}}` (endcaps) is calculated.
4186
4187 Parameters:
4188 decay_string (str): name of the input decay string with selected charged stable daughters,
4189 for example: ``Lambda0:merged -> ^p+ ^pi-``.
4190 Alternatively, it can be a particle list for charged stable particles
4191 as defined in ``Const::chargedStableSet``, for example: ``mu+:all``.
4192 The charge-conjugate particle list will be also processed automatically.
4193 path (basf2.Path): path to which module(s) will be added.
4194 *detectors: detectors for which track isolation variables will be calculated.
4195 Choose among: ``{'CDC', 'TOP', 'ARICH', 'ECL', 'KLM'}``.
4196 reference_list_name (Optional[str]): name of the input charged stable particle list for the reference tracks.
4197 By default, the ``:all`` ParticleList of the same type
4198 of the selected particle in ``decay_string`` is used.
4199 The charge-conjugate particle list will be also processed automatically.
4200 vars_for_nearest_part (Optional[list(str)]): a list of variables to calculate for the nearest particle in the reference
4201 list at each detector surface. It uses the metavariable `minET2ETDistVar`.
4202 If unset, only the distances to the nearest neighbour
4203 per detector are calculated.
4204 highest_prob_mass_for_hex (Optional[bool]): if this option is set to True (default), the helix extrapolation
4205 for the particles will use the track fit result for the most
4206 probable mass hypothesis, namely, the one that gives the highest
4207 chi2Prob of the fit. Otherwise, it uses the mass hypothesis that
4208 corresponds to the particle lists PDG.
4209 exclude_pid_det_weights (Optional[bool]): if this option is set to False (default), the isolation score
4210 calculation will take into account the weight that each detector has on the PID
4211 for the particle species of interest.
4212
4213 Returns:
4214 dict(int, list(str)): a dictionary mapping the PDG of each reference particle list to its isolation variables.
4215
4216 """
4217
4218 import pdg
4219 from ROOT import Belle2, TDatabasePDG
4220
4221 decayDescriptor = Belle2.DecayDescriptor()
4222 if not decayDescriptor.init(decay_string):
4223 B2FATAL(f"Invalid particle list {decay_string} in calculateTrackIsolation!")
4224 no_reference_list_name = not reference_list_name
4225
4226 det_and_layers = {
4227 "CDC": list(range(9)),
4228 "TOP": [0],
4229 "ARICH": [0],
4230 "ECL": [0, 1],
4231 "KLM": [0],
4232 }
4233 if any(d not in det_and_layers for d in detectors):
4234 B2FATAL(
4235 "Your input detector list: ",
4236 detectors,
4237 " contains an invalid choice. Please select among: ",
4238 list(
4239 det_and_layers.keys()))
4240
4241 # The module allows only one daughter to be selected at a time,
4242 # that's why here we preprocess the input decay string.
4243 select_symbol = '^'
4244 processed_decay_strings = []
4245 if select_symbol in decay_string:
4246 splitted_ds = decay_string.split(select_symbol)
4247 for i in range(decay_string.count(select_symbol)):
4248 tmp = list(splitted_ds)
4249 tmp.insert(i+1, select_symbol)
4250 processed_decay_strings += [''.join(tmp)]
4251 else:
4252 processed_decay_strings += [decay_string]
4253
4254 reference_lists_to_vars = {}
4255
4256 for processed_dec in processed_decay_strings:
4257 if no_reference_list_name:
4258 decayDescriptor.init(processed_dec)
4259 selected_daughter_pdgs = decayDescriptor.getSelectionPDGCodes()
4260 if len(selected_daughter_pdgs) > 0:
4261 reference_list_name = f'{TDatabasePDG.Instance().GetParticle(abs(selected_daughter_pdgs[-1])).GetName()}:all'
4262 else:
4263 reference_list_name = f'{processed_dec.split(":")[0]}:all'
4264
4265 ref_pdg = pdg.from_name(reference_list_name.split(":")[0])
4266
4267 trackiso = path.add_module("TrackIsoCalculator",
4268 decayString=processed_dec,
4269 detectorNames=list(detectors),
4270 particleListReference=reference_list_name,
4271 useHighestProbMassForExt=highest_prob_mass_for_ext,
4272 excludePIDDetWeights=exclude_pid_det_weights)
4273 trackiso.set_name(f"TrackIsoCalculator_{'_'.join(detectors)}_{processed_dec}_VS_{reference_list_name}")
4274
4275 # Metavariables for the distances to the closest reference tracks at each detector surface.
4276 # Always calculate them.
4277 # Ensure the flag for the mass hypothesis of the fit is set.
4278 trackiso_vars = [
4279 f"minET2ETDist({d}, {d_layer}, {reference_list_name}, {int(highest_prob_mass_for_ext)})"
4280 for d in detectors for d_layer in det_and_layers[d]]
4281 # Track isolation score.
4282 trackiso_vars += [
4283 f"minET2ETIsoScore({reference_list_name}, {int(highest_prob_mass_for_ext)}, {', '.join(detectors)})",
4284 f"minET2ETIsoScoreAsWeightedAvg({reference_list_name}, {int(highest_prob_mass_for_ext)}, {', '.join(detectors)})",
4285 ]
4286 # Optionally, calculate the input variables for the nearest neighbour in the reference list.
4287 if vars_for_nearest_part:
4288 trackiso_vars.extend(
4289 [
4290 f"minET2ETDistVar({d}, {d_layer}, {reference_list_name}, {v})"
4291 for d in detectors for d_layer in det_and_layers[d] for v in vars_for_nearest_part
4292 ])
4293 trackiso_vars.sort()
4294
4295 reference_lists_to_vars[ref_pdg] = trackiso_vars
4296
4297 return reference_lists_to_vars
4298
4299
4300def calculateDistance(list_name, decay_string, mode='vertextrack', path=None):
4301 """
4302 Calculates distance between two vertices, distance of closest approach between a vertex and a track,\
4303 distance of closest approach between a vertex and btube. For track, this calculation ignores track curvature,\
4304 it's negligible for small distances.The user should use extraInfo(CalculatedDistance)\
4305 to get it. A full example steering file is at analysis/tests/test_DistanceCalculator.py
4306
4307 Example:
4308 .. code-block:: python
4309
4310 from modularAnalysis import calculateDistance
4311 calculateDistance('list_name', 'decay_string', "mode", path=user_path)
4312
4313 @param list_name name of the input ParticleList
4314 @param decay_string select particles between the distance of closest approach will be calculated
4315 @param mode Specifies how the distance is calculated
4316 vertextrack: calculate the distance of closest approach between a track and a\
4317 vertex, taking the first candidate as vertex, default
4318 trackvertex: calculate the distance of closest approach between a track and a\
4319 vertex, taking the first candidate as track
4320 2tracks: calculates the distance of closest approach between two tracks
4321 2vertices: calculates the distance between two vertices
4322 vertexbtube: calculates the distance of closest approach between a vertex and btube
4323 trackbtube: calculates the distance of closest approach between a track and btube
4324 @param path modules are added to this path
4325
4326 """
4327
4328 dist_mod = register_module('DistanceCalculator')
4329
4330 dist_mod.set_name('DistanceCalculator_' + list_name)
4331 dist_mod.param('listName', list_name)
4332 dist_mod.param('decayString', decay_string)
4333 dist_mod.param('mode', mode)
4334 path.add_module(dist_mod)
4335
4336
4337def addInclusiveDstarReconstruction(decayString, slowPionCut, DstarCut, path):
4338 """
4339 Adds the InclusiveDstarReconstruction module to the given path.
4340 This module creates a D* particle list by estimating the D* four momenta
4341 from slow pions, specified by a given cut. The D* energy is approximated
4342 as E(D*) = m(D*)/(m(D*) - m(D)) * E(pi). The absolute value of the D*
4343 momentum is calculated using the D* PDG mass and the direction is collinear
4344 to the slow pion direction. The charge of the given pion list has to be consistent
4345 with the D* charge
4346
4347 @param decayString Decay string, must be of form ``D* -> pi``
4348 @param slowPionCut Cut applied to the input pion list to identify slow pions
4349 @param DstarCut Cut applied to the output D* list
4350 @param path the module is added to this path
4351 """
4352
4353 incl_dstar = register_module("InclusiveDstarReconstruction")
4354 incl_dstar.param("decayString", decayString)
4355 incl_dstar.param("slowPionCut", slowPionCut)
4356 incl_dstar.param("DstarCut", DstarCut)
4357 path.add_module(incl_dstar)
4358
4359
4360def scaleError(outputListName, inputListName,
4361 scaleFactors=[1.149631, 1.085547, 1.151704, 1.096434, 1.086659],
4362 scaleFactorsNoPXD=[1.149631, 1.085547, 1.151704, 1.096434, 1.086659],
4363 d0Resolution=[0.00115328, 0.00134704],
4364 z0Resolution=[0.00124327, 0.0013272],
4365 d0MomThr=0.500000,
4366 z0MomThr=0.500000,
4367 path=None):
4368 """
4369 This module creates a new charged particle list.
4370 The helix errors of the new particles are scaled by constant factors.
4371 Two sets of five scale factors are defined for tracks with and without a PXD hit.
4372 The scale factors are in order of (d0, phi0, omega, z0, tanlambda).
4373 For tracks with a PXD hit, in order to avoid severe underestimation of d0 and z0 errors,
4374 lower limits (best resolution) can be set in a momentum-dependent form.
4375 This module is supposed to be used only for TDCPV analysis and for low-momentum (0-3 GeV/c) tracks in BBbar events.
4376 Details will be documented in a Belle II note, BELLE2-NOTE-PH-2021-038.
4377
4378 @param inputListName Name of input charged particle list to be scaled
4379 @param outputListName Name of output charged particle list with scaled error
4380 @param scaleFactors List of five constants to be multiplied to each of helix errors (for tracks with a PXD hit)
4381 @param scaleFactorsNoPXD List of five constants to be multiplied to each of helix errors (for tracks without a PXD hit)
4382 @param d0Resolution List of two parameters, (a [cm], b [cm/(GeV/c)]),
4383 defining d0 best resolution as sqrt{ a**2 + (b / (p*beta*sinTheta**1.5))**2 }
4384 @param z0Resolution List of two parameters, (a [cm], b [cm/(GeV/c)]),
4385 defining z0 best resolution as sqrt{ a**2 + (b / (p*beta*sinTheta**2.5))**2 }
4386 @param d0MomThr d0 best resolution is kept constant below this momentum
4387 @param z0MomThr z0 best resolution is kept constant below this momentum
4388
4389 """
4390
4391 scale_error = register_module("HelixErrorScaler")
4392 scale_error.set_name('ScaleError_' + inputListName)
4393 scale_error.param('inputListName', inputListName)
4394 scale_error.param('outputListName', outputListName)
4395 scale_error.param('scaleFactors_PXD', scaleFactors)
4396 scale_error.param('scaleFactors_noPXD', scaleFactorsNoPXD)
4397 scale_error.param('d0ResolutionParameters', d0Resolution)
4398 scale_error.param('z0ResolutionParameters', z0Resolution)
4399 scale_error.param('d0MomentumThreshold', d0MomThr)
4400 scale_error.param('z0MomentumThreshold', z0MomThr)
4401 path.add_module(scale_error)
4402
4403
4404def estimateAndAttachTrackFitResult(inputListName, path=None):
4405 """
4406 Create a TrackFitResult from the momentum of the Particle assuming it originates from the IP and make a relation between them.
4407 The covariance, detector hit information, and fit-related information (pValue, NDF) are assigned meaningless values. The input
4408 Particles must not have already Track or TrackFitResult and thus are supposed to be composite particles, recoil, dummy
4409 particles, and so on.
4410
4411
4412 .. warning:: Since the source type is not overwritten as Track, not all track-related variables are guaranteed to be available.
4413
4414
4415 @param inputListName Name of input ParticleList
4416 """
4417
4418 estimator = register_module("TrackFitResultEstimator")
4419 estimator.set_name("trackFitResultEstimator_" + inputListName)
4420 estimator.param("inputListName", inputListName)
4421 path.add_module(estimator)
4422
4423
4424def correctEnergyBias(inputListNames, tableName, path=None):
4425 """
4426 Scale energy of the particles according to the scaling factor.
4427 If the particle list contains composite particles, the energy of the daughters are scaled.
4428 Subsequently, the energy of the mother particle is updated as well.
4429
4430 Parameters:
4431 inputListNames (list(str)): input particle list names
4432 tableName : stored in localdb and created using ParticleWeightingLookUpCreator
4433 path (basf2.Path): module is added to this path
4434 """
4435
4436 import b2bii
4437 if b2bii.isB2BII():
4438 B2ERROR("The energy bias cannot be corrected with this tool for Belle data.")
4439
4440 correctenergybias = register_module('EnergyBiasCorrection')
4441 correctenergybias.param('particleLists', inputListNames)
4442 correctenergybias.param('tableName', tableName)
4443 path.add_module(correctenergybias)
4444
4445
4446def twoBodyISRPhotonCorrector(outputListName, inputListName, massiveParticle, path=None):
4447 """
4448 Sets photon kinematics to corrected values in two body decays with an ISR photon
4449 and a massive particle. The original photon kinematics are kept in the input
4450 particleList and can be accessed using the originalParticle() metavariable on the
4451 new list.
4452
4453 @param ouputListName new ParticleList filled with copied Particles
4454 @param inputListName input ParticleList with original Particles
4455 @param massiveParticle name or PDG code of massive particle participating in the two
4456 body decay with the ISR photon
4457 @param path modules are added to this path
4458 """
4459
4460 # set the corrected energy of the photon in a new list
4461 photon_energy_correction = register_module('TwoBodyISRPhotonCorrector')
4462 photon_energy_correction.set_name('TwoBodyISRPhotonCorrector_' + outputListName)
4463 photon_energy_correction.param('outputGammaList', outputListName)
4464 photon_energy_correction.param('inputGammaList', inputListName)
4465
4466 # prepare PDG code of massive particle
4467 if isinstance(massiveParticle, int):
4468 photon_energy_correction.param('massiveParticlePDGCode', massiveParticle)
4469 else:
4470 from ROOT import Belle2
4471 decayDescriptor = Belle2.DecayDescriptor()
4472 if not decayDescriptor.init(massiveParticle):
4473 raise ValueError("TwoBodyISRPhotonCorrector: value of massiveParticle must be" +
4474 " an int or valid decay string.")
4475 pdgCode = decayDescriptor.getMother().getPDGCode()
4476 photon_energy_correction.param('massiveParticlePDGCode', pdgCode)
4477
4478 path.add_module(photon_energy_correction)
4479
4480
4481def addPhotonEfficiencyRatioVariables(inputListNames, tableName, path=None):
4482 """
4483 Add photon Data/MC detection efficiency ratio weights to the specified particle list
4484
4485 Parameters:
4486 inputListNames (list(str)): input particle list names
4487 tableName : taken from database with appropriate name
4488 path (basf2.Path): module is added to this path
4489 """
4490
4491 import b2bii
4492 if b2bii.isB2BII():
4493 B2ERROR("For Belle data the photon data/MC detection efficiency ratio is not available with this tool.")
4494
4495 photon_efficiency_correction = register_module('PhotonEfficiencySystematics')
4496 photon_efficiency_correction.param('particleLists', inputListNames)
4497 photon_efficiency_correction.param('tableName', tableName)
4498 path.add_module(photon_efficiency_correction)
4499
4500
4501def addPi0VetoEfficiencySystematics(particleList, decayString, tableName, threshold, mode='standard', suffix='', path=None):
4502 """
4503 Add pi0 veto Data/MC efficiency ratio weights to the specified particle list
4504
4505 @param particleList the input ParticleList
4506 @param decayString specify hard photon to be performed pi0 veto (e.g. 'B+:sig -> rho+:sig ^gamma:hard')
4507 @param tableName table name corresponding to payload version (e.g. 'Pi0VetoEfficiencySystematics_Mar2022')
4508 @param threshold pi0 veto threshold (0.10, 0.11, ..., 0.99)
4509 @param mode choose one mode (same as writePi0EtaVeto) out of 'standard', 'tight', 'cluster' and 'both'
4510 @param suffix optional suffix to be appended to the usual extraInfo name
4511 @param path the module is added to this path
4512
4513 The following extraInfo are available related with the given particleList:
4514
4515 * Pi0VetoEfficiencySystematics_{mode}{suffix}_data_MC_ratio : weight of Data/MC for the veto efficiency
4516 * Pi0VetoEfficiencySystematics_{mode}{suffix}_data_MC_uncertainty_stat : the statistical uncertainty of the weight
4517 * Pi0VetoEfficiencySystematics_{mode}{suffix}_data_MC_uncertainty_sys : the systematic uncertainty of the weight
4518 * Pi0VetoEfficiencySystematics_{mode}{suffix}_data_MC_uncertainty_total : the total uncertainty of the weight
4519 * Pi0VetoEfficiencySystematics_{mode}{suffix}_threshold : threshold of the pi0 veto
4520 """
4521
4522 import b2bii
4523 if b2bii.isB2BII():
4524 B2ERROR("For Belle data the pi0 veto data/MC efficiency ratio weights are not available via this tool.")
4525
4526 pi0veto_efficiency_correction = register_module('Pi0VetoEfficiencySystematics')
4527 pi0veto_efficiency_correction.param('particleLists', particleList)
4528 pi0veto_efficiency_correction.param('decayString', decayString)
4529 pi0veto_efficiency_correction.param('tableName', tableName)
4530 pi0veto_efficiency_correction.param('threshold', threshold)
4531 pi0veto_efficiency_correction.param('mode', mode)
4532 pi0veto_efficiency_correction.param('suffix', suffix)
4533 path.add_module(pi0veto_efficiency_correction)
4534
4535
4536def getAnalysisGlobaltag(timeout=180) -> str:
4537 """
4538 Returns a string containing the name of the latest and recommended analysis globaltag.
4539
4540 Parameters:
4541 timeout: Seconds to wait for b2conditionsdb-recommend
4542 """
4543
4544 import b2bii
4545 if b2bii.isB2BII():
4546 B2ERROR("The getAnalysisGlobaltag() function cannot be used for Belle data.")
4547
4548 # b2conditionsdb-recommend relies on a different repository, so it's better to protect
4549 # this function against potential failures of check_output.
4550 try:
4551 tags = subprocess.check_output(
4552 ['b2conditionsdb-recommend', '--oneline'],
4553 timeout=timeout
4554 ).decode('UTF-8').rstrip().split(' ')
4555 analysis_tag = ''
4556 for tag in tags:
4557 if tag.startswith('analysis_tools'):
4558 analysis_tag = tag
4559 return analysis_tag
4560 # In case of issues with git, b2conditionsdb-recommend may take too much time.
4561 except subprocess.TimeoutExpired as te:
4562 B2FATAL(f'A {te} exception was raised during the call of getAnalysisGlobaltag(). '
4563 'The function took too much time to retrieve the requested information '
4564 'from the versioning repository.\n'
4565 'Please try to re-run your job. In case of persistent failures, there may '
4566 'be issues with the DESY collaborative services, so please contact the experts.')
4567 except subprocess.CalledProcessError as ce:
4568 B2FATAL(f'A {ce} exception was raised during the call of getAnalysisGlobaltag(). '
4569 'Please try to re-run your job. In case of persistent failures, please contact '
4570 'the experts.')
4571
4572
4573def getAnalysisGlobaltagB2BII() -> str:
4574 """
4575 Get recommended global tag for B2BII analysis.
4576 """
4577
4578 import b2bii
4579 if not b2bii.isB2BII():
4580 B2ERROR('The getAnalysisGlobaltagB2BII() function cannot be used for Belle II data.')
4581 from versioning import recommended_b2bii_analysis_global_tag
4582 return recommended_b2bii_analysis_global_tag()
4583
4584
4585def getECLKLID(particleList: str, variable='ECLKLID', path=None):
4586 """
4587 The function calculates the PID value for Klongs that are constructed from ECL cluster.
4588
4589 @param particleList the input ParticleList
4590 @param variable the variable name for Klong ID
4591 @param path modules are added to this path
4592 """
4593
4594 import b2bii
4595
4596 if b2bii.isB2BII():
4597 B2ERROR("The ECL variables based Klong Identification is only available for Belle II data.")
4598
4599 from variables import variables
4600 path.add_module('MVAExpert', listNames=particleList, extraInfoName='ECLKLID', identifier='ECLKLID')
4601
4602 variables.addAlias(variable, 'conditionalVariableSelector(isFromECL and PDG==130, extraInfo(ECLKLID), constant(NaN))')
4603
4604
4605def getNbarIDMVA(particleList: str, path=None):
4606 """
4607 This function can give a score to predict if it is a anti-n0.
4608 It is not used to predict n0.
4609 Currently, this can be used only for ECL cluster.
4610 output will be stored in extraInfo(nbarID); -1 means MVA invalid
4611
4612 @param particleList The input ParticleList name or a decay string which contains a full mother particle list name.
4613 Only one selected daughter is supported.
4614 @param path modules are added to this path
4615 """
4616 import b2bii
4617 from ROOT import Belle2
4618
4619 if b2bii.isB2BII():
4620 B2ERROR("The MVA-based anti-neutron PID is only available for Belle II data.")
4621
4622 from variables import variables
4623
4624 variables.addAlias('V1', 'clusterHasPulseShapeDiscrimination')
4625 variables.addAlias('V2', 'clusterE')
4626 variables.addAlias('V3', 'clusterLAT')
4627 variables.addAlias('V4', 'clusterE1E9')
4628 variables.addAlias('V5', 'clusterE9E21')
4629 variables.addAlias('V6', 'clusterZernikeMVA')
4630 variables.addAlias('V7', 'clusterAbsZernikeMoment40')
4631 variables.addAlias('V8', 'clusterAbsZernikeMoment51')
4632
4633 variables.addAlias(
4634 'nbarIDValid',
4635 'passesCut(V1 == 1 and V2 >= 0 and V3 >= 0 and V4 >= 0 and V5 >= 0 and V6 >= 0 and V7 >= 0 and V8 >= 0)')
4636 variables.addAlias('nbarIDmod', 'conditionalVariableSelector(nbarIDValid == 1, extraInfo(nbarIDFromMVA), constant(-1.0))')
4637
4638 path.add_module('MVAExpert', listNames=particleList, extraInfoName='nbarIDFromMVA', identifier='db_nbarIDECL')
4639 decayDescriptor = Belle2.DecayDescriptor()
4640 if not decayDescriptor.init(particleList):
4641 raise ValueError(f"Provided decay string is invalid: {particleList}")
4642 if decayDescriptor.getNDaughters() == 0:
4643 variablesToExtraInfo(particleList, {'nbarIDmod': 'nbarID'}, option=2, path=path)
4644 else:
4645 listname = decayDescriptor.getMother().getFullName()
4646 variablesToDaughterExtraInfo(listname, particleList, {'nbarIDmod': 'nbarID'}, option=2, path=path)
4647
4648
4649def reconstructDecayWithNeutralHadron(decayString, cut, allowGamma=False, allowAnyParticleSource=False, path=None, **kwargs):
4650 r"""
4651 Reconstructs decay with a long-lived neutral hadron e.g.
4652 :math:`B^0 \to J/\psi K_L^0`,
4653 :math:`B^0 \to p \bar{n} D^*(2010)^-`.
4654
4655 The calculation is done with IP constraint and mother mass constraint.
4656
4657 The decay string passed in must satisfy the following rules:
4658
4659 - The neutral hadron must be **selected** in the decay string with the
4660 caret (``^``) e.g. ``B0:sig -> J/psi:sig ^K_L0:sig``. (Note the caret
4661 next to the neutral hadron.)
4662 - There can only be **one neutral hadron in a decay**.
4663 - The neutral hadron has to be a direct daughter of its mother.
4664
4665 .. note:: This function forwards its arguments to `reconstructDecay`,
4666 so please check the documentation of `reconstructDecay` for all
4667 possible arguments.
4668
4669 @param decayString A decay string following the mentioned rules
4670 @param cut Cut to apply to the particle list
4671 @param allowGamma Whether allow the selected particle to be ``gamma``
4672 @param allowAnyParticleSource Whether allow the selected particle to be from any source.
4673 Should only be used when studying control sample.
4674 @param path The path to put in the module
4675 """
4676
4677 reconstructDecay(decayString, cut, path=path, **kwargs)
4678 module = register_module('NeutralHadron4MomentumCalculator')
4679 module.set_name('NeutralHadron4MomentumCalculator_' + decayString)
4680 module.param('decayString', decayString)
4681 module.param('allowGamma', allowGamma)
4682 module.param('allowAnyParticleSource', allowAnyParticleSource)
4683 path.add_module(module)
4684
4685
4686def updateMassHypothesis(particleList, pdg, writeOut=False, path=None):
4687 """
4688 Module to update the mass hypothesis of a given input particle list with the chosen PDG.
4689 A new particle list is created with updated mass hypothesis.
4690 The allowed mass hypotheses for both input and output are electrons, muons, pions, kaons and protons.
4691
4692 .. note:
4693 The new particle list is named after the input one, with the additional suffix ``_converted_from_OLDHYPOTHESIS``,
4694 e.g. ``e+:all`` converted to muons becomes ``mu+:all_converted_from_e``.
4695
4696 @param particleList The input particle list name
4697 @param pdg The PDG code for the new mass hypothesis, in [11, 13, 211, 321, 2212]
4698 @param writeOut Whether `RootOutput` module should save the new particle list
4699 @param path Modules are added to this path
4700 """
4701 mass_updater = register_module("ParticleMassHypothesesUpdater")
4702 mass_updater.set_name("ParticleMassHypothesesUpdater_" + particleList + "_to_" + str(pdg))
4703 mass_updater.param("particleList", particleList)
4704 mass_updater.param("writeOut", writeOut)
4705 mass_updater.param("pdgCode", pdg)
4706 path.add_module(mass_updater)
4707
4708
4709func_requiring_analysisGT = [
4710 correctTrackEnergy, scaleTrackMomenta, smearTrackMomenta, oldwritePi0EtaVeto, writePi0EtaVeto, lowEnergyPi0Identification,
4711 getBeamBackgroundProbability, getFakePhotonProbability, tagCurlTracks, applyChargedPidMVA, correctEnergyBias,
4712 addPhotonEfficiencyRatioVariables, addPi0VetoEfficiencySystematics, getNbarIDMVA, getECLKLID]
4713for _ in func_requiring_analysisGT:
4714 _.__doc__ += "\n .. note:: This function (optionally) requires a payload stored in the analysis GlobalTag. "\
4715 "Please append or prepend the latest one from `getAnalysisGlobaltag` or `getAnalysisGlobaltagB2BII`.\n"
4716
4717
4718if __name__ == '__main__':
4719 from basf2.utils import pretty_print_module
4720 pretty_print_module(__name__, "modularAnalysis")
isB2BII()
Definition b2bii.py:14
setB2BII()
Definition b2bii.py:21
tuple parse(str cut, verbose=False)
Definition b2parser.py:981
This class provides a set of constants for the framework.
Definition Const.h:34
The DecayDescriptor stores information about a decay tree or parts of a decay tree.
Describe one component of the Geometry.
Magnetic field map.
static DBStore & Instance()
Instance of a singleton DBStore.
Definition DBStore.cc:26
add_mdst_output(path, mc=True, filename='mdst.root', additionalBranches=[], dataDescription=None)
Definition mdst.py:37
from_name(name)
Definition pdg.py:63
add_udst_output(path, filename, particleLists=None, additionalBranches=None, dataDescription=None, mc=True)
Definition udst.py:27