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