LHCb Bender Tutorial v23++: Getting started with Bender

This hands-on tutorial is an introduction to Bender - Python-based user friendly environment for physics analysis.

The purpose of these exercises is to allow you to write a simple algorithm for decay selection and analysis.

The exercises cover the following topics:

If the content of this pseudo-"hands-on" tutorial is a bit boring for you or you know well all the described topics please refer to these or even these pages in between the exercises.

Prerequisites

It is assumed that you are more or less familiar with the basic tutorial, also some level of familiarity with the DaVinci tutorial is assumed. Some familiarity with basic GaudiPython and python-based histograms & N-tuples is welcomed. It is also recommended to follow LoKi tutorial and the basic GaudiPython tutorial. You are also invited to the lhcb-bender mailing list.

Setup the environment for Bender

For this particular tutorial we'll concentrate on the interactive jobs and let the batch system and GRID, Ganga and DIRAC tool some rest. Batch and GRID-aware actions for Bender-based analysis are not covered by this tutorial.

 1000> SetupProject Bender v23r4

Shortcuts for solutions & examples

For shortcuts, one can have a look into solutions, that are accessible in Tutorial/BenderTutor package:

 1000> ls -al $BENDERTUTORROOT/solutions

For examples, discussed in this tutorial, see the code in

 1000> ls -al $BENDERTUTORROOT/python/BenderTutor

Structure of Bender-based python module

The general structure of Bender-based python module consists of three parts:
  1. User code, usually the set of import-statements and algorithms
  2. Configuration part, where one configures the job. Note that Bender does not live in vacuum, and certain configuration of job is need
  3. A few lines that actually start the job

None of these part is actually mandatory, but in case one follows this structure, one gets universal python module that is equally well applicable for pure interactive testing using interactive prompt, for small interactive-like jobs, for batch-like jobs and for Grid.

Simple "do-nothing" module

The simplest "do-nothing" (even more simple than "Hello, world!"), but totally valid module is

 1000## 1) some user code :
 1001def run ( nEvents ) :
 1002    
 1003    for i in range( 0 , min( nEvents , 10 ) ) :  print ' I run event %i ' % i
 1004        
 1005    return 0
 1006
 1007## 2) configuration step 
 1008def configure (  datafiles ,  catalogs = [] , castor = False , params = {} ) :   
 1009    
 1010    print 'I am configuration step!'
 1011    return 0
 1012
 1013## 3) steer the job
 1014if '__main__' == __name__ : 
 1015    
 1016    print 'This runs only if module  is used as the script! '
 1017    
 1018    run ( 10 ) 

It is highly desirable and recommended to put some "decorations" a top of this minimalistic lines:

  1. add magic #!/usr/bin/env python line as the top line of the module/script
  2. make the script executable: chmod +x ./DoNothing.py
  3. add a python documentation close to the begin of the script
  4. fill some useful python attributes with the proper informaton
    • __author__
    • __date__
    • __version__
  5. do not forget to add documenatio in Doxygen-style and use in comments following tags
    • @file
    • @author
    • ...

(1) and (2) will allow you to use module as executabel script withthous specifying python as executable program:

 1000> ./DoNothing.py 

For simple scripts the items (3-4) are likely not nesessary, but ait will be very helpful for you (and your colleagues) to reuse your code lines later.

Properly decorated module is available a $BENDERTUTORROOT/python/BenderTutor/DoNothing.py

Another useless, less primitive, but still simple "Hello, world!" example

In this example we'll use the real algorithm, the real input data, the real DaVinci-like configuration of job (but ignoring the actual content of input data)

(1) User code: algorithm, that does nothing, just prints 'Hello,world!'

 1000# =============================================================================
 1001## import everything from Bender, including the default "run"-function  
 1002from Bender.Main import *
 1003# =============================================================================
 1004## @class HelloWorld
 1005#  simple Python/Bender class for classical 'Hello,World!' example
 1006class HelloWorld(Algo):   ## IMPORTANT: we inherit from Algo-base class 
 1007    """
 1008    The most trivial algorithm to print 'Hello,world!'
 1009    """
 1010    ## the main 'analysis' method 
 1011    def analyse( self ) :   ## IMPORTANT! 
 1012        """
 1013        The main 'analysis' method
 1014        """
 1015        ## use the native Python print to stdout:
 1016        print       'Hello, world! (using native Python)'
 1017        
 1018        ## use Gaudi-style printout:
 1019        self.Print( 'Hello, World! (using Gaudi)')
 1020        
 1021        return SUCCESS      ## IMPORTANT!!! 
 1022# =============================================================================
 1023

(2) The job configuration:

 1000# =============================================================================
 1001## The configuration of the job
 1002def configure ( inputdata        ,    ## the list of input files  
 1003                catalogs = []    ,    ## xml-catalogs (filled by GRID)
 1004                castor   = False ,    ## use the direct access to castor/EOS ? 
 1005                params   = {}    ) :
 1006    
 1007    from Configurables import DaVinci
 1008    ## delegate the actual configurtaion to DaVinci 
 1009    dv = DaVinci ( DataType   = '2012' , InputType  = 'MDST' )
 1010    
 1011    ## define the input data
 1012    setData  ( inputdata , catalogs , castor )
 1013    
 1014    ## get/create application manager
 1015    gaudi = appMgr() 
 1016        
 1017    ## (1) create the algorithm. defined above 
 1018    alg = HelloWorld( 'Hello' )
 1019    
 1020    ## (2) replace the list of top level algorithm by
 1021    #     new list, which contains only *THIS* algorithm
 1022    gaudi.setAlgorithms( [ alg ] )
 1023             
 1024    return SUCCESS 
 1025# =============================================================================
 1026

Finally: (3) job steering for interactive usage:

 1000# =============================================================================
 1001## Job steering 
 1002if __name__ == '__main__' :
 1003
 1004    ## job configuration, get input data from following BK-path:
 1005    ## BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST'   )
 1006    inputdata = [
 1007        '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000221_1.psix.mdst',
 1008        '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000282_1.psix.mdst',
 1009        '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000238_1.psix.mdst',
 1010        ]
 1011    
 1012    configure( inputdata , castor = True )  ## for interactive usage, allow direct access to CASTOR/EOS 
 1013    
 1014    ## event loop, use default run-function from Bender 
 1015    run(10)
 1016        
 1017# =============================================================================
 1018

Complete example (including the proper decoration and documentatiton lines) is available a $BENDERTUTORROOT/python/BenderTutor/HelloWorld.py

The first useful example: inspect some particles at the given TES

In this example we'll try to use input data and simply inspect the content of certaint TES location at input μDST. As an input we'll use μDST from B&Q WG-selection #7, the path in bookkeeping: /LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST.

For this example we can easily reuse the steering part form the previous example, but we need to modify both user code and configuration parts.

(1) User code: algorithm, that inspects the content of input TES location

 1000# =============================================================================
 1001## import everything from bender 
 1002from Bender.Main import *
 1003# =============================================================================
 1004## @class InspectParticles
 1005#  Inspect particles from the certain TES location 
 1006#  @author Vanya BELYAEV Ivan.Belyaev@nikhef.nl
 1007class InspectParticles(Algo):
 1008    """
 1009    The trivial algorithm to inspect input TES locations 
 1010    """
 1011    ## the main 'analysis' method 
 1012    def analyse( self ) :   ## IMPORTANT! 
 1013        """
 1014        The main 'analysis' method
 1015        """
 1016        ## get *ALL* particles from the input locations 
 1017        particles = self.select ( 'all' ,  ALL )
 1018        if particles.empty() : return self.Warning('No input particles', SUCCESS )
 1019
 1020        ## simple dump of particles
 1021        print 'Found  %d particles ' % len ( particles ) 
 1022        print  particles ## dump them! 
 1023
 1024        print ' Loop and print only the decay modes: '
 1025        for b in particles: print b.decay() 
 1026
 1027        print ' Loop over particles and print name, mass and pT'
 1028        for b in particles:
 1029            print b.name() , 'mass=%.3f GeV, pT=%.2f GeV ' % ( M( b ) / GeV , PT ( b ) / GeV ) 
 1030
 1031        return SUCCESS      ## IMPORTANT!!! 
 1032# =============================================================================
 1033
Here ALL at line 1017 is LoKi-functor that accepts all particles. One can use any LoKi functor, or expression that evaluated to boolean value, and only those particles from input locations that satisfy these criteria will be selected, e.g.
  • ( PT > 1 * GeV )
  • in_range ( 2 , Y , 4.5 ) & ( PT > 1 * GeV )
  • ... etc..
Also, instead of LoKi-functors, the decay descriptors can be used:
 1000bmesons = self.select ( 'b' , 'Beauty -> J/psi(1S) K+ K-' ) ## very useful when input locations contain several decays 
 1001for b in bmesons : print b.decay() 
 1002
 1003jpsifromb = self.select ( 'psi' , 'Beauty -> ^( J/psi(1S) -> mu+ mu-) K+ K-' )  
 1004for p in jpsifromb : print p.decay() 
 1005
 1006kaons = self.select('kaons' , 'Beauty -> J/psi(1S) ^K+ ^K-' ) 

The result of one selection can be subsequencely refined uisng three-argument form of self.select statement:

 1000kaons = self.select('kaons' , 'Beauty -> J/psi(1S) ^K+ ^K-' ) 
 1001positive = self.select('pos' , kaons , Q > 0 ) 
 1002negative = self.select('neg' , kaons , Q < 0 ) 
Here and above M, PT, Y and Q are LoKi-functions that act on the particles. See the incomplete list of them here and here.

(2) The job configuration:

Since in this examepl we do care abotu the input data, the configuration is a little bit more complicated:
 1000## The configuration of the job
 1001def configure ( inputdata        ,    ## the list of input files  
 1002                catalogs = []    ,    ## xml-catalogs (filled by GRID)
 1003                castor   = False ,    ## use the direct access to castor/EOS ? 
 1004                params   = {}    ) :
 1005  
 1006      
 1007    ## import DaVinci 
 1008    from Configurables import DaVinci
 1009    ## delegate the actual configuration to DaVinci
 1010    rootInTES = '/Event/PSIX'
 1011    dv = DaVinci ( DataType   = '2012'    ,
 1012                   InputType  = 'MDST'    ,
 1013                   RootInTES  = rootInTES )
 1014    
 1015    ## add the name of Bender algorithm into User sequence sequence 
 1016    alg_name = 'Inspector'
 1017    dv.UserAlgorithms += [ alg_name ]
 1018    
 1019    ## define the input data
 1020    setData  ( inputdata , catalogs , castor )
 1021    
 1022    ## get/create application manager
 1023    gaudi = appMgr() 
 1024    
 1025    ## (1) create the algorithm with given name 
 1026    alg = InspectParticles (
 1027        alg_name ,
 1028        RootInTES = rootInTES , ## we are reading uDST
 1029        Inputs    = [ 'Phys/SelPsi2KForPsiX/Particles' ]
 1030        )
 1031             
 1032    return SUCCESS 
 1033# =============================================================================
 1034

(3) job steering for interactive usage

For this example we can easily reuse the steering part from the previous example.

Complete example (including the proper decoration and documentatiton lines) is available as $BENDERTUTORROOT/python/BenderTutor/InspectParticles.py

Few tips on interactive usage

for purely interactive usage it is very convinient to start the python with -i key or start ipython instead of bare python:
 1000python -i ./InspectParticles.py
In this scenario, after running over the specified number of events, python open the interactive prompt and the futher commands could be issues interactively, e.g. one can run over more events run(100); run over all events in input files run(-1), inspect the algorithms:
 1000>>> gaudi = appMgr()
 1001>>> alg = gaudi.algorithm('TheNameOfMyAlgorithm')
A lot of functionality available at interactive python prompt, see e.g. here and here

Add the histograms and n-tuples

It is very easy to extend the previous example and add the histograms and n-tuples to the code of the user code

Adding the histograms

Adding this histograms is just a trivial, one just need to insert the following lines inside analyse method:
 1000## get *ALL* particles from the input locations 
 1001        particles = self.select ( 'allinputs', ALL  )
 1002        if particles.empty() : return self.Warning('No input particles', SUCCESS )
 1003
 1004        ## loop over particles and fill few histograms: 
 1005        for p in particles :
 1006            #          what        histo-ID    low-edge  high-edge  #bins 
 1007            self.plot( PT (p)/GeV , 'pt(B)'  , 0        , 20       , 50  ) 
 1008            self.plot( M  (p)/GeV , 'm(B)'   , 5.2      , 5.4      , 40  ) 
 1009            self.plot( M1 (p)/GeV , 'm(psi)' , 3.0      , 3.2      , 20  )
 1010            self.plot( M23(p)/GeV , 'm(KK)'  , 1.0      , 1.040    , 20  )
The histograms are booked automatically.

Adding n-tuples

Adding n-tuples is also very simple, on ejust need to insert the following lines into analyse method:
 1000## 
 1001        tup = self.nTuple('MyTuple')
 1002        for p in particles :
 1003
 1004            tup.column_float ( 'mB'   , M   ( p ) / GeV )
 1005            tup.column_float ( 'ptB'  , PT  ( p ) / GeV ) 
 1006            tup.column_float ( 'mPsi' , M1  ( p ) / GeV )
 1007            tup.column_float ( 'mKK'  , M23 ( p ) / GeV ) 
 1008
 1009            tup.write() 

To define the output filenames for the files with histograms and n-tuples, the usual DaVinci configuration is used:

 1000## 
 1001    from Configurables import DaVinci
 1002    ## delegate the actual configuration to DaVinci
 1003    rootInTES = '/Event/PSIX'
 1004    dv = DaVinci ( DataType      = '2012'        ,
 1005                   InputType     = 'MDST'        ,
 1006                   RootInTES     = rootInTES     ,
 1007                   HistogramFile = 'Histos.root' , ## IMPORTANT 
 1008                   TupleFile     = 'Tuples.root'   ## IMPORTANT 
 1009                   )

Tips and tricks for histograms and tuples

At the interactive prompt one can make a certain inspection of histograms and n-tuples:

 1000>>> gaudi = appMgr()
 1001>>> alg = gaudi.algorithm('HistosAndTuples')
 1002>>> histos = alg.Histos()
 1003>>> for k in histos :  print alg.Histos(key).dump(20,20) ## dump histogram as text
 1004    ...  
 1005>>> alg.HistoPrint =  True ## make a short summary on booked histograms 
 1006HistosAndTuples   SUCCESS List of booked 1D histograms in directory         "HistosAndTuples" :-
 1007 | ID                        |   Title                                       |    #    |     Mean   |    RMS     |  Skewness  |  Kurtosis  |
 1008 | m(B)                      | "m(B)"                                        |   475   |     5.3108 | 0.060107   |   -0.27166 |    -1.2296 |
 1009 | m(KK)                     | "m(KK)"                                       |   475   |     1.0213 | 0.0071546  |    0.51193 |    0.99013 |
 1010 | m(psi)                    | "m(psi)"                                      |   475   |     3.0991 | 0.030669   |   0.024126 |     1.7185 |
 1011 | pt(B)                     | "pt(B)"                                       |   475   |     4.0809 | 3.4154     |     1.8964 |     4.1495 |
 1012>>> alg.NTuplePrint = True ## get a short summary on n-tuples
 1013HistosAndTuples   SUCCESS List of booked N-Tuples in directory "FILE1/HistosAndTuples"
 1014HistosAndTuples   SUCCESS  ID=MyTuple       Title="MyTuple"                                 #items=4  {mB,ptB,mPsi,mKK}
 1015

Complete example (including the proper decoration and documentatiton lines) is available as $BENDERTUTORROOT/python/BenderTutor/HistosAndTuples.py

More advanced functionality for n-tuples

There is a helper module BenderTools.Fill that allows to simplify filling if n-tuples with more or less common information, e.g. for various PIDs and "common" kinematics.

For a given particle there is a simple way to put into n-tuple 4-momentum

 1000tup        =  ... 
 1001particle =  ...
 1002tup.column ( 'p4' , particle.momentum() / GeV ) ## ann 4-components at once 
 1003

BenderTools.Fill module

The module BenderTools.Fill provides functionality for "power fill" of the basic information in one go. Clearly it does not cover all the cases, but helps to fill many basic kinematical quantities. The module provides many dedicate functions, e.g.
  • treatKine to fill the basic kinematic in one go
  • treatKaons, treatMuons, ... etc... including pion, protons, diphotons, gammas, ....
  • treatTracks to fill infomation about all daughter tracks
To activate this functions, see $BENDERTUTORROOT/python/BenderTutor/Tuples2.py example.
 1000# ============================================================================= 
 1001## import everything from bender 
 1002from   Bender.Main import *
 1003## enhace functionality for n-tuples 
 1004import BenderTools.Fill 
 1005# =============================================================================
 1006## @class Tuples2
 1007#  Enhanced functionality for n-tuples 
 1008#  @author Vanya BELYAEV Ivan.Belyaev@itep.ru
 1009class Tuples2(Algo):
 1010    """
 1011    Inspect particles from the certain TES location and fill histos and tuple
 1012    """
 1013    def initialize ( self ) :
 1014
 1015        sc = Algo.initialize ( self ) ## initialize the base class
 1016        if sc.isFailure() : return sc
 1017        
 1018        ## initialize functionality for enhanced n-tuples 
 1019        sc = self.fill_initialize ()     ## IMPORTANT 
 1020        return sc 
 1021
 1022    def finalize ( self ) :    
 1023        self.fill_finalize  ()                  ## IMPORTANT 
 1024        return Algo.finalize ( self )
 1025    
 1026    ## the main 'analysis' method 
 1027    def analyse( self ) :   ## IMPORTANT! 
 1028        """
 1029        The main 'analysis' method
 1030        """
 1031        ## get particles from the input locations 
 1032        particles = self.select ( 'bmesons', 'Beauty -> J/psi(1S) K+ K-'  )
 1033        if particles.empty() : return self.Warning('No input particles', SUCCESS )
 1034        
 1035        tup = self.nTuple('MyTuple')
 1036        for p in particles :
 1037            
 1038            psi = p(1) ## the first daughter: J/psi 
 1039            
 1040            ## fill few kinematic variables for particles,
 1041            ## use suffix to mark the variables propertly            
 1042            self.treatKine   ( tup , p   , '_b'   )
 1043            self.treatKine   ( tup , psi , '_psi' )
 1044            
 1045            self.treatKaons  ( tup , p ) ## fill some basic information for all kaons
 1046            self.treatMuons  ( tup , p ) ## fill some basic information for all muons
 1047            self.treatTracks ( tup , p ) ## fill some basic information for all charged tracks
 1048
 1049            tup.write() 
 1050
 1051        ## 
 1052        return SUCCESS      ## IMPORTANT!!!
 1053

There is an easy possibility to put into n-tuple the basic infomation from LHCb::RecSummary object:

 1000## get Rec-summary from TES
 1001 rc_summary   = self.get('/Event/Rec/Summary').summaryData()
 1002 tup = self.nTuple ( ... ) 
 1003 self.addRecSummary ( tup , rc_summary )  ## put rec-summary information into n-tuple
 1004

Also the information from LHCb::ODIN objects can be added to n-tuple in one go:

 1000## get ODIN from TES
 1001odin  = self.get_ ( '/Event/DAQ/ODIN'   , True )
 1002tup   = self.nTuple( ... )
 1003tup.column_aux ( odin ) 

For given mother particles it is very easy to add information about invariant masses of all daughetr combuinations, optionally applying constrains (for constraints DecayTreeFitter is used.

 1000tup = self.nTuple( ... ) 
 1001for B in particles : 
 1002       self.fillMasses ( tup , B , '' ) ## just fill masses of all combinations  
 1003       self.fillMasses ( tup , B , 'cPV' , True ) ## fill masses after DTF-refit with PV-constraint
 1004       self.fillMasses ( tup , B , 'cPVM' , True , 'J/psi(1S)' ) ## fill masses after DTF-refit with PV-constraint and J/psi-mass consrtraint
 1005

Making the combinatorics

All previous examples were devoted to the looping over the particles, created at earlier steps, e.g. at Stripping. But Bender also allows to perform combinatoric and creation of composite particles. Let's consider the creation of candidates in Bender using candidates created earlier in Stripping.

(1) User code: algorithm, that makes B+ from J/ψ and K+

 1000## @class MakeB 
 1001#  Make combinatorics and composed particle creation in Bender 
 1002#  @author Vanya BELYAEV Ivan.Belyaev@itep.ru
 1003class MakeB(Algo):
 1004    """
 1005    Make combinatorics and composed particle creation in Bender 
 1006    """
 1007    ## the main 'analysis' method 
 1008    def analyse( self ) :   ## IMPORTANT! 
 1009        """
 1010        The main 'analysis' method
 1011        """
 1012        ## get J/psi mesons from the input
 1013        psis  = self.select ( 'psi' , 'J/psi(1S) -> mu+ mu-' )
 1014        if psis.empty() : return self.Warning('No J/psi candidates are found!', SUCCESS )
 1015        
 1016        ## get energetic kaons from the input: note we select both K+ and K-
 1017        kaons = self.select ( 'k'  , ( 'K+' == ABSID ) & ( PT > 1 * GeV ) &  ( PROBNNk > 0.2 ) )
 1018        
 1019        ## prepare useful functors: mass, chi2 and c*tau from DecayTreeFitter 
 1020        mfit  = DTF_FUN  ( M , True , strings('J/psi(1S)') ) ## use PV and J/psi-mass constraint
 1021        c2dtf = DTF_CHI2NDOF ( True , strings('J/psi(1S)') ) ## ditto
 1022        ctau  = DTF_CTAU ( 0 , True , strings('J/psi(1S)') ) ## ditto 
 1023                                
 1024        tup = self.nTuple('MyTuple')
 1025
 1026        ## make a loop over J/psi K combinations :
 1027        for b in self.loop( 'psi k' , 'B+' ) :
 1028            ## fast evaluation of mass
 1029            m12 = b.mass(1,2) / GeV 
 1030            if not 4.9 < m12  < 5.6 : continue ## skip bad combinations 
 1031
 1032            ## check the common vertex
 1033            vchi2 = VCHI2 ( b ) 
 1034            if not 0<= vchi2 < 20   : continue   ## skip large chi2 and fit failures  
 1035            
 1036            ## get the mass of B
 1037            mass = M ( b ) / GeV 
 1038            if not 4.9 < mass < 5.6 : continue ## skip bad combinations 
 1039
 1040            psi   = b(1) ## the first daughter 
 1041            kaon  = b(2) ## the second daughter
 1042            if not psi  : continue
 1043            if not kaon : continue
 1044            
 1045            if   Q ( kaon ) < 0 : b.setPID ( 'B-' ) ## readjust PID 
 1046            else                : b.setPID ( 'B+' ) ## readjust PID 
 1047            
 1048            m_fit  = mfit  ( b ) / GeV
 1049            if not  5.0 <= m_fit  <= 5.5 : continue   ## skip bad combinations 
 1050            c_tau  = ctau  ( b )
 1051            if not -1.0 <= c_tau  <= 100 : continue  ## skip bad combinations 
 1052            c2_dtf = c2dtf ( b )  
 1053            if not -1.0 <= c2_dtf <=  10 : continue  ## skip bad combinations 
 1054                        
 1055            tup.column  (  'mB'    , mass   ) ## simple mass 
 1056            tup.column  (  'mBdtf' , m_fit  )   ## mass after DecayTreeFitter 
 1057            tup.column  (  'c2dtf' , c2_dtf )  ## chi2 from DecayTreeFitter 
 1058            tup.column  (  'ctau'  , c_tau  ) ## lifetime (c*tau) from DecayreeFitter 
 1059            
 1060            tup.write()
 1061            
 1062            ## finally, save good candiated! 
 1063            b.save ( 'MyB' )                        
 1064
 1065        ## check  all saved B-candidates and make filter-passed decision  
 1066        savedb = self.selected('MyB')
 1067        if 0 < len ( savedb ) : self.setFilterPassed( True ) 
 1068             
 1069        return SUCCESS      ## IMPORTANT!!! 
 1070

(2) Configuration step to use DIMUON.DST as the input

At configuration step we need to feed the algorithm with previously selected J/ψ-candidates and kaons. Considering DIMIUON.DST form the Stripping20 as the input, the possible configuration looks as:
 1000## The configuration of the job
 1001def configure ( inputdata        ,    ## the list of input files  
 1002                catalogs = []    ,    ## xml-catalogs (filled by GRID)
 1003                castor   = False ,    ## use the direct access to castor/EOS ? 
 1004                params   = {}    ) :
 1005    
 1006    ## read only events with Detached J/psi line fired   IMPORTANT!!! 
 1007    Jpsi_location  = '/Event/Dimuon/Phys/FullDSTDiMuonJpsi2MuMuDetachedLine/Particles'
 1008    from PhysConf.Filters import LoKi_Filters
 1009    fltrs = LoKi_Filters ( STRIP_Code = "HLT_PASS_RE('Stripping.*DiMuonJpsi2MuMuDeta.*')"  )
 1010    
 1011    ## import DaVinci    
 1012    from Configurables import DaVinci
 1013    ## delegate the actual configuration to DaVinci
 1014    dv = DaVinci ( EventPreFilters =  fltrs.filters ('Filters') , ## IMPORTANT
 1015                   DataType        = '2012'       ,
 1016                   InputType       = 'DST'        ,
 1017                   TupleFile       = 'MakeB.root' )
 1018    
 1019    ## add the name of Bender algorithm into User sequence sequence 
 1020    alg_name = 'MakeB'
 1021    dv.UserAlgorithms += [ alg_name ]
 1022    
 1023    from StandardParticles import StdLooseKaons 
 1024    kaons = StdLooseKaons.outputLocation()
 1025    
 1026    ## define the input data
 1027    setData  ( inputdata , catalogs , castor )
 1028    
 1029    ## get/create application manager
 1030    gaudi = appMgr() 
 1031    
 1032    ## (1) create the algorithm with given name 
 1033    alg   = MakeB (
 1034        alg_name  ,
 1035        Inputs            = [ Jpsi_location , kaons ] , 
 1036        ParticleCombiners = { '' : 'LoKi::VertexFitter' } )
 1037             
 1038    return SUCCESS 

(3) Job steering for interactive usage:

The configuration is essentially the same, as for previosu examples:
 1000## Job steering 
 1001if __name__ == '__main__' :    
 1002    ## job configuration
 1003    ## BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/90000000/DIMUON.DST' )
 1004    inputdata = [
 1005        '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00012742_1.dimuon.dst',
 1006        '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00015767_1.dimuon.dst',
 1007        '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0000/00020198_00007306_1.dimuon.dst',
 1008        '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00016402_1.dimuon.dst',
 1009        ]
 1010    configure( inputdata , castor = True )
 1011    
 1012    ## event loop 
 1013    run(50000)

Complete example (including the proper decoration and documentatiton lines) is available as $BENDERTUTORROOT/python/BenderTutor/MakeB.py

Bender & Ganga

To submit Bender jobs to GRID using Ganga, very simple script can be used
 1000my_area  = '/afs/cern.ch/user/i/ibelyaev/cmtuser'
 1001my_opts  = my_area + "/Bender_v23r2/Phys/VBWork/work/work_Bc2rho/Bc.py"
 1002#
 1003## prepare job :
 1004# 
 1005j = Job (
 1006    #
 1007    name         = 'MyJob',
 1008    #
 1009    application  = Bender (
 1010    version      = 'v23r4'   ,
 1011    module       = my_opts 
 1012    ) ,
 1013    #
 1014    outputfiles  = [ SandboxFile ( '*.root' ) , SandboxFile ( '*.xml'  ) ] ,
 1015    #
 1016    backend      = Dirac () ,
 1017    #
 1018    splitter     = SplitByFiles ( filesPerJob   =     5 ,
 1019                                  bulksubmit    =  True , 
 1020                                  ignoremissing = False ) 
 1021    )
 1022
 1023## get data from BK-dbase 
 1024q = BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST'   )
 1025dataset = q.getDataset()
 1026
 1027## set input data to job 
 1028j.inputdata = dataset
 1029
 1030## submit the job
 1031j.submit()

Edit | Attach | Watch | Print version | History: r8 | r4 < r3 < r2 < r1 | Backlinks | Raw View | Raw edit | More topic actions...
Topic revision: r2 - 2014-05-16 - VanyaBelyaev
 
    • 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-2022 by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
or Ideas, requests, problems regarding TWiki? use Discourse or Send feedback