Skip to content

Commit 9feb02a

Browse files
committed
refactor: do not assume num. rec. parts == num. assocs.
1 parent 10d174b commit 9feb02a

File tree

2 files changed

+135
-105
lines changed

2 files changed

+135
-105
lines changed

src/AnalysisEpicPodio.cxx

Lines changed: 130 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@
66
AnalysisEpicPodio::AnalysisEpicPodio(TString infileName_, TString outfilePrefix_)
77
: Analysis(infileName_, outfilePrefix_)
88
, crossCheckKinematics(false)
9-
{};
9+
{}
1010

11-
AnalysisEpicPodio::~AnalysisEpicPodio() {};
11+
AnalysisEpicPodio::~AnalysisEpicPodio() {}
1212

1313
void 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
*/
400387
void 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

Comments
 (0)