diff --git a/Phys/JetAccessories/CMakeLists.txt b/Phys/JetAccessories/CMakeLists.txt index a789d9cfe8b4903eae9db28428c88b74e61b52d1..439084d558765ac8afc9b3c1280b6f53f9648fe7 100644 --- a/Phys/JetAccessories/CMakeLists.txt +++ b/Phys/JetAccessories/CMakeLists.txt @@ -27,11 +27,11 @@ gaudi_add_module(JetAccessories SOURCES src/ClearDaughters.cpp src/HltJetBuilder.cpp - src/HltJetBuilderRun3.cpp - src/HltJetTag.cpp + src/FastJetBuilder.cpp + src/JetTag.cpp src/HltParticleFlow.cpp - src/HltParticleFlowFilter.cpp - src/HltParticleFlowRun3.cpp + src/ParticleFlowFilter.cpp + src/ParticleFlowMaker.cpp src/PFJetMakerAlg.cpp src/PFParticle.cpp src/ParticleFlow.cpp @@ -42,12 +42,14 @@ gaudi_add_module(JetAccessories LINK AIDA::aida Boost::headers + FastJet::FastJet Gaudi::GaudiAlgLib Gaudi::GaudiKernel Gaudi::GaudiUtilsLib JetAccessoriesLib LHCb::CaloDetLib LHCb::CaloUtils + LHCb::DetDescLib LHCb::DigiEvent LHCb::LHCbKernel LHCb::LHCbMathLib diff --git a/Phys/JetAccessories/src/HltJetBuilderRun3.cpp b/Phys/JetAccessories/src/FastJetBuilder.cpp similarity index 51% rename from Phys/JetAccessories/src/HltJetBuilderRun3.cpp rename to Phys/JetAccessories/src/FastJetBuilder.cpp index 81ce99a14582fe331bc90963cccfb729548e771f..c7285b2aa4e0234f476d93817094a1959349d21a 100644 --- a/Phys/JetAccessories/src/HltJetBuilderRun3.cpp +++ b/Phys/JetAccessories/src/FastJetBuilder.cpp @@ -10,14 +10,20 @@ \*****************************************************************************/ // STL. +#include #include #include +// Gaudi #include "GaudiAlg/GaudiTool.h" #include "GaudiAlg/Transformer.h" +// LHCb +#include "DetDesc/DetectorElement.h" +#include "DetDesc/GenericConditionAccessorHolder.h" + // Jets. -#include "Kernel/IJetMaker.h" +#include "Kernel/IParticleCombiner.h" #include "Kernel/JetEnums.h" // Event. @@ -39,6 +45,9 @@ #include "Kernel/Particle2Vertex.h" #include "Relations/Relation1D.h" +#include "fastjet/JetDefinition.hh" +#include "fastjet/PseudoJet.hh" + using namespace std; using namespace LHCb; @@ -69,14 +78,32 @@ TODO: Add a JetID filter , probably as a separate transformer. */ using P2PVTable = unique_ptr>; +// the actual type of input data container +using InputJ = LHCb::Particle::ConstVector; +// FastJet Jet type +using PJet = fastjet::PseudoJet; + +namespace { + int to_user_index( const int index ) { return index + 10000; } + int from_user_index( const int index ) { return index - 10000; } + PJet createJetInput( const LHCb::Particle& p, const int index ) { + // trivial function which "converts" LHCb particle into the fastjet particle + const Gaudi::LorentzVector& v = p.momentum(); + auto jetinput = PJet{v.Px(), v.Py(), v.Pz(), v.E()}; + jetinput.set_user_index( index ); + return jetinput; + } +} // namespace -class HltJetBuilderRun3 - : public Gaudi::Functional::Transformer { +class FastJetBuilder + : public Gaudi::Functional::Transformer> { public: - HltJetBuilderRun3( const std::string& name, ISvcLocator* svc ); // Constructor - // StatusCode initialize() override; // Initialize - LHCb::Particles operator()( const LHCb::Particles&, const LHCb::RecVertices& ) const override; // Main method + FastJetBuilder( const std::string& name, ISvcLocator* svc ); // Constructor + LHCb::Particles operator()( const LHCb::Particles&, const LHCb::RecVertices&, + const DetectorElement& ) const override; // Main method private: mutable Gaudi::Accumulators::SummingCounter<> m_nbInput{this, "Nb of Inputs"}; @@ -103,34 +130,101 @@ private: std::vector m_inBans; ///< Input locations of Particles to ban. std::vector m_inTags; ///< Input locations of Particle to tag. - /// Tool to FastJet - ToolHandle m_fj = {this, "FastJet", "LoKi::FastJetMaker"}; + // Below: FastJet related + + // FastJet configuration + enum m_sort { m_sorted_by_rapidity = 3, m_sorted_by_pt = 2, m_sorted_by_E = 1 }; + Gaudi::Property m_sort = {this, "Sort", 2, "FastJet: sorting criteria"}; + Gaudi::Property m_jetID = {this, "JetID", 98, "LHCb PID number for jets"}; + Gaudi::Property m_r = {this, "RParameter", 0.5, "FastJet: cone size"}; + Gaudi::Property m_ptmin = {this, "PtMin", 5000, + [=]( auto& ) { + if ( m_ptmin.value() < 0 ) + throw std::runtime_error{"min pt for FastJet is negative for FastJetBuilder"}; + }, + "FastJet: min pT"}; + + Gaudi::Property m_recom = {this, "Recombination", 0, + [=]( auto& ) { + constexpr auto valid = + std::array{fastjet::E_scheme, fastjet::pt_scheme, fastjet::pt2_scheme, + fastjet::Et_scheme, fastjet::Et2_scheme, fastjet::BIpt_scheme, + fastjet::BIpt2_scheme}; + if ( std::find( valid.begin(), valid.end(), m_strat.value() ) == valid.end() ) + throw std::runtime_error{"Invalid RecombinationScheme for FastJetBuilder"}; + }, + "FastJet: scheme : 0 E, 1 pT, 2 pT^2, ..."}; + + Gaudi::Property m_strat = {this, "Strategy", 1, + [=]( auto& ) { + constexpr auto valid = + std::array{fastjet::N2MinHeapTiled, fastjet::N2Tiled, fastjet::N2PoorTiled, + fastjet::N2Plain, fastjet::N3Dumb, fastjet::Best, + fastjet::NlnN, fastjet::NlnN3pi, fastjet::NlnN4pi, + fastjet::NlnNCam4pi, fastjet::NlnNCam2pi2R, fastjet::NlnNCam, + fastjet::plugin_strategy}; + if ( std::find( valid.begin(), valid.end(), m_strat.value() ) == valid.end() ) + throw std::runtime_error{"Invalid Strategy for FastJetBuilder"}; + }, + "FastJet: strategy: 0 N3Dumb, 1 Best, 2 NlnN, ...,"}; + Gaudi::Property m_type = {this, "Type", 2, + [=]( auto& ) { + constexpr auto valid = std::array{fastjet::kt_algorithm, + fastjet::cambridge_algorithm, + fastjet::genkt_algorithm, + fastjet::cambridge_for_passive_algorithm, + fastjet::genkt_for_passive_algorithm, + fastjet::plugin_algorithm}; + if ( std::find( valid.begin(), valid.end(), m_strat.value() ) == valid.end() ) + throw std::runtime_error{"Invalid JetFinder-algorithm for FastJetBuilder"}; + }, + "FastJet: anti-kt code: 0 kt, 1 cambridge, 2 anti-kt, ..."}; + + // Combiner to be used + ToolHandle m_combiner{this, "ParticleCombiner", "MomentumCombiner"}; + + // run fastjet + StatusCode makeFastJets( const std::vector Particles, LHCb::Particle::Vector& recojets, + const DetectorElement& geometry ) const; /// MsgSvc counters mutable Gaudi::Accumulators::MsgCounter m_msg_noBestPV{this, "Failed to relate a particle with its bestPV."}; mutable Gaudi::Accumulators::MsgCounter m_msg_FJfailed{this, "FastJet failed."}; + mutable Gaudi::Accumulators::MsgCounter m_msg_invalid_input{this, + "Invalid input particle -> FastJet input"}; + mutable Gaudi::Accumulators::MsgCounter m_msg_nojetdaughters{this, "Jets have no daughters"}; + mutable Gaudi::Accumulators::MsgCounter m_msg_invalidjetdaughter{this, "Invalid jet daughter index"}; + mutable Gaudi::Accumulators::MsgCounter m_msg_momcomb{this, + "Error in momentum combiner with jet daughters"}; + + // prepare fastjet definition + std::pair> + prepare( const std::vector Particles ) const; }; /*----------------------------------------------------------------------------- -Implementation file for class : HltJetBuilderRun3 +Implementation file for class : FastJetBuilder -----------------------------------------------------------------------------*/ // Declare the algorithm factory. -DECLARE_COMPONENT( HltJetBuilderRun3 ) +DECLARE_COMPONENT( FastJetBuilder ) //============================================================================= // Constructor. //============================================================================= -HltJetBuilderRun3::HltJetBuilderRun3( const std::string& name, ISvcLocator* svc ) - : Transformer( name, svc, {KeyValue{"Input", ""}, KeyValue{"PVLocation", "Rec/Vertex/Primary"}}, +FastJetBuilder::FastJetBuilder( const std::string& name, ISvcLocator* svc ) + : Transformer( name, svc, + {KeyValue{"Input", ""}, KeyValue{"PVLocation", "Rec/Vertex/Primary"}, + KeyValue{"StandardGeometryTop", "/dd/Structure/LHCb"}}, KeyValue{"Output", "Phys/Jets/Particles"} ) {} //============================================================================= // Execute. //============================================================================= -LHCb::Particles HltJetBuilderRun3::operator()( const LHCb::Particles& Particles, - const LHCb::RecVertices& PrimaryVertices ) const { +LHCb::Particles FastJetBuilder::operator()( const LHCb::Particles& Particles, const LHCb::RecVertices& PrimaryVertices, + const DetectorElement& geometry ) const { + m_nbInput += Particles.size(); auto c10 = m_nbJetCounter10.buffer(); auto c20 = m_nbJetCounter20.buffer(); @@ -162,8 +256,7 @@ LHCb::Particles HltJetBuilderRun3::operator()( const LHCb::Particles& Particle if ( vtx2prtRel ) { if ( msgLevel( MSG::DEBUG ) ) debug() << "Making jets by PVs" << endmsg; for ( auto vtx : PrimaryVertices ) { - auto tmppartRange = - vtx2prtRel->relations( vtx ); // LHCb::Relation1D::Range + auto tmppartRange = vtx2prtRel->relations( vtx ); std::vector tmppart; tmppart.reserve( tmppartRange.size() + tmpneutrals.size() ); for ( const auto prt : tmppartRange ) tmppart.push_back( prt.to() ); @@ -173,7 +266,7 @@ LHCb::Particles HltJetBuilderRun3::operator()( const LHCb::Particles& Particle // Make the jets std::vector tmprecojets; tmprecojets.reserve( 40 ); - if ( m_fj->makeJets( tmppart.begin(), tmppart.end(), tmprecojets ).isFailure() ) ++m_msg_FJfailed; + if ( makeFastJets( tmppart, tmprecojets, geometry ).isFailure() ) ++m_msg_FJfailed; if ( msgLevel( MSG::DEBUG ) ) debug() << "Vtx: " << vtx << " #jets: " << tmprecojets.size() << endmsg; recojets.reserve( tmprecojets.size() ); @@ -189,20 +282,20 @@ LHCb::Particles HltJetBuilderRun3::operator()( const LHCb::Particles& Particle } else { // Make jets inclusively if ( msgLevel( MSG::DEBUG ) ) debug() << "Making jets inclusively" << endmsg; - std::vector tmppart{Particles.begin(), Particles.end()}; + std::vector tmppart{Particles.begin(), Particles.end()}; // Make the jets std::vector tmprecojets; tmprecojets.reserve( 40 ); - if ( m_fj->makeJets( tmppart.begin(), tmppart.end(), tmprecojets ).isFailure() ) ++m_msg_FJfailed; + if ( makeFastJets( tmppart, tmprecojets, geometry ).isFailure() ) ++m_msg_FJfailed; recojets.reserve( tmprecojets.size() ); for ( auto* jet : tmprecojets ) { - recojets.insert( jet ); auto vtx = m_prtVtxTool->relatedPV( jet, PrimaryVertices ); jet->addInfo( LHCb::JetIDInfo::vtx_x, vtx->position().X() ); jet->addInfo( LHCb::JetIDInfo::vtx_y, vtx->position().Y() ); jet->addInfo( LHCb::JetIDInfo::vtx_z, vtx->position().Z() ); + recojets.insert( jet ); } } @@ -232,7 +325,7 @@ LHCb::Particles HltJetBuilderRun3::operator()( const LHCb::Particles& Particle } // Add additional information. -void HltJetBuilderRun3::addInfo( Particle* prt ) const { +void FastJetBuilder::addInfo( Particle* prt ) const { double cpx( 0 ), cpy( 0 ), cptMax( 0 ), nptMax( 0 ), width( 0 ), norm( 0 ), trks( 0 ), pt( prt->momentum().Pt() ); Particle::ConstVector dtrs; LoKi::Extract::getParticles( prt, back_inserter( dtrs ), LoKi::Cuts::HASPROTO && LoKi::Cuts::ISBASIC ); @@ -259,8 +352,8 @@ void HltJetBuilderRun3::addInfo( Particle* prt ) const { } // Relate particles with bestPVs -P2PVTable HltJetBuilderRun3::relateP2Vtx( std::vector Particles, - const LHCb::RecVertices& PrimaryVertices ) const { +P2PVTable FastJetBuilder::relateP2Vtx( std::vector Particles, + const LHCb::RecVertices& PrimaryVertices ) const { if ( !m_prtVtxTool ) { return nullptr; } if ( PrimaryVertices.empty() ) { if ( msgLevel( MSG::DEBUG ) ) debug() << "No PrimaryVertices in this event" << endmsg; @@ -278,9 +371,108 @@ P2PVTable HltJetBuilderRun3::relateP2Vtx( std::vector Par if ( msgLevel( MSG::DEBUG ) ) debug() << "====> Particle: " << prt << " Particle ID: " << prt->particleID().pid() << " Best PV: " << bestPV << " Chi2/nDofF: " << bestPV->chi2PerDoF() << " PV position: " << bestPV->position() << endmsg; - // auto sc = vtx2prtRel->relate( dynamic_cast( bestPV ), prt ); auto sc = vtx2prtRel->relate( static_cast( bestPV ), prt ); if ( sc.isFailure() ) ++m_msg_noBestPV; } return vtx2prtRel; } + +// prepare FastJet +std::pair> +FastJetBuilder::prepare( const std::vector Particles ) const { + + fastjet::JetFinder finder = (fastjet::JetFinder)m_type.value(); + fastjet::RecombinationScheme scheme = (fastjet::RecombinationScheme)m_recom.value(); + fastjet::Strategy strategy = (fastjet::Strategy)m_strat.value(); + + auto ret = std::pair{fastjet::JetDefinition{finder, m_r, scheme, strategy}, std::vector{}}; + auto& [jet_def, jets] = ret; + + if ( msgLevel( MSG::DEBUG ) ) { + debug() << "fastjet::JetDefinition:" << endmsg; + debug() << jet_def.description() << endmsg; + } + + jets.reserve( Particles.size() ); + for ( const auto& [i, p] : LHCb::range::enumerate( Particles ) ) { + if ( !p ) { + ++m_msg_invalid_input; + continue; + } + jets.push_back( createJetInput( *p, to_user_index( i ) ) ); + } + + return ret; +} +// Run FastJet +StatusCode FastJetBuilder::makeFastJets( const std::vector Particles, + LHCb::Particle::Vector& recojets, const DetectorElement& geometry ) const { + + // prepare the input data and define the jets + auto [jetDef, jetinputs] = prepare( Particles ); + + if ( jetinputs.empty() ) { + recojets.reserve( 0 ); + if ( msgLevel( MSG::DEBUG ) ) { debug() << "No particle inputs to reconstruct jets" << endmsg; } + return StatusCode::SUCCESS; + } + + // Reconstructed Jets from FastJet + std::vector jets; + + auto clusters = fastjet::ClusterSequence( jetinputs, jetDef ); + + switch ( m_sort ) { + case FastJetBuilder::m_sorted_by_rapidity: + jets = fastjet::sorted_by_rapidity( clusters.inclusive_jets( m_ptmin ) ); + break; + case FastJetBuilder::m_sorted_by_pt: + jets = fastjet::sorted_by_pt( clusters.inclusive_jets( m_ptmin ) ); + break; + case FastJetBuilder::m_sorted_by_E: + jets = fastjet::sorted_by_E( clusters.inclusive_jets( m_ptmin ) ); + break; + default: + jets = fastjet::sorted_by_pt( clusters.inclusive_jets( m_ptmin ) ); + break; + } + + if ( msgLevel( MSG::DEBUG ) ) { debug() << "No jets from fastjet::ClusterSequence" << endmsg; } + + recojets.reserve( jets.size() ); + + for ( const PJet& jet : jets ) { + const std::vector& constituents = clusters.constituents( jet ); + + // jet daughter list to be used in the combiner + LHCb::Particle::ConstVector daughters; + + for ( const PJet& c : constituents ) { + // find the appropriate input particle + const int index = from_user_index( c.user_index() ); + if ( 0 > index || (int)jetinputs.size() <= index ) { + ++m_msg_invalidjetdaughter; + continue; + } + const LHCb::Particle* p = Particles[index]; + daughters.push_back( p ); + } + if ( daughters.empty() ) { + ++m_msg_nojetdaughters; + continue; + } + LHCb::Particle pJet; + LHCb::Vertex vJet; + auto sc = m_combiner->combine( daughters, pJet, vJet, geometry ); + pJet.setParticleID( LHCb::ParticleID( m_jetID ) ); + if ( sc.isFailure() ) { + ++m_msg_momcomb; + continue; + } + // redefine the momentum with the FastJet output + pJet.setMomentum( Gaudi::LorentzVector( jet.px(), jet.py(), jet.pz(), jet.e() ) ); + recojets.push_back( pJet.clone() ); + } + + return StatusCode::SUCCESS; +} diff --git a/Phys/JetAccessories/src/HltJetTag.cpp b/Phys/JetAccessories/src/JetTag.cpp similarity index 86% rename from Phys/JetAccessories/src/HltJetTag.cpp rename to Phys/JetAccessories/src/JetTag.cpp index 9a5214fbbae28c39bbcc1f8b4dfb09c79179ff2a..dd7f833b85cf954ce769d1d0c8ae019e0271a532 100644 --- a/Phys/JetAccessories/src/HltJetTag.cpp +++ b/Phys/JetAccessories/src/JetTag.cpp @@ -29,12 +29,12 @@ Receives lists of jets and return only the jets that are tagged by the list of t The tagging condition is DeltaR { +class JetTag : public Gaudi::Functional::Transformer { public: /// Constructor. - HltJetTag( const string& name, ISvcLocator* svc ); + JetTag( const string& name, ISvcLocator* svc ); // Main method LHCb::Particles operator()( const LHCb::Particles&, const LHCb::Particles&, const LHCb::RecVertices& ) const override; @@ -49,15 +49,15 @@ private: "Flag to use flight direction if tag is composite."}; }; -DECLARE_COMPONENT( HltJetTag ) +DECLARE_COMPONENT( JetTag ) /// Constructor. -HltJetTag::HltJetTag( const string& name, ISvcLocator* svc ) +JetTag::JetTag( const string& name, ISvcLocator* svc ) : Transformer( name, svc, {KeyValue{"Jets", ""}, KeyValue{"Tags", ""}, KeyValue{"PVLocation", ""}}, {"Output", ""} ) {} -LHCb::Particles HltJetTag::operator()( const LHCb::Particles& Jets, const LHCb::Particles& Tags, - const LHCb::RecVertices& PrimaryVertices ) const { +LHCb::Particles JetTag::operator()( const LHCb::Particles& Jets, const LHCb::Particles& Tags, + const LHCb::RecVertices& PrimaryVertices ) const { static int count = 0; ++count; diff --git a/Phys/JetAccessories/src/HltParticleFlowFilter.cpp b/Phys/JetAccessories/src/ParticleFlowFilter.cpp similarity index 87% rename from Phys/JetAccessories/src/HltParticleFlowFilter.cpp rename to Phys/JetAccessories/src/ParticleFlowFilter.cpp index 70dc81c0e2df9b3fc2a1128b75bcf44151d67148..9358adb04144b01655f81dcc112b16daceb3e2f5 100644 --- a/Phys/JetAccessories/src/HltParticleFlowFilter.cpp +++ b/Phys/JetAccessories/src/ParticleFlowFilter.cpp @@ -27,12 +27,12 @@ Receives two lists of particles: listA and listB. Removes any particle from list */ -class HltParticleFlowFilter +class ParticleFlowFilter : public Gaudi::Functional::Transformer { public: /// Constructor. - HltParticleFlowFilter( const string& name, ISvcLocator* svc ); + ParticleFlowFilter( const string& name, ISvcLocator* svc ); // Main method LHCb::Particles operator()( const LHCb::Particles&, const LHCb::Particles& ) const override; @@ -40,15 +40,15 @@ public: private: }; -DECLARE_COMPONENT( HltParticleFlowFilter ) +DECLARE_COMPONENT( ParticleFlowFilter ) /// Constructor. -HltParticleFlowFilter::HltParticleFlowFilter( const string& name, ISvcLocator* svc ) +ParticleFlowFilter::ParticleFlowFilter( const string& name, ISvcLocator* svc ) : Transformer( name, svc, {KeyValue{"Inputs", ""}, KeyValue{"ParticlesToBan", ""}}, {"Output", "Phys/ParticleFlow/Particles"} ) {} -LHCb::Particles HltParticleFlowFilter::operator()( const LHCb::Particles& Inputs, - const LHCb::Particles& InputsToBan ) const { +LHCb::Particles ParticleFlowFilter::operator()( const LHCb::Particles& Inputs, + const LHCb::Particles& InputsToBan ) const { static int count = 0; ++count; diff --git a/Phys/JetAccessories/src/HltParticleFlowRun3.cpp b/Phys/JetAccessories/src/ParticleFlowMaker.cpp similarity index 96% rename from Phys/JetAccessories/src/HltParticleFlowRun3.cpp rename to Phys/JetAccessories/src/ParticleFlowMaker.cpp index 48c753d11f21e8979d163b602676234e3e7685a7..edff904c5d6bdf704d0a9d78dfbca1bb0f8bb6a8 100644 --- a/Phys/JetAccessories/src/HltParticleFlowRun3.cpp +++ b/Phys/JetAccessories/src/ParticleFlowMaker.cpp @@ -44,11 +44,11 @@ We plan to include LoKi::CheckOverlap for next version. using out_t = LHCb::Particles; using inputs = const Gaudi::Functional::vector_of_const_; -class HltParticleFlowRun3 : public Gaudi::Functional::MergingTransformer { +class ParticleFlowMaker : public Gaudi::Functional::MergingTransformer { public: /// Constructor. - HltParticleFlowRun3( const string& name, ISvcLocator* svc ); + ParticleFlowMaker( const string& name, ISvcLocator* svc ); // Main method LHCb::Particles operator()( inputs const& Inputs ) const override; @@ -70,13 +70,13 @@ private: this, "09: # ProtoParticles from Composite Particles rejected"}; }; -DECLARE_COMPONENT( HltParticleFlowRun3 ) +DECLARE_COMPONENT( ParticleFlowMaker ) /// Constructor. -HltParticleFlowRun3::HltParticleFlowRun3( const string& name, ISvcLocator* svc ) +ParticleFlowMaker::ParticleFlowMaker( const string& name, ISvcLocator* svc ) : MergingTransformer( name, svc, {"Inputs", {}}, {"Output", "Phys/ParticleFlow/Particles"} ) {} -LHCb::Particles HltParticleFlowRun3::operator()( inputs const& Inputs ) const { +LHCb::Particles ParticleFlowMaker::operator()( inputs const& Inputs ) const { ++m_count;