Project
Loading...
Searching...
No Matches
TRKDigitizerSpec.cxx
Go to the documentation of this file.
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#include "TRKDigitizerSpec.h"
18#include "Framework/Lifetime.h"
19#include "Framework/Task.h"
31#include "TRKBase/AlmiraParam.h"
34
35#include <TChain.h>
36#include <TStopwatch.h>
37
38#include <algorithm>
39#include <memory>
40#include <string>
41
42using namespace o2::framework;
44
45namespace
46{
47std::vector<OutputSpec> makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth)
48{
49 std::vector<OutputSpec> outputs;
50 outputs.emplace_back(detOrig, "DIGITS", 0, Lifetime::Timeframe);
51 outputs.emplace_back(detOrig, "DIGITSROF", 0, Lifetime::Timeframe);
52 if (mctruth) {
53 outputs.emplace_back(detOrig, "DIGITSMC2ROF", 0, Lifetime::Timeframe);
54 outputs.emplace_back(detOrig, "DIGITSMCTR", 0, Lifetime::Timeframe);
55 }
56 outputs.emplace_back(detOrig, "ROMode", 0, Lifetime::Timeframe);
57 return outputs;
58}
59} // namespace
60
61namespace o2::trk
62{
63using namespace o2::base;
65{
66 public:
68
69 TRKDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {}
70
72 {
73 mDisableQED = ic.options().get<bool>("disable-qed");
74 mLocalRespFile = ic.options().get<std::string>("local-response-file");
75 }
76
78 {
79 if (mFinished) {
80 return;
81 }
82 mFirstOrbitTF = pc.services().get<o2::framework::TimingInfo>().firstTForbit;
83 const o2::InteractionRecord firstIR(0, mFirstOrbitTF);
85
86 // read collision context from input
87 auto context = pc.inputs().get<o2::steer::DigitizationContext*>("collisioncontext");
88 context->initSimChains(mID, mSimChains);
89 const bool withQED = context->isQEDProvided() && !mDisableQED;
90 auto& timesview = context->getEventRecords(withQED);
91 LOG(info) << "GOT " << timesview.size() << " COLLISION TIMES";
92 LOG(info) << "SIMCHAINS " << mSimChains.size();
93
94 // if there is nothing to do ... return
95 if (timesview.empty()) {
96 return;
97 }
98 TStopwatch timer;
99 timer.Start();
100 LOG(info) << " CALLING TRK DIGITIZATION ";
101
102 mDigitizer.setDigits(&mDigits);
103 mDigitizer.setROFRecords(&mROFRecords);
104 mDigitizer.setMCLabels(&mLabels);
105
106 // digits are directly put into DPL owned resource
107 auto& digitsAccum = pc.outputs().make<std::vector<itsmft::Digit>>(Output{mOrigin, "DIGITS", 0});
108
109 const int roFrameLengthInBC = mDigitizer.getParams().getROFrameLengthInBC();
110 const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / roFrameLengthInBC;
111 const int nROFsTF = nROFsPerOrbit * raw::HBFUtils::Instance().getNOrbitsPerTF();
112 mROFRecordsAccum.reserve(nROFsTF);
113
114 auto accumulate = [this, &digitsAccum]() {
115 // accumulate result of single event processing, called after processing every event supplied
116 // AND after the final flushing via digitizer::fillOutputContainer
117 if (mDigits.empty()) {
118 LOG(debug) << "No digits to accumulate";
119 return; // no digits were flushed, nothing to accumulate
120 }
121 LOG(debug) << "Accumulating " << mDigits.size() << " digits ";
122 auto ndigAcc = digitsAccum.size();
123 std::copy(mDigits.begin(), mDigits.end(), std::back_inserter(digitsAccum));
124
125 // fix ROFrecords references on ROF entries
126 auto nROFRecsOld = mROFRecordsAccum.size();
127
128 for (int i = 0; i < mROFRecords.size(); i++) {
129 auto& rof = mROFRecords[i];
130 rof.setFirstEntry(ndigAcc + rof.getFirstEntry());
131 rof.print();
132
133 if (mFixMC2ROF < mMC2ROFRecordsAccum.size()) { // fix ROFRecord entry in MC2ROF records
134 for (int m2rid = mFixMC2ROF; m2rid < mMC2ROFRecordsAccum.size(); m2rid++) {
135 // need to register the ROFRecors entry for MC event starting from this entry
136 auto& mc2rof = mMC2ROFRecordsAccum[m2rid];
137 if (rof.getROFrame() == mc2rof.minROF) {
138 mFixMC2ROF++;
139 mc2rof.rofRecordID = nROFRecsOld + i;
140 mc2rof.print();
141 }
142 }
143 }
144 }
145
146 std::copy(mROFRecords.begin(), mROFRecords.end(), std::back_inserter(mROFRecordsAccum));
147 if (mWithMCTruth) {
148 mLabelsAccum.mergeAtBack(mLabels);
149 }
150 LOG(info) << "Added " << mDigits.size() << " digits ";
151 // clean containers from already accumulated stuff
152 mLabels.clear();
153 mDigits.clear();
154 mROFRecords.clear();
155 }; // end accumulate lambda
156
157 auto& eventParts = context->getEventParts(withQED);
158 // loop over all composite collisions given from context (aka loop over all the interaction records)
159 const int bcShift = mDigitizer.getParams().getROFrameBiasInBC();
160 // loop over all composite collisions given from context (aka loop over all the interaction records)
161 for (size_t collID = 0; collID < timesview.size(); ++collID) {
162 auto irt = timesview[collID];
163 if (irt.toLong() < bcShift) { // due to the ROF misalignment the collision would go to negative ROF ID, discard
164 continue;
165 }
166 irt -= bcShift; // account for the ROF start shift
167
168 mDigitizer.setEventTime(irt);
169 mDigitizer.resetEventROFrames(); // to estimate min/max ROF for this collID
170 // for each collision, loop over the constituents event and source IDs
171 // (background signal merging is basically taking place here)
172 for (auto& part : eventParts[collID]) {
173
174 // get the hits for this event and this source
175 mHits.clear();
176 context->retrieveHits(mSimChains, o2::detectors::SimTraits::DETECTORBRANCHNAMES[mID][0].c_str(), part.sourceID, part.entryID, &mHits);
177
178 if (!mHits.empty()) {
179 LOG(debug) << "For collision " << collID << " eventID " << part.entryID
180 << " found " << mHits.size() << " hits ";
181 mDigitizer.process(&mHits, part.entryID, part.sourceID); // call actual digitization procedure
182 }
183 }
184 mMC2ROFRecordsAccum.emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax());
185 accumulate();
186 }
187 mDigitizer.fillOutputContainer();
188 LOG(debug) << "mDigits size after fill: " << mDigits.size();
189 accumulate();
190
191 // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output)
192 std::vector<o2::itsmft::ROFRecord> expDigitRofVec(nROFsTF);
193 for (int iROF = 0; iROF < nROFsTF; ++iROF) {
194 auto& rof = expDigitRofVec[iROF];
195 const int orb = iROF * roFrameLengthInBC / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF;
196 const int bc = iROF * roFrameLengthInBC % o2::constants::lhc::LHCMaxBunches;
197 rof.setBCData(o2::InteractionRecord(bc, orb));
198 rof.setROFrame(iROF);
199 rof.setNEntries(0);
200 rof.setFirstEntry(-1);
201 }
202
203 for (const auto& rof : mROFRecordsAccum) {
204 const auto& ir = rof.getBCData();
205 const auto irToFirst = ir - firstIR;
206 const auto irROF = irToFirst.toLong() / roFrameLengthInBC;
207 if (irROF < 0 || irROF >= nROFsTF) {
208 continue;
209 }
210 auto& expROF = expDigitRofVec[irROF];
211 expROF.setFirstEntry(rof.getFirstEntry());
212 expROF.setNEntries(rof.getNEntries());
213 if (expROF.getBCData() != rof.getBCData()) {
214 LOGP(fatal, "detected mismatch between expected {} and received {}", expROF.asString(), rof.asString());
215 }
216 }
217
218 int prevFirst = 0;
219 for (auto& rof : expDigitRofVec) {
220 if (rof.getFirstEntry() < 0) {
221 rof.setFirstEntry(prevFirst);
222 }
223 prevFirst = rof.getFirstEntry();
224 }
225
226 pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, expDigitRofVec);
227 if (mWithMCTruth) {
228 std::vector<o2::itsmft::MC2ROFRecord> clippedMC2ROFRecords;
229 clippedMC2ROFRecords.reserve(mMC2ROFRecordsAccum.size());
230 for (auto mc2rof : mMC2ROFRecordsAccum) {
231 if (mc2rof.rofRecordID < 0 || mc2rof.minROF >= static_cast<uint32_t>(nROFsTF)) {
232 mc2rof.rofRecordID = -1;
233 mc2rof.minROF = 0;
234 mc2rof.maxROF = 0;
235 } else {
236 mc2rof.maxROF = std::min<uint32_t>(mc2rof.maxROF, nROFsTF - 1);
237 if (mc2rof.minROF > mc2rof.maxROF) {
238 mc2rof.rofRecordID = -1;
239 mc2rof.minROF = 0;
240 mc2rof.maxROF = 0;
241 } else {
242 mc2rof.rofRecordID = mc2rof.minROF;
243 }
244 }
245 clippedMC2ROFRecords.push_back(mc2rof);
246 }
247 pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, clippedMC2ROFRecords);
248 auto& sharedlabels = pc.outputs().make<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(Output{mOrigin, "DIGITSMCTR", 0});
249 mLabelsAccum.flatten_to(sharedlabels);
250 // free space of existing label containers
251 mLabels.clear_andfreememory();
252 mLabelsAccum.clear_andfreememory();
253 }
254 LOG(info) << mID.getName() << ": Sending ROMode= " << mROMode << " to GRPUpdater";
255 pc.outputs().snapshot(Output{mOrigin, "ROMode", 0}, mROMode);
256
257 timer.Stop();
258 LOG(info) << "Digitization took " << timer.CpuTime() << "s";
259
260 // we should be only called once; tell DPL that this process is ready to exit
261 pc.services().get<ControlService>().readyToQuit(QuitRequest::Me);
262
263 mFinished = true;
264 }
265
267 {
268 std::unique_ptr<TFile> file(TFile::Open(mLocalRespFile.data(), "READ"));
269 if (!file) {
270 LOG(fatal) << "Cannot open response file " << mLocalRespFile;
271 }
272 mDigitizer.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1"));
273 }
274
276 {
277 static bool initOnce{false};
278 if (!initOnce) {
279 initOnce = true;
280 auto& digipar = mDigitizer.getParams();
281
282 // configure digitizer
284 geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); // make sure L2G matrices are loaded
285 geom->Print();
286 mDigitizer.setGeometry(geom);
287
289 // pc.inputs().get<o2::trk::AlmiraParam*>("TRK_almiraparam");
290 const auto& aopt = o2::trk::AlmiraParam::Instance();
291 auto frameNS = aopt.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS;
292 digipar.setContinuous(true);
293 digipar.setROFrameBiasInBC(aopt.roFrameBiasInBC);
294 digipar.setROFrameLengthInBC(aopt.roFrameLengthInBC);
295 digipar.setROFrameLength(frameNS); // RO frame in ns
296 digipar.setStrobeDelay(aopt.strobeDelay);
297 digipar.setStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay);
298 // parameters of signal time response: flat-top duration, max rise time and q @ which rise time is 0
299 digipar.getSignalShape().setParameters(dopt.strobeFlatTop, dopt.strobeMaxRiseTime, dopt.strobeQRiseTime0);
300 digipar.setChargeThreshold(dopt.chargeThreshold); // charge threshold in electrons
301 digipar.setNoisePerPixel(dopt.noisePerPixel); // noise level
302 digipar.setTimeOffset(dopt.timeOffset);
303 digipar.setNSimSteps(dopt.nSimSteps);
304
306 LOG(info) << mID.getName() << " simulated in CONTINUOUS RO mode";
307
308 // if (oTRKParams::Instance().useDeadChannelMap) {
309 // pc.inputs().get<o2::itsmft::NoiseMap*>("TRK_dead"); // trigger final ccdb update
310 // }
311 pc.inputs().get<o2::itsmft::AlpideSimResponse*>("TRK_aptsresp");
312
313 // init digitizer
314 mDigitizer.init();
315 }
316 // Other time-dependent parameters can be added below
317 }
318
319 void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj)
320 {
321 if (matcher == ConcreteDataMatcher(mOrigin, "ALMIRAPARAM", 0)) {
322 LOG(info) << mID.getName() << " Almira param updated";
323 const auto& par = o2::trk::AlmiraParam::Instance();
324 par.printKeyValues();
325 return;
326 }
327 // if (matcher == ConcreteDataMatcher(mOrigin, "DEADMAP", 0)) {
328 // LOG(info) << mID.getName() << " static dead map updated";
329 // mDigitizer.setDeadChannelsMap((o2::itsmft::NoiseMap*)obj);
330 // return;
331 // }
332 if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) {
333 LOG(info) << mID.getName() << " loaded APTSResponseData";
334 if (mLocalRespFile.empty()) {
335 LOG(info) << "Using CCDB/APTS response file";
336 mDigitizer.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)obj);
337 mDigitizer.setResponseName("APTS");
338 } else {
339 LOG(info) << "Response function will be loaded from local file: " << mLocalRespFile;
341 mDigitizer.setResponseName("ALICE3");
342 }
343 }
344 }
345
346 private:
347 bool mWithMCTruth{true};
348 bool mFinished{false};
349 bool mDisableQED{false};
350 unsigned long mFirstOrbitTF = 0x0;
351 std::string mLocalRespFile{""};
352 const o2::detectors::DetID mID{o2::detectors::DetID::TRK};
354 o2::trk::Digitizer mDigitizer{};
355 std::vector<o2::itsmft::Digit> mDigits{};
356 std::vector<o2::itsmft::ROFRecord> mROFRecords{};
357 std::vector<o2::itsmft::ROFRecord> mROFRecordsAccum{};
358 std::vector<o2::trk::Hit> mHits{};
359 std::vector<o2::trk::Hit>* mHitsP{&mHits};
362 std::vector<o2::itsmft::MC2ROFRecord> mMC2ROFRecordsAccum{};
363 std::vector<TChain*> mSimChains{};
364
365 int mFixMC2ROF = 0; // 1st entry in mc2rofRecordsAccum to be fixed for ROFRecordID
367};
368
369DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth)
370{
371 std::string detStr = o2::detectors::DetID::getName(o2::detectors::DetID::TRK);
372 auto detOrig = o2::header::gDataOriginTRK;
373 std::vector<InputSpec> inputs;
374 inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast<SubSpecificationType>(channel), Lifetime::Timeframe);
375 // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam"));
376 // if (oTRKParams::Instance().useDeadChannelMap) {
377 // inputs.emplace_back("TRK_dead", "TRK", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("TRK/Calib/DeadMap"));
378 // }
379 inputs.emplace_back("TRK_aptsresp", "TRK", "APTSRESP", 0, Lifetime::Condition, ccdbParamSpec("IT3/Calib/APTSResponse"));
380
381 return DataProcessorSpec{detStr + "Digitizer",
382 inputs, makeOutChannels(detOrig, mctruth),
383 AlgorithmSpec{adaptFromTask<TRKDPLDigitizerTask>(mctruth)},
384 Options{
385 {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}},
386 {"local-response-file", o2::framework::VariantType::String, "", {"use response file saved locally at this path/filename"}}}};
387}
388
389} // namespace o2::trk
Definition of the base digitizer task class.
A const (ready only) version of MCTruthContainer.
Definition of the ITSMFT digit.
o2::framework::DataAllocator::SubSpecificationType SubSpecificationType
std::ostringstream debug
uint64_t bc
Definition RawEventData.h:5
int32_t i
Header of the General Run Parameters object.
Definition of the ITSMFT ROFrame (trigger) record.
Definition of the TRK digitizer.
virtual void init(o2::framework::InitContext &) final
A read-only version of MCTruthContainer allowing for storage optimisation.
A container to hold and manage MC truth information/labels.
void mergeAtBack(MCTruthContainer< TruthElement > const &other)
size_t flatten_to(ContainerType &container) const
Static class with identifiers, bitmasks and names for ALICE detectors.
Definition DetID.h:58
static constexpr const char * getName(ID id)
names of defined detectors
Definition DetID.h:146
static const std::array< std::vector< std::string >, DetID::nDetectors > DETECTORBRANCHNAMES
Definition SimTraits.h:39
void snapshot(const Output &spec, T const &object)
o2::header::DataHeader::SubSpecificationType SubSpecificationType
decltype(auto) make(const Output &spec, Args... args)
ConfigParamRegistry const & options()
Definition InitContext.h:33
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
bool initSimChains(o2::detectors::DetID detid, std::vector< TChain * > &simchains) const
void setResponse(const o2::itsmft::AlpideSimResponse *)
int getROFrameBiasInBC() const
Definition DigiParams.h:73
int getROFrameLengthInBC() const
Definition DigiParams.h:56
void setEventTime(const o2::InteractionTimeRecord &irt)
void fillOutputContainer(uint32_t maxFrame=0xffffffff)
o2::trk::DigiParams & getParams()
Definition Digitizer.h:50
void setResponseName(const std::string &name)
Definition Digitizer.h:48
void process(const std::vector< o2::trk::Hit > *hits, int evID, int srcID)
Steer conversion of hits to digits.
uint32_t getEventROFrameMin() const
Definition Digitizer.h:75
void setDigits(std::vector< o2::itsmft::Digit > *dig)
Definition Digitizer.h:45
void setROFRecords(std::vector< o2::itsmft::ROFRecord > *rec)
Definition Digitizer.h:47
void setMCLabels(o2::dataformats::MCTruthContainer< o2::MCCompLabel > *mclb)
Definition Digitizer.h:46
void setGeometry(const o2::trk::GeometryTGeo *gm)
Definition Digitizer.h:73
uint32_t getEventROFrameMax() const
Definition Digitizer.h:76
void resetEventROFrames()
Definition Digitizer.h:77
void Print(Option_t *opt="") const
void fillMatrixCache(int mask)
static GeometryTGeo * Instance()
TRKDPLDigitizerTask(bool mctruth=true)
void run(framework::ProcessingContext &pc)
void updateTimeDependentParams(ProcessingContext &pc)
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj)
void initDigitizerTask(framework::InitContext &ic) override
constexpr o2::header::DataOrigin gDataOriginTRK
Definition DataHeader.h:584
constexpr int LHCMaxBunches
constexpr double LHCBunchSpacingNS
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
std::vector< ConfigParamSpec > Options
header::DataHeader::SubSpecificationType SubSpecificationType
DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth)
class listing possible services
static constexpr int L2G
Definition Cartesian.h:54
int getNOrbitsPerTF() const
get IR corresponding to start of the HBF
Definition HBFUtils.h:49
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)