Skip to content

Commit e4e68aa

Browse files
authored
[PWGEM,Photon] Add new emcal mc task (#17823)
1 parent 1b8c02e commit e4e68aa

3 files changed

Lines changed: 380 additions & 25 deletions

File tree

PWGEM/PhotonMeson/Tasks/CMakeLists.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,3 +185,8 @@ o2physics_add_dpl_workflow(emcal-mc-sanity-check
185185
SOURCES emcalMcSanityCheck.cxx
186186
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore
187187
COMPONENT_NAME Analysis)
188+
189+
o2physics_add_dpl_workflow(emcal-mc-task
190+
SOURCES emcalMcTask.cxx
191+
PUBLIC_LINK_LIBRARIES O2::Framework O2Physics::AnalysisCore O2Physics::PWGEMPhotonMesonCore
192+
COMPONENT_NAME Analysis)
Lines changed: 371 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,371 @@
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 emcalMcTask.cxx
13+
/// \brief Analysis task for to obtain cluster properties from MC identified particles like photons and electrons
14+
/// \author M. Hemmer, marvin.hemmer@cern.ch
15+
16+
#include "PWGEM/PhotonMeson/Core/EMBitFlags.h"
17+
#include "PWGEM/PhotonMeson/Core/EMCPhotonCut.h"
18+
#include "PWGEM/PhotonMeson/Core/EMPhotonEventCut.h"
19+
#include "PWGEM/PhotonMeson/DataModel/EventTables.h"
20+
#include "PWGEM/PhotonMeson/DataModel/GammaTablesRedux.h"
21+
#include "PWGEM/PhotonMeson/Utils/EventHistograms.h"
22+
23+
#include <CommonConstants/PhysicsConstants.h>
24+
#include <Framework/AnalysisDataModel.h>
25+
#include <Framework/AnalysisTask.h>
26+
#include <Framework/Concepts.h>
27+
#include <Framework/Configurable.h>
28+
#include <Framework/HistogramRegistry.h>
29+
#include <Framework/HistogramSpec.h>
30+
#include <Framework/InitContext.h>
31+
#include <Framework/OutputObjHeader.h>
32+
#include <Framework/SliceCache.h>
33+
#include <Framework/runDataProcessing.h>
34+
35+
#include <TPDGCode.h>
36+
37+
#include <array>
38+
#include <cmath>
39+
#include <cstddef>
40+
#include <string>
41+
#include <string_view>
42+
#include <vector>
43+
44+
using namespace o2;
45+
using namespace o2::aod;
46+
using namespace o2::framework;
47+
using namespace o2::framework::expressions;
48+
using namespace o2::soa;
49+
using namespace o2::aod::pwgem::photon;
50+
51+
namespace o2::em::emcal::mc
52+
{
53+
54+
enum ParticleType : int {
55+
kPhoton = 0,
56+
kElectron,
57+
kPositron,
58+
kPi0,
59+
kEta,
60+
kOmega,
61+
kPion,
62+
kKaon,
63+
kOther,
64+
kNParticleTypes
65+
};
66+
} // namespace o2::em::emcal::mc
67+
68+
enum CentralityEstimator {
69+
None = 0,
70+
CFT0A = 1,
71+
CFT0C = 2,
72+
CFT0M = 3,
73+
NCentralityEstimators = 4
74+
};
75+
76+
enum class MapLevel {
77+
kGood = 1,
78+
kNoBad = 2,
79+
kInEMC = 3,
80+
kAll = 4
81+
};
82+
83+
struct EmcalMcTask {
84+
// configurable axis
85+
ConfigurableAxis thnConfigAxisE{"thnConfigAxisE", {400, 0., 20.}, "energy axis"};
86+
ConfigurableAxis thnConfigAxisEtaDiff{"thnConfigAxisEtaDiff", {300, -1., 2.}, "(eta rec - eta true)"};
87+
ConfigurableAxis thnConfigAxisPhiDiff{"thnConfigAxisPhiDiff", {300, -1., 2.}, "(phi rec - phi true"};
88+
ConfigurableAxis thnConfigAxisM02{"thnConfigAxisM02", {100, 0.0, 1.0}, "m02 mass axis"};
89+
ConfigurableAxis thnConfigAxisCent{"thnConfigAxisCent", {20, 0., 100.}, "centrality axis for the current event"};
90+
ConfigurableAxis thnConfigAxisMult{"thnConfigAxisMult", {60, 0., 60000.}, "multiplicity axis for the current event"};
91+
Configurable<bool> useCent{"useCent", false, "flag to enable usage of centrality instead of multiplicity as axis."};
92+
93+
EMPhotonEventCut fEMEventCut;
94+
struct : ConfigurableGroup {
95+
std::string prefix = "eventcuts";
96+
Configurable<float> cfgZvtxMax{"cfgZvtxMax", 10.f, "max. Zvtx"};
97+
Configurable<bool> cfgRequireSel8{"cfgRequireSel8", true, "require sel8 in event cut"};
98+
Configurable<bool> cfgRequireFT0AND{"cfgRequireFT0AND", true, "require FT0AND in event cut"};
99+
Configurable<bool> cfgRequireNoTFB{"cfgRequireNoTFB", false, "require No time frame border in event cut"};
100+
Configurable<bool> cfgRequireNoITSROFB{"cfgRequireNoITSROFB", false, "require no ITS readout frame border in event cut"};
101+
Configurable<bool> cfgRequireNoSameBunchPileup{"cfgRequireNoSameBunchPileup", false, "require no same bunch pileup in event cut"};
102+
Configurable<bool> cfgRequireVertexITSTPC{"cfgRequireVertexITSTPC", false, "require Vertex ITSTPC in event cut"}; // ITS-TPC matched track contributes PV.
103+
Configurable<bool> cfgRequireGoodZvtxFT0vsPV{"cfgRequireGoodZvtxFT0vsPV", false, "require good Zvtx between FT0 vs. PV in event cut"};
104+
Configurable<bool> cfgRequireEMCReadoutInMB{"cfgRequireEMCReadoutInMB", true, "require the EMC to be read out in an MB collision (kTVXinEMC)"};
105+
Configurable<bool> cfgRequireEMCHardwareTriggered{"cfgRequireEMCHardwareTriggered", false, "require the EMC to be hardware triggered (kEMC7 or kDMC7)"};
106+
Configurable<float> cfgFT0COccupancyMin{"cfgFT0COccupancyMin", -1, "min. FT0C occupancy"};
107+
Configurable<float> cfgFT0COccupancyMax{"cfgFT0COccupancyMax", 1000000000, "max. FT0C occupancy"};
108+
Configurable<float> cfgMinCent{"cfgMinCent", 0, "min. centrality (%)"};
109+
Configurable<float> cfgMaxCent{"cfgMaxCent", 90, "max. centrality (%)"};
110+
Configurable<int> centEstimator{"centEstimator", 2, "Centrality estimation (FT0A: 1, FT0C: 2, FT0M: 3)"};
111+
} eventcuts;
112+
113+
EMCPhotonCut fEMCCut;
114+
struct : ConfigurableGroup {
115+
std::string prefix = "emccuts";
116+
Configurable<std::string> clusterDefinition{"clusterDefinition", "kV3MostSplitSmallestTimeDiff", "Clusterizer to be selected, e.g. V3Default"};
117+
Configurable<float> cfgEMCminTime{"cfgEMCminTime", -25., "Minimum cluster time for EMCal time cut"};
118+
Configurable<float> cfgEMCmaxTime{"cfgEMCmaxTime", +30., "Maximum cluster time for EMCal time cut"};
119+
Configurable<float> cfgEMCminM02{"cfgEMCminM02", 0.1, "Minimum M02 for EMCal M02 cut"};
120+
Configurable<float> cfgEMCmaxM02{"cfgEMCmaxM02", 0.7, "Maximum M02 for EMCal M02 cut"};
121+
Configurable<float> cfgEMCminE{"cfgEMCminE", 0.7, "Minimum cluster energy for EMCal energy cut"};
122+
Configurable<int> cfgEMCminNCell{"cfgEMCminNCell", 1, "Minimum number of cells per cluster for EMCal NCell cut"};
123+
Configurable<std::vector<float>> cfgEMCTMEta{"cfgEMCTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"};
124+
Configurable<std::vector<float>> cfgEMCTMPhi{"cfgEMCTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"};
125+
Configurable<std::vector<float>> emcSecTMEta{"emcSecTMEta", {0.01f, 4.07f, -2.5f}, "|eta| <= [0]+(pT+[1])^[2] for EMCal track matching"};
126+
Configurable<std::vector<float>> emcSecTMPhi{"emcSecTMPhi", {0.015f, 3.65f, -2.f}, "|phi| <= [0]+(pT+[1])^[2] for EMCal track matching"};
127+
Configurable<float> cfgEMCEoverp{"cfgEMCEoverp", 1.75, "Minimum cluster energy over track momentum for EMCal track matching"};
128+
Configurable<bool> cfgEMCUseExoticCut{"cfgEMCUseExoticCut", true, "FLag to use the EMCal exotic cluster cut"};
129+
Configurable<bool> cfgEMCUseTM{"cfgEMCUseTM", false, "flag to use EMCal track matching cut or not"};
130+
Configurable<bool> emcUseSecondaryTM{"emcUseSecondaryTM", false, "flag to use EMCal secondary track matching cut or not"};
131+
Configurable<bool> cfgEnableQA{"cfgEnableQA", false, "flag to turn QA plots on/off"};
132+
} emccuts;
133+
134+
SliceCache cache;
135+
136+
using EMCalPhotons = soa::Join<aod::EMCEMEventIds, aod::MinClusters, aod::EMEMCClusterMCLabels>;
137+
138+
using Colls = soa::Join<aod::PMEvents, aod::EMEventsAlias, aod::EMEventsMult_000, aod::EMEventsCent_000, aod::EMMCEventLabels, aod::EmMagFields>;
139+
140+
using McColls = o2::soa::Join<o2::aod::EMMCEvents, o2::aod::BinnedGenPts>;
141+
using McParticles = EMMCParticles;
142+
143+
PresliceOptional<EMCalPhotons> perCollisionEMC = o2::aod::emccluster::pmeventId;
144+
PresliceOptional<MinMTracks> perEMCClusterMT = o2::aod::mintm::minClusterId;
145+
PresliceOptional<MinMSTracks> perEMCClusterMS = o2::aod::mintm::minClusterId;
146+
147+
HistogramRegistry registry{"registry", {}, OutputObjHandlingPolicy::AnalysisObject, false, false};
148+
149+
int mRunNumber{-1};
150+
float dBz{0.f};
151+
152+
static constexpr std::array<std::string_view, static_cast<size_t>(o2::em::emcal::mc::ParticleType::kNParticleTypes)> kSubDirs = {
153+
"photon/", "electron/", "positron/", "pi0/",
154+
"eta/", "omega/", "pion/", "kaon/", "other/"};
155+
156+
void defineEMEventCut()
157+
{
158+
fEMEventCut = EMPhotonEventCut("fEMEventCut", "fEMEventCut");
159+
fEMEventCut.SetRequireSel8(eventcuts.cfgRequireSel8);
160+
fEMEventCut.SetRequireFT0AND(eventcuts.cfgRequireFT0AND);
161+
fEMEventCut.SetZvtxRange(-eventcuts.cfgZvtxMax, +eventcuts.cfgZvtxMax);
162+
fEMEventCut.SetRequireNoTFB(eventcuts.cfgRequireNoTFB);
163+
fEMEventCut.SetRequireNoITSROFB(eventcuts.cfgRequireNoITSROFB);
164+
fEMEventCut.SetRequireNoSameBunchPileup(eventcuts.cfgRequireNoSameBunchPileup);
165+
fEMEventCut.SetRequireVertexITSTPC(eventcuts.cfgRequireVertexITSTPC);
166+
fEMEventCut.SetRequireGoodZvtxFT0vsPV(eventcuts.cfgRequireGoodZvtxFT0vsPV);
167+
fEMEventCut.SetRequireEMCReadoutInMB(eventcuts.cfgRequireEMCReadoutInMB);
168+
fEMEventCut.SetRequireEMCHardwareTriggered(eventcuts.cfgRequireEMCHardwareTriggered);
169+
}
170+
171+
void defineEMCCut()
172+
{
173+
fEMCCut = EMCPhotonCut("fEMCCut", "fEMCCut");
174+
175+
fEMCCut.SetTrackMatchingEtaParams(emccuts.cfgEMCTMEta->at(0), emccuts.cfgEMCTMEta->at(1), emccuts.cfgEMCTMEta->at(2));
176+
fEMCCut.SetTrackMatchingPhiParams(emccuts.cfgEMCTMPhi->at(0), emccuts.cfgEMCTMPhi->at(1), emccuts.cfgEMCTMPhi->at(2));
177+
178+
fEMCCut.SetSecTrackMatchingEtaParams(emccuts.emcSecTMEta->at(0), emccuts.emcSecTMEta->at(1), emccuts.emcSecTMEta->at(2));
179+
fEMCCut.SetSecTrackMatchingPhiParams(emccuts.emcSecTMPhi->at(0), emccuts.emcSecTMPhi->at(1), emccuts.emcSecTMPhi->at(2));
180+
fEMCCut.SetMinEoverP(emccuts.cfgEMCEoverp);
181+
182+
fEMCCut.SetMinE(emccuts.cfgEMCminE);
183+
fEMCCut.SetMinNCell(emccuts.cfgEMCminNCell);
184+
fEMCCut.SetM02Range(emccuts.cfgEMCminM02, emccuts.cfgEMCmaxM02);
185+
fEMCCut.SetTimeRange(emccuts.cfgEMCminTime, emccuts.cfgEMCmaxTime);
186+
fEMCCut.SetUseExoticCut(emccuts.cfgEMCUseExoticCut);
187+
fEMCCut.SetClusterizer(emccuts.clusterDefinition);
188+
fEMCCut.SetUseTM(emccuts.cfgEMCUseTM.value); // disables or enables TM
189+
fEMCCut.SetUseSecondaryTM(emccuts.emcUseSecondaryTM.value); // disables or enables secondary TM
190+
fEMCCut.SetDoQA(emccuts.cfgEnableQA.value);
191+
}
192+
193+
void init(InitContext&)
194+
{
195+
mRunNumber = 0;
196+
dBz = 0;
197+
198+
defineEMEventCut();
199+
defineEMCCut();
200+
fEMCCut.addQAHistograms(&registry);
201+
o2::aod::pwgem::photonmeson::utils::eventhistogram::addEventHistograms(&registry);
202+
203+
const AxisSpec thnAxisERec{thnConfigAxisE, "#it{E}_{Rec} (GeV)"};
204+
205+
const AxisSpec thnAxisM02{thnConfigAxisM02, "#it{M}_{02}"};
206+
207+
const AxisSpec thnAxisEtaDiff{thnConfigAxisEtaDiff, "#it{#eta}_{Rec} - #it{#eta}_{Gen}"};
208+
const AxisSpec thnAxisPhiDiff{thnConfigAxisPhiDiff, "#it{#varphi}_{Rec} - #it{#varphi}_{Gen}"};
209+
210+
AxisSpec thnAxisCentOrMult{1, 0., 1., "Centrality/Multiplicity"}; // placeholder, overwritten in init
211+
if (useCent.value) {
212+
// PbPb: use centrality
213+
thnAxisCentOrMult = {thnConfigAxisCent, "Centrality (%)"};
214+
} else {
215+
// pp: use multiplicity
216+
thnAxisCentOrMult = {thnConfigAxisMult, "FT0C Multiplicity"};
217+
}
218+
219+
registry.add("photon/hM02", "cluster m02 vs energy vs cent/mult", HistType::kTH3F, {thnAxisM02, thnAxisERec, thnAxisCentOrMult});
220+
registry.add("photon/hEtaRel", "relative #eta vs energy vs cent/mult", HistType::kTH3F, {thnAxisEtaDiff, thnAxisERec, thnAxisCentOrMult});
221+
registry.add("photon/hPhiRel", "relative #varphi vs energy vs cent/mult", HistType::kTH3F, {thnAxisPhiDiff, thnAxisERec, thnAxisCentOrMult});
222+
223+
registry.addClone("photon/", "electron/");
224+
registry.addClone("photon/", "positron/");
225+
registry.addClone("photon/", "pi0/");
226+
registry.addClone("photon/", "eta/");
227+
registry.addClone("photon/", "omega/");
228+
registry.addClone("photon/", "pion/");
229+
registry.addClone("photon/", "kaon/");
230+
registry.addClone("photon/", "other/");
231+
232+
}; // end init
233+
234+
template <o2::soa::is_iterator TCollision>
235+
float getCentralityOrMultiplicity(TCollision const& collision)
236+
{
237+
if (useCent.value) {
238+
return getCentrality(collision);
239+
}
240+
// pp: use raw FT0C multiplicity
241+
return collision.multFT0C();
242+
}
243+
244+
/// Get the centrality
245+
/// \param collision is the collision with the centrality information
246+
template <o2::soa::is_iterator TCollision>
247+
float getCentrality(TCollision const& collision)
248+
{
249+
float cent = -999.;
250+
switch (eventcuts.centEstimator) {
251+
case CentralityEstimator::CFT0M:
252+
cent = collision.centFT0M();
253+
break;
254+
case CentralityEstimator::CFT0A:
255+
cent = collision.centFT0A();
256+
break;
257+
case CentralityEstimator::CFT0C:
258+
cent = collision.centFT0C();
259+
break;
260+
default:
261+
LOG(warning) << "Centrality estimator not valid. Possible values are T0M, T0A, T0C. Fallback to T0C";
262+
cent = collision.centFT0C();
263+
break;
264+
}
265+
return cent;
266+
}
267+
268+
/// \brief check if standard event cuts + FT0 occupancy + centrality + QVec good is
269+
/// \param collision collision that will be checked
270+
/// \return true if collision survives all checks, otherwise false
271+
template <o2::soa::is_iterator TCollision>
272+
bool isFullEventSelected(TCollision const& collision, bool fillHisto = false)
273+
{
274+
if (fillHisto) {
275+
o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<0>(&registry, collision);
276+
}
277+
if (!(fEMEventCut.IsSelected(collision))) {
278+
// general event selection
279+
return false;
280+
}
281+
if (!(eventcuts.cfgFT0COccupancyMin <= collision.ft0cOccupancyInTimeRange() && collision.ft0cOccupancyInTimeRange() < eventcuts.cfgFT0COccupancyMax)) {
282+
// occupancy selection
283+
return false;
284+
}
285+
float centOrMult = getCentralityOrMultiplicity(collision);
286+
if (useCent && (centOrMult < eventcuts.cfgMinCent || centOrMult > eventcuts.cfgMaxCent)) {
287+
// event selection
288+
return false;
289+
}
290+
if (fillHisto) {
291+
o2::aod::pwgem::photonmeson::utils::eventhistogram::fillEventInfo<1>(&registry, collision);
292+
registry.fill(HIST("Event/before/hCollisionCounter"), 12.0); // accepted
293+
registry.fill(HIST("Event/after/hCollisionCounter"), 12.0); // accepted
294+
}
295+
return true;
296+
}
297+
298+
// One templated fill function instead of 9 copy-pasted blocks
299+
template <const int type, o2::soa::is_iterator TCluster, o2::soa::is_iterator TMC>
300+
void fillClusterHistos(HistogramRegistry& histRegistry, TCluster const& clu, TMC const& mcPart, float centOrMult)
301+
{
302+
static constexpr std::string_view subDir = kSubDirs[type];
303+
304+
histRegistry.fill(HIST(subDir) + HIST("hM02"), clu.m02(), clu.e(), centOrMult);
305+
histRegistry.fill(HIST(subDir) + HIST("hEtaRel"), clu.eta() - mcPart.eta(), clu.e(), centOrMult);
306+
histRegistry.fill(HIST(subDir) + HIST("hPhiRel"), clu.phi() - mcPart.phi(), clu.e(), centOrMult);
307+
}
308+
309+
// PCM-EMCal same event
310+
void processEmcal(Colls const& collisions, EMCalPhotons const& clusters, MinMTracks const& matchedPrims, MinMSTracks const& matchedSeconds, EMMCParticles const& mcParticles)
311+
{
312+
if (clusters.size() <= 0) {
313+
LOG(info) << "Skipping DF because there are not photons!";
314+
return;
315+
}
316+
EMBitFlags emcFlags(clusters.size());
317+
if (clusters.size() > 0) {
318+
fEMCCut.AreSelectedRunning(emcFlags, clusters, matchedPrims, matchedSeconds, &registry);
319+
}
320+
321+
// create iterators for photon mc particles
322+
auto mcPhoton1 = mcParticles.begin();
323+
324+
for (const auto& collision : collisions) {
325+
isFullEventSelected(collision, true);
326+
327+
float centOrMult = getCentralityOrMultiplicity(collision);
328+
329+
auto photonsEMCPerCollision = clusters.sliceBy(perCollisionEMC, collision.globalIndex());
330+
331+
for (const auto& photonEMC : photonsEMCPerCollision) {
332+
if (!(emcFlags.test(photonEMC.globalIndex()))) {
333+
continue;
334+
}
335+
if (photonEMC.emmcparticleIds().empty()) {
336+
// this is a cluster with just noise, skip
337+
continue;
338+
}
339+
// we only want to look at the largest contribution
340+
mcPhoton1.setCursor(photonEMC.emmcparticleIds()[0]);
341+
342+
if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kGamma) {
343+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kPhoton>(registry, photonEMC, mcPhoton1, centOrMult);
344+
} else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kElectron) {
345+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kElectron>(registry, photonEMC, mcPhoton1, centOrMult);
346+
} else if (mcPhoton1.pdgCode() == -PDG_t::kElectron) {
347+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kPositron>(registry, photonEMC, mcPhoton1, centOrMult);
348+
} else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kPi0) {
349+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kPi0>(registry, photonEMC, mcPhoton1, centOrMult);
350+
} else if (std::abs(mcPhoton1.pdgCode()) == o2::constants::physics::Pdg::kEta) {
351+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kEta>(registry, photonEMC, mcPhoton1, centOrMult);
352+
} else if (std::abs(mcPhoton1.pdgCode()) == o2::constants::physics::Pdg::kOmega) {
353+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kOmega>(registry, photonEMC, mcPhoton1, centOrMult);
354+
} else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kPiPlus) {
355+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kPion>(registry, photonEMC, mcPhoton1, centOrMult);
356+
} else if (std::abs(mcPhoton1.pdgCode()) == PDG_t::kKPlus) {
357+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kKaon>(registry, photonEMC, mcPhoton1, centOrMult);
358+
} else {
359+
fillClusterHistos<o2::em::emcal::mc::ParticleType::kOther>(registry, photonEMC, mcPhoton1, centOrMult);
360+
}
361+
} // for (const auto& photonEMC : photonsEMCPerCollision) {
362+
}
363+
}
364+
PROCESS_SWITCH(EmcalMcTask, processEmcal, "Process for emcal", true);
365+
366+
}; // End struct EmcalMcTask
367+
368+
WorkflowSpec defineDataProcessing(ConfigContext const& context)
369+
{
370+
return WorkflowSpec{adaptAnalysisTask<EmcalMcTask>(context)};
371+
}

0 commit comments

Comments
 (0)