diff --git a/include/podio/Reader.h b/include/podio/Reader.h new file mode 100644 index 000000000..254a9f1ea --- /dev/null +++ b/include/podio/Reader.h @@ -0,0 +1,96 @@ +#ifndef PODIO_READER_H +#define PODIO_READER_H + +#include "podio/Frame.h" +#include "podio/podioVersion.h" + +namespace podio { + +class Reader { +public: + struct ReaderConcept { + virtual ~ReaderConcept() = default; + + virtual podio::Frame readNextFrame(const std::string& name) = 0; + virtual podio::Frame readFrame(const std::string& name, size_t index) = 0; + virtual size_t getEntries(const std::string& name) = 0; + virtual podio::Frame readNextEvent() = 0; + virtual podio::Frame readEvent(size_t index) = 0; + virtual size_t getEvents() = 0; + virtual podio::version::Version currentFileVersion() const = 0; + }; + + template + struct ReaderModel : public ReaderConcept { + ReaderModel(T* reader) : m_reader(reader) { + } + ReaderModel(const ReaderModel&) = delete; + ReaderModel& operator=(const ReaderModel&) = delete; + + podio::Frame readNextFrame(const std::string& name) override { + auto maybeFrame = m_reader->readNextEntry(name); + if (maybeFrame) { + return std::move(maybeFrame); + } + throw std::runtime_error("Could not read frame (reading beyond bounds?)"); + } + podio::Frame readNextEvent() override { + return readNextFrame(podio::Category::Event); + } + + podio::Frame readFrame(const std::string& name, size_t index) override { + auto maybeFrame = m_reader->readEntry(name, index); + if (maybeFrame) { + return std::move(maybeFrame); + } + throw std::runtime_error("Could not read frame (reading beyond bounds?)"); + } + podio::Frame readEvent(size_t index) override { + return readFrame(podio::Category::Event, index); + } + size_t getEntries(const std::string& name) override { + return m_reader->getEntries(name); + } + size_t getEvents() override { + return getEntries(podio::Category::Event); + } + podio::version::Version currentFileVersion() const override { + return m_reader->currentFileVersion(); + } + T* m_reader{nullptr}; + }; + + std::unique_ptr m_self{nullptr}; + + template + Reader(std::unique_ptr); + + podio::Frame readNextFrame(const std::string& name) { + return m_self->readNextFrame(name); + } + podio::Frame readNextEvent() { + return readNextFrame(podio::Category::Event); + } + podio::Frame readFrame(const std::string& name, size_t index) { + return m_self->readFrame(name, index); + } + podio::Frame readEvent(size_t index) { + return readFrame(podio::Category::Event, index); + } + size_t getEntries(const std::string& name) { + return m_self->getEntries(name); + } + size_t getEvents() { + return getEntries(podio::Category::Event); + } + podio::version::Version currentFileVersion() const { + return m_self->currentFileVersion(); + } +}; + +std::unique_ptr makeReader(const std::string& filename); +std::unique_ptr makeReader(const std::vector& filename); + +} // namespace podio + +#endif // PODIO_READER_H diff --git a/include/podio/Writer.h b/include/podio/Writer.h new file mode 100644 index 000000000..40a466dca --- /dev/null +++ b/include/podio/Writer.h @@ -0,0 +1,67 @@ +#ifndef PODIO_WRITER_H +#define PODIO_WRITER_H + +#include "podio/Frame.h" +#include "podio/podioVersion.h" + +namespace podio { + +class Writer { +public: + struct WriterConcept { + virtual ~WriterConcept() = default; + + virtual void writeFrame(const podio::Frame& frame, const std::string& category) = 0; + virtual void writeFrame(const podio::Frame& frame, const std::string& category, + const std::vector& collections) = 0; + virtual void writeEvent(const podio::Frame& frame) = 0; + virtual void writeEvent(const podio::Frame& frame, const std::vector& collections) = 0; + }; + + template + struct WriterModel : public WriterConcept { + WriterModel(T* writer) : m_writer(writer) { + } + WriterModel(const WriterModel&) = delete; + WriterModel& operator=(const WriterModel&) = delete; + + void writeFrame(const podio::Frame& frame, const std::string& category) override { + return m_writer->writeFrame(frame, category); + } + void writeFrame(const podio::Frame& frame, const std::string& category, + const std::vector& collections) override { + return m_writer->writeFrame(frame, category, collections); + } + void writeEvent(const podio::Frame& frame) override { + return writeFrame(frame, podio::Category::Event); + } + void writeEvent(const podio::Frame& frame, const std::vector& collections) override { + return writeFrame(frame, podio::Category::Event, collections); + } + std::unique_ptr m_writer{nullptr}; + }; + + std::unique_ptr m_self{nullptr}; + + template + Writer(std::unique_ptr); + + void writeFrame(const podio::Frame& frame, const std::string& category) { + return m_self->writeFrame(frame, category); + } + void writeFrame(const podio::Frame& frame, const std::string& category, const std::vector& collections) { + return m_self->writeFrame(frame, category, collections); + } + void writeEvent(const podio::Frame& frame) { + return writeFrame(frame, podio::Category::Event); + } + void writeEvent(const podio::Frame& frame, const std::vector& collections) { + return writeFrame(frame, podio::Category::Event, collections); + } +}; + +std::unique_ptr makeWriter(const std::string& filename, const std::string& type = "default"); + +} // namespace podio + +#endif // PODIO_WRITER_H diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5e669dc72..de5af7c39 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -80,6 +80,8 @@ SET(root_sources ROOTFrameWriter.cc ROOTFrameReader.cc ROOTLegacyReader.cc + Reader.cc + Writer.cc ) if(ENABLE_RNTUPLE) list(APPEND root_sources @@ -92,6 +94,8 @@ SET(root_headers ${PROJECT_SOURCE_DIR}/include/podio/ROOTFrameReader.h ${PROJECT_SOURCE_DIR}/include/podio/ROOTLegacyReader.h ${PROJECT_SOURCE_DIR}/include/podio/ROOTFrameWriter.h + ${PROJECT_SOURCE_DIR}/include/podio/Reader.h + ${PROJECT_SOURCE_DIR}/include/podio/Writer.h ) if(ENABLE_RNTUPLE) list(APPEND root_headers diff --git a/src/Reader.cc b/src/Reader.cc new file mode 100644 index 000000000..f6c6024b0 --- /dev/null +++ b/src/Reader.cc @@ -0,0 +1,77 @@ +#include "podio/Reader.h" + +#include "podio/ROOTFrameReader.h" +#ifdef PODIO_ENABLE_RNTUPLE + #include "podio/ROOTRNTupleReader.h" +#endif +#ifdef PODIO_ENABLE_SIO + #include "podio/SIOFrameReader.h" +#endif + +#include "TFile.h" +#include "TKey.h" +#include + +namespace podio { + +template +Reader::Reader(std::unique_ptr reader) : m_self(std::make_unique>(reader.release())) { +} + +std::unique_ptr makeReader(const std::string& filename) { + return makeReader(std::vector{filename}); +} + +std::unique_ptr makeReader(const std::vector& filenames) { + + auto suffix = filenames[0].substr(filenames[0].find_last_of(".") + 1); + for (size_t i = 1; i < filenames.size(); ++i) { + if (filenames[i].substr(filenames[i].find_last_of(".") + 1) != suffix) { + std::cout << "ERROR: All files must have the same extension" << std::endl; + return nullptr; + } + } + + std::unique_ptr reader; + + if (suffix == "root") { + // Check only the first file for RNTuples + TFile* file = TFile::Open(filenames[0].c_str()); + bool hasRNTuple = false; + + for (auto key : *file->GetListOfKeys()) { + auto tkey = dynamic_cast(key); + + // if (tkey && tkey->GetClassName() == "ROOT::Experimental::RNTuple") { + if (tkey && std::string(tkey->GetClassName()) == "ROOT::Experimental::RNTuple") { + hasRNTuple = true; + break; + } + } + if (hasRNTuple) { +#ifdef PODIO_ENABLE_RNTUPLE + auto actualReader = std::make_unique(); + actualReader->openFiles(filenames); + reader = std::make_unique(std::move(actualReader)); +#else + throw std::runtime_error("ROOT RNTuple reader not available. Please recompile with ROOT RNTuple support."); +#endif + } else { + auto actualReader = std::make_unique(); + actualReader->openFiles(filenames); + reader = std::make_unique(std::move(actualReader)); + } + } else if (suffix == "sio") { +#ifdef PODIO_ENABLE_SIO + auto actualReader = std::make_unique(); + actualReader->openFiles(filenames); + reader = std::make_unique(std::move(actualReader)); +#else + throw std::runtime_error("SIO reader not available. Please recompile with SIO support."); +#endif + } + + return reader; +} + +} // namespace podio diff --git a/src/Writer.cc b/src/Writer.cc new file mode 100644 index 000000000..2874081d9 --- /dev/null +++ b/src/Writer.cc @@ -0,0 +1,52 @@ +#include "podio/Writer.h" + +#include "podio/ROOTFrameWriter.h" +#ifdef PODIO_ENABLE_RNTUPLE + #include "podio/ROOTRNTupleWriter.h" +#endif +#ifdef PODIO_ENABLE_SIO + #include "podio/SIOFrameWriter.h" +#endif + +#include + +namespace podio { + +template +Writer::Writer(std::unique_ptr reader) : m_self(std::make_unique>(reader.release())) { +} + +std::unique_ptr makeWriter(const std::string& filename, const std::string& type) { + + auto endsWith = [](const std::string& str, const std::string& suffix) { + return str.size() >= suffix.size() && 0 == str.compare(str.size() - suffix.size(), suffix.size(), suffix); + }; + + std::unique_ptr writer; + if ((type == "default" && endsWith(filename, ".root")) || type == "root") { + std::cout << "Calling makeWriter (root)" << std::endl; + auto actualWriter = std::make_unique(filename); + writer = std::make_unique(std::move(actualWriter)); + } else if (type == "rntuple") { +#ifdef PODIO_ENABLE_RNTUPLE + std::cout << "Calling makeWriter (rntuple)" << std::endl; + auto actualWriter = std::make_unique(filename); + writer = std::make_unique(std::move(actualWriter)); +#else + std::cout << "ERROR: RNTuple writer not enabled" << std::endl; +#endif + } else if ((type == "default" && endsWith(filename, ".sio")) || type == "sio") { +#ifdef PODIO_ENABLE_SIO + std::cout << "Calling makeWriter (sio)" << std::endl; + auto actualWriter = std::make_unique(filename); + writer = std::make_unique(std::move(actualWriter)); +#else + std::cout << "ERROR: SIO writer not enabled" << std::endl; +#endif + } else { + std::cout << "ERROR: Unknown writer type " << type << std::endl; + } + return writer; +} + +} // namespace podio diff --git a/tests/root_io/CMakeLists.txt b/tests/root_io/CMakeLists.txt index 1bc906755..20674dc5b 100644 --- a/tests/root_io/CMakeLists.txt +++ b/tests/root_io/CMakeLists.txt @@ -6,6 +6,8 @@ set(root_dependent_tests read_python_frame_root.cpp read_frame_root_multiple.cpp read_and_write_frame_root.cpp + write_interface.cpp + read_interface.cpp ) if(ENABLE_RNTUPLE) set(root_dependent_tests @@ -20,6 +22,8 @@ foreach( sourcefile ${root_dependent_tests} ) CREATE_PODIO_TEST(${sourcefile} "${root_libs}") endforeach() +set_property(TEST read_interface PROPERTY DEPENDS write_interface) + set_tests_properties( read_frame_root read_frame_root_multiple diff --git a/tests/root_io/read_interface.cpp b/tests/root_io/read_interface.cpp new file mode 100644 index 000000000..0886ac32a --- /dev/null +++ b/tests/root_io/read_interface.cpp @@ -0,0 +1,110 @@ +#include "read_frame.h" + +#include "podio/ROOTFrameReader.h" +#include "podio/Reader.h" +#ifdef PODIO_ENABLE_RNTUPLE + #include "podio/ROOTRNTupleReader.h" +#endif + +int read_frames(std::unique_ptr reader) { + + if (reader->getEntries(podio::Category::Event) != 10) { + std::cerr << "Could not read back the number of events correctly. " + << "(expected:" << 10 << ", actual: " << reader->getEntries(podio::Category::Event) << ")" << std::endl; + return 1; + } + + if (reader->getEntries(podio::Category::Event) != reader->getEntries("other_events")) { + std::cerr << "Could not read back the number of events correctly. " + << "(expected:" << 10 << ", actual: " << reader->getEntries("other_events") << ")" << std::endl; + return 1; + } + + // Read the frames in a different order than when writing them here to make + // sure that the writing/reading order does not impose any usage requirements + for (size_t i = 0; i < reader->getEntries(podio::Category::Event); ++i) { + auto frame = reader->readNextFrame(podio::Category::Event); + if (frame.get("emptySubsetColl") == nullptr) { + std::cerr << "Could not retrieve an empty subset collection" << std::endl; + return 1; + } + if (frame.get("emptyCollection") == nullptr) { + std::cerr << "Could not retrieve an empty collection" << std::endl; + return 1; + } + + processEvent(frame, i, reader->currentFileVersion()); + + auto otherFrame = reader->readNextFrame("other_events"); + processEvent(otherFrame, i + 100, reader->currentFileVersion()); + // The other_events category also holds external collections + processExtensions(otherFrame, i + 100, reader->currentFileVersion()); + // As well as a test for the vector members subset category + checkVecMemSubsetColl(otherFrame); + } + + // if (reader->readNextFrame(podio::Category::Event)) { + // std::cerr << "Trying to read more frame data than is present should return a nullptr" << std::endl; + // return 1; + // } + + std::cout << "========================================================\n" << std::endl; + // if (reader->readNextFrame("not_present")) { + // std::cerr << "Trying to read non-existant frame data should return a nullptr" << std::endl; + // return 1; + // } + + // Reading specific (jumping to) entry + { + auto frame = reader->readFrame(podio::Category::Event, 4); + processEvent(frame, 4, reader->currentFileVersion()); + // Reading the next entry after jump, continues from after the jump + auto nextFrame = reader->readNextFrame(podio::Category::Event); + processEvent(nextFrame, 5, reader->currentFileVersion()); + + auto otherFrame = reader->readFrame("other_events", 4); + processEvent(otherFrame, 4 + 100, reader->currentFileVersion()); + if (reader->currentFileVersion() > podio::version::Version{0, 16, 2}) { + processExtensions(otherFrame, 4 + 100, reader->currentFileVersion()); + } + + // Jumping back also works + auto previousFrame = reader->readFrame("other_events", 2); + processEvent(previousFrame, 2 + 100, reader->currentFileVersion()); + if (reader->currentFileVersion() > podio::version::Version{0, 16, 2}) { + processExtensions(previousFrame, 2 + 100, reader->currentFileVersion()); + } + + // Trying to read a Frame that is not present returns a nullptr + // if (reader->readFrame(podio::Category::Event, 10)) { + // std::cerr << "Trying to read a specific entry that does not exist should return a nullptr" << std::endl; + // return 1; + // } + } + + return 0; +} + +int main(int, char**) { + + auto reader = podio::makeReader("example_frame_interface.root"); + if (read_frames(std::move(reader))) { + return 1; + } + +#ifdef PODIO_ENABLE_RNTUPLE + auto readerRNTuple = podio::makeReader("example_frame_rntuple_interface.root"); + if (read_frames(std::move(readerRNTuple))) { + return 1; + } +#endif + +#ifdef PODIO_ENABLE_SIO + auto readerSIO = podio::makeReader("example_frame_sio_interface.sio"); + if (read_frames(std::move(readerSIO))) { + return 1; + } +#endif + + return 0; +} diff --git a/tests/root_io/write_interface.cpp b/tests/root_io/write_interface.cpp new file mode 100644 index 000000000..ec95110c7 --- /dev/null +++ b/tests/root_io/write_interface.cpp @@ -0,0 +1,63 @@ +#include "write_frame.h" + +#include "podio/Writer.h" + +// void write_frames(std::unique_ptr frameWriter) { +// for (int i = 0; i < 10; ++i) { +// auto frame = makeFrame(i); +// frameWriter->writeFrame(frame, podio::Category::Event, collsToWrite); +// } + +// for (int i = 100; i < 110; ++i) { +// auto frame = makeFrame(i); +// frameWriter->writeFrame(frame, "other_events"); +// } + +// frameWriter->finish(); +// } + +void write_frames(std::unique_ptr frameWriter) { + + for (int i = 0; i < 10; ++i) { + auto frame = makeFrame(i); + frameWriter->writeFrame(frame, podio::Category::Event, collsToWrite); + } + + for (int i = 100; i < 110; ++i) { + auto frame = makeFrame(i); + frameWriter->writeFrame(frame, "other_events"); + } +} + +int main(int, char**) { + + auto writer = podio::makeWriter("example_frame_interface.root"); + write_frames(std::move(writer)); + +#ifdef PODIO_ENABLE_RNTUPLE + auto writerRNTuple = podio::makeWriter("example_frame_rntuple_interface.root", "rntuple"); + write_frames(std::move(writerRNTuple)); +#endif + +#ifdef PODIO_ENABLE_SIO + auto writerSIO = podio::makeWriter("example_frame_sio_interface.sio", "sio"); + write_frames(std::move(writerSIO)); +#endif + + // std::unique_ptr frameWriter; + // frameWriter.reset(dynamic_cast(new + // podio::ROOTFrameWriter("example_frame_interface.root"))); + + // write_frames(std::move(frameWriter)); + + // #ifdef PODIO_ENABLE_RNTUPLE + // std::unique_ptr frameWriterRNTuple; + // frameWriterRNTuple.reset( + // dynamic_cast(new + // podio::ROOTRNTupleWriter("example_frame_rntuple_interface.root"))); + + // write_frames(std::move(frameWriterRNTuple)); + // #endif + + return 0; +}