Warning, /tutorial-reconstruction-algorithms/_episodes/07-putting-everything-together.md is written in an unsupported language. File is not indexed.
0001 ---
0002 title: "Putting everything together"
0003 teaching: 5
0004 exercises: 0
0005 objectives:
0006 - "Get the electron finder running end-to-end"
0007 ---
0008
0009
0010 ## The final ReconstructedElectron factory
0011
0012 Here is the final ReconstructedElectron factory. Our algorithm requires reconstructed tracks, calorimeter clusters, and associations between the two. As previously stated, the current implementation of the simple electron ID uses the truth association between tracks and clusters (association using matching between clusters and track projections will be implemented later). Thus, we need two sets of associations: association between the truth particle and the reconstructed charged particle, and association between the truth particle and the calorimeter cluster. Obviously, we will not create these objects from scratch. Rather, we will get them from the factories (and underlying algorithms) implemented to create these objects.
0013
0014
0015 `src/global/reco/ReconstructedElectrons_factory.h` in branch `nbrei_variadic_omnifactories`:
0016
0017 ~~~ c++
0018
0019 #pragma once
0020
0021 #include "extensions/jana/JOmniFactory.h"
0022
0023 #include "algorithms/reco/ElectronReconstruction.h"
0024
0025
0026 namespace eicrecon {
0027
0028 class ReconstructedElectrons_factory : public JOmniFactory<ReconstructedElectrons_factory, ElectronReconstructionConfig> {
0029 private:
0030
0031 // Underlying algorithm
0032 std::unique_ptr<eicrecon::ElectronReconstruction> m_algo;
0033
0034 // Declare inputs
0035 PodioInput<edm4hep::MCParticle> m_in_mc_particles {this, "MCParticles"};
0036 PodioInput<edm4eic::ReconstructedParticle> m_in_rc_particles {this, "ReconstructedChargedParticles"};
0037 PodioInput<edm4eic::MCRecoParticleAssociation> m_in_rc_particles_assoc {this, "ReconstructedChargedParticleAssociations"};
0038
0039 VariadicPodioInput<edm4eic::MCRecoClusterParticleAssociation> m_in_clu_assoc {this};
0040
0041 // Declare outputs
0042 PodioOutput<edm4eic::ReconstructedParticle> m_out_reco_particles {this};
0043
0044 // Declare parameters
0045 ParameterRef<double> m_min_energy_over_momentum {this, "minEnergyOverMomentum", config().min_energy_over_momentum};
0046 ParameterRef<double> m_max_energy_over_momentum {this, "maxEnergyOverMomentum", config().max_energy_over_momentum};
0047
0048 // Declare services here, e.g.
0049 // Service<DD4hep_service> m_geoSvc {this};
0050
0051 public:
0052 void Configure() {
0053 // This is called when the factory is instantiated.
0054 // Use this callback to make sure the algorithm is configured.
0055 // The logger, parameters, and services have all been fetched before this is called
0056 m_algo = std::make_unique<eicrecon::ElectronReconstruction>();
0057
0058 // Pass config object to algorithm
0059 m_algo->applyConfig(config());
0060
0061 // If we needed geometry, we'd obtain it like so
0062 // m_algo->init(m_geoSvc().detector(), m_geoSvc().converter(), logger());
0063
0064 m_algo->init(logger());
0065 }
0066
0067 void ChangeRun(int64_t run_number) {
0068 // This is called whenever the run number is changed.
0069 // Use this callback to retrieve state that is keyed off of run number.
0070 // This state should usually be managed by a Service.
0071 // Note: You usually don't need this, because you can declare a Resource instead.
0072 }
0073
0074 void Process(int64_t run_number, uint64_t event_number) {
0075 // This is called on every event.
0076 // Use this callback to call your Algorithm using all inputs and outputs
0077 // The inputs will have already been fetched for you at this point.
0078 auto output = m_algo->execute(
0079 m_in_mc_particles(),
0080 m_in_rc_particles(),
0081 m_in_rc_particles_assoc(),
0082 m_in_clu_assoc()
0083 );
0084
0085 logger()->debug( "Event {}: Found {} reconstructed electron candidates", event_number, output->size() );
0086
0087 m_out_reco_particles() = std::move(output);
0088 // JANA will take care of publishing the outputs for you.
0089 }
0090 };
0091 } // namespace eicrecon
0092
0093 ~~~
0094 Next, we register this with the `reco` plugin in src/global/reco/reco.cc:
0095 ```c++
0096 app->Add(new JOmniFactoryGeneratorT<ReconstructedElectrons_factory>(
0097 "ReconstructedElectrons",
0098 {"MCParticles", "ReconstructedChargedParticles", "ReconstructedChargedParticleAssociations",
0099 "EcalBarrelScFiClusterAssociations",
0100 "EcalEndcapNClusterAssociations",
0101 "EcalEndcapPClusterAssociations",
0102 "EcalEndcapPInsertClusterAssociations",
0103 "EcalLumiSpecClusterAssociations",
0104 },
0105 {"ReconstructedElectrons"},
0106 {}, // Override config values here
0107 app
0108 ));
0109 ```
0110
0111
0112 And finally, we add its output collection name to the output include list in src/services/io/podio/JEventProcessorPODIO:
0113
0114 ```c++
0115 "ReconstructedElectrons",
0116
0117 ```
0118
0119
0120
0121 `src/algorithms/reco/ElectronReconstruction.h`:
0122
0123 ~~~ c++
0124
0125 #pragma once
0126
0127 #include <edm4eic/MCRecoClusterParticleAssociationCollection.h>
0128 #include <edm4eic/MCRecoParticleAssociationCollection.h>
0129 #include <edm4eic/ReconstructedParticleCollection.h>
0130 #include <edm4hep/MCParticleCollection.h>
0131 #include <spdlog/logger.h>
0132 #include <memory>
0133 #include <vector>
0134
0135 #include "ElectronReconstructionConfig.h"
0136 #include "algorithms/interfaces/WithPodConfig.h"
0137
0138 namespace eicrecon {
0139
0140 class ElectronReconstruction : public WithPodConfig<ElectronReconstructionConfig>{
0141 public:
0142
0143 // Initialization will set the pointer of the logger
0144 void init(std::shared_ptr<spdlog::logger> logger);
0145
0146 // Algorithm will apply E/p cut to reconstructed tracks based on truth track-cluster associations
0147 std::unique_ptr<edm4eic::ReconstructedParticleCollection> execute(
0148 const edm4hep::MCParticleCollection *mcparts,
0149 const edm4eic::ReconstructedParticleCollection *rcparts,
0150 const edm4eic::MCRecoParticleAssociationCollection *rcassoc,
0151 const std::vector<const edm4eic::MCRecoClusterParticleAssociationCollection*> &in_clu_assoc
0152 );
0153
0154 // Could overload execute here to, for instance, use track projections
0155 // for track-cluster association (instead of truth information)
0156
0157 private:
0158 std::shared_ptr<spdlog::logger> m_log; // pointer to logger
0159 double m_electron{0.000510998928}; // electron mass
0160 };
0161 } // namespace eicrecon
0162
0163 ~~~
0164
0165
0166
0167 Note that the algorithm's parameters are enclosed in a Config object which lives at `src/algorithms/reco/ElectronReconstructionConfig.h`
0168 and can be accessed via the protected member variable `m_cfg`.
0169
0170 ```c++
0171 struct ElectronReconstructionConfig {
0172
0173 double min_energy_over_momentum = 0.9;
0174 double max_energy_over_momentum = 1.2;
0175
0176 };
0177 ```
0178
0179
0180 The algorithm itself lives at `src/algorithms/reco/ElectronReconstruction.cc`:
0181
0182 ~~~ c++
0183
0184 #include "ElectronReconstruction.h"
0185
0186 #include <edm4eic/ClusterCollection.h>
0187 #include <edm4hep/utils/vector_utils.h>
0188 #include <fmt/core.h>
0189
0190 namespace eicrecon {
0191
0192 void ElectronReconstruction::init(std::shared_ptr<spdlog::logger> logger) {
0193 m_log = logger;
0194 }
0195
0196 std::unique_ptr<edm4eic::ReconstructedParticleCollection> ElectronReconstruction::execute(
0197 const edm4hep::MCParticleCollection *mcparts,
0198 const edm4eic::ReconstructedParticleCollection *rcparts,
0199 const edm4eic::MCRecoParticleAssociationCollection *rcassoc,
0200 const std::vector<const edm4eic::MCRecoClusterParticleAssociationCollection*> &in_clu_assoc
0201 ) {
0202
0203 // Step 1. Loop through MCParticle - cluster associations
0204 // Step 2. Get Reco particle for the Mc Particle matched to cluster
0205 // Step 3. Apply E/p cut using Reco cluster Energy and Reco Particle momentum
0206
0207 // Some obvious improvements:
0208 // - E/p cut from real study optimized for electron finding and hadron rejection
0209 // - use of any HCAL info?
0210 // - check for duplicates?
0211
0212 // output container
0213 auto out_electrons = std::make_unique<edm4eic::ReconstructedParticleCollection>();
0214
0215 for ( const auto *col : in_clu_assoc ){ // loop on cluster association collections
0216 for ( auto clu_assoc : (*col) ){ // loop on MCRecoClusterParticleAssociation in this particular collection
0217 auto sim = clu_assoc.getSim(); // McParticle
0218 auto clu = clu_assoc.getRec(); // RecoCluster
0219
0220 m_log->trace( "SimId={}, CluId={}", clu_assoc.getSimID(), clu_assoc.getRecID() );
0221 m_log->trace( "MCParticle: Energy={} GeV, p={} GeV, E/p = {} for PDG: {}", clu.getEnergy(), edm4hep::utils::magnitude(sim.getMomentum()), clu.getEnergy() / edm4hep::utils::magnitude(sim.getMomentum()), sim.getPDG() );
0222
0223
0224 // Find the Reconstructed particle associated to the MC Particle that is matched with this reco cluster
0225 // i.e. take (MC Particle <-> RC Cluster) + ( MC Particle <-> RC Particle ) = ( RC Particle <-> RC Cluster )
0226 auto reco_part_assoc = rcassoc->begin();
0227 for (; reco_part_assoc != rcassoc->end(); ++reco_part_assoc) {
0228 if (reco_part_assoc->getSimID() == (unsigned) clu_assoc.getSimID()) {
0229 break;
0230 }
0231 }
0232
0233 // if we found a reco particle then test for electron compatibility
0234 if ( reco_part_assoc != rcassoc->end() ){
0235 auto reco_part = reco_part_assoc->getRec();
0236 double EoverP = clu.getEnergy() / edm4hep::utils::magnitude(reco_part.getMomentum());
0237 m_log->trace( "ReconstructedParticle: Energy={} GeV, p={} GeV, E/p = {} for PDG (from truth): {}", clu.getEnergy(), edm4hep::utils::magnitude(reco_part.getMomentum()), EoverP, sim.getPDG() );
0238
0239 // Apply the E/p cut here to select electons
0240 if ( EoverP >= m_cfg.min_energy_over_momentum && EoverP <= m_cfg.max_energy_over_momentum ) {
0241 out_electrons->push_back(reco_part.clone());
0242 }
0243
0244 } else {
0245 m_log->debug( "Could not find reconstructed particle for SimId={}", clu_assoc.getSimID() );
0246 }
0247
0248 } // loop on MC particle to cluster associations in collection
0249 } // loop on collections
0250
0251 m_log->debug( "Found {} electron candidates", out_electrons->size() );
0252 return out_electrons;
0253 }
0254
0255 }
0256
0257 ~~~