diff --git a/.github/workflows/linux-eic-shell.yml b/.github/workflows/linux-eic-shell.yml index 679269ebe3..e4c8793ab4 100644 --- a/.github/workflows/linux-eic-shell.yml +++ b/.github/workflows/linux-eic-shell.yml @@ -119,7 +119,7 @@ jobs: USE_TSAN: ON - CXX: clang++ CMAKE_BUILD_TYPE: Release - release: 26.02.0-stable + release: 26.03.0-stable - CXX: clang++ CMAKE_BUILD_TYPE: Release release: 26.04.1-stable diff --git a/CMakeLists.txt b/CMakeLists.txt index d5862db775..33390139c6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -220,6 +220,29 @@ find_package(spdlog ${spdlog_VERSION_MIN} REQUIRED) # Guidelines Support Library find_package(Microsoft.GSL CONFIG) +# Check for JANA2 v2.4.3 JOmniFactory issues (fixed in PR #464) +if(JANA_VERSION VERSION_EQUAL "2.4.3") + try_run( + JANA_BUG_RESULT + JANA_BUG_COMPILED + "${CMAKE_BINARY_DIR}" + "${CMAKE_SOURCE_DIR}/cmake/test_jana243_f18b66c6.cpp" + LINK_LIBRARIES + ${JANA_LIB} + podio::podio + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${JANA_INCLUDE_DIR}" + COMPILE_OUTPUT_VARIABLE _unused_compile_out + RUN_OUTPUT_VARIABLE _unused_run_out) + + if(JANA_BUG_COMPILED AND JANA_BUG_RESULT EQUAL 1) + message( + FATAL_ERROR + "JANA2 v2.4.3 has JOmniFactory bugs. Apply fixes with: " + "`git cherry-pick -m 1 0421819` or use JANA2 > v2.4.3. " + "Ref: https://github.com/JeffersonLab/JANA2/pull/464") + endif() +endif() + # Remove PODIO_JSON_OUTPUT (ref: https://github.com/AIDASoft/podio/issues/475) get_target_property(EDM4HEP_INTERFACE_COMPILE_DEFINITIONS EDM4HEP::edm4hep INTERFACE_COMPILE_DEFINITIONS) diff --git a/cmake/test_jana243_f18b66c6.cpp b/cmake/test_jana243_f18b66c6.cpp new file mode 100644 index 0000000000..57fc2cf5f0 --- /dev/null +++ b/cmake/test_jana243_f18b66c6.cpp @@ -0,0 +1,26 @@ +// Test program to detect JANA2 v2.4.3 Input bug +// The bug is fixed in commit f18b66c6 +// +// Bug: Input constructor sets m_databundle_name = m_type_name +// but GetCollection() uses m_tag (which is never set) +// Fix: Removes m_tag member, uses m_databundle_name everywhere + +#include + +// Helper class that can access protected Input +struct TestHasInputs : public jana::components::JHasInputs { + static bool has_bug() { + TestHasInputs owner; + Input test_input(&owner); + + // In buggy version: constructor sets m_databundle_name = "int" + // In fixed version: constructor doesn't set m_databundle_name (empty) + + return test_input.GetDatabundleName() == "int"; + } +}; + +int main() { + // Return 1 if bug detected, 0 if fixed + return TestHasInputs::has_bug() ? 1 : 0; +} diff --git a/src/detectors/B0ECAL/B0ECAL.cc b/src/detectors/B0ECAL/B0ECAL.cc index 667b52ec0f..df004e0887 100644 --- a/src/detectors/B0ECAL/B0ECAL.cc +++ b/src/detectors/B0ECAL/B0ECAL.cc @@ -2,7 +2,9 @@ // Copyright (C) 2022 - 2025 Whitney Armstrong, Sylvester Joosten, Chao Peng, David Lawrence, Wouter Deconinck, Kolja Kauder, Nathan Brei, Dmitry Kalinkin, Derek Anderson, Michael Pitt #include +#include #include +#include #include #include #include @@ -21,6 +23,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/detectors/B0TRK/B0TRK.cc b/src/detectors/B0TRK/B0TRK.cc index d7b53cc1fe..4054143eea 100644 --- a/src/detectors/B0TRK/B0TRK.cc +++ b/src/detectors/B0TRK/B0TRK.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Digitization app->Add(new JOmniFactoryGeneratorT( diff --git a/src/detectors/BEMC/BEMC.cc b/src/detectors/BEMC/BEMC.cc index 67377f3e8c..90dbb20a65 100644 --- a/src/detectors/BEMC/BEMC.cc +++ b/src/detectors/BEMC/BEMC.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/detectors/BHCAL/BHCAL.cc b/src/detectors/BHCAL/BHCAL.cc index 04ebfdd430..9289b785c9 100644 --- a/src/detectors/BHCAL/BHCAL.cc +++ b/src/detectors/BHCAL/BHCAL.cc @@ -2,7 +2,9 @@ // Copyright (C) 2022 - 2024 David Lawrence, Derek Anderson, Wouter Deconinck #include +#include #include +#include #include #include #include @@ -25,6 +27,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); @@ -165,7 +168,10 @@ void InitPlugin(JApplication* app) { "HcalBarrelSplitMergeProtoClusters", {"HcalBarrelTrackClusterMatches", "HcalBarrelClustersWithoutShapes", "CalorimeterTrackProjections"}, - {"HcalBarrelSplitMergeProtoClusters", "HcalBarrelTrackSplitMergeProtoClusterLinks"}, + { + "HcalBarrelSplitMergeProtoClusters", + "HcalBarrelTrackSplitMergeProtoClusterLinks", + }, {.minSigCut = -2.0, .avgEP = 0.50, .sigEP = 0.25, diff --git a/src/detectors/BTOF/BTOF.cc b/src/detectors/BTOF/BTOF.cc index 242dc74da0..11c88b5db2 100644 --- a/src/detectors/BTOF/BTOF.cc +++ b/src/detectors/BTOF/BTOF.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Convert raw digitized hits into calibrated hits // time walk correction is still TBD diff --git a/src/detectors/BTRK/BTRK.cc b/src/detectors/BTRK/BTRK.cc index badae53dea..1109b0b09e 100644 --- a/src/detectors/BTRK/BTRK.cc +++ b/src/detectors/BTRK/BTRK.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Digitization app->Add(new JOmniFactoryGeneratorT( diff --git a/src/detectors/BVTX/BVTX.cc b/src/detectors/BVTX/BVTX.cc index c3830307a1..34e8955e4e 100644 --- a/src/detectors/BVTX/BVTX.cc +++ b/src/detectors/BVTX/BVTX.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Digitization app->Add(new JOmniFactoryGeneratorT( diff --git a/src/detectors/DIRC/DIRC.cc b/src/detectors/DIRC/DIRC.cc index e2339a4349..e8c3611e88 100644 --- a/src/detectors/DIRC/DIRC.cc +++ b/src/detectors/DIRC/DIRC.cc @@ -1,7 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (C) 2022 - 2025 Christopher Dilks, Nilanga Wickramaarachchi, Dmitry Kalinkin +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // configuration parameters /////////////////////////////////////////////// diff --git a/src/detectors/DRICH/DRICH.cc b/src/detectors/DRICH/DRICH.cc index 7784376948..8be8900b19 100644 --- a/src/detectors/DRICH/DRICH.cc +++ b/src/detectors/DRICH/DRICH.cc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // configuration parameters /////////////////////////////////////////////// @@ -129,9 +131,10 @@ void InitPlugin(JApplication* app) { "DRICHGasTracks", {"CentralCKFTracks", "CentralCKFActsTrackStates", "CentralCKFActsTracks"}, {"DRICHGasTracks"}, gas_track_cfg, app)); - app->Add(new JOmniFactoryGeneratorT("DRICHMergedTracks", - {"DRICHAerogelTracks", "DRICHGasTracks"}, - {"DRICHMergedTracks"}, {}, app)); + app->Add(new JOmniFactoryGeneratorT( + {.tag = "DRICHMergedTracks", + .variadic_input_names = {{"DRICHAerogelTracks", "DRICHGasTracks"}}, + .output_names = {"DRICHMergedTracks"}})); // PID algorithm app->Add(new JOmniFactoryGeneratorT( @@ -142,9 +145,11 @@ void InitPlugin(JApplication* app) { // merge aerogel and gas PID results app->Add(new JOmniFactoryGeneratorT( - "DRICHMergedIrtCherenkovParticleID", - {"DRICHAerogelIrtCherenkovParticleID", "DRICHGasIrtCherenkovParticleID"}, - {"DRICHMergedIrtCherenkovParticleID"}, merge_cfg, app)); + {.tag = "DRICHMergedIrtCherenkovParticleID", + .variadic_input_names = {{"DRICHAerogelIrtCherenkovParticleID", + "DRICHGasIrtCherenkovParticleID"}}, + .output_names = {"DRICHMergedIrtCherenkovParticleID"}, + .configs = merge_cfg})); // clang-format on } diff --git a/src/detectors/ECTOF/ECTOF.cc b/src/detectors/ECTOF/ECTOF.cc index 9bff9a0459..2dd683f7b7 100644 --- a/src/detectors/ECTOF/ECTOF.cc +++ b/src/detectors/ECTOF/ECTOF.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -30,6 +31,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // cluster all hits in a sensor into one hit location // Currently it's just a simple weighted average diff --git a/src/detectors/ECTRK/ECTRK.cc b/src/detectors/ECTRK/ECTRK.cc index acaa36bd57..3c7295aba6 100644 --- a/src/detectors/ECTRK/ECTRK.cc +++ b/src/detectors/ECTRK/ECTRK.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -18,6 +20,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Digitization app->Add(new JOmniFactoryGeneratorT( diff --git a/src/detectors/EEMC/EEMC.cc b/src/detectors/EEMC/EEMC.cc index c432bce26c..cb73ac0274 100644 --- a/src/detectors/EEMC/EEMC.cc +++ b/src/detectors/EEMC/EEMC.cc @@ -2,7 +2,9 @@ // Copyright (C) 2022 - 2025 Sylvester Joosten, Chao, Chao Peng, Whitney Armstrong, Thomas Britton, David Lawrence, Dhevan Gangadharan, Wouter Deconinck, Dmitry Kalinkin, Derek Anderson #include +#include #include +#include #include #include #include @@ -26,6 +28,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); @@ -162,19 +165,15 @@ void InitPlugin(JApplication* app) { "EcalEndcapNParticleIDTarget", }, app)); + app->Add(new JOmniFactoryGeneratorT( - "EcalEndcapNParticleIDInference", - { - "EcalEndcapNParticleIDInput_features", - }, - { - "EcalEndcapNParticleIDOutput_label", - "EcalEndcapNParticleIDOutput_probability_tensor", - }, - { - .modelPath = "calibrations/onnx/EcalEndcapN_pi_rejection.onnx", - }, - app)); + {.tag = "EcalEndcapNParticleIDInference", + .variadic_input_names = {{"EcalEndcapNParticleIDInput_features"}}, + .variadic_output_names = {{"EcalEndcapNParticleIDOutput_label", + "EcalEndcapNParticleIDOutput_probability_tensor"}}, + .configs = { + .modelPath = "calibrations/onnx/EcalEndcapN_pi_rejection.onnx", + }})); app->Add(new JOmniFactoryGeneratorT( "EcalEndcapNParticleIDPostML", { @@ -195,7 +194,10 @@ void InitPlugin(JApplication* app) { "EcalEndcapNSplitMergeProtoClusters", {"EcalEndcapNTrackClusterMatches", "EcalEndcapNClustersWithoutPID", "CalorimeterTrackProjections"}, - {"EcalEndcapNSplitMergeProtoClusters", "EcalEndcapNTrackSplitMergeProtoClusterLinks"}, + { + "EcalEndcapNSplitMergeProtoClusters", + "EcalEndcapNTrackSplitMergeProtoClusterLinks", + }, {.minSigCut = -1.0, .avgEP = 1.0, .sigEP = 0.10, @@ -206,8 +208,7 @@ void InitPlugin(JApplication* app) { )); app->Add(new JOmniFactoryGeneratorT( "EcalEndcapNSplitMergeClustersWithoutShapes", - {"EcalEndcapNSplitMergeProtoClusters", - "EcalEndcapNRawHitLinks", // edm4eic::MCRecoCalorimeterHitLink + {"EcalEndcapNSplitMergeProtoClusters", "EcalEndcapNRawHitLinks", "EcalEndcapNRawHitAssociations"}, {"EcalEndcapNSplitMergeClustersWithoutShapes", "EcalEndcapNSplitMergeClusterLinksWithoutShapes", diff --git a/src/detectors/EHCAL/EHCAL.cc b/src/detectors/EHCAL/EHCAL.cc index 03d69ceaf3..37511d5e0e 100644 --- a/src/detectors/EHCAL/EHCAL.cc +++ b/src/detectors/EHCAL/EHCAL.cc @@ -2,7 +2,9 @@ // Copyright (C) 2022 - 2025 Sylvester Joosten, Chao, Chao Peng, Whitney Armstrong, David Lawrence, Friederike Bock, Nathan Brei, Wouter Deconinck, Dmitry Kalinkin, Derek Anderson #include +#include #include +#include #include #include #include @@ -23,6 +25,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); // Make sure digi and reco use the same value @@ -145,7 +148,10 @@ void InitPlugin(JApplication* app) { app->Add(new JOmniFactoryGeneratorT( "HcalEndcapNSplitMergeProtoClusters", {"HcalEndcapNTrackClusterMatches", "HcalEndcapNClusters", "CalorimeterTrackProjections"}, - {"HcalEndcapNSplitMergeProtoClusters", "HcalEndcapNTrackSplitMergeProtoClusterLinks"}, + { + "HcalEndcapNSplitMergeProtoClusters", + "HcalEndcapNTrackSplitMergeProtoClusterLinks", + }, {.minSigCut = -2.0, .avgEP = 0.60, .sigEP = 0.40, @@ -156,8 +162,7 @@ void InitPlugin(JApplication* app) { )); app->Add(new JOmniFactoryGeneratorT( "HcalEndcapNClustersWithoutShapes", - {"HcalEndcapNSplitMergeProtoClusters", - "HcalEndcapNRawHitLinks", // edm4eic::MCRecoCalorimeterHitLink + {"HcalEndcapNSplitMergeProtoClusters", "HcalEndcapNRawHitLinks", "HcalEndcapNRawHitAssociations"}, {"HcalEndcapNSplitMergeClustersWithoutShapes", "HcalEndcapNSplitMergeClusterLinksWithoutShapes", diff --git a/src/detectors/FEMC/FEMC.cc b/src/detectors/FEMC/FEMC.cc index b101023ac1..57e0eddeee 100644 --- a/src/detectors/FEMC/FEMC.cc +++ b/src/detectors/FEMC/FEMC.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -31,6 +32,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/detectors/FHCAL/FHCAL.cc b/src/detectors/FHCAL/FHCAL.cc index 3702f6ee3c..1dade23131 100644 --- a/src/detectors/FHCAL/FHCAL.cc +++ b/src/detectors/FHCAL/FHCAL.cc @@ -2,7 +2,9 @@ // Copyright (C) 2023 - 2025 Friederike Bock, Wouter Deconinck #include +#include #include +#include #include #include #include @@ -27,6 +29,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); @@ -304,7 +307,10 @@ void InitPlugin(JApplication* app) { app->Add(new JOmniFactoryGeneratorT( "LFHCALSplitMergeProtoClusters", {"LFHCALTrackClusterMatches", "LFHCALClusters", "CalorimeterTrackProjections"}, - {"LFHCALSplitMergeProtoClusters", "LFHCALTrackSplitMergeProtoClusterLinks"}, + { + "LFHCALSplitMergeProtoClusters", + "LFHCALTrackSplitMergeProtoClusterLinks", + }, {.minSigCut = -2.0, .avgEP = 0.50, .sigEP = 0.25, diff --git a/src/detectors/FOFFMTRK/FOFFMTRK.cc b/src/detectors/FOFFMTRK/FOFFMTRK.cc index 5ff1a6d710..f2a3c87541 100644 --- a/src/detectors/FOFFMTRK/FOFFMTRK.cc +++ b/src/detectors/FOFFMTRK/FOFFMTRK.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -18,7 +20,9 @@ extern "C" { void InitPlugin(JApplication* app) { InitJANAPlugin(app); + using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; //Digitized hits, especially for thresholds app->Add(new JOmniFactoryGeneratorT( diff --git a/src/detectors/LOWQ2/LOWQ2.cc b/src/detectors/LOWQ2/LOWQ2.cc index 4ef8cffa8f..82ad77cf84 100644 --- a/src/detectors/LOWQ2/LOWQ2.cc +++ b/src/detectors/LOWQ2/LOWQ2.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -45,6 +46,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; std::string readout = "TaggerTrackerHits"; @@ -136,62 +138,71 @@ void InitPlugin(JApplication* app) { } app->Add(new JOmniFactoryGeneratorT>( - "TaggerTrackerSplitHits", {"TaggerTrackerRecHits"}, geometryDivisionCollectionNames, - { - .function = GeometrySplit{geometryDivisions, readout, geometryLabels}, - }, - app)); + {.tag = "TaggerTrackerSplitHits", + .input_names = {"TaggerTrackerRecHits"}, + .variadic_output_names = {geometryDivisionCollectionNames}, + .configs = { + .function = GeometrySplit{geometryDivisions, readout, geometryLabels}, + }})); app->Add(new JOmniFactoryGeneratorT( - "TaggerTrackerClustering", geometryDivisionCollectionNames, outputClusterCollectionNames, - { - .readout = "TaggerTrackerHits", - .x_field = "x", - .y_field = "y", - .hit_time_limit = 10 * edm4eic::unit::ns, - }, - app)); + {.tag = "TaggerTrackerClustering", + .variadic_input_names = {geometryDivisionCollectionNames}, + .variadic_output_names = {outputClusterCollectionNames}, + .configs = { + .readout = "TaggerTrackerHits", + .x_field = "x", + .y_field = "y", + .hit_time_limit = 10 * edm4eic::unit::ns, + }})); // Linear tracking for each module, loop over modules for (std::size_t i = 0; i < moduleIDs.size(); i++) { - std::string outputTrackTag = outputTrackTags[i]; - std::string outputTrackLinkTag = outputTrackLinkTags[i]; - std::string outputTrackAssociationTag = outputTrackAssociationTags[i]; - std::vector inputClusterTags = moduleClusterTags[i]; - - inputClusterTags.emplace_back("TaggerTrackerRawHitLinks"); - inputClusterTags.emplace_back("TaggerTrackerRawHitAssociations"); - + std::string outputTrackTag = outputTrackTags[i]; + std::string outputTrackLinkTag = outputTrackLinkTags[i]; + std::string outputTrackAssociationTag = outputTrackAssociationTags[i]; + std::vector inputClusterTags(moduleClusterTags[i]); app->Add(new JOmniFactoryGeneratorT( - outputTrackTag, {inputClusterTags}, - {outputTrackTag, outputTrackLinkTag, outputTrackAssociationTag}, - { - .layer_hits_max = 200, - .chi2_max = 0.001, - .n_layer = 4, - .layer_weights = {1.0, 1.0, 1.0, 1.0}, - .restrict_direction = true, - .optimum_theta = -M_PI + 0.026, - .optimum_phi = 0, - .step_angle_tolerance = 0.05, - }, - app)); + {.tag = outputTrackTag, + .input_names = {"TaggerTrackerRawHitLinks", "TaggerTrackerRawHitAssociations"}, + .variadic_input_names = {inputClusterTags}, + .output_names = + { + outputTrackTag, + outputTrackLinkTag, + outputTrackAssociationTag, + }, + .configs = { + .layer_hits_max = 200, + .chi2_max = 0.001, + .n_layer = 4, + .layer_weights = {1.0, 1.0, 1.0, 1.0}, + .restrict_direction = true, + .optimum_theta = -M_PI + 0.026, + .optimum_phi = 0, + .step_angle_tolerance = 0.05, + }})); } // Combine the tracks from each module into one collection app->Add(new JOmniFactoryGeneratorT>( - "TaggerTrackerLocalTracks", outputTrackTags, {"TaggerTrackerLocalTracks"}, app)); + {.tag = "TaggerTrackerLocalTracks", + .variadic_input_names = {outputTrackTags}, + .output_names = {"TaggerTrackerLocalTracks"}})); // Combine the track links from each module into one collection app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "TaggerTrackerLocalTrackLinks", outputTrackLinkTags, {"TaggerTrackerLocalTrackLinks"}, app)); + {.tag = "TaggerTrackerLocalTrackLinks", + .variadic_input_names = {outputTrackLinkTags}, + .output_names = {"TaggerTrackerLocalTrackLinks"}})); // Combine the associations from each module into one collection app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "TaggerTrackerLocalTrackAssociations", outputTrackAssociationTags, - {"TaggerTrackerLocalTrackAssociations"}, app)); + {.tag = "TaggerTrackerLocalTrackAssociations", + .variadic_input_names = {outputTrackAssociationTags}, + .output_names = {"TaggerTrackerLocalTrackAssociations"}})); app->Add(new JOmniFactoryGeneratorT( "TaggerTrackerTransportationPreML", @@ -199,15 +210,14 @@ void InitPlugin(JApplication* app) { {"TaggerTrackerFeatureTensor", "TaggerTrackerTargetTensor"}, { .beamE = 10.0, - }, - app)); + })); app->Add(new JOmniFactoryGeneratorT( - "TaggerTrackerTransportationInference", {"TaggerTrackerFeatureTensor"}, - {"TaggerTrackerPredictionTensor"}, - { - .modelPath = "calibrations/onnx/Low-Q2_Steering_Reconstruction.onnx", - }, - app)); + {.tag = "TaggerTrackerTransportationInference", + .variadic_input_names = {{"TaggerTrackerFeatureTensor"}}, + .variadic_output_names = {{"TaggerTrackerPredictionTensor"}}, + .configs = { + .modelPath = "calibrations/onnx/Low-Q2_Steering_Reconstruction.onnx", + }})); app->Add(new JOmniFactoryGeneratorT( "TaggerTrackerTransportationPostML", {"TaggerTrackerPredictionTensor", "TaggerTrackerLocalTrackAssociations", "MCBeamElectrons"}, @@ -215,7 +225,6 @@ void InitPlugin(JApplication* app) { "TaggerTrackerReconstructedParticleAssociations"}, { .beamE = 10.0, - }, - app)); + })); } } diff --git a/src/detectors/LUMISPECCAL/LUMISPECCAL.cc b/src/detectors/LUMISPECCAL/LUMISPECCAL.cc index 8fd5a33eac..bdce6be6ff 100644 --- a/src/detectors/LUMISPECCAL/LUMISPECCAL.cc +++ b/src/detectors/LUMISPECCAL/LUMISPECCAL.cc @@ -2,7 +2,9 @@ // Copyright (C) 2022 - 2025 Sylvester Joosten, Chao, Chao Peng, Whitney Armstrong, David Lawrence, Dhevan Gangadharan, Nathan Brei,, Wouter Deconinck, Dmitry Kalinkin, Derek Anderson #include +#include #include +#include #include #include #include @@ -21,6 +23,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/detectors/MPGD/MPGD.cc b/src/detectors/MPGD/MPGD.cc index 4c2879dcca..96afc03b5e 100644 --- a/src/detectors/MPGD/MPGD.cc +++ b/src/detectors/MPGD/MPGD.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -40,6 +41,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // ***** PIXEL or 2DSTRIP DIGITIZATION? // - This determines which of the MPGDTrackerDigi or SiliconTrackerDigi diff --git a/src/detectors/PFRICH/PFRICH.cc b/src/detectors/PFRICH/PFRICH.cc index 7d06098937..bc80253b68 100644 --- a/src/detectors/PFRICH/PFRICH.cc +++ b/src/detectors/PFRICH/PFRICH.cc @@ -5,7 +5,9 @@ // Copyright (C) 2024, Dmitry Kalinkin #include +#include #include +#include #include #include #include @@ -22,6 +24,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // configuration parameters /////////////////////////////////////////////// diff --git a/src/detectors/RPOTS/RPOTS.cc b/src/detectors/RPOTS/RPOTS.cc index c28d68e920..6a3d771fd7 100644 --- a/src/detectors/RPOTS/RPOTS.cc +++ b/src/detectors/RPOTS/RPOTS.cc @@ -4,7 +4,9 @@ // #include +#include #include +#include #include #include #include @@ -20,7 +22,9 @@ extern "C" { void InitPlugin(JApplication* app) { InitJANAPlugin(app); + using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; MatrixTransferStaticConfig recon_cfg; PolynomialMatrixReconstructionConfig recon_poly_cfg; diff --git a/src/detectors/ZDC/ZDC.cc b/src/detectors/ZDC/ZDC.cc index cc4c528ffb..d3bcc703f9 100644 --- a/src/detectors/ZDC/ZDC.cc +++ b/src/detectors/ZDC/ZDC.cc @@ -2,7 +2,9 @@ // Copyright (C) 2021 - 2025, Chao Peng, Sylvester Joosten, Whitney Armstrong, David Lawrence, Friederike Bock, Wouter Deconinck, Nathan Brei, Sebouh Paul, Dmitry Kalinkin, Barak Schmookler #include +#include #include +#include #include #include #include @@ -24,6 +26,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/extensions/jana/JOmniFactory.h b/src/extensions/jana/JOmniFactory.h index 83b00b7ad1..c29ea59445 100644 --- a/src/extensions/jana/JOmniFactory.h +++ b/src/extensions/jana/JOmniFactory.h @@ -4,574 +4,136 @@ #pragma once -/** - * Omnifactories are a lightweight layer connecting JANA to generic algorithms - * It is assumed multiple input data (controlled by input tags) - * which might be changed by user parameters. - */ - -#include -#include +#include +#if defined(JANA_VERSION_MAJOR) && defined(JANA_VERSION_MINOR) && defined(JANA_VERSION_PATCH) +#define EICRECON_JANA_IS_243 \ + (JANA_VERSION_MAJOR == 2 && JANA_VERSION_MINOR == 4 && JANA_VERSION_PATCH == 3) +#else +#define EICRECON_JANA_IS_243 1 +#endif +#if !EICRECON_JANA_IS_243 +#include +#endif +#include #include #include #if SPDLOG_VERSION >= 11400 && (!defined(SPDLOG_NO_TLS) || !SPDLOG_NO_TLS) #include #endif -#include "services/io/podio/datamodel_glue.h" #include "services/log/Log_service.h" +#include #include #include -struct EmptyConfig {}; - -template -class JOmniFactory : public JMultifactory { -public: - /// ======================== - /// Handle input collections - /// ======================== - - struct InputBase { - std::string type_name; - std::vector collection_names; - bool is_variadic = false; - - virtual void GetCollection(const JEvent& event) = 0; - }; - - template class Input : public InputBase { - - std::vector m_data; - - public: - Input(JOmniFactory* owner, std::string default_tag = "") { - owner->RegisterInput(this); - this->collection_names.push_back(default_tag); - this->type_name = JTypeInfo::demangle(); - } - - const std::vector& operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void GetCollection(const JEvent& event) { - try { - m_data = event.Get(this->collection_names[0], !IsOptional); - } catch (const JException& e) { - if constexpr (!IsOptional) { - throw JException("JOmniFactory: Failed to get collection %s: %s", - this->collection_names[0].c_str(), e.what()); - } - } - } - }; - - template class PodioInput : public InputBase { - - const typename PodioTypeMap::collection_t* m_data; - - public: - PodioInput(JOmniFactory* owner, std::string default_collection_name = "") { - owner->RegisterInput(this); - this->collection_names.push_back(default_collection_name); - this->type_name = JTypeInfo::demangle(); - } - - const typename PodioTypeMap::collection_t* operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void GetCollection(const JEvent& event) { - try { - m_data = event.GetCollection(this->collection_names[0], !IsOptional); - } catch (const JException& e) { - if constexpr (!IsOptional) { - throw JException("JOmniFactory: Failed to get collection %s: %s", - this->collection_names[0].c_str(), e.what()); - } - } - } - }; - - template class VariadicPodioInput : public InputBase { - - std::vector::collection_t*> m_data; - - public: - VariadicPodioInput(JOmniFactory* owner, std::vector default_names = {}) { - owner->RegisterInput(this); - this->collection_names = default_names; - this->type_name = JTypeInfo::demangle(); - this->is_variadic = true; - } - - const std::vector::collection_t*> operator()() { - return m_data; - } - - private: - friend class JOmniFactory; - - void GetCollection(const JEvent& event) { - m_data.clear(); - for (auto& coll_name : this->collection_names) { - try { - m_data.push_back(event.GetCollection(coll_name, !IsOptional)); - } catch (const JException& e) { - if constexpr (!IsOptional) { - throw JException("JOmniFactory: Failed to get collection %s: %s", coll_name.c_str(), - e.what()); - } - } - } - } - }; - - void RegisterInput(InputBase* input) { m_inputs.push_back(input); } - - /// ========================= - /// Handle output collections - /// ========================= - - struct OutputBase { - std::string type_name; - std::vector collection_names; - bool is_variadic = false; - - virtual void CreateHelperFactory(JOmniFactory& fac) = 0; - virtual void SetCollection(JOmniFactory& fac) = 0; - virtual void Reset() = 0; - }; +namespace eicrecon { - template class Output : public OutputBase { - std::vector m_data; - - public: - Output(JOmniFactory* owner, std::string default_tag_name = "") { - owner->RegisterOutput(this); - this->collection_names.push_back(default_tag_name); - this->type_name = JTypeInfo::demangle(); - } - - std::vector& operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void CreateHelperFactory(JOmniFactory& fac) override { - fac.DeclareOutput(this->collection_names[0]); - } - - void SetCollection(JOmniFactory& fac) override { - fac.SetData(this->collection_names[0], this->m_data); - } - - void Reset() override { m_data.clear(); } - }; - - template class PodioOutput : public OutputBase { - - std::unique_ptr::collection_t> m_data; - - public: - PodioOutput(JOmniFactory* owner, std::string default_collection_name = "") { - owner->RegisterOutput(this); - this->collection_names.push_back(default_collection_name); - this->type_name = JTypeInfo::demangle(); - } - - std::unique_ptr::collection_t>& operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void CreateHelperFactory(JOmniFactory& fac) override { - fac.DeclarePodioOutput(this->collection_names[0]); - } - - void SetCollection(JOmniFactory& fac) override { - if (m_data == nullptr) { - throw JException("JOmniFactory: SetCollection failed due to missing output collection '%s'", - this->collection_names[0].c_str()); - // Otherwise this leads to a PODIO segfault - } - fac.SetCollection(this->collection_names[0], std::move(this->m_data)); - } - - void Reset() override { - m_data = std::move(std::make_unique::collection_t>()); - } - }; - - template class VariadicPodioOutput : public OutputBase { - - std::vector::collection_t>> m_data; - - public: - VariadicPodioOutput(JOmniFactory* owner, - std::vector default_collection_names = {}) { - owner->RegisterOutput(this); - this->collection_names = default_collection_names; - this->type_name = JTypeInfo::demangle(); - this->is_variadic = true; - } - - std::vector::collection_t>>& operator()() { - return m_data; - } - - private: - friend class JOmniFactory; - - void CreateHelperFactory(JOmniFactory& fac) override { - for (auto& coll_name : this->collection_names) { - fac.DeclarePodioOutput(coll_name); - } - } +/** + * This shim class for jana::components::JOmniFactory serves two purposes: + * - plugging in the spdlog-based logger service we are using in EICrecon + * - setting the event number in the spdlog Mapped Diagnostic Context (MDC) + */ +template +class JOmniFactory : public jana::components::JOmniFactory { +private: + using JANA_JOmniFactory = jana::components::JOmniFactory; - void SetCollection(JOmniFactory& fac) override { - if (m_data.size() != this->collection_names.size()) { - throw JException("JOmniFactory: VariadicPodioOutput SetCollection failed: Declared %d " - "collections, but provided %d.", - this->collection_names.size(), m_data.size()); - // Otherwise this leads to a PODIO segfault - } - std::size_t i = 0; - for (auto& coll_name : this->collection_names) { - fac.SetCollection(coll_name, std::move(this->m_data[i++])); - } - } + // Hide Process(JEvent) in private to prevent accidental use + using JANA_JOmniFactory::Process; - void Reset() override { - m_data.clear(); - for (auto& coll_name [[maybe_unused]] : this->collection_names) { - m_data.push_back(std::make_unique::collection_t>()); - } - } - }; + /// Current logger + std::shared_ptr m_logger; - void RegisterOutput(OutputBase* output) { m_outputs.push_back(output); } +public: + template using ParameterRef = jana::components::JComponent::ParameterRef; - // ================= - // Handle parameters - // ================= + template using Parameter = jana::components::JComponent::Parameter; - struct ParameterBase { - std::string m_name; - std::string m_description; - virtual void Configure(JParameterManager& parman, const std::string& prefix) = 0; - virtual void Configure(std::map fields) = 0; - }; + template using Service = jana::components::JComponent::Service; - template class ParameterRef : public ParameterBase { + template using Input = jana::components::JHasInputs::Input; - T* m_data; + template using VariadicInput = jana::components::JHasInputs::VariadicPodioInput; + template + class PodioInput : public jana::components::JHasInputs::PodioInput { public: - ParameterRef(JOmniFactory* owner, std::string name, T& slot, std::string description = "") { - owner->RegisterParameter(this); - this->m_name = name; - this->m_description = description; - m_data = &slot; - } - - const T& operator()() { return *m_data; } - - private: - friend class JOmniFactory; - - void Configure(JParameterManager& parman, const std::string& prefix) override { - parman.SetDefaultParameter(prefix + ":" + this->m_name, *m_data, this->m_description); - } - void Configure(std::map fields) override { - auto it = fields.find(this->m_name); - if (it != fields.end()) { - const auto& value_str = it->second; - JParameterManager::Parse(value_str, *m_data); - } - } + explicit PodioInput(JOmniFactory* owner, std::string default_collection_name = "") + : jana::components::JHasInputs::PodioInput( + owner, jana::components::JHasInputs::InputOptions{ + .name = std::move(default_collection_name), + .is_optional = IsOptional, + }) {} + + explicit PodioInput(JOmniFactory* owner, jana::components::JHasInputs::InputOptions options) + : jana::components::JHasInputs::PodioInput(owner, options) {} }; - template class Parameter : public ParameterBase { - - T m_data; - + template + class VariadicPodioInput : public jana::components::JHasInputs::VariadicPodioInput { public: - Parameter(JOmniFactory* owner, std::string name, T default_value, std::string description) { - owner->RegisterParameter(this); - this->m_name = name; - this->m_description = description; - m_data = default_value; - } - - const T& operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void Configure(JParameterManager& parman, const std::string& /* prefix */) override { - parman.SetDefaultParameter(m_prefix + ":" + this->m_name, m_data, this->m_description); - } - void Configure(std::map fields) override { - auto it = fields.find(this->m_name); - if (it != fields.end()) { - const auto& value_str = it->second; - if constexpr (10000 * JVersion::major + 100 * JVersion::minor + 1 * JVersion::patch < - 20102) { - m_data = JParameterManager::Parse(value_str); - } else { - JParameterManager::Parse(value_str, m_data); - } - } - } + explicit VariadicPodioInput(JOmniFactory* owner, std::vector default_names = {}) + : jana::components::JHasInputs::VariadicPodioInput( + owner, jana::components::JHasInputs::VariadicInputOptions{ + .names = std::move(default_names), + .is_optional = IsOptional, + }) {} + + explicit VariadicPodioInput(JOmniFactory* owner, + jana::components::JHasInputs::VariadicInputOptions options) + : jana::components::JHasInputs::VariadicPodioInput(owner, options) {} }; - void RegisterParameter(ParameterBase* parameter) { m_parameters.push_back(parameter); } +#if EICRECON_JANA_IS_243 + template using PodioOutput = jana::components::JHasOutputs::PodioOutput; + template + using VariadicPodioOutput = jana::components::JHasOutputs::VariadicPodioOutput; +#else + template using PodioOutput = jana::components::PodioOutput; + template + using VariadicPodioOutput = jana::components::VariadicPodioOutput; +#endif - void ConfigureAllParameters(std::map fields) { - for (auto* parameter : this->m_parameters) { - parameter->Configure(fields); - } + inline void PreInit(std::string tag, JEventLevel level, + std::vector input_collection_names, + std::vector input_collection_levels, + std::vector> variadic_input_collection_names, + std::vector variadic_input_collection_levels, + std::vector output_collection_names, + std::vector> variadic_output_collection_names) { + + // PreInit using the underlying JANA JOmniFactory + JANA_JOmniFactory::PreInit(tag, level, input_collection_names, input_collection_levels, + variadic_input_collection_names, variadic_input_collection_levels, + output_collection_names, variadic_output_collection_names); + + // But obtain our own logger (defines the parameter option) + m_logger = + this->GetApplication()->template GetService()->logger(this->GetPrefix()); } - // =============== - // Handle services - // =============== - - struct ServiceBase { - virtual void Init(JApplication* app) = 0; - }; - - template class Service : public ServiceBase { - - std::shared_ptr m_data; - - public: - Service(JOmniFactory* owner) { owner->RegisterService(this); } - - ServiceT& operator()() { return *m_data; } - - private: - friend class JOmniFactory; - - void Init(JApplication* app) { m_data = app->GetService(); } - }; - - void RegisterService(ServiceBase* service) { m_services.push_back(service); } - - // ================ - // Handle resources - // ================ - - struct ResourceBase { - virtual void ChangeRun(const JEvent& event) = 0; - }; - - template - class Resource : public ResourceBase { - ResourceT m_data; - LambdaT m_lambda; - - public: - Resource(JOmniFactory* owner, LambdaT lambda) : m_lambda(lambda) { - owner->RegisterResource(this); - }; - - const ResourceT& operator()() { return m_data; } - - private: - friend class JOmniFactory; - - void ChangeRun(const JEvent& event) { - auto run_nr = event.GetRunNumber(); - std::shared_ptr service = event.GetJApplication()->template GetService(); - m_data = m_lambda(service, run_nr); - } + virtual void Execute(int32_t run_number, uint64_t event_number) { +#if SPDLOG_VERSION >= 11400 && (!defined(SPDLOG_NO_TLS) || !SPDLOG_NO_TLS) + spdlog::mdc::put("e", std::to_string(event_number)); +#endif + static_cast(this)->Process(run_number, event_number); }; - void RegisterResource(ResourceBase* resource) { m_resources.push_back(resource); } - -public: - std::vector m_inputs; - std::vector m_outputs; - std::vector m_parameters; - std::vector m_services; - std::vector m_resources; - -private: - // App belongs on JMultifactory, it is just missing temporarily - JApplication* m_app; - - // Plugin name belongs on JMultifactory, it is just missing temporarily - std::string m_plugin_name; - - // Prefix for parameters and loggers, derived from plugin name and tag in PreInit(). - std::string m_prefix; - - /// Current logger - std::shared_ptr m_logger; - - /// Configuration - ConfigT m_config; - -public: - std::size_t FindVariadicCollectionCount(std::size_t total_input_count, - std::size_t variadic_input_count, - std::size_t total_collection_count, bool is_input) { - - std::size_t variadic_collection_count = - total_collection_count - (total_input_count - variadic_input_count); - - if (variadic_input_count == 0) { - // No variadic inputs: check that collection_name count matches input count exactly - if (total_input_count != total_collection_count) { - throw JException( - "JOmniFactory '%s': Wrong number of %s collection names: %d expected, %d found.", - m_prefix.c_str(), (is_input ? "input" : "output"), total_input_count, - total_collection_count); - } - } else { - // Variadic inputs: check that we have enough collection names for the non-variadic inputs - if (total_input_count - variadic_input_count > total_collection_count) { - throw JException("JOmniFactory '%s': Not enough %s collection names: %d needed, %d found.", - m_prefix.c_str(), (is_input ? "input" : "output"), - total_input_count - variadic_input_count, total_collection_count); - } - - // Variadic inputs: check that the variadic collection names is evenly divided by the variadic input count - if (variadic_collection_count % variadic_input_count != 0) { - throw JException("JOmniFactory '%s': Wrong number of %s collection names: %d found total, " - "but %d can't be distributed among %d variadic inputs evenly.", - m_prefix.c_str(), (is_input ? "input" : "output"), total_collection_count, - variadic_collection_count, variadic_input_count); - } - } - return variadic_collection_count; - } - - inline void PreInit(std::string tag, std::vector default_input_collection_names, - std::vector default_output_collection_names) { - - m_prefix = (this->GetPluginName().empty()) ? tag : this->GetPluginName() + ":" + tag; - - // Obtain collection name overrides if provided. - // Priority = [JParameterManager, JOmniFactoryGenerator] - m_app->SetDefaultParameter(m_prefix + ":InputTags", default_input_collection_names, - "Input collection names"); - m_app->SetDefaultParameter(m_prefix + ":OutputTags", default_output_collection_names, - "Output collection names"); - - // Figure out variadic inputs - std::size_t variadic_input_count = 0; - for (auto* input : m_inputs) { - if (input->is_variadic) { - variadic_input_count += 1; - } - } - std::size_t variadic_input_collection_count = FindVariadicCollectionCount( - m_inputs.size(), variadic_input_count, default_input_collection_names.size(), true); - - // Set input collection names - for (std::size_t i = 0; auto* input : m_inputs) { - input->collection_names.clear(); - if (input->is_variadic) { - for (std::size_t j = 0; j < (variadic_input_collection_count / variadic_input_count); ++j) { - input->collection_names.push_back(default_input_collection_names[i++]); - } - } else { - input->collection_names.push_back(default_input_collection_names[i++]); - } - } - - // Figure out variadic outputs - std::size_t variadic_output_count = 0; - for (auto* output : m_outputs) { - if (output->is_variadic) { - variadic_output_count += 1; - } - } - std::size_t variadic_output_collection_count = FindVariadicCollectionCount( - m_outputs.size(), variadic_output_count, default_output_collection_names.size(), true); - - // Set output collection names and create corresponding helper factories - for (std::size_t i = 0; auto* output : m_outputs) { - output->collection_names.clear(); - if (output->is_variadic) { - for (std::size_t j = 0; j < (variadic_output_collection_count / variadic_output_count); - ++j) { - output->collection_names.push_back(default_output_collection_names[i++]); - } - } else { - output->collection_names.push_back(default_output_collection_names[i++]); - } - output->CreateHelperFactory(*this); - } - - // Obtain logger (defines the parameter option) - m_logger = m_app->GetService()->logger(m_prefix); - } - - void Init() override { - auto app = GetApplication(); - for (auto* parameter : m_parameters) { - parameter->Configure(*(app->GetJParameterManager()), m_prefix); - } - for (auto* service : m_services) { - service->Init(app); - } - static_cast(this)->Configure(); - } - - void BeginRun(const std::shared_ptr& event) override { - for (auto* resource : m_resources) { - resource->ChangeRun(*event); - } - static_cast(this)->ChangeRun(event->GetRunNumber()); - } - - virtual void ChangeRun(int32_t /* run_number */) override {}; - virtual void Process(int32_t /* run_number */, uint64_t /* event_number */) {}; - void Process(const std::shared_ptr& event) override { - try { - for (auto* input : m_inputs) { - input->GetCollection(*event); - } - for (auto* output : m_outputs) { - output->Reset(); - } -#if SPDLOG_VERSION >= 11400 && (!defined(SPDLOG_NO_TLS) || !SPDLOG_NO_TLS) - spdlog::mdc::put("e", std::to_string(event->GetEventNumber())); -#endif - static_cast(this)->Process(event->GetRunNumber(), event->GetEventNumber()); - for (auto* output : m_outputs) { - output->SetCollection(*this); - } - } catch (std::exception& e) { - throw JException(e.what()); - } + /// Retrieve reference to already-configured logger + std::shared_ptr& logger() { + // Some JANA releases may call Configure() before our PreInit() path. + // Lazily initialize to keep factory Configure() safe across layouts. + if (m_logger == nullptr) { + m_logger = + this->GetApplication()->template GetService()->logger(this->GetPrefix()); + } + return m_logger; } +}; - using ConfigType = ConfigT; - - void SetApplication(JApplication* app) { m_app = app; } - - JApplication* GetApplication() { return m_app; } - - void SetPluginName(std::string plugin_name) { m_plugin_name = plugin_name; } - - std::string GetPluginName() { return m_plugin_name; } - - inline std::string GetPrefix() { return m_prefix; } +} // namespace eicrecon - /// Retrieve reference to already-configured logger - std::shared_ptr& logger() { return m_logger; } - - /// Retrieve reference to embedded config object - ConfigT& config() { return m_config; } -}; +#undef EICRECON_JANA_IS_243 diff --git a/src/extensions/jana/JOmniFactoryGeneratorT.h b/src/extensions/jana/JOmniFactoryGeneratorT.h index f5846bf4fc..511b1d5cd2 100644 --- a/src/extensions/jana/JOmniFactoryGeneratorT.h +++ b/src/extensions/jana/JOmniFactoryGeneratorT.h @@ -4,96 +4,37 @@ #pragma once -#include -#include -#include - -template class JOmniFactoryGeneratorT : public JFactoryGenerator { -public: - using FactoryConfigType = typename FactoryT::ConfigType; +#include +#include -private: - struct TypedWiring { - std::string m_tag; - std::vector m_default_input_tags; - std::vector m_default_output_tags; - FactoryConfigType m_default_cfg; /// Must be properly copyable! - }; +#include +#include +#include - struct UntypedWiring { - std::string m_tag; - std::vector m_default_input_tags; - std::vector m_default_output_tags; - std::map m_config_params; - }; +namespace eicrecon { +// Fallthrough to JANA's built-in JOmniFactoryGeneratorT, but allow for unused app argument in constructor +template +class JOmniFactoryGeneratorT : public jana::components::JOmniFactoryGeneratorT { public: - explicit JOmniFactoryGeneratorT(std::string tag, std::vector default_input_tags, - std::vector default_output_tags, - FactoryConfigType cfg, JApplication* app) { - m_app = app; - m_wirings.push_back({.m_tag = tag, - .m_default_input_tags = default_input_tags, - .m_default_output_tags = default_output_tags, - .m_default_cfg = cfg}); - }; - - explicit JOmniFactoryGeneratorT(std::string tag, std::vector default_input_tags, - std::vector default_output_tags, JApplication* app) { - m_app = app; - m_wirings.push_back({.m_tag = tag, - .m_default_input_tags = default_input_tags, - .m_default_output_tags = default_output_tags, - .m_default_cfg = {}}); - } - - explicit JOmniFactoryGeneratorT(JApplication* app) : m_app(app) {} - - void AddWiring(std::string tag, std::vector default_input_tags, - std::vector default_output_tags, FactoryConfigType cfg) { - - m_wirings.push_back({.m_tag = tag, - .m_default_input_tags = default_input_tags, - .m_default_output_tags = default_output_tags, - .m_default_cfg = cfg}); - } - - void AddWiring(std::string tag, std::vector default_input_tags, - std::vector default_output_tags, - std::map config_params) { - - // Create throwaway factory so we can populate its config using our map. - FactoryT factory; - factory.ConfigureAllParameters(config_params); - auto config = factory.config(); - - m_wirings.push_back({.m_tag = tag, - .m_default_input_tags = default_input_tags, - .m_default_output_tags = default_output_tags, - .m_default_cfg = config}); - } - - void GenerateFactories(JFactorySet* factory_set) override { - - for (const auto& wiring : m_wirings) { + using FactoryConfigType = typename FactoryT::ConfigType; + using TypedWiring = typename jana::components::JOmniFactoryGeneratorT::TypedWiring; - FactoryT* factory = new FactoryT; - factory->SetApplication(m_app); - factory->SetPluginName(this->GetPluginName()); - factory->SetFactoryName(JTypeInfo::demangle()); - factory->config() = wiring.m_default_cfg; + explicit JOmniFactoryGeneratorT() = default; - // Set up all of the wiring prereqs so that Init() can do its thing - // Specifically, it needs valid input/output tags, a valid logger, and - // valid default values in its Config object - factory->PreInit(wiring.m_tag, wiring.m_default_input_tags, wiring.m_default_output_tags); + explicit JOmniFactoryGeneratorT(std::string tag, std::vector input_names, + std::vector output_names, FactoryConfigType configs, + JApplication* /* app */ = nullptr) + : jana::components::JOmniFactoryGeneratorT(tag, input_names, output_names, + configs) {} - // Factory is ready - factory_set->Add(factory); - } - } + explicit JOmniFactoryGeneratorT(std::string tag, std::vector input_names, + std::vector output_names, + JApplication* /* app */ = nullptr) + : jana::components::JOmniFactoryGeneratorT(tag, input_names, output_names) {} -private: - std::vector m_wirings; - JApplication* m_app; + explicit JOmniFactoryGeneratorT(TypedWiring&& wiring) + : jana::components::JOmniFactoryGeneratorT(std::move(wiring)) {} }; + +} // namespace eicrecon diff --git a/src/factories/digi/CFDROCDigitization_factory.h b/src/factories/digi/CFDROCDigitization_factory.h index 50276c0494..4450b4532a 100644 --- a/src/factories/digi/CFDROCDigitization_factory.h +++ b/src/factories/digi/CFDROCDigitization_factory.h @@ -35,8 +35,6 @@ class CFDROCDigitization_factory m_algo->init(); } - void ChangeRun(int32_t /* run_number */) {} - void Process(int32_t /* run_number */, uint64_t /* event_number */) { m_algo->process({m_in_sim_track()}, {m_out_reco_particles().get()}); } diff --git a/src/factories/meta/CollectionCollector_factory.h b/src/factories/meta/CollectionCollector_factory.h index b10f29d5f3..6432fa9bb5 100644 --- a/src/factories/meta/CollectionCollector_factory.h +++ b/src/factories/meta/CollectionCollector_factory.h @@ -3,6 +3,8 @@ #pragma once +#include + #include "extensions/jana/JOmniFactory.h" #include "algorithms/meta/CollectionCollector.h" @@ -18,8 +20,8 @@ class CollectionCollector_factory std::unique_ptr m_algo; typename JOmniFactory, - NoConfig>::template VariadicPodioInput - m_inputs{this}; + NoConfig>::template VariadicPodioInput + m_inputs{this, {.is_optional = IsOptional}}; typename JOmniFactory, NoConfig>::template PodioOutput m_output{this}; @@ -35,6 +37,13 @@ class CollectionCollector_factory void Process(int32_t /* run_number */, uint64_t /* event_number */) { std::vector> in_collections; for (const auto& in_collection : m_inputs()) { + if (in_collection == nullptr) { + if constexpr (IsOptional) { + continue; + } + throw JException("CollectionCollector '%s' received null required input collection.", + this->GetPrefix().c_str()); + } in_collections.push_back(gsl::not_null{in_collection}); } typename T::collection_type* merged_collection = m_output().get(); diff --git a/src/factories/reco/ChargedMCParticleSelector_factory.h b/src/factories/reco/ChargedMCParticleSelector_factory.h index 74c652ea39..db0cabd34e 100644 --- a/src/factories/reco/ChargedMCParticleSelector_factory.h +++ b/src/factories/reco/ChargedMCParticleSelector_factory.h @@ -18,7 +18,7 @@ class ChargedMCParticleSelector_factory std::unique_ptr m_algo; // input collection - PodioInput m_pars_in{this, "GeneratedParticles"}; + PodioInput m_pars_in{this}; // output collection PodioOutput m_pars_out{this}; diff --git a/src/factories/reco/ChargedReconstructedParticleSelector_factory.h b/src/factories/reco/ChargedReconstructedParticleSelector_factory.h index fb640a6614..57e008bb3b 100644 --- a/src/factories/reco/ChargedReconstructedParticleSelector_factory.h +++ b/src/factories/reco/ChargedReconstructedParticleSelector_factory.h @@ -18,7 +18,7 @@ class ChargedReconstructedParticleSelector_factory std::unique_ptr m_algo; // input collection - PodioInput m_pars_in{this, "GeneratedParticles"}; + PodioInput m_pars_in{this}; // output collection PodioOutput m_pars_out{this}; diff --git a/src/factories/reco/LGADHitCalibration_factory.h b/src/factories/reco/LGADHitCalibration_factory.h index 05745b84f2..f686087fc8 100644 --- a/src/factories/reco/LGADHitCalibration_factory.h +++ b/src/factories/reco/LGADHitCalibration_factory.h @@ -30,8 +30,6 @@ class LGADHitCalibration_factory m_algo->init(); } - void ChangeRun(int32_t /* run_number */) {} - void Process(int32_t /* run_number */, uint64_t /* event_number */) { m_algo->process({m_raw_hits_input()}, {m_rec_hits_output().get()}); } diff --git a/src/factories/reco/ReconstructedElectrons_factory.h b/src/factories/reco/ReconstructedElectrons_factory.h index c18c5ef712..e77fb4cf20 100644 --- a/src/factories/reco/ReconstructedElectrons_factory.h +++ b/src/factories/reco/ReconstructedElectrons_factory.h @@ -19,7 +19,7 @@ class ReconstructedElectrons_factory std::unique_ptr m_algo; // Declare inputs - PodioInput m_in_rc_particles{this, "ReconstructedParticles"}; + PodioInput m_in_rc_particles{this}; // Declare outputs PodioOutput m_out_reco_particles{this}; diff --git a/src/factories/tracking/LGADHitClustering_factory.h b/src/factories/tracking/LGADHitClustering_factory.h index 20f757f45c..45424a998b 100644 --- a/src/factories/tracking/LGADHitClustering_factory.h +++ b/src/factories/tracking/LGADHitClustering_factory.h @@ -29,8 +29,6 @@ class LGADHitClustering_factory m_algo->init(); } - void ChangeRun(int32_t /* run_number */) {} - void Process(int32_t /* run_number */, uint64_t /* event_number */) { m_algo->process({m_hits_input()}, {m_clusters_output().get()}); } diff --git a/src/global/beam/beam.cc b/src/global/beam/beam.cc index d94afcfd34..b16a53d8d8 100644 --- a/src/global/beam/beam.cc +++ b/src/global/beam/beam.cc @@ -5,11 +5,10 @@ #include #include +#include #include #include -#include #include -#include #include #include #include @@ -24,6 +23,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Divide MCParticle collection based on generator status and PDG std::vector outCollections{"MCBeamElectrons", "MCBeamProtons", @@ -33,16 +33,19 @@ void InitPlugin(JApplication* app) { {1, 11}, {1, 2212}, {1, 2112}}; app->Add(new JOmniFactoryGeneratorT>( - "BeamParticles", {"MCParticles"}, outCollections, - { - .function = - ValueSplit<&edm4hep::MCParticle::getGeneratorStatus, &edm4hep::MCParticle::getPDG>{ - values}, - }, - app)); + {.tag = "BeamParticles", + .input_names = {"MCParticles"}, + .variadic_output_names = {outCollections}, + .configs = { + .function = + ValueSplit<&edm4hep::MCParticle::getGeneratorStatus, &edm4hep::MCParticle::getPDG>{ + values}, + }})); // Combine beam protons and neutrons into beam hadrons app->Add(new JOmniFactoryGeneratorT>( - "MCBeamHadrons", {"MCBeamProtons", "MCBeamNeutrons"}, {"MCBeamHadrons"}, app)); + {.tag = "MCBeamHadrons", + .variadic_input_names = {{"MCBeamProtons", "MCBeamNeutrons"}}, + .output_names = {"MCBeamHadrons"}})); } } diff --git a/src/global/particle_flow/particle_flow.cc b/src/global/particle_flow/particle_flow.cc index 3f0a5844ff..6bb62cd689 100644 --- a/src/global/particle_flow/particle_flow.cc +++ b/src/global/particle_flow/particle_flow.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -27,6 +28,7 @@ extern "C" { void InitPlugin(JApplication* app) { using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; InitJANAPlugin(app); diff --git a/src/global/pid/pid.cc b/src/global/pid/pid.cc index fe2f3e72e3..22c7e5341c 100644 --- a/src/global/pid/pid.cc +++ b/src/global/pid/pid.cc @@ -1,7 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (C) 2022, 2023, Christopher Dilks +#include #include +#include #include #include #include @@ -15,6 +17,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // wiring between factories and data /////////////////////////////////////// diff --git a/src/global/pid_lut/pid_lut.cc b/src/global/pid_lut/pid_lut.cc index 9edae21842..6a22773421 100644 --- a/src/global/pid_lut/pid_lut.cc +++ b/src/global/pid_lut/pid_lut.cc @@ -1,7 +1,9 @@ // SPDX-License-Identifier: LGPL-3.0-or-later // Copyright (C) 2022-2025 Christopher Dilks, Simon Gardner +#include #include +#include #include #include #include @@ -21,6 +23,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; //------------------------------------------------------------------------- // PFRICH PID @@ -187,33 +190,35 @@ void InitPlugin(JApplication* app) { app->Add( new JOmniFactoryGeneratorT>( - "ReconstructedWithPFRICHTOFDIRCLOWQ2PIDChargedParticles", - {"ReconstructedChargedWithPFRICHTOFDIRCPIDParticles", - "TaggerTrackerReconstructedParticles"}, - {"ReconstructedWithPFRICHTOFDIRCLOWQ2PIDChargedParticles"}, app)); + {.tag = "ReconstructedWithPFRICHTOFDIRCLOWQ2PIDChargedParticles", + .variadic_input_names = {{"ReconstructedChargedWithPFRICHTOFDIRCPIDParticles", + "TaggerTrackerReconstructedParticles"}}, + .output_names = {"ReconstructedWithPFRICHTOFDIRCLOWQ2PIDChargedParticles"}})); app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "ReconstructedChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations", - {"ReconstructedChargedWithPFRICHTOFDIRCPIDParticleAssociations", - "TaggerTrackerReconstructedParticleAssociations"}, - {"ReconstructedChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations"}, app)); + {.tag = "ReconstructedChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations", + .variadic_input_names = {{"ReconstructedChargedWithPFRICHTOFDIRCPIDParticleAssociations", + "TaggerTrackerReconstructedParticleAssociations"}}, + .output_names = {"ReconstructedChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations"}})); // And the same for truth seeded particles and associations app->Add( new JOmniFactoryGeneratorT>( - "ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticles", - {"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCPIDParticles", - "TaggerTrackerReconstructedParticles"}, - {"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticles"}, app)); + {.tag = "ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticles", + .variadic_input_names = {{"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCPIDParticles", + "TaggerTrackerReconstructedParticles"}}, + .output_names = {"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticles"}})); app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations", - {"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCPIDParticleAssociations", - "TaggerTrackerReconstructedParticleAssociations"}, - {"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations"}, app)); + {.tag = "ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations", + .variadic_input_names = + {{"ReconstructedTruthSeededChargedWithPFRICHTOFDIRCPIDParticleAssociations", + "TaggerTrackerReconstructedParticleAssociations"}}, + .output_names = { + "ReconstructedTruthSeededChargedWithPFRICHTOFDIRCLOWQ2PIDParticleAssociations"}})); //------------------------------------------------------------------------- // DRICH PID diff --git a/src/global/reco/reco.cc b/src/global/reco/reco.cc index ac860f185c..6c09e1a595 100644 --- a/src/global/reco/reco.cc +++ b/src/global/reco/reco.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; // Finds associations matched to initial scattered electrons app->Add( @@ -64,15 +66,18 @@ void InitPlugin(JApplication* app) { "GeneratedParticles", {"MCParticles"}, {"GeneratedParticles"}, app)); app->Add(new JOmniFactoryGeneratorT>( - "EcalClusters", {"EcalEndcapNClusters", "EcalBarrelScFiClusters", "EcalEndcapPClusters"}, - {"EcalClusters"}, app)); + {.tag = "EcalClusters", + .variadic_input_names = {{"EcalEndcapNClusters", "EcalBarrelScFiClusters", + "EcalEndcapPClusters"}}, + .output_names = {"EcalClusters"}})); app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "EcalClusterAssociations", - {"EcalEndcapNClusterAssociations", "EcalBarrelScFiClusterAssociations", - "EcalEndcapPClusterAssociations"}, - {"EcalClusterAssociations"}, app)); + {.tag = "EcalClusterAssociations", + .variadic_input_names = {{"EcalEndcapNClusterAssociations", + "EcalBarrelScFiClusterAssociations", + "EcalEndcapPClusterAssociations"}}, + .output_names = {"EcalClusterAssociations"}})); app->Add(new JOmniFactoryGeneratorT( "ReconstructedParticlesWithAssoc", @@ -119,8 +124,9 @@ void InitPlugin(JApplication* app) { // InclusiveKinematicseSigma is deprecated and will be removed, use InclusiveKinematicsESigma instead app->Add(new JOmniFactoryGeneratorT>( - "InclusiveKinematicseSigma_legacy", {"InclusiveKinematicsESigma"}, - {"InclusiveKinematicseSigma"}, app)); + {.tag = "InclusiveKinematicseSigma_legacy", + .variadic_input_names = {{"InclusiveKinematicsESigma"}}, + .output_names = {"InclusiveKinematicseSigma"}})); app->Add(new JOmniFactoryGeneratorT< InclusiveKinematicsReconstructed_factory>( diff --git a/src/global/tracking/tracking.cc b/src/global/tracking/tracking.cc index 053f290c81..42e2659ef9 100644 --- a/src/global/tracking/tracking.cc +++ b/src/global/tracking/tracking.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,7 @@ void InitPlugin(JApplication* app) { InitJANAPlugin(app); using namespace eicrecon; + using eicrecon::JOmniFactoryGeneratorT; app->Add(new JOmniFactoryGeneratorT( "TrackerTruthSeeds", {"EventHeader", "MCParticles"}, @@ -56,36 +58,35 @@ void InitPlugin(JApplication* app) { std::vector> thetaRanges{{0, 50 * dd4hep::mrad}, {50 * dd4hep::mrad, 180 * dd4hep::deg}}; app->Add(new JOmniFactoryGeneratorT>( - "CentralB0TrackerTruthSeeds", {"TrackerTruthSeeds"}, - {"B0TrackerTruthSeeds", "CentralTrackerTruthSeeds"}, - { - .function = RangeSplit< - Chain<&edm4eic::TrackSeed::getParams, &edm4eic::TrackParameters::getTheta>>( - thetaRanges), - }, - app)); - + {.tag = "CentralB0TrackerTruthSeeds", + .input_names = {"TrackerTruthSeeds"}, + .variadic_output_names = {{"B0TrackerTruthSeeds", "CentralTrackerTruthSeeds"}}, + .configs = { + .function = RangeSplit>(thetaRanges), + }})); // CENTRAL TRACKER // Tracker hits collector app->Add(new JOmniFactoryGeneratorT>( - "CentralTrackingRecHits", - {"SiBarrelTrackerRecHits", "SiBarrelVertexRecHits", "SiEndcapTrackerRecHits", - "MPGDBarrelRecHits", "OuterMPGDBarrelRecHits", "BackwardMPGDEndcapRecHits", - "ForwardMPGDEndcapRecHits"}, - {"CentralTrackingRecHits"}, // Output collection name - app)); + {.tag = "CentralTrackingRecHits", + .variadic_input_names = {{"SiBarrelTrackerRecHits", "SiBarrelVertexRecHits", + "SiEndcapTrackerRecHits", "MPGDBarrelRecHits", + "OuterMPGDBarrelRecHits", "BackwardMPGDEndcapRecHits", + "ForwardMPGDEndcapRecHits"}}, + .output_names = {"CentralTrackingRecHits"}})); // Tracker hit associations collector app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "CentralTrackingRawHitAssociations", - {"SiBarrelRawHitAssociations", "SiBarrelVertexRawHitAssociations", - "SiEndcapTrackerRawHitAssociations", "MPGDBarrelRawHitAssociations", - "OuterMPGDBarrelRawHitAssociations", "BackwardMPGDEndcapRawHitAssociations", - "ForwardMPGDEndcapRawHitAssociations"}, - {"CentralTrackingRawHitAssociations"}, // Output collection name - app)); + {.tag = "CentralTrackingRawHitAssociations", + .variadic_input_names = {{"SiBarrelRawHitAssociations", "SiBarrelVertexRawHitAssociations", + "SiEndcapTrackerRawHitAssociations", + "MPGDBarrelRawHitAssociations", + "OuterMPGDBarrelRawHitAssociations", + "BackwardMPGDEndcapRawHitAssociations", + "ForwardMPGDEndcapRawHitAssociations"}}, + .output_names = {"CentralTrackingRawHitAssociations"}})); // Tracker hit links collector app->Add( @@ -460,7 +461,9 @@ void InitPlugin(JApplication* app) { // Add central and B0 tracks app->Add(new JOmniFactoryGeneratorT>( - "CombinedTracks", {"CentralCKFTracks", "B0TrackerCKFTracks"}, {"CombinedTracks"}, app)); + {.tag = "CombinedTracks", + .variadic_input_names = {{"CentralCKFTracks", "B0TrackerCKFTracks"}}, + .output_names = {"CombinedTracks"}})); app->Add(new JOmniFactoryGeneratorT( "PrimaryVerticesAMVF", @@ -489,18 +492,21 @@ void InitPlugin(JApplication* app) { app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "CombinedTrackAssociations", {"CentralCKFTrackAssociations", "B0TrackerCKFTrackAssociations"}, - {"CombinedTrackAssociations"}, app)); + {.tag = "CombinedTrackAssociations", + .variadic_input_names = {{"CentralCKFTrackAssociations", "B0TrackerCKFTrackAssociations"}}, + .output_names = {"CombinedTrackAssociations"}})); app->Add(new JOmniFactoryGeneratorT>( - "CombinedTruthSeededTracks", {"CentralCKFTruthSeededTracks", "B0TrackerCKFTruthSeededTracks"}, - {"CombinedTruthSeededTracks"}, app)); + {.tag = "CombinedTruthSeededTracks", + .variadic_input_names = {{"CentralCKFTruthSeededTracks", "B0TrackerCKFTruthSeededTracks"}}, + .output_names = {"CombinedTruthSeededTracks"}})); app->Add(new JOmniFactoryGeneratorT< CollectionCollector_factory>( - "CombinedTruthSeededTrackAssociations", - {"CentralCKFTruthSeededTrackAssociations", "B0TrackerCKFTruthSeededTrackAssociations"}, - {"CombinedTruthSeededTrackAssociations"}, app)); + {.tag = "CombinedTruthSeededTrackAssociations", + .variadic_input_names = {{"CentralCKFTruthSeededTrackAssociations", + "B0TrackerCKFTruthSeededTrackAssociations"}}, + .output_names = {"CombinedTruthSeededTrackAssociations"}})); app->Add(new JOmniFactoryGeneratorT( "ChargedTruthSeededParticlesWithAssociations",