Belle II Software  release-05-01-25
variableAliases.py
1 #!/usr/bin/env python3
2 
3 # Tired of using long names like ``genUpsilon4S(daughter(0, daughter(0, PDG)))``
4 # (which become even less readable in its ROOT compatible form as demonstrated
5 # in variableManager.py)?
6 # Well, despair no longer! With so called "aliases", you can define handy short
7 # names for your complicated variables.
8 #
9 # Contributors: Kilian Lieret (2020)
10 #
11 # For full documentation please refer to https://software.belle2.org
12 # Anything unclear? Ask questions at https://questions.belle2.org
13 #
14 
15 # vm is our shorthand name for the VariableManager instance. The VariableManager
16 # is responsible for the bookkeeping of our alias definitions.
17 from variables import variables as vm
18 
19 # More utilities for managing variables
20 import variables.utils as vu
21 
22 
23 # Let's start small and explicitly define alias for some simple variables:
24 # 'd0_x' is now shorthand for 'daughter(0, x)', etc.
25 vm.addAlias('d0_x', 'daughter(0, x)')
26 vm.addAlias('d0_y', 'daughter(0, y)')
27 vm.addAlias('d0_z', 'daughter(0, z)')
28 
29 # After each step, you can use vm.printAliases() to print a list of all
30 # aliases currently defined. In this tutorial we only do it once at the end
31 # to avoid cluttering the output.
32 
33 # Now as you see, we have a clear naming convention in our head, but typing it
34 # all out will quickly become cumbersome if we have lots of variables for lots
35 # of daughters.
36 # Let's define similar variables for the momenta, but with just one expression:
37 vu.create_aliases(
38  list_of_variables=["px", "py", "pz"],
39  wrapper="daughter(0, {variable})",
40  prefix="d0", # (an underscore is automatically appended to the prefix)
41 )
42 # {variable} is replaced by the name of each variable, so we just defined
43 # the aliases d0_px, d0_py, ...
44 # Adding more variables to the first daughter is now much easier, because we
45 # only have to add them to list_of_variables.
46 
47 # But in fact, this can be done even more conveniently, because the variable
48 # utils provide us with a dedicated convenience function just to define
49 # aliases for daughter(.., ..) variables (it's very common after all) Let's
50 # create aliases for the second daughter
51 
52 vu.create_daughter_aliases(
53  list_of_variables=["x", "y", "z", "px", "py", "pz"],
54  indices=1,
55 )
56 
57 # In fact, we can also use create_daughter_aliases to create aliases for
58 # grand(grand(grand))-daughters:
59 vu.create_daughter_aliases(
60  list_of_variables=["PDG"],
61  indices=(0, 0, 0),
62 )
63 # This will create the alias
64 # d0_d0_d0_PDG -> daughter(0, daughter(0, daughter(0, PDG)))
65 # As before, you can also specify a prefix with the prefix keyword argument.
66 
67 # Another similarly easy convenience function exists for MC truth variables,
68 # i.e. ``matchedMC(variable)``, which returns the value of the variable for the
69 # truth particle matched to the particle at hand.
70 vu.create_mctruth_aliases(
71  list_of_variables=["x", "y", "z"],
72 )
73 # This creates mc_x, mc_y, etc. You can also customize the prefix with the
74 # optional prefix keyword argument.
75 
76 # Let's finally visit one of the more complex convenience functions offered:
77 # create_aliases_for_selected(...). Let's say you're looking at the decay
78 # B0 -> [D0 -> pi+ K-] pi0 and want to create variables for the pi^+.
79 # Of course we could simply do that by hand or by using create_daughter_aliases
80 # (after all, the pi+ is simply daughter(0, daughter(0, ...))).
81 # But there's another way, directly from the decay string: Remember that you
82 # can mark a particle with ``^``: ``B0 -> [D0 -> pi+ K-] pi0``.
83 # We use that annotation to tell the variable utils which alias we want to
84 # create:
85 vu.create_aliases_for_selected(
86  list_of_variables=["x", "y", "z"],
87  decay_string="B0 -> [D0 -> ^pi+ K-] pi0",
88 )
89 # The shorthand name that will be defined by this is by default
90 # the names of all parent particle names separated by a underscore (if unique).
91 # If there are more particles with the same name, the daughter indices will
92 # be appended.
93 # We can also choose to use a similar naming convention as before:
94 # d0_d0_... (using daughter indices). For this, add use_names=False to the
95 # options.
96 # Take a look at the documentation for create_aliases_for_selected for more
97 # options and details regarding the naming convention!
98 
99 # Now the last example is not much shorter than what we did above with
100 # create_daughter_aliases. But that changes, because we can actually select
101 # more than one particle:
102 vu.create_aliases_for_selected(
103  list_of_variables=["x", "y", "z"],
104  decay_string="B0 -> [^D0 -> pi+ ^K-] ^pi0",
105 )
106 # This automatically creates simple aliases for the coordinates in the
107 # remaining particles in the decay!
108 
109 # As a final word of warning, keep in mind that aliases are defined globally
110 # and are not associated with particle lists. Disregarding this often leads to
111 # clashing aliases and confusion.
112 # Let's consider a practical example. Say you're considering D0 candidates from
113 # D0 -> K- pi+ and define
114 vm.addAlias("K_px", "daughter(0, px)")
115 # But later (after reconstructing your B meson), you're looking
116 # at the list of B candidates with B+ -> [D0 -> K- pi+] and define
117 vm.addAlias("K_px", "daughter(0, daughter(0, px))")
118 # This last definition will supersede the previous one!
119 # At least basf2 is nice enough to warn you about this and displays
120 # "[WARNING] An alias with the name 'K_px' exists and is set to
121 # 'daughter(0, px)', setting it to 'daughter(0, daughter(0, px))'.
122 # Be aware: only the last alias defined before processing the events will be
123 # used!"
124 
125 
126 # Let's print all of our aliases
127 vm.printAliases()
variables.utils
Definition: utils.py:1