Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-12-09 09:21:10

0001 
0002 #include "JEventProcessorPODIO.h"
0003 
0004 #include <JANA/JApplication.h>
0005 #include <JANA/JApplicationFwd.h>
0006 #include <JANA/Services/JParameterManager.h>
0007 #include <JANA/Utils/JTypeInfo.h>
0008 #include <fmt/core.h>
0009 #include <fmt/format.h>
0010 #include <podio/CollectionBase.h>
0011 #include <podio/Frame.h>
0012 #include <podio/ROOTWriter.h>
0013 #include <algorithm>
0014 #include <exception>
0015 #include <functional>
0016 #include <iterator>
0017 #include <regex>
0018 #include <sstream>
0019 #include <stdexcept>
0020 
0021 #include "services/log/Log_service.h"
0022 
0023 JEventProcessorPODIO::JEventProcessorPODIO() {
0024   SetTypeName(NAME_OF_THIS); // Provide JANA with this class's name
0025 
0026   japp->SetDefaultParameter("podio:output_file", m_output_file,
0027                             "Name of EDM4hep/podio output file to write to. Setting this will "
0028                             "cause the output file to be created and written to.");
0029 
0030   // Allow user to set PODIO:OUTPUT_FILE to "1" to specify using the default name.
0031   if (m_output_file == "1") {
0032     auto* param = japp->GetJParameterManager()->FindParameter("podio:output_file");
0033     if (param != nullptr) {
0034       param->SetValue(param->GetDefault());
0035       m_output_file = param->GetDefault();
0036     }
0037   }
0038 
0039   // Get the output directory path for creating a second copy of the output file at the end of processing.
0040   // (this is duplicating similar functionality in Juggler/Gaudi so assume it is useful).
0041   japp->SetDefaultParameter("podio:output_file_copy_dir", m_output_file_copy_dir,
0042                             "Directory name to make an additional copy of the output file to. Copy "
0043                             "will be done at end of processing. Default is empty string which "
0044                             "means do not make a copy. No check is made on path existing.");
0045 
0046   // Get the list of output collections to include/exclude
0047   std::vector<std::string> output_collections = {
0048       // Header and other metadata
0049       "EventHeader",
0050 
0051       // Truth record
0052       "MCParticles",
0053       "MCBeamElectrons",
0054       "MCBeamProtons",
0055       "MCScatteredElectrons",
0056       "MCScatteredProtons",
0057       "MCParticlesHeadOnFrameNoBeamFX",
0058 
0059       // Central tracking hits combined
0060       "CentralTrackerTruthSeeds",
0061       "CentralTrackingRecHits",
0062       "CentralTrackingRawHitAssociations",
0063       "CentralTrackSeedingResults",
0064       "CentralTrackerMeasurements",
0065 
0066       // Si tracker hits
0067       "SiBarrelTrackerRecHits",
0068       "SiBarrelVertexRecHits",
0069       "SiEndcapTrackerRecHits",
0070 
0071       "SiBarrelRawHits",
0072       "SiBarrelVertexRawHits",
0073       "SiEndcapTrackerRawHits",
0074 
0075       "SiBarrelHits",
0076       "VertexBarrelHits",
0077       "TrackerEndcapHits",
0078 
0079       "SiBarrelRawHitAssociations",
0080       "SiBarrelVertexRawHitAssociations",
0081       "SiEndcapTrackerRawHitAssociations",
0082 
0083       // TOF
0084       "TOFBarrelRecHits",
0085       "TOFEndcapRecHits",
0086 
0087       "TOFBarrelRawHits",
0088       "TOFEndcapRawHits",
0089 
0090       "TOFBarrelHits",
0091       "TOFBarrelClusterHits",
0092       "TOFBarrelADCTDC",
0093       "TOFEndcapHits",
0094 
0095       "TOFEndcapSharedHits",
0096       "TOFEndcapADCTDC",
0097 
0098       "TOFBarrelRawHitAssociations",
0099       "TOFEndcapRawHitAssociations",
0100 
0101       "CombinedTOFTruthSeededParticleIDs",
0102       "CombinedTOFParticleIDs",
0103 
0104       // DRICH
0105       "DRICHRawHits",
0106       "DRICHRawHitsAssociations",
0107       "DRICHAerogelTracks",
0108       "DRICHGasTracks",
0109       "DRICHAerogelIrtCherenkovParticleID",
0110       "DRICHGasIrtCherenkovParticleID",
0111       "DRICHTruthSeededParticleIDs",
0112       "DRICHParticleIDs",
0113 
0114       // PFRICH
0115       "RICHEndcapNRawHits",
0116       "RICHEndcapNRawHitsAssociations",
0117       "RICHEndcapNTruthSeededParticleIDs",
0118       "RICHEndcapNParticleIDs",
0119 
0120       // MPGD
0121       "MPGDBarrelRecHits",
0122       "OuterMPGDBarrelRecHits",
0123       "BackwardMPGDEndcapRecHits",
0124       "ForwardMPGDEndcapRecHits",
0125 
0126       "MPGDBarrelRawHits",
0127       "OuterMPGDBarrelRawHits",
0128       "BackwardMPGDEndcapRawHits",
0129       "ForwardMPGDEndcapRawHits",
0130 
0131       "MPGDBarrelHits",
0132       "OuterMPGDBarrelHits",
0133       "BackwardMPGDEndcapHits",
0134       "ForwardMPGDEndcapHits",
0135 
0136       "MPGDBarrelRawHitAssociations",
0137       "OuterMPGDBarrelRawHitAssociations",
0138       "BackwardMPGDEndcapRawHitAssociations",
0139       "ForwardMPGDEndcapRawHitAssociations",
0140 
0141       // LOWQ2 hits
0142       "TaggerTrackerHits",
0143       "TaggerTrackerSharedHits",
0144       "TaggerTrackerHitPulses",
0145       "TaggerTrackerCombinedPulses",
0146       "TaggerTrackerCombinedPulsesWithNoise",
0147       "TaggerTrackerRawHits",
0148       "TaggerTrackerRawHitAssociations",
0149       "TaggerTrackerM1L0ClusterPositions",
0150       "TaggerTrackerM1L1ClusterPositions",
0151       "TaggerTrackerM1L2ClusterPositions",
0152       "TaggerTrackerM1L3ClusterPositions",
0153       "TaggerTrackerM2L0ClusterPositions",
0154       "TaggerTrackerM2L1ClusterPositions",
0155       "TaggerTrackerM2L2ClusterPositions",
0156       "TaggerTrackerM2L3ClusterPositions",
0157       "TaggerTrackerM1LocalTracks",
0158       "TaggerTrackerM2LocalTracks",
0159       "TaggerTrackerM1LocalTrackAssociations",
0160       "TaggerTrackerM2LocalTrackAssociations",
0161       "TaggerTrackerLocalTracks",
0162       "TaggerTrackerLocalTrackAssociations",
0163       "TaggerTrackerReconstructedParticles",
0164       "TaggerTrackerReconstructedParticleAssociations",
0165 
0166       // Forward & Far forward hits
0167       "B0TrackerTruthSeeds",
0168       "B0TrackerRecHits",
0169       "B0TrackerRawHits",
0170       "B0TrackerHits",
0171       "B0TrackerRawHitAssociations",
0172       "B0TrackerSeedingResults",
0173       "B0TrackerMeasurements",
0174 
0175       "ForwardRomanPotRecHits",
0176       "ForwardOffMTrackerRecHits",
0177 
0178       "ForwardRomanPotRecParticles",
0179       "ForwardRomanPotStaticRecParticles",
0180       "ForwardOffMRecParticles",
0181 
0182       "ForwardRomanPotRawHits",
0183       "ForwardRomanPotRawHitAssociations",
0184       "ForwardOffMTrackerRawHits",
0185       "ForwardOffMTrackerRawHitAssociations",
0186 
0187       // Reconstructed data
0188       "GeneratedParticles",
0189       "GeneratedBreitFrameParticles",
0190       "ReconstructedParticles",
0191       "ReconstructedParticleAssociations",
0192       "ReconstructedTruthSeededChargedParticles",
0193       "ReconstructedTruthSeededChargedParticleAssociations",
0194       "ReconstructedChargedRealPIDParticles",
0195       "ReconstructedChargedRealPIDParticleIDs",
0196       "ReconstructedChargedParticles",
0197       "ReconstructedChargedParticleAssociations",
0198       "MCScatteredElectronAssociations",    // Remove if/when used internally
0199       "MCNonScatteredElectronAssociations", // Remove if/when used internally
0200       "ReconstructedBreitFrameParticles",
0201 
0202       // Central tracking
0203       "CentralTrackSegments",
0204       "CentralTrackVertices",
0205       "CentralCKFTruthSeededTrajectories",
0206       "CentralCKFTruthSeededTracks",
0207       "CentralCKFTruthSeededTrackAssociations",
0208       "CentralCKFTruthSeededTrackParameters",
0209       "CentralCKFTrajectories",
0210       "CentralCKFTracks",
0211       "CentralCKFTrackAssociations",
0212       "CentralCKFTrackParameters",
0213       // tracking properties - true seeding
0214       "CentralCKFTruthSeededTrajectoriesUnfiltered",
0215       "CentralCKFTruthSeededTracksUnfiltered",
0216       "CentralCKFTruthSeededTrackUnfilteredAssociations",
0217       "CentralCKFTruthSeededTrackParametersUnfiltered",
0218       // tracking properties - realistic seeding
0219       "CentralCKFTrajectoriesUnfiltered",
0220       "CentralCKFTracksUnfiltered",
0221       "CentralCKFTrackUnfilteredAssociations",
0222       "CentralCKFTrackParametersUnfiltered",
0223 
0224       // B0 tracking
0225       "B0TrackerCKFTruthSeededTrajectories",
0226       "B0TrackerCKFTruthSeededTracks",
0227       "B0TrackerCKFTruthSeededTrackAssociations",
0228       "B0TrackerCKFTruthSeededTrackParameters",
0229       "B0TrackerCKFTrajectories",
0230       "B0TrackerCKFTracks",
0231       "B0TrackerCKFTrackAssociations",
0232       "B0TrackerCKFTrackParameters",
0233       // tracking properties - true seeding
0234       "B0TrackerCKFTruthSeededTrajectoriesUnfiltered",
0235       "B0TrackerCKFTruthSeededTracksUnfiltered",
0236       "B0TrackerCKFTruthSeededTrackUnfilteredAssociations",
0237       "B0TrackerCKFTruthSeededTrackParametersUnfiltered",
0238       // tracking properties - realistic seeding
0239       "B0TrackerCKFTrajectoriesUnfiltered",
0240       "B0TrackerCKFTrackParametersUnfiltered",
0241       "B0TrackerCKFTracksUnfiltered",
0242       "B0TrackerCKFTrackUnfilteredAssociations",
0243 
0244       "CentralAndB0TrackVertices",
0245 
0246       // Inclusive kinematics
0247       "InclusiveKinematicsDA",
0248       "InclusiveKinematicsJB",
0249       "InclusiveKinematicsML",
0250       "InclusiveKinematicsSigma",
0251       "InclusiveKinematicseSigma", // Deprecated, use ESigma
0252       "InclusiveKinematicsESigma",
0253       "InclusiveKinematicsElectron",
0254       "InclusiveKinematicsTruth",
0255       "GeneratedJets",
0256       "GeneratedChargedJets",
0257       "GeneratedCentauroJets",
0258       "ReconstructedJets",
0259       "ReconstructedChargedJets",
0260       "ReconstructedCentauroJets",
0261       "ReconstructedElectrons",
0262       "ScatteredElectronsTruth",
0263       "ScatteredElectronsEMinusPz",
0264       "PrimaryVertices",
0265       "SecondaryVerticesHelix",
0266       "BarrelClusters",
0267       "HadronicFinalState",
0268 
0269       // Track projections
0270       "CalorimeterTrackProjections",
0271 
0272       // Ecal stuff
0273       "EcalEndcapNRawHits",
0274       "EcalEndcapNRecHits",
0275       "EcalEndcapNTruthClusters",
0276       "EcalEndcapNTruthClusterAssociations",
0277       "EcalEndcapNClusters",
0278       "EcalEndcapNClusterAssociations",
0279       "EcalEndcapNSplitMergeClusters",
0280       "EcalEndcapNSplitMergeClusterAssociations",
0281       "EcalEndcapPRawHits",
0282       "EcalEndcapPRecHits",
0283       "EcalEndcapPTruthClusters",
0284       "EcalEndcapPTruthClusterAssociations",
0285       "EcalEndcapPClusters",
0286       "EcalEndcapPClusterAssociations",
0287       "EcalEndcapPSplitMergeClusters",
0288       "EcalEndcapPSplitMergeClusterAssociations",
0289       "EcalBarrelClusters",
0290       "EcalBarrelClusterAssociations",
0291       "EcalBarrelTruthClusters",
0292       "EcalBarrelTruthClusterAssociations",
0293       "EcalBarrelImagingRawHits",
0294       "EcalBarrelImagingRecHits",
0295       "EcalBarrelImagingClusters",
0296       "EcalBarrelImagingClusterAssociations",
0297       "EcalBarrelScFiPAttenuatedHits",
0298       "EcalBarrelScFiPAttenuatedHitContributions",
0299       "EcalBarrelScFiNAttenuatedHits",
0300       "EcalBarrelScFiNAttenuatedHitContributions",
0301       "EcalBarrelScFiRawHits",
0302       "EcalBarrelScFiPPulses",
0303       "EcalBarrelScFiNPulses",
0304       "EcalBarrelScFiPCombinedPulses",
0305       "EcalBarrelScFiNCombinedPulses",
0306       "EcalBarrelScFiPCombinedPulsesWithNoise",
0307       "EcalBarrelScFiNCombinedPulsesWithNoise",
0308       "EcalBarrelScFiRecHits",
0309       "EcalBarrelScFiClusters",
0310       "EcalBarrelScFiClusterAssociations",
0311       "EcalLumiSpecRawHits",
0312       "EcalLumiSpecRecHits",
0313       "EcalLumiSpecTruthClusters",
0314       "EcalLumiSpecTruthClusterAssociations",
0315       "EcalLumiSpecClusters",
0316       "EcalLumiSpecClusterAssociations",
0317       "HcalEndcapNRawHits",
0318       "HcalEndcapNRecHits",
0319       "HcalEndcapNMergedHits",
0320       "HcalEndcapNClusters",
0321       "HcalEndcapNClusterAssociations",
0322       "HcalEndcapNSplitMergeClusters",
0323       "HcalEndcapNSplitMergeClusterAssociations",
0324       "HcalEndcapPInsertRawHits",
0325       "HcalEndcapPInsertRecHits",
0326       "HcalEndcapPInsertMergedHits",
0327       "HcalEndcapPInsertClusters",
0328       "HcalEndcapPInsertClusterAssociations",
0329       "LFHCALRawHits",
0330       "LFHCALRecHits",
0331       "LFHCALClusters",
0332       "LFHCALClusterAssociations",
0333       "LFHCALSplitMergeClusters",
0334       "LFHCALSplitMergeClusterAssociations",
0335       "HcalBarrelRawHits",
0336       "HcalBarrelRecHits",
0337       "HcalBarrelMergedHits",
0338       "HcalBarrelClusters",
0339       "HcalBarrelClusterAssociations",
0340       "HcalBarrelSplitMergeClusters",
0341       "HcalBarrelSplitMergeClusterAssociations",
0342       "B0ECalRawHits",
0343       "B0ECalRecHits",
0344       "B0ECalClusters",
0345       "B0ECalClusterAssociations",
0346       "HcalEndcapNTruthClusters",
0347       "HcalEndcapNTruthClusterAssociations",
0348       "HcalBarrelTruthClusters",
0349       "HcalBarrelTruthClusterAssociations",
0350 
0351       //ZDC Ecal
0352       "EcalFarForwardZDCRawHits",
0353       "EcalFarForwardZDCRecHits",
0354       "EcalFarForwardZDCClusters",
0355       "EcalFarForwardZDCClusterAssociations",
0356       "EcalFarForwardZDCTruthClusters",
0357       "EcalFarForwardZDCTruthClusterAssociations",
0358 
0359       //ZDC HCal
0360       "HcalFarForwardZDCRawHits",
0361       "HcalFarForwardZDCRecHits",
0362       "HcalFarForwardZDCSubcellHits",
0363       "HcalFarForwardZDCClusters",
0364       "HcalFarForwardZDCClusterAssociations",
0365       "HcalFarForwardZDCClustersBaseline",
0366       "HcalFarForwardZDCClusterAssociationsBaseline",
0367       "HcalFarForwardZDCTruthClusters",
0368       "HcalFarForwardZDCTruthClusterAssociations",
0369       "ReconstructedFarForwardZDCNeutrals",
0370       "ReconstructedFarForwardZDCLambdas",
0371       "ReconstructedFarForwardZDCLambdaDecayProductsCM",
0372 
0373       // DIRC
0374       "DIRCRawHits",
0375       "DIRCTruthSeededParticleIDs",
0376       "DIRCParticleIDs",
0377 
0378       "B0ECalRawHitAssociations",
0379       "EcalBarrelScFiRawHitAssociations",
0380       "EcalBarrelImagingRawHitAssociations",
0381       "HcalBarrelRawHitAssociations",
0382       "EcalEndcapNRawHitAssociations",
0383       "HcalEndcapNRawHitAssociations",
0384       "EcalEndcapPRawHitAssociations",
0385       "HcalEndcapPInsertRawHitAssociations",
0386       "LFHCALRawHitAssociations",
0387       "EcalLumiSpecRawHitAssociations",
0388       "EcalFarForwardZDCRawHitAssociations",
0389       "HcalFarForwardZDCRawHitAssociations",
0390       "EcalEndcapPTrackClusterMatches",
0391       "LFHCALTrackClusterMatches",
0392       "HcalEndcapPInsertClusterMatches",
0393       "EcalBarrelTrackClusterMatches",
0394       "HcalBarrelTrackClusterMatches",
0395       "EcalEndcapNTrackClusterMatches",
0396       "HcalEndcapNTrackClusterMatches",
0397 
0398   };
0399   std::vector<std::string> output_exclude_collections; // need to get as vector, then convert to set
0400   std::string output_include_collections = "DEPRECATED";
0401   japp->SetDefaultParameter("podio:output_include_collections", output_include_collections,
0402                             "DEPRECATED. Use podio:output_collections instead.");
0403   if (output_include_collections != "DEPRECATED") {
0404     output_collections.clear();
0405     JParameterManager::Parse(output_include_collections, output_collections);
0406     m_output_include_collections_set = true;
0407   }
0408   japp->SetDefaultParameter(
0409       "podio:output_collections", output_collections,
0410       "Comma separated list of collection names to write out. If not set, all collections will be "
0411       "written (including ones from input file). Don't set this and use "
0412       "PODIO:OUTPUT_EXCLUDE_COLLECTIONS to write everything except a selection.");
0413   japp->SetDefaultParameter("podio:output_exclude_collections", output_exclude_collections,
0414                             "Comma separated list of collection names to not write out.");
0415   japp->SetDefaultParameter(
0416       "podio:print_collections", m_collections_to_print,
0417       "Comma separated list of collection names to print to screen, e.g. for debugging.");
0418 
0419   m_output_collections =
0420       std::set<std::string>(output_collections.begin(), output_collections.end());
0421   m_output_exclude_collections =
0422       std::set<std::string>(output_exclude_collections.begin(), output_exclude_collections.end());
0423 }
0424 
0425 void JEventProcessorPODIO::Init() {
0426 
0427   auto* app = GetApplication();
0428   m_log     = app->GetService<Log_service>()->logger("JEventProcessorPODIO");
0429   m_writer  = std::make_unique<podio::ROOTWriter>(m_output_file);
0430   // TODO: NWB: Verify that output file is writable NOW, rather than after event processing completes.
0431   //       I definitely don't trust PODIO to do this for me.
0432 
0433   if (m_output_include_collections_set) {
0434     m_log->error("The podio:output_include_collections was provided, but is deprecated. Use "
0435                  "podio:output_collections instead.");
0436     throw std::runtime_error("The podio:output_include_collections was provided, but is "
0437                              "deprecated. Use podio:output_collections instead.");
0438   }
0439 }
0440 
0441 void JEventProcessorPODIO::FindCollectionsToWrite(const std::shared_ptr<const JEvent>& event) {
0442 
0443   // Set up the set of collections_to_write.
0444   std::vector<std::string> all_collections = event->GetAllCollectionNames();
0445 
0446   if (m_output_collections.empty()) {
0447     // User has not specified an include list, so we include _all_ PODIO collections present in the first event.
0448     for (const std::string& col : all_collections) {
0449       if (m_output_exclude_collections.find(col) == m_output_exclude_collections.end()) {
0450         m_collections_to_write.push_back(col);
0451         m_log->debug("Persisting collection '{}'", col);
0452       }
0453     }
0454   } else {
0455     m_log->debug("Persisting podio types from includes list");
0456 
0457     // We match up the include list with what is actually present in the event
0458     std::set<std::string> all_collections_set =
0459         std::set<std::string>(all_collections.begin(), all_collections.end());
0460 
0461     // Turn regexes among output collections into actual collection names
0462     std::set<std::string> matching_collections_set;
0463     std::vector<std::regex> output_collections_regex(m_output_collections.size());
0464     std::ranges::transform(m_output_collections, output_collections_regex.begin(),
0465                            [](const std::string& r) { return std::regex(r); });
0466     std::ranges::copy_if(all_collections_set,
0467                          std::inserter(matching_collections_set, matching_collections_set.end()),
0468                          [&](const std::string& c) {
0469                            return std::ranges::any_of(
0470                                output_collections_regex,
0471 
0472                                [&](const std::regex& r) { return std::regex_match(c, r); });
0473                          });
0474 
0475     for (const auto& col : matching_collections_set) {
0476       if (m_output_exclude_collections.find(col) == m_output_exclude_collections.end()) {
0477         // Included and not excluded
0478         if (all_collections_set.find(col) == all_collections_set.end()) {
0479           // Included, but not a valid PODIO type
0480           m_log->warn("Explicitly included collection '{}' not present in factory set, omitting.",
0481                       col);
0482         } else {
0483           // Included, not excluded, and a valid PODIO type
0484           m_collections_to_write.push_back(col);
0485           m_log->info("Persisting collection '{}'", col);
0486         }
0487       }
0488     }
0489   }
0490 }
0491 
0492 void JEventProcessorPODIO::Process(const std::shared_ptr<const JEvent>& event) {
0493 
0494   // Find all collections to write from the first event
0495   std::call_once(m_is_first_event, &JEventProcessorPODIO::FindCollectionsToWrite, this, event);
0496 
0497   // Print the contents of some collections, just for debugging purposes
0498   // Do this before writing just in case writing crashes
0499   if (!m_collections_to_print.empty()) {
0500     m_log->info("========================================");
0501     m_log->info("JEventProcessorPODIO: Event {}", event->GetEventNumber());
0502     ;
0503   }
0504   for (const auto& coll_name : m_collections_to_print) {
0505     m_log->info("------------------------------");
0506     m_log->info("{}", coll_name);
0507     try {
0508       const auto* coll_ptr = event->GetCollectionBase(coll_name);
0509       if (coll_ptr == nullptr) {
0510         m_log->info("missing");
0511       } else {
0512         std::stringstream ss;
0513         coll_ptr->print(ss);
0514         m_log->info(ss.str());
0515       }
0516     } catch (std::exception& e) {
0517       m_log->info("missing");
0518     }
0519   }
0520 
0521   m_log->trace("==================================");
0522   m_log->trace("Event #{}", event->GetEventNumber());
0523 
0524   // Make sure that all factories get called that need to be written into the frame.
0525   // We need to do this for _all_ factories unless we've constrained it by using includes/excludes.
0526   // Note that all collections need to be present in the first event, as podio::RootFrameWriter constrains us to write one event at a time, so there
0527   // is no way to add a new branch after the first event.
0528 
0529   // If we get an exception below while trying to add a factory for any
0530   // reason then mark that factory as bad and don't try running it again.
0531   // This is motivated by trying to write EcalBarrelSciGlass objects for
0532   // data simulated using the imaging calorimeter. In that case, it will
0533   // always throw an exception, but DD4hep also prints its own error message.
0534   // Thus, to prevent that error message every event, we must avoid calling
0535   // it.
0536 
0537   // Activate factories.
0538   std::vector<std::string> successful_collections;
0539   std::set<std::string> failed_collections;
0540   for (const std::string& coll : m_collections_to_write) {
0541     try {
0542       m_log->trace("Ensuring factory for collection '{}' has been called.", coll);
0543       const auto* coll_ptr = event->GetCollectionBase(coll);
0544       if (coll_ptr == nullptr) {
0545         // If a collection is missing from the frame, the podio root writer will segfault.
0546         // To avoid this, we treat this as a failing collection and omit from this point onwards.
0547         // However, this code path is expected to be unreachable because any missing collection will be
0548         // replaced with an empty collection in JFactoryPodioTFixed::Create.
0549         if (!failed_collections.contains(coll)) {
0550           m_log->error("Omitting PODIO collection '{}' because it is null", coll);
0551           failed_collections.insert(coll);
0552         }
0553       } else {
0554         m_log->trace("Including PODIO collection '{}'", coll);
0555         successful_collections.push_back(coll);
0556       }
0557     } catch (std::exception& e) {
0558       // Limit printing warning to just once per factory
0559       if (!failed_collections.contains(coll)) {
0560         m_log->error("Omitting PODIO collection '{}' due to exception: {}.", coll, e.what());
0561         failed_collections.insert(coll);
0562       }
0563     }
0564   }
0565 
0566   // Frame will contain data from all Podio factories that have been triggered,
0567   // including by the `event->GetCollectionBase(coll);` above.
0568   // Note that collections MUST be present in frame. If a collection is null, the writer will segfault.
0569   const auto* frame = event->GetSingle<podio::Frame>();
0570   {
0571     std::lock_guard<std::mutex> lock(m_mutex);
0572     m_writer->writeFrame(*frame, "events", m_collections_to_write);
0573   }
0574 }
0575 
0576 void JEventProcessorPODIO::Finish() {
0577   if (m_output_include_collections_set) {
0578     m_log->error("The podio:output_include_collections was provided, but is deprecated. Use "
0579                  "podio:output_collections instead.");
0580     throw std::runtime_error("The podio:output_include_collections was provided, but is "
0581                              "deprecated. Use podio:output_collections instead.");
0582   }
0583 
0584   m_writer->finish();
0585 }