TWiki
>
LHCb Web
>
LHCbComputing
>
LHCbSoftwareTutorials
>
BenderTutorial
>
BenderTutorialV23
(2016-02-06,
VanyaBelyaev
)
(raw view)
E
dit
A
ttach
P
DF
---+!! LHCb [[http://cern.ch/lhcb%2Dcomp/Analysis/Bender][Bender]] Tutorial v24r0: Getting started with [[http://cern.ch/LHCb-release-area/DOC/bender][Bender]] This hands-on tutorial is an introduction to [[http://cern.ch/LHCb-release-area/DOC/bender][Bender]] - [[http://python.org][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: %TOC% 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 [[http://cern.ch/lhcb%2Dcomp/Analysis/Bender/stul.html][these]] or even [[http://www.lib.ru/ILFPETROV/ilf_petrov_12_chairs_engl.txt][these]] pages in between the exercises. ---++ Prerequisites It is assumed that you are more or less familiar with the [[LHCbSoftwareTrainingBasics][basic tutorial]], also some level of familiarity with the [[DaVinciTutorial][DaVinci tutorial]] is assumed. Some familiarity with basic GaudiPython and python-based [[LHCb.FAQ.LHCbFAQ#GaudiHistosPython][histograms]] & [[LHCb.FAQ.LHCbFAQ#GaudiTuplesPython][N-tuples]] is welcomed. It is also recommended to follow [[LoKiTutorial][LoKi tutorial]] and the basic [[http://cern.ch/lhcb-reconstruction/Python/Dst_as_Ntuple_files/frame.htm][GaudiPython tutorial]]. You are also invited to the [[mailto:lhcb-bender@cern.c][lhcb-bender]] mailing list. ---++ Setup the environment for [[http://cern.ch/LHCb-release-area/DOC/bender][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 [[http://cern.ch/LHCb-release-area/DOC/bender][Bender]]-based analysis are not covered by this tutorial. %SYNTAX{ syntax="sh" numbered="1000" numstep="1"}% > SetupProject Bender v24r0 %ENDSYNTAX% ---++ Shortcuts for solutions & examples For shortcuts, one can have a look into _solutions_, that are accessible in =Tutorial/BenderTutor= package: %SYNTAX{ syntax="sh" numbered="1000" numstep="1"}% > ls -al $BENDERTUTORROOT/solutions %ENDSYNTAX% For examples, discussed in this tutorial, see the code in %SYNTAX{ syntax="sh" numbered="1000" numstep="1"}% > ls -al $BENDERTUTORROOT/python/BenderTutor %ENDSYNTAX% ---++ Structure of Bender-based python module The general structure of Bender-based python module consists of three parts: 0 User code, usually the set of import-statements and algorithms 0 Configuration part, where one configures the job. Note that Bender does not live in vacuum, and certain configuration of job is need 0 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. #DoNothing ---+++ Right structure of Bender module: simple _"do-nothing"_ module The simplest _"do-nothing"_ (even more simple than _"Hello, world!"_), but totally valid module is %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## 1) some user code : def run ( nEvents ) : for i in range( 0 , min( nEvents , 10 ) ) : print ' I run event %i ' % i return 0 ## 2) configuration step def configure ( datafiles , catalogs = [] , castor = False , params = {} ) : print 'I am configuration step!' return 0 ## 3) steer the job if '__main__' == __name__ : print 'This runs only if module is used as the script! ' run ( 10 ) %ENDSYNTAX% It is highly desirable and recommended to put some "decorations" a top of this minimalistic lines: 0 add magic <code>#!/usr/bin/env python</code> line as the top line of the module/script 0 make the script executable: <code>chmod +x ./DoNothing.py</code> 0 add a python documentation close to the begin of the script 0 fill some useful python attributes with the proper informaton * <code>__author__</code> * <code>__date__</code> * <code>__version__</code> 0 do not forget to add documenatio in Doxygen-style and use in comments following tags * <code>@file</code> * <code>@author</code> * ... (1) and (2) will allow you to use module as executable script without specifying <code>python</code> as executable program: %SYNTAX{ syntax="sh" numbered="1000" numstep="1"}% > ./DoNothing.py %ENDSYNTAX% 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 <code>$BENDERTUTORROOT/python/BenderTutor/DoNothing.py</code> *IMPORTANT* _Right structure_ of Bender module is important for GRID submission: ==Bender== application in ==Ganga== expects this structure, and ==Ganga== job fail otherwise #HelloWorld ---++ 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!'__ %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% # ============================================================================= ## import everything from Bender, including the default "run"-function from Bender.Main import * # ============================================================================= ## @class HelloWorld # simple Python/Bender class for classical 'Hello,World!' example class HelloWorld(Algo): ## IMPORTANT: we inherit from Algo-base class """ The most trivial algorithm to print 'Hello,world!' """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## use the native Python print to stdout: print 'Hello, world! (using native Python)' ## use Gaudi-style printout: self.Print( 'Hello, World! (using Gaudi)') return SUCCESS ## IMPORTANT!!! # ============================================================================= %ENDSYNTAX% ---+++ (2) The job configuration: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% # ============================================================================= ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : from Configurables import DaVinci ## delegate the actual configurtaion to DaVinci dv = DaVinci ( DataType = '2012' , InputType = 'MDST' ) ## define the input data setData ( inputdata , catalogs , castor ) ## inform bender about input files and access to data ## get/create application manager gaudi = appMgr() ## (1) create the algorithm. defined above alg = HelloWorld( 'Hello' ) ## (2) replace the list of top level algorithm by # new list, which contains only *THIS* algorithm gaudi.setAlgorithms( [ alg ] ) return SUCCESS # ============================================================================= %ENDSYNTAX% #SteeringJob ---+++ Finally: (3) job steering for interactive usage: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% # ============================================================================= ## Job steering if __name__ == '__main__' : ## job configuration, get input data from following BK-path: ## BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST' ) inputdata = [ '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000221_1.psix.mdst', '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000282_1.psix.mdst', '/lhcb/LHCb/Collision12/PSIX.MDST/00035290/0000/00035290_00000238_1.psix.mdst', ] configure( inputdata , castor = True ) ## for interactive usage, allow direct access to CASTOR/EOS ## event loop, use default run-function from Bender run(10) # ============================================================================= %ENDSYNTAX% Complete example (including the proper decoration and documentatiton lines) is available a <code>$BENDERTUTORROOT/python/BenderTutor/HelloWorld.py</code> #InspectParticles ---++ 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: <code>/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST</code>. For this example we can easily reuse the [[#SteeringJob][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 %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% # ============================================================================= ## import everything from bender from Bender.Main import * # ============================================================================= ## @class InspectParticles # Inspect particles from the certain TES location # @author Vanya BELYAEV Ivan.Belyaev@nikhef.nl class InspectParticles(Algo): """ The trivial algorithm to inspect input TES locations """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get *ALL* particles from the input locations particles = self.select ( 'all' , ALL ) if particles.empty() : return self.Warning('No input particles', SUCCESS ) ## simple dump of particles print 'Found %d particles ' % len ( particles ) print particles ## dump them! print ' Loop and print only the decay modes: ' for b in particles: print b.decay() print ' Loop over particles and print name, mass and pT' for b in particles: print b.name() , 'mass=%.3f GeV, pT=%.2f GeV ' % ( M( b ) / GeV , PT ( b ) / GeV ) return SUCCESS ## IMPORTANT!!! # ============================================================================= %ENDSYNTAX% Here <code>ALL</code> 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. * <code> ( PT > 1 * GeV )</code> * <code> in_range ( 2 , Y , 4.5 ) & ( PT > 1 * GeV ) </code> * ... etc.. Also, instead of LoKi-functors, the decay descriptors can be used: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% bmesons = self.select ( 'b' , 'Beauty -> J/psi(1S) K+ K-' ) ## very useful when input locations contain several decays for b in bmesons : print b.decay() jpsifromb = self.select ( 'psi' , 'Beauty -> ^( J/psi(1S) -> mu+ mu-) K+ K-' ) for p in jpsifromb : print p.decay() kaons = self.select('kaons' , 'Beauty -> J/psi(1S) ^K+ ^K-' ) %ENDSYNTAX% The result of one selection can be subsequencely refined uisng three-argument form of <code>self.select</code> statement: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% kaons = self.select('kaons' , 'Beauty -> J/psi(1S) ^K+ ^K-' ) positive = self.select('pos' , kaons , Q > 0 ) negative = self.select('neg' , kaons , Q < 0 ) %ENDSYNTAX% Here and above <code>M</code>, <code>PT</code>, <code>Y</code> and <code>Q</code> are LoKi-functions that act on the particles. See the incomplete list of them [[LoKiHybridFilters][here]] and [[LoKiParticleFunctions][here]]. ---+++ (2) The job configuration: Since in this examepl we do care abotu the input data, the configuration is a little bit more complicated: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci rootInTES = '/Event/PSIX' dv = DaVinci ( DataType = '2012' , InputType = 'MDST' , RootInTES = rootInTES ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'Inspector' dv.UserAlgorithms += [ alg_name ] ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = InspectParticles ( alg_name , RootInTES = rootInTES , ## we are reading uDST Inputs = [ 'Phys/SelPsi2KForPsiX/Particles' ] ) return SUCCESS # ============================================================================= %ENDSYNTAX% ---+++ (3) job steering for interactive usage For this example we can easily reuse the [[#SteeringJob][steering part]] from the previous example. Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/InspectParticles.py</code> #InteractiveTips1 ---+++ Few tips on interactive usage for purely interactive usage it is very convinient to start the python with <code>-i</code> key or start <code>ipython</code> instead of bare <code>python</code>: %SYNTAX{ syntax="sh" numbered="1000" numstep="1"}% python -i ./InspectParticles.py %ENDSYNTAX% In this scenario, after running over the specified number of events, <code>python</code> open the interactive prompt and the futher commands could be issues interactively, e.g. one can run over more events <code>run(100)</code>; run over all events in input files <code>run(-1)</code>, inspect the algorithms: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% >>> gaudi = appMgr() >>> alg = gaudi.algorithm('TheNameOfMyAlgorithm') %ENDSYNTAX% A lot of functionality available at interactive python prompt, see e.g. [[GaudiPythonTutorial][here]] and [[http://cern.ch/lhcb-reconstruction/Panoramix/Panoramix_intro.pdf][here]] #HistosAndTuples ---++ Add the histograms and n-tuples It is very easy to extend [[#InspectParticles][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 <code>analyse</code> method: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## get *ALL* particles from the input locations particles = self.select ( 'allinputs', ALL ) if particles.empty() : return self.Warning('No input particles', SUCCESS ) ## loop over particles and fill few histograms: for p in particles : # what histo-ID low-edge high-edge #bins self.plot( PT (p)/GeV , 'pt(B)' , 0 , 20 , 50 ) self.plot( M (p)/GeV , 'm(B)' , 5.2 , 5.4 , 40 ) self.plot( M1 (p)/GeV , 'm(psi)' , 3.0 , 3.2 , 20 ) self.plot( M23(p)/GeV , 'm(KK)' , 1.0 , 1.040 , 20 ) %ENDSYNTAX% The histograms are booked automatically. ---+++ Adding n-tuples Adding _n-tuples_ is also very simple, on ejust need to insert the following lines into <code>analyse</code> method: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## tup = self.nTuple('MyTuple') for p in particles : tup.column_float ( 'mB' , M ( p ) / GeV ) tup.column_float ( 'ptB' , PT ( p ) / GeV ) tup.column_float ( 'mPsi' , M1 ( p ) / GeV ) tup.column_float ( 'mKK' , M23 ( p ) / GeV ) tup.write() %ENDSYNTAX% To define the output filenames for the files with _histograms_ and _n-tuples_, the usual DaVinci configuration is used: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## from Configurables import DaVinci ## delegate the actual configuration to DaVinci rootInTES = '/Event/PSIX' dv = DaVinci ( DataType = '2012' , InputType = 'MDST' , RootInTES = rootInTES , HistogramFile = 'Histos.root' , ## IMPORTANT TupleFile = 'Tuples.root' ## IMPORTANT ) %ENDSYNTAX% ---+++ Tips and tricks for histograms and tuples At the interactive prompt one can make a certain inspection of _histograms_ and _n-tuples_: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% >>> gaudi = appMgr() >>> alg = gaudi.algorithm('HistosAndTuples') >>> histos = alg.Histos() >>> for k in histos : print alg.Histos(key).dump(20,20) ## dump histogram as text ... >>> alg.HistoPrint = True ## make a short summary on booked histograms HistosAndTuples SUCCESS List of booked 1D histograms in directory "HistosAndTuples" :- | ID | Title | # | Mean | RMS | Skewness | Kurtosis | | m(B) | "m(B)" | 475 | 5.3108 | 0.060107 | -0.27166 | -1.2296 | | m(KK) | "m(KK)" | 475 | 1.0213 | 0.0071546 | 0.51193 | 0.99013 | | m(psi) | "m(psi)" | 475 | 3.0991 | 0.030669 | 0.024126 | 1.7185 | | pt(B) | "pt(B)" | 475 | 4.0809 | 3.4154 | 1.8964 | 4.1495 | >>> alg.NTuplePrint = True ## get a short summary on n-tuples HistosAndTuples SUCCESS List of booked N-Tuples in directory "FILE1/HistosAndTuples" HistosAndTuples SUCCESS ID=MyTuple Title="MyTuple" #items=4 {mB,ptB,mPsi,mKK} %ENDSYNTAX% Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/HistosAndTuples.py</code> ---++ More advanced functionality for _n-tuples_ There is a helper module <code>BenderTools.Fill</code> 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 %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% tup = ... particle = ... tup.column ( 'p4' , particle.momentum() / GeV ) ## ann 4-components at once %ENDSYNTAX% #BenderToolsFill ---+++ <code>BenderTools.Fill</code> module The module <code>BenderTools.Fill</code> 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. * <code>treatKine</code> to fill the basic kinematic in one go * <code>treatKaons</code>, <code>treatMuons</code>, ... etc... including pion, protons, diphotons, gammas, .... * <code>treatTracks</code> to fill infomation about all daughter tracks To activate this functions, see <code>$BENDERTUTORROOT/python/BenderTutor/Tuples2.py</code> example. %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% # ============================================================================= ## import everything from bender from Bender.Main import * ## enhace functionality for n-tuples import BenderTools.Fill # ============================================================================= ## @class Tuples2 # Enhanced functionality for n-tuples # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class Tuples2(Algo): """ Inspect particles from the certain TES location and fill histos and tuple """ def initialize ( self ) : sc = Algo.initialize ( self ) ## initialize the base class if sc.isFailure() : return sc ## initialize functionality for enhanced n-tuples sc = self.fill_initialize () ## IMPORTANT return sc def finalize ( self ) : self.fill_finalize () ## IMPORTANT return Algo.finalize ( self ) ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get particles from the input locations particles = self.select ( 'bmesons', 'Beauty -> J/psi(1S) K+ K-' ) if particles.empty() : return self.Warning('No input particles', SUCCESS ) tup = self.nTuple('MyTuple') for p in particles : psi = p(1) ## the first daughter: J/psi ## fill few kinematic variables for particles, ## use suffix to mark the variables propertly self.treatKine ( tup , p , '_b' ) self.treatKine ( tup , psi , '_psi' ) self.treatKaons ( tup , p ) ## fill some basic information for all kaons self.treatMuons ( tup , p ) ## fill some basic information for all muons self.treatTracks ( tup , p ) ## fill some basic information for all charged tracks tup.write() ## return SUCCESS ## IMPORTANT!!! %ENDSYNTAX% ---++++ Variables added by <code>treatKine</code> method | Name | LoKi-functor and/or meaning | Comment | | <code>pid</code> | <code>ID </code> | | | <code>pt</code> | <code>PT/GeV</code> | | | <code>m</code> | <code>M/GeV</code> | | | <code>eta</code> | <code>ETA </code> | | | <code>phi</code> | <code>PHI </code> | | | <code>p4(E,X,Y,Z)</code> | 4-momentum in GeV || | <code>c2ip</code> | χ<sup>2</sup>(IP) using <code>BPVIPCHI2()</code> | currently for all particles, protection to be added soon | | <code>y</code> | <code>Y</code> | for _non-basic_ particles only | | <code>lv01</code> | cosθ<sup>*</sup> using <code>LV01</code> | for _non-basic_ particles only | | <code>ctau</code> | c×τ using <code>BPVLTIME()*c_light</code> | for particles with valid end-vertex only | | <code>ctau9</code> | c×τ using <code>BPVLTIME(9)*c_light</code> | for particles with valid end-vertex only | | <code>ctau25</code> | c×τ using <code>BPVLTIME(25)*c_light</code> | for particles with valid end-vertex only | | <code>dtf</code> | χ<sup>2</sup>(DTF)/ndf using <code>DTF_CHI2NDOF(True)</code> | for particles with valid end-vertex only, likely to be removed soon | | <code>dls</code> | <code>LoKi.Particles.DecayLengthSignificanceDV()</code> | for particles with valid end-vertex only | | <code>vchi2</code> | χ<sup>2</sup>(VX) using <code>VFASPF( VCHI2 ) </code> | for particles with valid end-vertex only | | <code>vchi2ndf</code> | χ<sup>2</sup>(VX)/ndf using <code>VFASPF ( VCHI2PDOF)</code> | for particles with valid end-vertex only | All actual names of variables are appended with <code>suffix</code> used in <code>treatKine</code> method. #FillRecSummary There is an easy possibility to put into _n-tuple_ the basic infomation from <code>LHCb::RecSummary</code> object: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## get Rec-summary from TES rc_summary = self.get('/Event/Rec/Summary').summaryData() tup = self.nTuple ( ... ) self.addRecSummary ( tup , rc_summary ) ## put rec-summary information into n-tuple %ENDSYNTAX% #FillOdin Also the information from <code>LHCb::ODIN</code> objects can be added to _n-tuple_ in one go: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## get ODIN from TES odin = self.get_ ( '/Event/DAQ/ODIN' , True ) tup = self.nTuple( ... ) tup.column_aux ( odin ) %ENDSYNTAX% #FillMasses For given mother particles it is very easy to add information about invariant masses of all daughetr combuinations, optionally applying constrains (for constraints <code>DecayTreeFitter</code> is used. %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% tup = self.nTuple( ... ) for B in particles : self.fillMasses ( tup , B , '' ) ## just fill masses of all combinations self.fillMasses ( tup , B , 'cPV' , True ) ## fill masses after DTF-refit with PV-constraint self.fillMasses ( tup , B , 'cPVM' , True , 'J/psi(1S)' ) ## fill masses after DTF-refit with PV-constraint and J/psi-mass consrtraint %ENDSYNTAX% #MakeB ---++ 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 <latex>B^+\rightarrow J/\psi K^+</latex> candidates in Bender using <latex>J/\psi</latex> candidates created earlier in Stripping. ---+++ (1) User code: algorithm, that makes B<sup>+</sup> from J/ψ and K<sup>+</sup> %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## @class MakeB # Make combinatorics and composed particle creation in Bender # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class MakeB(Algo): """ Make combinatorics and composed particle creation in Bender """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get J/psi mesons from the input psis = self.select ( 'psi' , 'J/psi(1S) -> mu+ mu-' ) if psis.empty() : return self.Warning('No J/psi candidates are found!', SUCCESS ) ## get energetic kaons from the input: note we select both K+ and K- kaons = self.select ( 'k' , ( 'K+' == ABSID ) & ( PT > 1 * GeV ) & ( PROBNNk > 0.2 ) ) ## prepare useful functors: mass, chi2 and c*tau from DecayTreeFitter mfit = DTF_FUN ( M , True , strings('J/psi(1S)') ) ## use PV and J/psi-mass constraint c2dtf = DTF_CHI2NDOF ( True , strings('J/psi(1S)') ) ## ditto ctau = DTF_CTAU ( 0 , True , strings('J/psi(1S)') ) ## ditto tup = self.nTuple('MyTuple') ## make a loop over J/psi K combinations : for b in self.loop( 'psi k' , 'B+' ) : ## THIS LINE MAKES COMBINATORICS! ## fast evaluation of mass m12 = b.mass(1,2) / GeV if not 4.9 < m12 < 5.6 : continue ## skip bad combinations ## check the common vertex vchi2 = VCHI2 ( b ) if not 0<= vchi2 < 20 : continue ## skip large chi2 and fit failures ## get the mass of B mass = M ( b ) / GeV if not 4.9 < mass < 5.6 : continue ## skip bad combinations psi = b(1) ## the first daughter kaon = b(2) ## the second daughter if not psi : continue if not kaon : continue if Q ( kaon ) < 0 : b.setPID ( 'B-' ) ## readjust PID else : b.setPID ( 'B+' ) ## readjust PID m_fit = mfit ( b ) / GeV if not 5.0 <= m_fit <= 5.5 : continue ## skip bad combinations c_tau = ctau ( b ) if not -1.0 <= c_tau <= 100 : continue ## skip bad combinations c2_dtf = c2dtf ( b ) if not -1.0 <= c2_dtf <= 10 : continue ## skip bad combinations tup.column ( 'mB' , mass ) ## simple mass tup.column ( 'mBdtf' , m_fit ) ## mass after DecayTreeFitter tup.column ( 'c2dtf' , c2_dtf ) ## chi2 from DecayTreeFitter tup.column ( 'ctau' , c_tau ) ## lifetime (c*tau) from DecayreeFitter tup.write() ## finally, save good candiated! b.save ( 'MyB' ) ## check all saved B-candidates and make filter-passed decision savedb = self.selected('MyB') if 0 < len ( savedb ) : self.setFilterPassed( True ) return SUCCESS ## IMPORTANT!!! %ENDSYNTAX% ---+++ (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: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## read only events with Detached J/psi line fired IMPORTANT!!! Jpsi_location = '/Event/Dimuon/Phys/FullDSTDiMuonJpsi2MuMuDetachedLine/Particles' from PhysConf.Filters import LoKi_Filters fltrs = LoKi_Filters ( STRIP_Code = "HLT_PASS_RE('Stripping.*DiMuonJpsi2MuMuDeta.*')" ) ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci dv = DaVinci ( EventPreFilters = fltrs.filters ('Filters') , ## IMPORTANT DataType = '2012' , InputType = 'DST' , TupleFile = 'MakeB.root' ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'MakeB' dv.UserAlgorithms += [ alg_name ] from StandardParticles import StdLooseKaons kaons = StdLooseKaons.outputLocation() ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = MakeB ( alg_name , Inputs = [ Jpsi_location , kaons ] , ParticleCombiners = { '' : 'LoKi::VertexFitter' } ) return SUCCESS %ENDSYNTAX% ---+++ (3) Job steering for interactive usage: The configuration is essentially the same, as for previosu examples: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## Job steering if __name__ == '__main__' : ## job configuration ## BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/90000000/DIMUON.DST' ) inputdata = [ '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00012742_1.dimuon.dst', '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00015767_1.dimuon.dst', '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0000/00020198_00007306_1.dimuon.dst', '/lhcb/LHCb/Collision12/DIMUON.DST/00020198/0001/00020198_00016402_1.dimuon.dst', ] configure( inputdata , castor = True ) ## event loop run(50000) %ENDSYNTAX% Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/MakeB.py</code> #BenderGanga ---++ Bender & Ganga To submit Bender jobs to GRID using Ganga, very simple script can be used %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% my_area = '/afs/cern.ch/user/i/ibelyaev/cmtuser' my_opts = my_area + "/Bender_v23r2/Phys/VBWork/work/work_Bc2rho/Bc.py" # ## prepare job : # j = Job ( # name = 'MyJob', # application = Bender ( version = 'v23r4' , module = my_opts ## your Bender-based module ) , # outputfiles = [ SandboxFile ( '*.root' ) , SandboxFile ( '*.xml' ) ] , # backend = Dirac () , # splitter = SplitByFiles ( filesPerJob = 5 , bulksubmit = True , ignoremissing = False ) ) ## get data from BK-dbase q = BKQuery ( '/LHCb/Collision12/Beam4000GeV-VeloClosed-MagDown/Real Data/Reco14/Stripping20/WGBandQSelection7/90000000/PSIX.MDST' ) dataset = q.getDataset() ## set input data to job j.inputdata = dataset ## submit the job j.submit() %ENDSYNTAX% Ganga expect that the module provides two functions: 0 <code>configure</code>, that gets as the first argument the list of input files and xml-catalogs are supplied as the second argument 0 <code>run</code>, that gets an integer argument: number of evens to run #AccessToMCtruth ---++ Access to MC-information The next simple example illustrate the treatment of MC-truth data in Bender. By MC-truth data we mean <code>LHCb::MCParticle</code> and <code>LHCb::MCVertex</code> structures. E.g. lets create a _n-tuple_ with kinematic of J/ψ from [[http://svnweb.cern.ch/world/wsvn/lhcb/DBASE/trunk/Gen/DecFiles/dkfiles/Bs_JpsiKKpipi%2Cmm%3DTightCut.dec][<latex>B_s\rightarrow J/\psi K^+K^- \pi^+ \pi^-</latex>]] decay. Theatment of pure MC-data is very similar to the treatment of reconstructed data. One just needs to use another class and the base class for user algorithm <code>AlgoMC</code> instead of <code>Algo</code> and import all functionality from <code>Bender.MainMC</code> instead of <code>Bender.Main</code> ---+++ (1) User code: algorithm, that fills _n-tuple_ with Jψ kinematics from true MC <latex>B_s\rightarrow J/\psi K^+K^- \pi^+ \pi^-</latex> decay %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## import everything from bender from Bender.MainMC import * ## IMPORTANT: note the module name! # ============================================================================= ## @class MCtruth # Fill n-tuple with some MC-truth information # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class MCtruth(AlgoMC): ## <--- Note the base class here """ Make combinatorics and composed particle creation in Bender """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get "all" decays, ignoring possible intermediate resonances and/or additional photons mybs = self.mcselect ( 'mybs' , '[ B_s0 ==> J/psi(1S) K+ K- pi+ pi-]CC' ) if mybs.empty() : ## it should ``never'' happen!!! self.Warning('No true Bs-decays are found!') allb = self.mcselect( 'allmcb' , BEAUTY ) print allb return SUCCESS ## 1) get decays to K*K* mybs1 = self.mcselect ( 'mybs1' , '[B_s0 => J/psi(1S) K*(892)0 K*(892)~0 ]CC' ) ## 2) get decays to K* K pi mybs2 = self.mcselect ( 'mybs2' , '[ [B_s0]cc => J/psi(1S) K*(892)0 K- pi+ ]CC' ) ## 3) get decays to J/psi phi rho mybs3 = self.mcselect ( 'mybs3' , '[B_s0 => J/psi(1S) ( phi(1020) => K+ K- ) rho(770)0 ]CC' ) ## 4) get decays to J/psi phi pi pi mybs4 = self.mcselect ( 'mybs4' , '[B_s0 => J/psi(1S) ( phi(1020) => K+ K- ) pi+ pi- ]CC' ) ## 5) get decays to J/psi K K pi pi mybs5 = self.mcselect ( 'mybs5' , '[B_s0 => J/psi(1S) K+ K- pi+ pi- ]CC' ) ## 6) get decays to psi2S phi mybs6 = self.mcselect ( 'mybs6' , '[B_s0 => ( psi(2S) ==> J/psi(1S) pi+ pi- ) ( phi(1020) => K+ K- ) ]CC' ) ## 7) get decays to psi2S K K mybs7 = self.mcselect ( 'mybs7' , '[B_s0 => ( psi(2S) ==> J/psi(1S) pi+ pi- ) K+ K- ]CC' ) ## 8) get decays to X(3872) phi mybs8 = self.mcselect ( 'mybs8' , '[B_s0 => ( X_1(3872) ==> J/psi(1S) pi+ pi- ) ( phi(1020) => K+ K- ) ]CC' ) ## 9) get decays to X(3872) K K mybs9 = self.mcselect ( 'mybs9' , '[B_s0 => ( X_1(3872) ==> J/psi(1S) pi+ pi- ) K+ K- ]CC' ) tup1 = self.nTuple ( 'MyTuple' ) tup1.column_int ( 'nBs' , len ( mybs ) ) tup1.column_int ( 'nBs1' , len ( mybs1 ) ) tup1.column_int ( 'nBs2' , len ( mybs2 ) ) tup1.column_int ( 'nBs3' , len ( mybs3 ) ) tup1.column_int ( 'nBs4' , len ( mybs4 ) ) tup1.column_int ( 'nBs5' , len ( mybs5 ) ) tup1.column_int ( 'nBs6' , len ( mybs6 ) ) tup1.column_int ( 'nBs7' , len ( mybs7 ) ) tup1.column_int ( 'nBs8' , len ( mybs8 ) ) tup1.column_int ( 'nBs9' , len ( mybs9 ) ) tup1.write() if len(mybs) != len(mybs1)+len(mybs2)+len(mybs3)+len(mybs4)+len(mybs5)+len(mybs6)+len(mybs7)+len(mybs8)+len(mybs9) : self.Warning('Something wrong happens with decay descriptors!') ## it shoduld never happen! print len(mybs), len(mybs1), len(mybs2), len(mybs3) , len(mybs4), len(mybs5) , len(mybs6) , len(mybs7), len(mybs8),len(mybs9) print mybs tup = self.nTuple ( 'MyTuple2' ) psi_selector = LoKi.MCChild.Selector( 'J/psi(1S)' == MCID ) ## helper utility to get daughter J/psi km_selector = LoKi.MCChild.Selector( 'K-' == MCID ) ## helper utiliy to get daughter K- kp_selector = LoKi.MCChild.Selector( 'K+' == MCID ) ## helper utilitt to get daughter K+ for b in mybs : psi = b.child ( psi_selector ) ## get daughter J/psi if not psi : continue km = b.child ( km_selector ) ## get daughter K- if not km : continue kp = b.child ( kp_selector ) ## get daughter K+ if not kp : continue tup.column ( 'psi' , psi.momentum() / GeV ) ## fill n-tuple tup.column ( 'kp' , kp .momentum() / GeV ) tup.column ( 'km' , km .momentum() / GeV ) tup.write () ## return SUCCESS ## IMPORTANT!!! %ENDSYNTAX% Note the usage of <code>self.mcselect</code> instead of <code>self.select</code>. The prefix <code>mc</code> denotes the treatment of pure MC-data ---+++ (2) Configuration step to use MC-files as the input The actual configuration for pure MC data is even more simple. %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci dv = DaVinci ( Simulation = True , ## IMPORTANT DDDBtag = 'dddb-20130929-1' , ## IMPORTANT CondDBtag = 'sim-20130522-1-vc-mu100' , ## IMPORTANT DataType = '2012' , InputType = 'DST' , TupleFile = 'MCtruth.root' ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'MCtruth' dv.UserAlgorithms += [ alg_name ] ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = MCtruth( alg_name , PPMCs = [] ) return SUCCESS %ENDSYNTAX% ---+++ (3) job steering for interactive usage %SYNTAX{ syntax="python" numbered="1000" numstep="1}% if __name__ == '__main__' : ## job configuration ## data from: BKQuery('/MC/2012/Beam4000GeV-2012-MagUp-Nu2.5-Pythia8/Sim08d/Digi13/Trig0x409f0045/Reco14a/Stripping20NoPrescalingFlagged/13246002/ALLSTREAMS.DST') inputdata = [ '/lhcb/MC/2012/ALLSTREAMS.DST/00033494/0000/00033494_00000013_1.allstreams.dst', '/lhcb/MC/2012/ALLSTREAMS.DST/00033494/0000/00033494_00000040_1.allstreams.dst', '/lhcb/MC/2012/ALLSTREAMS.DST/00033494/0000/00033494_00000022_1.allstreams.dst', '/lhcb/MC/2012/ALLSTREAMS.DST/00033494/0000/00033494_00000004_1.allstreams.dst', ] configure( inputdata , castor = True ) ## event loop run(1000 ) %ENDSYNTAX% Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/MCtruth.py</code> #AccessToMCmatch ---++ Access to MC-truth matching information the next example illustrates the usage of _MC-truth matching_ functionality. In Bender it is done using the deficated functor <code>MCTRUTH</code>. The functor is fed with certain MC-particles <code>LHCb::MCParticle</code> (or several MC-particles) and being applied to _reconstrucructed_ particles <code>LHCb::Particle</code> is returns <code>True</code> or <code>False</code> depending if this _reconstructed_ particle gets direct or indirect contribution from given MC particles. Lets try to use _MC-truth matching_ functionality to study various MC-truth flags. The example is a "mixture" of [[#MakeB][two]] [[#AcessToMCtruth][previous]] examples: we'll reconstruct <latex>B_s \rightarrow J/\psi K^+ K^- \pi^+ \pi^-</latex> candidates and check if they correspond to MC-truth decays. ---+++ (1) User code: algorithm, that reconstructs <latex>B_s\rightarrow J/\psi K^+K^- \pi^+ \pi^-</latex> decay and checks MC-truth matching %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## import everything from bender from Bender.MainMC import * ## @class MCmatch # Very simple manipulations with MC-truth matched events # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class MCmatch(AlgoMC): ## <--- Note the base class here """ Make combinatorics and composed particle creation in Bender """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get "all" decays mybs = self.mcselect ( 'mybs' , '[ B_s0 ==> (J/psi(1S)=> mu+ mu-) K+ K- pi+ pi-]CC' ) if mybs.empty() : self.Warning('No true Bs-decays are found!') allb = self.mcselect( 'allmcb' , BEAUTY ) print allb return SUCCESS ## get psis from MC-decays mypsi = self.mcselect ( 'mypsi' , '[ B_s0 ==> ^(J/psi(1S)=>mu+ mu-) K+ K- pi+ pi-]CC' ) ## get pions from MC-decays : note carets mypi = self.mcselect ( 'mypi' , '[ B_s0 ==> (J/psi(1S)=>mu+ mu-) K+ K- ^pi+ ^pi-]CC' ) ## get psis from MC-decays : note carets myk = self.mcselect ( 'myk' , '[ B_s0 ==> (J/psi(1S)=>mu+ mu-) ^K+ ^K- pi+ pi-]CC' ) ## create mc-truth functors trueB = MCTRUTH ( mybs , self.mcTruth () ) ## IMPORTANT !!! truePsi = MCTRUTH ( mypsi , self.mcTruth () ) ## IMPORTANT !!! trueK = MCTRUTH ( myk , self.mcTruth () ) ## IMPORTANT !!! truePi = MCTRUTH ( mypi , self.mcTruth () ) ## IMPORTANT !!! ## jump to reco-world reco_psi = self.select( 'psi', 'J/psi(1S)' == ID ) if reco_psi.empty() : return SUCCESS ## selct only MC-truth matched pions: reco_pi = self.select( 'pi' , ( 'pi+' == ABSID ) & truePi ) ## NB: use only truth matched pions! if reco_pi .empty() : return SUCCESS reco_k = self.select( 'k' , 'K+' == ABSID ) if reco_pi .empty() : return SUCCESS ## prepare useful functors: mfit = DTF_FUN ( M , True , strings('J/psi(1S)') ) ## use PV and J/psi-mass constraint c2dtf = DTF_CHI2NDOF ( True , strings('J/psi(1S)') ) ## ditto ctau = DTF_CTAU ( 0 , True , strings('J/psi(1S)') ) ## ditto tup = self.nTuple('MyTuple') ## 1 2 3 4 5 Bs = self.loop ( 'psi k k pi pi ' , 'B_s0' ) for b in Bs : jpsi = b(1) k1 = b(2) k2 = b(3) pi1 = b(4) pi2 = b(5) if 0 < Q( k1)*Q( k2) : continue ## skip wrong charge combinations if 0 < Q(pi1)*Q(pi2) : continue ## skip wrong charge combinations m = b.mass() / GeV if not 4.9 <= m <= 5.6 : continue vchi2 = VCHI2 ( b ) if not -1.0 <= vchi2 <= 100 : continue m_fit = mfit ( b ) / GeV if not 5.0 <= m_fit <= 5.5 : continue c_tau = ctau ( b ) if not -1.0 <= c_tau <= 100 : continue c2_dtf = c2dtf ( b ) if not -1.0 <= c2_dtf <= 10 : continue tup.column ( 'mBdtf' , m_fit ) tup.column ( 'c2dtf' , c2_dtf ) tup.column ( 'ctau' , c_tau ) tup.column_float ( 'm' , M ( b ) / GeV ) tup.column_float ( 'mpsi' , M(jpsi) / GeV ) tup.column ( 'psi' , jpsi.momentum() / GeV ) tup.column_bool ( 'trueB' , trueB ( b ) ) tup.column_bool ( 'truePsi' , truePsi ( jpsi ) ) tup.column_bool ( 'trueK1' , trueK ( k1 ) ) tup.column_bool ( 'trueK2' , trueK ( k2 ) ) tup.column_bool ( 'truePi1' , truePi ( pi1 ) ) tup.column_bool ( 'truePi2' , truePi ( pi2 ) ) tup.write () ## return SUCCESS ## IMPORTANT!!! %ENDSYNTAX% ---+++ (2) Configuration step to use MC-files as the input %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## read only events with Detached J/psi line fired Jpsi_location = '/Event/AllStreams/Phys/FullDSTDiMuonJpsi2MuMuDetachedLine/Particles' from PhysConf.Filters import LoKi_Filters fltrs = LoKi_Filters ( STRIP_Code = "HLT_PASS_RE('Stripping.*DiMuonJpsi2MuMuDeta.*')" ) ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci dv = DaVinci ( EventPreFilters = fltrs.filters ('Filters') , ## IMPORTANT Simulation = True , ## IMPORTANT DDDBtag = 'dddb-20130929-1' , ## IMPORTANT CondDBtag = 'sim-20130522-1-vc-mu100' , ## IMPORTANT DataType = '2012' , InputType = 'DST' , PrintFreq = 10 , TupleFile = 'MCmatch.root' ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'MCmatch' dv.UserAlgorithms += [ alg_name ] from StandardParticles import StdLooseKaons kaons = StdLooseKaons.outputLocation() from StandardParticles import StdLoosePions pions = StdLoosePions.outputLocation() ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = MCmatch ( alg_name , Inputs = [ Jpsi_location , kaons , pions ] , ParticleCombiners = { '' : 'LoKi::VertexFitter' } , PPMCs = [ "Relations/Rec/ProtoP/Charged"] ## to speedup a bit ) return SUCCESS %ENDSYNTAX% ---+++ (3) job steering for interactive usage The actual steering part is the same as for [[#AccessToMCtruth][previous]] example. Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/MCmatch.py</code> #BenderTisTos ---++ Access to Trigger/TISTOS information in Bender Bender provides the dedicated module <code>BenderTools.TisTos</code> that greatly simplifies the treatment of Trigger/TISTOS-information. It allows to put into _n-tuple_ the TISTOS information for the given signal candidates and the set of trigger lines to be checked, as well as to get the dump of trigger information to pick up the certain trigger lines fore more detailed investigation. ---+++ (1) User code: algorithm, that gets <latex>B_s \rightarrow J/\psi K^+ K^-</latex> candidates from input tapes, filter them and fill _n-tuple_ with Trigger/TISTOS information %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## enhance functionality for n-tuples import BenderTools.Fill import BenderTools.TisTos ##IMPORTANT! # ============================================================================= ## @class TisTosTuple # Add Trigger/TISTOS info into n-tuple # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class TisTosTuple(Algo): def initialize ( self ) : sc = Algo.initialize ( self ) ## initialize the base class if sc.isFailure() : return sc ## initialize functionality for enhanced n-tuples sc = self.fill_initialize () if sc.isFailure() : return sc ## container to collect trigger infomration triggers = {} triggers ['psi'] = {} ## the lines to be investigated in details lines = {} lines [ "psi" ] = {} ## six mandatory keys: lines [ "psi" ][ 'L0TOS' ] = 'L0(DiMuon|Muon)Decision' lines [ "psi" ][ 'L0TIS' ] = 'L0(Hadron|DiMuon|Muon|Electron|Photon)Decision' lines [ "psi" ][ 'Hlt1TOS' ] = 'Hlt1(DiMuon|TrackMuon).*Decision' lines [ "psi" ][ 'Hlt1TIS' ] = 'Hlt1(DiMuon|SingleMuon|Track).*Decision' lines [ "psi" ][ 'Hlt2TOS' ] = 'Hlt2DiMuon.*Decision' lines [ "psi" ][ 'Hlt2TIS' ] = 'Hlt2(Charm|Topo|DiMuon|Single).*Decision' sc = self.tisTos_initialize ( triggers , lines ) if sc.isFailure() : return sc return SUCCESS def finalize ( self ) : self.tisTos_finalize () ## do not forget to finalize it self. fill_finalize () ## do not forget to finalize it return Algo.finalize ( self ) ## do not forget to finalize it ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! ## get particles from the input locations particles = self.select ( 'bmesons', 'Beauty -> J/psi(1S) K+ K-' ) if particles.empty() : return self.Warning('No input particles', SUCCESS ) ## prepare useful functors: mfit = DTF_FUN ( M , True , strings('J/psi(1S)') ) ## use PV and J/psi-mass constraint c2dtf = DTF_CHI2NDOF ( True , strings('J/psi(1S)') ) ## ditto ctau = DTF_CTAU ( 0 , True , strings('J/psi(1S)') ) ## ditto tup = self.nTuple('MyTuple') for b in particles : psi =b(1) ## the first daughter: J/psi c2_dtf = c2dtf ( b ) if not -1.0 <= c2_dtf <= 5 : continue m_fit = mfit ( b ) / GeV if not 5.2 <= m_fit <= 5.4 : continue c_tau = ctau ( b ) if not 0.1 <= c_tau <= 10 : continue ## fill few kinematic variables for particles, self.treatKine ( tup , b , '_b' ) self.treatKine ( tup , psi , '_psi' ) ## collect trigger information for J/psi self.decisions ( psi , self.triggers['psi'] ) ## fill n-tuple with TISTOS infornation for J/psi self.tisTos ( psi , ## particle tup , ## n-tuple 'psi_' , ## prefix for variable name in n-tuple self.lines['psi'] ## trigger lines to be inspected ) tup.write() ## return SUCCESS ## IMPORTANT!!! %ENDSYNTAX% The method <code>tisTos</code> adds following (integer) varibales into _n-tuple_ * <code>psi_l0phys</code> * <code>psi_l0tis</code> * <code>psi_l0tos</code> * <code>psi_l1glob</code> * <code>psi_l1phys</code> * <code>psi_l1tis</code> * <code>psi_l1tos</code> * <code>psi_l2glob</code> * <code>psi_l2phys</code> * <code>psi_l2tis</code> * <code>psi_l2tos</code> (Note the leading prefix <code>psi_</code>).These variables encode the information from Trigger/TISTOS decicion for the given J/ψ-candidate and the specified set of lines. E.g. expression <code>((psi_l0t0s&2)==2)</code> evaluates to <code>True</code> for L0-TOS (J/ψ)-case, and <code>((psi_l2tis&4)==4)</code> evaluates to <code>True</code> for Hlt2-TIS(J/ψ)-case. The description of magic numbers (2,4,...) can be found in [[http://svnweb.cern.ch/world/wsvn/lhcb/Phys/trunk/Phys/DaVinciInterfaces/Kernel/ITisTos.h][ITisTos.h]]-file: | 0 | Unknown | | 1 | TPS | | 2 | TOS: trigger on signal | | 3 | TUS | | 4 | TIS | | 6 | TOS&TIS | | 8 | Trigger decision | In case one has an allergy on bit-wise operations, everything can be simplified and a flag <code>verbose=True</code> could be added into <code>tisTos</code> method: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## fill n-tuple with TISTOS infornation for J/psi self.tisTos ( psi , ## particle tup , ## n-tuple 'psi_' , ## prefix for variable name in n-tuple self.lines['psi'] , ## trigger lines to be inspected verbose = True ## if someone hates bit-wise operations ) %ENDSYNTAX% In this case, in addition to variables, listed above new boolean variables <code>*_tis</code>, <code>*_tos</code>, <code>*_tus</code>, <code>*_tps</code> and <code>*_dec</code> will be added to _n-tuple_, allowing to avoid bit-wise operations at later steps. ---+++ (2) Configuration step to read μDST %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci rootInTES = '/Event/PSIX' dv = DaVinci ( DataType = '2012' , InputType = 'MDST' , RootInTES = rootInTES , TupleFile = 'TisTosTuples.root' ## IMPORTANT ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'TisTos' dv.UserAlgorithms += [ alg_name ] ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = TisTosTuple ( alg_name , RootInTES = rootInTES , ## we are reading uDST Inputs = [ 'Phys/SelPsi2KForPsiX/Particles' ] ) return SUCCESS %ENDSYNTAX% ---+++ (3) job steering for interactive usage The actual steering part is identical to [[#SteeringJob][this one]] Complete example (including the proper decoration and documentatiton lines) is available as <code>$BENDERTUTORROOT/python/BenderTutor/TisTosTuple.py</code> ---++ Accessing to <code>HepMC</code> information Bender is also able to deal with Generator/<code>HepMC</code> information. E.g. next simple example gets as input <code>.gen</code>-file with <latex>B_c^+</latex> decays and makes a histogram with transverse momentum of signal meson. Also it finds other long-lived beauty particle in event and prints its decay. ---+++ (1) User code: algorithm, that deals with <code>HepMC</code> data %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## import everything from bender from Bender.MainMC import * # ============================================================================= ## @class Generator # Very simple manipulations with Generator/HepMC information # @author Vanya BELYAEV Ivan.Belyaev@itep.ru class Generator(AlgoMC): ## <--- Note the base class here """ Very simple manipulations with Generator/HepMC information """ ## the main 'analysis' method def analyse( self ) : ## IMPORTANT! """ The main 'analysis' method """ ## get "all" decays mybc = self.gselect ( 'mybc' , 'B_c+' == GABSID ) if mybc.empty () : return self.Warning('No true Bc are found!' ,SUCCESS ) for b in mybc : self.plot( GPT( b ) / GeV , 'pt(Bc)' , 0 , 50 , 50 ) ## get all other beauty long-lived particles from PartProp.Nodes import LongLived LongLived.validate ( self.ppSvc() ) otherb = self.gselect ( 'otherb' , GBEAUTY & ( 'B_c+' != GABSID ) & GDECNODE ( LongLived ) ) for b in otherb : print 'Other B:' , b.decay() return SUCCESS %ENDSYNTAX% ---+++ (2) Configuration step to read <code>.gen</code>-file The configuration step is very easy: %SYNTAX{ syntax="python" numbered="1000" numstep="1"}% ## The configuration of the job def configure ( inputdata , ## the list of input files catalogs = [] , ## xml-catalogs (filled by GRID) castor = False , ## use the direct access to castor/EOS ? params = {} ) : ## import DaVinci from Configurables import DaVinci ## delegate the actual configuration to DaVinci dv = DaVinci ( Simulation = True , ## IMPORTANT DataType = '2012' , InputType = 'DST' ) ## add the name of Bender algorithm into User sequence sequence alg_name = 'GenTruth' dv.UserAlgorithms += [ alg_name ] # ## specific acion for HepMC-files # from BenderTools.GenFiles import genAction genAction() ## define the input data setData ( inputdata , catalogs , castor ) ## get/create application manager gaudi = appMgr() ## (1) create the algorithm with given name alg = Generator( alg_name , PPMCs = [] ) return SUCCESS %ENDSYNTAX%
E
dit
|
A
ttach
|
Watch
|
P
rint version
|
H
istory
: r8
<
r7
<
r6
<
r5
<
r4
|
B
acklinks
|
V
iew topic
|
WYSIWYG
|
M
ore topic actions
Topic revision: r8 - 2016-02-06
-
VanyaBelyaev
Log In
LHCb
LHCb Web
LHCb Web Home
Changes
Index
Search
LHCb webs
LHCbComputing
LHCb FAQs
LHCbOnline
LHCbPhysics
LHCbVELO
LHCbST
LHCbOT
LHCbPlume
LHCbRICH
LHCbMuon
LHCbTrigger
LHCbDetectorAlignment
LHCbTechnicalCoordination
LHCbUpgrade
Public webs
Public webs
ABATBEA
ACPP
ADCgroup
AEGIS
AfricaMap
AgileInfrastructure
ALICE
AliceEbyE
AliceSPD
AliceSSD
AliceTOF
AliFemto
ALPHA
Altair
ArdaGrid
ASACUSA
AthenaFCalTBAna
Atlas
AtlasLBNL
AXIALPET
CAE
CALICE
CDS
CENF
CERNSearch
CLIC
Cloud
CloudServices
CMS
Controls
CTA
CvmFS
DB
DefaultWeb
DESgroup
DPHEP
DM-LHC
DSSGroup
EGEE
EgeePtf
ELFms
EMI
ETICS
FIOgroup
FlukaTeam
Frontier
Gaudi
GeneratorServices
GuidesInfo
HardwareLabs
HCC
HEPIX
ILCBDSColl
ILCTPC
IMWG
Inspire
IPv6
IT
ItCommTeam
ITCoord
ITdeptTechForum
ITDRP
ITGT
ITSDC
LAr
LCG
LCGAAWorkbook
Leade
LHCAccess
LHCAtHome
LHCb
LHCgas
LHCONE
LHCOPN
LinuxSupport
Main
Medipix
Messaging
MPGD
NA49
NA61
NA62
NTOF
Openlab
PDBService
Persistency
PESgroup
Plugins
PSAccess
PSBUpgrade
R2Eproject
RCTF
RD42
RFCond12
RFLowLevel
ROXIE
Sandbox
SocialActivities
SPI
SRMDev
SSM
Student
SuperComputing
Support
SwfCatalogue
TMVA
TOTEM
TWiki
UNOSAT
Virtualization
VOBox
WITCH
XTCA
Welcome Guest
Login
or
Register
Cern Search
TWiki Search
Google Search
LHCb
All webs
Copyright &© 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