42template <
typename Float>
45 float p0,
p1,
p2, p3, p4, p5, p6, p7;
59 Double_t arg1 = (log(
x1) - p0) /
p1;
60 val +=
p2 * (1.0 / (
x1 *
p1 * sqrt(2 * TMath::Pi()))) *
exp(-0.5 * arg1 * arg1);
65 Double_t arg2 = (log(x2) - p4) / p5;
66 val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) *
exp(-0.5 * arg2 * arg2);
75 float p0,
p1,
p2, p3, p4, p5, p6, p7;
88 Double_t z1 = (log(
x1) - p0) / (sqrt(2) *
p1);
89 val +=
p2 * 0.5 * (1 + TMath::Erf(z1));
94 Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5);
95 val += p6 * 0.5 * (1 + TMath::Erf(z2));
126 auto const mask = (
x >= 0.0f);
129 Vc::float_v
const a(-0.45458f);
130 Vc::float_v
const b(-0.83344945f);
131 Vc::float_v
result = -(Vc::exp(
b * arg) /
b - Vc::exp(
a * arg) /
a) / 7.8446501f;
138 assert(std::is_sorted(std::begin(
times), std::end(
times)));
141 for (
float&
n : mNoiseSamples) {
146 auto value_at = [&](
float time) {
150 Vc::float_v tableVal(0);
151 const float* tp =
times.data();
152 size_t m =
times.size() / Vc::float_v::size();
153 for (
size_t i = 0;
i <
m; ++
i) {
155 tp += Vc::float_v::size();
156 Vc::prefetchForOneRead(tp);
161 for (
size_t i = Vc::float_v::size() *
m;
i <
times.size(); ++
i, ++tp) {
167 int timeOffset = timeIndex / mSincTable.size();
168 timeIndex %= mSincTable.size();
169 if (timeOffset >= mNumNoiseSamples) {
170 timeOffset = mNumNoiseSamples - 1;
171 LOG(
debug) <<
"timeOffset >= mNumNoiseSamples";
173 if (timeOffset <= -mNumNoiseSamples) {
174 timeOffset = -mNumNoiseSamples + 1;
175 LOG(
debug) <<
"timeOffset <= -mNumNoiseSamples";
177 Vc::float_v noiseVal(0);
178 const float* np = mNoiseSamples.data();
179 tp = mSincTable[timeIndex].data() + mNumNoiseSamples - timeOffset;
181 m = mNumNoiseSamples / Vc::float_v::Size;
182 for (
size_t i = 0;
i <
m; ++
i) {
184 tp += Vc::float_v::Size;
185 Vc::prefetchForOneRead(tp);
187 np += Vc::float_v::Size;
188 Vc::prefetchForOneRead(np);
189 acc += noiseVal * tableVal;
193 for (
size_t i = Vc::float_v::Size *
m;
i < mNumNoiseSamples; ++
i, ++tp, ++np) {
194 val += (*np) * (*tp);
198 auto const min_time = std::max(deadTime, *std::min_element(std::begin(
times),
201 bool is_positive =
true;
204 std::fill_n(std::begin(mSignalCache), std::size(mSignalCache), -1.0f);
208 float const val = value_at(
time);
210 if (
index >= 0 &&
index < mSignalCache.size()) {
214 float val_prev = 0.0f;
216 val_prev = ((index_prev < 0 || index_prev >= mSignalCache.size() || mSignalCache[index_prev] < 0.0f)
218 : mSignalCache[index_prev]);
219 float const cfd_val = 5.0f * val_prev -
val;
220 if (std::abs(
val) >
params.mCFD_trsh && !is_positive && cfd_val > 0.0f) {
228 is_positive = cfd_val > 0.0f;
241 const float* tp =
times.data();
242 size_t const m =
times.size() / Vc::float_v::Size;
243 for (
size_t i = 0;
i <
m; ++
i) {
245 tp += Vc::float_v::Size;
246 Vc::prefetchForOneRead(tp);
251 for (
size_t i = Vc::float_v::Size *
m;
i <
times.size(); ++
i, ++tp) {
258 std::vector<o2::ft0::Digit>& digitsBC,
259 std::vector<o2::ft0::ChannelData>& digitsCh,
260 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
265 LOG(
debug) <<
" process firstBCinDeque " << firstBCinDeque <<
" mIntRecord " << mIntRecord;
266 if (firstBCinDeque != mIntRecord) {
271 for (
auto const& hit : *hits) {
272 if (hit.GetEnergyLoss() > 0) {
276 Int_t hit_ch = hit.GetDetectorID();
279 if (mDeadChannelMap && !mDeadChannelMap->
isChannelAlive(hit_ch)) {
285 Bool_t is_A_side = (hit_ch < 4 * mGeometry.
NCellsA);
290 Double_t hit_time = hit.GetTime() - timeOfFlight + timeOffset + mIntRecord.getTimeOffsetWrtBC();
292 if (hit_time > 150) {
297 if (mCache.size() <= relBC.bc) {
298 mCache.resize(relBC.bc + 1);
300 mCache[relBC.bc].hits.emplace_back(
BCCache::particle{hit_ch, hit_time - relBC.bc2ns()});
302 Int_t parentID = hit.GetTrackID();
303 if (parentID != parent) {
304 mCache[relBC.bc].labels.emplace(parentID, mEventID, mSrcID, hit_ch);
310void Digitizer::storeBC(BCCache&
bc,
311 std::vector<o2::ft0::Digit>& digitsBC,
312 std::vector<o2::ft0::ChannelData>& digitsCh,
313 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
316 if (
bc.hits.empty()) {
320 static bool pmLutInitialized =
false;
321 static std::array<uint8_t, o2::ft0::Constants::sNCHANNELS_PM> mChID2PMhash{};
322 static std::map<uint8_t, bool> mMapPMhash2isAside;
324 if (!pmLutInitialized) {
325 std::map<std::string, uint8_t> mapFEE2hash;
329 auto lutSorted = lut;
330 std::sort(lutSorted.begin(), lutSorted.end(),
331 [](
const auto&
first,
const auto& second) { return first.mModuleName < second.mModuleName; });
334 for (
const auto& lutEntry : lutSorted) {
335 const auto& moduleName = lutEntry.mModuleName;
336 const auto& moduleType = lutEntry.mModuleType;
337 const auto& strChID = lutEntry.mChannelID;
339 auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos});
341 if (moduleName.find(
"PMA") != std::string::npos) {
342 mMapPMhash2isAside.insert({binPos,
true});
343 }
else if (moduleName.find(
"PMC") != std::string::npos) {
344 mMapPMhash2isAside.insert({binPos,
false});
349 if (std::regex_match(strChID, std::regex(
"^[0-9]{1,3}$"))) {
350 int chID = std::stoi(strChID);
352 mChID2PMhash[chID] = mapFEE2hash[moduleName];
354 LOG(fatal) <<
"Incorrect LUT entry: chID " << strChID <<
" | " << moduleName;
356 }
else if (moduleType !=
"TCM") {
357 LOG(fatal) <<
"Non-TCM module w/o numerical chID: chID " << strChID <<
" | " << moduleName;
359 tcmHash = mapFEE2hash[moduleName];
363 pmLutInitialized =
true;
366 int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0;
367 int summ_ampl_A = 0, summ_ampl_C = 0;
368 int sum_A_ampl = 0, sum_C_ampl = 0;
370 std::vector<int> sum_ampl_ipmt(nPMTs, 0);
372 std::map<uint8_t, int> mapPMhash2sumAmpl;
373 for (
const auto&
entry : mMapPMhash2isAside) {
374 mapPMhash2sumAmpl.insert({
entry.first, 0});
379 int first = digitsCh.size(), nStored = 0;
380 auto& particles =
bc.hits;
381 std::sort(std::begin(particles), std::end(particles));
382 auto channel_end = particles.begin();
383 std::vector<float> channel_times;
384 for (Int_t ipmt = 0; ipmt <
params.mMCPs; ++ipmt) {
385 auto channel_begin = channel_end;
386 channel_end = std::find_if(channel_begin, particles.end(),
387 [ipmt](BCCache::particle
const& p) { return p.hit_ch != ipmt; });
391 if (channel_end - channel_begin <
params.mAmp_trsh) {
394 channel_times.resize(channel_end - channel_begin);
395 std::transform(channel_begin, channel_end, channel_times.begin(), [](BCCache::particle
const& p) { return p.hit_time; });
396 int chain = (std::rand() % 2) ? 1 : 0;
397 auto cfd =
get_time(channel_times, mDeadTimes[ipmt].intrec.bc2ns() -
398 firstBCinDeque.
bc2ns() +
399 mDeadTimes[ipmt].deadTime);
400 mDeadTimes[ipmt].intrec = firstBCinDeque;
401 mDeadTimes[ipmt].deadTime = cfd.deadTime;
411 int smeared_time = 1000. * (*cfd.particle -
params.mCfdShift) *
params.mChannelWidthInverse + miscalib;
412 bool is_time_in_signal_gate = (smeared_time > -
params.mTime_trg_gate && smeared_time <
params.mTime_trg_gate);
414 float amp = is_time_in_signal_gate ?
params.mMV_2_Nchannels *
charge : 0;
419 LOG(
debug) << mEventID <<
" bc " << firstBCinDeque.
bc <<
" orbit " << firstBCinDeque.
orbit <<
", ipmt " << ipmt <<
", smeared_time " << smeared_time <<
" nStored " << nStored <<
" offset " << miscalib;
420 if (is_time_in_signal_gate) {
425 mapPMhash2sumAmpl[mChID2PMhash[
static_cast<uint8_t>(ipmt)]] +=
static_cast<int>(amp);
428 digitsCh.emplace_back(ipmt, smeared_time,
int(amp),
chain);
433 Bool_t is_A_side = (ipmt < 4 * mGeometry.
NCellsA);
434 if (!is_time_in_signal_gate) {
438 sum_ampl_ipmt[ipmt] += amp;
443 mean_time_A += smeared_time;
447 mean_time_C += smeared_time;
451 for (
size_t i = 0;
i < sum_ampl_ipmt.size();
i++) {
452 sum_ampl_ipmt[
i] = sum_ampl_ipmt[
i] >> 3;
454 sum_A_ampl += sum_ampl_ipmt[
i];
456 sum_C_ampl += sum_ampl_ipmt[
i];
461 int sum_PM_ampl_debug = 0;
462 int sum_PM_ampl_A_debug = 0;
463 int sum_PM_ampl_C_debug = 0;
464 for (
const auto&
entry : mapPMhash2sumAmpl) {
465 int pmAmpl = (
entry.second >> 3);
466 sum_PM_ampl_debug += pmAmpl;
467 auto itSide = mMapPMhash2isAside.find(
entry.first);
468 if (itSide != mMapPMhash2isAside.end()) {
469 if (itSide->second) {
470 sum_PM_ampl_A_debug += pmAmpl;
472 sum_PM_ampl_C_debug += pmAmpl;
476 LOG(
debug) <<
"Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug
477 <<
" A-side=" << sum_PM_ampl_A_debug
478 <<
" C-side=" << sum_PM_ampl_C_debug;
480 Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0;
483 is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 *
params.mtrg_central_trh;
484 is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 *
params.mtrg_semicentral_trh && !is_Central;
485 uint32_t
amplA = is_A ? summ_ampl_A * 0.125 : -5000;
486 uint32_t
amplC = is_C ? summ_ampl_C * 0.125 : -5000;
487 int timeA = is_A ? mean_time_A / n_hit_A : -5000;
488 int timeC = is_C ? mean_time_C / n_hit_C : -5000;
490 isVertex = is_A && is_C && (vertex_time > -
params.mTime_trg_vertex_gate && vertex_time <
params.mTime_trg_vertex_gate);
491 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;
493 bool isLaser =
false;
494 bool isOutputsAreBlocked =
false;
495 bool isDataValid =
true;
497 triggers.
setTriggers(is_A, is_C, isVertex, is_Central, is_SemiCentral, int8_t(n_hit_A), int8_t(n_hit_C),
499 digitsBC.emplace_back(
first, nStored, firstBCinDeque, triggers, mEventID - 1);
500 digitsTrig.emplace_back(firstBCinDeque, is_A, is_C, isVertex, is_Central, is_SemiCentral);
501 size_t const nBC = digitsBC.size();
503 labels.addElement(nBC - 1, lbl);
508 LOG(
debug) <<
"Event ID: " << mEventID <<
", bc " << firstBCinDeque.
bc <<
", N hit " <<
bc.hits.size();
511 <<
" mean time C: " << triggers.
getTimeC() <<
" nStored " << nStored;
518 std::vector<o2::ft0::ChannelData>& digitsCh,
519 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
523 assert(firstBCinDeque <= mIntRecord);
525 while (firstBCinDeque < mIntRecord && !mCache.empty()) {
526 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig,
labels);
530 firstBCinDeque = mIntRecord;
534 std::vector<o2::ft0::ChannelData>& digitsCh,
535 std::vector<o2::ft0::DetTrigInput>& digitsTrig,
539 assert(firstBCinDeque <= mIntRecord);
541 while (!mCache.empty()) {
542 storeBC(mCache.front(), digitsBC, digitsCh, digitsTrig,
labels);
550 auto const sinc = [](
double x) {
x *= TMath::Pi();
return (std::abs(
x) < 1e-12) ? 1.0 : std::sin(
x) /
x; };
554 mNumNoiseSamples = std::ceil(
params.mBunchWidth /
params.mNoisePeriod);
555 mNoiseSamples.resize(mNumNoiseSamples);
558 for (
size_t i = 0,
n = mSincTable.size();
i <
n; ++
i) {
562 mSincTable[
i].resize(3 * mNumNoiseSamples);
563 for (
int j = -mNumNoiseSamples;
j < 2 * mNumNoiseSamples; ++
j) {
564 mSincTable[
i][mNumNoiseSamples +
j] =
params.mNoiseVar * sinc((
time + 0.5f *
params.mBunchWidth) /
params.mNoisePeriod -
j);
579 LOG(info) <<
" @@@ Digitizer::init " << std::endl;
593 LOG(info) <<
" Run Digitzation with parametrs: \n"
594 <<
" CFD amplitude threshold \n " <<
params.mCFD_trsh <<
" CFD signal gate in ps \n"
595 <<
params.mTime_trg_gate <<
"shift to have signal around zero after CFD trancformation \n"
596 <<
params.mCfdShift <<
"CFD distance between 0.3 of max amplitude to max \n"
597 <<
params.mCFDShiftPos <<
"MIP -> mV " <<
params.mMip_in_V <<
" Pe in MIP \n"
598 <<
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"