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