23#include <unordered_map>
43template <
typename Float>
46 float p0,
p1,
p2, p3, p4, p5, p6, p7;
60 Double_t arg1 = (log(
x1) - p0) /
p1;
61 val +=
p2 * (1.0 / (
x1 *
p1 * sqrt(2 * TMath::Pi()))) *
exp(-0.5 * arg1 * arg1);
66 Double_t arg2 = (log(x2) - p4) / p5;
67 val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) *
exp(-0.5 * arg2 * arg2);
76 float p0,
p1,
p2, p3, p4, p5, p6, p7;
89 Double_t z1 = (log(
x1) - p0) / (sqrt(2) *
p1);
90 val +=
p2 * 0.5 * (1 + TMath::Erf(z1));
95 Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5);
96 val += p6 * 0.5 * (1 + TMath::Erf(z2));
127 auto const mask = (
x >= 0.0f);
130 Vc::float_v
const a(-0.45458f);
131 Vc::float_v
const b(-0.83344945f);
132 Vc::float_v
result = -(Vc::exp(
b * arg) /
b - Vc::exp(
a * arg) /
a) / 7.8446501f;
139 assert(std::is_sorted(std::begin(
times), std::end(
times)));
142 for (
float&
n : mNoiseSamples) {
147 auto value_at = [&](
float time) {
151 Vc::float_v tableVal(0);
152 const float* tp =
times.data();
153 size_t m =
times.size() / Vc::float_v::size();
154 for (
size_t i = 0;
i <
m; ++
i) {
156 tp += Vc::float_v::size();
157 Vc::prefetchForOneRead(tp);
162 for (
size_t i = Vc::float_v::size() *
m;
i <
times.size(); ++
i, ++tp) {
168 int timeOffset = timeIndex / mSincTable.size();
169 timeIndex %= mSincTable.size();
170 if (timeOffset >= mNumNoiseSamples) {
171 timeOffset = mNumNoiseSamples - 1;
172 LOG(
debug) <<
"timeOffset >= mNumNoiseSamples";
174 if (timeOffset <= -mNumNoiseSamples) {
175 timeOffset = -mNumNoiseSamples + 1;
176 LOG(
debug) <<
"timeOffset <= -mNumNoiseSamples";
178 Vc::float_v noiseVal(0);
179 const float* np = mNoiseSamples.data();
180 tp = mSincTable[timeIndex].data() + mNumNoiseSamples - timeOffset;
182 m = mNumNoiseSamples / Vc::float_v::Size;
183 for (
size_t i = 0;
i <
m; ++
i) {
185 tp += Vc::float_v::Size;
186 Vc::prefetchForOneRead(tp);
188 np += Vc::float_v::Size;
189 Vc::prefetchForOneRead(np);
190 acc += noiseVal * tableVal;
194 for (
size_t i = Vc::float_v::Size *
m;
i < mNumNoiseSamples; ++
i, ++tp, ++np) {
195 val += (*np) * (*tp);
199 auto const min_time = std::max(deadTime, *std::min_element(std::begin(
times),
202 bool is_positive =
true;
205 std::fill_n(std::begin(mSignalCache), std::size(mSignalCache), -1.0f);
209 float const val = value_at(
time);
211 if (
index >= 0 &&
index < mSignalCache.size()) {
215 float val_prev = 0.0f;
217 val_prev = ((index_prev < 0 || index_prev >= mSignalCache.size() || mSignalCache[index_prev] < 0.0f)
219 : mSignalCache[index_prev]);
220 float const cfd_val = 5.0f * val_prev -
val;
221 if (std::abs(
val) >
params.mCFD_trsh && !is_positive && cfd_val > 0.0f) {
229 is_positive = cfd_val > 0.0f;
242 const float* tp =
times.data();
243 size_t const m =
times.size() / Vc::float_v::Size;
244 for (
size_t i = 0;
i <
m; ++
i) {
246 tp += Vc::float_v::Size;
247 Vc::prefetchForOneRead(tp);
252 for (
size_t i = Vc::float_v::Size *
m;
i <
times.size(); ++
i, ++tp) {
259 std::vector<o2::ft0::Digit>& digitsBC,
260 std::vector<o2::ft0::ChannelData>& digitsCh,
261 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
266 LOG(
debug) <<
" process firstBCinDeque " << firstBCinDeque <<
" mIntRecord " << mIntRecord;
267 if (firstBCinDeque != mIntRecord) {
272 for (
auto const& hit : *hits) {
273 if (hit.GetEnergyLoss() > 0) {
277 Int_t hit_ch = hit.GetDetectorID();
280 if (mDeadChannelMap && !mDeadChannelMap->
isChannelAlive(hit_ch)) {
286 Bool_t is_A_side = (hit_ch < 4 * mGeometry.
NCellsA);
291 Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC();
293 if (hit_time > 150) {
298 if (mCache.size() <= relBC.bc) {
299 mCache.resize(relBC.bc + 1);
301 mCache[relBC.bc].hits.emplace_back(
BCCache::particle{hit_ch, hit_time - relBC.bc2ns()});
303 Int_t parentID = hit.GetTrackID();
304 if (parentID != parent) {
305 mCache[relBC.bc].labels.emplace(parentID, mEventID, mSrcID, hit_ch);
311void Digitizer::storeBC(BCCache&
bc,
312 std::vector<o2::ft0::Digit>& digitsBC,
313 std::vector<o2::ft0::ChannelData>& digitsCh,
314 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
317 if (
bc.hits.empty()) {
321 static bool pmLutInitialized =
false;
322 static std::array<uint8_t, o2::ft0::Constants::sNCHANNELS_PM> mChID2PMhash{};
323 static std::map<uint8_t, bool> mMapPMhash2isAside;
325 if (!pmLutInitialized) {
326 std::map<std::string, uint8_t> mapFEE2hash;
330 auto lutSorted = lut;
331 std::sort(lutSorted.begin(), lutSorted.end(),
332 [](
const auto&
first,
const auto& second) { return first.mModuleName < second.mModuleName; });
335 for (
const auto& lutEntry : lutSorted) {
336 const auto& moduleName = lutEntry.mModuleName;
337 const auto& moduleType = lutEntry.mModuleType;
338 const auto& strChID = lutEntry.mChannelID;
340 auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos});
342 if (moduleName.find(
"PMA") != std::string::npos) {
343 mMapPMhash2isAside.insert({binPos,
true});
344 }
else if (moduleName.find(
"PMC") != std::string::npos) {
345 mMapPMhash2isAside.insert({binPos,
false});
350 if (std::regex_match(strChID, std::regex(
"^[0-9]{1,3}$"))) {
351 int chID = std::stoi(strChID);
353 mChID2PMhash[chID] = mapFEE2hash[moduleName];
355 LOG(fatal) <<
"Incorrect LUT entry: chID " << strChID <<
" | " << moduleName;
357 }
else if (moduleType !=
"TCM") {
358 LOG(fatal) <<
"Non-TCM module w/o numerical chID: chID " << strChID <<
" | " << moduleName;
360 tcmHash = mapFEE2hash[moduleName];
364 pmLutInitialized =
true;
367 int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0;
368 int summ_ampl_A = 0, summ_ampl_C = 0;
369 int sum_A_ampl = 0, sum_C_ampl = 0;
371 std::vector<int> sum_ampl_ipmt(nPMTs, 0);
373 std::map<uint8_t, int> mapPMhash2sumAmpl;
374 for (
const auto&
entry : mMapPMhash2isAside) {
375 mapPMhash2sumAmpl.insert({
entry.first, 0});
381 static bool pmGroupsInitialized =
false;
382 static std::vector<std::array<int, 4>> pmtChannelGroups;
383 if (!pmGroupsInitialized) {
384 std::unordered_map<uint8_t, std::vector<int>> tmpGroups;
386 tmpGroups[mChID2PMhash[
static_cast<uint8_t>(ch)]].push_back(ch);
389 for (
auto& [pmHash, chVec] : tmpGroups) {
390 std::sort(chVec.begin(), chVec.end());
391 if (chVec.size() % 4 != 0) {
392 LOG(fatal) <<
"PM hash " <<
int(pmHash) <<
" has " << chVec.size()
393 <<
" channels in LUT, expected multiplicity of 4";
395 for (
size_t i = 0;
i < chVec.size();
i += 4) {
396 std::array<int, 4> arr = {chVec[
i + 0], chVec[
i + 1], chVec[
i + 2], chVec[
i + 3]};
397 pmtChannelGroups.push_back(arr);
400 pmGroupsInitialized =
true;
403 int first = digitsCh.size(), nStored = 0;
404 auto& particles =
bc.hits;
405 std::sort(std::begin(particles), std::end(particles));
406 auto channel_end = particles.begin();
407 std::vector<float> channel_times;
408 std::vector<float> baseAmp(
params.mMCPs, 0.f);
409 std::vector<float> finalAmp(
params.mMCPs, 0.f);
410 std::vector<int> chTime(
params.mMCPs, -5000);
411 std::vector<int> chChain(
params.mMCPs, 0);
412 std::vector<bool> chValid(
params.mMCPs,
false);
414 static const std::array<std::array<int, 3>, 4> localNeighbours = {{{{1, 2, 3}},
420 for (Int_t ipmt = 0; ipmt <
params.mMCPs; ++ipmt) {
421 auto channel_begin = channel_end;
422 channel_end = std::find_if(channel_begin, particles.end(),
423 [ipmt](BCCache::particle
const& p) { return p.hit_ch != ipmt; });
425 if (channel_end - channel_begin <
params.mAmp_trsh) {
428 channel_times.resize(channel_end - channel_begin);
429 std::transform(channel_begin, channel_end, channel_times.begin(), [](BCCache::particle
const& p) { return p.hit_time; });
430 int chain = (std::rand() % 2) ? 1 : 0;
431 auto cfd =
get_time(channel_times, mDeadTimes[ipmt].intrec.bc2ns() -
432 firstBCinDeque.
bc2ns() +
433 mDeadTimes[ipmt].deadTime);
434 mDeadTimes[ipmt].intrec = firstBCinDeque;
435 mDeadTimes[ipmt].deadTime = cfd.deadTime;
445 int smeared_time = 1000. * (*cfd.particle -
params.mCfdShift) *
params.mChannelWidthInverse + miscalib;
446 bool is_time_in_signal_gate = (smeared_time > -
params.mTime_trg_gate && smeared_time <
params.mTime_trg_gate);
448 float amp = is_time_in_signal_gate ?
params.mMV_2_Nchannels *
charge : 0.f;
456 LOG(
debug) << mEventID <<
" bc " << firstBCinDeque.
bc <<
" orbit " << firstBCinDeque.
orbit
457 <<
", ipmt " << ipmt <<
", smeared_time " << smeared_time
458 <<
" nStored " << nStored <<
" offset " << miscalib
459 <<
" base amp " << amp;
460 if (is_time_in_signal_gate) {
466 finalAmp[ipmt] = amp;
467 chTime[ipmt] = smeared_time;
468 chChain[ipmt] =
chain;
469 chValid[ipmt] =
true;
472 for (
const auto&
channels : pmtChannelGroups) {
473 for (
int localIdx = 0; localIdx < 4; ++localIdx) {
475 if (!chValid[
src] || baseAmp[
src] <= 0.f) {
479 const int nb1 =
channels[localNeighbours[localIdx][0]];
480 const int nb2 =
channels[localNeighbours[localIdx][1]];
481 const int diag =
channels[localNeighbours[localIdx][2]];
483 const float directXtalk = baseAmp[
src] *
params.Cross_Talk_Frac;
484 const float diagXtalk = baseAmp[
src] * (
params.Cross_Talk_Frac / 3.f);
486 finalAmp[nb1] += directXtalk;
487 finalAmp[nb2] += directXtalk;
488 finalAmp[diag] += diagXtalk;
490 if (!chValid[nb1] && directXtalk >=
params.mAmpThresholdForCrossTalkDigit) {
492 chTime[nb1] = chTime[
src];
493 chChain[nb1] = chChain[
src];
496 if (!chValid[nb2] && directXtalk >=
params.mAmpThresholdForCrossTalkDigit) {
498 chTime[nb2] = chTime[
src];
499 chChain[nb2] = chChain[
src];
502 if (!chValid[diag] && diagXtalk >=
params.mAmpThresholdForCrossTalkDigit) {
503 chValid[diag] =
true;
504 chTime[diag] = chTime[
src];
505 chChain[diag] = chChain[
src];
510 for (Int_t ipmt = 0; ipmt <
params.mMCPs; ++ipmt) {
511 if (!chValid[ipmt]) {
515 float amp = finalAmp[ipmt];
519 const bool hasPrimarySignal = (baseAmp[ipmt] > 0.f);
520 const bool isCrossTalkOnly = (!hasPrimarySignal && amp > 0.f);
522 if (isCrossTalkOnly && amp <
params.mAmpThresholdForCrossTalkDigit) {
526 const int smeared_time = chTime[ipmt];
527 const int chain = chChain[ipmt];
528 const bool is_time_in_signal_gate = (smeared_time > -
params.mTime_trg_gate && smeared_time <
params.mTime_trg_gate);
531 mapPMhash2sumAmpl[mChID2PMhash[
static_cast<uint8_t>(ipmt)]] +=
static_cast<int>(amp);
534 digitsCh.emplace_back(ipmt, smeared_time,
int(amp),
chain);
537 Bool_t is_A_side = (ipmt < 4 * mGeometry.
NCellsA);
538 if (!is_time_in_signal_gate) {
542 sum_ampl_ipmt[ipmt] += amp;
547 mean_time_A += smeared_time;
551 mean_time_C += smeared_time;
555 for (
size_t i = 0;
i < sum_ampl_ipmt.size();
i++) {
556 sum_ampl_ipmt[
i] = sum_ampl_ipmt[
i] >> 3;
558 sum_A_ampl += sum_ampl_ipmt[
i];
560 sum_C_ampl += sum_ampl_ipmt[
i];
565 int sum_PM_ampl_debug = 0;
566 int sum_PM_ampl_A_debug = 0;
567 int sum_PM_ampl_C_debug = 0;
568 for (
const auto&
entry : mapPMhash2sumAmpl) {
569 int pmAmpl = (
entry.second >> 3);
570 sum_PM_ampl_debug += pmAmpl;
571 auto itSide = mMapPMhash2isAside.find(
entry.first);
572 if (itSide != mMapPMhash2isAside.end()) {
573 if (itSide->second) {
574 sum_PM_ampl_A_debug += pmAmpl;
576 sum_PM_ampl_C_debug += pmAmpl;
580 LOG(
debug) <<
"Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug
581 <<
" A-side=" << sum_PM_ampl_A_debug
582 <<
" C-side=" << sum_PM_ampl_C_debug;
584 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0;
587 is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 *
params.mtrg_central_trh;
588 is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 *
params.mtrg_semicentral_trh && !is_Central;
589 uint32_t
amplA = is_A ? summ_ampl_A * 0.125 : -5000;
590 uint32_t
amplC = is_C ? summ_ampl_C * 0.125 : -5000;
591 int timeA = is_A ? mean_time_A / n_hit_A : -5000;
592 int timeC = is_C ? mean_time_C / n_hit_C : -5000;
594 isVertex = is_A && is_C && (vertex_time > -
params.mTime_trg_vertex_gate && vertex_time <
params.mTime_trg_vertex_gate);
595 LOG(
debug) <<
" A " << is_A <<
" timeA " <<
timeA <<
" mean_time_A " << mean_time_A <<
" n_hit_A " << n_hit_A <<
" C " << is_C <<
" timeC " <<
timeC <<
" mean_time_C " << mean_time_C <<
" n_hit_C " << n_hit_C <<
" vertex_time " << vertex_time;
597 bool isLaser =
false;
598 bool isOutputsAreBlocked =
false;
599 bool isDataValid =
true;
601 triggers.
setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C),
603 digitsBC.emplace_back(
first, nStored, firstBCinDeque, triggers, mEventID - 1);
604 digitsTrig.emplace_back(firstBCinDeque, is_A, is_C, isVertex, is_Central, is_SemiCentral);
605 size_t const nBC = digitsBC.size();
607 labels.addElement(nBC - 1, lbl);
612 LOG(
debug) <<
"Event ID: " << mEventID <<
", bc " << firstBCinDeque.
bc <<
", N hit " <<
bc.hits.size();
615 <<
" mean time C: " << triggers.
getTimeC() <<
" nStored " << nStored;
622 std::vector<o2::ft0::ChannelData>& digitsCh,
623 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
627 assert(firstBCinDeque <= mIntRecord);
629 while (firstBCinDeque < mIntRecord && !mCache.empty()) {
630 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig,
labels);
634 firstBCinDeque = mIntRecord;
638 std::vector<o2::ft0::ChannelData>& digitsCh,
639 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
643 assert(firstBCinDeque <= mIntRecord);
645 while (!mCache.empty()) {
646 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig,
labels);
654 auto const sinc = [](
double x) {
x *= TMath::Pi();
return (std::abs(
x) < 1e-12) ? 1.0 : std::sin(
x) /
x; };
658 mNumNoiseSamples = std::ceil(
params.mBunchWidth /
params.mNoisePeriod);
659 mNoiseSamples.resize(mNumNoiseSamples);
662 for (
size_t i = 0,
n = mSincTable.size();
i <
n; ++
i) {
666 mSincTable[
i].resize(3 * mNumNoiseSamples);
667 for (
int j = -mNumNoiseSamples;
j < 2 * mNumNoiseSamples; ++
j) {
668 mSincTable[
i][mNumNoiseSamples +
j] =
params.mNoiseVar * sinc((
time + 0.5f *
params.mBunchWidth) /
params.mNoisePeriod -
j);
683 LOG(info) <<
" @@@ Digitizer::init " << std::endl;
697 LOG(info) <<
" Run Digitzation with parametrs: \n"
698 <<
" CFD amplitude threshold \n " <<
params.mCFD_trsh <<
" CFD signal gate in ps \n"
699 <<
params.mTime_trg_gate <<
"shift to have signal around zero after CFD trancformation \n"
700 <<
params.mCfdShift <<
"CFD distance between 0.3 of max amplitude to max \n"
701 <<
params.mCFDShiftPos <<
"MIP -> mV " <<
params.mMip_in_V <<
" Pe in MIP \n"
702 <<
params.mPe_in_mip <<
"noise level " <<
params.mNoiseVar <<
" noise frequency \n"
std::vector< std::string > labels
std::vector< unsigned long > times
#define O2ParamImpl(classname)
uint64_t exp(uint64_t base, uint8_t exp) noexcept
General constants in FT0.
Configurable digitization parameters.
constexpr int p1()
constexpr to accelerate the coordinates changing
Definition of a container to keep Monte Carlo truth external to simulation objects.
Header to collect physics constants.
static const FT0DigParam & Instance()
static SingleLUT & Instance(const Table_t *table=nullptr, long timestamp=-1)
uint8_t getNChanC() const
void setTriggers(uint8_t trgsig, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC)
uint8_t getNChanA() const
void flush(std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
void printParameters() const
CFDOutput get_time(const std::vector< float > ×, float deadTime)
VcType signalFormVc(VcType x) const
void process(const std::vector< o2::ft0::HitType > *hits, std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
void flush_all(std::vector< o2::ft0::Digit > &digitsBC, std::vector< o2::ft0::ChannelData > &digitsCh, std::vector< o2::ft0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::ft0::MCLabel > &label)
double measure_amplitude(const std::vector< float > ×) const
float signalForm(float x) const
static constexpr int NCellsA
static constexpr int NCellsC
GLuint GLfloat GLfloat GLfloat x1
GLboolean GLboolean GLboolean b
GLenum const GLfloat * params
GLuint GLsizei const GLchar * label
GLboolean GLboolean GLboolean GLboolean a
uint8_t itsSharedClusterMap uint8_t
constexpr float LightSpeedCm2NS
Float signalForm_i(Float x)
Vc::float_v signalForm_integralVc(Vc::float_v x)
float signalForm_integral(float x)
uint16_t bc
bunch crossing ID of interaction
static double bc2ns(int bc, unsigned int orbit)
const bool isChannelAlive(const uint8_t &chId) const
static constexpr std::size_t sNCHANNELS_PM
static constexpr double SIGNAL_CACHE_DT
static constexpr int SIGNAL_TABLE_SIZE
std::array< int16_t, o2::ft0::Geometry::Nchannels > mTimeOffsets
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< ChannelData > channels