Particle Selection Toolkit
Introduction
This wiki explains the use of new python wrapper classes that are intended to simplify both the writing and use of particle selections in
DaVinci. The classes are released in the new package
PhysSel/PhysSelPython
, which is part of the
PHYS
project, and are particularly powerful when it comes to building a selection out of other selections. They do away with the need to use
importOptions
, and hence to know the directories where different selections are located. It also returns to the (python) user useful information and data structures that simplify the use of the selections in a typical DaVinci job, but also in jobs that may analyse the DST or
MicroDST output of a DaVinci job. Furthermore, the fact that the selections are written in python modules allows to make use of the inbuilt python documentation framework, so authors of selectors can make the necessary details about a selection available to users in an interactive python environment.
Warning
This wiki explains how to write a selection starting from scratch (i.e. final state particles).
You should not do that on a stripped DST. In that case use the candidate available on the DST.
DaVinciTutorial8 explains how to use the selection framework to do this. You can simply make a DST location of Particles look like a Selection by using
AutomaticData
.
Motivation
In most cases GaudiSequencers are full of algorithms, with each algorithm requiring data from and providing data to others. Currently, these are defined in one or more
.py
files, which are obtained via
importOptions
. These are no more than glorified
.opts
files, and they share many of their disadvantages:
- The need to know the location of the file defining the GaudiSequencer. Normally this requires having certain environment variables set.
- The need to know the name of the sequencer, so it can be grabbed and added to the application manager.
- The need to know which one of the algorithms in the sequencer is the one writing the data out, i.e. the one performing the selection we care about.
- The need to look at the file (or files imported to it via
importOptions
) to get information about, for example, the cuts applied.
- When the sequencer is defined in the same file as the algorithms that the top selection depends on, one cannot get these algorithms without also instantiating the top algorithm plus the sequencer. This is not a big problem, but it seems unnecessary.
This is an absurd and error-prone situation. The selection "framework" (it is really two concepts expressed in a handful of simple python classes) uses python concepts to address these issues.
Selection toolkit concepts and classes
The building blocks of the selection toolkit are the
Selection and the
SelectionSequence. A
Selection is a basic unit that either passes or fails, and may write LHCb::Particles to a location which it exports with the
outputLocation()
method. It knows about other Selections that it requires to pass in order to obtain input particles. A
SelectionSequence essentially takes a
Selection object, resolves its
Selection requirements, and makes a flat list of Selections. It exports a self-contained
GaudiSequencer with all the algorithm configurables necessary to perform the selection via the
sequence()
method. It also allows the user to specify a set of algorithms to run before or after the selection sequence. The building blocks are designed to be configured at construction, and do not support and changes to configuration. This is the only way they can be safely re-used, and the only way one can be sure that the intended configuration is what has been used in a program. Components that take a name as constructor argument create a Gaudi Configurable internally, so there is a mechanism in place that only allows a given name to be used once. The Selection Toolkit is thus very different to the plain Gaudi Configurables, and users should remember this. Fortunately, disallowed operations result in exceptions, so there is little chance of misconfiguring a job.
Selection
This is the building block of the framework. A Selection is a python entity that uses other Selections to build particles and write them to a TES location. The TES location is exported to the user via the
outputLocation()
method, and the Selections needed via the
RequiredSelections
field. A Selection requires all its
RequiredSelections
to pass. In itself, it does nothing, since it relies on its required selections to be run beforehand. It is expressed in three particle-producing classes:
-
Selection
.
-
MergedSelection
.
-
AutomaticData
(also known as DataOnDemand
).
There is a special Selection class that is not designed to write data, it simply accepts or rejects an event, but otherwise looks just like any other Selection:
There are several helper
selections that coudl be useful for certain cases
-
PrintSelection
allows to print the content of selection (for debugging)
-
LimitSelection
allows to set a limit of numbers of candidates in selection
Both are specializations of very powerful and flexible
PassThroughSelection
SelectionSequence
A SelectionSequence is a model of an entity that contains all the necessary Selections needed for a selection chain to work, and exports these in a correctly ordered and configured GaudiSequencer via the method
sequence()
. It also makes the output locations of the data written by the head of the decay chain available via the
outputLocations()
method. There are two implementations of the SelectionSequence.
- The
SelectionSequence
class is a python entity that, given a single Selection instance (refered to as "top selection" from now on), recursively iterates through all the RequiredSelections to create an ordered list of all the Selections that must be run. It creates a GaudiAlgorithm with all the necessary algorithm Configurables. This can be passed directly to the application manager and are accessible via the method sequence()
. The output TES location of the top selection is available via the method outputLocations()
. The SelectionSequence is the glue that sticks the Sequence building blocks together.
- The
MultiSelectionSequence
class is a python entity that is instantiated from an arbitrary number of SelectionSequences. It's sequence()
method returns a GaudiSequencer with the sequences of its component SelectionSequences in OR mode. This is used to run a selection which is the OR of a series of independent Selections.
The
StrippingStream
class conforms to the SelectionSequence concept, but needs to be processed with a
StrippingConf
before it's fields can be considered to be consistent. This means a correctly created
StrippingStream
object can be used just like any other SelectionSequence.
Getting help: documentation
To get the most up-to-date information on the building block classes, it is simplest to look at the python documentation:
# set some environment that gives us the necessary $PYTHON_PATH
SetupProject DaVinci v25r6
python
>>> from PhysSelPython.Wrappers import Selection, SelectionSequence, DataOnDemand, AutomaticData, MultiSelectionSequence, MergedSelection
>>> help(Selection)
>>> help(SelectionSequence)
>>> help(DataOnDemand) # same as AutomaticData
>>> help(MultiSelectionSequence)
>>> help(MergedSelection)
Particle selection implementations in python modules
Instances of the above can be defined in python modules, such that they can be re-used in other modules or directly in scripts. The module structure can be used to reflect the nature of the selections (for example, one can easily guess what
StrippingSelections.FredsCrazyBs2JpsiPhi.Phi
or
OfflineSelections.PrescaledJps2MuMu.Jpsi
could mean). Furthermore, python documentation tools can be used to provide information about the details of each component of a selection. There is currently no convention on how to set up the "raw" modules, but this can be changed a posteriory by use of master modules that import different components of the "raw" ones. Therefore, do not pay too much attention to the module structure in the examples below, for they are just one way of doing things.
Examples
There are a many working examples in CVS,
-
Ex/MicroDSTExample/python/MicroDSTExample/Selections
, tag v2r10
or newer (in ANALYSIS
project)
-
PhysSel/B2DsX/python/B2DsX
, tag jpalac_20090716
or newer.
-
PhysSel/Ccbar/python/Ccbar
.
-
Phys/StrippingSelections/python/StrippingSelections
has working examples for three different Bs2JpsiPhi
selections (search for StrippingBs2JpsiPhi*.py
). Phys/StrippingSelections/python/StrippingSelections/StreamBMuon
shows how a StrippingStream
is made out of them.
In the following sections, we give examples on how to write a new selection using these python classes. Pay no attention to the actual cuts applied, these are not necessarily good selections!
Building a simple selection algorithm using standard input particles
Here, we will build a J/Psi
Selection
algorithm that only requires some standard muons, which are available on-demand. Assume this selection is called
MyJpsi.py
and is in python module
JpsiSelections
. The standard muons have to be wrapped with the
DataOnDemand
class, which mimicks a
Selection
.
import GaudiKernel.SystemOfUnits as Units
from Gaudi.Configuration import *
from Configurables import FilterDesktop
# Wrap StdLooseMuons
SelStdLooseMuons = DataOnDemand(Location = 'Phys/StdLooseDiMuon/Particles') # Phys/StdLooseDiMuon doesn't find the particles ==> added /Particles
_MyJpsi = FilterDesktop('MyJpsi')
_MyJpsi.Code = """
(MAXTREE('mu+'==ABSID, TRCHI2DOF) < 5.0)
& (MINTREE('mu+'==ABSID, PIDmu) > -5.0)
& (MAXTREE('mu+'==ABSID, PIDK) < 5.0)
& (ADMASS('J/psi(1S)') < 42.*MeV)
"""
from PhysSelPython.Wrappers import Selection
SelMyJpsi = Selection("SelMyJpsi", Algorithm = _MyJpsi, RequiredSelections = [SelStdLooseMuons] )
Note that the
InputLocations
of the
FilterDesktop
are not set. These are set by the
Selection
constructor, and are taken from the
RequiredSelections
arguments.
Building a selection algorithm that uses other selection algorithms
Here, we build a
Selection
that takes as input particles from two
Selections
previously defined as described above. Assume that a selection
MyPhi
has been written in a similar way to
MyJpsi
in
MyPsi.py
, in module
PhiSelections
.
import GaudiKernel.SystemOfUnits as Units
from Gaudi.Configuration import *
from Configurables import CombineParticles
# define what we get in case from <module_name> import *
__all__ = ( 'SelMyBs')
_MyBs = CombineParticles('MyBs')
_MyBs.DecayDescriptor = "B_s0 -> J/psi(1S) phi(1020)"
_MyBs.CombinationCut = "ADAMASS('B_s0') < 600.*MeV"
_MyBs.MotherCut = "(VFASPF(VCHI2/VDOF)<5.) & (BPVIPCHI2() < 10.)"
_MyBs.ReFitPVs = True
from JpsiSelections.MyJpsi import SelMyJpsi
from PhiSelections.MyPhi import SelMyPhi
from PhysSelPython.Wrappers import Selection
SelMyBs = Selection("SelMyBs", Algorithm = _MyBs, RequiredSelections = [SelMyJpsi, SelMyPhi] )
The
Selection
class will use the
Selection.algName()
method of each of the
RequiredSelections
to set the
InputLocations
of
MyBs
, and will store the
Selections
, such that they can be used to build a sequencer that can run all the algorithms necessary for the selection.
Combine two steps in one: SimpleSelection
As seen from previous examples, typically one has two steps for creation of
Seelction
- creation and configuration of algorithm
- creation of
Selection
object
These two steps can be merged into one with usage of
SimpleSelection
helper:
from PhysSelPython.Wrappers import SimpleSelection , AutomaticData
dimuons = AutomaticData(Location = 'Phys/StdLooseDiMuon/Particles')
from GaudiConfUtils.ConfigurableGenerators import FilterDesktop
selMyJpsi = SimpleSelection (
'MyJpsi' , ## the name
FilterDesktop, ## algorithms TYPE
[ dimuons ] , ## list of input/required selections
## all remaining arguments are algorithm properties:
Code = """
(MAXTREE('mu+'==ABSID, TRCHI2DOF) < 5.0)
& (MINTREE('mu+'==ABSID, PIDmu) > -5.0)
& (MAXTREE('mu+'==ABSID, PIDK) < 5.0)
& (ADMASS('J/psi(1S)') < 42.*MeV)
""")
It allows to achieve more compact (but still easy readable) code minimizing number of intermediate local objects. Note that we now import the
FilterDesktop
algorithm from
GaudiConfUtils.ConfigurableGenerators
instead of directly from
Configurables
.
Debugging of selections
There are several useful methods for
debugging your selection:
- one can visualize the selection graph using
SelPy.graph
function
- one can force the selection to print the selected content using
PrintSelection
- one can use the tricks described here
PrintSelection
PrintSelection
is a very simple way to print the content of your selection. It just inserts
PrintDecayTree
algorithm into the algorithm sequence
from PhysConf.Selections import PrintSelection
## some selection
selection = PrintSelection ( selection )
The function returns
decorated selection that could be used as input for other selections ans selection sequences.
Each time non-empty selection appears in the data-flow, the content is printed to terminal/log-file.
visualization
To understand better the structure and the data flow for complicated selections, one can build a selection graph using
SelPy.graph
function
from SelPy.graph import view
## some selection
selection = ...
view (selection )
When the script is executed, it produces nice graph. that illustrate the structure of the selection, and interdependencies of various algorithms
Building a sequencer
Finally, we can use the
SelectionSequencer
class to put together a valid GaudiSequencer that can then be used to feed to an application configurable. This is a trivial step, since all the necessary information about all selections required is accessible from the top selection.
# define what we get in case from <module_name> import *
__all__ = ( 'SeqMyBs',)
from BsSelections.MyBs import SelMyBs
from PhysSelPython.Wrappers import SelectionSequence
SeqMyBs = SelectionSequence("SeqMyBs", TopSelection = SelMyBs)
Prescales
Any additional algorithms can be pre-pendeded to the sequence using
EventPreSelector
(array). Typically to add a prescale:
from Configurables import DeterministicPrescaler
prescale = DeterministicPrescaler("MyPrescale", AcceptFraction = 0.1)
SeqMyBs = SelectionSequence("SeqMyBs", TopSelection = SelMyBs, EventPreSelector = [ prescale ])
Specific selection
There are many other very useful objects in
Selection
framework, in particular
-
FilterSelection
: very simple function that wraps FilterDesktop
algorithm in SimlpeSelection
described above
-
CombineSelction
wraps CombineParticles
algorithm in SimpleSelection
-
TupleSelection
wraps DecayTreeTuple
algorithm in SimpleSelection
-
LimitSelection
is a useful concept when one needs to require certain number of objects in selection, e.g. not more than 400 pions
-
CheckPVSelection
is a useful way to insert check on PV-existence into the algorithm flow
-
TriggerSelection
allowes to make a filtering on trigger decision. There exist four varinats of generic TriggerSelection
-
L0Selection
allows to check L0-decision via inspection of L0DUReport
object
-
Hlt1Selection
allows to check Hlt1-decision
-
Hlt1Selection
checks Hlt1-decision
-
StrippingSelection
checks Strippign-decision
-
MomentumScaling
: simple pseudo-selection that inserts global TrackScaleSate
algorithm into the sequence
-
MomentumSmear
: simlpe pseuso-selection, that inserts TrackSmearState
algorithm into the sequence
-
PassThroughSelection
generi selection used for implementation of some of selections. described above
-
PayloadSelection
generic selection that allows to insert arbotrary algorithm ("useful payload") into selection sequence. It could be an arbitrary type of algorith, e.g. even the primitive "Hello,world!"
All of them can be illustrated using this example:
from PhysConf.Selections import ( PrintSelection ,
FilterSelection , CombineSelection ,
Hlt1Selection , Hlt2Selection ,
L0Selection , StrippingSelection ,
MomentumScaling , TupleSelection )
## start selection using soem standard seelctions
from StandardParticles import StdLoosePions as pions
from StandardParticles import StdLooseKaons as kaons
from StandardParticles import StdAllLoosePions as soft_pions
## filter them
pions = FilterSelection (
'GOOD_pions' , ## the name
[ pions ] , ## input locations
Code = "PT>1*GeV" ## algoriothm properties
)
## print it
pions = PrintSelection ( pions )
## filter kaons
kaons = FilterSelection (
'GOOD_kaons' , ## the name
[ kaons ] , ## input locations
Code = "PT>1*GeV" ## algoriothm properties
)
## print it
kaons = PrintSelection ( kaons )
## apply some trigger selections:
l0 = L0Selection ( "L0_trigger" , "L0_CHANNEL_SUB('Muon')" )
hlt1 = Hlt1Selection( "L1_trigger" , "Hlt1.*(C|c)arm.*" )
hlt2 = Hlt2Selection( "L2_trigger" , "Hlt2.*D0.*" )
strip = StrippingSelection( "Stripping" , ".*D0.*" )
## make D0
d0 = CombineSelection (
"D0" , ## the name
[ hlt2 , strip , pions , kaons ] , ## input
DecayDescriptor = "[ D0 -> K- pi+]cc" ,
CombinationCut = " in_range ( 1.6 * GeV , AM , 2.0 * GeV )",
MotherCut = " CHI2VX < 10 "
)
## print it!
d0 = PrintSelection ( d0 )
## if we have D0, apply momentumn scaling
d0 = MomentumScaling ( d0 )
## make Dstar
dstar = CombineSelection (
"Dstar" , ## the name
[ l0 , hlt1 , d0 , soft_pions ] , ## input
DecayDescriptor = "[ D*(2010)+ -> D0 pi+]cc" ,
CombinationCut = " AM - AM1 < 160 * MeV " ,
MotherCut = " CHI2VX < 10 "
)
## print it!
dstar = PrintSelection ( dstar )
This selection correspond to a following sequence of algoriths:
L0_trigger
L1_trigger
L2_trigger
Stripping
SelFilterPhys_StdLoosePions_Particles
GOOD_pions
GOOD_pions_PRINT
SelFilterPhys_StdLooseKaons_Particles
GOOD_kaons
GOOD_kaons_PRINT
D0
D0_PRINT
SCALER
SelFilterPhys_StdAllLoosePions_Particles
Dstar
Bringing it all together: grabbing the sequencer and adding it to a DaVinci configurable.
It is very easy to get the sequence from the modules where the
SelectionSequence
has been written and add it to the DaVinci configurable's main sequence:
from BsSelections.Sequences import SeqMyBs as theSequence
MySelection = theSequence.sequence()
dv = DaVinci()
dv.DataType = '2010'
dv.EvtMax = 500
dv.UserAlgorithms = [MySelection]
dv.Input = [...]
There's an added advantage: it is also possible to add to the sequence another analysis DVAlgorithm, taking as input particles from the main selection in the sequence,
without knowing the name of the main selection algorithm:
from BsSelections.Sequences import SeqMyBs
from Configurables import SomeDVAlgoType
myUserAlgo = SomeDVAlgoType('MyUserAlgo')
# explicit path is either '/Event/Phys/'+ SeqMyBs.algName()
# or 'Event/Hlt/'+ SeqMyBs.algName()
myUserAlgo.InputLocations = [ SeqMyBs.outputLocation() ]
SeqMyBs.sequence().Members += [ myUserAlgo ]
# add SeqMyBs' GaudiSequencer to DaVinci configurable
DaVinci().UserAlgorithms = [ SeqMyBs.sequence() ]
Making a MicroDST
It is equally trivial to use the
SelectionSequence
to make a MicroDST without explicitly knowing the names of any algorithms:
from Gaudi.Configuration import *
from Configurables import DaVinci, MicroDSTWriter
from BsSelections.Sequences import SeqMyBs0 as selSeq0
from BsSelections.Sequences import SeqMyBs1 as selSeq1
conf = MicroDSTWriter()
conf.OutputPrefix = "MicroDST"
conf.OutputFilePrefix = "Test"
conf.SelectionSequences = [selSeq0, selSeq1]
conf.CopyL0DUReport = False
conf.CopyHltDecReports = False
conf.CopyMCTruth = True
dv = DaVinci()
dv.DataType = 'DC06'
dv.EvtMax = 500
dv.UserAlgorithms = [ conf.sequence() ]
This should result in the creation of two MicroDSTs,
TestSeqMyBs0.mdst
and
TestSeqMyBs1.mdst
. It is possible to use a
MultiSelectionSequence
to make a single MicroDST with an OR of the two selections above:
...
from BsSelections.Sequences import SeqMyBs1 as selSeq1
from PhysSelPython.Wrappers import MultiSelectionSequence
multiSeq = MultiSelectionSequence('Seq0orSeq1', Sequences = [selSeq0, selSeq1])
conf = MicroDSTWriter()
...
conf.SelectionSequences = [multiSeq]
...
Making a StrippingLine
Creating a StrippingLine is very straight-forward. We first create a Selection, as shown in the examples above:
_Jpsi = CombineParticles("", ...)
_muons = DataOnDemand( ... )
# create the selection
Jpsi = Selection( "Sel"+name,
Algorithm = _Jpsi,
RequiredSelections = [ _muons ] )
For versions of
DaVinci greater or equal to v25r1 it is possible to pass the Selection as a constructor argument:
line = StrippingLine('JpsiInclusive'
, prescale = 0.25
, algos = [ Jpsi ] )
For earlier versions, it was possible to pass a SelectionSequence object, but it was deemed unsafe, since the combination of
EventPreselectors and pre-defined StrippingLine event pre-selections (minimum number of orimary vertices, pre-scales) is not well defined.
Beware that passing more than one Selection to the StrippingLine's algos argument is not a well defined operation and should not be done.
--
JuanPalacios - 26-Feb-2010
--
VanyaBelyaev - 2016-02-10