66AnalysisEpicPodio::AnalysisEpicPodio (TString infileName_, TString outfilePrefix_)
77  : Analysis(infileName_, outfilePrefix_)
88  , crossCheckKinematics(false )
9- {}; 
9+ {}
1010
11- AnalysisEpicPodio::~AnalysisEpicPodio () {}; 
11+ AnalysisEpicPodio::~AnalysisEpicPodio () {}
1212
1313void  AnalysisEpicPodio::Execute ()
1414{
@@ -21,10 +21,9 @@ void AnalysisEpicPodio::Execute()
2121    for (const  auto  fileName : fileList)
2222      infilesFlat.push_back (fileName);
2323
24-   //  create PODIO event store 
24+   //  create PODIO reader 
2525  podioReader.openFiles (infilesFlat);
26-   evStore.setReader (&podioReader);
27-   ENT = podioReader.getEntries ();
26+   ENT = podioReader.getEntries (inputTreeName);
2827  if (maxEvents>0 ) ENT = std::min (maxEvents,ENT);
2928
3029  //  calculate Q2 weights
@@ -59,11 +58,7 @@ void AnalysisEpicPodio::Execute()
5958    if (verbose) fmt::print (" \n\n {:=<70}\n "  ,fmt::format (" EVENT {} "  ,e));
6059
6160    //  next event
62-     //  FIXME: check that we analyze ALL of the events: do we miss the first or last one?
63-     if (e>0 ) {
64-       evStore.clear ();
65-       podioReader.endOfEvent ();
66-     }
61+     auto  podioFrame = podio::Frame (podioReader.readNextEntry (inputTreeName));
6762
6863    //  resets
6964    kin->ResetHFS ();
@@ -76,9 +71,10 @@ void AnalysisEpicPodio::Execute()
7671    int  num_rec_electrons    = 0 ;
7772
7873    //  read particle collections for this event
79-     const  auto & simParts    = evStore.get <edm4hep::MCParticleCollection>(" MCParticles"  );
80-     const  auto & recParts    = evStore.get <edm4eic::ReconstructedParticleCollection>(" ReconstructedParticles"  );
81-     const  auto & mcRecAssocs = evStore.get <edm4eic::MCRecoParticleAssociationCollection>(" ReconstructedParticlesAssoc"  );
74+     //  FIXME: which collections to read: "ReconstructedParticles*" or "ReconstructedChargedParticles*"?
75+     const  auto & simParts    = podioFrame.get <edm4hep::MCParticleCollection>(" MCParticles"  );
76+     const  auto & recParts    = podioFrame.get <edm4eic::ReconstructedParticleCollection>(" ReconstructedChargedParticles"  );
77+     const  auto & mcRecAssocs = podioFrame.get <edm4eic::MCRecoParticleAssociationCollection>(" ReconstructedChargedParticlesAssociations"  );
8278
8379    //  data objects
8480    edm4hep::MCParticle mcPartEleBeam;
@@ -87,7 +83,7 @@ void AnalysisEpicPodio::Execute()
8783
8884    //  loop over generated particles
8985    if (verbose) fmt::print (" \n {:-<60}\n "  ," MCParticles "  );
90-     for (auto  simPart : simParts) {
86+     for (const   auto &  simPart : simParts) {
9187
9288      //  print out this MCParticle
9389      //  if(verbose) PrintParticle(simPart);
@@ -131,10 +127,10 @@ void AnalysisEpicPodio::Execute()
131127    } //  end loop over generated particles
132128
133129    //  check for found generated particles
134-     if (num_ele_beams==0 )     { ErrorPrint (" WARNING: missing MC electron beam"  );      continue ; }; 
135-     if (num_ion_beams==0 )     { ErrorPrint (" WARNING: missing MC ion beam"  );           continue ; }; 
136-     if (num_sim_electrons==0 ) { ErrorPrint (" WARNING: missing scattered electron"  );    continue ; }; 
137-     if (double_counted_beam)  { ErrorPrint (" WARNING: found multiple beam particles"  ); continue ; }; 
130+     if (num_ele_beams==0 )     { ErrorPrint (" WARNING: missing MC electron beam"  );      continue ; }
131+     if (num_ion_beams==0 )     { ErrorPrint (" WARNING: missing MC ion beam"  );           continue ; }
132+     if (num_sim_electrons==0 ) { ErrorPrint (" WARNING: missing scattered electron"  );    continue ; }
133+     if (double_counted_beam)  { ErrorPrint (" WARNING: found multiple beam particles"  ); continue ; }
138134
139135    //  set Kinematics 4-momenta
140136    kinTrue->vecEleBeam   = GetP4 (mcPartEleBeam);
@@ -157,67 +153,61 @@ void AnalysisEpicPodio::Execute()
157153     * - first define a first-order function (`payload`), then call `LoopMCRecoAssocs` 
158154     * - see `LoopMCRecoAssocs` for `payload` signature 
159155     */  
160-     auto  AllRecPartsToHFS = [&] (auto & simPart, auto & recPart, auto  recPDG) {
156+     auto  AllRecPartsToHFS = [&] (const   auto & simPart,  const  auto & recPart, auto  recPDG) {
161157      kin->AddToHFS (GetP4 (recPart));
162158    };
163159    if (verbose) fmt::print (" \n {:-<60}\n "  ," MC<->Reco ASSOCIATIONS "  );
164-     LoopMCRecoAssocs (mcRecAssocs, AllRecPartsToHFS, verbose);
160+     LoopMCRecoAssocs (recParts,  mcRecAssocs, AllRecPartsToHFS, verbose);
165161
166162    //  find reconstructed electron
167163    //  ============================================================================
168164    /*  FIXME: need realistic electron finder; all of the following options rely
169-      * on MC-truth matching; is there any common upstream realistic electron finder 
165+      * on MC-truth matching; is there any common upstream realistic electron finder?  
170166     */  
171167
172168    //  find scattered electron by simply matching to truth
173-     //  FIXME: not working, until we have truth matching and/or reconstructed PID
174-     //  FIXME: does `simPart==mcPartElectron` work as expected?
175-     /* 
176-     auto FindRecoEleByTruth = [&] (auto& simPart, auto& recPart, auto recPDG) { 
177-       if(recPDG==constants::pdgElectron && simPart==mcPartElectron) { 
169+     auto  FindRecoEleByTruth = [&] (const  auto & simPart, const  auto & recPart, auto  recPDG) {
170+       if (recPDG==constants::pdgElectron && simPart.id ()==mcPartElectron.id ()) {
178171        num_rec_electrons++;
179172        kin->vecElectron  = GetP4 (recPart);
180-       };  
173+       }
181174    };
182-     LoopMCRecoAssocs(mcRecAssocs, FindRecoEleByTruth); 
183-     */  
184- 
185-     //  use electron finder from upstream algorithm `InclusiveKinematics*`
186-     //  FIXME: is the correct upstream electron finder used here? The
187-     //  `InclusiveKinematics*` recon algorithms seem to rely on
188-     //  `Jug::Base::Beam::find_first_scattered_electron(mcParts)` and matching
189-     //  to truth; this guarantees we get the correct reconstructed scattered
190-     //  electron
191-     const  auto & disCalcs = evStore.get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematicsElectron"  );
192-     if (disCalcs.size () != 1 ) ErrorPrint (fmt::format (" WARNING: disCalcs.size = {} != 1 for this event"  ,disCalcs.size ()));
193-     for (const  auto & calc : disCalcs) {
194-       auto  ele = calc.getScat ();
195-       if ( ! ele.isAvailable ()) {
196-         ErrorPrint (" WARNING: `disCalcs` scattered electron unavailable"  );
197-         continue ;
175+     LoopMCRecoAssocs (recParts, mcRecAssocs, FindRecoEleByTruth);
176+ 
177+     //  Fallback: use electron finder from upstream algorithm `InclusiveKinematics*`
178+     if (num_rec_electrons == 0 ) {
179+       ErrorPrint (" WARNING: first attempt to find reconstructed electron failed; trying InclusiveKinematics collection"  );
180+       const  auto & disCalcs = podioFrame.get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematicsElectron"  );
181+       if (disCalcs.size () != 1 ) ErrorPrint (fmt::format (" WARNING: disCalcs.size = {} != 1 for this event"  ,disCalcs.size ()));
182+       for (const  auto & calc : disCalcs) {
183+         auto  ele = calc.getScat ();
184+         if ( ! ele.isAvailable ()) {
185+           ErrorPrint (" WARNING: `disCalcs` scattered electron unavailable"  );
186+           continue ;
187+         }
188+         num_rec_electrons++;
189+         kin->vecElectron  = GetP4 (ele);
198190      }
199-       num_rec_electrons++;
200-       kin->vecElectron  = GetP4 (ele);
201191    }
202192
203193    //  check for found reconstructed scattered electron
204-     if (num_rec_electrons == 0 ) { ErrorPrint (" WARNING: reconstructed scattered electron not found"  ); continue ; }; 
205-     if (num_rec_electrons >  1 ) { ErrorPrint (" WARNING: found more than 1 reconstructed scattered electron"  ); }; 
194+     if (num_rec_electrons == 0 ) { ErrorPrint (" WARNING: reconstructed scattered electron not found"  ); continue ; }
195+     if (num_rec_electrons >  1 ) { ErrorPrint (" WARNING: found more than 1 reconstructed scattered electron"  ); }
206196
207197    //  subtract electron from hadronic final state variables
208198    kin->SubtractElectronFromHFS ();
209199    kinTrue->SubtractElectronFromHFS ();
210200
211201    //  skip the event if there are no reconstructed particles (other than the
212202    //  electron), otherwise hadronic recon methods will fail
213-     if (kin->countHadrons  == 0 ) { ErrorPrint (" WARNING: no hadrons"  ); }; 
203+     if (kin->countHadrons  == 0 ) { ErrorPrint (" WARNING: no hadrons"  ); }
214204
215205    //  calculate DIS kinematics; skip the event if the calculation did not go well
216206    if ( ! kin->CalculateDIS (reconMethod)     ) continue ; //  reconstructed
217207    if ( ! kinTrue->CalculateDIS (reconMethod) ) continue ; //  generated (truth)
218208
219209    //  Get the weight for this event's Q2
220-     //    FIXME: we are in a podio::EventStore  event loop, thus we need an
210+     //    FIXME: we are in a PODIO reader  event loop, thus we need an
221211    //           alternative to `chain->GetTreeNumber()`; currently disabling weighting
222212    //           for now, by setting `wTrack=1.0`
223213    //  auto Q2weightFactor = GetEventQ2Weight(kinTrue->Q2, inLookup[chain->GetTreeNumber()]);
@@ -235,7 +225,7 @@ void AnalysisEpicPodio::Execute()
235225    /*  - calculate SIDIS kinematics
236226     * - fill output data structures 
237227     */  
238-     auto  SidisOutput = [&] (auto & simPart, auto & recPart, auto  recPDG) {
228+     auto  SidisOutput = [&] (const   auto & simPart,  const  auto & recPart, auto  recPDG) {
239229
240230      //  final state cut
241231      //  - check PID, to see if it's a final state we're interested in
@@ -262,7 +252,7 @@ void AnalysisEpicPodio::Execute()
262252      //  - `IsActiveEvent()` is only true if at least one bin gets filled for this track
263253      if ( writeSidisTree && HD->IsActiveEvent () ) ST->FillTree (wTrack);
264254    };
265-     LoopMCRecoAssocs (mcRecAssocs, SidisOutput);
255+     LoopMCRecoAssocs (recParts,  mcRecAssocs, SidisOutput);
266256
267257
268258    //  read kinematics calculations from upstream /////////////////////////
@@ -278,7 +268,7 @@ void AnalysisEpicPodio::Execute()
278268      fmt::print (" \n {:-<75}\n "  ," KINEMATICS, calculated from upstream: "  );
279269      PrintRow (" "  , std::vector<std::string>({ " x"  , " Q2"  , " W"  , " y"  , " nu"   }), true );
280270      for (const  auto  upstreamReconMethod : upstreamReconMethodList)
281-         for (const  auto & calc : evStore .get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematics"  +upstreamReconMethod) )
271+         for (const  auto & calc : podioFrame .get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematics"  +upstreamReconMethod) )
282272          PrintRow ( upstreamReconMethod, std::vector<float >({
283273              calc.getX (),
284274              calc.getQ2 (),
@@ -303,7 +293,7 @@ void AnalysisEpicPodio::Execute()
303293      if (associatedUpstreamMethod != " NONE"  ) {
304294        fmt::print (" {:-<75}\n "  ,fmt::format (" DIFFERENCE: upstream({}) - local({}): "  ,associatedUpstreamMethod,reconMethod));
305295        for (const  auto  upstreamMethod : std::vector<std::string>({" Truth"  ,associatedUpstreamMethod})) {
306-           const  auto & upstreamCalcs = evStore .get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematics"  +upstreamMethod);
296+           const  auto & upstreamCalcs = podioFrame .get <edm4eic::InclusiveKinematicsCollection>(" InclusiveKinematics"  +upstreamMethod);
307297          for (const  auto & upstreamCalc : upstreamCalcs) {
308298            auto  K    = upstreamMethod==" Truth"   ? kinTrue : kin;
309299            auto  name = upstreamMethod==" Truth"   ? " Truth"   : " Reconstructed"  ;
@@ -324,9 +314,6 @@ void AnalysisEpicPodio::Execute()
324314  fmt::print (" end event loop\n "  );
325315
326316  //  finish execution
327-   evStore.clear ();
328-   podioReader.endOfEvent ();
329-   podioReader.closeFile ();
330317  Finish ();
331318}
332319
@@ -398,69 +385,111 @@ void AnalysisEpicPodio::PrintParticle(const edm4eic::ReconstructedParticle& P) {
398385//     payload signature: (simPart, recPart, reconstructed PDG) 
399386 */  
400387void  AnalysisEpicPodio::LoopMCRecoAssocs (
388+     const  edm4eic::ReconstructedParticleCollection&     recParts,
401389    const  edm4eic::MCRecoParticleAssociationCollection& mcRecAssocs,
402390    std::function<void (const  edm4hep::MCParticle&, const  edm4eic::ReconstructedParticle&, int )> payload,
403391    bool printParticles
404392    )
405393{
406-   for (const  auto & assoc : mcRecAssocs ) {
407- 
408-     //  get reconstructed and simulated particles, and check for matching
409-     auto  recPart = assoc.getRec (); //  reconstructed particle
410-     auto  simPart = assoc.getSim (); //  simulated (truth) particle
411-     //  if(!simPart.isAvailable()) continue; // FIXME: consider using this once we have matching
412- 
413-     //  print out this reconstructed particle, and its matching truth 
414-     if (printParticles) {
415-       fmt::print (" \n    {:->35}\n "  ,"  reconstructed particle:"  );
416-       PrintParticle (recPart);
417-       fmt::print (" \n    {:.>35}\n "  ,"  truth match:"  );
418-       if (simPart.isAvailable ())
419-         PrintParticle (simPart);
420-       else 
421-         fmt::print ("      {:>35}\n "  ," NO MATCH"  );
422-       fmt::print (" \n "  );
423-     }
424394
425-     //  get reconstructed PDG from PID
426-     bool  usedTruthPID = false ;
427-     auto  recPDG = GetReconstructedPDG (simPart, recPart, usedTruthPID);
428-     if (verbose) fmt::print ("    GetReconstructedPDG = {}\n "  ,recPDG);
429-     //  if(usedTruthPID) continue; // FIXME: consider using this once we have decent PID
395+   //  check collection sizes equality
396+   /*  
397+    * NOTE: since it's possible that `recParts.size() != `mcRecAssocs.size()`, 
398+    *       we first loop over `recParts`, and find the association in `mcRecAssocs` 
399+    *       which contains it; this allows us to handle the case when we have a 
400+    *       reconstructed particle with no associated MC truth particle 
401+    * 
402+    * FIXME: for now we just warn about these unequal-size cases; we should think of a way to 
403+    *        properly handle them 
404+    */  
405+   if (recParts.size () > mcRecAssocs.size ())
406+     ErrorPrint (" WARNING: there are more reconstructed particles than MC-Reco associations"  );
407+   else  if (recParts.size () < mcRecAssocs.size ())
408+     ErrorPrint (" ERROR: there are more MC-Reco associations than reconstructed particles; this could be an upstream issue"  );
409+ 
410+   //  loop over reconstructed particles
411+   for (const  auto & recPart : recParts) {
412+ 
413+     //  find the associated MC particle
414+     bool  assocFound = false ;
415+     for (const  auto & assoc : mcRecAssocs ) {
416+       if (recPart.id () == assoc.getRec ().id ()) {
417+         assocFound = true ;
418+ 
419+         //  get MC truth particle
420+         const  auto & simPart = assoc.getSim ();
421+ 
422+         //  print out this reconstructed particle, and its matching truth 
423+         if (printParticles) {
424+           fmt::print (" \n    {:->35}\n "  ,"  reconstructed particle:"  );
425+           PrintParticle (recPart);
426+           fmt::print (" \n    {:.>35}\n "  ,"  truth match:"  );
427+           if (simPart.isAvailable ())
428+             PrintParticle (simPart);
429+           else 
430+             fmt::print ("      {:>35}\n "  ," NO MATCH"  );
431+           fmt::print (" \n "  );
432+         }
433+ 
434+         //  handle case of missing MC truth particle
435+         //  FIXME: handle case this better, instead of just ignoring it
436+         if (!simPart.isAvailable ()) {
437+           ErrorPrint (" WARNING: found reconstructed particle with MC-Reco association, but no simulated particle is linked"  );
438+           break ;
439+         }
440+ 
441+         //  get reconstructed PDG from PID
442+         bool  usedTruthPID = false ;
443+         auto  recPDG = GetPDG (simPart, recPart, usedTruthPID);
444+         if (verbose) fmt::print ("    GetPDG = {}\n "  ,recPDG);
445+         //  if(usedTruthPID) continue; // FIXME: consider using this once we have decent PID
430446
431-     //  run payload
432-     payload (simPart, recPart, recPDG);
447+         //  run payload
448+         payload (simPart, recPart, recPDG);
449+ 
450+       }
451+     } //  end loop over `mcRecAssocs`
452+ 
453+     //  handle reconstructed particles with no MC association
454+     //  FIXME: what should we do about these?
455+     if (!assocFound) {
456+       ErrorPrint (" WARNING: found reconstructed particle with no associated MC particle"  );
457+     }
458+ 
459+   } //  end loop over `recParts`
433460
434-   } //  end loop over Reco<->MC associations
435461} //  end LoopMCRecoAssocs
436462
437463
438464//  get PDG from reconstructed particle; resort to true PDG, if
439465//  PID is unavailable (sets `usedTruth` to true)
440- int  AnalysisEpicPodio::GetReconstructedPDG  (
466+ int  AnalysisEpicPodio::GetPDG  (
441467    const  edm4hep::MCParticle& simPart,
442468    const  edm4eic::ReconstructedParticle& recPart,
443469    bool & usedTruth
444470    )
445471{
446-   int  pdg = 0 ;
447-   usedTruth = false ;
448- 
449-   //  if using edm4hep::ReconstructedParticle:
450-   /* 
451-   if(recPart.getParticleIDUsed().isAvailable()) // FIXME: not available 
452-     pdg = recPart.getParticleIDUsed().getPDG(); 
453-   */  
454-   
455-   //  if using edm4eic::ReconstructedParticle:
456-   //  pdg = recPart.getPDG(); // FIXME: not available either
457472
458-   //  if reconstructed PID is unavailable, use MC PDG
459-   if (pdg==0 ) {
460-     usedTruth = true ;
461-     if (simPart.isAvailable ())
462-       pdg = simPart.getPDG ();
463-   }
473+   //  get PID PDG
474+   usedTruth        = false ;
475+   auto  pidHypBest  = recPart.getParticleIDUsed (); //  presumably the best PID hypothesis
476+   auto  pidGoodness = recPart.getGoodnessOfPID (); //  FIXME: should we use this?
477+   if (pidHypBest.isAvailable ())
478+     return  pidHypBest.getPDG ();
479+ 
480+   //  FIXME: should we check other PID hypotheses?
481+   //  for(auto pidHyp : recPart.getParticleIDs()) {
482+   //    auto pdg        = pidHyp.getPDG();
483+   //    auto likelihood = pidHyp.getLikelihood();
484+   //    // ...
485+   //  }
486+ 
487+   //  otherwise get true PDG from associated MC particle
488+   usedTruth = true ;
489+   if (simPart.isAvailable ())
490+     return  simPart.getPDG ();
464491
465-   return  pdg;
492+   //  last chance // FIXME: someday we may remove `ReconstructedParticle::PDG` from EDM4eic
493+   ErrorPrint (" WARNING: falling back to `ReconstructedParticle::PDG` member, which may be deprecated soon"  );
494+   return  recPart.getPDG ();
466495}
0 commit comments