Belle II Software development
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.
23from variables import variables as vm
24
25# More utilities for managing variables
26import 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.
31vm.addAlias('d0_x', 'daughter(0, x)')
32vm.addAlias('d0_y', 'daughter(0, y)')
33vm.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:
43vu.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
58vu.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:
65vu.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.
76vu.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:
91vu.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:
108vu.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
120vm.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
123vm.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
133vm.printAliases()