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:
  • EventSelection.
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

  1. creation and configuration of algorithm
  2. 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

Debugging of selections

There are several useful methods for debugging your selection:

  1. one can visualize the selection graph using SelPy.graph function
  2. one can force the selection to print the selected content using PrintSelection
  3. 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 ) 


## 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

Edit | Attach | Watch | Print version | History: r30 < r29 < r28 < r27 < r26 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r30 - 2018-03-03 - RosenMatev
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    LHCb All webs login

This site is powered by the TWiki collaboration platform Powered by PerlCopyright & 2008-2019 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback