diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/CMakeLists.txt b/Trigger/TrigAnalysis/TriggerMatchingTool/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..701bd630e43f4e3af3f21f37294bd58babfb9a33 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/CMakeLists.txt @@ -0,0 +1,42 @@ +################################################################################ +# Package: TriggerMatchingTool +################################################################################ + +# Declare the package name: +atlas_subdir( TriggerMatchingTool ) + +# Declare the package's dependencies: +atlas_depends_on_subdirs( PUBLIC + Control/AthToolSupport/AsgTools + Event/xAOD/xAODBase + GaudiKernel + Trigger/TrigAnalysis/TrigDecisionTool + Trigger/TrigEvent/TrigNavStructure + PRIVATE + Control/AthAnalysisBaseComps + Event/FourMomUtils ) + +# External dependencies: +find_package( Boost COMPONENTS filesystem thread system ) +find_package( ROOT COMPONENTS Core Tree MathCore Hist RIO pthread ) + +# Component(s) in the package: +atlas_add_library( TriggerMatchingToolLib + Root/*.cxx + src/*.cxx + PUBLIC_HEADERS TriggerMatchingTool + PRIVATE_INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES AsgTools xAODBase GaudiKernel TrigNavStructure TrigDecisionToolLib + PRIVATE_LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} AthAnalysisBaseComps FourMomUtils ) + +atlas_add_component( TriggerMatchingTool + src/components/*.cxx + INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} AsgTools xAODBase GaudiKernel TrigDecisionToolLib TrigNavStructure AthAnalysisBaseComps FourMomUtils TriggerMatchingToolLib ) + +atlas_add_dictionary( TriggerMatchingToolDict + TriggerMatchingTool/TriggerMatchingToolDict.h + TriggerMatchingTool/selection.xml + INCLUDE_DIRS ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${Boost_LIBRARIES} ${ROOT_LIBRARIES} AsgTools xAODBase GaudiKernel TrigDecisionToolLib TrigNavStructure AthAnalysisBaseComps FourMomUtils TriggerMatchingToolLib ) + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/LinkDef.h b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/LinkDef.h new file mode 100644 index 0000000000000000000000000000000000000000..f9fa452558b6689ef1035f603096f64ab7ab8510 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/LinkDef.h @@ -0,0 +1,12 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifdef __CINT__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; +#pragma link C++ nestedclass; + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingImplementation.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingImplementation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..370766276d328f08484c58cfed85c09991cf45f3 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingImplementation.cxx @@ -0,0 +1,58 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TriggerMatchingTool/MatchingImplementation.h" +#include "TriggerMatchingTool/MatchingTool.h" +#include "MinimalSumAssociation.h" + +namespace Trig { + +MatchingImplementation::MatchingImplementation(MatchingTool& mt, double threshold) : asg::AsgMessaging("MatchingImplementation"), m_tool(mt), m_threshold(threshold) { + msg().setLevel(MSG::DEBUG); + m_strategies[Trig::MatchingStrategy::MinimalSum] = std::unique_ptr<IAssociationStrategy>(new MinimalSumAssociation()); +} + +Trig::TrigDecisionTool* MatchingImplementation::tdt(){ + return m_tool.m_trigDecTool.operator->(); +} + +bool MatchingImplementation::assocIsMatched(IAssociationStrategy::index_assignment_t association, const std::vector<std::vector<double> >& matrix){ + int ndim = matrix.size(); + if(!ndim) return false; + + bool result = true; + for(auto trig_reco : association){ + double distance = matrix[trig_reco.first][trig_reco.second]; + bool single_assoc = distance < m_threshold; + result = result && single_assoc; + ATH_MSG_DEBUG("reco: " << trig_reco.first << " associated to trig: " << trig_reco.second << + " with distance: " << distance << " ok: " << single_assoc << " overall: " << result); + } + return result; +} + +bool MatchingImplementation::matchDistanceMatrix(const std::vector<std::vector<double> >& matrix, const Trig::MatchingStrategy::Strategy strategy){ + int nrows = matrix.size(); + int ncols = matrix.at(0).size(); + + ATH_MSG_DEBUG("matching a " << nrows << "x" << ncols << "matrix now"); + + auto MSG_MATRIX = MSG::DEBUG; + if(msgLvl(MSG_MATRIX)){ + msg() << MSG_MATRIX << "===========" << endreq; + for(auto& row : matrix){ + msg() << MSG_MATRIX << "|"; + for(auto distance : row){ + msg() << MSG_MATRIX << distance << " , "; + } + msg() << MSG_MATRIX << "|" << endreq; + } + msg() << MSG_MATRIX << "===========" << endreq; + } + + auto association_map = m_strategies[strategy]->associate(matrix); + return assocIsMatched(association_map,matrix); +} + +} \ No newline at end of file diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingTool.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..c4974aeae23b6604c9992bd61f8762da6f5aa377 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MatchingTool.cxx @@ -0,0 +1,203 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TriggerMatchingTool/MatchingTool.h" +#include "TriggerMatchingTool/MatchingImplementation.h" +#include "xAODBase/IParticle.h" + +#include "FourMomUtils/xAODP4Helpers.h" + +namespace Trig { + +MatchingTool::MatchingTool(const std::string& name) : + asg::AsgTool(name), + m_impl(new Trig::MatchingImplementation(*this)), + m_trigDecTool("Trig::TrigDecisionTool/TrigDecisionTool"), + m_matchingThreshold(0) +{ + declareProperty( "TrigDecisionTool", m_trigDecTool); + +#ifndef XAOD_STANDALONE +// declareProperty( "DefaultMatchingThreshold", m_matchingThreshold = 0.4)->declareUpdateHandler(&MatchingTool::updateThreshold, this ); + auto props = getProperties(); + for( Property* prop : props ) { + if( prop->name() != "OutputLevel" ) { + continue; + } + prop->declareUpdateHandler( &MatchingTool::updateOutputLevel, this ); + break; + } +#else +// declareProperty( "DefaultMatchingThreshold", m_matchingThreshold = 0.4); +#endif + +} + +#ifndef XAOD_STANDALONE +void MatchingTool::updateOutputLevel(Property& p) { + this->msg_update_handler(p); //calls original handler + impl()->msg().setLevel(AthMessaging::msg().level()); //pass on our message level to the matchingimplementation +} + +void MatchingTool::updateThreshold(Property& /*p*/) { + ATH_MSG_DEBUG("Matching Threshold is updated to:" << m_matchingThreshold); + impl()->setThreshold( m_matchingThreshold ); +} +#endif + +MatchingTool::~MatchingTool(){ + delete m_impl; +} + +StatusCode MatchingTool::initialize() { + impl()->setThreshold( m_matchingThreshold ); + + ATH_CHECK( m_trigDecTool.retrieve() ); + + return StatusCode::SUCCESS; +} + +bool MatchingTool::matchCombination(const std::vector<const xAOD::IParticle*>& recoObjects, Trig::Combination& comb){ + std::map<xAOD::Type::ObjectType,std::vector<const xAOD::IParticle*> > type_separated; + + for(const auto& obj : recoObjects){ + if(type_separated.find(obj->type()) == type_separated.end()){ + std::vector<const xAOD::IParticle*> typevec; + type_separated[obj->type()] = typevec; + } + type_separated[obj->type()].push_back(obj); + } + + ATH_MSG_DEBUG("found: " << type_separated.size() << " unique objects types to match"); + for(auto& type_vec : type_separated){ + ATH_MSG_DEBUG("type: " << type_vec.first << "(" << type_vec.second.size() << " elements)"); + } + + bool overall_status = true; + std::map<xAOD::Type::ObjectType,bool> status; + for(auto& type_vec : type_separated){ + auto single_status = matchSingleType(type_vec.second, comb); + ATH_MSG_DEBUG("type: " << type_vec.first << " status: " << single_status); + status[type_vec.first] = single_status; + overall_status = overall_status && single_status; + } + + return overall_status; +} + +bool MatchingTool::matchSingleType(const std::vector<const xAOD::IParticle*>& recoObjects, Trig::Combination& comb){ + ATH_MSG_DEBUG("matching combination with " << comb.tes().size() << " TEs"); + + auto recoType = recoObjects.at(0)->type(); + ATH_MSG_DEBUG("reco type is " << recoType); + + HLT::class_id_type clid = 0; + std::string container_typename(""); + + if(m_typeMap.isKnown(recoType)){ + auto clid_container = m_typeMap.get(recoType); + clid = clid_container.first; + container_typename = clid_container.second; + ATH_MSG_DEBUG("getting trigger features (clid: " << clid << " and type: " << container_typename << ")"); + } + else{ + ATH_MSG_WARNING("could not find corresponding trigger type, can't match"); + return false; + } + + auto iparticle_feats = comb.getIParticle(clid,container_typename); + + ATH_MSG_DEBUG("found: " << iparticle_feats.size() << " xAOD::IParticle"); + + for(auto& feat : iparticle_feats){ + ATH_MSG_DEBUG(" ==> pt: " << feat.cptr()->pt() << " and eta: " << feat.cptr()->eta() << " address: " << feat.cptr()); + } + + if(recoObjects.size() > iparticle_feats.size()){ + ATH_MSG_WARNING("more reco objects (" << recoObjects.size() << ") than trigger object (" << iparticle_feats.size() << "). no hope of matching!"); + return false; + } + + ATH_MSG_DEBUG("now matching: " << recoObjects.size() << " reco objects to " << iparticle_feats.size() << " trigger objects"); + + std::vector<const xAOD::IParticle*> trigObjects; + for(auto& feat : iparticle_feats){trigObjects.push_back(feat.cptr());} + + auto distance_matrix = distanceMatrix(recoObjects,trigObjects); + + ATH_MSG_DEBUG("made distance matrix"); + + bool match_result = impl()->matchDistanceMatrix(distance_matrix); + + ATH_MSG_DEBUG("got matching result: " << match_result); + + return match_result; +} + +bool MatchingTool::match(const xAOD::IParticle& recoObject, const std::string& chain, double matchThreshold) { + impl()->setThreshold( matchThreshold ); + std::vector<const xAOD::IParticle*> recoObjects(1,&recoObject); + bool out = match(recoObjects, chain); + impl()->setThreshold( m_matchingThreshold ); + return out; +} + +bool MatchingTool::match(const std::vector<const xAOD::IParticle*>& recoObjects, const std::string& chain, double matchThreshold) { + impl()->setThreshold( matchThreshold ); + bool out = match(recoObjects, chain); + impl()->setThreshold( m_matchingThreshold ); + return out; +} + +bool MatchingTool::match(const std::vector<const xAOD::IParticle*>& recoObjects, const std::string& chain){ + ATH_MSG_DEBUG("matching " << recoObjects.size() << " reco objects to chain: " << chain ); + + auto chainGroup = impl()->tdt()->getChainGroup(chain); + bool decision = chainGroup->isPassed(); + ATH_MSG_DEBUG(chain << " is passed: " << decision); + if(!decision) return false; + + auto featureContainer = chainGroup->features(); + auto combinations = featureContainer.getCombinations(); + + ATH_MSG_DEBUG("chain has: " << combinations.size() << " combos"); + + bool result = false; + for(auto& comb : combinations){ + bool combResult = matchCombination(recoObjects,comb); + ATH_MSG_DEBUG("matching result for this combination: " << combResult); + result = result || combResult; + if(result) break; //no need to continue if result is true + } + + ATH_MSG_DEBUG("overall matching result: " << result); + + return result; +} + +Trig::MatchingImplementation* MatchingTool::impl(){ + return m_impl; +} + +double MatchingTool::IParticleMetric(const xAOD::IParticle* lhs, const xAOD::IParticle* rhs){ + return xAOD::P4Helpers::deltaR(lhs,rhs,false /*use pseudorapidity to avoid calling p4*/);//return lhs->p4().DeltaR(rhs->p4()); +} + +std::vector<std::vector<double> > MatchingTool::distanceMatrix(const std::vector<const xAOD::IParticle*>& reco, + const std::vector<const xAOD::IParticle*>& trigger){ + std::vector<std::vector<double> > rows; + for(const auto& rec : reco){ + std::vector<double> distances_to_rec; + for(const auto& trig : trigger){ + distances_to_rec.push_back(IParticleMetric(rec,trig)); + } + rows.push_back(distances_to_rec); + } + return rows; +} + +} //Trig namespace + + + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.cxx new file mode 100644 index 0000000000000000000000000000000000000000..da85d43587bf75d292d1747c647e52f489a0abb0 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.cxx @@ -0,0 +1,40 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "MinimalSumAssociation.h" +#include "munkres.h" + + +MinimalSumAssociation::MinimalSumAssociation() : asg::AsgMessaging("MinimalSumAssociation"){ + msg().setLevel(MSG::DEBUG); +} + +IAssociationStrategy::index_assignment_t MinimalSumAssociation::associate(const std::vector<std::vector<double> >& matrix){ + IAssociationStrategy::index_assignment_t resultmap; + int nrows = matrix.size(); + int ncols = matrix.at(0).size(); + + auto workmatrix = matrix; + //if we have more columns than rows we pad the matrix to make it square + if(nrows < ncols){ + for(int i = (ncols - nrows); i > 0;i--){ + workmatrix.push_back(std::vector<double>(ncols,0)); + } + } + + munkres::vec_type costs; + costs.reserve(ncols); + munkres munk(workmatrix); + + bool debug = false; + auto result = munk.run(costs,debug); + + for(int i = 0;i < nrows;++i){ + resultmap[i] = result[i]; + } + + return resultmap; +} diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.h b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.h new file mode 100644 index 0000000000000000000000000000000000000000..5ca655b13d0b8594018e6bfe697bf315f1a19261 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/MinimalSumAssociation.h @@ -0,0 +1,20 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIGGERMATCHINGTOOL_MINIMALSUMASSOCIATION_H +#define TRIGGERMATCHINGTOOL_MINIMALSUMASSOCIATION_H + +#include "TriggerMatchingTool/IAssociationStrategy.h" +#include "AsgTools/AsgMessaging.h" + + +class MinimalSumAssociation : public IAssociationStrategy, public asg::AsgMessaging { +public: + MinimalSumAssociation(); + index_assignment_t associate(const std::vector<std::vector<double> >& matrix); +}; + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/TypeMap.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/TypeMap.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b3349df8f02df11536eb42daeaf2322c60cfd705 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/TypeMap.cxx @@ -0,0 +1,25 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "TriggerMatchingTool/TypeMap.h" + +TypeMap::TypeMap(){ + m_typemap[xAOD::Type::Muon] = std::make_pair(1178459224, "xAOD::MuonContainer" ); + m_typemap[xAOD::Type::Electron] = std::make_pair(1087532415, "xAOD::ElectronContainer"); + m_typemap[xAOD::Type::Photon] = std::make_pair(1105575213, "xAOD::PhotonContainer"); + m_typemap[xAOD::Type::Tau] = std::make_pair(1177172564, "xAOD::TauJetContainer"); +} + +bool TypeMap::isKnown(const xAOD::Type::ObjectType& recoType){ + auto it = m_typemap.find(recoType); + return (it!=m_typemap.end()); +} + +TypeMap::clid_string_t TypeMap::get(const xAOD::Type::ObjectType& recoType){ + auto it = m_typemap.find(recoType); + if(it!=m_typemap.end()){ + return it->second; + } + return std::make_pair(0,""); +} diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8da000f9928b33b136a0471740f79e90fd02f985 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.cxx @@ -0,0 +1,292 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#include "munkres.h" +#include "boost/io/ios_state.hpp" + +munkres::munkres(matrix_type costs): + m_costmatrix(costs), + m_costs_orig(costs), + m_step(0), + m_dim(costs.size()) +{ + m_rowIsCovered = std::vector<bool>(m_dim,false); + m_colIsCovered = std::vector<bool>(m_dim,false); + vec_type maskrow(m_dim,0); + for(int i=0;i<m_dim;++i) m_maskmatrix.push_back(maskrow); +} + +munkres::result_type munkres::run(vec_type& costvector, bool debug){ + if(debug) printcosts(); + bool done = false; + m_step = 1; + while(!done){ + if(debug){ + std::cout << "doing step " << m_step << std::endl; + printcosts(); + printmask(); + } + switch(m_step){ + case 1: + step_one(); + break; + case 2: + step_two(); + break; + case 3: + step_three(); + break; + case 4: + step_four(); + break; + case 5: + step_five(); + break; + case 6: + step_six(); + break; + case 7: + done = true; + break; + } + } + if(debug) std::cout << "done running munkres algorithm: " << std::endl; + result_type result; + costvector.clear(); + + for(int row=0;row<m_dim;++row){ + int col = find_in_row(row,kStar); + result.push_back(col); + costvector.push_back(m_costs_orig[row][result.back()]); + } + + if(debug){ + printcosts(); + for(uint i=0;i<result.size();++i) std::cout << "row: " << i << " -> col: " << result[i] << " cost: " << m_costs_orig[i][result[i]]<< std::endl; + } + return result; +} + +void munkres::step_one(){ + //subtract row minimum from each row element + //then go to step 2 + for(int row=0;row<m_dim;++row){ + double min = m_costmatrix[row][0]; + for(int col=0;col<m_dim;++col){ // find minimum + if(m_costmatrix[row][col] < min) min = m_costmatrix[row][col]; + } + for(int col=0;col<m_dim;++col){ // subtract from all elements + m_costmatrix[row][col] -= min; + } + } + m_step = 2; +} + +void munkres::step_two(){ + //star a zero matrix element if there is no other zero in it column or row + //we will cover each row and column that have a zero so we know we don't have to + //check these rows and columns anymore for zeros to star + //then go to step 3 + + for(int row=0;row<m_dim;++row){ + for(int col=0;col<m_dim;++col){ + if(!m_rowIsCovered[row] && !m_colIsCovered[col] && m_costmatrix[row][col] == 0){ + m_rowIsCovered[row] = true; + m_colIsCovered[col] = true; + m_maskmatrix[row][col] = 1; + } + } + } + //reset cover vectors + for(int i=0;i<m_dim;++i){ + m_rowIsCovered[i] = false; + m_colIsCovered[i] = false; + } + m_step = 3; +} + +void munkres::step_three(){ + //cover each column with a starred zero + //if we have m_dim coered columns we're done and exit + //else we continue with step four + + for(int row=0;row<m_dim;++row){ + for(int col=0;col<m_dim;++col){ + if(m_maskmatrix[row][col] == 1) m_colIsCovered[col] = true; // this is a starred zero cover column + } + } + + int nCoveredCols = 0; + for(int col=0;col<m_dim;++col) if(m_colIsCovered[col]) nCoveredCols++; + + m_step = (nCoveredCols == m_dim) ? 7 : 4; +} + +void munkres::find_a_zero(int& row, int& col){ + for(row=0;row<m_dim;++row){ + for(col=0;col<m_dim;++col){ + if(m_costmatrix[row][col] == 0 && !m_colIsCovered[col] && !m_rowIsCovered[row]) return; + } + } + //not returned until now set flags + row = -1; + col = -1; +} + +int munkres::find_in_row(const int row, markertype what){ + for(int col = 0;col<m_dim;++col) if(m_maskmatrix[row][col] == what) return col; + //not returned until now + return -1; +} + +int munkres::find_in_col(const int col, markertype what){ + for(int row = 0;row<m_dim;++row) if(m_maskmatrix[row][col] == what) return row; + //not returned until now + return -1; +} + +void munkres::step_four(){ + // Find a noncovered zero and prime it. + // If there is no starred zero in the row containing this primed zero, Go to Step 5. + // Otherwise, cover this row and uncover the column containing the starred zero. + // Continue in this manner until there are no uncovered zeros left. + // Save the smallest uncovered value and Go to Step 6. + + bool done = false; + while(!done){ + int row, col; + find_a_zero(row,col); + if(row == -1){ + //there is no uncovered zero go to step 6 + done = true; + m_step = 6; + } + else{ + // std::cout << "found uncovered zero at " << row << ", " << col << std::endl; + //check if there is a starred zero in this row + m_maskmatrix[row][col]=2; + int starred0atcol = find_in_row(row,kStar); + if(starred0atcol != -1){ + m_rowIsCovered[row] = true; + m_colIsCovered[starred0atcol] = false; + // printcosts(); + // printmask(); + }else{ + done = true; + m_step = 5; + } + } + } +} + +void munkres::augment_path(const std::vector<coords>& p){ + for(unsigned int i = 0;i<p.size();++i){ + const int row = p[i].first; + const int col = p[i].second; + if(m_maskmatrix[row][col] == kStar){ + m_maskmatrix[row][col] = 0; //unstar each star; + } + else{//primed zeros + m_maskmatrix[row][col] = kStar; //star each primed and unprime it; + } + } +} + +void munkres::erase_primes_and_covers(){ + for(int row=0;row<m_dim;++row){ + m_rowIsCovered[row] = false; + m_colIsCovered[row] = false;//queadratic matrix + for(int col=0;col<m_dim;++col){ + if(m_maskmatrix[row][col] == kPrime) m_maskmatrix[row][col] = 0; + } + } +} + +void munkres::step_five(){ + // Construct a series of alternating primed and starred zeros as follows. + // Let Z0 represent the uncovered primed zero found in Step 4. + // Let Z1 denote the starred zero in the column of Z0 (if any). + // Let Z2 denote the primed zero in the row of Z1 (there will always be one). + // Continue until the series terminates at a primed zero that has no starred zero in its column. + // Unstar each starred zero of the series, star each primed zero of the series, + // erase all primes and uncover every line in the matrix. Return to Step 3. + + bool done = false; + std::vector<coords> path; + int row,col; + find_a_zero(row,col); + + path.push_back(coords(row,col)); + + int n = 0; + while(!done && n<4){n++; + int starred0atrow = find_in_col(path.back().second,kStar); + if(starred0atrow > -1){ + path.push_back(coords(starred0atrow,path.back().second)); + } + else{ + done = true; + } + if(!done){ + int primed0atcol = find_in_row(path.back().first,kPrime); + path.push_back(coords(path.back().first,primed0atcol)); + } + } + // std::cout << "found path: " << std::endl; + // for(unsigned int i=0;i<path.size();++i){ + // std::cout << "(" << path[i].first << "," << path[i].second << ")" << ((i<path.size()-1) ? "->" : "" ); + // } std::cout << std::endl; + + augment_path(path); + erase_primes_and_covers(); + + m_step = 3; +} + +double munkres::find_min_uncov(){ + double min = 1e10; + for(int row=0;row<m_dim;++row){ + for(int col=0;col<m_dim;++col){ + if(!m_rowIsCovered[row] && !m_colIsCovered[col] && m_costmatrix[row][col] < min){ + min = m_costmatrix[row][col]; + } + } + } + return min; +} + +void munkres::step_six(){ + // Add the value found in Step 4 to every element of each covered row, + // and subtract it from every element of each uncovered column. + // Return to Step 4 without altering any stars, primes, or covered lines. + // Notice that this step uses the smallest uncovered value in the cost matrix to modify the matrix. + + double minuncov = find_min_uncov(); + for(int row=0;row<m_dim;++row){ + for(int col=0;col<m_dim;++col){ + if(m_rowIsCovered[row]) m_costmatrix[row][col] += minuncov; + if(!m_colIsCovered[col]) m_costmatrix[row][col] -= minuncov; + } + } + m_step = 4; +} + +void munkres::printmatrix(const matrix_type& m){ + boost::io::ios_all_saver ias(std::cout); + std::cout << std::setw(5) << std::setprecision(3) << "cov|"; + for(int col=0;col<m_dim;++col){ + std::cout << std::setw(7) << std::setprecision(3) << (m_colIsCovered[col] ? "+|" : "|"); + } std::cout << std::endl; + + for(int row=0;row<m_dim;++row){ + std::cout << std::setw(5) << std::setprecision(3) << (m_rowIsCovered[row] ? "+ |" : "|"); + for(int col=0;col<m_dim;++col){ + std::cout << std::setw(5) << std::setprecision(3) << m[row][col]; + if(m_maskmatrix[row][col] == kPrime) std::cout << "'"; + if(m_maskmatrix[row][col] == kStar) std::cout << "*"; + else std::cout << " "; + std::cout << "|"; + } std::cout << std::endl; + } +} diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.h b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.h new file mode 100644 index 0000000000000000000000000000000000000000..d525f8984decbf23aa41d5aba1fdf8ba7effdfff --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/Root/munkres.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + + +#ifndef MUNKRES_H +#define MUNKRES_H + +#include <iostream> +#include <vector> +#include <iomanip> +#include <map> + +// adapted for C++ from http://csclab.murraystate.edu/bob.pilgrim/445/munkres.html +// standalone code at: https://github.com/lukasheinrich/munkres-cpp + +class munkres{ +public: + typedef std::vector<double> vec_type; + typedef std::vector<vec_type > matrix_type; + typedef std::pair<int,int> coords; + typedef std::vector<int> result_type; + + enum markertype {kStar = 1, kPrime = 2}; + + munkres(matrix_type costs); + void printcosts(){printmatrix(m_costmatrix);} + result_type run(vec_type& costvector, bool debug = false); + +private: + void step_one(); + void step_two(); + void step_three(); + void step_four(); + void step_five(); + void step_six(); + + void printmask() {printmatrix(m_maskmatrix);} + void printmatrix(const matrix_type&); + + void find_a_zero(int& row,int& col); + int find_in_row(const int row, markertype what); + int find_in_col(const int col, markertype what); + double find_min_uncov(); + + void augment_path(const std::vector<coords>& path); + void erase_primes_and_covers(); + + matrix_type m_costmatrix; + matrix_type m_costs_orig; + matrix_type m_maskmatrix; + int m_step; + + std::vector<bool> m_rowIsCovered; + std::vector<bool> m_colIsCovered; + const int m_dim; +}; +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IAssociationStrategy.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IAssociationStrategy.h new file mode 100644 index 0000000000000000000000000000000000000000..4a952a25765ecb65e8ed00cea5c5e3cde7aa168c --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IAssociationStrategy.h @@ -0,0 +1,20 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIGGERMATCHINGTOOL_IASSOCIATIONSTRATEGY_H +#define TRIGGERMATCHINGTOOL_IASSOCIATIONSTRATEGY_H + +#include <map> +#include <vector> + +class IAssociationStrategy { +public: + virtual ~IAssociationStrategy(){} + typedef std::map<unsigned int,unsigned int> index_assignment_t; + virtual index_assignment_t associate(const std::vector<std::vector<double> >& matrix) = 0; +}; + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IMatchingTool.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IMatchingTool.h new file mode 100644 index 0000000000000000000000000000000000000000..e8c180a44e67961cbaba608ec248d6977ace75c8 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/IMatchingTool.h @@ -0,0 +1,39 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef IMATCHINGTOOL_H +#define IMATCHINGTOOL_H + + +// Framework include(s): +#include "AsgTools/IAsgTool.h" + +namespace xAOD{ + class IParticle; +} + + + +namespace Trig { + +class MatchingImplementation; + +class IMatchingTool : virtual public asg::IAsgTool { + ASG_TOOL_INTERFACE(IMatchingTool) +public: + + ///single object trigger matching. matchThreshold is typically the deltaR requirement to obtain positive matching + virtual bool match(const xAOD::IParticle& recoObject, const std::string& chain, double matchThreshold=0.1) = 0; + ///multi-object trigger matching + virtual bool match(const std::vector<const xAOD::IParticle*>& recoObjects, const std::string& chain, double matchThreshold=0.1) = 0; + +protected: + virtual MatchingImplementation* impl() = 0; +}; + +} + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingImplementation.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingImplementation.h new file mode 100644 index 0000000000000000000000000000000000000000..e95bad4ea473a657d5eed4576fcf7a0929575bf3 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingImplementation.h @@ -0,0 +1,48 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + + +#ifndef TRIGGERMATCHINGTOOL_MATCHINGIMPLEMENTATION_H +#define TRIGGERMATCHINGTOOL_MATCHINGIMPLEMENTATION_H + +#include <map> + +#include "TriggerMatchingTool/MatchingTool.h" +#include "TriggerMatchingTool/IAssociationStrategy.h" +#include "AsgTools/AsgMessaging.h" + +namespace Trig { + + +class TrigDecisionTool; + + +class MatchingTool; + +namespace MatchingStrategy { + enum Strategy { + MinimalSum = 0, + MaximalMatched = 1 + }; +} + + +class MatchingImplementation : public asg::AsgMessaging { +public: + MatchingImplementation(MatchingTool& mt, double threshold=0.4); + Trig::TrigDecisionTool* tdt(); + bool matchDistanceMatrix(const std::vector<std::vector<double> >& matrix, const Trig::MatchingStrategy::Strategy strategy = Trig::MatchingStrategy::MinimalSum); + inline void setThreshold(double in) { m_threshold=in; } +private: + bool assocIsMatched(IAssociationStrategy::index_assignment_t association, const std::vector<std::vector<double> >& matrix); + MatchingTool& m_tool; + std::map<Trig::MatchingStrategy::Strategy,std::unique_ptr<IAssociationStrategy> > m_strategies; + double m_threshold; //the distance threshold for a match +}; + +} + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingTool.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingTool.h new file mode 100644 index 0000000000000000000000000000000000000000..c3120c7fc7b27784f4a6b19e5e1e004762a0652a --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/MatchingTool.h @@ -0,0 +1,64 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIGGERMATCHINGTOOL_MATCHINGTOOL_H +#define TRIGGERMATCHINGTOOL_MATCHINGTOOL_H + +// Framework include(s): +#include "AsgTools/AsgTool.h" +#include "AsgTools/ToolHandle.h" + +#include "TriggerMatchingTool/IMatchingTool.h" +#include "TriggerMatchingTool/TypeMap.h" +#include "TriggerMatchingTool/IMatchingTool.h" +#include "TrigDecisionTool/Combination.h" +#include "TrigDecisionTool/TrigDecisionTool.h" + +namespace Trig { + +class MatchingTool : public asg::AsgTool, + virtual public Trig::IMatchingTool { + ASG_TOOL_CLASS(MatchingTool,IMatchingTool) + +public: + friend class MatchingImplementation; + MatchingTool(const std::string& name); + ~MatchingTool(); + StatusCode initialize(); + + bool match(const std::vector<const xAOD::IParticle*>& recoObjects, const std::string& chain); + bool match(const std::vector<const xAOD::IParticle*>& recoObjects, const std::string& chain, double matchTreshold); + bool match(const xAOD::IParticle& recoObjects, const std::string& chain, double matchTreshold); + +protected: + MatchingImplementation* impl(); + + bool matchSingleType(const std::vector<const xAOD::IParticle*>& subRecoObjects, Trig::Combination& comb); + + bool matchCombination(const std::vector<const xAOD::IParticle*>& recoObjects, Trig::Combination& comb); + + +#ifndef XAOD_STANDALONE + void updateOutputLevel(Property& p); + void updateThreshold(Property& p); +#endif + +private: + + double IParticleMetric(const xAOD::IParticle* lhs, const xAOD::IParticle* rhs); + std::vector<std::vector<double> > distanceMatrix(const std::vector<const xAOD::IParticle*>& reco, + const std::vector<const xAOD::IParticle*>& trigger); + + + MatchingImplementation* m_impl; + TypeMap m_typeMap; + ToolHandle<Trig::TrigDecisionTool> m_trigDecTool; + double m_matchingThreshold; +}; + +} + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TriggerMatchingToolDict.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TriggerMatchingToolDict.h new file mode 100644 index 0000000000000000000000000000000000000000..8df3475e9b06014fdbbfe2c03f464e192e25328a --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TriggerMatchingToolDict.h @@ -0,0 +1,11 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + + +#ifndef TRIGGERMATCHINGTOOL_TRIGGERMATCHINGTOOLDICT_H +#define TRIGGERMATCHINGTOOL_TRIGGERMATCHINGTOOLDICT_H + +#include "TriggerMatchingTool/MatchingTool.h" + +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TypeMap.h b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TypeMap.h new file mode 100644 index 0000000000000000000000000000000000000000..d55a79043a60aac925c634ac1cf18ea9a9718950 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/TypeMap.h @@ -0,0 +1,23 @@ +// Dear emacs, this is -*- c++ -*- + +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIGGERMATCHINGTOOL_TYPEMAP_H +#define TRIGGERMATCHINGTOOL_TYPEMAP_H + +#include "TrigNavStructure/Types.h" +#include "xAODBase/IParticle.h" +#include <map> + +class TypeMap { +public: + typedef std::pair<HLT::class_id_type,std::string> clid_string_t; + TypeMap(); + bool isKnown(const xAOD::Type::ObjectType& recoType); + clid_string_t get(const xAOD::Type::ObjectType& recoType); +private: + std::map<xAOD::Type::ObjectType,clid_string_t> m_typemap; +}; +#endif diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/selection.xml b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/selection.xml new file mode 100644 index 0000000000000000000000000000000000000000..255b7689e33dfb9453aebaa8600765757a4748ef --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/TriggerMatchingTool/selection.xml @@ -0,0 +1,6 @@ + +<lcgdict> + <namespace name="Trig" /> + <class name="Trig::MatchingTool" /> + <class name="Trig::IMatchingTool" /> +</lcgdict> diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/Makefile.RootCore b/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/Makefile.RootCore new file mode 100644 index 0000000000000000000000000000000000000000..7400da22852ffae7d7ddaa663aa751bafef68d7c --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/Makefile.RootCore @@ -0,0 +1,60 @@ +# this makefile also gets parsed by shell scripts +# therefore it does not support full make syntax and features +# edit with care + +# for full documentation check: +# https://twiki.cern.ch/twiki/bin/viewauth/Atlas/RootCore#Package_Makefile + + +# the name of the package: +PACKAGE = TriggerMatchingTool + +# the libraries to link with this one: +PACKAGE_PRELOAD = + +# additional compilation flags to pass (not propagated to dependent packages): +PACKAGE_CXXFLAGS = + +# additional compilation flags to pass (propagated to dependent packages): +PACKAGE_OBJFLAGS = + +# additional linker flags to pass (for compiling the library): +PACKAGE_LDFLAGS = + +# additional linker flags to pass (for compiling binaries): +PACKAGE_BINFLAGS = + +# additional linker flags to pass (propagated to client libraries): +PACKAGE_LIBFLAGS = + +# the list of packages we depend on: +PACKAGE_DEP = AsgTools TrigDecisionTool xAODBase FourMomUtils + +# the list of packages we use if present, but that we can work without : +PACKAGE_TRYDEP = + +# list pattern of scripts to link directly into binary path: +PACKAGE_SCRIPTS = + +# whether to use pedantic compilation: +PACKAGE_PEDANTIC = 1 + +# whether to turn *off* optimization (set to dict to do it only for +# dictionaries): +PACKAGE_NOOPT = 0 + +# whether to build no library (needs to be set if no source files are +# present): +PACKAGE_NOCC = 0 + +# whether we build a reflex dictionary: +PACKAGE_REFLEX = 1 + +# the list of all unit tests that should be called in recursive testing, +# i.e. in unit tests that call other unit tests +# for that unit tests need to pass on all machines, and run very fast +PACKAGE_RECURSIVE_UT = + + + +include $(ROOTCOREDIR)/Makefile-common diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/requirements b/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/requirements new file mode 100644 index 0000000000000000000000000000000000000000..40f7657aec6e44f6225b54d7099600a842191591 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/cmt/requirements @@ -0,0 +1,35 @@ +package TriggerMatchingTool +author Lukas Heinrich <lukas.heinrich@cern.ch> + +use AtlasPolicy AtlasPolicy-* + + +public +use GaudiInterface GaudiInterface-* External +use AsgTools AsgTools-* Control/AthToolSupport +use TrigDecisionTool TrigDecisionTool-* Trigger/TrigAnalysis +use TrigNavStructure TrigNavStructure-* Trigger/TrigEvent +use xAODBase xAODBase-* Event/xAOD + +private +use AtlasBoost AtlasBoost-* External +use FourMomUtils FourMomUtils-* Event + +use AthAnalysisBaseComps AthAnalysisBaseComps-* Control + +#uncomment the next line to use ROOT libraries in your package +#use AtlasROOT AtlasROOT-* External + +#use xAODEventInfo xAODEventInfo-* Event/xAOD + +end_private + + +apply_pattern dual_use_library files="../Root/*.cxx ../src/*.cxx" + +#Reflex Dictionary Generation: +private +use AtlasReflex AtlasReflex-* External +apply_pattern lcgdict dict=TriggerMatchingTool selectionfile=selection.xml headerfiles="../TriggerMatchingTool/TriggerMatchingToolDict.h" +end_private + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/share/TestMatchingToolAlgJobOptions.py b/Trigger/TrigAnalysis/TriggerMatchingTool/share/TestMatchingToolAlgJobOptions.py new file mode 100644 index 0000000000000000000000000000000000000000..b207a969f500814604ad18b31c0124ab6265b3fe --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/share/TestMatchingToolAlgJobOptions.py @@ -0,0 +1,18 @@ +#Skeleton joboption for a simple analysis job + +theApp.EvtMax=10 #says how many events to run over. Set to -1 for all events + +import AthenaPoolCnvSvc.ReadAthenaPool #sets up reading of POOL files (e.g. xAODs) +inputfile = os.environ.get("ASG_TEST_FILE_MC", "/afs/cern.ch/user/a/asgbase/patspace/xAODs/r6630/mc15_13TeV.361106.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zee.recon.AOD.e3601_s2576_s2132_r6630_tid05358812_00/AOD.05358812._000010.pool.root.1") +svcMgr.EventSelector.InputCollections=[inputfile] + +#optional: configure the matching tool +ToolSvc += CfgMgr.Trig__MatchingTool("MyMatchingTool",OutputLevel=DEBUG) + +algseq = CfgMgr.AthSequencer("AthAlgSeq") #gets the main AthSequencer +algseq += CfgMgr.TestMatchingToolAlg() #adds an instance of your alg to it + + + +include("AthAnalysisBaseComps/SuppressLogging.py") #Optional include to suppress as much athena output as possible. Keep at bottom of joboptions so that it doesn't suppress the logging of the things you have configured above + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/share/testAthenaMatchingTool.py b/Trigger/TrigAnalysis/TriggerMatchingTool/share/testAthenaMatchingTool.py new file mode 100755 index 0000000000000000000000000000000000000000..e04ac1e7323aa5dd6ef2958d77a08d221f4c2b5c --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/share/testAthenaMatchingTool.py @@ -0,0 +1,52 @@ +from AthenaCommon.AthenaCommonFlags import athenaCommonFlags +from RecExConfig.RecFlags import rec +from RecExConfig.RecAlgsFlags import recAlgs +from InDetRecExample.InDetJobProperties import InDetFlags +InDetFlags.doSecVertexFinder.set_Value_and_Lock(False) +from AthenaCommon.AppMgr import ToolSvc + + +athenaCommonFlags.FilesInput=["/afs/cern.ch/user/a/asgbase/patspace/xAODs/r6630/mc15_13TeV.361106.PowhegPythia8EvtGen_AZNLOCTEQ6L1_Zee.recon.AOD.e3601_s2576_s2132_r6630_tid05358812_00/AOD.05358812._000010.pool.root.1"] +athenaCommonFlags.EvtMax=10 +#athenaCommonFlags.EvtMax=-1 +rec.readAOD=True +# switch off detectors +rec.doForwardDet=False +rec.doInDet=False +rec.doCalo=False +rec.doMuon=False +rec.doEgamma=False +rec.doTrigger = True; recAlgs.doTrigger=False # disable trigger (maybe necessary if detectors switched off) +rec.doMuon=False +rec.doMuonCombined=False +rec.doWriteAOD=False +rec.doWriteESD=False +rec.doDPD=False +rec.doTruth=False + + +# autoconfiguration might trigger undesired feature +rec.doESD.set_Value_and_Lock(False) # uncomment if do not run ESD making algorithms +rec.doWriteESD.set_Value_and_Lock(False) # uncomment if do not write ESD +rec.doAOD.set_Value_and_Lock(False) # uncomment if do not run AOD making algorithms +rec.doWriteAOD.set_Value_and_Lock(False) # uncomment if do not write AOD +rec.doWriteTAG.set_Value_and_Lock(False) # uncomment if do not write TAG +include ("RecExCommon/RecExCommon_topOptions.py") +ToolSvc.TrigDecisionTool.TrigDecisionKey='xTrigDecision' +from AthenaCommon.AlgSequence import AlgSequence +from AthenaCommon.AppMgr import ToolSvc +theJob = AlgSequence() + +ToolSvc += CfgMgr.Trig__MatchingTool("MyMatchingTool",OutputLevel=DEBUG) + + +#algseq = CfgMgr.AthSequencer("AthAlgSeq") #gets the main AthSequencer +#algseq += CfgMgr.TestMatchingToolAlg() #adds an instance of your alg to it + + +from TriggerMatchingTool.TriggerMatchingToolConf import TestMatchingToolAlg +alg = TestMatchingToolAlg() +theJob += alg + +include("TriggerTest/TriggerTestCommon.py") + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fad009e52b79980aebaae469deac9cf3276af953 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.cxx @@ -0,0 +1,92 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + + +#include "TestMatchingToolAlg.h" +#include "xAODBase/IParticleContainer.h" + +StatusCode TestMatchingToolAlg::initialize() { + m_tmt.setTypeAndName("Trig::MatchingTool/MyMatchingTool"); + CHECK(m_tmt.retrieve()); //important to retrieve here, because TrigDecisionTool must be initialized before event loop + return StatusCode::SUCCESS; +} + +StatusCode TestMatchingToolAlg::execute() { + + //For more documentation on the tool, see: https://twiki.cern.ch/twiki/bin/view/Atlas/XAODMatchingTool + //As of Feb 2016: + //Recommended threshold for egamma triggers: 0.07 + //Recommended threshold for muon triggers: 0.1 + + //here's an example of using the tool + const xAOD::IParticleContainer* electrons = 0; + const xAOD::IParticleContainer *taus = 0; + const xAOD::IParticleContainer *muons = 0; + CHECK( evtStore()->retrieve( electrons, "Electrons" )); + CHECK( evtStore()->retrieve( muons, "Muons" ) ); + CHECK( evtStore()->retrieve( taus, "TauJets" ) ); + if(electrons) ATH_MSG_INFO("Offline Electron container size " << electrons->size()); + if(muons) ATH_MSG_INFO("Offline Muon container size " << muons->size()); + if(taus) ATH_MSG_INFO("Offline Tau container size " << taus->size()); + //tool takes a vector of IParticles, and a trigger chain expression + //can also take a single IParticle for single object trigger matching + std::vector<const xAOD::IParticle*> myParticles; + + //here's an example of a single object trigger + if (electrons) { + for(uint i = 0; i< electrons->size(); i++) { + myParticles.clear(); + myParticles.push_back( electrons->at(i) ); + ATH_MSG_INFO("HLT_e17_lhloose Matching Decision = " << m_tmt->match(myParticles,"HLT_e17_lhloose",0.07 /*explicit dR threhsold*/) ); + + // here's an example of a combined trigger + // e-mu + if(muons){ + for(uint j = 0; j < muons->size(); j++) { + myParticles.clear(); + myParticles.push_back(electrons->at(i)); + myParticles.push_back(muons->at(j)); + ATH_MSG_INFO("HLT_e17_lhloose_mu14 = " << m_tmt->match(myParticles,"HLT_e17_lhloose_mu14")); + } + } + // e-tau + if(taus){ + for(uint j = 0; j < taus->size(); j++) { + myParticles.clear(); + myParticles.push_back(electrons->at(i)); + myParticles.push_back(taus->at(j)); + ATH_MSG_INFO("HLT_e17_lhmedium_iloose_tau25_medium1_tracktwo = " << m_tmt->match(myParticles,"HLT_e17_lhmedium_iloose_tau25_medium1_tracktwo")); + } + } + } + } + + // here's an example for muon trigger, using the method for single-object trigger matching + if(muons){ + for(auto muon : *muons) { + ATH_MSG_INFO("HLT_mu18 = " << m_tmt->match(*muon,"HLT_mu18")); + } + } + // here's an examplefor a tau trigger + if(taus){ + for(uint j = 0; j < taus->size(); j++) { + myParticles.clear(); + myParticles.push_back(taus->at(j)); + ATH_MSG_INFO("HLT_tau25_loose1_ptonly = " << m_tmt->match(myParticles,"HLT_tau25_loose1_ptonly")); + } + } + + //here's an example for a dilepton trigger + //form pairs to test a dilepton trigger + for(uint i = 0; i< electrons->size()-1; i++) { + for(uint j = i+1; j < electrons->size(); j++) { + myParticles.clear(); + myParticles.push_back( electrons->at(i) ); + myParticles.push_back( electrons->at(j) ); + ATH_MSG_INFO("HLT_2e17_lhloose Matching Decision = " << m_tmt->match(myParticles,"HLT_2e17_lhloose") ); + } + } + + return StatusCode::SUCCESS; +} diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.h b/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.h new file mode 100644 index 0000000000000000000000000000000000000000..65066d4fc511145bece9922601daba39b13b2fcf --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/src/TestMatchingToolAlg.h @@ -0,0 +1,26 @@ +/* + Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef TRIGGERMATCHINGTOOL_TESTMATCHINGTOOLALG_H +#define TRIGGERMATCHINGTOOL_TESTMATCHINGTOOLALG_H 1 + +#include "AthAnalysisBaseComps/AthAnalysisAlgorithm.h" + +#include "TriggerMatchingTool/IMatchingTool.h" + +class TestMatchingToolAlg: public ::AthAnalysisAlgorithm { + public: + TestMatchingToolAlg( const std::string& name, ISvcLocator* pSvcLocator ) : AthAnalysisAlgorithm( name, pSvcLocator ) {} + virtual ~TestMatchingToolAlg() {} + + virtual StatusCode initialize(); + virtual StatusCode execute(); + + private: + + ToolHandle<Trig::IMatchingTool> m_tmt; + +}; + +#endif //> !TRIGGERMATCHINGTOOL_TESTMATCHINGTOOLALG_H diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_entries.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_entries.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4837306b21e33777a6ddfcc9bf874463bfcefa3c --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_entries.cxx @@ -0,0 +1,16 @@ +#include "GaudiKernel/DeclareFactoryEntries.h" +#include "TriggerMatchingTool/MatchingTool.h" + +DECLARE_NAMESPACE_TOOL_FACTORY( Trig, MatchingTool ) + + + +#include "../TestMatchingToolAlg.h" +DECLARE_ALGORITHM_FACTORY( TestMatchingToolAlg ) + + + +DECLARE_FACTORY_ENTRIES( TriggerMatchingTool ) { + DECLARE_ALGORITHM( TestMatchingToolAlg ); + DECLARE_NAMESPACE_TOOL(Trig, MatchingTool) +} diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_load.cxx b/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_load.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d82dcdb6a7a4645023019aea26becb6e0606c1e6 --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/src/components/TriggerMatchingTool_load.cxx @@ -0,0 +1,4 @@ +#include "GaudiKernel/LoadFactoryEntries.h" + +LOAD_FACTORY_ENTRIES( TriggerMatchingTool ) + diff --git a/Trigger/TrigAnalysis/TriggerMatchingTool/test/TriggerMatchingTool.xml b/Trigger/TrigAnalysis/TriggerMatchingTool/test/TriggerMatchingTool.xml new file mode 100644 index 0000000000000000000000000000000000000000..d903c919690a54bf59ea49528623afe60a31788b --- /dev/null +++ b/Trigger/TrigAnalysis/TriggerMatchingTool/test/TriggerMatchingTool.xml @@ -0,0 +1,13 @@ + <?xml version="1.0"?> +<atn> + <TEST name="TestMatchingToolAlg" type="athena" suite="ASGTests"> + <options_atn>TriggerMatchingTool/TestMatchingToolAlgJobOptions.py</options_atn> + <timelimit>5</timelimit> + <author> Lukas Heinrich </author> + <mailto> lukas.heinrich@cern.ch </mailto> + <expectations> + <errorMessage>FAILURE (ERROR)</errorMessage> + <returnValue>0</returnValue> + </expectations> + </TEST> +</atn>