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