Project
Loading...
Searching...
No Matches
Digitizer.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
13
17
18#include "TMath.h"
19#include "TRandom.h"
20#include <algorithm>
21#include <cassert>
22#include <iostream>
23#include <Vc/Vc>
24
25using namespace o2::math_utils;
26using namespace o2::fdd;
27
29
31{
32 memset(&pulse, 0, Nchannels * sizeof(ChannelBCDataF));
33}
34
35//_____________________________________________________________________________
36void Digitizer::process(const std::vector<o2::fdd::Hit>& hits,
37 std::vector<o2::fdd::Digit>& digitsBC,
38 std::vector<o2::fdd::ChannelData>& digitsCh,
39 std::vector<o2::fdd::DetTrigInput>& digitsTrig,
41{
42 // loop over all hits and produce digits
43 // LOG(info) << "Processing IR = " << mIntRecord << " | NHits = " << hits.size();
44
45 flush(digitsBC, digitsCh, digitsTrig, labels); // flush cached signal which cannot be affect by new event
46
47 auto sorted_hits{hits};
48 std::sort(sorted_hits.begin(), sorted_hits.end(), [](o2::fdd::Hit const& a, o2::fdd::Hit const& b) {
49 return a.GetTrackID() < b.GetTrackID();
50 });
51 // LOG(info) << "Pulse";
52 // Conversion of hits to the analogue pulse shape
53 for (auto& hit : sorted_hits) {
54 int iChannel = hit.GetDetectorID();
55
56 // If the dead channel map is used, and the channel with ID 'hit_ch' is dead, don't process this hit.
57 if (mDeadChannelMap && !mDeadChannelMap->isChannelAlive(iChannel)) {
58 continue;
59 }
60
61 if (hit.GetTime() > 20e3) {
62 const int maxWarn = 10;
63 static int warnNo = 0;
64 if (warnNo < maxWarn) {
65 LOG(warning) << "Ignoring hit with time_in_event = " << hit.GetTime() << " ns"
66 << ((++warnNo < maxWarn) ? "" : " (suppressing further warnings)");
67 }
68 continue;
69 }
70
71 std::array<o2::InteractionRecord, NBC2Cache> cachedIR;
72 int nPhotoElectrons = simulateLightYield(iChannel, hit.GetNphot());
73
74 double delayScintillator = mRndScintDelay.getNextValue();
75 double timeHit = delayScintillator + hit.GetTime();
76
77 // Subtract time-of-flight from hit time
78 const float timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS;
79 const float timeOffset = iChannel < 8 ? FDDDigParam::Instance().hitTimeOffsetC : FDDDigParam::Instance().hitTimeOffsetA;
80
81 timeHit += -timeOfFlight + timeOffset;
82 timeHit += mIntRecord.getTimeNS();
83 o2::InteractionRecord irHit(timeHit); // BC in which the hit appears (might be different from interaction BC for slow particles)
84
85 int nCachedIR = 0;
86 for (int i = BCCacheMin; i < BCCacheMax + 1; i++) {
87 double tNS = timeHit + o2::constants::lhc::LHCBunchSpacingNS * i;
88 cachedIR[nCachedIR].setFromNS(tNS);
89 if (tNS < 0 && cachedIR[nCachedIR] > irHit) {
90 continue; // don't go to negative BC/orbit (it will wrap)
91 }
92 setBCCache(cachedIR[nCachedIR++]); // ensure existence of cached container
93 }
94 // if digit for this sector does not exist, create one otherwise add to it
95 createPulse(nPhotoElectrons, hit.GetTrackID(), timeHit, cachedIR, nCachedIR, iChannel);
96 } // hit loop
97}
98
99//_____________________________________________________________________________
100void Digitizer::createPulse(int nPhE, int parID, double timeHit, std::array<o2::InteractionRecord, NBC2Cache> const& cachedIR, int nCachedIR, int channel)
101{
102 auto const roundVc = [&](int i) -> int {
103 return (i / Vc::float_v::Size) * Vc::float_v::Size;
104 };
105
106 double time0 = cachedIR[0].bc2ns(); // start time of the 1st cashed BC
107 float timeDiff = time0 - timeHit;
108 if (channel < 9) {
109 timeDiff += parameters.TimeDelayFDC;
110 } else {
111 timeDiff += parameters.TimeDelayFDA;
112 }
113
114 // LOG(info) <<"Ch = "<<channel<<" NphE = " << nPhE <<" timeDiff "<<timeDiff;
115 float charge = TMath::Qe() * FDDDigParam::Instance().pmGain * mBinSize / (mPmtTimeIntegral * ChargePerADC);
116
117 Bool_t added[nCachedIR];
118 for (int ir = 0; ir < nCachedIR; ir++) {
119 added[ir] = kFALSE;
120 }
121
122 constexpr float BinSizeInv = 1.0 / mBinSize;
123 for (int iPhE = 0; iPhE < nPhE; ++iPhE) {
124 float tPhE = timeDiff + mRndSignalShape.getNextValue();
125 int const firstBin = roundVc(TMath::Max((int)0, (int)((tPhE - PMTransitTime) * BinSizeInv)));
126 int const lastBin = TMath::Min((int)NBC2Cache * NTimeBinsPerBC - 1, (int)((tPhE + 2.0 * PMTransitTime) * BinSizeInv));
127 // LOG(info) << "firstBin = "<<firstBin<<" lastbin "<<lastBin;
128
129 float const tempT = mBinSize * (0.5f + firstBin) - tPhE;
130 long iStart = std::lround((tempT + 2.0f * PMTransitTime) * BinSizeInv);
131 float const offset = tempT + 2.0f * PMTransitTime - float(iStart) * mBinSize;
132 long const iOffset = std::lround(offset * BinSizeInv * float(parameters.NResponseTables - 1));
133 if (iStart < 0) { // this should not happen
134 LOG(error) << "FDDDigitizer: table lookup failure";
135 }
136 iStart = roundVc(std::max(long(0), iStart));
137
138 Vc::float_v workVc;
139 Vc::float_v pmtVc;
140 float const* q = mPMResponseTables[parameters.NResponseTables / 2 + iOffset].data() + iStart;
141 float const* qEnd = &mPMResponseTables[parameters.NResponseTables / 2 + iOffset].back();
142
143 for (int ir = firstBin / NTimeBinsPerBC; ir <= lastBin / NTimeBinsPerBC; ir++) {
144 int localFirst = (ir == firstBin / NTimeBinsPerBC) ? firstBin : 0;
145 int localLast = (ir < lastBin / NTimeBinsPerBC) ? NTimeBinsPerBC : (lastBin - ir * NTimeBinsPerBC);
146 auto bcCache = getBCCache(cachedIR[ir]);
147 auto& analogSignal = (*bcCache).pulse[channel];
148 float* p = analogSignal.data() + localFirst;
149
150 for (int localBin = localFirst, iEnd = roundVc(localLast); q < qEnd && localBin < iEnd; localBin += Vc::float_v::Size) {
151 pmtVc.load(q);
152 q += Vc::float_v::Size;
153 Vc::prefetchForOneRead(q);
154 workVc.load(p);
155 workVc += mRndGainVar.getNextValueVc<Vc::float_v>() * charge * pmtVc;
156 workVc.store(p);
157 p += Vc::float_v::Size;
158 Vc::prefetchForOneRead(p);
159 }
160 added[ir] = kTRUE;
161 }
162 }
163 for (int ir = 0; ir < nCachedIR; ir++) {
164 if (added[ir]) {
165 auto bcCache = getBCCache(cachedIR[ir]);
166 (*bcCache).labels.emplace_back(parID, mEventID, mSrcID, channel);
167 }
168 }
169}
170//_____________________________________________________________________________
171void Digitizer::flush(std::vector<o2::fdd::Digit>& digitsBC,
172 std::vector<o2::fdd::ChannelData>& digitsCh,
173 std::vector<o2::fdd::DetTrigInput>& digitsTrig,
175{
176
177 // do we have something to flush? We can do this only for cached BC data which is distanced from currently processed BC by NBCReadOut
178 int nCached = mCache.size();
179 if (nCached < 1) {
180 return;
181 }
182 if (mIntRecord.differenceInBC(mCache.back()) > -BCCacheMin) {
183 LOG(debug) << "Generating new pedestal BL fluct. for BC range " << mCache.front() << " : " << mCache.back();
184 // generatePedestal();
185 } else {
186 return;
187 }
188 // o2::InteractionRecord ir0(mCache.front());
189 // int cacheSpan = 1 + mCache.back().differenceInBC(ir0);
190 // LOG(info) << "Cache spans " << cacheSpan << " with " << nCached << " BCs cached";
191
192 for (int ibc = 0; ibc < nCached; ibc++) { // digitize BCs which might not be affected by future events
193 auto& bc = mCache[ibc];
194 storeBC(bc, digitsBC, digitsCh, digitsTrig, labels);
195 }
196 // clean cache for BCs which are not needed anymore
197 // LOG(info) << "Cleaning cache";
198 mCache.erase(mCache.begin(), mCache.end());
199}
200//_____________________________________________________________________________
201void Digitizer::storeBC(const BCCache& bc,
202 std::vector<o2::fdd::Digit>& digitsBC, std::vector<o2::fdd::ChannelData>& digitsCh,
203 std::vector<o2::fdd::DetTrigInput>& digitsTrig,
205{
206 // LOG(info) << "Storing BC " << bc;
207 int first = digitsCh.size(), nStored = 0;
208 float totalChargeA = 0, totalChargeC = 0;
209 int n_hit_A = 0, n_hit_C = 0, total_time_A = 0, total_time_C = 0;
210 bool nChCside = 8;
211 for (int ic = 0; ic < Nchannels; ic++) {
212 float chargeADC = integrateCharge(bc.pulse[ic]);
213 int cfdTime = int(simulateTimeCFD(bc.pulse[ic]));
214
215 if (chargeADC != 0) {
216 if (ic < nChCside) {
217 totalChargeC += chargeADC;
218 total_time_C += cfdTime;
219 n_hit_C++;
220 } else {
221 totalChargeA += chargeADC;
222 total_time_A += cfdTime;
223 n_hit_A++;
224 }
225 uint8_t channelBits = parameters.defaultFEEbits;
226 if (std::rand() % 2) {
228 }
229 digitsCh.emplace_back(ic, cfdTime, int(chargeADC), channelBits);
230 nStored++;
231 }
232 }
233 // SET TRIGGERS
234 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0;
235 is_A = n_hit_A > 0;
236 is_C = n_hit_C > 0;
237 uint32_t amplA = is_A ? totalChargeA * 0.125 : -5000; // sum amplitude A side / 8 (hardware)
238 uint32_t amplC = is_C ? totalChargeC * 0.125 : -5000; // sum amplitude C side / 8 (hardware)
239 int timeA = is_A ? total_time_A / n_hit_A : -5000; // average time A side
240 int timeC = is_C ? total_time_C / n_hit_C : -5000; // average time C side
241 isVertex = is_A && is_C;
242
243 bool isLaser = false;
244 bool isOutputsAreBlocked = false;
245 bool isDataValid = true;
246 mTriggers.setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C),
247 amplA, amplC, timeA, timeC, isLaser, isOutputsAreBlocked, isDataValid);
248 if (nStored != 0) {
249 int nBC = digitsBC.size();
250 digitsBC.emplace_back(first, nStored, bc, mTriggers);
251 digitsTrig.emplace_back(bc, is_A, is_C, isVertex, is_Central, is_SemiCentral);
252
253 for (const auto& lbl : bc.labels) {
254 labels.addElement(nBC, lbl);
255 }
256 }
257}
258
259//_____________________________________________________________________________
260float Digitizer::integrateCharge(const ChannelBCDataF& pulse)
261{
262 float chargeADC = 0;
263 for (int iBin = 0; iBin < NTimeBinsPerBC; ++iBin) {
264 // pulse[iBin] /= ChargePerADC;
265 chargeADC += pulse[iBin];
266 }
267 // saturation
268 if (chargeADC > 4095) {
269 chargeADC = 4095;
270 }
271
272 // LOG(info) <<" Charge " << chargeADC;
273 return std::lround(chargeADC);
274}
275//_____________________________________________________________________________
276float Digitizer::simulateTimeCFD(const ChannelBCDataF& pulse)
277{
278
279 std::fill(mTimeCFD.begin(), mTimeCFD.end(), 0);
280 float timeCFD = -1024;
281 int binShift = TMath::Nint(parameters.TimeShiftCFD / mBinSize);
282 for (int iBin = 0; iBin < NTimeBinsPerBC; ++iBin) {
283 // if (mTime[channel][iBin] != 0) std::cout << mTime[channel][iBin] / parameters.mChargePerADC << ", ";
284 if (iBin >= binShift) {
285 mTimeCFD[iBin] = 5.0 * pulse[iBin - binShift] - pulse[iBin];
286 } else {
287 mTimeCFD[iBin] = -1.0 * pulse[iBin];
288 }
289 }
290 for (int iBin = 1; iBin < NTimeBinsPerBC; ++iBin) {
291 if (mTimeCFD[iBin - 1] < 0 && mTimeCFD[iBin] >= 0) {
292 timeCFD = mBinSize * float(iBin);
293 break;
294 }
295 }
296 // LOG(info) <<" Time " << timeCFD;
297 timeCFD *= invTimePerTDC; // ns -> counts
298 return std::lround(timeCFD);
299}
300//_____________________________________________________________________________
301o2::fdd::Digitizer::BCCache& Digitizer::setBCCache(const o2::InteractionRecord& ir)
302{
303 if (mCache.empty() || mCache.back() < ir) {
304 mCache.emplace_back();
305 auto& cb = mCache.back();
306 cb = ir;
307 return cb;
308 }
309 if (mCache.front() > ir) {
310 mCache.emplace_front();
311 auto& cb = mCache.front();
312 cb = ir;
313 return cb;
314 }
315
316 for (auto cb = mCache.begin(); cb != mCache.end(); cb++) {
317 if ((*cb) == ir) {
318 return *cb;
319 }
320 if (ir < (*cb)) {
321 auto cbnew = mCache.emplace(cb); // insert new element before cb
322 (*cbnew) = ir;
323 return (*cbnew);
324 }
325 }
326 return mCache.front();
327}
328//_____________________________________________________________________________
329o2::fdd::Digitizer::BCCache* Digitizer::getBCCache(const o2::InteractionRecord& ir)
330{
331 // get pointer on existing cache
332 for (auto cb = mCache.begin(); cb != mCache.end(); cb++) {
333 if ((*cb) == ir) {
334 return &(*cb);
335 }
336 }
337 return nullptr;
338}
339//_____________________________________________________________________________
341{
342 // mTriggers.set
343}
344//_______________________________________________________________________
346{
347 auto const roundVc = [&](int i) -> int {
348 return (i / Vc::float_v::Size) * Vc::float_v::Size;
349 };
350 // set up PMT response tables
351 float offset = -0.5f * mBinSize; // offset \in [-0.5..0.5] * mBinSize
352 int const nBins = roundVc(std::lround(4.0f * PMTransitTime / mBinSize));
353 for (auto& table : mPMResponseTables) {
354 table.resize(nBins);
355 float t = -2.0f * PMTransitTime + offset; // t \in offset + [-2 2] * DP::mPmtTransitTime
356 for (int j = 0; j < nBins; ++j) {
357 table[j] = Digitizer::PMResponse(t);
358 t += mBinSize;
359 }
360 offset += mBinSize / float(parameters.NResponseTables - 1);
361 }
362
363 TF1 scintDelayFn("fScintDelay", "gaus", -6.0f * IntTimeRes, +6.0f * IntTimeRes);
364 scintDelayFn.SetParameters(1, 0, IntTimeRes);
365 mRndScintDelay.initialize(scintDelayFn);
366
367 // Initialize function describing the PMT time response
368 TF1 pmtResponseFn("mPmtResponseFn", &Digitizer::PMResponse, -1.0f * PMTransitTime, +2.0f * PMTransitTime, 0);
369 pmtResponseFn.SetNpx(100);
370 mPmtTimeIntegral = pmtResponseFn.Integral(-1.0f * PMTransitTime, +2.0f * PMTransitTime);
371
372 // Initialize function describing PMT response to the single photoelectron
373 TF1 singlePhESpectrumFn("mSinglePhESpectrum",
374 &Digitizer::SinglePhESpectrum, 0, 30, 0);
375 float const meansPhE = singlePhESpectrumFn.Mean(0, 30);
376 mRndGainVar.initialize([&]() -> float {
377 return singlePhESpectrumFn.GetRandom(0, 30) / meansPhE;
378 });
379
380 TF1 signalShapeFn("signalShape", "crystalball", 0, 300);
381 signalShapeFn.SetParameters(1, parameters.ShapeSigma, parameters.ShapeSigma, parameters.ShapeAlpha, parameters.ShapeN);
382 mRndSignalShape.initialize([&]() -> float {
383 return signalShapeFn.GetRandom(0, 200);
384 });
385}
386//_______________________________________________________________________
388
389//_____________________________________________________________________________
390int Digitizer::simulateLightYield(int pmt, int nPhot)
391{
392 const float p = parameters.LightYield * PhotoCathodeEfficiency;
393 if (p == 1.0f || nPhot == 0) {
394 return nPhot;
395 }
396 const int n = int(nPhot < 100 ? gRandom->Binomial(nPhot, p) : gRandom->Gaus(p * nPhot + 0.5, TMath::Sqrt(p * (1 - p) * nPhot)));
397 return n;
398}
399//_____________________________________________________________________________
400Double_t Digitizer::PMResponse(Double_t* x, Double_t*)
401{
402 return Digitizer::PMResponse(x[0]);
403}
404//_____________________________________________________________________________
405Double_t Digitizer::PMResponse(Double_t x)
406{
407 // this function describes the PM time response to a single photoelectron
408 Double_t y = x + PMTransitTime;
409 return y * y * TMath::Exp(-y * y / (PMTransitTime * PMTransitTime));
410}
411//_____________________________________________________________________________
412Double_t Digitizer::SinglePhESpectrum(Double_t* x, Double_t*)
413{
414 // this function describes the PM amplitude response to a single photoelectron
415 Double_t y = x[0];
416 if (y < 0) {
417 return 0;
418 }
419 return (TMath::Poisson(y, PMNbOfSecElec) + PMTransparency * TMath::Poisson(y, 1.0));
420}
421//______________________________________________________________
423{
424 printf("Cached Orbit:%5d/BC:%4d", orbit, bc);
425 for (int ic = 0; ic < 16; ic++) {
426 printf("Ch[%d] | ", ic);
427 for (int ib = 0; ib < NTimeBinsPerBC; ib++) {
428 if (ib % 10 == 0) {
429 printf("%f ", pulse[ic][ib]);
430 }
431 }
432 printf("\n");
433 }
434}
435
#define O2ParamImpl(classname)
Configurable digitization parameters.
ClassImp(Digitizer)
int64_t timeC
int16_t charge
Definition RawEventData.h:5
int64_t amplA
uint64_t orbit
Definition RawEventData.h:6
int64_t timeA
int64_t amplC
uint64_t bc
Definition RawEventData.h:5
int32_t i
Definition of a container to keep Monte Carlo truth external to simulation objects.
uint32_t j
Definition RawData.h:0
std::ostringstream debug
A container to hold and manage MC truth information/labels.
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
int simulateLightYield(int pmt, int nPhot)
float simulateTimeCFD(const ChannelBCDataF &pulse)
float integrateCharge(const ChannelBCDataF &pulse)
void process(const std::vector< o2::fdd::Hit > &hits, std::vector< o2::fdd::Digit > &digitsBC, std::vector< o2::fdd::ChannelData > &digitsCh, std::vector< o2::fdd::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::fdd::MCLabel > &labels)
Definition Digitizer.cxx:36
void setTriggers(o2::fdd::Digit *digit)
void flush(std::vector< o2::fdd::Digit > &digitsBC, std::vector< o2::fdd::ChannelData > &digitsCh, std::vector< o2::fdd::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::fdd::MCLabel > &labels)
void setTriggers(uint8_t trgsig, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC)
Definition Triggers.h:97
void initialize(const RandomType randomType=RandomType::Gaus)
Definition RandomRing.h:145
GLdouble n
Definition glcorearb.h:1982
GLint GLenum GLint x
Definition glcorearb.h:403
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLintptr offset
Definition glcorearb.h:660
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr double LHCBunchSpacingNS
constexpr float LightSpeedCm2NS
constexpr float ChargePerADC
Definition Constants.h:31
constexpr float PMTransitTime
Definition Constants.h:34
constexpr float PMTransparency
Definition Constants.h:35
constexpr float PMNbOfSecElec
Definition Constants.h:36
constexpr float invTimePerTDC
Definition Constants.h:32
constexpr float PhotoCathodeEfficiency
Definition Constants.h:30
constexpr float IntTimeRes
Definition Constants.h:29
constexpr int NTimeBinsPerBC
Definition Constants.h:38
constexpr short Nchannels
Definition Constants.h:25
int64_t differenceInBC(const InteractionRecord &other) const
double getTimeNS() const
get time in ns from orbit=0/bc=0
static void setFlag(EEventDataBit bitFlag, uint8_t &mFEEBits)
Definition ChannelData.h:62
static constexpr uint8_t defaultFEEbits
std::array< ChannelBCDataF, Nchannels > pulse
Definition Digitizer.h:47
float hitTimeOffsetC
Hit time offset on the C side [ns].
Definition FDDDigParam.h:26
float hitTimeOffsetA
Hit time offset on the A side [ns].
Definition FDDDigParam.h:25
float pmGain
PM gain.
Definition FDDDigParam.h:28
const bool isChannelAlive(const uint8_t &chId) const
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)