19#include "TF1Convolution.h"
30 for (
auto& analogSignal : mPmtChargeVsTime) {
31 std::fill_n(std::begin(analogSignal), analogSignal.size(), 0);
34 mCfdStartIndex.fill(0);
46 mPmtChargeVsTime[detID].resize(mNBins);
51 TF1Convolution convolutionRingA1ToA4(
"expo",
"landau", 5.e-09, 90.e-09,
false);
52 TF1 convolutionRingA1ToA4Fn(
"convolutionFn", convolutionRingA1ToA4, 5.e-09, 90.e-09, convolutionRingA1ToA4.GetNpar());
57 TF1Convolution convolutionRing5(
"expo",
"landau", 5.e-09, 90.e-09,
false);
58 TF1 convolutionRing5Fn(
"convolutionFn", convolutionRing5, 5.e-09, 90.e-09, convolutionRing5.GetNpar());
62 mPmtResponseGlobalRingA1ToA4.resize(mNBins);
63 const float binSizeInNs = mBinSize * 1.e-09;
64 double x = (binSizeInNs) / 2.0;
65 for (
auto&
y : mPmtResponseGlobalRingA1ToA4) {
71 mPmtResponseGlobalRing5.resize(mNBins);
72 x = (binSizeInNs) / 2.0;
73 for (
auto&
y : mPmtResponseGlobalRing5) {
79 mCfdStartIndex.fill(0);
80 LOG(info) <<
"init -> finished";
84 std::vector<o2::fv0::Digit>& digitsBC,
85 std::vector<o2::fv0::ChannelData>& digitsCh,
86 std::vector<o2::fv0::DetTrigInput>& digitsTrig,
89 LOG(
debug) <<
"Begin with " << hits.size() <<
" hits";
92 std::vector<int> hitIdx(hits.size());
93 std::iota(std::begin(hitIdx), std::end(hitIdx), 0);
94 std::sort(std::begin(hitIdx), std::end(hitIdx),
95 [&hits](
int a,
int b) {
return hits[
a].GetTrackID() < hits[
b].GetTrackID(); });
98 for (
auto ids : hitIdx) {
99 const auto& hit = hits[
ids];
100 Int_t detId = hit.GetDetectorID();
106 Double_t hitEdep = hit.GetHitValue() * 1e3;
107 Float_t const hitTime = hit.GetTime() * 1e9;
112 float distanceFromXc = 0;
114 distanceFromXc = getDistFromCellCenter(detId, hit.GetX(), hit.GetY());
117 int iChannelPerCell = 0;
118 while (iChannelPerCell < 2) {
122 if (iChannelPerCell == 1) {
126 hitEdep = (hit.GetHitValue() * 1e3) * getSignalFraction(distanceFromXc, iChannelPerCell == 0);
133 float const nPhE = SimulateLightYield(detId, nPhotons);
135 Long64_t timeHit = hitTime;
138 std::array<o2::InteractionRecord, NBC2Cache> cachedIR;
140 for (
int i = BCCacheMin;
i < BCCacheMax + 1;
i++) {
142 cachedIR[nCachedIR].setFromNS(tNS);
143 if (tNS < 0 && cachedIR[nCachedIR] > irHit) {
147 setBCCache(cachedIR[nCachedIR++]);
150 createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId);
156void Digitizer::createPulse(
float mipFraction,
int parID,
const double hitTime,
const float hitR,
157 std::array<o2::InteractionRecord, NBC2Cache>
const& cachedIR,
int nCachedIR,
const int detId)
160 std::array<bool, NBC2Cache> added;
163 for (
int ir = 0;
ir < NBC2Cache;
ir++) {
164 auto bcCache = getBCCache(cachedIR[
ir]);
166 (*bcCache).mPmtChargeVsTime[ich].resize(mNTimeBinsPerBC);
176 if (isRing5(detId)) {
177 std::memcpy(&mPmtResponseTemp[NBinShift], &mPmtResponseGlobalRing5[0],
180 std::memcpy(&mPmtResponseTemp[NBinShift], &mPmtResponseGlobalRingA1ToA4[0],
184 if (isRing5(detId)) {
185 mPmtResponseTemp = mPmtResponseGlobalRing5;
186 mPmtResponseTemp.erase(mPmtResponseTemp.begin(), mPmtResponseTemp.begin() + abs(NBinShift));
188 mPmtResponseTemp = mPmtResponseGlobalRingA1ToA4;
189 mPmtResponseTemp.erase(mPmtResponseTemp.begin(), mPmtResponseTemp.begin() + abs(NBinShift));
195 for (
int ir = 0;
ir <
int(mPmtResponseTemp.size() / mNTimeBinsPerBC);
ir++) {
196 auto bcCache = getBCCache(cachedIR[
ir]);
198 for (
int iBin = 0; iBin < mNTimeBinsPerBC; iBin++) {
199 (*bcCache).mPmtChargeVsTime[detId][iBin] += (mPmtResponseTemp[
ir * mNTimeBinsPerBC + iBin] * mipFraction);
204 for (
int ir = 0;
ir < nCachedIR;
ir++) {
206 auto bcCache = getBCCache(cachedIR[
ir]);
207 (*bcCache).labels.emplace_back(parID, mEventId, mSrcId, detId);
213 std::vector<o2::fv0::ChannelData>& digitsCh,
214 std::vector<o2::fv0::DetTrigInput>& digitsTrig,
218 while (!mCache.empty()) {
219 auto const&
bc = mCache.front();
221 storeBC(
bc, digitsBC, digitsCh, digitsTrig,
labels);
229void Digitizer::storeBC(
const BCCache&
bc,
230 std::vector<o2::fv0::Digit>& digitsBC,
231 std::vector<o2::fv0::ChannelData>& digitsCh,
232 std::vector<o2::fv0::DetTrigInput>& digitsTrig,
236 size_t const nBC = digitsBC.size();
237 size_t const first = digitsCh.size();
238 int8_t nTotFiredCells = 0;
239 int8_t nTrgFiredCells = 0;
240 int totalChargeAllRing = 0;
242 double nSignalInner = 0;
243 double nSignalOuter = 0;
246 mLastBCCache.
clear();
247 mCfdStartIndex.fill(0);
252 double cfdWithOffset = SimulateTimeCfd(mCfdStartIndex[iPmt], mLastBCCache.
mPmtChargeVsTime[iPmt],
bc.mPmtChargeVsTime[iPmt]);
264 if (std::rand() % 2) {
277 digitsCh.emplace_back(iPmt, iCfdZero, iTotalCharge, channelBits);
281 if (std::abs(iCfdZero) < triggerGate) {
284 totalChargeAllRing += iTotalCharge;
295 if (nTotFiredCells < 1) {
298 if (nTrgFiredCells > 0) {
299 avgTime /= nTrgFiredCells;
304 bool isA, isAIn, isAOut, isCen, isSCen;
305 isA = nTrgFiredCells > 0;
306 isAIn = nSignalInner > 0;
307 isAOut = nSignalOuter > 0;
315 const bool unusedBitsInSim =
false;
316 const bool bitDataIsValid =
true;
317 triggers.
setTriggers(isA, isAIn, isAOut, isCen, isSCen, nTrgFiredCells, (int8_t)unusedZero,
318 (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid);
319 digitsBC.emplace_back(
first, nTotFiredCells,
bc, triggers, mEventId - 1);
320 digitsTrig.emplace_back(
bc, isA, isAIn, isAOut, isCen, isSCen);
322 labels.addElement(nBC, lbl);
330Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot)
const
332 const Float_t epsilon = 0.0001f;
334 if ((fabs(1.0f - p) < epsilon) || nPhot == 0) {
337 const Int_t
n = Int_t(nPhot < 100
338 ? gRandom->Binomial(nPhot, p)
339 : gRandom->Gaus((
p * nPhot) + 0.5, TMath::
Sqrt(
p * (1. -
p) * nPhot)));
343Float_t Digitizer::IntegrateCharge(
const ChannelDigitF& pulse)
const
347 if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) {
348 LOG(fatal) <<
"invalid indicess: chargeInMin=" << chargeIntMin <<
" chargeIntMax=" << chargeIntMax;
351 for (
int iTimeBin = chargeIntMin; iTimeBin < chargeIntMax; iTimeBin++) {
352 totalCharge += pulse[iTimeBin];
357Float_t Digitizer::SimulateTimeCfd(
int& startIndex,
const ChannelDigitF& pulseLast,
const ChannelDigitF& pulse)
const
369 Float_t sigPrev = 5 * pulseLast[mNTimeBinsPerBC - binShift - 1] - pulseLast[mNTimeBinsPerBC - 1];
370 for (Int_t iTimeBin = 0; iTimeBin < mNTimeBinsPerBC; ++iTimeBin) {
371 Float_t const sigCurrent = 5.0f * (iTimeBin >= binShift ? pulse[iTimeBin - binShift] : pulseLast[mNTimeBinsPerBC - binShift + iTimeBin]) - pulse[iTimeBin];
372 if (iTimeBin >= startIndex && std::abs(pulse[iTimeBin]) > cfdThrInCoulomb) {
373 if (sigPrev < 0.0f && sigCurrent >= 0.0f) {
374 timeCfd =
Float_t(iTimeBin) * mBinSize;
376 if (startIndex < mNTimeBinsPerBC) {
379 startIndex -= mNTimeBinsPerBC;
381 if (startIndex > mNTimeBinsPerBC) {
382 LOG(fatal) <<
"CFD dead-time was set to > 25 ns";
387 sigPrev = sigCurrent;
392float Digitizer::getDistFromCellCenter(UInt_t cellId,
double hitx,
double hity)
400 double a = -(
y0 - pCell->
y) / (
x0 - pCell->
x);
402 double c = -(
y0 -
a *
x0);
404 return (
a * hitx +
b * hity +
c) / TMath::Sqrt(
a *
a +
b *
b);
407float Digitizer::getSignalFraction(
float distanceFromXc,
bool isFirstChannel)
410 if (distanceFromXc > 0) {
411 return isFirstChannel ? fraction : (1. - fraction);
413 return isFirstChannel ? (1. - fraction) : fraction;
420 if (mCache.empty() || mCache.back() <
ir) {
421 mCache.emplace_back();
422 auto& cb = mCache.back();
426 if (mCache.front() >
ir) {
427 mCache.emplace_front();
428 auto& cb = mCache.front();
432 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
437 auto cbnew = mCache.emplace(cb);
442 return mCache.front();
448 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
456bool Digitizer::isRing5(
int detID)
std::vector< std::string > labels
#define O2ParamImpl(classname)
General constants in FV0.
Base definition of FV0 geometry.
ClassImp(o2::fv0::Digitizer)
Configurable digitization parameters.
static const FV0DigParam & Instance()
static const int16_t DEFAULT_AMP
static const int16_t DEFAULT_TIME
void setTriggers(uint8_t trgsig, uint8_t chanA, uint8_t chanC, int32_t aamplA, int32_t aamplC, int16_t atimeA, int16_t atimeC)
static const int16_t DEFAULT_ZERO
void flush(std::vector< o2::fv0::Digit > &digitsBC, std::vector< o2::fv0::ChannelData > &digitsCh, std::vector< o2::fv0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::fv0::MCLabel > &labels)
void process(const std::vector< o2::fv0::Hit > &hits, std::vector< o2::fv0::Digit > &digitsBC, std::vector< o2::fv0::ChannelData > &digitsCh, std::vector< o2::fv0::DetTrigInput > &digitsTrig, o2::dataformats::MCTruthContainer< o2::fv0::MCLabel > &labels)
static Geometry * instance(EGeoType initType=eUninitialized)
Point3Dsimple & getCellCenter(UInt_t cellId)
bool isRing5(UInt_t cellId)
void getGlobalPosition(float &x, float &y, float &z)
Utility functions to be accessed externally.
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
GLuint GLfloat GLfloat y0
constexpr double LHCBunchSpacingNS
constexpr float LightSpeedCm2NS
float sigmoidPmtRing5(float x)
int64_t differenceInBC(const InteractionRecord &other) const
double getTimeNS() const
get time in ns from orbit=0/bc=0
const bool isChannelAlive(const uint8_t &chId) const
void setFlag(uint8_t flag)
static constexpr int nFv0Channels
static constexpr float N_PHOTONS_PER_MEV
static constexpr float INV_CHARGE_PER_ADC
static constexpr float INV_TIME_PER_TDCCHANNEL
std::array< ChannelDigitF, Constants::nFv0Channels > mPmtChargeVsTime
float singleHitTimeThreshold
float getNormRingA1ToA4() const
float getNormRing5() const
float photoCathodeEfficiency
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)