.. _program_listing_file_Root_TauJetMatching.cxx:

Program Listing for File TauJetMatching.cxx
===========================================

|exhale_lsh| :ref:`Return to documentation for file <file_Root_TauJetMatching.cxx>` (``Root/TauJetMatching.cxx``)

.. |exhale_lsh| unicode:: U+021B0 .. UPWARDS ARROW WITH TIP LEFTWARDS

.. code-block:: cpp

   // c++ include(s):
   #include <iostream>
   #include <typeinfo>
   #include <map>
   
   // EL include(s):
   #include <EventLoop/Job.h>
   #include <EventLoop/StatusCode.h>
   #include <EventLoop/Worker.h>
   
   // EDM include(s):
   //#include "xAODCore/ShallowCopy.h"
   //#include "AthContainers/ConstDataVector.h"
   #include "xAODEventInfo/EventInfo.h"
   #include "xAODTau/TauJetContainer.h"
   #include "xAODJet/JetContainer.h"
   
   // package include(s):
   #include "xAODAnaHelpers/TauJetMatching.h"
   #include "xAODAnaHelpers/HelperClasses.h"
   #include "xAODAnaHelpers/HelperFunctions.h"
   
   // ROOT include(s):
   #include "TLorentzVector.h"
   
   // this is needed to distribute the algorithm to the workers
   ClassImp(TauJetMatching)
   
   
   TauJetMatching :: TauJetMatching () :
       Algorithm("TauJetMatching")
   {
   }
   
   TauJetMatching::~TauJetMatching() {}
   
   EL::StatusCode TauJetMatching :: setupJob (EL::Job& job)
   {
     // Here you put code that sets up the job on the submission object
     // so that it is ready to work with your algorithm, e.g. you can
     // request the D3PDReader service or add output files.  Any code you
     // put here could instead also go into the submission script.  The
     // sole advantage of putting it here is that it gets automatically
     // activated/deactivated when you add/remove the algorithm from your
     // job, which may or may not be of value to you.
   
     ANA_MSG_INFO( "Calling setupJob");
   
     job.useXAOD ();
     xAOD::Init( "TauJetMatching" ).ignore(); // call before opening first file
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: histInitialize ()
   {
     // Here you do everything that needs to be done at the very
     // beginning on each worker node, e.g. create histograms and output
     // trees.  This method gets called before any input files are
     // connected.
   
     ANA_MSG_INFO( "Calling histInitialize");
     ANA_CHECK( xAH::Algorithm::algInitialize());
   
     //if ( this->numInstances() > 1 ) {
     //  m_isUsedBefore = true;
     //  ANA_MSG_INFO( "\t An algorithm of the same type has been already used " << numInstances() << " times" );
     //}
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: fileExecute ()
   {
     // Here you do everything that needs to be done exactly once for every
     // single file, e.g. collect a list of all lumi-blocks processed
   
     ANA_MSG_INFO( "Calling fileExecute");
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: changeInput (bool /*firstFile*/)
   {
     // Here you do everything you need to do when we change input files,
     // e.g. resetting branch addresses on trees.  If you are using
     // D3PDReader or a similar service this method is not needed.
   
     ANA_MSG_INFO( "Calling changeInput");
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: initialize ()
   {
     // Here you do everything that you need to do after the first input
     // file has been connected and before the first event is processed,
     // e.g. create additional histograms based on which variables are
     // available in the input files.  You can also create all of your
     // histograms and trees in here, but be aware that this method
     // doesn't get called if no events are processed.  So any objects
     // you create here won't be available in the output if you have no
     // input events.
   
     ANA_MSG_INFO( "Initializing TauJetMatching Interface... ");
   
     // Let's see if the algorithm has been already used before:
     // if yes, will write object cutflow in a different histogram!
     //
     // This is the case when the selector algorithm is used for
     // preselecting objects, and then again for the final selection
     //
     ANA_MSG_INFO( "Algorithm name: " << m_name << " - of type " << m_className );
   
     m_event = wk()->xaodEvent();
     m_store = wk()->xaodStore();
   
     ANA_MSG_INFO( "Number of events in file: " << m_event->getEntries() );
   
     if ( m_inContainerName.empty() ){
       ANA_MSG_ERROR( "InputContainer is empty!");
       return EL::StatusCode::FAILURE;
     }
   
     if ( m_inJetContainerName.empty() ){
       ANA_MSG_ERROR( "InputJetContainer is empty!");
       return EL::StatusCode::FAILURE;
     }
   
     // ********************************
     //
     // Initialise TauJetMatchingTool
     //
     // ********************************
   
     // IMPORTANT: if no working point is specified the one in this configuration will be used
   
     ANA_MSG_INFO( "TauJetMatching Interface succesfully initialized!" );
   
     return EL::StatusCode::SUCCESS;
   }
   
   EL::StatusCode TauJetMatching :: execute ()
   {
     // Here you do everything that needs to be done on every single
     // events, e.g. read input variables, apply cuts, and fill
     // histograms and trees.  This is where most of your actual analysis
     // code will go.
   
     ANA_MSG_DEBUG( "Applying Tau Selection..." );
   
     const xAOD::EventInfo* eventInfo(nullptr);
     ANA_CHECK( HelperFunctions::retrieve(eventInfo, m_eventInfoContainerName, m_event, m_store, msg()) );
   
     const xAOD::TauJetContainer* inTaus(nullptr);
   
     const xAOD::JetContainer* inJets(nullptr);
     ANA_CHECK( HelperFunctions::retrieve(inJets, m_inJetContainerName, m_event, m_store, msg()) );
   
   
     // if input comes from xAOD, or just running one collection,
     // then get the one collection and be done with it
     //
     if ( m_inputAlgoSystNames.empty() ) {
   
       // this will be the collection processed - no matter what!!
       //
       ANA_CHECK( HelperFunctions::retrieve(inTaus, m_inContainerName, m_event, m_store, msg()) );
   
       // fill truth-matching map
       //
       std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > > match_map;
       
       match_map = findBestMatchDR( inJets, inTaus, m_DeltaR );
       
       executeDecoration(match_map, inTaus);
   
     } else { // get the list of systematics to run over
   
       // get vector of string giving the syst names of the upstream algo from TStore (rememeber: 1st element is a blank string: nominal case!)
       //
       std::vector< std::string >* systNames(nullptr);
       ANA_CHECK( HelperFunctions::retrieve(systNames, m_inputAlgoSystNames, 0, m_store, msg()) );
   
       ANA_MSG_DEBUG( " input list of syst size: " << static_cast<int>(systNames->size()) );
   
       // loop over systematic sets
       //
       for ( auto systName : *systNames ) {
   
         ANA_MSG_DEBUG( " syst name: " << systName << "  input container name: " << m_inContainerName+systName );
   
         ANA_CHECK( HelperFunctions::retrieve(inTaus, m_inContainerName + systName, m_event, m_store, msg()) );
         ANA_CHECK( HelperFunctions::retrieve(inJets, m_inJetContainerName, m_event, m_store, msg()) );
   
         std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > > match_map_sys;
         
         match_map_sys = findBestMatchDR( inJets, inTaus, m_DeltaR );
         
         executeDecoration(match_map_sys, inTaus);
       }
     }
   
     // look what we have in TStore
     //
     if(msgLvl(MSG::VERBOSE)) m_store->print();
   
     return EL::StatusCode::SUCCESS;
   
   }
   
   bool TauJetMatching :: executeDecoration ( std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > > match_map, const xAOD::TauJetContainer* inTaus)
   {
     static SG::AuxElement::Decorator< float > JetWidthDecor("JetWidth");
     static SG::AuxElement::ConstAccessor<float> jetWidthAcc("Width");
   
     static SG::AuxElement::Decorator< float > JetJvtDecor("JetJvt");
     static SG::AuxElement::ConstAccessor<float> jetJvtAcc("Jvt");
   
     ANA_MSG_DEBUG( "Initial Taus: " << static_cast<uint32_t>(inTaus->size()) );
     
     int iTau = -1;
   
     for ( auto tau_itr : *inTaus ) { // duplicated of basic loop
       iTau++;
   
       std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > >::const_iterator it_map = match_map.find (iTau);
   
       if (it_map != match_map.end()) {
         
         // jet width      
         if (jetWidthAcc.isAvailable(*match_map[iTau].second)) {
           JetWidthDecor(*tau_itr) = static_cast<float>( jetWidthAcc(*match_map[iTau].second) );
         } else { 
           JetWidthDecor(*tau_itr) = -1.;
         } 
      
         // jet jvt    
         if (jetJvtAcc.isAvailable(*match_map[iTau].second)) {
           JetJvtDecor(*tau_itr) = static_cast<float>( jetJvtAcc(*match_map[iTau].second) );
         } else { 
           JetJvtDecor(*tau_itr) = -1.;
         } 
   
   
       } else {
         JetWidthDecor(*tau_itr) = -1.;
         JetJvtDecor(*tau_itr) = -1.;
       }
     }
   
     ANA_MSG_DEBUG( "Left  executeDecoration..." );
   
     return true;
   }
   
   EL::StatusCode TauJetMatching :: postExecute ()
   {
     // Here you do everything that needs to be done after the main event
     // processing.  This is typically very rare, particularly in user
     // code.  It is mainly used in implementing the NTupleSvc.
   
     ANA_MSG_DEBUG( "Calling postExecute");
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: finalize ()
   {
     // This method is the mirror image of initialize(), meaning it gets
     // called after the last event has been processed on the worker node
     // and allows you to finish up any objects you created in
     // initialize() before they are written to disk.  This is actually
     // fairly rare, since this happens separately for each worker node.
     // Most of the time you want to do your post-processing on the
     // submission node after all your histogram outputs have been
     // merged.  This is different from histFinalize() in that it only
     // gets called on worker nodes that processed input events.
   
     return EL::StatusCode::SUCCESS;
   }
   
   
   
   EL::StatusCode TauJetMatching :: histFinalize ()
   {
     // This method is the mirror image of histInitialize(), meaning it
     // gets called after the last event has been processed on the worker
     // node and allows you to finish up any objects you created in
     // histInitialize() before they are written to disk.  This is
     // actually fairly rare, since this happens separately for each
     // worker node.  Most of the time you want to do your
     // post-processing on the submission node after all your histogram
     // outputs have been merged.  This is different from finalize() in
     // that it gets called on all worker nodes regardless of whether
     // they processed input events.
   
     ANA_MSG_INFO( "Calling histFinalize");
     ANA_CHECK( xAH::Algorithm::algFinalize());
     return EL::StatusCode::SUCCESS;
   }
   
   float TauJetMatching::getDR(float eta1, float eta2, float phi1, float phi2)
   {
    float deta = std::abs(eta1-eta2);
    float dphi = std::abs(phi1-phi2);
    dphi = ( dphi <= TMath::Pi() ) ? dphi : ( 2 * TMath::Pi() - dphi );
    return sqrt(deta*deta + dphi*dphi);
   }
   
   std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > > TauJetMatching::findBestMatchDR(const xAOD::JetContainer* jetCont,
                                                                                                               const xAOD::TauJetContainer* tauCont,
                                                                                                               float best_DR=1.0)
   { 
     // Find tau that best matches a jet using DR.
     // If matching is successful, returns a map where the key 
     // is the  container index of the matched tau and the value
     // is the pair of the matched tau and the corresponding jet
     
     float default_best_DR = best_DR;
     int ijet = -1;
     int best_ijet = -1;
     const xAOD::Jet* best_jet = nullptr;
   
     std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet*>> match_map;
     
     for (const auto jet : *jetCont) {
         ++ijet;
         
         int itau = -1;
         int best_itau = -1;
         const xAOD::TauJet* best_tau = nullptr;
         float DR = 0;
         
         for (const auto tau : *tauCont) {
           ++itau;
           
           DR = this->getDR(tau->eta(),jet->eta(),tau->phi(),jet->phi());
   
           if (DR < best_DR) {
             best_DR = DR;
             best_tau = tau;
             best_jet = jet;
             best_itau = itau;
             best_ijet = ijet;
           }
         }
         
         std::unordered_map<int, std::pair<const xAOD::TauJet*, const xAOD::Jet* > >::const_iterator got = match_map.find (best_itau);
         
         // if a new match is found for a previous 
         // tau keep the new match if it is better 
         
         if (got == match_map.end() and best_itau != -1 and best_ijet != -1) {
           match_map[best_itau] = std::pair<const xAOD::TauJet*, const xAOD::Jet*>(best_tau, best_jet);
         }
         else if (got != match_map.end() and best_itau != -1 and best_ijet != -1) {
           float old_DR = this->getDR(match_map[best_itau].first->eta(), match_map[best_itau].second->eta(),match_map[best_itau].first->phi(), match_map[best_itau].second->phi());
           if (old_DR > best_DR) {
             match_map[best_itau] = std::pair<const xAOD::TauJet*, const xAOD::Jet*>(best_tau, best_jet);
           }
         }
         
         // reset the best_DR to the default value
         best_DR = default_best_DR;
     }
   
     return match_map;
   
   }