Belle II Software  release-05-01-25
best_candidate_selection.py
1 import basf2
2 import math
3 import random
4 from collections import defaultdict
5 import modularAnalysis as anal
6 from ROOT import Belle2
7 
8 
9 class Generator(basf2.Module):
10  """Generate a list of 10 electrons which have stupid momenta just to sort
11  them later. And then add one electron where all momentum components are
12  nan"""
13 
14  def initialize(self):
15  """We need to register the mc particles"""
16 
17  self.mcp = Belle2.PyStoreArray("MCParticles")
18  self.mcp.registerInDataStore()
19 
20  def event(self):
21  """And then we generate particles"""
22  print("New event:")
23  for i in range(10):
24  p = self.mcp.appendNew()
25  p.setPDG(11)
26  p.setMassFromPDG()
27  p.setMomentum(random.randrange(1, 5), random.randrange(1, 5), random.randrange(1, 5))
28 
29  p = self.mcp.appendNew()
30  p.setPDG(11)
31  p.setMassFromPDG()
32  p.setMomentum(math.nan, math.nan, math.nan)
33 
34 
35 class RankChecker(basf2.Module):
36  """Check if the ranks are actually what we want"""
37 
38  def initialize(self):
39  """Create particle list object"""
40 
41  self.plist = Belle2.PyStoreObj("e-")
42 
43  def event(self):
44  """And check all the ranks"""
45  # make a list of all the values and a dict of all the exta infos
46  px = []
47  py = []
48  einfo = defaultdict(list)
49  for p in self.plist:
50  px.append(p.getPx())
51  py.append(p.getPy())
52  names = p.getExtraInfoNames()
53  for n in names:
54  einfo[n].append(p.getExtraInfo(n))
55 
56  # check the default name is set correctly if we don't specify an output variable
57  print(list(einfo.keys()))
58  assert 'M_rank' in einfo.keys(), "Default name is not as expected"
59 
60  # Now determine the correct ranks if multiple values are allowed:
61  # create a dictionary which will be value -> rank for all unique values
62  # in theory we just need loop over the sorted(set(values)) but we have
63  # special treatment for nans which should go always to the end of the
64  # list so sort with a special key that replaces nan by inf or -inf
65  # depending on sort order
66  px_value_ranks = {v: i for i, v in enumerate(sorted(set(px), reverse=True,
67  key=lambda v: -math.inf if math.isnan(v) else v), 1)}
68  py_value_ranks = {v: i for i, v in enumerate(sorted(set(py),
69  key=lambda v: math.inf if math.isnan(v) else v), 1)}
70 
71  # Ok, test if the rank from extra info actually corresponds to what we
72  # want
73  for v, r in zip(px, einfo["px_high_multi"]):
74  print(f"Value: {v}, rank: {r}, should be: {px_value_ranks[v]}")
75  assert r == px_value_ranks[v], "Rank is not correct"
76 
77  for v, r in zip(py, einfo["py_low_multi"]):
78  print(f"Value: {v}, rank: {r}, should be: {py_value_ranks[v]}")
79  assert r == py_value_ranks[v], "Rank is not correct"
80 
81  # so we checked multiRank=True. But for multiRank=False this is more
82  # complicated because ranking a second time will destroy the order
83  # of the previous sorts. But we can at least check if all the ranks
84  # form a range from 1..n if we sort them
85  simple_range = list(range(len(px)))
86  px_single_ranks = list(sorted(int(r) - 1 for r in einfo["px_high_single"]))
87  assert simple_range == px_single_ranks, "sorted ranks don't form a range from 1..n"
88  # but the second two rankings are on the same variable in the same
89  # order so they need to keep the order stable. so for py_low_single the
90  # ranks need to be the range without sorting
91  py_single_ranks = list(int(r) - 1 for r in einfo["py_low_single"])
92  assert simple_range == py_single_ranks, "ranks don't form a range from 1..n"
93 
94 
95 # fixed random numbers
96 random.seed(5)
97 # so lets create 10 events
98 path = basf2.Path()
99 path.add_module("EventInfoSetter", evtNumList=10)
100 # and put some electrons in there
101 path.add_module(Generator())
102 # load these electrons
103 anal.fillParticleListFromMC("e-", "", path=path)
104 # and sort them ...
105 anal.rankByHighest("e-", "M", path=path)
106 anal.rankByHighest("e-", "px", allowMultiRank=False, outputVariable="px_high_single", path=path)
107 anal.rankByHighest("e-", "px", allowMultiRank=True, outputVariable="px_high_multi", path=path)
108 anal.rankByLowest("e-", "py", allowMultiRank=False, outputVariable="py_low_single", path=path)
109 anal.rankByLowest("e-", "py", allowMultiRank=True, outputVariable="py_low_multi", path=path)
110 # and also check sorting
111 path.add_module(RankChecker())
112 
113 # we set numBest = 2: this is used also for the assert
114 numBest_value = 2
115 
116 
117 class NumBestChecker(basf2.Module):
118  """Check if 'numBest' works correctly"""
119 
120  def initialize(self):
121  """Create particle list 'e-:numbest' object"""
122 
123  self.plist = Belle2.PyStoreObj('e-:numBest')
124 
125  def event(self):
126  """Check if 'e-:numBest' has the expected size"""
127  size = self.plist.getListSize()
128  # The test fails if size > numBest_value
129  # since we will set numBest = numBest_value
130  assert size <= numBest_value, 'numBest test failed: there are too many Particles in the list!'
131 
132 
133 # create a new list
134 anal.fillParticleListFromMC('e-:numBest', '', path=path)
135 # sort the list, using numBest
136 anal.rankByHighest('e-:numBest', 'M', numBest=numBest_value, path=path)
137 # and check that numBest worked as expected
138 path.add_module(NumBestChecker())
139 
140 basf2.process(path)
best_candidate_selection.RankChecker.initialize
def initialize(self)
Definition: best_candidate_selection.py:38
best_candidate_selection.NumBestChecker
Definition: best_candidate_selection.py:117
best_candidate_selection.RankChecker
Definition: best_candidate_selection.py:35
best_candidate_selection.Generator.event
def event(self)
Definition: best_candidate_selection.py:20
Belle2::PyStoreObj
a (simplified) python wrapper for StoreObjPtr.
Definition: PyStoreObj.h:69
basf2.process
def process(path, max_event=0)
Definition: __init__.py:25
best_candidate_selection.Generator.mcp
mcp
MCParticle array.
Definition: best_candidate_selection.py:17
best_candidate_selection.NumBestChecker.event
def event(self)
Definition: best_candidate_selection.py:125
best_candidate_selection.RankChecker.plist
plist
particle list object
Definition: best_candidate_selection.py:41
best_candidate_selection.NumBestChecker.plist
plist
ParticleList object.
Definition: best_candidate_selection.py:123
best_candidate_selection.NumBestChecker.initialize
def initialize(self)
Definition: best_candidate_selection.py:120
Belle2::PyStoreArray
a (simplified) python wrapper for StoreArray.
Definition: PyStoreArray.h:58
best_candidate_selection.RankChecker.event
def event(self)
Definition: best_candidate_selection.py:43
best_candidate_selection.Generator.initialize
def initialize(self)
Definition: best_candidate_selection.py:14
best_candidate_selection.Generator
Definition: best_candidate_selection.py:9