diff --git a/Examples/CMakeLists.txt b/Examples/CMakeLists.txt index 632511346882fbec57421a390eb7eec551814b4d..7db41df710f98228150f5d40ba0decdc44c71c5d 100644 --- a/Examples/CMakeLists.txt +++ b/Examples/CMakeLists.txt @@ -1,10 +1,14 @@ gaudi_subdir(Examples v0r0) +find_package(podio REQUIRED) +find_package(plcio REQUIRED) + set(Examples_srcs src/HelloWorld/*.cpp src/FirstSvc/*.cpp src/SecondAlg/*.cpp + src/PlcioTest/*.cpp ) # Headers and Libraries @@ -13,8 +17,8 @@ gaudi_install_headers(Examples) # Modules gaudi_add_module(Examples ${Examples_srcs} - INCLUDE_DIRS GaudiKernel - LINK_LIBRARIES GaudiKernel + INCLUDE_DIRS GaudiKernel FWCore ${plcio_INCLUDE_DIRS} ${podio_INCLUDE_DIRS} + LINK_LIBRARIES GaudiKernel FWCore ${podio_LIBRARIES} $ENV{PLCIO}/lib/libplcio.so ) # Unit tests @@ -23,3 +27,9 @@ gaudi_add_test(HelloAlg gaudi_add_test(SecondAlg FRAMEWORK options/secondalg.py) + +gaudi_add_test(PlcioWriteAlg + FRAMEWORK options/plcio_write.py) + +gaudi_add_test(PlcioReadAlg + FRAMEWORK options/plcio_read.py) diff --git a/Examples/options/plcio_read.py b/Examples/options/plcio_read.py new file mode 100644 index 0000000000000000000000000000000000000000..1ba37f81b99e83b4802b79b605f027cbcb2eab1e --- /dev/null +++ b/Examples/options/plcio_read.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from Gaudi.Configuration import * + +from Configurables import CEPCDataSvc +dsvc = CEPCDataSvc("EventDataSvc", input="test.root") + +from Configurables import PlcioReadAlg +alg = PlcioReadAlg("PlcioReadAlg") + +from Configurables import PodioInput +podioinput = PodioInput("PodioReader", collections=[ + "MCParticleCol" + ]) + +# ApplicationMgr +from Configurables import ApplicationMgr +ApplicationMgr( TopAlg = [podioinput, alg], + EvtSel = 'NONE', + EvtMax = 10, + ExtSvc = [dsvc], + OutputLevel=DEBUG +) diff --git a/Examples/options/plcio_write.py b/Examples/options/plcio_write.py new file mode 100644 index 0000000000000000000000000000000000000000..49c02d77882edc85300a9d138ab1f44a7fc889f7 --- /dev/null +++ b/Examples/options/plcio_write.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +from Gaudi.Configuration import * + +from Configurables import CEPCDataSvc +dsvc = CEPCDataSvc("EventDataSvc") + +from Configurables import PlcioWriteAlg +alg = PlcioWriteAlg("PlcioWriteAlg") + +from Configurables import PodioOutput +out = PodioOutput("out") +out.filename = "test.root" +out.outputCommands = ["keep *"] + +# ApplicationMgr +from Configurables import ApplicationMgr +ApplicationMgr( TopAlg = [alg, out], + EvtSel = 'NONE', + EvtMax = 10, + ExtSvc=[dsvc], + OutputLevel=DEBUG +) diff --git a/Examples/src/PlcioTest/PlcioReadAlg.cpp b/Examples/src/PlcioTest/PlcioReadAlg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..224c7722d6fbf02a8a3931884d4320c6ccca9c16 --- /dev/null +++ b/Examples/src/PlcioTest/PlcioReadAlg.cpp @@ -0,0 +1,39 @@ +#include "PlcioReadAlg.h" +#include "plcio/MCParticleCollection.h" + +DECLARE_COMPONENT(PlcioReadAlg) + +PlcioReadAlg::PlcioReadAlg(const std::string& name, ISvcLocator* svcLoc) + : GaudiAlgorithm(name, svcLoc) +{ + declareProperty("MCParticleCol", m_hdl, "MCParticle collection (input)"); +} + +StatusCode PlcioReadAlg::initialize() +{ + debug() << "begin initialize PlcioReadAlg" << endmsg; + return GaudiAlgorithm::initialize(); +} + +StatusCode PlcioReadAlg::execute() +{ + debug() << "begin execute PlcioReadAlg" << endmsg; + auto mcCol = m_hdl.get(); + + for ( auto p : *mcCol ) { + debug() << p.getObjectID().index << " : ["; + for ( auto it = p.daughters_begin(), end = p.daughters_end(); it != end; ++it ) { + debug() << " " << it->getObjectID().index; + } + debug() << " ]; "; + } + debug() << endmsg; + + return StatusCode::SUCCESS; +} + +StatusCode PlcioReadAlg::finalize() +{ + debug() << "begin finalize PlcioReadAlg" << endmsg; + return GaudiAlgorithm::finalize(); +} diff --git a/Examples/src/PlcioTest/PlcioReadAlg.h b/Examples/src/PlcioTest/PlcioReadAlg.h new file mode 100644 index 0000000000000000000000000000000000000000..b4d58c29c77c3c8aa4c715f19275f3fd5c8e4736 --- /dev/null +++ b/Examples/src/PlcioTest/PlcioReadAlg.h @@ -0,0 +1,29 @@ +#ifndef TEST_PLCIO_WRITE_ALG_H +#define TEST_PLCIO_WRITE_ALG_H + +#include "FWCore/DataHandle.h" +#include "GaudiAlg/GaudiAlgorithm.h" + +namespace plcio { + class MCParticleCollection; +} + +class PlcioReadAlg : public GaudiAlgorithm +{ + friend class AlgFactory<PlcioReadAlg>; + + public : + + PlcioReadAlg(const std::string& name, ISvcLocator* svcLoc); + + virtual StatusCode initialize(); + virtual StatusCode execute(); + virtual StatusCode finalize(); + + private : + + DataHandle<plcio::MCParticleCollection> m_hdl{"MCParticleCol", Gaudi::DataHandle::Reader, this}; + +}; + +#endif // TEST_PLCIO_WRITE_ALG_H diff --git a/Examples/src/PlcioTest/PlcioWriteAlg.cpp b/Examples/src/PlcioTest/PlcioWriteAlg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..91b54a87fb36a9bd50915f943124f5a9b0fbfde7 --- /dev/null +++ b/Examples/src/PlcioTest/PlcioWriteAlg.cpp @@ -0,0 +1,44 @@ +#include "PlcioWriteAlg.h" +#include "plcio/MCParticleCollection.h" + +DECLARE_COMPONENT(PlcioWriteAlg) + +PlcioWriteAlg::PlcioWriteAlg(const std::string& name, ISvcLocator* svcLoc) + : GaudiAlgorithm(name, svcLoc) +{ + declareProperty("MCParticleCol", m_hdl, "MCParticle collection (output)"); +} + +StatusCode PlcioWriteAlg::initialize() +{ + debug() << "begin initialize PlcioWriteAlg" << endmsg; + return GaudiAlgorithm::initialize(); +} + +StatusCode PlcioWriteAlg::execute() +{ + debug() << "begin execute PlcioWriteAlg" << endmsg; + + auto mcCol = new plcio::MCParticleCollection; + + auto p1 = mcCol->create(); + auto p2 = mcCol->create(); + + for ( int i = 0; i < 4; ++i ) { + auto d = mcCol->create(); + d.addParent(p1); + d.addParent(p2); + p1.addDaughter(d); + p2.addDaughter(d); + } + + m_hdl.put(mcCol); + + return StatusCode::SUCCESS; +} + +StatusCode PlcioWriteAlg::finalize() +{ + debug() << "begin finalize PlcioWriteAlg" << endmsg; + return GaudiAlgorithm::finalize(); +} diff --git a/Examples/src/PlcioTest/PlcioWriteAlg.h b/Examples/src/PlcioTest/PlcioWriteAlg.h new file mode 100644 index 0000000000000000000000000000000000000000..053e49c986242acefbfcac943239505e69e2ffac --- /dev/null +++ b/Examples/src/PlcioTest/PlcioWriteAlg.h @@ -0,0 +1,29 @@ +#ifndef TEST_PLCIO_WRITE_ALG_H +#define TEST_PLCIO_WRITE_ALG_H + +#include "FWCore/DataHandle.h" +#include "GaudiAlg/GaudiAlgorithm.h" + +namespace plcio { + class MCParticleCollection; +} + +class PlcioWriteAlg : public GaudiAlgorithm +{ + friend class AlgFactory<PlcioWriteAlg>; + + public : + + PlcioWriteAlg(const std::string& name, ISvcLocator* svcLoc); + + virtual StatusCode initialize(); + virtual StatusCode execute(); + virtual StatusCode finalize(); + + private : + + DataHandle<plcio::MCParticleCollection> m_hdl{"MCParticleCol", Gaudi::DataHandle::Writer, this}; + +}; + +#endif // TEST_PLCIO_WRITE_ALG_H diff --git a/FWCore/CMakeLists.txt b/FWCore/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..07f309c5651a43f8fbaf587bd423d726d869cd31 --- /dev/null +++ b/FWCore/CMakeLists.txt @@ -0,0 +1,23 @@ +################################################################################ +# Package: FWCore +################################################################################ +gaudi_subdir(FWCore v0r1) + +find_package(podio REQUIRED) +find_package(ROOT COMPONENTS RIO Tree) + +# this declaration will not be needed in the future +gaudi_depends_on_subdirs(GaudiAlg GaudiKernel) +gaudi_install_scripts() +gaudi_install_python_modules() + +gaudi_add_library(FWCore + src/*.cpp + INCLUDE_DIRS ${podio_INCLUDE_DIRS} ROOT + LINK_LIBRARIES GaudiAlgLib GaudiKernel ${podio_LIBRARIES} ROOT + PUBLIC_HEADERS FWCore) + +gaudi_add_module(FWCorePlugins + src/components/*.cpp + LINK_LIBRARIES GaudiAlgLib GaudiKernel FWCore ROOT) + diff --git a/FWCore/FWCore/DataHandle.h b/FWCore/FWCore/DataHandle.h new file mode 100644 index 0000000000000000000000000000000000000000..642d082380a708cbe88a2becbf74fc98418a47a9 --- /dev/null +++ b/FWCore/FWCore/DataHandle.h @@ -0,0 +1,157 @@ +#ifndef FWCORE_DATAHANDLE_H +#define FWCORE_DATAHANDLE_H + +#include "FWCore/DataWrapper.h" +#include "FWCore/PodioDataSvc.h" + +#include "GaudiKernel/AlgTool.h" +#include "GaudiKernel/Algorithm.h" +#include <GaudiKernel/DataObjectHandle.h> +#include <GaudiKernel/GaudiException.h> +#include <GaudiKernel/Property.h> +#include <GaudiKernel/ServiceLocatorHelper.h> + +#include "TTree.h" + +#include <type_traits> + +template <typename T> +class DataHandle : public DataObjectHandle<DataWrapper<T>> { + +public: + friend class Algorithm; + friend class AlgTool; + +public: + DataHandle(); + + /// Initialises mother class + DataHandle(DataObjID& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg); + + DataHandle(const std::string& k, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg); + /** + * Retrieve object from transient data store + */ + const T* get(); + + /** + * Register object in transient store + */ + void put(T* object); + + /** + * Create and register object in transient store + */ + T* createAndPut(); + +private: + ServiceHandle<IDataProviderSvc> m_eds; + bool m_isGoodType{false}; + bool m_isCollection{false}; + T* m_dataPtr; +}; + +//--------------------------------------------------------------------------- +template <typename T> +DataHandle<T>::DataHandle(DataObjID& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg) + : DataObjectHandle<DataWrapper<T>>(descriptor, a, fatherAlg), m_eds("EventDataSvc", "DataHandle") { + +} +//--------------------------------------------------------------------------- +template <typename T> +DataHandle<T>::DataHandle(const std::string& descriptor, Gaudi::DataHandle::Mode a, IDataHandleHolder* fatherAlg) + : DataObjectHandle<DataWrapper<T>>(descriptor, a, fatherAlg), m_eds("EventDataSvc", "DataHandle") { + + if (a > 15) { // Gaudi::DataHandle::Mode is 'writer' + m_eds.retrieve(); + PodioDataSvc* pds; + pds = dynamic_cast<PodioDataSvc*>( m_eds.get()); + m_dataPtr = 0; + if (nullptr != pds) { + if (std::is_convertible<T*,podio::CollectionBase*>::value) { + // still handled in PodioOutput + } else { + TTree* tree = pds->eventDataTree(); + tree->Branch(descriptor.c_str(), &m_dataPtr); + } + } + } +} + +/** + * Try to retrieve from the transient store. If the retrieval succeded and + * this is the first time we retrieve, perform a dynamic cast to the desired + * object. Then finally set the handle as Read. + * If this is not the first time we cast and the cast worked, just use the + * static cast: we do not need the checks of the dynamic cast for every access! + */ +template <typename T> +const T* DataHandle<T>::get() { + DataObject* dataObjectp = nullptr; + auto sc = m_eds->retrieveObject(DataObjectHandle<DataWrapper<T>>::fullKey().key(), dataObjectp); + + if (LIKELY(sc.isSuccess())) { + if (UNLIKELY(!m_isGoodType && !m_isCollection)) { + // only do this once (if both are false after this, we throw exception) + m_isGoodType = nullptr != dynamic_cast<DataWrapper<T>*>(dataObjectp); + if (!m_isGoodType) { + auto tmp = dynamic_cast<DataWrapper<podio::CollectionBase>*>(dataObjectp); + if (tmp != nullptr) { + m_isCollection = nullptr != dynamic_cast<T*>(tmp->collectionBase()); + } + } + } + if (LIKELY(m_isGoodType)) { + DataObjectHandle<DataWrapper<T>>::setRead(); + return static_cast<DataWrapper<T>*>(dataObjectp)->getData(); + } else if (m_isCollection) { + // The reader does not know the specific type of the collection. So we need a reinterpret_cast if the handle was + // created by the reader. + DataWrapper<podio::CollectionBase>* tmp = static_cast<DataWrapper<podio::CollectionBase>*>(dataObjectp); + DataObjectHandle<DataWrapper<T>>::setRead(); + return reinterpret_cast<const T*>(tmp->collectionBase()); + } else { + std::string errorMsg("The type provided for " + DataObjectHandle<DataWrapper<T>>::toString() + + " is different from the one of the object in the store."); + throw GaudiException(errorMsg, "wrong product type", StatusCode::FAILURE); + } + } + std::string msg("Could not retrieve product " + DataObjectHandle<DataWrapper<T>>::toString()); + throw GaudiException(msg, "wrong product name", StatusCode::FAILURE); +} + +//--------------------------------------------------------------------------- +template <typename T> +void DataHandle<T>::put(T* objectp) { + DataWrapper<T>* dw = new DataWrapper<T>(); + m_dataPtr = objectp; + dw->setData(objectp); + DataObjectHandle<DataWrapper<T>>::put(dw); + +} +//--------------------------------------------------------------------------- +/** + * Create the collection, put it in the DataObjectHandle and return the + * pointer to the data. Call this function if you create a collection and + * want to save it. + */ +template <typename T> +T* DataHandle<T>::createAndPut() { + T* objectp = new T(); + this->put(objectp); + return objectp; +} + +// temporary to allow property declaration +namespace Gaudi { +template <class T> +class Property<::DataHandle<T>&> : public ::DataObjectHandleProperty { +public: + Property(const std::string& name, ::DataHandle<T>& value) : ::DataObjectHandleProperty(name, value) {} + + /// virtual Destructor + virtual ~Property() {} +}; +} + +#endif diff --git a/FWCore/FWCore/DataWrapper.h b/FWCore/FWCore/DataWrapper.h new file mode 100644 index 0000000000000000000000000000000000000000..a6ec7a48d1628767e94b527a875495ad720426c3 --- /dev/null +++ b/FWCore/FWCore/DataWrapper.h @@ -0,0 +1,46 @@ +#ifndef FWCORE_DATAWRAPPER_H +#define FWCORE_DATAWRAPPER_H + +#include <type_traits> + +// Include files +#include "GaudiKernel/DataObject.h" +#include "podio/CollectionBase.h" + +class GAUDI_API DataWrapperBase : public DataObject { +public: + // ugly hack to circumvent the usage of boost::any yet + // DataSvc would need a templated register method + virtual podio::CollectionBase* collectionBase() = 0; + virtual ~DataWrapperBase(){}; +}; + +template <class T> +class GAUDI_API DataWrapper : public DataWrapperBase { +public: + DataWrapper() : DataWrapperBase(), m_data(nullptr){}; + virtual ~DataWrapper(); + + const T* getData() { return m_data; } + void setData(T* data) { m_data = data; } + /// try to cast to collectionBase; may return nullptr; + virtual podio::CollectionBase* collectionBase(); + +private: + T* m_data; +}; + +template <class T> +DataWrapper<T>::~DataWrapper<T>() { + if (m_data != nullptr) delete m_data; +} + +template <class T> +podio::CollectionBase* DataWrapper<T>::collectionBase() { + if (std::is_base_of<podio::CollectionBase, T>::value) { + return reinterpret_cast<podio::CollectionBase*>(m_data); + } + return nullptr; +} + +#endif diff --git a/FWCore/FWCore/KeepDropSwitch.h b/FWCore/FWCore/KeepDropSwitch.h new file mode 100644 index 0000000000000000000000000000000000000000..d4bd4c0bc2d7525056b18d6f34914a7faa7a488e --- /dev/null +++ b/FWCore/FWCore/KeepDropSwitch.h @@ -0,0 +1,27 @@ +#ifndef EXAMPLES_KEEPDROPSWITCH_H +#define EXAMPLES_KEEPDROPSWITCH_H + +#include <map> +#include <string> +#include <vector> + +std::vector<std::string> split(const std::string& s, char delim); + +int wildcmp(const char* wild, const char* string); + +class KeepDropSwitch { +public: + enum Cmd { KEEP, DROP, UNKNOWN }; + typedef std::vector<std::string> CommandLines; + KeepDropSwitch() {} + explicit KeepDropSwitch(const CommandLines& cmds) { m_commandlines = cmds; } + bool isOn(const std::string& astring) const; + +private: + bool getFlag(const std::string& astring) const; + Cmd extractCommand(const std::string cmdLine) const; + CommandLines m_commandlines; + mutable std::map<std::string, bool> m_cache; +}; + +#endif diff --git a/FWCore/FWCore/PodioDataSvc.h b/FWCore/FWCore/PodioDataSvc.h new file mode 100644 index 0000000000000000000000000000000000000000..09194a30c404263f1b06f4457eafb02282a1e399 --- /dev/null +++ b/FWCore/FWCore/PodioDataSvc.h @@ -0,0 +1,83 @@ +#ifndef FWCORE_PODIODATASVC_H +#define FWCORE_PODIODATASVC_H + +#include "GaudiKernel/DataSvc.h" +#include "GaudiKernel/IConversionSvc.h" +// PODIO +#include "podio/CollectionBase.h" +#include "podio/CollectionIDTable.h" +#include "podio/EventStore.h" +#include "podio/ROOTReader.h" + +#include <utility> +// Forward declarations + +/** @class PodioEvtSvc EvtDataSvc.h + * + * An EvtDataSvc for PODIO classes + * + * @author B. Hegner + */ +class PodioDataSvc : public DataSvc { +public: + typedef std::vector<std::pair<std::string, podio::CollectionBase*>> CollRegistry; + + virtual StatusCode initialize(); + virtual StatusCode reinitialize(); + virtual StatusCode finalize(); + virtual StatusCode clearStore(); + + /// Standard Constructor + PodioDataSvc(const std::string& name, ISvcLocator* svc); + + /// Standard Destructor + virtual ~PodioDataSvc(); + + // Use DataSvc functionality except where we override + using DataSvc::registerObject; + /// Overriding standard behaviour of evt service + /// Register object with the data store. + virtual StatusCode registerObject(const std::string& fullPath, DataObject* pObject) final; + + StatusCode readCollection(const std::string& collectionName, int collectionID); + + virtual const CollRegistry& getCollections() const { return m_collections; } + virtual const CollRegistry& getReadCollections() const { return m_readCollections; } + virtual podio::CollectionIDTable* getCollectionIDs() { return m_collectionIDs; } + + /// Set the collection IDs (if reading a file) + void setCollectionIDs(podio::CollectionIDTable* collectionIds); + /// Resets caches of reader and event store, increases event counter + void endOfRead(); + + + TTree* eventDataTree() {return m_eventDataTree;} + + +private: + + // eventDataTree + TTree* m_eventDataTree; + /// PODIO reader for ROOT files + podio::ROOTReader m_reader; + /// PODIO EventStore, used to initialise collections + podio::EventStore m_provider; + /// Counter of the event number + int m_eventNum{0}; + /// Number of events in the file / to process + int m_eventMax{-1}; + + + SmartIF<IConversionSvc> m_cnvSvc; + + // special members for podio handling + std::vector<std::pair<std::string, podio::CollectionBase*>> m_collections; + std::vector<std::pair<std::string, podio::CollectionBase*>> m_readCollections; + podio::CollectionIDTable* m_collectionIDs; + +protected: + /// ROOT file name the input is read from. Set by option filename + std::vector<std::string> m_filenames; + std::string m_filename; +}; +#endif // CORE_PODIODATASVC_H diff --git a/FWCore/python/FWCore/__init__.py b/FWCore/python/FWCore/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/FWCore/python/FWCore/dump_joboptions.py b/FWCore/python/FWCore/dump_joboptions.py new file mode 100644 index 0000000000000000000000000000000000000000..eaf71c9aa02ca604f7c25c2777ed3f9e7cd00246 --- /dev/null +++ b/FWCore/python/FWCore/dump_joboptions.py @@ -0,0 +1,30 @@ +from __future__ import print_function +import argparse +import ROOT + +parser = argparse.ArgumentParser(description='Job Options Dumper') +parser.add_argument( dest='fname', type=str, help="name of file to read") + + +def dump_joboptions(filename): + """ Simple and straightforward dump of the output of Gaudi's JobOptionsSvc + to stdout. + + Parameters + ---------- + + filename: str + The name of the CEPCSW output file containing joboptions to print + """ + f = ROOT.TFile(filename) + t = f.metadata + for event in t: + s = event.gaudiConfigOptions + for e in s: + print(e) + +if __name__ == "__main__": + args = parser.parse_args() + dump_joboptions(args.fname) + + diff --git a/FWCore/python/FWCore/joboptions.py b/FWCore/python/FWCore/joboptions.py new file mode 100644 index 0000000000000000000000000000000000000000..47f8173d837c452551803be2690acb6d5e513ff2 --- /dev/null +++ b/FWCore/python/FWCore/joboptions.py @@ -0,0 +1,15 @@ +import argparse + +def parse_standard_job_options(scriptname=""): + """ + Returns the parsed arguments, adding a parser with commonly needed opts: + - args.nevents -- number of events (int), specify with --nevents + - args.inputfile -- the input file (string), specify with --inputfile + - args.outputfile -- the output file (string), specify with --outputfile + """ + parser = argparse.ArgumentParser() + parser.add_argument('--inputfile', type=str, default='', help='specify an input file') + parser.add_argument('--outputfile', type=str, default='', help='specify an output file') + parser.add_argument('--nevents', type=int, default=None, help='specify number of events to process') + args, _ = parser.parse_known_args() + return args diff --git a/FWCore/src/KeepDropSwitch.cpp b/FWCore/src/KeepDropSwitch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6617c03208e39556e2594ebe48d7bf0c29796b8b --- /dev/null +++ b/FWCore/src/KeepDropSwitch.cpp @@ -0,0 +1,100 @@ +#include "FWCore/KeepDropSwitch.h" + +#include <iostream> +#include <sstream> +#include <stdexcept> + +int wildcmp(const char* wild, const char* string) { + // Written by Jack Handy - <A href="mailto:jakkhandy@hotmail.com">jakkhandy@hotmail.com</A> + const char *cp = nullptr, *mp = nullptr; + while ((*string) && (*wild != '*')) { + if ((*wild != *string) && (*wild != '?')) { + return 0; + } + wild++; + string++; + } + while (*string) { + if (*wild == '*') { + if (!*++wild) { + return 1; + } + mp = wild; + cp = string + 1; + } else if ((*wild == *string) || (*wild == '?')) { + wild++; + string++; + } else { + wild = mp; + string = cp++; + } + } + while (*wild == '*') { + wild++; + } + return !*wild; +} + +std::vector<std::string> split(const std::string& s, char delim) { + std::vector<std::string> elems; + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + if (item != "") elems.push_back(item); + } + return elems; +} + +bool KeepDropSwitch::isOn(const std::string& astring) const { + typedef std::map<std::string, bool>::const_iterator MIter; + MIter im = m_cache.find(astring); + if (im != m_cache.end()) + return im->second; + else { + bool val = getFlag(astring); + m_cache.insert(std::pair<std::string, bool>(astring, val)); + return val; + } +} + +bool KeepDropSwitch::getFlag(const std::string& astring) const { + bool flag = true; + for (const auto& cmdline : m_commandlines) { + std::vector<std::string> words = split(cmdline, ' '); + if (words.size() != 2) { + std::ostringstream msg; + msg << "malformed command string : " << cmdline; + throw std::invalid_argument(msg.str()); + } + std::string cmd = words[0]; + std::string pattern = words[1]; + Cmd theCmd = UNKNOWN; + if (cmd == "keep") + theCmd = KEEP; + else if (cmd == "drop") + theCmd = DROP; + else { + std::ostringstream msg; + msg << "malformed command in line: " << std::endl; + msg << cmdline << std::endl; + msg << "should be keep or drop, lower case" << std::endl; + throw std::invalid_argument(msg.str()); + } + bool match = wildcmp(pattern.c_str(), astring.c_str()); + if (not match) + continue; + else if (theCmd == KEEP) + flag = true; + else + flag = false; + } + return flag; +} + +KeepDropSwitch::Cmd KeepDropSwitch::extractCommand(const std::string cmdline) const { + std::vector<std::string> words = split(cmdline, ' '); + for (auto& word : words) + std::cout << "'" << word << "' "; + std::cout << std::endl; + return UNKNOWN; +} diff --git a/FWCore/src/PodioDataSvc.cpp b/FWCore/src/PodioDataSvc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77da2e3644cca8be8d3d208ba78c665baef828fc --- /dev/null +++ b/FWCore/src/PodioDataSvc.cpp @@ -0,0 +1,120 @@ +#include "FWCore/PodioDataSvc.h" +#include "GaudiKernel/IConversionSvc.h" +#include "GaudiKernel/IEventProcessor.h" +#include "GaudiKernel/ISvcLocator.h" + +#include "FWCore/DataWrapper.h" + +#include "TTree.h" + +/// Service initialisation +StatusCode PodioDataSvc::initialize() { + // Nothing to do: just call base class initialisation + StatusCode status = DataSvc::initialize(); + ISvcLocator* svc_loc = serviceLocator(); + + + // Attach data loader facility + m_cnvSvc = svc_loc->service("EventPersistencySvc"); + status = setDataLoader(m_cnvSvc); + + if (m_filename != "") { + m_filenames.push_back(m_filename); + } + + if (m_filenames.size() > 0) { + if (m_filenames[0] != "") { + m_reader.openFiles(m_filenames); + m_eventMax = m_reader.getEntries(); + auto idTable = m_reader.getCollectionIDTable(); + + setCollectionIDs(idTable); + m_provider.setReader(&m_reader); + } + } + return status; +} +/// Service reinitialisation +StatusCode PodioDataSvc::reinitialize() { + // Do nothing for this service + return StatusCode::SUCCESS; +} +/// Service finalization +StatusCode PodioDataSvc::finalize() { + m_cnvSvc = 0; // release + DataSvc::finalize().ignore(); + return StatusCode::SUCCESS; +} + +StatusCode PodioDataSvc::clearStore() { + for (auto& collNamePair : m_collections) { + if (collNamePair.second != nullptr) { + collNamePair.second->clear(); + } + } + for (auto& collNamePair : m_readCollections) { + if (collNamePair.second != nullptr) { + collNamePair.second->clear(); + } + } + DataSvc::clearStore().ignore(); + m_collections.clear(); + m_readCollections.clear(); + return StatusCode::SUCCESS; +} + +void PodioDataSvc::endOfRead() { + if (m_eventMax != -1) { + m_provider.clearCaches(); + m_reader.endOfEvent(); + if (m_eventNum++ > m_eventMax) { + info() << "Reached end of file with event " << m_eventMax << endmsg; + IEventProcessor* eventProcessor; + service("ApplicationMgr", eventProcessor); + eventProcessor->stopRun(); + } + } +} + +void PodioDataSvc::setCollectionIDs(podio::CollectionIDTable* collectionIds) { + if (m_collectionIDs != nullptr) { + delete m_collectionIDs; + } + m_collectionIDs = collectionIds; +} + +/// Standard Constructor +PodioDataSvc::PodioDataSvc(const std::string& name, ISvcLocator* svc) + : DataSvc(name, svc), m_collectionIDs(new podio::CollectionIDTable()) { + + m_eventDataTree = new TTree("events", "Events tree"); + } + +/// Standard Destructor +PodioDataSvc::~PodioDataSvc() {} + +StatusCode PodioDataSvc::readCollection(const std::string& collName, int collectionID) { + podio::CollectionBase* collection(nullptr); + m_provider.get(collectionID, collection); + auto wrapper = new DataWrapper<podio::CollectionBase>; + int id = m_collectionIDs->add(collName); + collection->setID(id); + wrapper->setData(collection); + m_readCollections.emplace_back(std::make_pair(collName, collection)); + return DataSvc::registerObject(collName, wrapper); +} + +StatusCode PodioDataSvc::registerObject(const std::string& fullPath, DataObject* pObject) { + DataWrapperBase* wrapper = dynamic_cast<DataWrapperBase*>(pObject); + if (wrapper != nullptr) { + podio::CollectionBase* coll = wrapper->collectionBase(); + if (coll != nullptr) { + size_t pos = fullPath.find_last_of("/"); + std::string shortPath(fullPath.substr(pos + 1, fullPath.length())); + int id = m_collectionIDs->add(shortPath); + coll->setID(id); + m_collections.emplace_back(std::make_pair(shortPath, coll)); + } + } + return DataSvc::registerObject(fullPath, pObject); +} diff --git a/FWCore/src/components/CEPCDataSvc.cpp b/FWCore/src/components/CEPCDataSvc.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa870478432eaf570360b8bceb73406cf2e66692 --- /dev/null +++ b/FWCore/src/components/CEPCDataSvc.cpp @@ -0,0 +1,20 @@ +#include "CEPCDataSvc.h" + +#include "GaudiKernel/IConversionSvc.h" +#include "GaudiKernel/ISvcLocator.h" +#include "GaudiKernel/SvcFactory.h" + +// Instantiation of a static factory class used by clients to create +// instances of this service +DECLARE_SERVICE_FACTORY(CEPCDataSvc) + +/// Standard Constructor +CEPCDataSvc::CEPCDataSvc(const std::string& name, ISvcLocator* svc) + : PodioDataSvc(name, svc) +{ + declareProperty("inputs", m_filenames = {}, "Names of the files to read"); + declareProperty("input", m_filename = "", "Name of the file to read"); +} + +/// Standard Destructor +CEPCDataSvc::~CEPCDataSvc() {} diff --git a/FWCore/src/components/CEPCDataSvc.h b/FWCore/src/components/CEPCDataSvc.h new file mode 100644 index 0000000000000000000000000000000000000000..4953799c435618413978cb309abbda04ae78e83d --- /dev/null +++ b/FWCore/src/components/CEPCDataSvc.h @@ -0,0 +1,18 @@ +#ifndef FWCORE_CEPCDATASVC_H +#define FWCORE_CEPCDATASVC_H + +#include "FWCore/PodioDataSvc.h" + +class CEPCDataSvc : public PodioDataSvc +{ + friend class SvcFactory<CEPCDataSvc>; + +public: + /// Standard Constructor + CEPCDataSvc(const std::string& name, ISvcLocator* svc); + + /// Standard Destructor + virtual ~CEPCDataSvc(); +}; + +#endif // FWCORE_CEPCDATASVC_H diff --git a/FWCore/src/components/PodioInput.cpp b/FWCore/src/components/PodioInput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f031744cdbe8c27728eeed3356cc8bc030fcf9d5 --- /dev/null +++ b/FWCore/src/components/PodioInput.cpp @@ -0,0 +1,50 @@ +#include "PodioInput.h" + +#include "TFile.h" +#include "TROOT.h" + +#include "FWCore/DataWrapper.h" +#include "FWCore/PodioDataSvc.h" + +DECLARE_COMPONENT(PodioInput) + +PodioInput::PodioInput(const std::string& name, ISvcLocator* svcLoc) : GaudiAlgorithm(name, svcLoc) {} + +StatusCode PodioInput::initialize() { + if (GaudiAlgorithm::initialize().isFailure()) return StatusCode::FAILURE; + + // check whether we have the PodioEvtSvc active + m_podioDataSvc = dynamic_cast<PodioDataSvc*>(evtSvc().get()); + if (nullptr == m_podioDataSvc) return StatusCode::FAILURE; + + auto idTable = m_podioDataSvc->getCollectionIDs(); + for (auto& name : m_collectionNames) { + debug() << "Finding collection " << name << " in collection registry." << endmsg; + if (!idTable->present(name)) { + error() << "Requested product " << name << " not found." << endmsg; + return StatusCode::FAILURE; + } + m_collectionIDs.push_back(idTable->collectionID(name)); + } + return StatusCode::SUCCESS; +} + +StatusCode PodioInput::execute() { + size_t cntr = 0; + // Re-create the collections from ROOT file + for (auto& id : m_collectionIDs) { + const std::string& collName = m_collectionNames.value().at(cntr++); + debug() << "Registering collection to read " << collName << " with id " << id << endmsg; + if (m_podioDataSvc->readCollection(collName, id).isFailure()) { + return StatusCode::FAILURE; + } + } + // Tell data service that we are done with requested collections + m_podioDataSvc->endOfRead(); + return StatusCode::SUCCESS; +} + +StatusCode PodioInput::finalize() { + if (GaudiAlgorithm::finalize().isFailure()) return StatusCode::FAILURE; + return StatusCode::SUCCESS; +} diff --git a/FWCore/src/components/PodioInput.h b/FWCore/src/components/PodioInput.h new file mode 100644 index 0000000000000000000000000000000000000000..0319d78f63a715edd5bd42d5e08823328ed759f2 --- /dev/null +++ b/FWCore/src/components/PodioInput.h @@ -0,0 +1,43 @@ +#ifndef FWCORE_PODIOINPUT_H +#define FWCORE_PODIOINPUT_H +// Gaaudi +#include "GaudiAlg/GaudiAlgorithm.h" + +// STL +#include <string> +#include <vector> + +// forward declarations +// from FWCore: +class PodioDataSvc; + +/** @class PodioInput FWCore/components/PodioInput.h PodioInput.h + * + * Class that allows to read ROOT files written with PodioOutput + * + * @author J. Lingemann + */ + +class PodioInput : public GaudiAlgorithm { + friend class AlgFactory<PodioInput>; + +public: + /// Constructor. + PodioInput(const std::string& name, ISvcLocator* svcLoc); + /// Initialization of PodioInput. Acquires the data service, opens root file and creates trees. + virtual StatusCode initialize(); + /// Execute. Re-creates collections that are specified to be read and sets references. + virtual StatusCode execute(); + /// Finalize. Closes ROOT file. + virtual StatusCode finalize(); + +private: + /// Name of collections to read. Set by option collections (this is temporary) + Gaudi::Property<std::vector<std::string>> m_collectionNames{this, "collections", {}, "Places of collections to read"}; + /// Collection IDs (retrieved with CollectionIDTable from ROOT file, using collection names) + std::vector<int> m_collectionIDs; + /// Data service: needed to register objects and get collection IDs. Just an observing pointer. + PodioDataSvc* m_podioDataSvc; +}; + +#endif diff --git a/FWCore/src/components/PodioOutput.cpp b/FWCore/src/components/PodioOutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..6c531ad6e95ff410ed408ba3482fcaf6aef38047 --- /dev/null +++ b/FWCore/src/components/PodioOutput.cpp @@ -0,0 +1,133 @@ +#include "PodioOutput.h" +#include "GaudiKernel/IJobOptionsSvc.h" +#include "FWCore/PodioDataSvc.h" +#include "TFile.h" + +DECLARE_COMPONENT(PodioOutput) + +PodioOutput::PodioOutput(const std::string& name, ISvcLocator* svcLoc) + : GaudiAlgorithm(name, svcLoc), m_firstEvent(true) {} + +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 (0 == m_podioDataSvc) return StatusCode::FAILURE; + + m_file = std::unique_ptr<TFile>(new TFile(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_metadatatree = new TTree("metadata", "Metadata tree"); + m_switch = KeepDropSwitch(m_outputCommands); + return StatusCode::SUCCESS; +} + +void PodioOutput::resetBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, + bool prepare) { + for (auto& collNamePair : collections) { + auto collName = collNamePair.first; + if (m_switch.isOn(collName)) { + // Reconnect branches and collections + m_datatree->SetBranchAddress(collName.c_str(), collNamePair.second->getBufferAddress()); + auto colls = collNamePair.second->referenceCollections(); + if (colls != nullptr) { + int j = 0; + for (auto& c : (*colls)) { + m_datatree->SetBranchAddress((collName + "#" + std::to_string(j)).c_str(), &c); + ++j; + } + } + } + if (prepare) { + collNamePair.second->prepareForWrite(); + } + } +} + +void PodioOutput::createBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, + bool prepare) { + for (auto& collNamePair : collections) { + auto collName = collNamePair.first; + // TODO: we need the class name in a better way + std::string className(typeid(*(collNamePair.second)).name()); + size_t pos = className.find_first_not_of("0123456789"); + className.erase(0, pos); + // demangling the namespace: due to namespace additional characters were introduced: + // e.g. N3fcc18TrackHit + // remove any number+char before the namespace: + pos = className.find_first_of("0123456789"); + size_t pos1 = className.find_first_not_of("0123456789", pos); + className.erase(0, pos1); + // replace any numbers between namespace and class with "::" + pos = className.find_first_of("0123456789"); + pos1 = className.find_first_not_of("0123456789", pos); + className.replace(pos, pos1 - pos, "::"); + + pos = className.find("Collection"); + className.erase(pos, pos + 10); + 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(), collNamePair.second->getBufferAddress()); + // Create branches for collections holding relations + auto colls = collNamePair.second->referenceCollections(); + if (colls != nullptr) { + int j = 0; + for (auto& c : (*colls)) { + m_datatree->Branch((collName + "#" + std::to_string(j)).c_str(), c); + ++j; + } + } + } + debug() << isOn << " Registering collection " << collClassName << " " << collName.c_str() << " containing type " + << className << endmsg; + if (prepare) { + collNamePair.second->prepareForWrite(); + } + } +} + +StatusCode PodioOutput::execute() { + // for now assume identical content for every event + // register for writing + if (m_firstEvent) { + createBranches(m_podioDataSvc->getCollections(), true); + createBranches(m_podioDataSvc->getReadCollections(), false); + } else { + resetBranches(m_podioDataSvc->getCollections(), true); + resetBranches(m_podioDataSvc->getReadCollections(), false); + } + m_firstEvent = false; + debug() << "Filling DataTree .." << endmsg; + m_datatree->Fill(); + return StatusCode::SUCCESS; +} + +StatusCode PodioOutput::finalize() { + if (GaudiAlgorithm::finalize().isFailure()) return StatusCode::FAILURE; + // retrieve the configuration of the job + // and write it to file as vector of strings + std::vector<std::string> config_data; + auto jobOptionsSvc = service<IJobOptionsSvc>("JobOptionsSvc"); + auto configured_components = jobOptionsSvc->getClients(); + for (const auto& name : configured_components) { + auto properties = jobOptionsSvc->getProperties(name); + std::stringstream config_stream; + for (const auto& property : *properties) { + config_stream << name << " : " << property->name() << " = " << property->toString() << std::endl; + } + config_data.push_back(config_stream.str()); + } + m_metadatatree->Branch("gaudiConfigOptions", &config_data); + + m_metadatatree->Branch("CollectionIDs", m_podioDataSvc->getCollectionIDs()); + m_metadatatree->Fill(); + m_datatree->Write(); + m_file->Write(); + m_file->Close(); + info() << "Data written to: " << m_filename << endmsg; + return StatusCode::SUCCESS; +} diff --git a/FWCore/src/components/PodioOutput.h b/FWCore/src/components/PodioOutput.h new file mode 100644 index 0000000000000000000000000000000000000000..f1c93beabd1436202330011ffe741bcdfa5d2dab --- /dev/null +++ b/FWCore/src/components/PodioOutput.h @@ -0,0 +1,55 @@ +#ifndef FWCORE_PODIOOUTPUT_H +#define FWCORE_PODIOOUTPUT_H + +#include "FWCore/KeepDropSwitch.h" +#include "GaudiAlg/GaudiAlgorithm.h" +#include "podio/CollectionBase.h" + +#include "TTree.h" + +#include <vector> + +// forward declarations +class TFile; +class PodioDataSvc; + +class PodioOutput : public GaudiAlgorithm { + friend class AlgFactory<PodioOutput>; + +public: + /// Constructor. + PodioOutput(const std::string& name, ISvcLocator* svcLoc); + + /// Initialization of PodioOutput. Acquires the data service, creates trees and root file. + virtual StatusCode initialize(); + /// Execute. For the first event creates branches for all collections known to PodioDataSvc and prepares them for + /// writing. For the following events it reconnects the branches with collections and prepares them for write. + virtual StatusCode execute(); + /// Finalize. Writes the meta data tree; writes file and cleans up all ROOT-pointers. + virtual StatusCode finalize(); + +private: + void resetBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, bool prepare); + void createBranches(const std::vector<std::pair<std::string, podio::CollectionBase*>>& collections, bool prepare); + /// First event or not + bool m_firstEvent; + /// Root file name the output is written to + Gaudi::Property<std::string> m_filename{this, "filename", "output.root", "Name of the file to create"}; + /// Commands which output is to be kept + Gaudi::Property<std::vector<std::string>> m_outputCommands{ + this, "outputCommands", {"keep *"}, "A set of commands to declare which collections to keep or drop."}; + /// Switch for keeping or dropping outputs + KeepDropSwitch m_switch; + /// Needed for collection ID table + PodioDataSvc* m_podioDataSvc; + /// The actual ROOT file + std::unique_ptr<TFile> m_file; + /// The tree to be filled with collections + TTree* m_datatree; + /// The tree to be filled with meta data + TTree* m_metadatatree; + /// The stored collections + std::vector<podio::CollectionBase*> m_storedCollections; +}; + +#endif diff --git a/README.md b/README.md index 8f3ff57c698387f1df5918e0e8fb825371e1abc5..74feea3c0278300955daadc8228480c03f8d9350 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ # CEPCSW -CEPC offline software prototype based on Gaudi +CEPC offline software prototype based on Gaudi. + +The core part of this project (CEPCSW/FWCore) is taken from FCCSW/FWCore. +Please refer to https://github.com/HEP-FCC/FCCSW ## Quick start