Skip to content
Snippets Groups Projects
PodioOutput.cpp 8.27 KiB
// SPDX-License-Identifier: LGPL-3.0-or-later
// Copyright (C) 2022 Whitney Armstrong, Sylvester Joosten, Wouter Deconinck

#include "PodioOutput.h"
#include "podio/podioVersion.h"
#include "GaudiKernel/ISvcLocator.h"
#include "JugBase/PodioDataSvc.h"
#include "TFile.h"
#include "rootutils.h"

// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
DECLARE_COMPONENT(PodioOutput)

PodioOutput::PodioOutput(const std::string& name, ISvcLocator* svcLoc)
    : GaudiAlgorithm(name, svcLoc), m_firstEvent(true),
      m_podioDataSvc(nullptr), m_datatree(nullptr), m_metadatatree(nullptr),
      m_runMDtree(nullptr), m_evtMDtree(nullptr), m_colMDtree(nullptr) {}

StatusCode PodioOutput::initialize() {
  if (GaudiAlgorithm::initialize().isFailure()) {
    return StatusCode::FAILURE;
  }

  // check whether we have the PodioEvtSvc active
  m_podioDataSvc = dynamic_cast<PodioDataSvc*>(evtSvc().get());
  if (m_podioDataSvc == nullptr) {
    error() << "Failed to get the DataSvc" << endmsg;
    return StatusCode::FAILURE;
  }

  m_file = std::unique_ptr<TFile>(TFile::Open(m_filename.value().c_str(), "RECREATE", "data file"));
  // Both trees are written to the ROOT file and owned by it
  // PodioDataSvc has ownership of EventDataTree
  m_datatree = m_podioDataSvc->eventDataTree();
  m_datatree->SetDirectory(m_file.get());
  m_metadatatree = new TTree("metadata", "Metadata tree");
  m_runMDtree = new TTree("run_metadata", "Run metadata tree");
  m_evtMDtree = new TTree("evt_metadata", "Event metadata tree");
  m_colMDtree = new TTree("col_metadata", "Collection metadata tree");

  m_evtMDtree->Branch("evtMD", "GenericParameters", m_podioDataSvc->getProvider().eventMetaDataPtr() ) ;
  m_switch       = KeepDropSwitch(m_outputCommands);
  return StatusCode::SUCCESS;
}

void PodioOutput::resetBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections) {
  for (const auto& [collName, collBuffers] : collections) {
    auto buffers = collBuffers->getBuffers();
    auto* data = buffers.data;
    auto* references = buffers.references;
    auto* vecmembers = buffers.vectorMembers;

    if (m_switch.isOn(collName)) {
      // Reconnect branches and collections
      m_datatree->SetBranchAddress(collName.c_str(), data);
      auto* colls = references;
      if (colls != nullptr) {
        for (size_t j = 0; j < colls->size(); ++j) {
          const auto brName = podio::root_utils::refBranch(collName, j);
          auto* l_branch = m_datatree->GetBranch(brName.c_str());
          l_branch->SetAddress(&(*colls)[j]);
        }
      }
      auto* colls_v = vecmembers;
      if (colls_v != nullptr) {
        int j = 0;
        for (auto& [dataType, add] : (*colls_v)) {
          const auto brName = podio::root_utils::vecBranch(collName, j);
          m_datatree->SetBranchAddress(brName.c_str(), add);
          ++j;
        }
      }
    }
    collBuffers->prepareForWrite();
  }
}

void PodioOutput::createBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections) {
  for (const auto& [collName, collBuffers] : collections) {
    auto buffers = collBuffers->getBuffers();
    auto* data = buffers.data;
    auto* references = buffers.references;
    auto* vecmembers = buffers.vectorMembers;

    const std::string className     = collBuffers->getValueTypeName();
    const std::string collClassName = "vector<" + className + "Data>";

    int isOn = 0;
    if (m_switch.isOn(collName)) {
      isOn = 1;
      m_datatree->Branch(collName.c_str(), collClassName.c_str(), data);
      // Create branches for collections holding relations
      if (auto* refColls = references) {
        int j = 0;
        for (auto& c : (*refColls)) {
          const auto brName = podio::root_utils::refBranch(collName, j);
          m_datatree->Branch(brName.c_str(), c.get());
          ++j;
        }
      }
      // vector members
      if (auto* vminfo = vecmembers) {
        int j = 0;
        for (auto& [dataType, add] : (*vminfo)) {
          const std::string typeName = "vector<" + dataType + ">";
          const auto brName          = podio::root_utils::vecBranch(collName, j);
          m_datatree->Branch(brName.c_str(), typeName.c_str(), add);
          ++j;
        }
      }
    }

    const auto collID = m_podioDataSvc->getCollectionIDs()->collectionID(collName);
    const auto collType = collBuffers->getValueTypeName() + "Collection";
    m_collectionInfo.emplace_back(collID, std::move(collType), collBuffers->isSubsetCollection());

    debug() << isOn << " Registering collection " << collClassName << " " << collName.c_str() << " containing type "
            << className << endmsg;
    collBuffers->prepareForWrite();
  }
}

StatusCode PodioOutput::execute() {
  // for now assume identical content for every event
  // register for writing
  if (m_firstEvent) {
    m_collectionInfo.clear();
    createBranches(m_podioDataSvc->getCollections());
    createBranches(m_podioDataSvc->getReadCollections());
    m_metadatatree->Branch("CollectionTypeInfo", &m_collectionInfo);
  } else {
    resetBranches(m_podioDataSvc->getCollections());
    resetBranches(m_podioDataSvc->getReadCollections());
  }
  m_firstEvent = false;
  if (msgLevel(MSG::DEBUG)) {
    debug() << "Filling DataTree .." << endmsg;
  }
  m_datatree->Fill();
  m_evtMDtree->Fill();
  return StatusCode::SUCCESS;
}

/** PodioOutput::finalize
 * has to happen after all algorithms that touch the data store finish.
 * Here the job options are retrieved and stored to disk as a branch
 * in the metadata tree.
 *
 */
StatusCode PodioOutput::finalize() {
  info() << "Finalizing output algorithm" << endmsg;
  if (GaudiAlgorithm::finalize().isFailure()) {
    return StatusCode::FAILURE;
  }
  //// prepare job options metadata ///////////////////////
  // retrieve the configuration of the job
  // and write it to file as vector of strings
  debug() << "Preparing job options metadata" << endmsg;
  std::vector<std::string> config_data;
  const auto& jobOptionsSvc         = Gaudi::svcLocator()->getOptsSvc();
  const auto& configured_properties = jobOptionsSvc.items();
  for (const auto& per_property : configured_properties) {
    std::stringstream config_stream;
    // sample output:
    // HepMCToEDMConverter.genparticles = "GenParticles";
    // Note that quotes are added to all property values,
    // which leads to problems with ints, lists, dicts and bools.
    // For theses types, the quotes must be removed in postprocessing.
    config_stream << std::get<0>(per_property) << " = \"" << std::get<1>(per_property) << "\";" << std::endl;
    config_data.push_back(config_stream.str());
  }
  // Some default components are not captured by the job option service
  // and have to be traversed like this. Note that Gaudi!577 will improve this.
  debug() << "Appending default component metadata" << endmsg;
  for (const auto* name : {"ApplicationMgr", "MessageSvc", "NTupleSvc"}) {
    std::stringstream config_stream;
    auto svc = service<IProperty>(name);
    if (!svc.isValid()) {
      continue;
    }
    for (const auto* property : svc->getProperties()) {
      config_stream << name << "." << property->name() << " = \"" << property->toString() << "\";" << std::endl;
    }
    config_data.push_back(config_stream.str());
  }
  //// finalize trees and file //////////////////////////////
  debug() << "Finalizing trees and output file" << endmsg;
  m_file->cd();
  m_metadatatree->Branch("gaudiConfigOptions", &config_data);
#if (podio_VERSION_MAJOR > 0) || (podio_VERSION_MINOR >= 16)
  const auto collIDTable = m_podioDataSvc->getCollectionIDs().get();
#else
  const auto collIDTable = m_podioDataSvc->getCollectionIDs();
#endif
  m_metadatatree->Branch("CollectionIDs", collIDTable);
  m_metadatatree->Fill();
  m_colMDtree->Branch("colMD", "std::map<int,podio::GenericParameters>", m_podioDataSvc->getProvider().getColMetaDataMap() ) ;
  m_colMDtree->Fill();
  m_runMDtree->Branch("runMD", "std::map<int,podio::GenericParameters>", m_podioDataSvc->getProvider().getRunMetaDataMap() ) ;
  m_runMDtree->Fill();
  m_datatree->Write();
  m_file->Write();
  m_file->Close();
  info() << "Data written to: " << m_filename.value() << endmsg;
  if (!m_filenameRemote.value().empty()) {
    TFile::Cp(m_filename.value().c_str(), m_filenameRemote.value().c_str(), false);
    info() << " and copied to: " << m_filenameRemote.value() << endmsg;
  }
  return StatusCode::SUCCESS;
}