Back to home page

EIC code displayed by LXR

 
 

    


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 ~~~