Skip to content

Commit 9e9405e

Browse files
aknospesawenzel
authored andcommitted
[AOGM-212] Add EMCal Digitization to Data Processing layer (#1443)
* Add EMCal Digitization to Data Processing layer
1 parent 20497a5 commit 9e9405e

File tree

7 files changed

+370
-0
lines changed

7 files changed

+370
-0
lines changed

Steer/DigitizerWorkflow/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ O2_GENERATE_EXECUTABLE(
3434
src/TOFClusterWriterSpec.cxx
3535
src/FITDigitizerSpec.cxx
3636
src/FITDigitWriterSpec.cxx
37+
src/EMCALDigitizerSpec.cxx
38+
src/EMCALDigitWriterSpec.cxx
3739
src/GRPUpdaterSpec.cxx
3840

3941
BUCKET_NAME ${MODULE_BUCKET_NAME}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
/// @brief Processor spec for a ROOT file writer for EMCAL digits
12+
13+
#include "EMCALDigitWriterSpec.h"
14+
#include "Framework/CallbackService.h"
15+
#include "Framework/ControlService.h"
16+
#include <SimulationDataFormat/MCCompLabel.h>
17+
#include <SimulationDataFormat/MCTruthContainer.h>
18+
#include "TTree.h"
19+
#include "TBranch.h"
20+
#include "TFile.h"
21+
#include "EMCALBase/Digit.h"
22+
#include <memory> // for make_shared, make_unique, unique_ptr
23+
#include <vector>
24+
25+
using namespace o2::framework;
26+
using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType;
27+
28+
namespace o2
29+
{
30+
namespace emcal
31+
{
32+
33+
template <typename T>
34+
TBranch* getOrMakeBranch(TTree& tree, std::string brname, T* ptr)
35+
{
36+
if (auto br = tree.GetBranch(brname.c_str())) {
37+
br->SetAddress(static_cast<void*>(&ptr));
38+
return br;
39+
}
40+
// otherwise make it
41+
return tree.Branch(brname.c_str(), ptr);
42+
}
43+
44+
/// create the processor spec
45+
/// describing a processor receiving digits for EMCal writing them to file
46+
DataProcessorSpec getEMCALDigitWriterSpec()
47+
{
48+
auto initFunction = [](InitContext& ic) {
49+
// get the option from the init context
50+
auto filename = ic.options().get<std::string>("emcal-digit-outfile");
51+
auto treename = ic.options().get<std::string>("treename");
52+
53+
auto outputfile = std::make_shared<TFile>(filename.c_str(), "RECREATE");
54+
auto outputtree = std::make_shared<TTree>(treename.c_str(), treename.c_str());
55+
56+
// container for incoming digits
57+
auto digits = std::make_shared<std::vector<o2::EMCAL::Digit>>();
58+
59+
// the callback to be set as hook at stop of processing for the framework
60+
auto finishWriting = [outputfile, outputtree]() {
61+
outputtree->SetEntries(1);
62+
outputtree->Write();
63+
outputfile->Close();
64+
};
65+
ic.services().get<CallbackService>().set(CallbackService::Id::Stop, finishWriting);
66+
67+
// setup the processing function
68+
// using by-copy capture of the worker instance shared pointer
69+
// the shared pointer makes sure to clean up the instance when the processing
70+
// function gets out of scope
71+
auto processingFct = [outputfile, outputtree, digits](ProcessingContext& pc) {
72+
static bool finished = false;
73+
if (finished) {
74+
// avoid being executed again when marked as finished;
75+
return;
76+
}
77+
78+
// retrieve the digits from the input
79+
auto indata = pc.inputs().get<std::vector<o2::EMCAL::Digit>>("emcaldigits");
80+
LOG(INFO) << "RECEIVED DIGITS SIZE " << indata.size();
81+
*digits.get() = std::move(indata);
82+
83+
// connect this to a particular branch
84+
auto br = getOrMakeBranch(*outputtree.get(), "EMCALDigit", digits.get());
85+
br->Fill();
86+
87+
// retrieve labels from the input
88+
auto labeldata = pc.inputs().get<o2::dataformats::MCTruthContainer<o2::MCCompLabel>*>("emcaldigitlabels");
89+
LOG(INFO) << "EMCAL GOT " << labeldata->getNElements() << " LABELS ";
90+
auto labeldataraw = labeldata.get();
91+
// connect this to a particular branch
92+
auto labelbr = getOrMakeBranch(*outputtree.get(), "EMCALDigitMCTruth", &labeldataraw);
93+
labelbr->Fill();
94+
95+
finished = true;
96+
pc.services().get<ControlService>().readyToQuit(false);
97+
};
98+
99+
// return the actual processing function as a lambda function using variables
100+
// of the init function
101+
return processingFct;
102+
};
103+
104+
return DataProcessorSpec{
105+
"EMCALDigitWriter",
106+
Inputs{ InputSpec{ "emcaldigits", "EMC", "DIGITS", 0, Lifetime::Timeframe },
107+
InputSpec{ "emcaldigitlabels", "EMC", "DIGITSMCTR", 0, Lifetime::Timeframe } },
108+
{}, // no output
109+
AlgorithmSpec(initFunction),
110+
Options{
111+
{ "emcal-digit-outfile", VariantType::String, "emcaldigits.root", { "Name of the input file" } },
112+
{ "treename", VariantType::String, "o2sim", { "Name of top-level TTree" } },
113+
}
114+
};
115+
}
116+
} // end namespace emcal
117+
} // end namespace o2
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#ifndef STEER_DIGITIZERWORKFLOW_EMCALDIGITWRITER_H_
12+
#define STEER_DIGITIZERWORKFLOW_EMCALDIGITWRITER_H_
13+
14+
#include "Framework/DataProcessorSpec.h"
15+
16+
namespace o2
17+
{
18+
namespace emcal
19+
{
20+
21+
o2::framework::DataProcessorSpec getEMCALDigitWriterSpec();
22+
23+
} // end namespace emcal
24+
} // end namespace o2
25+
26+
#endif /* STEER_DIGITIZERWORKFLOW_EMCALDIGITWRITER_H_ */
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#include "EMCALDigitizerSpec.h"
12+
#include "Framework/ControlService.h"
13+
#include "Framework/DataProcessorSpec.h"
14+
#include "Framework/DataRefUtils.h"
15+
#include "Framework/Lifetime.h"
16+
#include "Headers/DataHeader.h"
17+
#include "TStopwatch.h"
18+
#include "Steer/HitProcessingManager.h" // for RunContext
19+
#include "TChain.h"
20+
21+
#include "EMCALSimulation/Digitizer.h"
22+
#include "DataFormatsParameters/GRPObject.h"
23+
#include <SimulationDataFormat/MCCompLabel.h>
24+
#include <SimulationDataFormat/MCTruthContainer.h>
25+
#include "DetectorsBase/GeometryManager.h"
26+
27+
using namespace o2::framework;
28+
using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType;
29+
30+
namespace o2
31+
{
32+
namespace emcal
33+
{
34+
35+
// helper function which will be offered as a service
36+
template <typename T>
37+
void retrieveHits(std::vector<TChain*> const& chains,
38+
const char* brname,
39+
int sourceID,
40+
int entryID,
41+
std::vector<T>* hits)
42+
{
43+
auto br = chains[sourceID]->GetBranch(brname);
44+
if (!br) {
45+
LOG(ERROR) << "No branch found";
46+
return;
47+
}
48+
br->SetAddress(&hits);
49+
br->GetEntry(entryID);
50+
}
51+
52+
DataProcessorSpec getEMCALDigitizerSpec(int channel)
53+
{
54+
// setup of some data structures shared between init and processing functions
55+
// (a shared pointer is used since then automatic cleanup is guaranteed with a lifetime beyond
56+
// one process call)
57+
auto simChains = std::make_shared<std::vector<TChain*>>();
58+
59+
// the instance of the actual digitizer
60+
auto digitizer = std::make_shared<o2::EMCAL::Digitizer>();
61+
62+
// containers for digits and labels
63+
auto digits = std::make_shared<std::vector<o2::EMCAL::Digit>>();
64+
auto digitsAccum = std::make_shared<std::vector<o2::EMCAL::Digit>>(); // accumulator for all digits
65+
auto labels = std::make_shared<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>();
66+
67+
// the actual processing function which get called whenever new data is incoming
68+
auto process = [simChains, digitizer, digits, digitsAccum, labels, channel](ProcessingContext& pc) {
69+
static bool finished = false;
70+
if (finished) {
71+
return;
72+
}
73+
// RS: at the moment using hardcoded flag for continuos readout
74+
static o2::parameters::GRPObject::ROMode roMode = o2::parameters::GRPObject::CONTINUOUS;
75+
76+
// read collision context from input
77+
auto context = pc.inputs().get<o2::steer::RunContext*>("collisioncontext");
78+
auto& timesview = context->getEventRecords();
79+
LOG(DEBUG) << "GOT " << timesview.size() << " COLLISSION TIMES";
80+
81+
// if there is nothing to do ... return
82+
if (timesview.size() == 0) {
83+
return;
84+
}
85+
86+
TStopwatch timer;
87+
timer.Start();
88+
89+
LOG(INFO) << " CALLING EMCAL DIGITIZATION ";
90+
91+
static std::vector<o2::EMCAL::Hit> hits;
92+
o2::dataformats::MCTruthContainer<o2::MCCompLabel> labelAccum;
93+
94+
auto& eventParts = context->getEventParts();
95+
// loop over all composite collisions given from context
96+
// (aka loop over all the interaction records)
97+
for (int collID = 0; collID < timesview.size(); ++collID) {
98+
digitizer->setEventTime(timesview[collID].timeNS);
99+
100+
// for each collision, loop over the constituents event and source IDs
101+
// (background signal merging is basically taking place here)
102+
for (auto& part : eventParts[collID]) {
103+
digitizer->setCurrEvID(part.entryID);
104+
digitizer->setCurrSrcID(part.sourceID);
105+
106+
// get the hits for this event and this source
107+
hits.clear();
108+
retrieveHits(*simChains.get(), "EMCHit", part.sourceID, part.entryID, &hits);
109+
110+
LOG(INFO) << "For collision " << collID << " eventID " << part.entryID << " found " << hits.size() << " hits ";
111+
112+
// call actual digitization procedure
113+
labels->clear();
114+
digits->clear();
115+
digitizer->process(hits, *digits.get());
116+
// copy digits into accumulator
117+
std::copy(digits->begin(), digits->end(), std::back_inserter(*digitsAccum.get()));
118+
labelAccum.mergeAtBack(*labels);
119+
LOG(INFO) << "Have " << digits->size() << " digits ";
120+
}
121+
}
122+
123+
LOG(INFO) << "Have " << labelAccum.getNElements() << " EMCAL labels ";
124+
// here we have all digits and we can send them to consumer (aka snapshot it onto output)
125+
pc.outputs().snapshot(Output{ "EMC", "DIGITS", 0, Lifetime::Timeframe }, *digitsAccum.get());
126+
pc.outputs().snapshot(Output{ "EMC", "DIGITSMCTR", 0, Lifetime::Timeframe }, labelAccum);
127+
LOG(INFO) << "EMCAL: Sending ROMode= " << roMode << " to GRPUpdater";
128+
pc.outputs().snapshot(Output{ "EMC", "ROMode", 0, Lifetime::Timeframe }, roMode);
129+
130+
timer.Stop();
131+
LOG(INFO) << "Digitization took " << timer.CpuTime() << "s";
132+
133+
// we should be only called once; tell DPL that this process is ready to exit
134+
pc.services().get<ControlService>().readyToQuit(false);
135+
finished = true;
136+
};
137+
138+
// init function returning the lambda taking a ProcessingContext
139+
auto initIt = [simChains, process, digitizer, labels](InitContext& ctx) {
140+
// setup the input chain for the hits
141+
simChains->emplace_back(new TChain("o2sim"));
142+
143+
// add the main (background) file
144+
simChains->back()->AddFile(ctx.options().get<std::string>("simFile").c_str());
145+
146+
// maybe add a particular signal file
147+
auto signalfilename = ctx.options().get<std::string>("simFileS");
148+
if (signalfilename.size() > 0) {
149+
simChains->emplace_back(new TChain("o2sim"));
150+
simChains->back()->AddFile(signalfilename.c_str());
151+
}
152+
153+
// make sure that the geometry is loaded (TODO will this be done centrally?)
154+
if (!gGeoManager) {
155+
o2::Base::GeometryManager::loadGeometry();
156+
}
157+
auto geom = o2::EMCAL::Geometry::GetInstance("EMCAL_COMPLETE", "Geant4", "EMV-EMCAL");
158+
// init digitizer
159+
digitizer->setGeometry(geom);
160+
digitizer->init();
161+
162+
// return the actual processing function which is now setup/configured
163+
return process;
164+
};
165+
166+
// create the full data processor spec using
167+
// a name identifier
168+
// input description
169+
// algorithmic description (here a lambda getting called once to setup the actual processing function)
170+
// options that can be used for this processor (here: input file names where to take the hits)
171+
return DataProcessorSpec{
172+
"EMCALDigitizer", Inputs{ InputSpec{ "collisioncontext", "SIM", "COLLISIONCONTEXT",
173+
static_cast<SubSpecificationType>(channel), Lifetime::Timeframe } },
174+
Outputs{ OutputSpec{ "EMC", "DIGITS", 0, Lifetime::Timeframe },
175+
OutputSpec{ "EMC", "DIGITSMCTR", 0, Lifetime::Timeframe },
176+
OutputSpec{ "EMC", "ROMode", 0, Lifetime::Timeframe } },
177+
AlgorithmSpec{ initIt },
178+
Options{ { "simFile", VariantType::String, "o2sim.root", { "Sim (background) input filename" } },
179+
{ "simFileS", VariantType::String, "", { "Sim (signal) input filename" } },
180+
{ "pileup", VariantType::Int, 1, { "whether to run in continuous time mode" } } }
181+
// I can't use VariantType::Bool as it seems to have a problem
182+
};
183+
}
184+
} // end namespace emcal
185+
} // end namespace o2
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Copyright CERN and copyright holders of ALICE O2. This software is
2+
// distributed under the terms of the GNU General Public License v3 (GPL
3+
// Version 3), copied verbatim in the file "COPYING".
4+
//
5+
// See http://alice-o2.web.cern.ch/license for full licensing information.
6+
//
7+
// In applying this license CERN does not waive the privileges and immunities
8+
// granted to it by virtue of its status as an Intergovernmental Organization
9+
// or submit itself to any jurisdiction.
10+
11+
#ifndef STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_
12+
#define STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_
13+
14+
#include "Framework/DataProcessorSpec.h"
15+
16+
namespace o2
17+
{
18+
namespace emcal
19+
{
20+
21+
o2::framework::DataProcessorSpec getEMCALDigitizerSpec(int channel);
22+
23+
} // end namespace emcal
24+
} // end namespace o2
25+
26+
#endif /* STEER_DIGITIZERWORKFLOW_EMCALDIGITIZER_H_ */

Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
#include "FITDigitizerSpec.h"
4141
#include "FITDigitWriterSpec.h"
4242

43+
// for EMCal
44+
#include "EMCALDigitizerSpec.h"
45+
#include "EMCALDigitWriterSpec.h"
46+
4347
// GRP
4448
#include "DataFormatsParameters/GRPObject.h"
4549
#include "GRPUpdaterSpec.h"
@@ -313,6 +317,15 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext)
313317
specs.emplace_back(o2::fit::getFITDigitWriterSpec());
314318
}
315319

320+
// the EMCal part
321+
if (isEnabled(o2::detectors::DetID::EMC)) {
322+
detList.emplace_back(o2::detectors::DetID::EMC);
323+
// connect the EMCal digitization
324+
specs.emplace_back(o2::emcal::getEMCALDigitizerSpec(fanoutsize++));
325+
// connect the EMCal digit writer
326+
specs.emplace_back(o2::emcal::getEMCALDigitWriterSpec());
327+
}
328+
316329
// GRP updater: must come after all detectors since requires their list
317330
specs.emplace_back(o2::parameters::getGRPUpdaterSpec(detList));
318331

0 commit comments

Comments
 (0)