diff --git a/Phys/DaVinciKernel/include/Kernel/Node.h b/Phys/DaVinciKernel/include/Kernel/Node.h index 0ccd1b09e85cced1953a7a1cbefee9d85a2940fa..b510bdc94acc7dc4b1fcb16f6c27125d3aa5c7ca 100644 --- a/Phys/DaVinciKernel/include/Kernel/Node.h +++ b/Phys/DaVinciKernel/include/Kernel/Node.h @@ -8,9 +8,7 @@ * granted to it by virtue of its status as an Intergovernmental Organization * * or submit itself to any jurisdiction. * \*****************************************************************************/ - -#ifndef NODE_H -#define NODE_H 1 +#pragma once #include "Event/MCParticle.h" #include "Event/Particle.h" @@ -26,26 +24,69 @@ #include #include -namespace detail { - using tuple_nodeprop_t = std::tuple; - using ptr_of_ptrv1 = std::shared_ptr; - using ptr_of_ptrv1mc = std::shared_ptr; -} // namespace detail - namespace Decay { + + namespace detail { + /// check if node is marked with ^ (invoked in constructor) + bool checkisMarked( std::string_view node_name ) { + // output true if the expression has a "^" + return node_name.find( '^' ) != node_name.npos; + }; + + /// check if node has CC (invoked in constructor) + bool checkhasCC( std::string_view node_name ) { + // output true if the expression has "CC" + return node_name.find( "CC" ) != node_name.npos; + } + + /// prune the node from operators and returns the particle name only (invoked in constructor) + std::tuple pruneNodeName( const std::string& node_name ) { // TODO: make sure this + // is well + // documented + std::string pruned_node_name; + bool hasCC = checkhasCC( node_name ); + bool isMarked = checkisMarked( node_name ); + // cleans the nodes from operators and returns the particle name only + constexpr auto expression = []( bool cc, bool marked ) { + if ( cc && marked ) { // if node has self CC and is marked + return std::regex( "\\^" // hat + "\\s*\\[" // any whitespaces and a square bracket + "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) + // that is matched "(\\S+)" then any whitespace + "\\]\\s*CC" // closing square bracket then any whitespace and then a CC + ); + } else if ( cc && !marked ) { // if node has self CC + return std::regex( "\\[" // square bracket + "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) + // that is matched "(\\S+)" then any whitespace + "\\]\\s*CC" // closing square bracket then any whitespace and then a CC + ); + } else if ( !cc && marked ) { // if node is marked + return std::regex( "\\^" // hat + "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) + // that is matched "(\\S+)" then any whitespace + ); + } else { + return std::regex( "^\\s*" // any initial whitespaces + "(\\S+)" // matches particle name + "\\s*$" // any final whitespaces + ); + } + }; + + std::smatch what; + std::regex_search( node_name, what, expression( hasCC, isMarked ) ); + return std::make_tuple( what.str( 1 ), hasCC, isMarked ); + } + } // namespace detail + class Node { public: /// constructors - Node(); Node( const std::string& node_name ); - Node( const int& pid, const bool& hasCC, const bool& isMarked ); - Node( const detail::ptr_of_ptrv1& particle ); - Node( const detail::ptr_of_ptrv1mc& mcparticle ); - - /// copy constructor - Node( const Node& node ); - /// copy assignment operator - Node& operator=( const Node& node ); + Node( const int pid, const bool hasCC, const bool isMarked ); + Node( const LHCb::Particle& ); + Node( const LHCb::MCParticle& ); /// get charge conjugated node Node getChargeConjugate( bool reversehasCC = false ) const; @@ -77,85 +118,45 @@ namespace Decay { friend std::ostream& operator<<( std::ostream& os, const Node& node ); /// operator == implementations friend bool operator==( const Node& lhs, const Node& rhs ); - /// operator != implementations - friend bool operator!=( const Node& lhs, const Node& rhs ); /// non-templated class implementation, comparaison between a Particle and a Node template friend bool operator==( const Node& lhs, const PARTICLE* const rhs ); - template - friend bool operator!=( const Node& lhs, const PARTICLE* const rhs ) { - return !( lhs == rhs ); - } - /// non-templated class implementation, comparaison between a Particle and a Node - template - friend bool operator==( const PARTICLE* const lhs, const Node& rhs ); - template - friend bool operator!=( const PARTICLE* const lhs, const Node& rhs ) { - return !( lhs == rhs ); - } private: - /// check if node is marked with ^ (invoked in constructor) - bool checkisMarked( const std::string& node_name ) const; - /// check if node has CC (invoked in constructor) - bool checkhasCC( const std::string& node_name ) const; - /// prune the node from operators and returns the particle name only (invoked in constructor) - detail::tuple_nodeprop_t pruneNodeName( const std::string& node_name ) const; /// get pid from name int getPidFromName( const std::string& node_name ) const; /// get pid from name - std::string getNameFromPid( const int& pid ) const; + std::string getNameFromPid( int pid ) const; /// get antiparticle pid - int getAntiParticlePid( const int& pid ) const; + int getAntiParticlePid( int pid ) const; /// node ID - int m_pid; + int m_pid = 0; /// check if node has CC - bool m_hasCC; + bool m_hasCC = false; /// node marked with ^ - bool m_isMarked; + bool m_isMarked = false; /// messaging service ServiceHandle m_msgSvc{ "MessageSvc", "MessageSvcName" }; /// particle property service //"LHCb::" prefix in string since its defined in namespace! ServiceHandle m_ppSvc{ "LHCb::ParticlePropertySvc", "LHCb::ParticlePropertySvcName" }; }; -} // namespace Decay - -namespace Decay { - - Node::Node() : m_pid{ 0 }, m_hasCC{ false }, m_isMarked{ false } {} Node::Node( const std::string& node_name ) { - detail::tuple_nodeprop_t tuple_nodeprops = pruneNodeName( node_name ); - m_pid = getPidFromName( std::get<0>( tuple_nodeprops ) ); - m_hasCC = std::get<1>( tuple_nodeprops ); - m_isMarked = std::get<2>( tuple_nodeprops ); + auto tuple_nodeprops = detail::pruneNodeName( node_name ); + m_pid = getPidFromName( std::get<0>( tuple_nodeprops ) ); + m_hasCC = std::get<1>( tuple_nodeprops ); + m_isMarked = std::get<2>( tuple_nodeprops ); } - Node::Node( const detail::ptr_of_ptrv1& particle ) - : m_pid{ ( *particle.get() )->particleID().pid() }, m_hasCC{ false }, m_isMarked{ false } {} - - Node::Node( const detail::ptr_of_ptrv1mc& mcparticle ) - : m_pid{ ( *mcparticle.get() )->particleID().pid() }, m_hasCC{ false }, m_isMarked{ false } {} - // constructor for Node with pid, hasCC and isMarked - Node::Node( const int& pid, const bool& hasCC, const bool& isMarked ) + Node::Node( const int pid, const bool hasCC, const bool isMarked ) : m_pid{ pid }, m_hasCC{ hasCC }, m_isMarked{ isMarked } {} - Node::Node( const Node& node ) { // copy constructor - m_pid = node.m_pid; - m_hasCC = node.m_hasCC; - m_isMarked = node.m_isMarked; - } + Node::Node( LHCb::Particle const& particle ) : Node{ particle.particleID().pid(), false, false } {} - Node& Node::operator=( const Node& node ) { // copy assignment operator - if ( &node == this ) return *this; - m_pid = node.m_pid; - m_hasCC = node.m_hasCC; - m_isMarked = node.m_isMarked; - return *this; - } + Node::Node( LHCb::MCParticle const& mcparticle ) : Node{ mcparticle.particleID().pid(), false, false } {} Node Node::getChargeConjugate( bool reversehasCC ) const { // get charge conjugated node // make a copy of the current node @@ -168,7 +169,7 @@ namespace Decay { return node_cc; } - int Node::getAntiParticlePid( const int& pid ) const { + int Node::getAntiParticlePid( int pid ) const { // get the particle property of the node const auto* part_prop = m_ppSvc->find( LHCb::ParticleID( pid ) ); // ptr to LHCb::ParticleProperty if ( !part_prop ) @@ -183,81 +184,16 @@ namespace Decay { ". Please check your descriptor.", "Decay::Node::getChargeConjugate()", StatusCode::FAILURE }; - return ( part_prop_cc->pid() ).pid(); - } - - bool Node::checkisMarked( const std::string& node_name ) const { - // output true if the expression has a "^" - return std::regex_search( node_name, std::regex{ "\\^" } ); // matches a hat - }; - - bool Node::checkhasCC( const std::string& node_name ) const { - // output true if the expression has "CC" - return std::regex_search( node_name, std::regex{ "C{2}" } ); // matches 2 C's in a row + return part_prop_cc->pid().pid(); } /// returns a vector of node possibilities based on the CC property of the Node std::vector Node::getPossibilities() const { - // make a empty vector of nodes - std::vector vectorOfNodes; // set hasCC property to false for all possibilities because we want vector of "simple" nodes - bool hasCC{ false }; if ( m_hasCC ) { - // if has cc reserve two nodes in memory - vectorOfNodes.reserve( 2 ); - vectorOfNodes.emplace_back( m_pid, hasCC, m_isMarked ); - vectorOfNodes.emplace_back( getAntiParticlePid( m_pid ), hasCC, m_isMarked ); - return vectorOfNodes; - } - vectorOfNodes.reserve( 1 ); - vectorOfNodes.emplace_back( m_pid, hasCC, m_isMarked ); - return vectorOfNodes; - } - - detail::tuple_nodeprop_t Node::pruneNodeName( const std::string& node_name ) const { // TODO: make sure this - // is well - // documented - std::string pruned_node_name; - bool hasCC = checkhasCC( node_name ); - bool isMarked = checkisMarked( node_name ); - // cleans the nodes from operators and returns the particle name only - if ( hasCC && isMarked ) { // if node has self CC and is marked - std::regex expression( "\\^" // hat - "\\s*\\[" // any whitespaces and a square bracket - "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) - // that is matched "(\\S+)" then any whitespace - "\\]\\s*CC" // closing square bracket then any whitespace and then a CC - ); - std::smatch what; - std::regex_search( node_name, what, expression ); - pruned_node_name = what.str( 1 ); - } else if ( hasCC && !isMarked ) { // if node has self CC - std::regex expression( "\\[" // square bracket - "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) - // that is matched "(\\S+)" then any whitespace - "\\]\\s*CC" // closing square bracket then any whitespace and then a CC - ); - std::smatch what; - std::regex_search( node_name, what, expression ); - pruned_node_name = what.str( 1 ); - } else if ( !hasCC && isMarked ) { // if node is marked - std::regex expression( "\\^" // hat - "\\s*(\\S+)\\s*" // any whitespace then one or more non-whitespace character(s) - // that is matched "(\\S+)" then any whitespace - ); - std::smatch what; - std::regex_search( node_name, what, expression ); - pruned_node_name = what.str( 1 ); - } else { - std::regex expression( "^\\s*" // any initial whitespaces - "(\\S+)" // matches particle name - "\\s*$" // any final whitespaces - ); - std::smatch what; - std::regex_search( node_name, what, expression ); - pruned_node_name = what.str( 1 ); + return { Node{ m_pid, false, m_isMarked }, Node{ getAntiParticlePid( m_pid ), false, m_isMarked } }; } - return std::make_tuple( pruned_node_name, hasCC, isMarked ); + return { Node{ m_pid, false, m_isMarked } }; } int Node::getPidFromName( const std::string& name ) const { @@ -265,10 +201,10 @@ namespace Decay { if ( !part_prop ) throw GaudiException{ "Could not interpret the node name " + name + ". Please check your descriptor.", "Decay::Node::getPidFromName()", StatusCode::FAILURE }; - return ( part_prop->pid() ).pid(); + return part_prop->pid().pid(); } - std::string Node::getNameFromPid( const int& pid ) const { + std::string Node::getNameFromPid( int pid ) const { const auto* part_prop = m_ppSvc->find( LHCb::ParticleID( pid ) ); // ptr to LHCb::ParticleProperty if ( !part_prop ) throw GaudiException{ "Could not interpret the particle ID " + std::to_string( pid ) + @@ -287,8 +223,6 @@ namespace Decay { os << "^" << node.name(); else os << node.name(); - // MsgStream log( m_msgSvc.get(), "Decay::Node" ); - // log << MSG::INFO << "Node: Defining boost regex: " << endmsg; return os; } @@ -299,31 +233,18 @@ namespace Decay { if ( lhs.m_hasCC ) { // Return a copy of this node with charged conjugate node and hasCC flag set to false // hasCC flag has to be false otherwise stuck in infinite loop - Node lhs_cc{ lhs.getChargeConjugate( reversehasCC ) }; - return rhs == lhs_cc; + return rhs == lhs.getChargeConjugate( reversehasCC ); } else if ( rhs.m_hasCC ) { // Return a copy of this node with charged conjugate node and hasCC flag set to false // hasCC flag has to be false otherwise stuck in infinite loop - Node rhs_cc{ rhs.getChargeConjugate( reversehasCC ) }; - return lhs == rhs_cc; + return lhs == rhs.getChargeConjugate( reversehasCC ); } return false; } - bool operator!=( const Node& lhs, const Node& rhs ) { return !( lhs == rhs ); } - template bool operator==( const Node& lhs, const PARTICLE* const rhs ) { - if ( lhs.m_pid == ( rhs->particleID() ).pid() ) return true; - return false; - } - - template - bool operator==( const PARTICLE* const lhs, const Node& rhs ) { - if ( ( lhs->particleID() ).pid() == rhs.m_pid ) return true; - return false; + return lhs.m_pid == rhs->particleID().pid(); } } // namespace Decay - -#endif // NODE_H diff --git a/Phys/DaVinciKernel/include/Kernel/Tree.h b/Phys/DaVinciKernel/include/Kernel/Tree.h index 001adc6cb2c9a8a92afeee93dd7d0050fb5a3644..0fbbaa9618690eef12c62ce7ec49bafd686e02aa 100644 --- a/Phys/DaVinciKernel/include/Kernel/Tree.h +++ b/Phys/DaVinciKernel/include/Kernel/Tree.h @@ -8,9 +8,15 @@ * granted to it by virtue of its status as an Intergovernmental Organization * * or submit itself to any jurisdiction. * \*****************************************************************************/ +#pragma once -#ifndef DECAYTREE_H -#define DECAYTREE_H 1 +#include "Event/Particle.h" +#include "GaudiKernel/GaudiException.h" +#include "GaudiKernel/IMessageSvc.h" +#include "GaudiKernel/MsgStream.h" +#include "GaudiKernel/ServiceHandle.h" +#include "Kernel/Arrow.h" +#include "Kernel/Node.h" #include #include @@ -22,61 +28,61 @@ #include #include -#include "Event/Particle.h" -#include "GaudiKernel/GaudiException.h" -#include "GaudiKernel/IMessageSvc.h" -#include "GaudiKernel/MsgStream.h" -#include "GaudiKernel/ServiceHandle.h" -#include "Kernel/Arrow.h" -#include "Kernel/Node.h" - namespace Decay { class Tree; -} // namespace Decay - -// forward declare for detail namespace -namespace detail { - struct classprops_t { - Decay::Node headNode; - Decay::Arrow first_arrow; - std::vector daughters; - bool hasCC; - bool isMarked; - }; - struct arrowprops_t { - Decay::Arrow first_arrow; - unsigned int n_first_arrow; - size_t first_arrow_pos; - unsigned int n_arrowtypes; - }; - using tuple_parentdaughter_t = std::tuple; - using vec_str_t = std::vector; - using vec_dt_t = std::vector; - using vec_vec_dt_t = std::vector; + template - using vec_ptr_t = std::vector; + concept isParticle = std::same_as || std::same_as; + + // forward declare for detail namespace + namespace detail { + struct classprops_t { + Decay::Node headNode; + Decay::Arrow first_arrow; + std::vector daughters; + bool hasCC; + bool isMarked; + }; + struct arrowprops_t { + Decay::Arrow first_arrow; + unsigned int n_first_arrow; + size_t first_arrow_pos; + unsigned int n_arrowtypes; + }; + using tuple_parentdaughter_t = std::tuple; + using vec_str_t = std::vector; + using vec_dt_t = std::vector; + using vec_vec_dt_t = std::vector; + template + using vec_ptr_t = std::vector; - classprops_t parseDescriptor( std::string descriptor ); - vec_vec_dt_t cartesianProduct( const vec_vec_dt_t& vec_vec_dt ); -} // namespace detail + classprops_t parseDescriptor( std::string descriptor ); + vec_vec_dt_t cartesianProduct( const vec_vec_dt_t& vec_vec_dt ); + + void pruneDescriptor( std::string& s ); + + /// Helper function to get daughter for particle and MCParticle + auto getParticleDaughters( const LHCb::Particle& particle ) { return particle.daughtersVector(); } + + auto getParticleDaughters( const LHCb::MCParticle& particle ) { + const auto* goodVtx = particle.goodEndVertex(); + // if goodVtx is null (could be basic particles) so just return an empty vector + if ( !goodVtx ) return LHCb::MCParticle::ConstVector{}; + return LHCb::MCParticle::ConstVector( goodVtx->products().begin(), goodVtx->products().end() ); + } + + } // namespace detail -namespace Decay { class Tree { public: /// constructors - Tree(); Tree( const std::string& descriptor ); Tree( detail::classprops_t ); Tree( Node ); /// TODO: Could sort daughter subtree descriptors by their depth (also applies to constructor Tree(particle) ) Tree( Node headNode, Arrow arrow, detail::vec_dt_t daughters, bool hasCC, bool isMarked ); - Tree( detail::ptr_of_ptrv1 particle ); // Particle constructor - Tree( detail::ptr_of_ptrv1mc mcparticle ); // MCParticle constructor - - /// copy constructor - Tree( const Tree& dt ); - /// copy assignment operator - Tree& operator=( const Tree& dt ); + Tree( LHCb::Particle const& ); // Particle constructor + Tree( LHCb::MCParticle const& ); // MCParticle constructor /// get charge conjugated node Tree getChargeConjugate( bool reversehasCC = false ) const; @@ -130,8 +136,6 @@ namespace Decay { /// TODO: remove in current implementation friend bool operator==( const Tree& lhs, const Tree& rhs ); /// friend function for operator!= - /// TODO: remove in current implementation - friend bool operator!=( const Tree& lhs, const Tree& rhs ) { return !( lhs == rhs ); } // friend function comparing two trees (Note: Arguments are non-const references) // They are non-const since the isMarked and hasMatched flags of the trees are modified. /// TODO: remove @@ -140,30 +144,23 @@ namespace Decay { /// DecayFinder no template implementation /// friend function comparing a particle and a tree note: internally makes a copy of the tree to change the /// m_hasMatched property of the tree to deal with identical particles - template - friend bool operator==( const PARTICLE* const lhs, const Tree& rhs ); - /// friend function comparing a particle and a tree note: internally makes a copy of the tree to change the - /// m_hasMatched property of the tree to deal with identical particles - template - friend bool operator==( const Tree& lhs, const PARTICLE* const rhs ); + template + friend bool operator==( const PARTICLE& lhs, const Tree& rhs ); /// loop over the particle and the decaytree to evaluate if they match - template - bool hasMatchWithParticle( const PARTICLE* const particle ); + template + bool hasMatchWithParticle( const PARTICLE& particle ); /// finds the marked particles and fills the output vector - template + template void collectMarkedParticles( const PARTICLE* particle, std::vector& output ) const; private: /// TODO: Check that the following private functions are well documented /// private function that works with collectMarkedParticlesImpl - template + template void collectMarkedParticlesRecursive( const PARTICLE* particle, std::vector& output ); /// set all the headNodes isMarked along with current and sub-trees isMarked flag /// TODO: remove void setAllisMarked( bool isMarked ); - /// Helper function to get daughter for particle and MCParticle - template - auto getParticleDaughters( const PARTICLE* const particle ) const; /// head node Node m_headNode; @@ -176,14 +173,12 @@ namespace Decay { /// is marked flag in decay e.g. ^(B0 -> K+ K-) or ^([B0 -> K+ K-]CC) bool m_isMarked{ false }; /// decay tree has been matched - std::optional m_hasMatched{ std::nullopt }; + std::optional m_hasMatched{}; /// messaging service /// TODO: Need to set the verbosity of messaging service ServiceHandle m_msgSvc{ "MessageSvc", "MessageSvcName" }; }; - Tree::Tree() = default; - Tree::Tree( Node headNode ) : m_headNode{ std::move( headNode ) } {} Tree::Tree( Node headNode, Arrow arrow, detail::vec_dt_t daughters, bool hasCC, bool isMarked ) @@ -191,8 +186,7 @@ namespace Decay { , m_Arrow{ arrow } , m_daughters{ std::move( daughters ) } , m_hasCC{ hasCC } - , m_isMarked{ isMarked } - , m_hasMatched{ std::nullopt } {} + , m_isMarked{ isMarked } {} Tree::Tree( detail::classprops_t props ) : Tree{ std::move( props.headNode ), props.first_arrow, std::move( props.daughters ), props.hasCC, @@ -200,58 +194,27 @@ namespace Decay { Tree::Tree( const std::string& descriptor_str ) : Tree{ detail::parseDescriptor( descriptor_str ) } {} - Tree::Tree( detail::ptr_of_ptrv1 particle ) : m_headNode{ Node( std::move( particle ) ) }, m_Arrow{ Arrow::Single } { + Tree::Tree( LHCb::Particle const& particle ) : m_headNode{ Node( particle ) }, m_Arrow{ Arrow::Single } { // get parent particle pointer from "pointer of pointer" - const auto* particle_ptr = *( particle.get() ); // check if it is basic - if ( !particle_ptr->isBasicParticle() ) { + if ( !particle.isBasicParticle() ) { // if not loop over daughter particles - for ( const auto* daug : particle_ptr->daughtersVector() ) { - // built a decay tree for each daughter particle - m_daughters.push_back( std::make_shared( daug ) ); - } + const auto& children = particle.daughtersVector(); + std::transform( children.begin(), children.end(), std::back_inserter( m_daughters ), + []( const auto& d ) { return Node{ *d }; } ); } } - Tree::Tree( detail::ptr_of_ptrv1mc mcparticle ) - : m_headNode{ Node( std::move( mcparticle ) ) }, m_Arrow{ Arrow::Single } { - // get parent particle pointer from "pointer of pointer" - const auto* mcparticle_ptr = *( mcparticle.get() ); - // get the good end vertex (the ) + Tree::Tree( LHCb::MCParticle const& mcparticle ) : m_headNode{ mcparticle }, m_Arrow{ Arrow::Single } { // get MCParticle's first end-vertex that likely destroyed the particle - const auto* goodVtx = mcparticle_ptr->goodEndVertex(); // MCVertex - if ( goodVtx ) { + if ( const auto* goodVtx = mcparticle.goodEndVertex(); goodVtx ) { // if not loop over daughter particles - for ( const auto& mcdaug : goodVtx->products() ) { // SmartRef - auto mcdaug_ptr = std::make_shared( mcdaug.data() ); - // built a decay tree for each daughter particle - m_daughters.emplace_back( mcdaug_ptr ); - } + const auto& children = goodVtx->products(); + std::transform( children.begin(), children.end(), std::back_inserter( m_daughters ), + []( const auto& ref ) { return *ref; } ); } } - /// copy constructor - Tree::Tree( const Tree& dt ) { - m_headNode = dt.m_headNode; - m_Arrow = dt.m_Arrow; - m_daughters = dt.m_daughters; - m_hasCC = dt.m_hasCC; - m_isMarked = dt.m_isMarked; - m_hasMatched = dt.m_hasMatched; - } - - /// copy assignment - Tree& Tree::operator=( const Tree& dt ) { - if ( &dt == this ) return *this; - m_hasCC = dt.m_hasCC; - m_isMarked = dt.m_isMarked; - m_headNode = dt.m_headNode; - m_daughters = dt.m_daughters; - m_hasMatched = dt.m_hasMatched; - m_Arrow = dt.m_Arrow; - return *this; - } - Tree Tree::getChargeConjugate( bool reversehasCC ) const { // get charge conjugated // copy of current tree // create new Tree with current tree @@ -357,128 +320,83 @@ namespace Decay { std::ostream& operator<<( std::ostream& os, const Tree& dt ) { return os << dt.descriptor(); } /////////////// implementation for DecayFinder - template - bool operator==( const PARTICLE* const lhs, const Tree& rhs ) { - if ( !lhs ) { - throw GaudiException{ "The input particle is null (nullptr). Please check!", - "Decay::operator==(const PARTICLE *lhs, const Decay::Tree &rhs)", StatusCode::FAILURE }; - } - // make a copy of the decay tree - Tree rhs_copy( rhs ); - return rhs_copy.hasMatchWithParticle( lhs ); - } - - template - bool operator==( const Tree& lhs, const PARTICLE* const rhs ) { - if ( !rhs ) { - throw GaudiException{ "The input particle is null (nullptr). Please check!", - "Decay::operator==(const Tree& lhs, const PARTICLE* rhs)", StatusCode::FAILURE }; - } + template + bool operator==( const PARTICLE& lhs, const Tree& rhs ) { // make a copy of the decay tree - Tree lhs_copy( lhs ); - return lhs_copy.hasMatchWithParticle( rhs ); - } - - template - auto Tree::getParticleDaughters( const PARTICLE* const particle ) const { - if ( !particle ) - throw GaudiException{ "The input particle to get daughters is null (nullptr). Please check!", - "Decay::Tree::getParticleDaughters(const PARTICLE *particle)", StatusCode::FAILURE }; - - return particle->daughtersVector(); + return Tree{ rhs }.hasMatchWithParticle( lhs ); } - template <> - auto Tree::getParticleDaughters( const LHCb::MCParticle* const particle ) const { - if ( !particle ) - throw GaudiException{ "The input MC particle to get daughters is null (nullptr). Please check!", - "Decay::Tree::getParticleDaughters(const PARTICLE *particle)", StatusCode::FAILURE }; - - const auto* goodVtx = particle->goodEndVertex(); - if ( !goodVtx ) - return LHCb::MCParticle::ConstVector( ( goodVtx->products() ).begin(), ( goodVtx->products() ).end() ); - // goodVtx is null (could be basic particles) so just return an empty vector - return LHCb::MCParticle::ConstVector{}; - } - - template - bool Tree::hasMatchWithParticle( const PARTICLE* const particle ) { + template + bool Tree::hasMatchWithParticle( const PARTICLE& particle ) { // set messaging service - MsgStream log( ( this->m_msgSvc ).get(), "Decay::Tree" ); + MsgStream log( ( m_msgSvc ).get(), "Decay::Tree" ); bool is_verbose = ( log.level() == MSG::VERBOSE ); // check for nullptr - if ( !particle ) { - throw GaudiException{ "The input particle is null (nullptr). Please check!", - "Decay::Tree::hasMatchWithParticle(const PARTICLE *particle)", StatusCode::FAILURE }; - } // create a reference tree from the reconstructed particle (for debugging) - auto part_ptr = std::make_shared( particle ); - Tree ref_dt( part_ptr ); + Tree ref_dt( particle ); // bool is_verbose = true; if ( is_verbose ) log << MSG::INFO << "Comparing tree: " << *this << "with particle: " << ref_dt << endmsg; // check if the head nodes match - if ( m_headNode != particle ) return false; + if ( m_headNode != &particle ) return false; // head nodes have matched if ( is_verbose ) - log << MSG::INFO << "Head nodes are equal b/w: " << this->m_headNode << " and RHS: " << ref_dt.m_headNode - << endmsg; + log << MSG::INFO << "Head nodes are equal b/w: " << m_headNode << " and RHS: " << ref_dt.m_headNode << endmsg; // check if the user tree doesn't have daughters - if ( !( this->hasDaughters() ) ) { - this->sethasMatched( true ); + if ( !( hasDaughters() ) ) { + sethasMatched( true ); return true; } // both trees have daughters if ( is_verbose ) log << MSG::INFO << "Trees: " << *this << " and particle: " << ref_dt << " both have daughters." << endmsg; // check if daughter size is equal - if ( getParticleDaughters( particle ).size() != ( this->m_daughters ).size() ) return false; + if ( detail::getParticleDaughters( particle ).size() != ( m_daughters ).size() ) return false; if ( is_verbose ) log << MSG::INFO << "Trees: " << *this << " and particle: " << ref_dt << " have same daughter sizes." << endmsg; // both trees have equal head node and equal daughter size // compare the particle daughter with the tree daughter - for ( const auto* particle_daughter : getParticleDaughters( particle ) ) { - for ( auto& tree_daughter : this->m_daughters ) { + for ( const auto* particle_daughter : detail::getParticleDaughters( particle ) ) { + for ( auto& tree_daughter : m_daughters ) { // create a reference daughter tree - auto part_daug_ptr = std::make_shared( particle_daughter ); if ( is_verbose ) log << MSG::INFO << "Comparing tree daughter: " << tree_daughter - << " and particle daughter: " << Tree( part_daug_ptr ) << endmsg; + << " and particle daughter: " << Tree( *particle_daughter ) << endmsg; // check if tree daughter has already been matched if ( tree_daughter.hasMatched() ) { if ( is_verbose ) log << MSG::INFO << "Tree daughter has already been matched: " << tree_daughter << endmsg; continue; } // check if the tree daughter matches with the particle daughter - else if ( tree_daughter.hasMatchWithParticle( particle_daughter ) ) { + else if ( tree_daughter.hasMatchWithParticle( *particle_daughter ) ) { if ( is_verbose ) log << MSG::INFO << "Match found for tree daughter: " << tree_daughter - << " and particle daughter: " << Tree( part_daug_ptr ) << endmsg; + << " and particle daughter: " << Tree( *particle_daughter ) << endmsg; break; // when there is a match } // no match has been found if ( is_verbose ) log << MSG::INFO << "No match for tree daughter: " << tree_daughter - << " and particle daughter: " << Tree( part_daug_ptr ) << endmsg; + << " and particle daughter: " << Tree( *particle_daughter ) << endmsg; } } // check if all the daughters have matched - if ( this->hasMatched() ) { + if ( hasMatched() ) { if ( is_verbose ) log << MSG::INFO << "Everything has matched, Tree: " << *this << " and Particle: " << ref_dt << endmsg; // need to set all the daughters to matched when the whole tree has matched. - this->sethasMatched( true ); + sethasMatched( true ); return true; } else { if ( is_verbose ) log << MSG::INFO << "Not everything is matched, Tree: " << *this << " and Particle " << ref_dt << endmsg; // Even when the individual particles in a tree have matched, but the whole tree hasn't matched // need to set all the daughters back to not being matched - this->sethasMatched( false ); + sethasMatched( false ); return false; } } - template + template void Tree::collectMarkedParticles( const PARTICLE* particle, std::vector& output ) const { // check for nullptr if ( !particle ) { @@ -490,7 +408,7 @@ namespace Decay { // make a copy of the tree here Tree temp_tree{ *this }; // check for hat in descriptor - if ( !std::regex_search( temp_tree.descriptor(), std::regex( "\\^" ) ) ) { + if ( temp_tree.descriptor().find( '^' ) == temp_tree.descriptor().npos ) { // when there is no hat, append the head particle output.push_back( particle ); return; @@ -646,7 +564,7 @@ namespace Decay { return true; } - template + template void Tree::collectMarkedParticlesRecursive( const PARTICLE* particle, std::vector& output ) { // check for nullptr if ( !particle ) { @@ -659,25 +577,25 @@ namespace Decay { if ( m_headNode != particle ) return; // head nodes have matched // check if the user tree doesn't have daughters - if ( !( this->hasDaughters() ) ) { + if ( !( hasDaughters() ) ) { // set has matched of head node w/o daughters to true (helps with identical particles) - this->sethasMatched( true ); + sethasMatched( true ); // if node or tree marked push to output vector - if ( this->isMarked() || ( this->headNode() ).isMarked() ) output.push_back( particle ); + if ( isMarked() || ( headNode() ).isMarked() ) output.push_back( particle ); return; } // both trees have daughters // if daughter size is unequal return - if ( getParticleDaughters( particle ).size() != ( this->daughters() ).size() ) return; + if ( detail::getParticleDaughters( *particle ).size() != ( daughters() ).size() ) return; // both trees have equal head node and equal daughter size // if node or tree marked push to output vector - if ( this->isMarked() || ( this->headNode() ).isMarked() ) output.push_back( particle ); + if ( isMarked() || ( headNode() ).isMarked() ) output.push_back( particle ); // compare the daughters - for ( const auto* particle_daughter : getParticleDaughters( particle ) ) { - for ( auto& tree_daughter : this->m_daughters ) { + for ( const auto* particle_daughter : detail::getParticleDaughters( *particle ) ) { + for ( auto& tree_daughter : m_daughters ) { if ( tree_daughter.hasMatched() ) continue; // daughter has already been matched continue (dealing with identical particles) - else if ( tree_daughter == particle_daughter ) { // daughters are equal, find the marked particle + else if ( tree_daughter == *particle_daughter ) { // daughters are equal, find the marked particle tree_daughter.collectMarkedParticlesRecursive( particle_daughter, output ); break; } @@ -693,383 +611,383 @@ namespace Decay { if ( hasDaughters() ) for ( auto& daughter : m_daughters ) daughter.setAllisMarked( isMarked ); } -}; // namespace Decay - -namespace detail { - - /// Cartesian product between the vectors in a vector of vectors, similar to the itertools.product in Python - /// e.g. for a vector of vectors of ints { {1, 2}, {11, 12, 13}, {21, 22} } it would return - /// { {1, 11, 21}, {1, 11, 22}, {1, 12, 21}, {1, 12, 22}, ... {2, 13, 22} } - vec_vec_dt_t cartesianProduct( const vec_vec_dt_t& vec_vec_dt ) { - // TODO: Try to clean this up a little bit - vec_vec_dt_t result = { {} }; // TODO: Try to reserve the memory - for ( const auto& vector1 : vec_vec_dt ) { - vec_vec_dt_t res_tmp; - for ( const auto& vector2 : result ) { - for ( const auto& decayTree : vector1 ) { - res_tmp.push_back( vector2 ); - res_tmp.back().push_back( decayTree ); + + namespace detail { + + /// Cartesian product between the vectors in a vector of vectors, similar to the itertools.product in Python + /// e.g. for a vector of vectors of ints { {1, 2}, {11, 12, 13}, {21, 22} } it would return + /// { {1, 11, 21}, {1, 11, 22}, {1, 12, 21}, {1, 12, 22}, ... {2, 13, 22} } + vec_vec_dt_t cartesianProduct( const vec_vec_dt_t& vec_vec_dt ) { + // TODO: Try to clean this up a little bit + vec_vec_dt_t result = { {} }; // TODO: Try to reserve the memory + for ( const auto& vector1 : vec_vec_dt ) { + vec_vec_dt_t res_tmp; + for ( const auto& vector2 : result ) { + for ( const auto& decayTree : vector1 ) { + res_tmp.push_back( vector2 ); + res_tmp.back().push_back( decayTree ); + } } + result = std::move( res_tmp ); // moves all elements from r to result leaving r empty } - result = std::move( res_tmp ); // moves all elements from r to result leaving r empty + return result; } - return result; - } - /// strips initial and trailing whitespaces from descriptor - void pruneDescriptor( std::string& descriptor ) { - // strips initial and trailing whitespaces from a string - std::regex expression_1( "^\\s*" ); // finds any number of whitespaces at start of string - descriptor = std::regex_replace( descriptor, expression_1, "" ); // removes initial whitespaces - std::regex expression_2( "\\s*$" ); // finds any number of whitespaces at end of string - descriptor = std::regex_replace( descriptor, expression_2, "" ); // removes trailing whitespaces - } + /// strips initial and trailing whitespaces from descriptor + void pruneDescriptor( std::string& s ) { + // strips initial and trailing whitespaces from a string + auto isspace = []( const char c ) { return std::isspace( c ); }; + s.erase( std::find_if_not( s.rbegin(), s.rend(), isspace ).base(), s.end() ); + s.erase( s.begin(), std::find_if_not( s.begin(), s.end(), isspace ) ); + } - /// check that the descriptor contains balanced brackets (any type) i.e. opening brackets have corresponding - /// closing one ((...)) - bool hasBalancedBrackets( std::string_view str ) { // function to check if brackets are - // balanced in a descriptor - // create a stack that holds characters - std::stack stk{}; - // iterate trough the string character by character - for ( auto i : str ) { - // if you encounter an opening bracket add it to the stack - if ( i == '(' || i == '[' || i == '{' ) { - stk.push( i ); - } - // if you encounter a closing bracket... - else if ( i == ')' || i == ']' || i == '}' ) { - // ...and if the stack is empty return false - if ( stk.empty() ) return false; - // ...otherwise compare it to the previous bracket stored in the stack - char ch = stk.top(); - // decrement the stack such that balanced brackets lead to an empty stack - stk.pop(); - if ( ch == '(' && i == ')' ) continue; - if ( ch == '[' && i == ']' ) continue; - if ( ch == '{' && i == '}' ) continue; - // returns false if not matched - return false; + /// check that the descriptor contains balanced brackets (any type) i.e. opening brackets have corresponding + /// closing one ((...)) + bool hasBalancedBrackets( std::string_view str ) { // function to check if brackets are + // balanced in a descriptor + // create a stack that holds characters + std::stack stk{}; + // iterate trough the string character by character + for ( auto i : str ) { + // if you encounter an opening bracket add it to the stack + if ( i == '(' || i == '[' || i == '{' ) { + stk.push( i ); + } + // if you encounter a closing bracket... + else if ( i == ')' || i == ']' || i == '}' ) { + // ...and if the stack is empty return false + if ( stk.empty() ) return false; + // ...otherwise compare it to the previous bracket stored in the stack + char ch = stk.top(); + // decrement the stack such that balanced brackets lead to an empty stack + stk.pop(); + if ( ch == '(' && i == ')' ) continue; + if ( ch == '[' && i == ']' ) continue; + if ( ch == '{' && i == '}' ) continue; + // returns false if not matched + return false; + } } + return stk.empty(); } - return stk.empty(); - } - /// strips the descriptor from [...]CC and sets m_hasCC flags (invoked in constructor) - bool stripCCFromDescriptor( std::string& descriptor ) { // function used for descriptor - // parsing - // define a helper lambda function to check cc - auto checkHasCC = []( const std::string& descriptor ) -> bool { - // output true if the expression has "CC" and false otherwise - std::regex expression( "^\\[" // square bracket at the beginning of string - ".*" // any character in between - "\\]\\s*CC$" // square bracket and CC at end of string - ); - return std::regex_search( descriptor, expression ); - }; + /// strips the descriptor from [...]CC and sets m_hasCC flags (invoked in constructor) + bool stripCCFromDescriptor( std::string& descriptor ) { // function used for descriptor + // parsing + // define a helper lambda function to check cc + auto checkHasCC = []( const std::string& descriptor ) -> bool { + // output true if the expression has "CC" and false otherwise + std::regex expression( "^\\[" // square bracket at the beginning of string + ".*" // any character in between + "\\]\\s*CC$" // square bracket and CC at end of string + ); + return std::regex_search( descriptor, expression ); + }; + + // set hasCC to false + bool hasCC = false; + + // prune descriptor if it has CC + pruneDescriptor( descriptor ); - // set hasCC to false - bool hasCC = false; - - // prune descriptor if it has CC - pruneDescriptor( descriptor ); - - // check that we have balanced square brackets then set hasCC to true and strip square brackets - if ( checkHasCC( descriptor ) ) { - // if "CC" then strip it from descriptor - // e.g. "[[X]CC -> Y1 [Y2]CC]CC" => "[X]CC -> Y1 [Y2]CC" - std::regex expression( "^\\[" // opening square bracket at start of string - "\\s*" // any whitespaces - "(.*)" // matches what is in between i.e. striped descriptor - "\\]" // closing bracket - "\\s*CC$" // any whitespaces followed by CC at end of string - ); - std::smatch what; - std::regex_search( descriptor, what, expression ); - // descriptor has been strip of outer-most [..]CC, now check if the square brackets are balanced - // This is required for cases where the descriptor is "[X]CC -> Y1 [Y2]CC" and - // the checkHassCC passes with descriptor = "X]CC -> Y1 [Y2" but this is an invalid descriptor. - if ( hasBalancedBrackets( what.str( 1 ) ) ) { // check for balanced brackets - hasCC = true; - descriptor = what.str( 1 ); - pruneDescriptor( descriptor ); - } // No need to throw an error here + // check that we have balanced square brackets then set hasCC to true and strip square brackets + if ( checkHasCC( descriptor ) ) { + // if "CC" then strip it from descriptor + // e.g. "[[X]CC -> Y1 [Y2]CC]CC" => "[X]CC -> Y1 [Y2]CC" + std::regex expression( "^\\[" // opening square bracket at start of string + "\\s*" // any whitespaces + "(.*)" // matches what is in between i.e. striped descriptor + "\\]" // closing bracket + "\\s*CC$" // any whitespaces followed by CC at end of string + ); + std::smatch what; + std::regex_search( descriptor, what, expression ); + // descriptor has been strip of outer-most [..]CC, now check if the square brackets are balanced + // This is required for cases where the descriptor is "[X]CC -> Y1 [Y2]CC" and + // the checkHassCC passes with descriptor = "X]CC -> Y1 [Y2" but this is an invalid descriptor. + if ( hasBalancedBrackets( what.str( 1 ) ) ) { // check for balanced brackets + hasCC = true; + descriptor = what.str( 1 ); + pruneDescriptor( descriptor ); + } // No need to throw an error here + } + return hasCC; } - return hasCC; - } - /// strips the descriptor from the hat ^(...) and sets m_isMarked flags (invoked in Tree constructor) - bool stripHatFromDescriptor( std::string& descriptor ) { // function used for descriptor - // parsing + /// strips the descriptor from the hat ^(...) and sets m_isMarked flags (invoked in Tree constructor) + bool stripHatFromDescriptor( std::string& descriptor ) { // function used for descriptor + // parsing + + // define a helper lambda function for checking if marked + auto checkisMarked = []( const std::string& descriptor ) { + const std::regex expression( "^\\^" // hat at start of string + "\\s*\\(" // any number of space and a bracket + ".*" // any characters + "\\)" // closing bracket + ); + return std::regex_search( descriptor, expression ); + }; + + // define a helper lambda function to check if there is brackets + auto hasBrackets = []( const std::string& descriptor ) { + const std::regex expression( "^\\(" // bracket at start of string + ".*" // any characters + "\\)" // bracket + ); + return std::regex_search( descriptor, expression ); + }; + + // set is marked to false + bool isMarked = false; + + // prune descriptor + pruneDescriptor( descriptor ); - // define a helper lambda function for checking if marked - auto checkisMarked = []( const std::string& descriptor ) { - std::regex expression( "^\\^" // hat at start of string - "\\s*\\(" // any number of space and a bracket - ".*" // any characters - "\\)" // closing bracket - ); - return std::regex_search( descriptor, expression ); - }; + // if descriptor has mark with brackets then strip mark and brackets setting isMarked + // i.e. "^(B->K+ K-)" -> "B->K+ K-" and isMarked = True + if ( checkisMarked( descriptor ) ) { + // decay is marked + isMarked = true; + // strips decay descriptor from hat + outside brackets + const std::regex expression( "^\\^" // hat and opening bracket at start of string + "\\s*\\(" // any whitespaces followed by opening bracket + "(.*)" // matches what is in between i.e. striped descriptor + "\\)$" // bracket at end of string + ); + std::smatch what; + std::regex_search( descriptor, what, expression ); + descriptor = what.str( 1 ); + pruneDescriptor( descriptor ); + } - // define a helper lambda function to check if there is brackets - auto hasBrackets = []( const std::string& descriptor ) { - std::regex expression( "^\\(" // bracket at start of string - ".*" // any characters - "\\)" // bracket - ); - return std::regex_search( descriptor, expression ); - }; + // if no mark and contains brackets then strip brackets setting isMarked to false + // i.e. "(B->K+ K-)" -> "B->K+ K-" + if ( hasBrackets( descriptor ) ) { // tests the matched string for brackets + // strips decay from outside brackets only + const std::regex expression( "^\\(\\s*" // opening bracket at start of string + "(.*)" // matches what is in between i.e. striped descriptor + "\\s*\\)$" // bracket at end of string + ); + std::smatch what; + std::regex_search( descriptor, what, expression ); + descriptor = what.str( 1 ); + pruneDescriptor( descriptor ); + } - // set is marked to false - bool isMarked = false; - - // prune descriptor - pruneDescriptor( descriptor ); - - // if descriptor has mark with brackets then strip mark and brackets setting isMarked - // i.e. "^(B->K+ K-)" -> "B->K+ K-" and isMarked = True - if ( checkisMarked( descriptor ) ) { - // decay is marked - isMarked = true; - // strips decay descriptor from hat + outside brackets - std::regex expression( "^\\^" // hat and opening bracket at start of string - "\\s*\\(" // any whitespaces followed by opening bracket - "(.*)" // matches what is in between i.e. striped descriptor - "\\)$" // bracket at end of string - ); - std::smatch what; - std::regex_search( descriptor, what, expression ); - descriptor = what.str( 1 ); - pruneDescriptor( descriptor ); + return isMarked; } - // if no mark and contains brackets then strip brackets setting isMarked to false - // i.e. "(B->K+ K-)" -> "B->K+ K-" - if ( hasBrackets( descriptor ) ) { // tests the matched string for brackets - // strips decay from outside brackets only - std::regex expression( "^\\(\\s*" // opening bracket at start of string - "(.*)" // matches what is in between i.e. striped descriptor - "\\s*\\)$" // bracket at end of string - ); - std::smatch what; - std::regex_search( descriptor, what, expression ); - descriptor = what.str( 1 ); - pruneDescriptor( descriptor ); - } + /// get the number of arrows in the descriptor of given type + inline unsigned int numberOfArrowsOfParticularType( std::string_view descriptor, Decay::Arrow arrow ) { + // counts the number of arrow of a given type in a descriptor + unsigned int count = 0; + std::string::size_type pos = 0; + auto target = toString( arrow ); + while ( ( pos = descriptor.find( target, pos ) ) != descriptor.npos ) { + ++count; + pos += target.length(); + } + return count; + }; - return isMarked; - } + /// Check if descriptor has arrows of any type + bool hasArrow( const std::string& descriptor ) { + return std::any_of( Decay::ValidArrows.begin(), Decay::ValidArrows.end(), [&descriptor]( Decay::Arrow arrow ) { + return numberOfArrowsOfParticularType( descriptor, arrow ) != 0; + } ); + }; - /// get the number of arrows in the descriptor of given type - inline unsigned int numberOfArrowsOfParticularType( std::string_view descriptor, Decay::Arrow arrow ) { - // counts the number of arrow of a given type in a descriptor - unsigned int count = 0; - std::string::size_type pos = 0; - auto target = toString( arrow ); - while ( ( pos = descriptor.find( target, pos ) ) != descriptor.npos ) { - ++count; - pos += target.length(); + vec_str_t getDaughtersFromString( const std::string& daug_str ) { // function used for + // descriptor parsing + // separates the daughter string into the individual daughter particles returning a vector of daughters + vec_str_t vec_daug; + // regex expression that matches whitespaces conditionally, see below + boost::regex expression( + "(?(2->3 4) gives 2->3 4 + vec_str_t findStringWithinOuterMostBrackets( const std::string& daug_str ) { // function used for + // descriptor parsing + vec_str_t outer_vec_daug; + // regex expression that matches the string in the outermost brackets + // TODO: check the documentation here is okay + boost::regex expression( + "(" // "(...)" capturing group for + // "b(?:m|(?R))*e" + // matches balanced patterns or nested patterns where "b" is the beginning + // of the patterns, "e" the end and "m" what can occur in the middle + + "(?:\\^\\s*\\(|\\()" // "b" = "(?:\\^\\s*\\(|\\()" + // beginning of the pattern is either a hat "\\^" + // followed by any number of whitespaces "\\s*" and a opening bracket + // "\\(" OR: ("|"), an single opening bracket "\\(" + + "(?:[^()]+|(?R))*" // "m" = "[^()]" + // character set that matches a single character not in the set + // the negation is indicated by the "^" inside the character set + // recursion gets incremented if a character from the set in encountered + // "*" means that pattern will repeat zero or more consecutive times + + "\\)" // "e" = "\\)" + // the pattern ends with a closing bracket "\\)" + + ")" // end of the capturing group + ); + /* simpler recursion example: "a(?R)*z" that can match the string aaazzz to understand how recursion works. + First "a" gets matched, then the engine reaches "(?R)" which tells it to repeat the regex (ie. look for a again) + at the current position in the string now the second "a" is matched at recursion level = 1 then engine reaches + "(?R)" again (increments recursion) now the third "a" is matched at recursion level = 2. When the regex repeats, + the next character in the string is "z", so match for "a" fails at recursion level = 2 which causes the "(?R)" to + fail but from the "*" character, "(?R)" is made optional so engine continues to the end of the expression and + matches the first "z". Now engine has reached the end of the regex but since it is two level deep in recursion, it + hasn't found an overall match. It exits the recursion after this successful match and matches the second "z". It + is still one level deep from which it exits after another successful match and founds the third "z". The engine in + now at the end of the regex and at level zero of recursion this time so it returns aaazzz + + ref: https://www.regular-expressions.info/recurse.html + */ + boost::sregex_token_iterator iter( daug_str.begin(), daug_str.end(), expression, + 0 ); // selects the sequence in the string that is matched + // by the regex expression + boost::sregex_token_iterator end; // end-of-reg-exp marker + for ( ; iter != end; ++iter ) { + if ( !detail::hasArrow( *iter ) ) // only stores decays + continue; - /// find string within outermost brackets e.g 1->(2->3 4) gives 2->3 4 - vec_str_t findStringWithinOuterMostBrackets( const std::string& daug_str ) { // function used for - // descriptor parsing - vec_str_t outer_vec_daug; - // regex expression that matches the string in the outermost brackets - // TODO: check the documentation here is okay - boost::regex expression( "(" // "(...)" capturing group for - // "b(?:m|(?R))*e" - // matches balanced patterns or nested patterns where "b" is the beginning - // of the patterns, "e" the end and "m" what can occur in the middle - - "(?:\\^\\s*\\(|\\()" // "b" = "(?:\\^\\s*\\(|\\()" - // beginning of the pattern is either a hat "\\^" - // followed by any number of whitespaces "\\s*" and a opening bracket - // "\\(" OR: ("|"), an single opening bracket "\\(" - - "(?:[^()]+|(?R))*" // "m" = "[^()]" - // character set that matches a single character not in the set - // the negation is indicated by the "^" inside the character set - // recursion gets incremented if a character from the set in encountered - // "*" means that pattern will repeat zero or more consecutive times - - "\\)" // "e" = "\\)" - // the pattern ends with a closing bracket "\\)" - - ")" // end of the capturing group - ); - /* simpler recursion example: "a(?R)*z" that can match the string aaazzz to understand how recursion works. - First "a" gets matched, then the engine reaches "(?R)" which tells it to repeat the regex (ie. look for a again) at - the current position in the string now the second "a" is matched at recursion level = 1 then engine reaches "(?R)" - again (increments recursion) now the third "a" is matched at recursion level = 2. When the regex repeats, the next - character in the string is "z", so match for "a" fails at recursion level = 2 which causes the "(?R)" to fail but - from the "*" character, "(?R)" is made optional so engine continues to the end of the expression and matches the - first "z". Now engine has reached the end of the regex but since it is two level deep in recursion, it hasn't found - an overall match. It exits the recursion after this successful match and matches the second "z". It is still one - level deep from which it exits after another successful match and founds the third "z". The engine in now at the end - of the regex and at level zero of recursion this time so it returns aaazzz - - ref: https://www.regular-expressions.info/recurse.html - */ - boost::sregex_token_iterator iter( daug_str.begin(), daug_str.end(), expression, - 0 ); // selects the sequence in the string that is matched - // by the regex expression - boost::sregex_token_iterator end; // end-of-reg-exp marker - for ( ; iter != end; ++iter ) { - if ( !detail::hasArrow( *iter ) ) // only stores decays - continue; - - outer_vec_daug.emplace_back( *iter ); - } - // replace the strings with empty strings so that only daughters with no decay trees are left ie simple nodes - std::string daug_str_new{ daug_str }; - for ( const auto& daug : outer_vec_daug ) { - std::string::size_type pos = daug_str_new.find( daug ); - daug_str_new.replace( pos, daug.length(), "" ); + outer_vec_daug.emplace_back( *iter ); + } + // replace the strings with empty strings so that only daughters with no decay trees are left ie simple nodes + std::string daug_str_new{ daug_str }; + for ( const auto& daug : outer_vec_daug ) { + std::string::size_type pos = daug_str_new.find( daug ); + daug_str_new.replace( pos, daug.length(), "" ); + } + detail::pruneDescriptor( daug_str_new ); + // create a new expression and iterator to separate daughters from a string of daughters + boost::regex n_expression( "(?(Jpsi => mu+ mu-) phi where "first arrow" is - // "->" the properties are (Instance of Arrow, number of arrows of first arrow type, position of first arrow, number - // of different arrow types in descriptor) - arrowprops_t getFirstArrowProps( const std::string& descriptor ) { - // instantiate a default first arrow - auto first_arrow = Decay::Arrow::Unknown; - // count number of first arrow type in descriptor - unsigned int n_first_arrow{ 0 }; - // instantiate a default position - std::size_t first_arrow_pos{ std::string::npos }; - // count the number of arrowtypes in string - unsigned int n_arrowtypes{ 0 }; - for ( const auto arrow : Decay::ValidArrows ) { // loop through all arrow types - if ( descriptor.find( toString( arrow ) ) != descriptor.npos ) { // found the arrow - if ( n_arrowtypes == 0 ) { // first iteration - // store arrow, number of its occurrences and position - first_arrow = arrow; - n_first_arrow = numberOfArrowsOfParticularType( descriptor, arrow ); - first_arrow_pos = descriptor.find( toString( arrow ) ); - } else { // second type of arrow found - if ( descriptor.find( toString( arrow ) ) < first_arrow_pos ) { // if the position is smaller than the - // previous one + // get the properties of first type of arrow in the descriptor e.g. B->(Jpsi => mu+ mu-) phi where "first arrow" is + // "->" the properties are (Instance of Arrow, number of arrows of first arrow type, position of first arrow, number + // of different arrow types in descriptor) + arrowprops_t getFirstArrowProps( const std::string& descriptor ) { + // instantiate a default first arrow + auto first_arrow = Decay::Arrow::Unknown; + // count number of first arrow type in descriptor + unsigned int n_first_arrow{ 0 }; + // instantiate a default position + std::size_t first_arrow_pos{ std::string::npos }; + // count the number of arrowtypes in string + unsigned int n_arrowtypes{ 0 }; + for ( const auto arrow : Decay::ValidArrows ) { // loop through all arrow types + if ( descriptor.find( toString( arrow ) ) != descriptor.npos ) { // found the arrow + if ( n_arrowtypes == 0 ) { // first iteration // store arrow, number of its occurrences and position first_arrow = arrow; n_first_arrow = numberOfArrowsOfParticularType( descriptor, arrow ); first_arrow_pos = descriptor.find( toString( arrow ) ); + } else { // second type of arrow found + if ( descriptor.find( toString( arrow ) ) < first_arrow_pos ) { // if the position is smaller than the + // previous one + // store arrow, number of its occurrences and position + first_arrow = arrow; + n_first_arrow = numberOfArrowsOfParticularType( descriptor, arrow ); + first_arrow_pos = descriptor.find( toString( arrow ) ); + } } + n_arrowtypes++; } - n_arrowtypes++; } + return arrowprops_t{ first_arrow, n_first_arrow, first_arrow_pos, n_arrowtypes }; } - return arrowprops_t{ first_arrow, n_first_arrow, first_arrow_pos, n_arrowtypes }; - } - /// split the descriptor into head node and daughters string according to the first arrow and first arrow length - tuple_parentdaughter_t getParentDaughterString( const std::string& descriptor, const std::size_t& firstArrowPos, - const std::size_t& firstArrowLength ) { - auto parent = descriptor.substr( 0, firstArrowPos ); - auto daugs = descriptor.substr( firstArrowPos + firstArrowLength ); - pruneDescriptor( parent ); - pruneDescriptor( daugs ); - return std::tuple( std::move( parent ), std::move( daugs ) ); - }; - - /// A helper function to make a tuple of class properties (used in constructor of decaytree) - classprops_t makeClassProps( std::string& descriptor ) { - // set hasCC and isMarked - auto isMarked = stripHatFromDescriptor( descriptor ); - auto hasCC = stripCCFromDescriptor( descriptor ); - - // get the properties of first type of arrow in the descriptor e.g. B->(Jpsi => mu+ mu-) (phi -> K+ K-) where "first - // arrow" is "->" the properties are (Instance of First Arrow, number of arrows of first arrow type, position of - // first arrow, number of different arrow types in descriptor) - const auto [first_arrow, n_first_arrow, first_arrow_pos, n_arrowtypes] = getFirstArrowProps( descriptor ); - bool hasOneArrow = ( n_first_arrow == 1 && n_arrowtypes == 1 ); + /// split the descriptor into head node and daughters string according to the first arrow and first arrow length + tuple_parentdaughter_t getParentDaughterString( const std::string& descriptor, const std::size_t& firstArrowPos, + const std::size_t& firstArrowLength ) { + auto parent = descriptor.substr( 0, firstArrowPos ); + auto daugs = descriptor.substr( firstArrowPos + firstArrowLength ); + pruneDescriptor( parent ); + pruneDescriptor( daugs ); + return std::tuple( std::move( parent ), std::move( daugs ) ); + }; - // set head node - // get the properties of first type of arrow in the descriptor e.g. B->(Jpsi => mu+ mu-) phi where "first arrow" is - // "->" - const auto& [parent_str, daugs_str] = - getParentDaughterString( descriptor, first_arrow_pos, toString( first_arrow ).size() ); - Decay::Node headNode( parent_str ); - - // set daughters - // Get daughter string according to number of arrows - // - Allow for simple case with one arrow e.g. B -> K+ K-. Note the descriptor - // "[B->K+K-]" would fail but "(B->K+K-)" would pass. TODO: allow for both?. - // - Also allow for complex case e.g. B -> (Jpsi -> mu+ mu-) (phi(1020) -> K+ K-). - vec_str_t vec_daug = - hasOneArrow ? getDaughtersFromString( daugs_str ) : findStringWithinOuterMostBrackets( daugs_str ); - vec_dt_t daughters{ vec_daug.begin(), vec_daug.end() }; // make vector of daughters - - return { headNode, first_arrow, daughters, hasCC, isMarked }; - }; + /// A helper function to make a tuple of class properties (used in constructor of decaytree) + classprops_t makeClassProps( std::string& descriptor ) { + // set hasCC and isMarked + auto isMarked = stripHatFromDescriptor( descriptor ); + auto hasCC = stripCCFromDescriptor( descriptor ); + + // get the properties of first type of arrow in the descriptor e.g. B->(Jpsi => mu+ mu-) (phi -> K+ K-) where + // "first arrow" is "->" the properties are (Instance of First Arrow, number of arrows of first arrow type, + // position of first arrow, number of different arrow types in descriptor) + const auto [first_arrow, n_first_arrow, first_arrow_pos, n_arrowtypes] = getFirstArrowProps( descriptor ); + bool hasOneArrow = ( n_first_arrow == 1 && n_arrowtypes == 1 ); + + // set head node + // get the properties of first type of arrow in the descriptor e.g. B->(Jpsi => mu+ mu-) phi where "first arrow" + // is + // "->" + const auto& [parent_str, daugs_str] = + getParentDaughterString( descriptor, first_arrow_pos, toString( first_arrow ).size() ); + Decay::Node headNode( parent_str ); + + // set daughters + // Get daughter string according to number of arrows + // - Allow for simple case with one arrow e.g. B -> K+ K-. Note the descriptor + // "[B->K+K-]" would fail but "(B->K+K-)" would pass. TODO: allow for both?. + // - Also allow for complex case e.g. B -> (Jpsi -> mu+ mu-) (phi(1020) -> K+ K-). + vec_str_t vec_daug = + hasOneArrow ? getDaughtersFromString( daugs_str ) : findStringWithinOuterMostBrackets( daugs_str ); + vec_dt_t daughters{ vec_daug.begin(), vec_daug.end() }; // make vector of daughters + + return { headNode, first_arrow, daughters, hasCC, isMarked }; + }; - /// Master function to build decay tree setting member variables with descriptor (invoked in constructor) - classprops_t parseDescriptor( std::string descriptor ) { - // Check if contains balanced brackets. The brackets could be "(" or "[" or "{". - if ( !detail::hasBalancedBrackets( descriptor ) ) { - throw GaudiException{ "Descriptor does not have balanced brackets. Please check the descriptor!", - "Decay::Node::parseDescriptor(descriptor)", StatusCode::FAILURE }; - } - if ( !detail::hasArrow( descriptor ) ) { // Case1: check if no arrow in descriptor i.e. node only - Decay::Node headNode( descriptor ); // construct the head node - auto arrow_in_descriptor = Decay::Arrow::Unknown; // Arrow::Unknown - detail::vec_dt_t daughters{}; // no daughters - bool hasCC{ false }, isMarked{ false }; // properties handled at the node level - return { headNode, arrow_in_descriptor, daughters, hasCC, isMarked }; + /// Master function to build decay tree setting member variables with descriptor (invoked in constructor) + classprops_t parseDescriptor( std::string descriptor ) { + // Check if contains balanced brackets. The brackets could be "(" or "[" or "{". + if ( !detail::hasBalancedBrackets( descriptor ) ) { + throw GaudiException{ "Descriptor does not have balanced brackets. Please check the descriptor!", + "Decay::Node::parseDescriptor(descriptor)", StatusCode::FAILURE }; + } + if ( !detail::hasArrow( descriptor ) ) { // Case1: check if no arrow in descriptor i.e. node only + Decay::Node headNode( descriptor ); // construct the head node + auto arrow_in_descriptor = Decay::Arrow::Unknown; // Arrow::Unknown + detail::vec_dt_t daughters{}; // no daughters + bool hasCC{ false }, isMarked{ false }; // properties handled at the node level + return { headNode, arrow_in_descriptor, daughters, hasCC, isMarked }; + } + // Build decay tree according how many arrows we have (calling parseDescriptor recursively) + return makeClassProps( descriptor ); } - // Build decay tree according how many arrows we have (calling parseDescriptor recursively) - return makeClassProps( descriptor ); - } -} // namespace detail -#endif // TREE_H + } // namespace detail +}; // namespace Decay diff --git a/Phys/DaVinciKernel/src/component/DecayFinder.cpp b/Phys/DaVinciKernel/src/component/DecayFinder.cpp index 335693cadade5b5a407e5b1ac276696b65c28f6a..cbe5e61e7f29a3a67c4beb02b75d41ccc207966b 100644 --- a/Phys/DaVinciKernel/src/component/DecayFinder.cpp +++ b/Phys/DaVinciKernel/src/component/DecayFinder.cpp @@ -124,13 +124,12 @@ namespace Decay { StatusCode::FAILURE ); // construct a reference tree from the reconstructed particle (for debugging) - auto part_ptr = std::make_shared( particle ); - this->debug() << "Particle decay tree: " << Tree{ part_ptr } << endmsg; + this->debug() << "Particle decay tree: " << Tree{ *particle } << endmsg; // loop through the decay possibilities and check if there is match // between the reconstructed particle and user possibility for ( const auto& possibility : possibilities ) { this->debug() << "Looping through the possibilities: " << possibility << endmsg; - if ( possibility == particle ) { // operator== internally creates a copy of the possibility + if ( possibility == *particle ) { // operator== internally creates a copy of the possibility // print some info if verbosity is set this->debug() << "Particle tree matches descriptor: " << possibility << endmsg; this->debug() << "Size of output_parts before matching: " << output_parts.size() << endmsg;