Program Listing for File TauJetMatching.cxx

Return to documentation for file (Root/TauJetMatching.cxx)

// 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;

}