Skip to content

Commit f84aa6b

Browse files
committed
This is a combination of 2 commits.
[PWGEM] Add new task for sanity check of EMCal cluster during different stages - Add new task for sanity check of EMCal clusters and the connected MC infoation before skimming, after skimming and after (re)association of the McParticles to EmMcParticles.
1 parent e08d74f commit f84aa6b

2 files changed

Lines changed: 274 additions & 0 deletions

File tree

PWGEM/PhotonMeson/Tasks/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,8 @@ o2physics_add_dpl_workflow(emcal-photon-mc-task
205205
SOURCES emcalPhotonMcTask.cxx
206206
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore
207207
COMPONENT_NAME Analysis)
208+
209+
o2physics_add_dpl_workflow(emcal-mc-sanity-check
210+
SOURCES emcalMcSanityCheck.cxx
211+
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore
212+
COMPONENT_NAME Analysis)
Lines changed: 269 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,269 @@
1+
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2+
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3+
// All rights not expressly granted are reserved.
4+
//
5+
// This software is distributed under the terms of the GNU General Public
6+
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7+
//
8+
// In applying this license CERN does not waive the privileges and immunities
9+
// granted to it by virtue of its status as an Intergovernmental Organization
10+
// or submit itself to any jurisdiction.
11+
12+
/// \file emcalMcSanityCheck.cxx
13+
/// \brief Task to test MC info for EMCal during different stages of out workflow
14+
/// \author M. Hemmer, marvin.hemmer@cern.ch
15+
16+
#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h"
17+
#include "PWGEM/PhotonMeson/DataModel/gammaTables.h"
18+
#include "PWGEM/PhotonMeson/Utils/emcalHistoDefinitions.h"
19+
#include "PWGJE/DataModel/EMCALClusters.h"
20+
21+
#include <Framework/ASoA.h>
22+
#include <Framework/AnalysisDataModel.h>
23+
#include <Framework/AnalysisTask.h>
24+
#include <Framework/Configurable.h>
25+
#include <Framework/HistogramRegistry.h>
26+
#include <Framework/HistogramSpec.h>
27+
#include <Framework/InitContext.h>
28+
#include <Framework/OutputObjHeader.h>
29+
#include <Framework/runDataProcessing.h>
30+
31+
#include <TH1.h>
32+
33+
#include <cmath>
34+
#include <cstddef>
35+
#include <cstdint>
36+
#include <limits>
37+
#include <string>
38+
#include <vector>
39+
40+
using namespace o2;
41+
using namespace o2::aod;
42+
using namespace o2::framework;
43+
using namespace o2::framework::expressions;
44+
using namespace o2::soa;
45+
46+
constexpr float TooLow = 0.9f;
47+
constexpr float TooHigh = 1.1f;
48+
49+
// LSB from packing in MinClusters is 1 MeV
50+
constexpr float EnergyPackingLsb = 1.f / o2::aod::emcdownscaling::downscalingFactors[o2::aod::emcdownscaling::kEnergy];
51+
52+
// Rounding error from packing should be half the LSB so 0.5 MeV
53+
constexpr float EnergyComparisonEpsilon = 0.5f * EnergyPackingLsb;
54+
55+
// uint16_t saturates at 65535 so max value of energy for MinClusters should be 65.535 GeV
56+
constexpr float EnergyPackingSaturationValue = static_cast<float>(std::numeric_limits<uint16_t>::max()) * EnergyPackingLsb;
57+
58+
enum class EnergyComparisonStatus : uint8_t {
59+
Match = 0, // agrees within half a packing LSB
60+
Mismatch = 1, // disagrees beyond epsilon, and not explained by saturation
61+
Saturated = 2 // reference energy exceeded the uint16_t packing range
62+
};
63+
64+
enum class McParticleComparisonStatus : uint8_t {
65+
Match = 0, // same particle species, energy within epsilon, cluster fraction same
66+
DiffSpecies = 1, // not the same species
67+
DiffEnergy = 2, // energy not within epsilon
68+
DiffFraction = 3 // cluster fraction not within epsilon
69+
};
70+
71+
enum class EnergyRatioStatus : uint8_t {
72+
TooLow = 0,
73+
Ok = 1,
74+
TooHigh = 2
75+
};
76+
77+
struct EmcalMcSanityCheck {
78+
79+
using BeforeSkimmerCluster = soa::Join<EMCALClusters, EMCALMCClusters>;
80+
using AfterSkimmerCluster = soa::Join<MinClusters, EMCClusterMCLabels_001>;
81+
using AfterAssociateCluster = soa::Join<MinClusters, EMEMCClusterMCLabels_001>;
82+
using BeforeAfterAssociateCluster = soa::Join<MinClusters, EMCClusterMCLabels_001, EMEMCClusterMCLabels_001>;
83+
84+
HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false};
85+
86+
void init(InitContext&)
87+
{
88+
const AxisSpec axisParticleClusterFracRatioStatus{3, -0.5, 2.5};
89+
90+
auto hParticleClusterFracRatioStatus = registry.add<TH1>("BeforeSkimmer/hParticleClusterFracRatioStatus", "hParticleClusterFracRatioStatus;;counts", HistType::kTH1D, {axisParticleClusterFracRatioStatus});
91+
hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(1, "#it{E}_{part}/(frac#it{E}_{clus}) < 0.9");
92+
hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(2, "0.9 #leq #it{E}_{part}/(frac#it{E}_{clus}) < 1.1");
93+
hParticleClusterFracRatioStatus->GetXaxis()->SetBinLabel(3, "#it{E}_{part}/(frac#it{E}_{clus}) > 1.1");
94+
95+
if (doprocessAfterSkimmer) {
96+
registry.addClone("BeforeSkimmer/", "AfterSkimmer/");
97+
}
98+
99+
if (doprocessAfterAssociation) {
100+
registry.addClone("BeforeSkimmer/", "AfterAssociate/");
101+
}
102+
103+
if (doprocessCompareBeforeAfterAssociate) {
104+
auto hMcParticleStatus = registry.add<TH1>("hMcParticleStatus", "hMcParticleStatus;;counts", HistType::kTH1D, {{4, -0.5, 3.5}});
105+
hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast<int>(McParticleComparisonStatus::Match) + 1, "Match");
106+
hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast<int>(McParticleComparisonStatus::DiffSpecies) + 1, "DiffSpecies");
107+
hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast<int>(McParticleComparisonStatus::DiffEnergy) + 1, "DiffEnergy");
108+
hMcParticleStatus->GetXaxis()->SetBinLabel(static_cast<int>(McParticleComparisonStatus::DiffFraction) + 1, "DiffFraction");
109+
}
110+
}; // end init
111+
112+
EnergyRatioStatus getEnergyRatioStatus(float clusterE, float mcParticleE, float frac)
113+
{
114+
float ratio = mcParticleE / (clusterE * frac);
115+
if (TooLow <= ratio && ratio < TooHigh) {
116+
return EnergyRatioStatus::Ok;
117+
}
118+
if (ratio < TooLow) {
119+
return EnergyRatioStatus::TooLow;
120+
}
121+
// everything else now has to be TooHigh
122+
return EnergyRatioStatus::TooHigh;
123+
}
124+
125+
// referenceEnergy: full-precision energy (e.g. BeforeSkimmer's cluster.energy()).
126+
// compressedEnergy: energy unpacked from a uint16_t-packed table (e.g. MinClusters' cluster.e()).
127+
EnergyComparisonStatus compareClusterEnergy(float referenceEnergy, float compressedEnergy)
128+
{
129+
if (referenceEnergy > EnergyPackingSaturationValue) {
130+
return EnergyComparisonStatus::Saturated;
131+
}
132+
return std::abs(referenceEnergy - compressedEnergy) <= EnergyComparisonEpsilon
133+
? EnergyComparisonStatus::Match
134+
: EnergyComparisonStatus::Mismatch;
135+
}
136+
137+
void processBeforeSkimmer(BeforeSkimmerCluster const& clusters, McParticles_001 const& mcParticles)
138+
{
139+
if (clusters.size() == 0 || mcParticles.size() == 0) {
140+
return;
141+
}
142+
auto mcParticle = mcParticles.begin();
143+
144+
for (const auto& cluster : clusters) {
145+
if (!cluster.has_mcParticle()) {
146+
continue;
147+
}
148+
const auto& ids = cluster.mcParticleIds();
149+
const auto& fracs = cluster.amplitudeA();
150+
for (std::size_t i = 0; i < ids.size(); ++i) {
151+
const auto& id = ids[i];
152+
const auto& frac = fracs[i];
153+
mcParticle.setCursor(id);
154+
registry.fill(HIST("BeforeSkimmer/hParticleClusterFracRatioStatus"), static_cast<int>(getEnergyRatioStatus(cluster.energy(), mcParticle.e(), frac)));
155+
}
156+
}
157+
}
158+
PROCESS_SWITCH(EmcalMcSanityCheck, processBeforeSkimmer, "Process EMCal cluster information before the skimmerGammaCalo", true);
159+
160+
void processAfterSkimmer(AfterSkimmerCluster const& clusters, McParticles_001 const& mcParticles)
161+
{
162+
if (clusters.size() == 0 || mcParticles.size() == 0) {
163+
return;
164+
}
165+
auto mcParticle = mcParticles.begin();
166+
167+
for (const auto& cluster : clusters) {
168+
if (!cluster.has_mcParticle()) {
169+
continue;
170+
}
171+
const auto& ids = cluster.mcParticleIds();
172+
const auto& fracs = cluster.amplitude();
173+
for (std::size_t i = 0; i < ids.size(); ++i) {
174+
const auto& id = ids[i];
175+
const auto& frac = fracs[i];
176+
mcParticle.setCursor(id);
177+
registry.fill(HIST("AfterSkimmer/hParticleClusterFracRatioStatus"), static_cast<int>(getEnergyRatioStatus(cluster.e(), mcParticle.e(), frac)));
178+
}
179+
}
180+
}
181+
PROCESS_SWITCH(EmcalMcSanityCheck, processAfterSkimmer, "Process EMCal cluster information after the skimmerGammaCalo", true);
182+
183+
void processAfterAssociation(AfterAssociateCluster const& clusters, EMMCParticles_001 const& mcParticles)
184+
{
185+
if (clusters.size() == 0 || mcParticles.size() == 0) {
186+
return;
187+
}
188+
auto mcParticle = mcParticles.begin();
189+
190+
for (const auto& cluster : clusters) {
191+
if (!cluster.has_emmcparticle()) {
192+
continue;
193+
}
194+
const auto& ids = cluster.emmcparticleIds();
195+
const auto& fracs = cluster.amplitude();
196+
for (std::size_t i = 0; i < ids.size(); ++i) {
197+
const auto& id = ids[i];
198+
const auto& frac = fracs[i];
199+
mcParticle.setCursor(id);
200+
registry.fill(HIST("AfterAssociate/hParticleClusterFracRatioStatus"), static_cast<int>(getEnergyRatioStatus(cluster.e(), mcParticle.e(), frac)));
201+
}
202+
}
203+
}
204+
PROCESS_SWITCH(EmcalMcSanityCheck, processAfterAssociation, "Process EMCal cluster information before the associateMCinfoPhoton", false);
205+
206+
void processCompareBeforeAfterAssociate(MinClusters const& clusters,
207+
EMCClusterMCLabels_001 const& beforeLabels,
208+
EMEMCClusterMCLabels_001 const& afterLabels,
209+
McParticles_001 const& mcParticles,
210+
EMMCParticles_001 const& emMcParticles)
211+
{
212+
if (clusters.size() == 0 || mcParticles.size() == 0 || emMcParticles.size() == 0) {
213+
return;
214+
}
215+
216+
auto mcParticle = mcParticles.begin();
217+
auto emMcParticle = emMcParticles.begin();
218+
219+
for (int64_t i = 0; i < clusters.size(); ++i) {
220+
auto before = beforeLabels.iteratorAt(i);
221+
auto after = afterLabels.iteratorAt(i);
222+
223+
if (!after.has_emmcparticle() || !before.has_mcParticle()) {
224+
continue;
225+
}
226+
227+
if (after.emmcparticleIds().size() != before.mcParticleIds().size()) {
228+
LOG(fatal) << "Number of EmMcParticles and McParticles does not match (" << after.emmcparticleIds().size() << " vs " << before.mcParticleIds().size() << ")";
229+
}
230+
for (size_t id = 0; id < after.emmcparticleIds().size(); ++id) {
231+
bool isSameSpecies = true;
232+
bool isSameEnergy = true;
233+
bool hasSameFraction = true;
234+
235+
mcParticle.setCursor(before.mcParticleIds()[id]);
236+
emMcParticle.setCursor(after.emmcparticleIds()[id]);
237+
238+
if (mcParticle.pdgCode() != emMcParticle.pdgCode()) {
239+
isSameSpecies = false;
240+
}
241+
if (mcParticle.e() != emMcParticle.e()) {
242+
isSameEnergy = false;
243+
}
244+
if (before.amplitude()[id] != after.amplitude()[id]) {
245+
hasSameFraction = false;
246+
}
247+
248+
if (isSameSpecies && isSameEnergy && hasSameFraction) {
249+
registry.fill(HIST("hMcParticleStatus"), 0);
250+
}
251+
if (!isSameSpecies) {
252+
registry.fill(HIST("hMcParticleStatus"), 1);
253+
}
254+
if (!isSameEnergy) {
255+
registry.fill(HIST("hMcParticleStatus"), 2);
256+
}
257+
if (!hasSameFraction) {
258+
registry.fill(HIST("hMcParticleStatus"), 3);
259+
}
260+
}
261+
}
262+
}
263+
PROCESS_SWITCH(EmcalMcSanityCheck, processCompareBeforeAfterAssociate, "Process and compare EMCal cluster and McParticle information before and after the associateMCinfoPhoton", false);
264+
}; // End struct EmcalMcSanityCheck
265+
266+
WorkflowSpec defineDataProcessing(ConfigContext const& context)
267+
{
268+
return WorkflowSpec{adaptAnalysisTask<EmcalMcSanityCheck>(context)};
269+
}

0 commit comments

Comments
 (0)