From cb5cf312e0e229574c7864dac2239d9ca588f062 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Fri, 13 Dec 2024 17:27:55 +0100 Subject: [PATCH 01/15] DDG4: add EDM4hepFile Reader --- DDG4/edm4hep/EDM4hepFileReader.cpp | 284 +++++++++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 DDG4/edm4hep/EDM4hepFileReader.cpp diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp new file mode 100644 index 000000000..e198f10e5 --- /dev/null +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -0,0 +1,284 @@ +//========================================================================== +// AIDA Detector description implementation +//-------------------------------------------------------------------------- +// Copyright (C) Organisation europeenne pour la Recherche nucleaire (CERN) +// All rights reserved. +// +// For the licensing terms see $DD4hepINSTALL/LICENSE. +// For the list of contributors see $DD4hepINSTALL/doc/CREDITS. +// +// Author : A.Sailer +// +//========================================================================== + +/** \addtogroup Geant4EventReader + * + @{ + \package EDM4hepFileReader + * \brief Plugin to read EDM4hep files + * + * + @} +*/ + +#include +#include +#include +#include +#include +#include +#include + +typedef dd4hep::detail::ReferenceBitMask PropertyMask; + + +namespace dd4hep::sim { + + using MCPARTICLE_MAP=std::map; + + /// get the parameters from the input EDM4hep frame and store them in the EventParameters extension + template void EventParameters::ingestParameters(T const& source) { + // for(auto const& key: intKeys) { + // m_intValues[key] = std::move(intVec); + // } + // for(auto const& key: floatKeys) { + // m_fltValues[key] = std::move(floatVec); + // } + // for(auto const& key: stringKeys) { + // m_strValues[key] = std::move(stringVec); + // } + // } + } + + + /// Base class to read EDM4hep files + /** + * \version 1.0 + * \ingroup DD4HEP_SIMULATION + */ + class EDM4hepFileReader : public Geant4EventReader { + protected: + /// Reference to reader object + podio::ROOTReader m_reader {}; + std::string m_collectionName; + public: + /// Initializing constructor + EDM4hepFileReader(const std::string& nam); + + /// Read an event and fill a vector of MCParticles. + virtual EventReaderStatus readParticleCollection(int event_number, const edm4hep::MCParticleCollection** particles); + virtual EventReaderStatus setParameters(std::map< std::string, std::string >& parameters); + + /// Read an event and fill a vector of MCParticles. + virtual EventReaderStatus readParticles(int event_number, Vertices& vertices, std::vector& particles); + /// Read an event and return a LCCollectionVec of MCParticles. + }; + + + + +/// Initializing constructor +dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) +: Geant4EventReader(nam) +, m_collectionName("MCParticles") +{ + printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); + m_reader.openFile(nam); + auto categories = m_reader.getAvailableCategories(); + for(auto cat: categories) { + std::cout << "Category " << cat << std::endl; + } + + + + m_directAccess = true; +} + + +namespace { + inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, edm4hep::MCParticle* part) { + MCPARTICLE_MAP::const_iterator ip=mcparts.find(part); + if ( ip == mcparts.end() ) { + throw std::runtime_error("Unknown particle identifier look-up!"); + } + return (*ip).second; + } +} + + +/// Read an event and fill a vector of MCParticles. +EDM4hepFileReader::EventReaderStatus +EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vector& particles) { + + podio::Frame frame = m_reader.readEntry("events", event_number); + const auto& primaries = frame.get(m_collectionName); + if ( primaries.isValid() ) { + auto eventNumber = frame.getParameter("EventNumber").value_or(event_number); + auto runNumber = frame.getParameter("RunNumber").value_or(0); + printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", + m_collectionName.c_str(), eventNumber, runNumber); + // Create input event parameters context + try { + Geant4Context* ctx = context(); + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(runNumber); + parameters->setEventNumber(eventNumber); + parameters->ingestParameters(frame); + ctx->event().addExtension(parameters); + } + catch(std::exception &) {} + } else { + return EVENT_READER_EOF; + } + + printout(INFO,"EDM4hepFileReader", "We read the particle collection"); + primaries.print(std::cout); + std::cout << "is valid after " << primaries.isValid() << std::endl; + int NHEP = primaries.size(); + // check if there is at least one particle + if ( NHEP == 0 ) { + printout(WARNING,"EDM4hepFileReader", "We have no particles"); + return EVENT_READER_NO_PRIMARIES; + } + + MCPARTICLE_MAP mcparts; + std::vector mcpcoll; + mcpcoll.resize(NHEP,0); + for(int i=0; ipdgID = pdg; + p->charge = int(mcp.getCharge()*3.0); + p->psx = mom[0]*CLHEP::GeV; + p->psy = mom[1]*CLHEP::GeV; + p->psz = mom[2]*CLHEP::GeV; + p->time = mcp.getTime()*CLHEP::ns; + p->properTime = mcp.getTime()*CLHEP::ns; + p->vsx = vsx[0]*CLHEP::mm; + p->vsy = vsx[1]*CLHEP::mm; + p->vsz = vsx[2]*CLHEP::mm; + p->vex = vex[0]*CLHEP::mm; + p->vey = vex[1]*CLHEP::mm; + p->vez = vex[2]*CLHEP::mm; + p->process = 0; + p->spin[0] = spin[0]; + p->spin[1] = spin[1]; + p->spin[2] = spin[2]; + p->colorFlow[0] = color[0]; + p->colorFlow[1] = color[1]; + p->mass = mcp.getMass()*CLHEP::GeV; + const auto par = mcp.getParents(), &dau=mcp.getDaughters(); + for(int num=dau.size(),k=0; kdaughters.insert(GET_ENTRY(mcparts,&dau_k)); + } + for(int num=par.size(),k=0; kparents.insert(GET_ENTRY(mcparts, &par_k)); + } + + PropertyMask status(p->status); + int genStatus = mcp.getGeneratorStatus(); + // Copy raw generator status + p->genStatus = genStatus&G4PARTICLE_GEN_STATUS_MASK; + m_inputAction->setGeneratorStatus(genStatus, status); + + //fg: we simply add all particles without parents as with their own vertex. + // This might include the incoming beam particles, e.g. in + // the case of lcio files written with Whizard2, which is slightly odd, + // however should be treated correctly in Geant4InputHandling.cpp. + // We no longer make an attempt to identify the incoming particles + // based on the generator status, as this varies widely with different + // generators. + + if ( p->parents.size() == 0 ) { + + Geant4Vertex* vtx = new Geant4Vertex ; + vertices.emplace_back( vtx ); + vtx->x = p->vsx; + vtx->y = p->vsy; + vtx->z = p->vsz; + vtx->time = p->time; + + vtx->out.insert(p->id) ; + } + + if ( mcp.isCreatedInSimulation() ) status.set(G4PARTICLE_SIM_CREATED); + if ( mcp.isBackscatter() ) status.set(G4PARTICLE_SIM_BACKSCATTER); + if ( mcp.vertexIsNotEndpointOfParent() ) status.set(G4PARTICLE_SIM_PARENT_RADIATED); + if ( mcp.isDecayedInTracker() ) status.set(G4PARTICLE_SIM_DECAY_TRACKER); + if ( mcp.isDecayedInCalorimeter() ) status.set(G4PARTICLE_SIM_DECAY_CALO); + if ( mcp.hasLeftDetector() ) status.set(G4PARTICLE_SIM_LEFT_DETECTOR); + if ( mcp.isStopped() ) status.set(G4PARTICLE_SIM_STOPPED); + if ( mcp.isOverlay() ) status.set(G4PARTICLE_SIM_OVERLAY); + particles.emplace_back(p); + } + return EVENT_READER_OK; +} + + +/// Read an event and fill a vector of MCParticles. +Geant4EventReader::EventReaderStatus +dd4hep::sim::EDM4hepFileReader::readParticleCollection(int event_number, const edm4hep::MCParticleCollection** particles) { + std::cout << "Reading event number " << event_number << std::endl; + + podio::Frame frame = m_reader.readEntry("events", event_number); + + *particles = (&frame.get(m_collectionName)); + // std::cout << "collection is valid?: " << particles->isValid() << std::endl; + // std::cout << "size " << particles->size() << std::endl; + // std::cout << particles->getTypeName() << std::endl; + + + (*particles)->print(std::cout); + + if ( (*particles)->isValid() ) { + auto eventNumber = frame.getParameter("EventNumber").value_or(event_number); + auto runNumber = frame.getParameter("RunNumber").value_or(0); + printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", + m_collectionName.c_str(), eventNumber, runNumber); + + // Create input event parameters context + try { + Geant4Context* ctx = context(); + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(runNumber); + parameters->setEventNumber(eventNumber); + parameters->ingestParameters(frame); + ctx->event().addExtension(parameters); + } + catch(std::exception &) {} + + return EVENT_READER_OK; + } + return EVENT_READER_EOF; +} + + + +/// Set the parameters for the class, in particular the name of the MCParticle +/// list +Geant4EventReader::EventReaderStatus +dd4hep::sim::EDM4hepFileReader::setParameters( std::map< std::string, std::string > & parameters ) { + _getParameterValue( parameters, "MCParticleCollectionName", m_collectionName, m_collectionName); + return EVENT_READER_OK; +} + + + +} //end dd4hep::sim + +DECLARE_GEANT4_EVENT_READER_NS(dd4hep::sim,EDM4hepFileReader) From 0969e1f664d33a83eea02f1bf8faad1a0d186ec8 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Fri, 13 Dec 2024 17:28:10 +0100 Subject: [PATCH 02/15] DDSim: allow EDM4hep File reader --- DDG4/python/DDSim/DD4hepSimulation.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/DDG4/python/DDSim/DD4hepSimulation.py b/DDG4/python/DDSim/DD4hepSimulation.py index f037833f8..4fd0f2b51 100644 --- a/DDG4/python/DDSim/DD4hepSimulation.py +++ b/DDG4/python/DDSim/DD4hepSimulation.py @@ -49,6 +49,7 @@ ".stdhep", ".slcio", ".HEPEvt", ".hepevt", ".pairs", ".hepmc", + ".root", ] + HEPMC3_SUPPORTED_EXTENSIONS @@ -445,6 +446,9 @@ def run(self): gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/GuineaPig%d" % index) gen.Input = "Geant4EventReaderGuineaPig|" + inputFile gen.Parameters = self.guineapig.getParameters() + elif inputFile.endswith(".root"): + gen = DDG4.GeneratorAction(kernel, "Geant4InputAction/EDM4hep%d" % index) + gen.Input = "EDM4hepFileReader|" + inputFile else: # this should never happen because we already check at the top, but in case of some LogicError... raise RuntimeError("Unknown input file type: %s" % inputFile) From b40617a94395eb9c001ad55ace7a8d2234124895 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Mon, 16 Dec 2024 13:06:18 +0100 Subject: [PATCH 03/15] ReadEDM4hep: use objectID.index to identify particles --- DDG4/edm4hep/EDM4hepFileReader.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index e198f10e5..54c81b4a9 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -34,7 +34,9 @@ typedef dd4hep::detail::ReferenceBitMask PropertyMask; namespace dd4hep::sim { - using MCPARTICLE_MAP=std::map; + // we use the index of the objectID to identify particles + // we will not support MCParticles from different collections + using MCPARTICLE_MAP=std::map; /// get the parameters from the input EDM4hep frame and store them in the EventParameters extension template void EventParameters::ingestParameters(T const& source) { @@ -96,8 +98,8 @@ dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) namespace { - inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, edm4hep::MCParticle* part) { - MCPARTICLE_MAP::const_iterator ip=mcparts.find(part); + inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, int partID) { + MCPARTICLE_MAP::const_iterator ip = mcparts.find(partID); if ( ip == mcparts.end() ) { throw std::runtime_error("Unknown particle identifier look-up!"); } @@ -142,12 +144,12 @@ EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vect } MCPARTICLE_MAP mcparts; - std::vector mcpcoll; - mcpcoll.resize(NHEP,0); + std::vector mcpcoll; + mcpcoll.resize(NHEP); for(int i=0; idaughters.insert(GET_ENTRY(mcparts,&dau_k)); + p->daughters.insert(GET_ENTRY(mcparts,dau_k.getObjectID().index)); } for(int num=par.size(),k=0; kparents.insert(GET_ENTRY(mcparts, &par_k)); + p->parents.insert(GET_ENTRY(mcparts, par_k.getObjectID().index)); } PropertyMask status(p->status); From 9472b7896cad6497843a23b24a86469b1e609166 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Mon, 16 Dec 2024 13:53:40 +0100 Subject: [PATCH 04/15] EDM4hepReader: ingest all kinds of parameters --- DDG4/edm4hep/EDM4hepFileReader.cpp | 119 ++++++++++++++--------------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index 54c81b4a9..eee04d43c 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -38,18 +39,45 @@ namespace dd4hep::sim { // we will not support MCParticles from different collections using MCPARTICLE_MAP=std::map; - /// get the parameters from the input EDM4hep frame and store them in the EventParameters extension - template void EventParameters::ingestParameters(T const& source) { - // for(auto const& key: intKeys) { - // m_intValues[key] = std::move(intVec); - // } - // for(auto const& key: floatKeys) { - // m_fltValues[key] = std::move(floatVec); - // } - // for(auto const& key: stringKeys) { - // m_strValues[key] = std::move(stringVec); - // } - // } + /// get the parameters from the GenericParameters of the input EDM4hep frame and store them in the EventParameters extension + template void EventParameters::ingestParameters(T const& source) { + auto const& intKeys = source.template getKeys(); + for(auto const& key: intKeys) { + m_intValues[key] = source.template get>(key).value(); + } + auto const& floatKeys = source.template getKeys(); + for(auto const& key: floatKeys) { + m_fltValues[key] = source.template get>(key).value(); + } + auto const& doubleKeys = source.template getKeys(); + for(auto const& key: doubleKeys) { + m_dblValues[key] = source.template get>(key).value(); + } + using std::string; + auto const& stringKeys = source.template getKeys(); + for(auto const& key: stringKeys) { + m_strValues[key] = source.template get>(key).value(); + } + } + + template void RunParameters::ingestParameters(T const& source) { + auto const& intKeys = source.template getKeys(); + for(auto const& key: intKeys) { + m_intValues[key] = source.template get>(key).value(); + } + auto const& floatKeys = source.template getKeys(); + for(auto const& key: floatKeys) { + m_fltValues[key] = source.template get>(key).value(); + } + auto const& doubleKeys = source.template getKeys(); + for(auto const& key: doubleKeys) { + m_dblValues[key] = source.template get>(key).value(); + } + using std::string; + auto const& stringKeys = source.template getKeys(); + for(auto const& key: stringKeys) { + m_strValues[key] = source.template get>(key).value(); + } } @@ -68,12 +96,14 @@ namespace dd4hep::sim { EDM4hepFileReader(const std::string& nam); /// Read an event and fill a vector of MCParticles. - virtual EventReaderStatus readParticleCollection(int event_number, const edm4hep::MCParticleCollection** particles); virtual EventReaderStatus setParameters(std::map< std::string, std::string >& parameters); /// Read an event and fill a vector of MCParticles. virtual EventReaderStatus readParticles(int event_number, Vertices& vertices, std::vector& particles); /// Read an event and return a LCCollectionVec of MCParticles. + + void registerRunParameters(); + }; @@ -86,18 +116,25 @@ dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) { printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); m_reader.openFile(nam); - auto categories = m_reader.getAvailableCategories(); - for(auto cat: categories) { - std::cout << "Category " << cat << std::endl; - } - - - m_directAccess = true; } + +void EDM4hepFileReader::registerRunParameters() { + try { + auto *parameters = new RunParameters(); + podio::Frame metaFrame = m_reader.readEntry("metadata", 0); + parameters->ingestParameters(metaFrame.getParameters()); + context()->run().addExtension(parameters); + + } catch(std::exception &e) { + printout(ERROR,"EDM4hepFileReader::registerRunParameters","Failed to register run parameters: %s", e.what()); + } +} + namespace { + /// Helper function to look up MCParticles from mapping inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, int partID) { MCPARTICLE_MAP::const_iterator ip = mcparts.find(partID); if ( ip == mcparts.end() ) { @@ -125,7 +162,7 @@ EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vect EventParameters *parameters = new EventParameters(); parameters->setRunNumber(runNumber); parameters->setEventNumber(eventNumber); - parameters->ingestParameters(frame); + parameters->ingestParameters(frame.getParameters()); ctx->event().addExtension(parameters); } catch(std::exception &) {} @@ -231,46 +268,6 @@ EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vect return EVENT_READER_OK; } - -/// Read an event and fill a vector of MCParticles. -Geant4EventReader::EventReaderStatus -dd4hep::sim::EDM4hepFileReader::readParticleCollection(int event_number, const edm4hep::MCParticleCollection** particles) { - std::cout << "Reading event number " << event_number << std::endl; - - podio::Frame frame = m_reader.readEntry("events", event_number); - - *particles = (&frame.get(m_collectionName)); - // std::cout << "collection is valid?: " << particles->isValid() << std::endl; - // std::cout << "size " << particles->size() << std::endl; - // std::cout << particles->getTypeName() << std::endl; - - - (*particles)->print(std::cout); - - if ( (*particles)->isValid() ) { - auto eventNumber = frame.getParameter("EventNumber").value_or(event_number); - auto runNumber = frame.getParameter("RunNumber").value_or(0); - printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", - m_collectionName.c_str(), eventNumber, runNumber); - - // Create input event parameters context - try { - Geant4Context* ctx = context(); - EventParameters *parameters = new EventParameters(); - parameters->setRunNumber(runNumber); - parameters->setEventNumber(eventNumber); - parameters->ingestParameters(frame); - ctx->event().addExtension(parameters); - } - catch(std::exception &) {} - - return EVENT_READER_OK; - } - return EVENT_READER_EOF; -} - - - /// Set the parameters for the class, in particular the name of the MCParticle /// list Geant4EventReader::EventReaderStatus From 093cf2bf5769c1a65e8c4cfe1de950a868e25f2c Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 09:17:10 +0100 Subject: [PATCH 05/15] ReadEDM4hep: read runs entry for run parameters --- DDG4/edm4hep/EDM4hepFileReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index eee04d43c..42800733b 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -123,7 +123,7 @@ dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) void EDM4hepFileReader::registerRunParameters() { try { auto *parameters = new RunParameters(); - podio::Frame metaFrame = m_reader.readEntry("metadata", 0); + podio::Frame metaFrame = m_reader.readEntry("runs", 0); parameters->ingestParameters(metaFrame.getParameters()); context()->run().addExtension(parameters); From 1a24134c7972680facd89450190341a35f7734db Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 09:21:19 +0100 Subject: [PATCH 06/15] DDTest: add edm4hep to eventreader test --- DDTest/CMakeLists.txt | 3 +++ DDTest/inputFiles/ZH250_ISR.edm4hep.root | Bin 0 -> 46864 bytes DDTest/src/test_EventReaders.cc | 3 +++ 3 files changed, 6 insertions(+) create mode 100644 DDTest/inputFiles/ZH250_ISR.edm4hep.root diff --git a/DDTest/CMakeLists.txt b/DDTest/CMakeLists.txt index de36d1fd5..9472aa307 100644 --- a/DDTest/CMakeLists.txt +++ b/DDTest/CMakeLists.txt @@ -109,6 +109,9 @@ if (DD4HEP_USE_GEANT4) if(DD4HEP_USE_HEPMC3) target_compile_definitions(${TEST_NAME} PRIVATE DD4HEP_USE_HEPMC3) endif() + if(DD4HEP_USE_EDM4HEP) + target_compile_definitions(${TEST_NAME} PRIVATE DD4HEP_USE_EDM4HEP) + endif() if(DD4HEP_USE_LCIO) target_compile_definitions(${TEST_NAME} PRIVATE DD4HEP_USE_LCIO) endif() diff --git a/DDTest/inputFiles/ZH250_ISR.edm4hep.root b/DDTest/inputFiles/ZH250_ISR.edm4hep.root new file mode 100644 index 0000000000000000000000000000000000000000..2b6b92e4ee31ba7aec1cca248e0bf285949866f6 GIT binary patch literal 46864 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

tz_){Jdp`MJg$DLu_tgdK1yT-?*P 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 E06kzJ6951J literal 0 HcmV?d00001 diff --git a/DDTest/src/test_EventReaders.cc b/DDTest/src/test_EventReaders.cc index d48985884..9bc6cb280 100644 --- a/DDTest/src/test_EventReaders.cc +++ b/DDTest/src/test_EventReaders.cc @@ -46,6 +46,9 @@ int main(int argc, char** argv ){ tests.push_back( TestTuple( "HEPMC3FileReader", "g4pythia.hepmc", /*skipEOF= */ true) ); tests.push_back( TestTuple( "HEPMC3FileReader", "Pythia_output.hepmc", /*skipEOF= */ true) ); #endif + #ifdef DD4HEP_USE_EDM4HEP + tests.push_back( TestTuple( "EDM4hepFileReader", "ZH250_ISR.edm4hep.root", /*skipEOF= */ true) ); + #endif try{ for(std::vector::const_iterator it = tests.begin(); it != tests.end(); ++it) { From 74a4aca7ae5424cac430088349eb9f2f28889d85 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 14:56:37 +0100 Subject: [PATCH 07/15] ReadEDM4hep: read EventHeader information --- DDG4/edm4hep/EDM4hepFileReader.cpp | 68 ++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 22 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index 42800733b..2e7da7b72 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -22,11 +22,15 @@ */ #include + #include -#include #include #include +#include + +#include #include + #include #include @@ -38,7 +42,7 @@ namespace dd4hep::sim { // we use the index of the objectID to identify particles // we will not support MCParticles from different collections using MCPARTICLE_MAP=std::map; - + /// get the parameters from the GenericParameters of the input EDM4hep frame and store them in the EventParameters extension template void EventParameters::ingestParameters(T const& source) { auto const& intKeys = source.template getKeys(); @@ -91,6 +95,8 @@ namespace dd4hep::sim { /// Reference to reader object podio::ROOTReader m_reader {}; std::string m_collectionName; + std::string m_eventHeaderCollectionName; + public: /// Initializing constructor EDM4hepFileReader(const std::string& nam); @@ -113,6 +119,7 @@ namespace dd4hep::sim { dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) : Geant4EventReader(nam) , m_collectionName("MCParticles") +, m_eventHeaderCollectionName("EventHeader") { printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); m_reader.openFile(nam); @@ -123,7 +130,9 @@ dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) void EDM4hepFileReader::registerRunParameters() { try { auto *parameters = new RunParameters(); - podio::Frame metaFrame = m_reader.readEntry("runs", 0); + podio::Frame runFrame = m_reader.readEntry("runs", 0); + parameters->ingestParameters(runFrame.getParameters()); + podio::Frame metaFrame = m_reader.readEntry("metadata", 0); parameters->ingestParameters(metaFrame.getParameters()); context()->run().addExtension(parameters); @@ -132,7 +141,7 @@ void EDM4hepFileReader::registerRunParameters() { } } - + namespace { /// Helper function to look up MCParticles from mapping inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, int partID) { @@ -144,42 +153,57 @@ namespace { } } - + /// Read an event and fill a vector of MCParticles. EDM4hepFileReader::EventReaderStatus EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vector& particles) { podio::Frame frame = m_reader.readEntry("events", event_number); const auto& primaries = frame.get(m_collectionName); - if ( primaries.isValid() ) { - auto eventNumber = frame.getParameter("EventNumber").value_or(event_number); - auto runNumber = frame.getParameter("RunNumber").value_or(0); - printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", - m_collectionName.c_str(), eventNumber, runNumber); - // Create input event parameters context - try { + if (primaries.isValid()) { + //Read the event header collection and add it to the context as an extension + const auto& eventHeaderCollection = frame.get(m_eventHeaderCollectionName); + if(eventHeaderCollection.isValid() && eventHeaderCollection.size() == 1){ + const auto& eh = eventHeaderCollection.at(0); + auto eventNumber = eh.getEventNumber(); + auto runNumber = eh.getRunNumber(); Geant4Context* ctx = context(); - EventParameters *parameters = new EventParameters(); - parameters->setRunNumber(runNumber); - parameters->setEventNumber(eventNumber); - parameters->ingestParameters(frame.getParameters()); - ctx->event().addExtension(parameters); + auto clonedEh = new edm4hep::MutableEventHeader(eh.clone()); + ctx->event().addExtension(clonedEh); + + printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", + m_collectionName.c_str(), eventNumber, runNumber); + // Create input event parameters context + try { + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(runNumber); + parameters->setEventNumber(eventNumber); + parameters->ingestParameters(frame.getParameters()); + ctx->event().addExtension(parameters); + } catch(std::exception &) {} + } else { + printout(WARNING,"EDM4hepFileReader","No EventHeader collection found"); + try { + Geant4Context* ctx = context(); + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(0); + parameters->setEventNumber(event_number); + parameters->ingestParameters(frame.getParameters()); + ctx->event().addExtension(parameters); + } catch(std::exception &) {} } - catch(std::exception &) {} } else { return EVENT_READER_EOF; } printout(INFO,"EDM4hepFileReader", "We read the particle collection"); - primaries.print(std::cout); - std::cout << "is valid after " << primaries.isValid() << std::endl; int NHEP = primaries.size(); // check if there is at least one particle if ( NHEP == 0 ) { printout(WARNING,"EDM4hepFileReader", "We have no particles"); return EVENT_READER_NO_PRIMARIES; } - + MCPARTICLE_MAP mcparts; std::vector mcpcoll; mcpcoll.resize(NHEP); @@ -277,7 +301,7 @@ dd4hep::sim::EDM4hepFileReader::setParameters( std::map< std::string, std::strin } - + } //end dd4hep::sim DECLARE_GEANT4_EVENT_READER_NS(dd4hep::sim,EDM4hepFileReader) From 858cf1eddde63b4c55f19c2a79469074610d4241 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 14:56:55 +0100 Subject: [PATCH 08/15] Geant4Output2EDM4hep: get EventHeader context if it exists --- DDG4/edm4hep/Geant4Output2EDM4hep.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/DDG4/edm4hep/Geant4Output2EDM4hep.cpp b/DDG4/edm4hep/Geant4Output2EDM4hep.cpp index 1ceb1e00c..fec904d00 100644 --- a/DDG4/edm4hep/Geant4Output2EDM4hep.cpp +++ b/DDG4/edm4hep/Geant4Output2EDM4hep.cpp @@ -460,14 +460,24 @@ void Geant4Output2EDM4hep::saveEvent(OutputContext& ctxt) { // this does not compile as create() is we only get a const ref - need to review PODIO EventStore API edm4hep::EventHeaderCollection header_collection; + auto header = header_collection.create(); header.setRunNumber(runNumber); header.setEventNumber(eventNumber); header.setWeight(eventWeight); //not implemented in EDM4hep ? header.setDetectorName(context()->detectorDescription().header().name()); - header.setTimeStamp( std::time(nullptr) ) ; - m_frame.put( std::move(header_collection), "EventHeader"); + header.setTimeStamp(std::time(nullptr)); + + // extract event header, in case we come from edm4hep input + auto* meh = context()->event().extension(false); + if(meh) { + header.setTimeStamp(meh->getTimeStamp()); + for (auto const& weight: meh->getWeights()) { + header.addToWeights(weight); + } + } + m_frame.put(std::move(header_collection), "EventHeader"); saveEventParameters(m_eventParametersInt); saveEventParameters(m_eventParametersFloat); saveEventParameters(m_eventParametersString); From ffecca7caf9f77cde25316c98ee1519681eadd43 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 16:49:36 +0100 Subject: [PATCH 09/15] ReadEDM4hep: protect against missing g4context, minor cleanups --- DDG4/edm4hep/EDM4hepFileReader.cpp | 357 +++++++++++++++-------------- 1 file changed, 179 insertions(+), 178 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index 2e7da7b72..ff68a8c10 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -43,7 +43,8 @@ namespace dd4hep::sim { // we will not support MCParticles from different collections using MCPARTICLE_MAP=std::map; - /// get the parameters from the GenericParameters of the input EDM4hep frame and store them in the EventParameters extension + /// get the parameters from the GenericParameters of the input EDM4hep frame and store them in the EventParameters + /// extension template void EventParameters::ingestParameters(T const& source) { auto const& intKeys = source.template getKeys(); for(auto const& key: intKeys) { @@ -64,6 +65,8 @@ namespace dd4hep::sim { } } + /// get the parameters from the GenericParameters of the input EDM4hep run frame and store them in the RunParameters + /// extension template void RunParameters::ingestParameters(T const& source) { auto const& intKeys = source.template getKeys(); for(auto const& key: intKeys) { @@ -84,8 +87,7 @@ namespace dd4hep::sim { } } - - /// Base class to read EDM4hep files + /// Class to read EDM4hep files /** * \version 1.0 * \ingroup DD4HEP_SIMULATION @@ -94,213 +96,212 @@ namespace dd4hep::sim { protected: /// Reference to reader object podio::ROOTReader m_reader {}; + /// Name of the MCParticle collection to read std::string m_collectionName; + /// Name of the EventHeader collection to read std::string m_eventHeaderCollectionName; public: /// Initializing constructor EDM4hepFileReader(const std::string& nam); - /// Read an event and fill a vector of MCParticles. + /// Read parameters set for this action virtual EventReaderStatus setParameters(std::map< std::string, std::string >& parameters); /// Read an event and fill a vector of MCParticles. virtual EventReaderStatus readParticles(int event_number, Vertices& vertices, std::vector& particles); - /// Read an event and return a LCCollectionVec of MCParticles. - + /// Call to register the run parameters void registerRunParameters(); }; - - - -/// Initializing constructor -dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) -: Geant4EventReader(nam) -, m_collectionName("MCParticles") -, m_eventHeaderCollectionName("EventHeader") -{ - printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); - m_reader.openFile(nam); - m_directAccess = true; -} - - -void EDM4hepFileReader::registerRunParameters() { - try { - auto *parameters = new RunParameters(); - podio::Frame runFrame = m_reader.readEntry("runs", 0); - parameters->ingestParameters(runFrame.getParameters()); - podio::Frame metaFrame = m_reader.readEntry("metadata", 0); - parameters->ingestParameters(metaFrame.getParameters()); - context()->run().addExtension(parameters); - - } catch(std::exception &e) { - printout(ERROR,"EDM4hepFileReader::registerRunParameters","Failed to register run parameters: %s", e.what()); + /// Initializing constructor + dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) + : Geant4EventReader(nam) + , m_collectionName("MCParticles") + , m_eventHeaderCollectionName("EventHeader") + { + printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); + m_reader.openFile(nam); + m_directAccess = true; } -} + void EDM4hepFileReader::registerRunParameters() { + try { + auto *parameters = new RunParameters(); + podio::Frame runFrame = m_reader.readEntry("runs", 0); + parameters->ingestParameters(runFrame.getParameters()); + podio::Frame metaFrame = m_reader.readEntry("metadata", 0); + parameters->ingestParameters(metaFrame.getParameters()); + context()->run().addExtension(parameters); + } catch(std::exception &e) { + printout(ERROR,"EDM4hepFileReader::registerRunParameters","Failed to register run parameters: %s", e.what()); + } + } -namespace { - /// Helper function to look up MCParticles from mapping - inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, int partID) { - MCPARTICLE_MAP::const_iterator ip = mcparts.find(partID); - if ( ip == mcparts.end() ) { - throw std::runtime_error("Unknown particle identifier look-up!"); + namespace { + /// Helper function to look up MCParticles from mapping + inline int GET_ENTRY(MCPARTICLE_MAP const& mcparts, int partID) { + MCPARTICLE_MAP::const_iterator ip = mcparts.find(partID); + if ( ip == mcparts.end() ) { + throw std::runtime_error("Unknown particle identifier look-up!"); + } + return (*ip).second; } - return (*ip).second; } -} - - -/// Read an event and fill a vector of MCParticles. -EDM4hepFileReader::EventReaderStatus -EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vector& particles) { - - podio::Frame frame = m_reader.readEntry("events", event_number); - const auto& primaries = frame.get(m_collectionName); - if (primaries.isValid()) { - //Read the event header collection and add it to the context as an extension - const auto& eventHeaderCollection = frame.get(m_eventHeaderCollectionName); - if(eventHeaderCollection.isValid() && eventHeaderCollection.size() == 1){ - const auto& eh = eventHeaderCollection.at(0); - auto eventNumber = eh.getEventNumber(); - auto runNumber = eh.getRunNumber(); - Geant4Context* ctx = context(); - auto clonedEh = new edm4hep::MutableEventHeader(eh.clone()); - ctx->event().addExtension(clonedEh); + /// Read an event and fill a vector of MCParticles + EDM4hepFileReader::EventReaderStatus + EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vector& particles) { + m_currEvent = event_number; + podio::Frame frame = m_reader.readEntry("events", event_number); + const auto& primaries = frame.get(m_collectionName); + int eventNumber = event_number, runNumber = 0; + if (primaries.isValid()) { + //Read the event header collection and add it to the context as an extension + const auto& eventHeaderCollection = frame.get(m_eventHeaderCollectionName); + if(eventHeaderCollection.isValid() && eventHeaderCollection.size() == 1){ + const auto& eh = eventHeaderCollection.at(0); + eventNumber = eh.getEventNumber(); + runNumber = eh.getRunNumber(); + try { + Geant4Context* ctx = context(); + ctx->event().addExtension(new edm4hep::MutableEventHeader(eh.clone())); + } catch(std::exception& ) {} + // Create input event parameters context + try { + Geant4Context* ctx = context(); + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(runNumber); + parameters->setEventNumber(eventNumber); + parameters->ingestParameters(frame.getParameters()); + ctx->event().addExtension(parameters); + } catch(std::exception &) {} + } else { + printout(WARNING,"EDM4hepFileReader","No EventHeader collection found or too many event headers!"); + try { + Geant4Context* ctx = context(); + EventParameters *parameters = new EventParameters(); + parameters->setRunNumber(0); + parameters->setEventNumber(event_number); + parameters->ingestParameters(frame.getParameters()); + ctx->event().addExtension(parameters); + } catch(std::exception &) {} + } printout(INFO,"EDM4hepFileReader","read collection %s from event %d in run %d ", m_collectionName.c_str(), eventNumber, runNumber); - // Create input event parameters context - try { - EventParameters *parameters = new EventParameters(); - parameters->setRunNumber(runNumber); - parameters->setEventNumber(eventNumber); - parameters->ingestParameters(frame.getParameters()); - ctx->event().addExtension(parameters); - } catch(std::exception &) {} + } else { - printout(WARNING,"EDM4hepFileReader","No EventHeader collection found"); - try { - Geant4Context* ctx = context(); - EventParameters *parameters = new EventParameters(); - parameters->setRunNumber(0); - parameters->setEventNumber(event_number); - parameters->ingestParameters(frame.getParameters()); - ctx->event().addExtension(parameters); - } catch(std::exception &) {} + return EVENT_READER_EOF; } - } else { - return EVENT_READER_EOF; - } - - printout(INFO,"EDM4hepFileReader", "We read the particle collection"); - int NHEP = primaries.size(); - // check if there is at least one particle - if ( NHEP == 0 ) { - printout(WARNING,"EDM4hepFileReader", "We have no particles"); - return EVENT_READER_NO_PRIMARIES; - } - - MCPARTICLE_MAP mcparts; - std::vector mcpcoll; - mcpcoll.resize(NHEP); - for(int i=0; ipdgID = pdg; - p->charge = int(mcp.getCharge()*3.0); - p->psx = mom[0]*CLHEP::GeV; - p->psy = mom[1]*CLHEP::GeV; - p->psz = mom[2]*CLHEP::GeV; - p->time = mcp.getTime()*CLHEP::ns; - p->properTime = mcp.getTime()*CLHEP::ns; - p->vsx = vsx[0]*CLHEP::mm; - p->vsy = vsx[1]*CLHEP::mm; - p->vsz = vsx[2]*CLHEP::mm; - p->vex = vex[0]*CLHEP::mm; - p->vey = vex[1]*CLHEP::mm; - p->vez = vex[2]*CLHEP::mm; - p->process = 0; - p->spin[0] = spin[0]; - p->spin[1] = spin[1]; - p->spin[2] = spin[2]; - p->colorFlow[0] = color[0]; - p->colorFlow[1] = color[1]; - p->mass = mcp.getMass()*CLHEP::GeV; - const auto par = mcp.getParents(), &dau=mcp.getDaughters(); - for(int num=dau.size(),k=0; kdaughters.insert(GET_ENTRY(mcparts,dau_k.getObjectID().index)); - } - for(int num=par.size(),k=0; kparents.insert(GET_ENTRY(mcparts, par_k.getObjectID().index)); + printout(INFO,"EDM4hepFileReader", "We read the particle collection"); + int NHEP = primaries.size(); + // check if there is at least one particle + if ( NHEP == 0 ) { + printout(WARNING,"EDM4hepFileReader", "We have no particles"); + return EVENT_READER_NO_PRIMARIES; } - PropertyMask status(p->status); - int genStatus = mcp.getGeneratorStatus(); - // Copy raw generator status - p->genStatus = genStatus&G4PARTICLE_GEN_STATUS_MASK; - m_inputAction->setGeneratorStatus(genStatus, status); - - //fg: we simply add all particles without parents as with their own vertex. - // This might include the incoming beam particles, e.g. in - // the case of lcio files written with Whizard2, which is slightly odd, - // however should be treated correctly in Geant4InputHandling.cpp. - // We no longer make an attempt to identify the incoming particles - // based on the generator status, as this varies widely with different - // generators. - - if ( p->parents.size() == 0 ) { - - Geant4Vertex* vtx = new Geant4Vertex ; - vertices.emplace_back( vtx ); - vtx->x = p->vsx; - vtx->y = p->vsy; - vtx->z = p->vsz; - vtx->time = p->time; - - vtx->out.insert(p->id) ; + MCPARTICLE_MAP mcparts; + std::vector mcpcoll; + mcpcoll.resize(NHEP); + for(int i=0; ipdgID = pdg; + p->charge = int(mcp.getCharge()*3.0); + p->psx = mom[0]*CLHEP::GeV; + p->psy = mom[1]*CLHEP::GeV; + p->psz = mom[2]*CLHEP::GeV; + p->time = mcp.getTime()*CLHEP::ns; + p->properTime = mcp.getTime()*CLHEP::ns; + p->vsx = vsx[0]*CLHEP::mm; + p->vsy = vsx[1]*CLHEP::mm; + p->vsz = vsx[2]*CLHEP::mm; + p->vex = vex[0]*CLHEP::mm; + p->vey = vex[1]*CLHEP::mm; + p->vez = vex[2]*CLHEP::mm; + p->process = 0; + p->spin[0] = spin[0]; + p->spin[1] = spin[1]; + p->spin[2] = spin[2]; + p->colorFlow[0] = color[0]; + p->colorFlow[1] = color[1]; + p->mass = mcp.getMass()*CLHEP::GeV; + const auto par = mcp.getParents(), &dau=mcp.getDaughters(); + for(int num=dau.size(),k=0; kdaughters.insert(GET_ENTRY(mcparts,dau_k.getObjectID().index)); + } + for(int num=par.size(),k=0; kparents.insert(GET_ENTRY(mcparts, par_k.getObjectID().index)); + } + + PropertyMask status(p->status); + int genStatus = mcp.getGeneratorStatus(); + // Copy raw generator status + p->genStatus = genStatus&G4PARTICLE_GEN_STATUS_MASK; + if(m_inputAction) { + // in some tests we do not set up the inputAction + m_inputAction->setGeneratorStatus(genStatus, status); + } + + //fg: we simply add all particles without parents as with their own vertex. + // This might include the incoming beam particles, e.g. in + // the case of lcio files written with Whizard2, which is slightly odd, + // however should be treated correctly in Geant4InputHandling.cpp. + // We no longer make an attempt to identify the incoming particles + // based on the generator status, as this varies widely with different + // generators. + + if ( p->parents.size() == 0 ) { + + Geant4Vertex* vtx = new Geant4Vertex ; + vertices.emplace_back( vtx ); + vtx->x = p->vsx; + vtx->y = p->vsy; + vtx->z = p->vsz; + vtx->time = p->time; + + vtx->out.insert(p->id) ; + } + + if ( mcp.isCreatedInSimulation() ) status.set(G4PARTICLE_SIM_CREATED); + if ( mcp.isBackscatter() ) status.set(G4PARTICLE_SIM_BACKSCATTER); + if ( mcp.vertexIsNotEndpointOfParent() ) status.set(G4PARTICLE_SIM_PARENT_RADIATED); + if ( mcp.isDecayedInTracker() ) status.set(G4PARTICLE_SIM_DECAY_TRACKER); + if ( mcp.isDecayedInCalorimeter() ) status.set(G4PARTICLE_SIM_DECAY_CALO); + if ( mcp.hasLeftDetector() ) status.set(G4PARTICLE_SIM_LEFT_DETECTOR); + if ( mcp.isStopped() ) status.set(G4PARTICLE_SIM_STOPPED); + if ( mcp.isOverlay() ) status.set(G4PARTICLE_SIM_OVERLAY); + particles.emplace_back(p); + } + return EVENT_READER_OK; } - return EVENT_READER_OK; -} - -/// Set the parameters for the class, in particular the name of the MCParticle -/// list -Geant4EventReader::EventReaderStatus -dd4hep::sim::EDM4hepFileReader::setParameters( std::map< std::string, std::string > & parameters ) { - _getParameterValue( parameters, "MCParticleCollectionName", m_collectionName, m_collectionName); - return EVENT_READER_OK; -} - + /// Set the parameters for the class, in particular the name of the MCParticle + /// list + Geant4EventReader::EventReaderStatus + dd4hep::sim::EDM4hepFileReader::setParameters( std::map< std::string, std::string > & parameters ) { + _getParameterValue(parameters, "MCParticleCollectionName", m_collectionName, m_collectionName); + _getParameterValue(parameters, "EventHeaderCollectionName", m_eventHeaderCollectionName, m_eventHeaderCollectionName); + return EVENT_READER_OK; + } } //end dd4hep::sim From 7e6e4d1cfd45b6b4180259239a3ac263e12bd441 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 16:50:50 +0100 Subject: [PATCH 10/15] LCIOReader: protect against missing Gean4InputAction, like in test_eventreader --- DDG4/lcio/LCIOEventReader.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/DDG4/lcio/LCIOEventReader.cpp b/DDG4/lcio/LCIOEventReader.cpp index e919ab9d1..4b0bc2b7a 100644 --- a/DDG4/lcio/LCIOEventReader.cpp +++ b/DDG4/lcio/LCIOEventReader.cpp @@ -121,7 +121,10 @@ LCIOEventReader::readParticles(int event_number, int genStatus = mcp->getGeneratorStatus(); // Copy raw generator status p->genStatus = genStatus&G4PARTICLE_GEN_STATUS_MASK; - m_inputAction->setGeneratorStatus(genStatus, status); + if(m_inputAction) { + // in some tests we do not set up the inputAction + m_inputAction->setGeneratorStatus(genStatus, status); + } //fg: we simply add all particles without parents as with their own vertex. // This might include the incoming beam particles, e.g. in From 0e84b3aae5aa719b0099e68ee24db2b4090e7ea9 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 16:51:16 +0100 Subject: [PATCH 11/15] test_EventReaders: typos, tweaks, testing EDM4hep with direct access --- DDTest/src/test_EventReaders.cc | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/DDTest/src/test_EventReaders.cc b/DDTest/src/test_EventReaders.cc index 9bc6cb280..2384402ed 100644 --- a/DDTest/src/test_EventReaders.cc +++ b/DDTest/src/test_EventReaders.cc @@ -64,29 +64,27 @@ int main(int argc, char** argv ){ continue; } test( thisReader->currentEventNumber() == 0 , readerType + std::string("Initial Event Number") ); - thisReader->moveToEvent(1); - test( thisReader->currentEventNumber() == 1 , readerType + std::string("Event Number after Skip") ); + if (!thisReader->hasDirectAccess()) { + thisReader->moveToEvent(1); + test( thisReader->currentEventNumber() == 1 , readerType + std::string("Event Number after Skip") ); + } std::vector particles; std::vector vertices ; - - dd4hep::sim::Geant4EventReader::EventReaderStatus sc = thisReader->readParticles(3,vertices,particles); + dd4hep::sim::Geant4EventReader::EventReaderStatus sc = thisReader->readParticles(2,vertices,particles); std::for_each(particles.begin(),particles.end(),dd4hep::detail::deleteObject); test( thisReader->currentEventNumber() == 2 && sc == dd4hep::sim::Geant4EventReader::EVENT_READER_OK, readerType + std::string("Event Number Read") ); - //Reset Reader to check what happens if moving to far in the file + //Reset Reader to check what happens if moving too far in the file if (not skipEOF) { thisReader = dd4hep::PluginService::Create(readerType, std::move(inputFile)); sc = thisReader->moveToEvent(1000000); test( sc != dd4hep::sim::Geant4EventReader::EVENT_READER_OK , readerType + std::string("EventReader False") ); } } - } catch( std::exception &e ){ - //} catch( ... ){ - - test.log( e.what() ); - test.error( "exception occurred" ); + test.error("Exception occurred:"); + test.log(e.what()); } return 0; } From 818d1f619f6773a06b4bc4bd66528fa422684a9c Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Tue, 17 Dec 2024 17:17:26 +0100 Subject: [PATCH 12/15] ReadEDM4hep: make metadata and runs frame optional --- DDG4/edm4hep/EDM4hepFileReader.cpp | 20 ++++++++++++++++---- DDTest/CMakeLists.txt | 9 +++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index ff68a8c10..1613f14bd 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -129,10 +129,22 @@ namespace dd4hep::sim { void EDM4hepFileReader::registerRunParameters() { try { auto *parameters = new RunParameters(); - podio::Frame runFrame = m_reader.readEntry("runs", 0); - parameters->ingestParameters(runFrame.getParameters()); - podio::Frame metaFrame = m_reader.readEntry("metadata", 0); - parameters->ingestParameters(metaFrame.getParameters()); + try { + podio::Frame runFrame = m_reader.readEntry("runs", 0); + parameters->ingestParameters(runFrame.getParameters()); + } catch (std::runtime_error& e) { + // we ignore if we do not have runs information + } catch(std::invalid_argument&) { + // we ignore if we do not have runs information + } + try { + podio::Frame metaFrame = m_reader.readEntry("metadata", 0); + parameters->ingestParameters(metaFrame.getParameters()); + } catch (std::runtime_error& e) { + // we ignore if we do not have metadata information + } catch(std::invalid_argument&) { + // we ignore if we do not have metadata information + } context()->run().addExtension(parameters); } catch(std::exception &e) { printout(ERROR,"EDM4hepFileReader::registerRunParameters","Failed to register run parameters: %s", e.what()); diff --git a/DDTest/CMakeLists.txt b/DDTest/CMakeLists.txt index 9472aa307..e24d9d623 100644 --- a/DDTest/CMakeLists.txt +++ b/DDTest/CMakeLists.txt @@ -164,6 +164,15 @@ if (DD4HEP_USE_GEANT4) PASS_REGULAR_EXPRESSION "Deleting object StepActionCLI1" ) + if(DD4HEP_USE_EDM4HEP) + add_test( t_ddsimEDM4hepPlugins "${CMAKE_INSTALL_PREFIX}/bin/run_test.sh" + ddsim --compactFile=${CMAKE_INSTALL_PREFIX}/DDDetectors/compact/SiD.xml --runType=batch -N=3 + --outputFile=ddsim_edm4hep_ZH.root + --inputFiles=${CMAKE_CURRENT_SOURCE_DIR}/inputFiles/ZH250_ISR.edm4hep.root + --part.userParticleHandler= + ) + endif() + endif() install(DIRECTORY include/DD4hep DESTINATION include) From 8ff2418a9b855ba7d4d1cc4b83eb6f169cb903f3 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Wed, 18 Dec 2024 13:51:30 +0100 Subject: [PATCH 13/15] ReadEDM4hep: do not treat colorflow, since it will be gone soon --- DDG4/edm4hep/EDM4hepFileReader.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index 1613f14bd..cf81ac8c7 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -232,7 +232,6 @@ namespace dd4hep::sim { const auto vsx = mcp.getVertex(); const auto vex = mcp.getEndpoint(); const auto spin = mcp.getSpin(); - const auto color = mcp.getColorFlow(); const int pdg = mcp.getPDG(); p->pdgID = pdg; p->charge = int(mcp.getCharge()*3.0); @@ -251,8 +250,8 @@ namespace dd4hep::sim { p->spin[0] = spin[0]; p->spin[1] = spin[1]; p->spin[2] = spin[2]; - p->colorFlow[0] = color[0]; - p->colorFlow[1] = color[1]; + p->colorFlow[0] = 0; + p->colorFlow[1] = 0; p->mass = mcp.getMass()*CLHEP::GeV; const auto par = mcp.getParents(), &dau=mcp.getDaughters(); for(int num=dau.size(),k=0; k Date: Wed, 18 Dec 2024 15:19:03 +0100 Subject: [PATCH 14/15] ReadEDM4hep: simplify ingestion functions --- DDG4/edm4hep/EDM4hepFileReader.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index cf81ac8c7..f21441fef 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -46,44 +46,34 @@ namespace dd4hep::sim { /// get the parameters from the GenericParameters of the input EDM4hep frame and store them in the EventParameters /// extension template void EventParameters::ingestParameters(T const& source) { - auto const& intKeys = source.template getKeys(); - for(auto const& key: intKeys) { + for(auto const& key: source.template getKeys()) { m_intValues[key] = source.template get>(key).value(); } - auto const& floatKeys = source.template getKeys(); - for(auto const& key: floatKeys) { + for(auto const& key: source.template getKeys()) { m_fltValues[key] = source.template get>(key).value(); } - auto const& doubleKeys = source.template getKeys(); - for(auto const& key: doubleKeys) { + for(auto const& key: source.template getKeys()) { m_dblValues[key] = source.template get>(key).value(); } - using std::string; - auto const& stringKeys = source.template getKeys(); - for(auto const& key: stringKeys) { - m_strValues[key] = source.template get>(key).value(); + for(auto const& key: source.template getKeys()) { + m_strValues[key] = source.template get>(key).value(); } } /// get the parameters from the GenericParameters of the input EDM4hep run frame and store them in the RunParameters /// extension template void RunParameters::ingestParameters(T const& source) { - auto const& intKeys = source.template getKeys(); - for(auto const& key: intKeys) { + for(auto const& key: source.template getKeys()) { m_intValues[key] = source.template get>(key).value(); } - auto const& floatKeys = source.template getKeys(); - for(auto const& key: floatKeys) { + for(auto const& key: source.template getKeys()) { m_fltValues[key] = source.template get>(key).value(); } - auto const& doubleKeys = source.template getKeys(); - for(auto const& key: doubleKeys) { + for(auto const& key: source.template getKeys()) { m_dblValues[key] = source.template get>(key).value(); } - using std::string; - auto const& stringKeys = source.template getKeys(); - for(auto const& key: stringKeys) { - m_strValues[key] = source.template get>(key).value(); + for(auto const& key: source.template getKeys()) { + m_strValues[key] = source.template get>(key).value(); } } From 60603ac2817b9a6e92fcda3bb0e0cedf441cb257 Mon Sep 17 00:00:00 2001 From: Andre Sailer Date: Wed, 18 Dec 2024 15:19:26 +0100 Subject: [PATCH 15/15] ReadEDM4hep: use generic Reader --- DDG4/CMakeLists.txt | 2 +- DDG4/edm4hep/EDM4hepFileReader.cpp | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/DDG4/CMakeLists.txt b/DDG4/CMakeLists.txt index 0a341469d..04f6f6fae 100644 --- a/DDG4/CMakeLists.txt +++ b/DDG4/CMakeLists.txt @@ -105,7 +105,7 @@ ENDIF() IF(TARGET EDM4HEP::edm4hep) dd4hep_add_plugin(DDG4EDM4HEP SOURCES edm4hep/*.cpp - USES DD4hep::DDG4 EDM4HEP::edm4hep EDM4HEP::edm4hepDict podio::podio podio::podioDict podio::podioRootIO + USES DD4hep::DDG4 EDM4HEP::edm4hep EDM4HEP::edm4hepDict podio::podio podio::podioDict podio::podioRootIO podio::podioIO ) install(TARGETS DDG4EDM4HEP EXPORT DD4hep LIBRARY DESTINATION lib) set_target_properties(DDG4EDM4HEP PROPERTIES VERSION ${DD4hep_VERSION} SOVERSION ${DD4hep_SOVERSION}) diff --git a/DDG4/edm4hep/EDM4hepFileReader.cpp b/DDG4/edm4hep/EDM4hepFileReader.cpp index f21441fef..563f8ce66 100644 --- a/DDG4/edm4hep/EDM4hepFileReader.cpp +++ b/DDG4/edm4hep/EDM4hepFileReader.cpp @@ -32,7 +32,7 @@ #include #include -#include +#include typedef dd4hep::detail::ReferenceBitMask PropertyMask; @@ -85,7 +85,7 @@ namespace dd4hep::sim { class EDM4hepFileReader : public Geant4EventReader { protected: /// Reference to reader object - podio::ROOTReader m_reader {}; + podio::Reader m_reader; /// Name of the MCParticle collection to read std::string m_collectionName; /// Name of the EventHeader collection to read @@ -108,11 +108,11 @@ namespace dd4hep::sim { /// Initializing constructor dd4hep::sim::EDM4hepFileReader::EDM4hepFileReader(const std::string& nam) : Geant4EventReader(nam) + , m_reader(podio::makeReader(nam)) , m_collectionName("MCParticles") , m_eventHeaderCollectionName("EventHeader") { printout(INFO,"EDM4hepFileReader","Created file reader. Try to open input %s",nam.c_str()); - m_reader.openFile(nam); m_directAccess = true; } @@ -120,7 +120,7 @@ namespace dd4hep::sim { try { auto *parameters = new RunParameters(); try { - podio::Frame runFrame = m_reader.readEntry("runs", 0); + podio::Frame runFrame = m_reader.readFrame("runs", 0); parameters->ingestParameters(runFrame.getParameters()); } catch (std::runtime_error& e) { // we ignore if we do not have runs information @@ -128,7 +128,7 @@ namespace dd4hep::sim { // we ignore if we do not have runs information } try { - podio::Frame metaFrame = m_reader.readEntry("metadata", 0); + podio::Frame metaFrame = m_reader.readFrame("metadata", 0); parameters->ingestParameters(metaFrame.getParameters()); } catch (std::runtime_error& e) { // we ignore if we do not have metadata information @@ -156,7 +156,7 @@ namespace dd4hep::sim { EDM4hepFileReader::EventReaderStatus EDM4hepFileReader::readParticles(int event_number, Vertices& vertices, std::vector& particles) { m_currEvent = event_number; - podio::Frame frame = m_reader.readEntry("events", event_number); + podio::Frame frame = m_reader.readFrame("events", event_number); const auto& primaries = frame.get(m_collectionName); int eventNumber = event_number, runNumber = 0; if (primaries.isValid()) {