LoKi "tree/node" utilities for decay pattern matching

Decay Tree Nodes

From pure C++ point of view "decay node" is a simple predicate which acts on LHCb::ParticleID object. It implements the abstract interface LoKi::Decays::iNode, see the file $LOKICOREROOT/LoKi/iNode.h.

 1000using namespace Decays 
 1010
 1020// get the node:
 1030
 1040const Decays::iNode& goodPID = .... ;
 1050
 1060
 1070// get PID:
 1080
 1090const LHCb::ParticleID& pid = ... ;
 1100
 1110
 1120// use the node to test the PID:
 1130
 1140
 1150const bool good = goodNode ( pid ) ;
 1160
 1170if ( good )  { /** good PID! */ }
 1180else          {  /** bad   PID! */ }

The basic decay tree nodes

The following predicates/symbols are defined in namespace Decays::Nodes, see the file $PARTPROPROOT/Kernel/Nodes.h:

Any
the most trivial predicate with always evaluated to true
Pid
Simple predicate which checks if the particle ID corresponds to the predefined particle ID, e.g. the functor Pid("D0") evaluates to true if the particle ID corresponds to the particle code of -meson
CC
simple predicate which checks thf the particle IF corresponds to the predefined code (or its antiparticle), .g. the functor CC("D0") evaluates to true if the particle ID corresponds to the particle code of or meson.
Lepton
it evaluates to true for any "lepton", defined according to LHCb::ParticleID::isLepton
Nu
it evaluates to true for "neutral lepton", which is neutrino, defined according to LHCb::ParticleID::isLepton and LHCb::ParticleID::threeCharge
Ell
it evaluates to true for "charged lepton", defined according to LHCb::ParticleID::isLepton and LHCb::ParticleID::threeCharge
EllPlus
it evaluates to true for "positively charged lepton", defined according to LHCb::ParticleID::isLepton and LHCb::ParticleID::threeCharge
EllMinus
it evaluates to true for "negatively charged lepton", defined according to LHCb::ParticleID::isLepton and LHCb::ParticleID::threeCharge
Hadron
it evaluates to true for "hadron", defined according to LHCb::ParticleID::isHadron
Meson
it evaluates to true for "meson", defined according to LHCb::ParticleID::isMeson
Baryon
it evaluates to true for "baryon", defined according to LHCb::ParticleID::isBaryon
Charged
it evaluates to true for "charged" particles, defined according to LHCb::ParticleID::threeCharge
Positive
it evaluates to true for "positively charged" particles, defined according to LHCb::ParticleID::threeCharge
Negative
it evaluates to true for "negatively charged" particles, defined according to LHCb::ParticleID::threeCharge
Neutral
it evaluates to true for "neutral" particles, defined according to LHCb::ParticleID::threeCharge
HasQuark
it evaluates to true for particles that contains certain quark, defined according to LHCb::ParticleID::hasQuark
JSpin
it evaluates to true for particles that have certain J-spin, defined according to LHCb::ParticleID::jSpin
LSpin
it evaluates to true for particles that have certain L-spin, defined according to LHCb::ParticleID::lSpin
SSpin
it evaluates to true for particles that have certain S-spin, defined according to LHCb::ParticleID::sSpin
CTau
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) within certain range, e.g. CTau(1*mm,1*km)
ShortLived_
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) less than hight edge , e.g. ShortLived_(1*micrometer)
LongLived_
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) larger than low edge , e.g. LongLived_(1*micrometer)
ShortLived
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) less than 0.1 micrometer
LongLived
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) larger than 0.1 micrometer
Stable
it evaluates to true for particles that have nominal proper lifetime (in c*tau units) larger than 1 meter
StableCharged
it is equivalent to Stable&Charged
Mass
it evaluated to true for particles that have nominal mass within certain range, e.g. Mass(1*GeV,5*GeV)
Light
it evaluate to true for particles that have nominal mass lighter than some high edge, e.g. Light(1*GeV)
Heavy
it evaluate to true for particles that have nominal mass heavier than some low edge, e.g. Heavy(1*GeV)

The operations with decay tree nodes

From the basic decay tree nodes one can create more complicated decay tree nodes using the boolean operators. e.g.

  • Charged() && Meson()
  • Positive() || Meson()
  • Meson()
  • ~Meson()

The result of operations is easy to store into the simple object of type Node, which is the simplest (assignable) implementation of iNode abstract interface:

 1000using namespace Decays ;
 1010
 1020const Node node = Nodes::Pid("D0") || Nodes::Lepton() || Nodes::Meson() ;
 1030

Each constructed node can be print in the readable way:

 1000using namespace Decays ;
 1010
 1020const Node node = Nodes::Pid("D0") || Nodes::Lepton() || ~Nodes::Meson() ;
 1030
 1040std::cout << node << std::endl ;
 1050 
It will print something like ( ( "D0" | Lepton ) | Meson) . Please note, that using the parser tools being developed by Sascha Mazurov, it is possible to "invert" the string represenation of the functor and to construct the predicate from its valid string representation.

Important

Majority of nodes MUST be validated before the actual usage:

 1000using namespace Decays ;
 1010
 1020const Node node = Nodes::Pid("D0") || Nodes::Lepton() || ~Nodes::Meson() ;
 1030
 1040IParticlePropertySvc* svc = ...  ;
 1050
 1060// validate the node
 1070StatusCode sc = node.validate( svc ) ;
 1080if ( sc.isFailure() ) {  ... INVALID NODE ... } ;
 1090 

The string representations:

Predicate Instance String representation
Nodes::Any()       X
Nodes::Pid("B+")  B+
Nodes::CC("B+")  [B+]cc
Nodes::Meson()   Meson
Nodes::Baryon()   Baryon
Nodes::Hadron()   Hadron
Nodes::Lepton()   Lepton
Nodes::Neutral()   X0
Nodes::Charged()   Xq
Nodes::Positive()   X+
Nodes::Negative()   X-
Nodes::Nu()   Nu-
Nodes::Ell()   l
Nodes::EllPlus()   l+
Nodes::EllMinus()   l-
Nodes::HasQuark(LHCb::ParticleID::down)   Xd
Nodes::HasQuark(LHCb::ParticleID::up)   Xu
Nodes::HasQuark(LHCb::ParticleID::strange)   Xs
Nodes::HasQuark(LHCb::ParticleID::charm)   Xc
Nodes::HasQuark(LHCb::ParticleID::bottom)   Xb
Nodes::JSpin(3)   JSpin(3)
Nodes::LSpin(3)   LSpin(3)
Nodes::SSpin(3)   SSpin(3)

Trees for reconstructed particles

The decay trees are the simple predicates, action on const LHCb::Particle* objects and testing the decay tree pattern, see $LOKICOREROOT/LoKi/iTree and $LOKIPHYSROOT/LoKi/Decays.h:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030
 1040// get the tree
 1050
 1060const iTree& goodTree = .... ;
 1070
 1080
 1090// get the decay 
 1100
 1110const LHCb::Particle* B = ... ;
 1120
 1130
 1140// use the tree to test the decay 
 1150
 1160
 1170const bool good = goodTree ( B  ) ;
 1180
 1190if ( good )  { /** good decay! */ }
 1200else          {  /** bad   decay! */ }

The head of the decay tree is always the node, and the branches could be nodes and/or other (sub)trees.

The basic decay trees for reconstructed particles

The basic trees for recontructed particles are defined in namespace LoKi::Decays::Trees:

Exclusive
the tree descriptor for description decays with the fixed structure, in particular "exclusive" decays. However many of inclusive decays with known structire also could be described by this class
Inclusive
the tree descriptor for description of inclusive decays
Optional
the decay description for description of decays with some "optional" components

The operations with the basic decay trees

From the basic decay treees one can create more complicated decay tree nodes using the boolean operators. The result of operations is easy to store into the simple object of type Tree_, which is the simplest (assignable) implementation of iTree_ abstract interface.

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070const iTree& t1 = ... ;
 1080const iTree& t2 = ... ;
 1090const iTree& t3 = ... ;
 1100
 1110
 1120// operations:
 1130
 1140const Tree tree1 = t1 && t2 || !t3 ;
 1150
 1160// print the tree:
 1170
 1180std::cout << tree << std::endl;
 1190
Please note, that using the parser tools being developed by Sascha Mazurov, it is possible to "invert" the string represenation of the decay tree and to construct the predicate from its valid string representation.

Important

All trees MUST be validated before the actual usage:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070const iTree& t1 = ... ;
 1080const iTree& t2 = ... ;
 1090const iTree& t3 = ... ;
 1100
 1110
 1120// operations:
 1130
 1140const Tree tree1 = t1 && t2 || !t3 ;
 1150
 1160// VALIDATE IT!
 1170
 1180LHCb::IParticlePropertySvc* svc = ...  ;
 1190
 1200// validate the tree
 1210StatusCode sc = tree1.validate( svc ) ;
 1220if ( sc.isFailure() ) {  ... INVALID TREE... } ;
 1230 

How to create simple decay descriptors?

The most simple descriptors

Assuming one needs to create the decay descriptor to match the decay . It is rather easy:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Exclusive tree ( "B0" ) ;
 1090// add the first daughter:
 1100tree += "pi+" ;
 1110// add the second daughter:
 1120tree += "pi-"  ;
 1130
 1140...
 1150{ /** do not forget to validate tree */ } 
 1160...
 1170// use it  
 1180
 1190const LHCb::Particle* B = ... ;
 1200
 1210if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
 1220
 1230std::cout << tree << std::endl ;
In this case the string representation of the tree will be " B0 -> pi+ pi- "

If one needs to consider both and decays it could be done with the simple redefintion of the decay head:

 1000// define the head:
 1010Trees::Exclusive tree ( Nodes::CC("B0") ) ;
 1020
 1030....
 1040
 1050
 1060
 1070std::cout << tree << std::endl ;
In this case the string representation of the tree will be " [ B0 ]cc -> pi+ pi- "

If one needs to consider both and decays currently it is better to do using the logical "OR" operation:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Exclusive t1 ( "B0" ) ;
 1090// add the first daughter:
 1100t1 += "K+" ;
 1110// add the second daughter:
 1120t1 += "pi-"  ;
 1130
 1140// define the head:
 1150Trees::Exclusive t2 ( "B~0" ) ;
 1160// add the first daughter:
 1170t2 += "K-" ;
 1180// add the second daughter:
 1190t2 += "pi+"  ;
 1200
 1210const Tree tree = t1 || t2 ;
 1220...
 1230{ /** do not forget to validate tree */ } 
 1240...
 1250// use it  
 1260
 1270const LHCb::Particle* B = ... ;
 1280
 1290if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
 1300
 1310std::cout << tree << std::endl ; 
In this case the string representation of the tree will be " ( ( B0 -> K+ pi- ) | ( B~0 -> K- pi+ ) ) "

Inclusive and "almost-inclusive" decays

If one needs to create the descriptor for the inclusive decay , where stands for arbitrary number of arbitrary particles, one needs to use Trees::Inclusive:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Inclusive tree ( "B0" ) ;
 1090// add the first daughter:
 1100tree += "pi+" ;
 1110// add the second daughter:
 1120tree += "pi-"  ;
 1130
 1140...
 1150{ /** do not forget to validate tree */ } 
 1160...
 1170// use it  
 1180
 1190const LHCb::Particle* B = ... ;
 1200
 1210if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
 1220
 1230std::cout << tree << std::endl ; 
In this case the string representation of the tree will be " B0 -> pi+ pi- ... "

Please note that if one needs to create the descriptor for the decay with one unknown arbitrary particle , where stands for one arbitrary particle, it is better to use Trees::Exclusive with the special node Nodes::Any

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Exclusive tree ( "B0" ) ;
 1090// add the first daughter:
 1100tree += "pi+" ;
 1110// add the second daughter:
 1120tree += "pi-"  ;
 1130// add the special 'Any' node
 1140tree += Nodes::Any() 
 1150 
 1160...
 1170{ /** do not forget to validate tree */ } 
 1180...
 1190// use it  
 1200
 1210const LHCb::Particle* B = ... ;
 1220
 1230if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
In this case the string representation of the tree will be " B0 -> pi+ pi- X "

Decays with "optional" components

If one needs to create the descriptor for the decays and one can use the descriptor of type Trees::Optional:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Optional tree ( "B0" ) ;
 1090// add the first daughter:
 1100tree += "pi+" ;
 1110// add the second daughter:
 1120tree += "pi-"  ;
 1130// ad doptional pi0 
 1140tree.addOptional ( "pi0" ) 
 1150...
 1160{ /** do not forget to validate tree */ } 
 1170...
 1180// use it  
 1190
 1200const LHCb::Particle* B = ... ;
 1210
 1220if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
In this case the string representation of the tree will be " B0 -> pi+ pi- { pi0 } "

Several optional particles are allowed:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// get the trees
 1060
 1070// define the head:
 1080Trees::Optional tree ( "B0" ) ;
 1090// add the first daughter:
 1100tree += "pi+" ;
 1110// add the second daughter:
 1120tree += "pi-"  ;
 1130// ad doptional pi0 
 1140tree.addOptional ( "pi0" ) 
 1150tree.addOptional ( "pi0" ) 
 1160
 1170...
 1180{ /** do not forget to validate tree */ } 
 1190...
 1200// use it  
 1210
 1220const LHCb::Particle* B = ... ;
 1230
 1240if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
 1250
 1260std::cout << tree << std::endl ;
In this case the string representation of the tree will be " B0 -> pi+ pi- { pi0 } { pi0 } "

Chaining of the decay

Clearly each decay tree can be considered as some kind of sub-tree for another decay tree, e.g. one can specify the decay using the following descriptor:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050// define the psi
 1060Trees::Exclusive psi ( "J/psi(1S)" ) ;
 1070psi += "mu+";
 1080psi += "mu-" ;
 1090
 1100// define phi:
 1110Trees::Exclusive phi ( "phi(1020)" );
 1120phi += "K+" ;
 1130phi += "K-" ;
 1140
 1150// define B_s:
 1160Trees::Exclusive tree ( "B_s0" ) ;
 1170tree += psi ; 
 1180tree += phi ;
 1190
 1200...
 1210{ /** do not forget to validate tree */ } 
 1220...
 1230// use it  
 1240
 1250const LHCb::Particle* B = ... ;
 1260
 1270if ( tree ( B ) ) { /* WE NEED THIS DECAY!! */ } 
 1280
 1290std::cout << tree << std::endl ;
In this case the string representation of the tree will be " ( B_s0 -> ( J/psi(1S) -> mu+ mu- ) ( phi(1020) -> K+ K- ) ) "

The matching algorithms: "match-daughters" versus "match-in-sections"

The default decay matching algorithm ("match-daughters") checks the decay structure for the direct daughters. Sometimes it is useful to perform the matching "ignoring" the intermediate resonance structure. For this case one can use the special decay matching algorithm "match-in-sections". In this case the decay is matched with the descriptor, if there exist some section of the decay with matched with the descriptor. E.g. consider the decay . This decay has following valid "sections":

"Match-in-section" algorithm will try to match these sections with respect to the decay descriptor. The matching algorithm is defines with the special key in the contructors of the tree:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050Trees::Exclusive t1 ( "B0" ,  Trees::Daughters ) ; // "match-daughters", the default one 
 1060Trees::Exclusive t2 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1070
And the decay above can be matched with one of the following "match-in-sections" descriptors:
 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050Trees::Exclusive s1 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1060Trees::Exclusive s2 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1070Trees::Exclusive s3 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1080Trees::Exclusive s4 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1090
 1100s1 += "J/psi(1S)" ;
 1110s1 += "phi(1020)";
 1120
 1130s2 += "J/psi(1S)" ;
 1140s2 += "K+" ;
 1150s2 += "K-" ;
 1160
 1170s3 += "mu+" ;
 1180s3 += "mu-" ;
 1190s3 += "phi(1020)" ;
 1200
 1210s4 += "mu+" ;
 1220s4 += "mu-" ;
 1230s4 += "K+" ;
 1240s4 += "K-" '
 1250
 1260std::cout << s1 << std::endl ;
 1270std::cout << s2 << std::endl ;
 1280std::cout << s3 << std::endl ;
 1290std::cout << s4 << std::endl ;
The string representations in these case are:
  • " ( B_s0 --> Jpsi(1S) phi(1020) ) ",
  • " ( B_s0 --> Jpsi(1S) K+ K- ) ",
  • " ( B_s0 --> mu+ mu- phi(1020) ) "
  • " ( B_s0 --> mu+ mu- K+ K- ) "
for s1,s2, s3, and s4 correspondingly. Please note that "match-in-sections" algorithm is indicated with long arrow -->.

It is also the convinient way to match few variosu decays with the same decay descriptior, e.g. the following decays

  • ,
  • ,
  • ,
can be matched with the same "three-pion" descriptor:

 1000using namespace Decays ;
 1010
 1020typedef Decays::iTree_<const LHCb::Particle*> iTree ;
 1030typedef Decays::Tree_<const LHCb::Particle*> Tree ;
 1040
 1050Trees::Exclusive s1 ( "B0" ,  Trees::Section ) ; // "match-in-sections"
 1060
 1070s1 += "pi+"  ;
 1080s1 += "pi-"  ;
 1090s1 += "pi0"  ;
 1100
 1110...
 1120
 1130std::cout << s1 << std::endl ;
The string representation of the decay is ( B0 --> pi+ pi- pi0 ) (note the long arrow -->)

Trees for Monte Carlo particles

Trees for Generator/HepMC particles

-- Vanya BELYAEV - 08 Oct 2008

Edit | Attach | Watch | Print version | History: r9 < r8 < r7 < r6 < r5 | Backlinks | Raw View | WYSIWYG | More topic actions
Topic revision: r9 - 2010-11-25 - TJGershon
 
    • Cern Search Icon Cern Search
    • TWiki Search Icon TWiki Search
    • Google Search Icon Google Search

    LHCb/FAQ All webs login

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