37 std::vector<o2::fdd::Digit>& digitsBC,
38 std::vector<o2::fdd::ChannelData>& digitsCh,
39 std::vector<o2::fdd::DetTrigInput>& digitsTrig,
47 auto sorted_hits{hits};
49 return a.GetTrackID() < b.GetTrackID();
53 for (
auto& hit : sorted_hits) {
54 int iChannel = hit.GetDetectorID();
57 if (mDeadChannelMap && !mDeadChannelMap->
isChannelAlive(iChannel)) {
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)");
71 std::array<o2::InteractionRecord, NBC2Cache> cachedIR;
74 double delayScintillator = mRndScintDelay.getNextValue();
75 double timeHit = delayScintillator + hit.GetTime();
78 const float timeOfFlight = hit.GetPos().R() / o2::constants::physics::LightSpeedCm2NS;
81 timeHit += -timeOfFlight + timeOffset;
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) {
92 setBCCache(cachedIR[nCachedIR++]);
95 createPulse(nPhotoElectrons, hit.GetTrackID(), timeHit, cachedIR, nCachedIR, iChannel);
100void Digitizer::createPulse(
int nPhE,
int parID,
double timeHit, std::array<o2::InteractionRecord, NBC2Cache>
const& cachedIR,
int nCachedIR,
int channel)
102 auto const roundVc = [&](
int i) ->
int {
103 return (
i / Vc::float_v::Size) * Vc::float_v::Size;
106 double time0 = cachedIR[0].bc2ns();
107 float timeDiff = time0 - timeHit;
117 Bool_t added[nCachedIR];
118 for (
int ir = 0;
ir < nCachedIR;
ir++) {
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)));
129 float const tempT = mBinSize * (0.5f + firstBin) - tPhE;
130 long iStart = std::lround((tempT + 2.0f *
PMTransitTime) * BinSizeInv);
134 LOG(error) <<
"FDDDigitizer: table lookup failure";
136 iStart = roundVc(std::max(
long(0), iStart));
140 float const* q = mPMResponseTables[parameters.
NResponseTables / 2 + iOffset].data() + iStart;
141 float const* qEnd = &mPMResponseTables[parameters.
NResponseTables / 2 + iOffset].back();
146 auto bcCache = getBCCache(cachedIR[
ir]);
147 auto& analogSignal = (*bcCache).pulse[channel];
148 float*
p = analogSignal.data() + localFirst;
150 for (
int localBin = localFirst, iEnd = roundVc(localLast); q < qEnd && localBin < iEnd; localBin += Vc::float_v::Size) {
152 q += Vc::float_v::Size;
153 Vc::prefetchForOneRead(q);
155 workVc += mRndGainVar.getNextValueVc<Vc::float_v>() *
charge * pmtVc;
157 p += Vc::float_v::Size;
158 Vc::prefetchForOneRead(p);
163 for (
int ir = 0;
ir < nCachedIR;
ir++) {
165 auto bcCache = getBCCache(cachedIR[
ir]);
166 (*bcCache).labels.emplace_back(parID, mEventID, mSrcID, channel);
172 std::vector<o2::fdd::ChannelData>& digitsCh,
173 std::vector<o2::fdd::DetTrigInput>& digitsTrig,
178 int nCached = mCache.size();
183 LOG(
debug) <<
"Generating new pedestal BL fluct. for BC range " << mCache.front() <<
" : " << mCache.back();
192 for (
int ibc = 0; ibc < nCached; ibc++) {
193 auto&
bc = mCache[ibc];
194 storeBC(
bc, digitsBC, digitsCh, digitsTrig,
labels);
198 mCache.erase(mCache.begin(), mCache.end());
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,
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;
215 if (chargeADC != 0) {
217 totalChargeC += chargeADC;
218 total_time_C += cfdTime;
221 totalChargeA += chargeADC;
222 total_time_A += cfdTime;
226 if (std::rand() % 2) {
229 digitsCh.emplace_back(ic, cfdTime,
int(chargeADC), channelBits);
234 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0;
237 uint32_t
amplA = is_A ? totalChargeA * 0.125 : -5000;
238 uint32_t
amplC = is_C ? totalChargeC * 0.125 : -5000;
239 int timeA = is_A ? total_time_A / n_hit_A : -5000;
240 int timeC = is_C ? total_time_C / n_hit_C : -5000;
241 isVertex = is_A && is_C;
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),
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);
254 labels.addElement(nBC, lbl);
265 chargeADC += pulse[iBin];
268 if (chargeADC > 4095) {
273 return std::lround(chargeADC);
279 std::fill(mTimeCFD.begin(), mTimeCFD.end(), 0);
280 float timeCFD = -1024;
281 int binShift = TMath::Nint(parameters.
TimeShiftCFD / mBinSize);
284 if (iBin >= binShift) {
285 mTimeCFD[iBin] = 5.0 * pulse[iBin - binShift] - pulse[iBin];
287 mTimeCFD[iBin] = -1.0 * pulse[iBin];
291 if (mTimeCFD[iBin - 1] < 0 && mTimeCFD[iBin] >= 0) {
292 timeCFD = mBinSize * float(iBin);
298 return std::lround(timeCFD);
303 if (mCache.empty() || mCache.back() <
ir) {
304 mCache.emplace_back();
305 auto& cb = mCache.back();
309 if (mCache.front() >
ir) {
310 mCache.emplace_front();
311 auto& cb = mCache.front();
316 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
321 auto cbnew = mCache.emplace(cb);
326 return mCache.front();
332 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
347 auto const roundVc = [&](
int i) ->
int {
348 return (
i / Vc::float_v::Size) * Vc::float_v::Size;
351 float offset = -0.5f * mBinSize;
352 int const nBins = roundVc(std::lround(4.0f *
PMTransitTime / mBinSize));
353 for (
auto& table : mPMResponseTables) {
356 for (
int j = 0;
j < nBins; ++
j) {
357 table[
j] = Digitizer::PMResponse(t);
365 mRndScintDelay.initialize(scintDelayFn);
369 pmtResponseFn.SetNpx(100);
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;
380 TF1 signalShapeFn(
"signalShape",
"crystalball", 0, 300);
382 mRndSignalShape.initialize([&]() ->
float {
383 return signalShapeFn.GetRandom(0, 200);
393 if (p == 1.0f || nPhot == 0) {
396 const int n = int(nPhot < 100 ? gRandom->Binomial(nPhot, p) : gRandom->Gaus(p * nPhot + 0.5, TMath::Sqrt(p * (1 - p) * nPhot)));
400Double_t Digitizer::PMResponse(Double_t*
x, Double_t*)
402 return Digitizer::PMResponse(
x[0]);
405Double_t Digitizer::PMResponse(Double_t
x)
412Double_t Digitizer::SinglePhESpectrum(Double_t*
x, Double_t*)
424 printf(
"Cached Orbit:%5d/BC:%4d",
orbit,
bc);
425 for (
int ic = 0; ic < 16; ic++) {
426 printf(
"Ch[%d] | ", ic);
429 printf(
"%f ", pulse[ic][ib]);
#define O2ParamImpl(classname)
Configurable digitization parameters.
std::vector< o2::MCCompLabel > labels
Definition of a container to keep Monte Carlo truth external to simulation objects.
static const FDDDigParam & Instance()
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)
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)
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
constexpr float ChargePerADC
constexpr float PMTransitTime
constexpr float PMTransparency
constexpr float PMNbOfSecElec
constexpr float invTimePerTDC
constexpr float PhotoCathodeEfficiency
constexpr float IntTimeRes
constexpr int NTimeBinsPerBC
constexpr short Nchannels
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)
static constexpr float ShapeSigma
static constexpr float TimeDelayFDC
static constexpr float ShapeAlpha
static constexpr int NResponseTables
static constexpr uint8_t defaultFEEbits
static constexpr float TimeDelayFDA
static constexpr float ShapeN
static constexpr float TimeShiftCFD
static constexpr float LightYield
std::array< ChannelBCDataF, Nchannels > pulse
float hitTimeOffsetC
Hit time offset on the C side [ns].
float hitTimeOffsetA
Hit time offset on the A side [ns].
const bool isChannelAlive(const uint8_t &chId) const
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)