Skip to content
Snippets Groups Projects
DigiROOTInput.cpp 6.49 KiB
Newer Older
//==========================================================================
//  AIDA Detector description implementation 
//--------------------------------------------------------------------------
// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN)
// All rights reserved.
//
// For the licensing terms see $DD4hepINSTALL/LICENSE.
// For the list of contributors see $DD4hepINSTALL/doc/CREDITS.
//
// Author     : M.Frank
//
//==========================================================================

// Framework include files
#include <DD4hep/InstanceCount.h>
#include <DDDigi/DigiROOTInput.h>
#include <DD4hep/Printout.h>
#include <DD4hep/Factories.h>

// ROOT include files
#include <TROOT.h>
#include <TFile.h>
#include <TTree.h>

// C/C++ include files
#include <stdexcept>
#include <map>

using namespace dd4hep::digi;

/// Helper class to hide connection parameters
/**
 *
 *  \author  M.Frank
 *  \version 1.0
 *  \ingroup DD4HEP_DIGITIZATION
 */
class DigiROOTInput::internals_t   {
public:
Markus Frank's avatar
Markus Frank committed
  typedef std::function<void(DataSegment&, int, const char*, void*)> func_t;

  class converter_t   {
  public:
    TBranch* branch   { nullptr };
    TClass*  cls      { nullptr };
    std::unique_ptr<func_t>   call     { };
  };

  /// Mutex to allow re-use of a single source for multiple input streams
  std::mutex m_lock        { };
Markus Frank's avatar
Markus Frank committed

  /// Branches present in the current file
  std::map<unsigned long, converter_t>  branches;
  /// Reference to the current ROOT file to be read
  TFile*     file   { };
  /// Reference to the ROOT tree to be read
  TTree*     tree   { nullptr };
  /// Pointer to current input source
  int        input  { INPUT_START };
  /// Current entry inside the current input source
  Long64_t   entry  { -1 };

public:
  /// Default constructor
  internals_t () = default;
  /// Default destructor
  ~internals_t ()   {
    if ( file )    {
      file->Close();
      delete file;
      file = nullptr;
    }
  }
  /// Coverter function from input data to DDDige data
  std::unique_ptr<func_t> input_converter(TClass* cl)  const   {
    using detail::str_replace;
    std::string cln = str_replace(cl->GetName(),"::","_");
    cln = str_replace(str_replace(str_replace(cln,'<','_'),'>','_'),'*',"");
    std::string factory = "DD4hep_DDDigiConverter_"+cln;
    func_t *fptr = (func_t*)PluginService::Create<void*>(factory.c_str());
    if ( 0 == fptr )   {
      dd4hep::except("DigiROOTInput","Failed to access function pointer to read ROOT datatype: %s",cl->GetName());
    }
    return std::unique_ptr<func_t>(fptr);
  }
};


/// Standard constructor
DigiROOTInput::DigiROOTInput(const DigiKernel& kernel, const std::string& nam)
  : DigiInputAction(kernel, nam)
{
  declareProperty("tree", m_tree_name = "EVENT");
  declareProperty("location", m_location = "inputs");
  declareProperty("containers", m_containers);
  InstanceCount::increment(this);
}

/// Default destructor
DigiROOTInput::~DigiROOTInput()   {
  InstanceCount::decrement(this);
}

/// Open new input file
void DigiROOTInput::open_new_file()   const  {
  int len = this->m_inputs.size();
  imp = std::make_unique<internals_t>();
  if ( this->m_inputs.empty() ) imp->input = 0;
  while ( (imp->input+1) < len )   {
    const auto& fname = m_inputs[++imp->input];
    imp->file = TFile::Open(fname.c_str());
    if ( imp->file && imp->file->IsZombie() )  {
      delete imp->file;
      imp->file = nullptr;
      error("OpenInput ++ Failed to open input source %s", fname.c_str());
    }
    else if ( imp->file )  {
      imp->tree = (TTree*)imp->file->Get(this->m_tree_name.c_str());
      if ( !imp->tree )   {
	error("OpenInput ++ Failed to access tree: %s in input: %s", 
	      this->m_tree_name.c_str(), fname.c_str());
	continue;
      }
      imp->branches.clear();
      auto* branches = imp->tree->GetListOfBranches();
      TObjArrayIter it(branches);
      for(Int_t i=0; i<branches->GetEntriesFast(); ++i)   {
	TBranch*  b = (TBranch*)branches->At(i);
	TClass* cls = gROOT->GetClass( b->GetClassName(), kTRUE );
	/// If there are no required branches, we convert everything
	if ( this->m_containers.empty() )    {
Markus Frank's avatar
Markus Frank committed
	  Key key(b->GetName(), this->m_mask);
	  b->SetAutoDelete( kFALSE );
	  imp->branches[key.value()] = {b, cls, imp->input_converter(cls)};
	  continue;
	}
	/// Otherwise only the entities asked for
	for( const auto& bname : this->m_containers )    {
	  if ( bname == b->GetName() )   {
Markus Frank's avatar
Markus Frank committed
	    Key key(b->GetName(), this->m_mask);
	    b->SetAutoDelete( kFALSE );
	    imp->branches[key.value()] = {b, cls, imp->input_converter(cls)};
	    break;
	  }
	}
      }
      break;
    }
    else   {
      error("OpenInput ++ Failed to open input source %s", fname.c_str());
    }
    imp->file = nullptr;
  }
  if ( imp->input >= len )   {
    imp.reset();
    except("OpenInput ++ Failed to open NEW input source");
  }
}

/// Pre-track action callback
void DigiROOTInput::execute(DigiContext& context)  const   {
  if ( imp )   {
    std::lock_guard<std::mutex> lock(context.global_io_lock());
    Int_t total = imp->tree->GetEntries();
    if ( (1+imp->entry) >= total )   {
      imp.reset();
    }
  }
  /// If necessary open a new file
  if ( !imp )   {
    std::lock_guard<std::mutex> lock(context.global_io_lock());
    open_new_file();
  }
  
  if ( nullptr == imp->file )    {
    except("+++ No open file present. Configuration error?");
  }
  if ( imp->branches.empty() )    {
    except("+++ No branches to be loaded. Configuration error!");
  }
  std::size_t input_len = 0;
  //
  //  We have to lock all ROOT based actions. Consequences are SEGV otherwise.
  //
  auto& event = context.event;
  std::lock_guard<std::mutex> lock(context.global_io_lock());
  ++imp->entry;
  imp->tree->LoadTree( imp->entry );

  /// We only get here with a valid input
  for( const auto& br : imp->branches )    {
    void* obj = br.second.cls->New();
    br.second.branch->SetAddress(&obj);
  }
  DataSegment& segment = event->get_segment(this->m_location);
  for( const auto& br : imp->branches )    {
    TBranch* b = br.second.branch;
    int nb = b->GetEntry( imp->entry );
    if ( nb < 0 ) { // This is definitely an error...ROOT says if reads fail, -1 is issued.
      continue;
    }
    debug("%s+++ Loaded %8d bytes from branch %s", event->id(), nb, b->GetName());
    input_len += nb;
    const auto&  func = *br.second.call;
    void** addr = (void**)b->GetAddress();
Markus Frank's avatar
Markus Frank committed
    func(segment, this->m_mask, b->GetName(), *addr);
  }
  info("%s+++ Read event %6ld [%ld bytes] from tree %s file: %s",
       event->id(), imp->entry, input_len, imp->tree->GetName(), imp->file->GetName());
}