diff --git a/Control/xAODDataSource/CMakeLists.txt b/Control/xAODDataSource/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..f44640b46fd6a0c07f6ede4bcc5221079ba1ab47 --- /dev/null +++ b/Control/xAODDataSource/CMakeLists.txt @@ -0,0 +1,62 @@ +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + +# Set the name of the package. +atlas_subdir( xAODDataSource ) + +# Set its dependencies on other packages. +atlas_depends_on_subdirs( + PUBLIC + Control/xAODRootAccess ) + +# External dependencies. VDT is necessary because the DataFrame code has +# a public dependency on it. +find_package( ROOT COMPONENTS Core Tree TreePlayer RIO ROOTDataFrame ) +find_package( VDT ) + +# Build the library. +atlas_add_library( xAODDataSourceLib + xAODDataSource/*.h Root/*.h Root/*.cxx + PUBLIC_HEADERS xAODDataSource + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} ${VDT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} ${VDT_LIBRARIES} xAODRootAccess ) + +# Build its dictionary. +atlas_add_dictionary( xAODDataSourceDict + xAODDataSource/xAODDataSourceDict.h xAODDataSource/selection.xml + LINK_LIBRARIES xAODDataSourceLib ) + +# Build the package's test(s). +atlas_add_test( dataSourceEvent_test + SOURCES test/dataSourceEvent_test.cxx + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} xAODRootAccess xAODDataSourceLib ) + +atlas_add_test( dataSource_test + SOURCES test/dataSource_test.cxx + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} xAODRootAccess xAODDataSourceLib ) + +atlas_add_test( dataFrame_test + SOURCES test/dataFrame_test.cxx + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} xAODRootAccess xAODBase xAODDataSourceLib + # These patterns are needed to suppress some warning messages in debug builds + LOG_IGNORE_PATTERN "[0-9a-f][0-9a-f] [0-9a-f][0-9a-f]|\\^~~~|vptr" ) + +atlas_add_test( dataFrameTypeConversion_test + SOURCES test/dataFrameTypeConversion_test.cxx + LINK_LIBRARIES xAODRootAccess xAODBase xAODEgamma xAODDataSourceLib + # These patterns are needed to suppress some warning messages in debug builds + LOG_IGNORE_PATTERN "[0-9a-f][0-9a-f] [0-9a-f][0-9a-f]|\\^~~~|vptr" ) + +atlas_add_test( dataFrameElementLink_test + SOURCES test/dataFrameElementLink_test.cxx + LINK_LIBRARIES xAODRootAccess xAODBase xAODEgamma xAODMuon xAODDataSourceLib + # These patterns are needed to suppress some warning messages in debug builds + LOG_IGNORE_PATTERN "[0-9a-f][0-9a-f] [0-9a-f][0-9a-f]|\\^~~~|vptr" ) + +atlas_add_test( dataFrame_pytest + SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/test/dataFrame_test.py ) + +# Install files from the package. +atlas_install_python_modules( python/*.py ) diff --git a/Control/xAODDataSource/README.md b/Control/xAODDataSource/README.md new file mode 100644 index 0000000000000000000000000000000000000000..3f13f0064c9961db2cc824abc17d580d344c0efb --- /dev/null +++ b/Control/xAODDataSource/README.md @@ -0,0 +1,5 @@ +# xAOD Data Source for ROOT::RDataFrame + +This package holds some code to allow using +[ROOT::RDataFrame](https://root.cern/doc/master/classROOT_1_1RDataFrame.html) +for reading (D)xAOD files. diff --git a/Control/xAODDataSource/Root/MakeDataFrame.cxx b/Control/xAODDataSource/Root/MakeDataFrame.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d6fa1b5516a75a59cbb0061daac809456a59a7ad --- /dev/null +++ b/Control/xAODDataSource/Root/MakeDataFrame.cxx @@ -0,0 +1,32 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "xAODDataSource/MakeDataFrame.h" +#include "RDataSource.h" + +// System include(s). +#include <memory> + +namespace xAOD { + + ROOT::RDataFrame MakeDataFrame( std::string_view fileNameGlob, + std::string_view treeName, + bool verboseOutput ) { + + auto source = std::make_unique< RDataSource >( fileNameGlob, treeName ); + source->setVerboseOutput( verboseOutput ); + return ROOT::RDataFrame( std::move( source ) ); + } + + ROOT::RDataFrame MakeDataFrame( const std::vector< std::string >& fileNames, + std::string_view treeName, + bool verboseOutput ) { + + auto source = std::make_unique< RDataSource >( fileNames, treeName ); + source->setVerboseOutput( verboseOutput ); + return ROOT::RDataFrame( std::move( source ) ); + } + +} // namespace xAOD diff --git a/Control/xAODDataSource/Root/RDataSource.cxx b/Control/xAODDataSource/Root/RDataSource.cxx new file mode 100644 index 0000000000000000000000000000000000000000..2c7bd65f42e44f99dece8493f34548685fdf160c --- /dev/null +++ b/Control/xAODDataSource/Root/RDataSource.cxx @@ -0,0 +1,432 @@ +// +// Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "RDataSource.h" + +// xAOD include(s). +#include "xAODRootAccess/TEvent.h" +#include "xAODRootAccess/tools/Message.h" + +// ROOT include(s). +#include <TChain.h> +#include <TFile.h> +#include <TROOT.h> +#include <TError.h> + +// System include(s). +#include <algorithm> +#include <memory> +#include <stdexcept> + +namespace { + + /// Convenience function for creating a chain + std::unique_ptr< TChain > + makeChain( const std::vector< std::string >& fileNames, + std::string_view treeName ) { + + // Create the chain object. + std::unique_ptr< TChain > chain; + { + R__LOCKGUARD( gROOTMutex ); + chain = std::make_unique< TChain >( treeName.data() ); + } + + // Set it up. + chain->ResetBit( kMustCleanup ); + for( const std::string& fileName : fileNames ) { + chain->Add( fileName.c_str() ); + } + + // Return the newly created chain. + return chain; + } + +} // private namespace + +namespace xAOD { + + /// Helper print operator + template< typename FIRST, typename SECOND > + std::ostream& operator<< ( std::ostream& out, + const std::pair< FIRST, SECOND >& pair ) { + + out << "[" << pair.first << ", " << pair.second << "]"; + return out; + } + + /// Helper print operator + template< typename T > + std::ostream& operator<< ( std::ostream& out, const std::vector< T >& vec ) { + + out << "["; + for( size_t i = 0; i < vec.size(); ++i ) { + out << vec[ i ]; + if( i + 1 < vec.size() ) { + out << ", "; + } + } + out << "]"; + return out; + } + + /// Helper print operator + template< typename T > + std::ostream& operator<< ( std::ostream& out, + const std::unordered_map< T, T >& umap ) { + + out << "{"; + bool first = true; + for( auto& upair : umap ) { + if( ! first ) { + out << ", "; + } + out << upair.first << " : " << upair.second; + first = false; + } + out << "}"; + return out; + } + +} // namespace xAOD + +/// Helper macro for printing verbose messages for debugging +#define PRINT_VERBOSE(MSG) \ + do { \ + if( m_verboseOutput ) { \ + std::cout << "xAOD::RDataSource VERBOSE " << MSG << std::endl; \ + } \ + } while( false ) + +namespace xAOD { + + RDataSource::RDataSource( std::string_view fileNameGlob, + std::string_view treeName ) + : RDataSource( std::vector< std::string >( { fileNameGlob.data() } ), + treeName ) { + + } + + RDataSource::RDataSource( const std::vector< std::string >& fileNames, + std::string_view treeName ) + : m_fileNames( fileNames ), m_treeName( treeName ), + m_verboseOutput( kFALSE ) { + + readInputMetadata(); + } + + RDataSource::~RDataSource() { + + // I don't understand why, but ROOT really doesn't like it if the + // chains are not the first to be deleted from memory. :-/ + m_chains.clear(); + } + + void RDataSource::SetNSlots( unsigned int slots ) { + + // Some sanity checks. + if( slots == 0 ) { + ::Error( "xAOD::RDataSource::SetNSlots", + XAOD_MESSAGE( "Zero slots requested" ) ); + throw std::invalid_argument( "Zero slots requested" ); + } + if( m_events.size() != 0 ) { + ::Error( "xAOD::RDataSource::SetNSlots", + XAOD_MESSAGE( "Function called multiple times" ) ); + throw std::runtime_error( "Function called multiple times" ); + } + + // Reserve the correct number of elements. + m_chains.reserve( slots ); + m_events.reserve( slots ); + m_stores.reserve( slots ); + PRINT_VERBOSE( "SetNSlots: Reserved " << slots + << " slots for the chains, events and stores" ); + + // Create the event objects already at this point. + for( unsigned int i = 0; i < slots; ++i ) { + + // Set up the chain, event and store. + m_chains.push_back( ::makeChain( m_fileNames, m_treeName ) ); + m_events.push_back( std::make_unique< RDataSourceEvent >() ); + m_stores.push_back( std::make_unique< TStore >() ); + TChain* chain = m_chains.back().get(); + RDataSourceEvent* event = m_events.back().get(); + + // Initialise the event object. + if( ! event->readFrom( chain ).isSuccess() ) { + ::Error( "xAOD::RDataSource::SetNSlots", + XAOD_MESSAGE( "Failed to set up xAOD::RDataSourceEvent " + "for slot %u" ), i ); + throw std::runtime_error( "Failed to set up " + "xAOD::RDataSourceEvent" ); + } + + // Load entry 0 for it. Notice that this is a waste of CPU and I/O + // on the surface. But it's not... This triggers the initialisation of + // the files/trees used by these chains. Which happens much more + // quickly in a serial way in a single thread than in multiple threads + // at the same time. To be followed up with the ROOT developers... + if( event->getEntry( 0 ) < 0 ) { + ::Error( "xAOD::RDataSource::SetNSlots", + XAOD_MESSAGE( "Failed to load entry 0 for slot %u" ), i ); + throw std::runtime_error( "Failed to load entry for slot" ); + } + PRINT_VERBOSE( "SetNSlots: Initialised objects for slot " << i ); + } + + // Return gracefully. + return; + } + + void RDataSource::Initialise() { + + // A sanity check. + if( m_entryRanges.size() != 0 ) { + ::Fatal( "xAOD::RDataSource::Initialise", + XAOD_MESSAGE( "Function called on an initialised object" ) ); + } + PRINT_VERBOSE( "Initialise: Initialising the data source" ); + + // Create a chain that will help determine the optimal entry ranges + // to process. + auto chain = ::makeChain( m_fileNames, m_treeName ); + TObjArray* filesInChain = chain->GetListOfFiles(); + + // Loop over the input files of the chain. + Long64_t fileOffset = 0; + for( Int_t ifile = 0; ifile < filesInChain->GetEntries(); ++ifile ) { + + // Open the file directly. + const char* fileName = filesInChain->At( ifile )->GetTitle(); + auto file = std::unique_ptr< TFile >( TFile::Open( fileName, + "READ" ) ); + if( ( ! file ) || file->IsZombie() ) { + ::Error( "xAOD::RDataSource::Initialise", + XAOD_MESSAGE( "Failed to open file: %s" ), fileName ); + throw std::runtime_error( "Failed to open file: " + + std::string( fileName ) ); + } + + // Access the event tree inside of it. + TTree* tree = + dynamic_cast< TTree* >( file->Get( m_treeName.c_str() ) ); + if( ! tree ) { + // A file with no event tree is not necessarily a problem. It could + // just be a file that has no events left in it after all + // selections. + continue; + } + + // Extract the ideal entry ranges from the file. + const Long64_t entries = tree->GetEntries(); + TTree::TClusterIterator clusterIter( tree->GetClusterIterator( 0 ) ); + Long64_t clusterStart = 0; + while( ( clusterStart = clusterIter() ) < entries ) { + m_entryRanges.emplace_back( fileOffset + clusterStart, + fileOffset + + clusterIter.GetNextEntry() ); + } + + // Increment the file offset value. + fileOffset += entries; + } + PRINT_VERBOSE( "Initialise: Created entry ranges: " << m_entryRanges ); + + // Return gracefully. + return; + } + + void RDataSource::InitSlot( unsigned int slot, ULong64_t firstEntry ) { + + // A sanity check. + if( m_events.size() <= slot ) { + ::Error( "xAOD::RDataSource::InitSlot", + XAOD_MESSAGE( "Invalid slot (%u) received" ), slot ); + throw std::runtime_error( "Invalid slot received" ); + } + + // Load the first entry for it. + if( m_events[ slot ]->getEntry( firstEntry ) < 0 ) { + ::Error( "xAOD::RDataSource::InitSlot", + XAOD_MESSAGE( "Failed to load entry %lld for slot %u" ), + firstEntry, slot ); + throw std::runtime_error( "Failed to load entry for slot" ); + } + PRINT_VERBOSE( "InitSlot: Retrieved entry " << firstEntry << " for slot " + << slot ); + + // Activate and clear the store. + m_stores[ slot ]->setActive(); + m_stores[ slot ]->clear(); + PRINT_VERBOSE( "InitSlot: Activated and cleared transient store for slot " + << slot ); + + // Return gracefully. + return; + } + + void RDataSource::FinaliseSlot( unsigned int slot ) { + + // Simply print what's happening. + PRINT_VERBOSE( "FinaliseSlot: Called for slot " << slot ); + + // Return gracefully. + return; + } + + void RDataSource::Finalise() { + + // Simply print what's happening. + PRINT_VERBOSE( "Finalise: Function called" ); + + // Return gracefully. + return; + } + + const std::vector< std::string >& RDataSource::GetColumnNames() const { + + return m_columnNames; + } + + bool RDataSource::HasColumn( std::string_view name ) const { + + return std::find( m_columnNames.begin(), m_columnNames.end(), + name ) != m_columnNames.end(); + } + + std::string RDataSource::GetTypeName( std::string_view column ) const { + + // Make sure that the column/object is known. + if( ! HasColumn( column ) ) { + ::Error( "xAOD::RDataSource::GetTypeName", + XAOD_MESSAGE( "Column/object \"%s\" not available" ), + column.data() ); + throw std::runtime_error( "Column/object \"" + column + + "\" not available" ); + } + + // Get the type. + auto itr = m_classNameMap.find( column.data() ); + if( itr == m_classNameMap.end() ) { + // Note that the fatal message will abort the entire job in all cases. + ::Fatal( "xAOD::RDataSource::GetTypeName", + XAOD_MESSAGE( "Internal logic error found" ) ); + } + PRINT_VERBOSE( "GetTypeName: Type name for column \"" << column + << "\" is: " << itr->second ); + return itr->second; + } + + RDataSource::EntryRanges_t RDataSource::GetEntryRanges() { + + // When ROOT asks for the entry ranges, we have to tell it which ones + // have not been processed yet. Since we process all entries right away + // (SetEntry(...) does not have logic for not processing a requested + // entry), the logic here is to empty out the m_entryRanges variable on + // this call. So that on the next call an empty range would be returned. + const EntryRanges_t dummy( std::move( m_entryRanges ) ); + return dummy; + } + + bool RDataSource::SetEntry( unsigned int slot, ULong64_t entry ) { + + // A sanity check. + if( m_events.size() <= slot ) { + ::Error( "xAOD::RDataSource::SetEntry", + XAOD_MESSAGE( "Invalid slot (%u) received" ), slot ); + throw std::runtime_error( "Invalid slot received" ); + } + PRINT_VERBOSE( "SetEntry: Called for slot " << slot << " and entry " + << entry ); + + // Switch to the requested entry. + m_events[ slot ]->updateObjectsForEntry( entry ); + + // Activate and clear the store. + m_stores[ slot ]->setActive(); + m_stores[ slot ]->clear(); + + // The entry is always processed. + return true; + } + + void RDataSource::setVerboseOutput( Bool_t value ) { + + m_verboseOutput = value; + return; + } + + Bool_t RDataSource::isVerboseOutput() const { + + return m_verboseOutput; + } + + RDataSource::Record_t + RDataSource::GetColumnReadersImpl( std::string_view column, + const std::type_info& typeInfo ) { + + // Make sure that the column/object is known. + if( ! HasColumn( column ) ) { + ::Error( "xAOD::RDataSource::GetColumnReadersImpl", + XAOD_MESSAGE( "Column/object \"%s\" not available" ), + column.data() ); + throw std::runtime_error( "Column/object \"" + column + + "\" not available" ); + } + PRINT_VERBOSE( "GetColumnReadersImpl: Creating column readers for \"" + << column << "/" << SG::normalizedTypeinfoName( typeInfo ) + << "\"" ); + + // Create the comlumn reader pointers. + Record_t result( m_events.size() ); + for( size_t i = 0; i < m_events.size(); ++i ) { + result[ i ] = m_events[ i ]->columnReader( column, typeInfo ); + } + return result; + } + + void RDataSource::readInputMetadata() { + + // Create a temporary event object. + auto chain = ::makeChain( m_fileNames, m_treeName ); + RDataSourceEvent event; + if( ! event.readFrom( chain.get() ).isSuccess() ) { + ::Error( "xAOD::RDataSource::readInputMetadata", + XAOD_MESSAGE( "Failed to connect to the input chain" ) ); + throw std::runtime_error( "Failed to connect to the input chain" ); + } + + // Load the first event of the input, if one is available. + if( event.getEntries() > 0 ) { + if( event.getEntry( 0 ) < 0 ) { + ::Error( "xAOD::RDataSource::readInputMetadata", + "Couldn't load the first event of the input" ); + throw std::runtime_error( "Couldn't load the first event of the " + "input" ); + } + } + + // Fill the column and type name variables. + m_columnNames.clear(); m_classNameMap.clear(); + auto names = event.columnAndTypeNames(); + m_columnNames.reserve( names.size() ); + m_classNameMap.reserve( names.size() ); + for( const auto& pair : names ) { + m_columnNames.push_back( pair.first ); + m_classNameMap[ pair.first ] = pair.second; + } + PRINT_VERBOSE( "readInputMetadata: m_columnNames = " << m_columnNames ); + PRINT_VERBOSE( "readInputMetadata: m_classNameMap = " << m_classNameMap ); + + // ROOT memory management is weird... We must delete the chain first, + // before the TEvent object on top of it would be deleted... + chain.reset(); + + // Return gracefully. + return; + } + +} // namespace xAOD diff --git a/Control/xAODDataSource/Root/RDataSource.h b/Control/xAODDataSource/Root/RDataSource.h new file mode 100644 index 0000000000000000000000000000000000000000..fd37cc827a6dd71fa786ded8752421940016514a --- /dev/null +++ b/Control/xAODDataSource/Root/RDataSource.h @@ -0,0 +1,142 @@ +// Dear emacs, this is -*- c++ -*- +// +// Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +// +#ifndef XAODDATASOURCE_RDATASOURCE_H +#define XAODDATASOURCE_RDATASOURCE_H + +// Local include(s). +#include "RDataSourceEvent.h" + +// Framework include(s). +#include "xAODRootAccess/TStore.h" + +// ROOT include(s). +#include <ROOT/RDataSource.hxx> +#include <ROOT/RStringView.hxx> +#include <TChain.h> + +// System include(s). +#include <memory> +#include <string> +#include <unordered_map> +#include <vector> + +namespace xAOD { + + /// Data source for xAOD input files + /// + /// This data source can be used to allow @c ROOT::RDataFrame to process + /// (D)xAOD inputs. + /// + /// @author Umesh Worlikar <Umesh.Dharmaji.Worlikar@cern.ch> + /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch> + /// + class RDataSource final : public ROOT::RDF::RDataSource { + + public: + /// Constructor with the file name pattern + RDataSource( std::string_view fileNameGlob, + std::string_view treeName = "CollectionTree" ); + /// Constructor with list of file names + RDataSource( const std::vector< std::string >& fileNames, + std::string_view treeName = "CollectionTree" ); + /// Destructor + ~RDataSource(); + + /// Type describing the entry ranges of the input file(s) + typedef std::vector< std::pair< ULong64_t, ULong64_t > > EntryRanges_t; + + /// @name Functions implemented from @c ROOT::RDF::RDataSource + /// @{ + + /// Set the number of threads/slots that the data source should use + virtual void SetNSlots( unsigned int slots ) override final; + /// Initialise the data source, before the start of the event loop + virtual void Initialise() override final; + /// Initialise one of the slots/threads + virtual void + InitSlot( unsigned int slot, ULong64_t firstEntry ) override final; + + /// Close the input file reading in one of the slots/threads + virtual void FinaliseSlot( unsigned int slot ) override final; + /// Finalise the data source, after the event loop + virtual void Finalise() override final; + + /// Get the column/object names for the input file(s) + virtual const std::vector< std::string >& + GetColumnNames() const override final; + /// Check if the dataset has a certain column/object + virtual bool HasColumn( std::string_view name ) const override final; + /// Get the type name of a given column/object + virtual std::string + GetTypeName( std::string_view column ) const override final; + + /// Get the entry ranges in the input file(s) + virtual EntryRanges_t GetEntryRanges() override final; + /// Set which entry a give slot/thread should be processing + virtual bool + SetEntry( unsigned int slot, ULong64_t entry ) override final; + + /// @} + + /// Set whether verbose output should be printed (for debugging) + void setVerboseOutput( Bool_t value = kTRUE ); + /// Check whether verbose output is set up to be printed + Bool_t isVerboseOutput() const; + + private: + /// Return the type-erased vector of pointers to pointers to column values + virtual Record_t + GetColumnReadersImpl( std::string_view column, + const std::type_info& typeInfo ) override final; + /// Fill the metadata variables + void readInputMetadata(); + + /// @name Arguments provided by the user + /// @{ + + /// Files to read + std::vector< std::string > m_fileNames; + /// Name of the event tree in the input files + std::string m_treeName; + /// Whether verbose output should be printed or not + Bool_t m_verboseOutput; + + /// @} + + /// @name Variables valid right after construction + /// @{ + + /// Names of the columns/objects on the input + std::vector< std::string > m_columnNames; + /// The object name -> class name map + std::unordered_map< std::string, std::string > m_classNameMap; + + /// @} + + /// @name Variables that become valid after calling @c Initialise() + /// @{ + + /// Optimal entry ranges to split the processing into + EntryRanges_t m_entryRanges; + + /// @} + + /// @name Variables that become valid after calling @c SetNSlots(...) + /// @{ + + /// Chains used in the file I/O + std::vector< std::unique_ptr< TChain > > m_chains; + /// Event objects performing the file I/O + std::vector< std::unique_ptr< RDataSourceEvent > > m_events; + /// In-memory whiteboards used during the event loop + std::vector< std::unique_ptr< TStore > > m_stores; + + /// @} + + }; // class RDataSource + +} // namespace xAOD + +#endif // XAODDATASOURCE_RDATASOURCE_H diff --git a/Control/xAODDataSource/Root/RDataSourceEvent.cxx b/Control/xAODDataSource/Root/RDataSourceEvent.cxx new file mode 100644 index 0000000000000000000000000000000000000000..d119d76838237b290081f13bf0c9b9b10337ee6a --- /dev/null +++ b/Control/xAODDataSource/Root/RDataSourceEvent.cxx @@ -0,0 +1,141 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "RDataSourceEvent.h" + +// xAOD include(s). +#include "xAODRootAccess/tools/Message.h" +#include "xAODRootAccess/tools/Utils.h" +#include "xAODRootAccess/tools/TObjectManager.h" +#include "xAODRootAccess/tools/THolder.h" + +// ROOT include(s). +#include <TClass.h> +#include <TTree.h> +#include <TError.h> + +// System include(s). +#include <functional> + +namespace xAOD { + + RDataSourceEvent::RDataSourceEvent() + : TEvent( TEvent::kClassAccess ) { + + } + + std::vector< std::pair< std::string, std::string > > + RDataSourceEvent::columnAndTypeNames() { + + // This makes sure that the input is connected to. + if( ( ! m_inTree ) && ( getEntry( 0 ) < 0 ) ) { + ::Error( "xAOD::RDataSourceEvent::columnAndTypeNames", + XAOD_MESSAGE( "No input file opened yet" ) ); + throw std::runtime_error( "No input file opened yet" ); + } + + // A sanity check. + if( ! inputEventFormat() ) { + ::Error( "xAOD::RDataSourceEvent::columnAndTypeNames", + XAOD_MESSAGE( "No xAOD::EventFormat object available" ) ); + throw std::runtime_error( "No xAOD::EventFormat object available" ); + } + + // The result object. + std::vector< std::pair< std::string, std::string > > result; + + // Loop over the known objects. + EventFormat::const_iterator ef_itr = inputEventFormat()->begin(); + EventFormat::const_iterator ef_end = inputEventFormat()->end(); + for( ; ef_itr != ef_end; ++ef_itr ) { + + // A convenient reference. + const xAOD::EventFormatElement& efe = ef_itr->second; + + // Skip auxiliary containers. + if( efe.branchName().rfind( "Aux." ) == + ( efe.branchName().size() - 4 ) ) { + continue; + } + // Skip dynamic branches. + if( efe.parentName() != "" ) { + continue; + } + // Skip unavailable branches. + if( ! m_inTree->GetBranch( efe.branchName().c_str() ) ) { + continue; + } + // Skip unknown types. + ::TClass* cl = ::TClass::GetClass( efe.className().c_str() ); + if( ! cl ) { + continue; + } + // Skip auxiliary containers that are mislabeled. + if( cl->InheritsFrom( "SG::IConstAuxStore" ) ) { + continue; + } + + // Make sure that we have a functional dictionary for the type. + const std::type_info* ti = cl->GetTypeInfo(); + if( ! ti ) { + ::Warning( "xAOD::RDataSourceEvent::columnAndTypeNames", + "No std::type_info available for type %s (key=%s)", + cl->GetName(), efe.branchName().c_str() ); + continue; + } + + // And do one final check that we can really read this type. + if( ! contains( efe.branchName(), *ti ) ) { + continue; + } + + // Remember this objects. + result.emplace_back( efe.branchName(), efe.className() ); + } + + // Return the collected results. + return result; + } + + void* RDataSourceEvent::columnReader( std::string_view columnName, + const std::type_info& typeInfo ) { + + // Make sure that this is the active event in the current thread. + setActive(); + + // Create/access the pointer for this object. + return &( m_objects[ std::make_pair( std::string( columnName ), + &typeInfo ) ] ); + } + + void RDataSourceEvent::updateObjectsForEntry( Long64_t entry ) { + + // Make sure that this is the active event in the current thread. + setActive(); + + // Switch to the new entry. + if( getEntry( entry ) < 0 ) { + ::Error( "xAOD::RDataSourceEvent::updateObjectsForEntry", + XAOD_MESSAGE( "Failed to get entry %lld" ), entry ); + throw std::runtime_error( "Failed to get entry" ); + } + + // Loop over all "registered" objects. + for( auto& obj : m_objects ) { + // (Re-)Access this object. + obj.second = getInputObject( obj.first.first, *( obj.first.second ) ); + } + + // Return gracefully. + return; + } + + std::size_t + RDataSourceEvent::key_hash::operator()( const Key_t& key ) const { + + return std::hash< std::string >()( key.first + key.second->name() ); + } + +} // namespace xAOD diff --git a/Control/xAODDataSource/Root/RDataSourceEvent.h b/Control/xAODDataSource/Root/RDataSourceEvent.h new file mode 100644 index 0000000000000000000000000000000000000000..4b13bb45ea8548230664d341955d8bd0ab6074a8 --- /dev/null +++ b/Control/xAODDataSource/Root/RDataSourceEvent.h @@ -0,0 +1,63 @@ +// Dear emacs, this is -*- c++ -*- +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// +#ifndef XAODDATASOURCE_RDATASOURCEEVENT_H +#define XAODDATASOURCE_RDATASOURCEEVENT_H + +// xAOD include(s). +#include "xAODRootAccess/TEvent.h" + +// ROOT include(s). +#include <ROOT/RStringView.hxx> + +// System include(s). +#include <string> +#include <vector> +#include <utility> +#include <typeinfo> +#include <unordered_map> + +namespace xAOD { + + /// Extension to @c xAOD::TEvent, used by @c xAOD::RDataSource + /// + /// In order to get access to the internals of @c xAOD::TEvent, and provide + /// pointers for @c ROOT::RDataFrame in a convenient way, this class provides + /// a layer between the two codebases. + /// + /// @author Attila Krasznahorkay <Attila.Krasznahorkay@cern.ch> + /// + class RDataSourceEvent final : public TEvent { + + public: + /// Default constructor + RDataSourceEvent(); + + /// Get the available columm and type names from the input + std::vector< std::pair< std::string, std::string > > columnAndTypeNames(); + /// Get a "column reader", a pointer to a pointer to the object + void* columnReader( std::string_view columnName, + const std::type_info& typeInfo ); + /// Update all objects in memory for a new event + void updateObjectsForEntry( Long64_t entry ); + + private: + /// Type of the key used in the internal map of object pointers + typedef std::pair< std::string, const std::type_info* > Key_t; + + /// Custom hash functor for use with @c Key_t + class key_hash { + public: + /// Function calculating the hash for this type + std::size_t operator()( const Key_t& key ) const; + }; + + /// Objects served to RDataFrame + std::unordered_map< Key_t, const void*, key_hash > m_objects; + + }; // class RDataSourceEvent + +} // namespace xAOD + +#endif // XAODDATASOURCE_RDATASOURCEEVENT_H diff --git a/Control/xAODDataSource/python/Helpers.py b/Control/xAODDataSource/python/Helpers.py new file mode 100644 index 0000000000000000000000000000000000000000..48c3eb7db4fd63dd6a5409b78151ca737ead2b11 --- /dev/null +++ b/Control/xAODDataSource/python/Helpers.py @@ -0,0 +1,22 @@ +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration + +# Necessary import(s). +import ROOT + +def MakexAODDataFrame( inputs ): + """Helper function creating a ROOT::RDataFrame object reading xAOD files + + The function returns an instance of ROOT::RDataFrame that uses + xAOD::RDataSource for reading its inputs. + + Keyword arguments: + inputs -- A single string, or a list of string selecting the input file(s) + Note that the string(s) may contain wildcards and environment + variables as well. + """ + + # Make sure that the dictionary is loaded. + ROOT.xAOD.ROOT6_xAODDataSource_WorkAround_Dummy() + + # Use the C++ function for the heavy lifting. + return ROOT.xAOD.MakeDataFrame( inputs ) diff --git a/Control/xAODDataSource/python/__init__.py b/Control/xAODDataSource/python/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..4e669a90b8bb38f0bfd2b2b839d01aa2194e673f --- /dev/null +++ b/Control/xAODDataSource/python/__init__.py @@ -0,0 +1 @@ +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration diff --git a/Control/xAODDataSource/share/dataFrameElementLink_test.ref b/Control/xAODDataSource/share/dataFrameElementLink_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..cbebbd9bcc4f59daa2b377258469d53ded9fe66a --- /dev/null +++ b/Control/xAODDataSource/share/dataFrameElementLink_test.ref @@ -0,0 +1,3 @@ +xAOD::Init INFO Environment initialised for data access +el_hist->GetMaximumBin() = 70 +mu_hist->GetMaximumBin() = 64 diff --git a/Control/xAODDataSource/share/dataFrameTypeConversion_test.ref b/Control/xAODDataSource/share/dataFrameTypeConversion_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..755cad1e963f841de40e489382a85398e6c8c3f1 --- /dev/null +++ b/Control/xAODDataSource/share/dataFrameTypeConversion_test.ref @@ -0,0 +1,3 @@ +xAOD::Init INFO Environment initialised for data access +el_pt_from_iparticle.Mean() = 10735.6 +el_pt_from_electron.Mean() = 10735.6 diff --git a/Control/xAODDataSource/share/dataFrame_pytest.ref b/Control/xAODDataSource/share/dataFrame_pytest.ref new file mode 100644 index 0000000000000000000000000000000000000000..5120eaf54adc80ebe9c1412cffb39fb1eb812f1f --- /dev/null +++ b/Control/xAODDataSource/share/dataFrame_pytest.ref @@ -0,0 +1,2 @@ +xAOD::Init INFO Environment initialised for data access +ElectronsPt entries = 15903 diff --git a/Control/xAODDataSource/share/dataFrame_test.ref b/Control/xAODDataSource/share/dataFrame_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..5120eaf54adc80ebe9c1412cffb39fb1eb812f1f --- /dev/null +++ b/Control/xAODDataSource/share/dataFrame_test.ref @@ -0,0 +1,2 @@ +xAOD::Init INFO Environment initialised for data access +ElectronsPt entries = 15903 diff --git a/Control/xAODDataSource/share/dataSourceEvent_test.ref b/Control/xAODDataSource/share/dataSourceEvent_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..703dfded31eac81f598b484318a643f25bbc064b --- /dev/null +++ b/Control/xAODDataSource/share/dataSourceEvent_test.ref @@ -0,0 +1,2 @@ +xAOD::Init INFO Environment initialised for data access +[[AntiKt10LCTopoJets, DataVector<xAOD::Jet_v1>], [AntiKt10LCTopoTrimmedPtFrac5SmallR20Jets, DataVector<xAOD::Jet_v1>], [AntiKt10PV0TrackJets, DataVector<xAOD::Jet_v1>], [AntiKt2PV0TrackJets, DataVector<xAOD::Jet_v1>], [AntiKt4EMPFlowJets, DataVector<xAOD::Jet_v1>], [AntiKt4EMTopoJets, DataVector<xAOD::Jet_v1>], [AntiKt4EMTopoLowPtJets, DataVector<xAOD::Jet_v1>], [AntiKt4LCTopoJets, DataVector<xAOD::Jet_v1>], [AntiKt4LCTopoLowPtJets, DataVector<xAOD::Jet_v1>], [AntiKt4PV0TrackJets, DataVector<xAOD::Jet_v1>], [AntiKtVR30Rmax4Rmin02TrackJets, DataVector<xAOD::Jet_v1>], [BTagging_AntiKt4EMPFlow, DataVector<xAOD::BTagging_v1>], [BTagging_AntiKt4EMPFlowSecVtx, DataVector<xAOD::Vertex_v1>], [BTagging_AntiKt4EMTopo, DataVector<xAOD::BTagging_v1>], [BTagging_AntiKt4EMTopoJFVtx, DataVector<xAOD::BTagVertex_v1>], [BTagging_AntiKt4EMTopoSecVtx, DataVector<xAOD::Vertex_v1>], [BTagging_AntiKtVR30Rmax4Rmin02Track, DataVector<xAOD::BTagging_v1>], [BTagging_AntiKtVR30Rmax4Rmin02TrackSecVtx, DataVector<xAOD::Vertex_v1>], [CombinedMuonTrackParticles, DataVector<xAOD::TrackParticle_v1>], [EMOriginTopoClusters, DataVector<xAOD::CaloCluster_v1>], [Electrons, DataVector<xAOD::Electron_v1>], [EventInfo, xAOD::EventInfo_v1], [ExtrapolatedMuonTrackParticles, DataVector<xAOD::TrackParticle_v1>], [GSFConversionVertices, DataVector<xAOD::Vertex_v1>], [GSFTrackParticles, DataVector<xAOD::TrackParticle_v1>], [HLT_xAOD__BTaggingContainer_HLTBjetFex, DataVector<xAOD::BTagging_v1>], [HLT_xAOD__ElectronContainer_egamma_Electrons, DataVector<xAOD::Electron_v1>], [HLT_xAOD__JetContainer_EFJet, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_GSCJet, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_SplitJet, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10r_tcemsubjesFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10tcemnojcalibFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10tcemsubFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10tcemsubjesFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10tclcwsubFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a10tclcwsubjesFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a4tcemnojcalibFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__JetContainer_a4tcemsubjesFS, DataVector<xAOD::Jet_v1>], [HLT_xAOD__MuonContainer_MuonEFInfo, DataVector<xAOD::Muon_v1>], [HLT_xAOD__MuonContainer_MuonEFInfo_FullScan, DataVector<xAOD::Muon_v1>], [HLT_xAOD__PhotonContainer_egamma_Photons, DataVector<xAOD::Photon_v1>], [HLT_xAOD__TauJetContainer_TrigTauRecMerged, DataVector<xAOD::TauJet_v3>], [HLT_xAOD__TrackParticleContainer_InDetTrigTrackingxAODCnv_Muon_EFID, DataVector<xAOD::TrackParticle_v1>], [HLT_xAOD__TrackParticleContainer_InDetTrigTrackingxAODCnv_Muon_IDTrig, DataVector<xAOD::TrackParticle_v1>], [HLT_xAOD__TrigBphysContainer_EFBMuMuFex, DataVector<xAOD::TrigBphys_v1>], [HLT_xAOD__TrigBphysContainer_EFBMuMuXFex, DataVector<xAOD::TrigBphys_v1>], [HLT_xAOD__TrigBphysContainer_EFMultiMuFex, DataVector<xAOD::TrigBphys_v1>], [HLT_xAOD__TrigBphysContainer_EFTrackMass, DataVector<xAOD::TrigBphys_v1>], [HLT_xAOD__TrigBphysContainer_L2BMuMuFex, DataVector<xAOD::TrigBphys_v1>], [HLT_xAOD__TrigMissingETContainer_TrigEFMissingET, DataVector<xAOD::TrigMissingET_v1>], [HLT_xAOD__TrigMissingETContainer_TrigEFMissingET_mht, DataVector<xAOD::TrigMissingET_v1>], [HLT_xAOD__TrigMissingETContainer_TrigEFMissingET_topocl_PUC, DataVector<xAOD::TrigMissingET_v1>], [HLT_xAOD__TrigSpacePointCountsContainer_spacepoints, DataVector<xAOD::TrigSpacePointCounts_v1>], [HLT_xAOD__TrigT2MbtsBitsContainer_T2Mbts, DataVector<xAOD::TrigT2MbtsBits_v1>], [HLT_xAOD__TrigT2ZdcSignalsContainer_zdcsignals, DataVector<xAOD::TrigT2ZdcSignals_v1>], [HLT_xAOD__TrigTrackCountsContainer_trackcounts, DataVector<xAOD::TrigTrackCounts_v1>], [HLT_xAOD__TrigVertexCountsContainer_vertexcounts, DataVector<xAOD::TrigVertexCounts_v1>], [HLT_xAOD__VertexContainer_EFHistoPrmVtx, DataVector<xAOD::Vertex_v1>], [HLT_xAOD__VertexContainer_xPrimVx, DataVector<xAOD::Vertex_v1>], [InDetForwardTrackParticles, DataVector<xAOD::TrackParticle_v1>], [InDetTrackParticles, DataVector<xAOD::TrackParticle_v1>], [Kt4EMPFlowEventShape, xAOD::EventShape_v1], [Kt4EMTopoOriginEventShape, xAOD::EventShape_v1], [Kt4LCTopoOriginEventShape, xAOD::EventShape_v1], [LCOriginTopoClusters, DataVector<xAOD::CaloCluster_v1>], [LVL1EmTauRoIs, DataVector<xAOD::EmTauRoI_v2>], [LVL1EnergySumRoI, xAOD::EnergySumRoI_v2], [LVL1JetEtRoI, xAOD::JetEtRoI_v1], [LVL1JetRoIs, DataVector<xAOD::JetRoI_v2>], [LVL1MuonRoIs, DataVector<xAOD::MuonRoI_v1>], [METAssoc_AntiKt4EMPFlow, xAOD::MissingETAssociationMap_v1], [METAssoc_AntiKt4EMTopo, xAOD::MissingETAssociationMap_v1], [METAssoc_AntiKt4LCTopo, xAOD::MissingETAssociationMap_v1], [MET_Calo, xAOD::MissingETContainer_v1], [MET_Core_AntiKt4EMPFlow, xAOD::MissingETContainer_v1], [MET_Core_AntiKt4EMTopo, xAOD::MissingETContainer_v1], [MET_Core_AntiKt4LCTopo, xAOD::MissingETContainer_v1], [MET_EMTopo, xAOD::MissingETContainer_v1], [MET_EMTopoRegions, xAOD::MissingETContainer_v1], [MET_LocHadTopo, xAOD::MissingETContainer_v1], [MET_LocHadTopoRegions, xAOD::MissingETContainer_v1], [MET_Reference_AntiKt4EMPFlow, xAOD::MissingETContainer_v1], [MET_Reference_AntiKt4EMTopo, xAOD::MissingETContainer_v1], [MET_Reference_AntiKt4LCTopo, xAOD::MissingETContainer_v1], [MET_Track, xAOD::MissingETContainer_v1], [MuonSegments, DataVector<xAOD::MuonSegment_v1>], [MuonSpectrometerTrackParticles, DataVector<xAOD::TrackParticle_v1>], [Muons, DataVector<xAOD::Muon_v1>], [Photons, DataVector<xAOD::Photon_v1>], [PrimaryVertices, DataVector<xAOD::Vertex_v1>], [TauJets, DataVector<xAOD::TauJet_v3>], [TauTracks, DataVector<xAOD::TauTrack_v1>], [TrigConfKeys, xAOD::TrigConfKeys_v1], [TrigNavigation, xAOD::TrigNavigation_v1], [egammaClusters, DataVector<xAOD::CaloCluster_v1>], [xTrigDecision, xAOD::TrigDecision_v1]] diff --git a/Control/xAODDataSource/share/dataSource_test.ref b/Control/xAODDataSource/share/dataSource_test.ref new file mode 100644 index 0000000000000000000000000000000000000000..a93897127a571d335982eee89a1c9f664ee8a212 --- /dev/null +++ b/Control/xAODDataSource/share/dataSource_test.ref @@ -0,0 +1,3 @@ +xAOD::Init INFO Environment initialised for data access +[[0, 53], [53, 106], [106, 159], [159, 212], [212, 265], [265, 318], [318, 371], [371, 424], [424, 477], [477, 530], [530, 583], [583, 636], [636, 689], [689, 742], [742, 795], [795, 848], [848, 901], [901, 954], [954, 1007], [1007, 1060], [1060, 1113], [1113, 1166], [1166, 1219], [1219, 1272], [1272, 1325], [1325, 1378], [1378, 1431], [1431, 1484], [1484, 1537], [1537, 1590], [1590, 1643], [1643, 1696], [1696, 1749], [1749, 1802], [1802, 1855], [1855, 1908], [1908, 1961], [1961, 2014], [2014, 2067], [2067, 2120], [2120, 2157]] +[AntiKt10LCTopoJets, AntiKt10LCTopoTrimmedPtFrac5SmallR20Jets, AntiKt10PV0TrackJets, AntiKt2PV0TrackJets, AntiKt4EMPFlowJets, AntiKt4EMTopoJets, AntiKt4EMTopoLowPtJets, AntiKt4LCTopoJets, AntiKt4LCTopoLowPtJets, AntiKt4PV0TrackJets, AntiKtVR30Rmax4Rmin02TrackJets, BTagging_AntiKt4EMPFlow, BTagging_AntiKt4EMPFlowSecVtx, BTagging_AntiKt4EMTopo, BTagging_AntiKt4EMTopoJFVtx, BTagging_AntiKt4EMTopoSecVtx, BTagging_AntiKtVR30Rmax4Rmin02Track, BTagging_AntiKtVR30Rmax4Rmin02TrackSecVtx, CombinedMuonTrackParticles, EMOriginTopoClusters, Electrons, EventInfo, ExtrapolatedMuonTrackParticles, GSFConversionVertices, GSFTrackParticles, HLT_xAOD__BTaggingContainer_HLTBjetFex, HLT_xAOD__ElectronContainer_egamma_Electrons, HLT_xAOD__JetContainer_EFJet, HLT_xAOD__JetContainer_GSCJet, HLT_xAOD__JetContainer_SplitJet, HLT_xAOD__JetContainer_a10r_tcemsubjesFS, HLT_xAOD__JetContainer_a10tcemnojcalibFS, HLT_xAOD__JetContainer_a10tcemsubFS, HLT_xAOD__JetContainer_a10tcemsubjesFS, HLT_xAOD__JetContainer_a10tclcwsubFS, HLT_xAOD__JetContainer_a10tclcwsubjesFS, HLT_xAOD__JetContainer_a4tcemnojcalibFS, HLT_xAOD__JetContainer_a4tcemsubjesFS, HLT_xAOD__MuonContainer_MuonEFInfo, HLT_xAOD__MuonContainer_MuonEFInfo_FullScan, HLT_xAOD__PhotonContainer_egamma_Photons, HLT_xAOD__TauJetContainer_TrigTauRecMerged, HLT_xAOD__TrackParticleContainer_InDetTrigTrackingxAODCnv_Muon_EFID, HLT_xAOD__TrackParticleContainer_InDetTrigTrackingxAODCnv_Muon_IDTrig, HLT_xAOD__TrigBphysContainer_EFBMuMuFex, HLT_xAOD__TrigBphysContainer_EFBMuMuXFex, HLT_xAOD__TrigBphysContainer_EFMultiMuFex, HLT_xAOD__TrigBphysContainer_EFTrackMass, HLT_xAOD__TrigBphysContainer_L2BMuMuFex, HLT_xAOD__TrigMissingETContainer_TrigEFMissingET, HLT_xAOD__TrigMissingETContainer_TrigEFMissingET_mht, HLT_xAOD__TrigMissingETContainer_TrigEFMissingET_topocl_PUC, HLT_xAOD__TrigSpacePointCountsContainer_spacepoints, HLT_xAOD__TrigT2MbtsBitsContainer_T2Mbts, HLT_xAOD__TrigT2ZdcSignalsContainer_zdcsignals, HLT_xAOD__TrigTrackCountsContainer_trackcounts, HLT_xAOD__TrigVertexCountsContainer_vertexcounts, HLT_xAOD__VertexContainer_EFHistoPrmVtx, HLT_xAOD__VertexContainer_xPrimVx, InDetForwardTrackParticles, InDetTrackParticles, Kt4EMPFlowEventShape, Kt4EMTopoOriginEventShape, Kt4LCTopoOriginEventShape, LCOriginTopoClusters, LVL1EmTauRoIs, LVL1EnergySumRoI, LVL1JetEtRoI, LVL1JetRoIs, LVL1MuonRoIs, METAssoc_AntiKt4EMPFlow, METAssoc_AntiKt4EMTopo, METAssoc_AntiKt4LCTopo, MET_Calo, MET_Core_AntiKt4EMPFlow, MET_Core_AntiKt4EMTopo, MET_Core_AntiKt4LCTopo, MET_EMTopo, MET_EMTopoRegions, MET_LocHadTopo, MET_LocHadTopoRegions, MET_Reference_AntiKt4EMPFlow, MET_Reference_AntiKt4EMTopo, MET_Reference_AntiKt4LCTopo, MET_Track, MuonSegments, MuonSpectrometerTrackParticles, Muons, Photons, PrimaryVertices, TauJets, TauTracks, TrigConfKeys, TrigNavigation, egammaClusters, xTrigDecision] diff --git a/Control/xAODDataSource/test/dataFrameElementLink_test.cxx b/Control/xAODDataSource/test/dataFrameElementLink_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fc265ddbcc4a3118fd947cbdadee3b68fa34a79c --- /dev/null +++ b/Control/xAODDataSource/test/dataFrameElementLink_test.cxx @@ -0,0 +1,91 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "helpers.h" +#include "xAODDataSource/MakeDataFrame.h" + +// Framework include(s). +#include "xAODRootAccess/Init.h" + +// EDM include(s). +#include "AthContainers/ConstDataVector.h" +#include "xAODEgamma/ElectronContainer.h" +#include "xAODMuon/MuonContainer.h" + +int main() { + + // Set up the runtime environment. + ROOT::EnableImplicitMT(); + CHECK( xAOD::Init() ); + + // Create a data frame object. + auto df = xAOD::MakeDataFrame( "${ASG_TEST_FILE_DATA}" ); + + // Define new variables using element links. + auto variables = df + .Define( "HighPtElectrons", + []( const xAOD::ElectronContainer& electrons ) { + ConstDataVector< xAOD::ElectronContainer > + result( SG::VIEW_ELEMENTS ); + for( const xAOD::Electron* el : electrons ) { + if( el->pt() > 10000.0 ) { + result.push_back( el ); + } + } + return xAOD::ElectronContainer( *( result.asDataVector() ) ); + }, { "Electrons" } ) + .Define( "el_trk_pt_diff", + []( const xAOD::ElectronContainer& electrons ) { + std::vector< float > result; + result.reserve( electrons.size() ); + for( const xAOD::Electron* el : electrons ) { + result.push_back( el->pt() - el->trackParticle()->pt() ); + } + return result; + }, { "HighPtElectrons" } ) + .Define( "HighPtMuons", + []( const xAOD::MuonContainer& muons ) { + ConstDataVector< xAOD::MuonContainer > + result( SG::VIEW_ELEMENTS ); + for( const xAOD::Muon* mu : muons ) { + if( ( mu->pt() > 10000.0 ) && + ( mu->muonType() == xAOD::Muon::Combined ) ) { + result.push_back( mu ); + } + } + return xAOD::MuonContainer( *( result.asDataVector() ) ); + }, { "Muons" } ) + .Define( "mu_trk_pt_diff", + []( const xAOD::MuonContainer& muons ) { + std::vector< float > result; + result.reserve( muons.size() ); + for( const xAOD::Muon* mu : muons ) { + result.push_back( mu->pt() - + mu->trackParticle( xAOD::Muon::InnerDetectorTrackParticle )->pt() ); + } + return result; + }, { "HighPtMuons" } ); + + // Create histograms from these variables. + auto el_hist = variables.Histo1D( { "el_hist", + "Electron p_{T} - Track p_{T};" + "p_{T} diff. [MeV];Entries", + 128, -100000.0, 100000.0 }, + "el_trk_pt_diff" ); + auto mu_hist = variables.Histo1D( { "mu_hist", + "Muon p_{T} - Track p_{T};" + "p_{T} diff. [MeV];Entries", + 128, -10000.0, 10000.0 }, + "mu_trk_pt_diff" ); + + // Print some basic properties of the created histograms. + std::cout << "el_hist->GetMaximumBin() = " << el_hist->GetMaximumBin() + << std::endl; + std::cout << "mu_hist->GetMaximumBin() = " << mu_hist->GetMaximumBin() + << std::endl; + + // Return gracefully. + return 0; +} diff --git a/Control/xAODDataSource/test/dataFrameTypeConversion_test.cxx b/Control/xAODDataSource/test/dataFrameTypeConversion_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ac1c3fd5c4d0c239c9824a151060baacb38ff58e --- /dev/null +++ b/Control/xAODDataSource/test/dataFrameTypeConversion_test.cxx @@ -0,0 +1,58 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "helpers.h" +#include "xAODDataSource/MakeDataFrame.h" + +// Framework include(s). +#include "xAODRootAccess/Init.h" + +// EDM include(s). +#include "xAODBase/IParticleContainer.h" +#include "xAODEgamma/ElectronContainer.h" + +// System include(s). +#include <iostream> + +int main() { + + // Set up the runtime environment. + ROOT::EnableImplicitMT(); + CHECK( xAOD::Init() ); + + // Create a data frame object. + auto df = xAOD::MakeDataFrame( "${ASG_TEST_FILE_DATA}" ); + + // Use the electrons through their base class. + auto df1 = df.Define( "el_pt_from_iparticle", + []( const xAOD::IParticleContainer& particles ) { + std::vector< float > result; + result.reserve( particles.size() ); + for( const xAOD::IParticle* p : particles ) { + result.push_back( p->pt() ); + } + return result; + }, { "Electrons" } ); + + // Use the electrons through their concrete type. + auto df2 = df.Define( "el_pt_from_electron", + []( const xAOD::ElectronContainer& electrons ) { + std::vector< float > result; + result.reserve( electrons.size() ); + for( const xAOD::Electron* el : electrons ) { + result.push_back( el->pt() ); + } + return result; + }, { "Electrons" } ); + + // Make the code run. + std::cout << "el_pt_from_iparticle.Mean() = " + << df1.Mean( "el_pt_from_iparticle" ).GetValue() << std::endl; + std::cout << "el_pt_from_electron.Mean() = " + << df2.Mean( "el_pt_from_electron" ).GetValue() << std::endl; + + // Return gracefully. + return 0; +} diff --git a/Control/xAODDataSource/test/dataFrame_test.cxx b/Control/xAODDataSource/test/dataFrame_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..fbd9eb49450a404ae1431e9b3feda77b138d4cbc --- /dev/null +++ b/Control/xAODDataSource/test/dataFrame_test.cxx @@ -0,0 +1,45 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "helpers.h" +#include "xAODDataSource/MakeDataFrame.h" + +// xAOD include(s). +#include "xAODRootAccess/Init.h" +#include "xAODRootAccess/tools/Message.h" +#include "xAODBase/IParticleContainer.h" + +// ROOT include(s). +#include <ROOT/RDataFrame.hxx> +#include <TError.h> + +// System include(s). +#include <iostream> + +int main() { + + // Set up the runtime environment. + CHECK( xAOD::Init() ); + + // Create a data frame object. + auto df = xAOD::MakeDataFrame( "${ASG_TEST_FILE_DATA}" ); + + // Test its histogramming: + auto elPt = df.Define( "ElectronsPt", + []( const xAOD::IParticleContainer& particles ) { + std::vector< float > result; + result.reserve( particles.size() ); + for( const xAOD::IParticle* p : particles ) { + result.push_back( p->pt() ); + } + return result; + }, + { "Electrons" } ); + auto hist = elPt.Histo1D( "ElectronsPt" ); + std::cout << "ElectronsPt entries = " << hist->GetEntries() << std::endl; + + // Return gracefully. + return 0; +} diff --git a/Control/xAODDataSource/test/dataFrame_test.py b/Control/xAODDataSource/test/dataFrame_test.py new file mode 100755 index 0000000000000000000000000000000000000000..556d3c92a813578dffdfcd914f6a9ed4522ee8a5 --- /dev/null +++ b/Control/xAODDataSource/test/dataFrame_test.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# +# Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +# + +# Set up the runtime environment. +import ROOT +ROOT.xAOD.Init().ignore() + +# Create a data frame object. +from xAODDataSource.Helpers import MakexAODDataFrame +df = MakexAODDataFrame( "${ASG_TEST_FILE_DATA}" ) + +# Test its histogramming. +elPt = df.Define( "ElectronsPt", + """ + std::vector< float > result; + result.reserve( Electrons.size() ); + for( const xAOD::IParticle* p : Electrons ) { + result.push_back( p->pt() ); + } + return result; + """ ) +hist = elPt.Histo1D( "ElectronsPt" ) +print( "ElectronsPt entries = %i" % hist.GetEntries() ) diff --git a/Control/xAODDataSource/test/dataSourceEvent_test.cxx b/Control/xAODDataSource/test/dataSourceEvent_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..dcc83dc3200e5689410c6bda83c1467de0cc1d44 --- /dev/null +++ b/Control/xAODDataSource/test/dataSourceEvent_test.cxx @@ -0,0 +1,50 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "helpers.h" +#include "../Root/RDataSourceEvent.h" + +// xAOD include(s). +#include "xAODRootAccess/Init.h" +#include "xAODRootAccess/tools/Message.h" + +// ROOT include(s). +#include <TFile.h> +#include <TError.h> + +// System include(s). +#include <iostream> +#include <memory> + +int main() { + + // Set up the runtime environment. + CHECK( xAOD::Init() ); + + // Open the input file. + std::unique_ptr< TFile > ifile( TFile::Open( "${ASG_TEST_FILE_DATA}", + "READ" ) ); + if( ( ! ifile ) || ifile->IsZombie() ) { + Error( "dataSourceEvent_test", + XAOD_MESSAGE( "Could not open the input file" ) ); + return 1; + } + + // Set up the event object. + xAOD::RDataSourceEvent event; + CHECK( event.readFrom( ifile.get() ) ); + if( event.getEntry( 0 ) < 0 ) { + Error( "dataSourceEvent_test", + XAOD_MESSAGE( "Couldn't load the first event of the input " + "file" ) ); + return 1; + } + + // Print the column names and their C++ types. + std::cout << event.columnAndTypeNames() << std::endl; + + // Return gracefully. + return 0; +} diff --git a/Control/xAODDataSource/test/dataSource_test.cxx b/Control/xAODDataSource/test/dataSource_test.cxx new file mode 100644 index 0000000000000000000000000000000000000000..31e44cb02c53cf498646117ff6437d654123e2dd --- /dev/null +++ b/Control/xAODDataSource/test/dataSource_test.cxx @@ -0,0 +1,35 @@ +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// + +// Local include(s). +#include "helpers.h" +#include "../Root/RDataSource.h" + +// xAOD include(s). +#include "xAODRootAccess/Init.h" +#include "xAODRootAccess/tools/Message.h" + +// ROOT include(s). +#include <TError.h> + +// System include(s). +#include <iostream> + +int main() { + + // Set up the runtime environment. + CHECK( xAOD::Init() ); + + // Set up the data source. + xAOD::RDataSource ds( "${ASG_TEST_FILE_DATA}" ); + ds.Initialise(); + + // Print the ideal entry ranges to process. + std::cout << ds.GetEntryRanges() << std::endl; + // Print the available column/object names. + std::cout << ds.GetColumnNames() << std::endl; + + // Return gracefully. + return 0; +} diff --git a/Control/xAODDataSource/test/helpers.h b/Control/xAODDataSource/test/helpers.h new file mode 100644 index 0000000000000000000000000000000000000000..50140534c452158e8f3f992092e05834963b598d --- /dev/null +++ b/Control/xAODDataSource/test/helpers.h @@ -0,0 +1,50 @@ +// Dear emacs, this is -*- c++ -*- +// +// Copyright (C) 2002-2020 CERN for the benefit of the ATLAS collaboration +// +#ifndef XAODDATASOURCE_TEST_HELPERS_H +#define XAODDATASOURCE_TEST_HELPERS_H + +// Framework include(s). +#include "xAODRootAccess/tools/Message.h" + +// System include(s). +#include <iostream> +#include <vector> + +/// Helper macro for checking return values +#define CHECK( EXP ) \ + do { \ + auto ret = EXP; \ + if( ! ret.isSuccess() ) { \ + Error( "dataSource_test", \ + XAOD_MESSAGE( "Failed to execute: %s" ), #EXP ); \ + return 1; \ + } \ + } while( false ) + +/// Helper print operator +template< typename FIRST, typename SECOND > +std::ostream& operator<< ( std::ostream& out, + const std::pair< FIRST, SECOND >& pair ) { + + out << "[" << pair.first << ", " << pair.second << "]"; + return out; +} + +/// Helper print operator +template< typename T > +std::ostream& operator<< ( std::ostream& out, const std::vector< T >& vec ) { + + out << "["; + for( size_t i = 0; i < vec.size(); ++i ) { + out << vec[ i ]; + if( i + 1 < vec.size() ) { + out << ", "; + } + } + out << "]"; + return out; +} + +#endif // XAODDATASOURCE_TEST_HELPERS_H diff --git a/Control/xAODDataSource/xAODDataSource/MakeDataFrame.h b/Control/xAODDataSource/xAODDataSource/MakeDataFrame.h new file mode 100644 index 0000000000000000000000000000000000000000..4fe6a3853585e593401a08bb05b2f153f0aa976f --- /dev/null +++ b/Control/xAODDataSource/xAODDataSource/MakeDataFrame.h @@ -0,0 +1,31 @@ +// Dear emacs, this is -*- c++ -*- +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// +#ifndef XAODDATASOURCE_MAKEDATAFRAME_H +#define XAODDATASOURCE_MAKEDATAFRAME_H + +// ROOT include(s). +#include <ROOT/RDataFrame.hxx> +#include <ROOT/RStringView.hxx> + +// System include(s). +#include <string> +#include <vector> + +namespace xAOD { + + /// Helper function for creating an xAOD reading data frame + ROOT::RDataFrame + MakeDataFrame( std::string_view fileNameGlob, + std::string_view treeName = "CollectionTree", + bool verboseOutput = false ); + /// Helper function for creating an xAOD reading data frame + ROOT::RDataFrame + MakeDataFrame( const std::vector< std::string >& fileNames, + std::string_view treeName = "CollectionTree", + bool verboseOutput = false ); + +} // namespace xAOD + +#endif // XAODDATASOURCE_MAKEDATAFRAME_H diff --git a/Control/xAODDataSource/xAODDataSource/selection.xml b/Control/xAODDataSource/xAODDataSource/selection.xml new file mode 100644 index 0000000000000000000000000000000000000000..47cf3ce00ed73f311e963b29f3b728ff8e6f5a73 --- /dev/null +++ b/Control/xAODDataSource/xAODDataSource/selection.xml @@ -0,0 +1,7 @@ +<!-- Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration --> +<lcgdict> + + <struct name="xAOD::ROOT6_xAODDataSource_WorkAround_Dummy" /> + <function name="xAOD::MakeDataFrame" /> + +</lcgdict> diff --git a/Control/xAODDataSource/xAODDataSource/xAODDataSourceDict.h b/Control/xAODDataSource/xAODDataSource/xAODDataSourceDict.h new file mode 100644 index 0000000000000000000000000000000000000000..5f044bf21f07a004fe8104ba094b6a71897aec72 --- /dev/null +++ b/Control/xAODDataSource/xAODDataSource/xAODDataSourceDict.h @@ -0,0 +1,16 @@ +// Dear emacs, this is -*- c++ -*- +// +// Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration +// +#ifndef XAODDATASOURCE_XAODDATASOURCEDICT_H +#define XAODDATASOURCE_XAODDATASOURCEDICT_H + +// Local include(s). +#include "xAODDataSource/MakeDataFrame.h" + +namespace xAOD { + /// Dummy struct for auto-loading the library + struct ROOT6_xAODDataSource_WorkAround_Dummy {}; +} + +#endif // XAODDATASOURCE_XAODDATASOURCEDICT_H diff --git a/Projects/AnalysisBase/package_filters.txt b/Projects/AnalysisBase/package_filters.txt index d10788fd25a3d17825d6f2a0e3fbb751b961a932..931a1535ab3f936b75bf73f524f6ff75915f0b2c 100644 --- a/Projects/AnalysisBase/package_filters.txt +++ b/Projects/AnalysisBase/package_filters.txt @@ -9,6 +9,7 @@ + Control/AthLinksSA + Control/AthToolSupport/.* + Control/CxxUtils ++ Control/xAODDataSource + Control/xAODRootAccess.* + Control/RootUtils + DetectorDescription/GeoPrimitives