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