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";
90 flush(digitsBC, digitsCh, digitsTrig, labels);
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();
101 Double_t hitEdep = hit.GetHitValue() * 1e3;
102 Float_t const hitTime = hit.GetTime() * 1e9;
107 float distanceFromXc = 0;
109 distanceFromXc = getDistFromCellCenter(detId, hit.GetX(), hit.GetY());
112 int iChannelPerCell = 0;
113 while (iChannelPerCell < 2) {
117 if (iChannelPerCell == 1) {
121 hitEdep = (hit.GetHitValue() * 1e3) * getSignalFraction(distanceFromXc, iChannelPerCell == 0);
128 float const nPhE = SimulateLightYield(detId, nPhotons);
130 Long64_t timeHit = hitTime;
133 std::array<o2::InteractionRecord, NBC2Cache> cachedIR;
135 for (
int i = BCCacheMin;
i < BCCacheMax + 1;
i++) {
137 cachedIR[nCachedIR].setFromNS(tNS);
138 if (tNS < 0 && cachedIR[nCachedIR] > irHit) {
142 setBCCache(cachedIR[nCachedIR++]);
145 createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId);
151void Digitizer::createPulse(
float mipFraction,
int parID,
const double hitTime,
const float hitR,
152 std::array<o2::InteractionRecord, NBC2Cache>
const& cachedIR,
int nCachedIR,
const int detId)
155 std::array<bool, NBC2Cache> added;
158 for (
int ir = 0;
ir < NBC2Cache;
ir++) {
159 auto bcCache = getBCCache(cachedIR[
ir]);
161 (*bcCache).mPmtChargeVsTime[ich].resize(mNTimeBinsPerBC);
171 if (isRing5(detId)) {
172 std::memcpy(&mPmtResponseTemp[NBinShift], &mPmtResponseGlobalRing5[0],
175 std::memcpy(&mPmtResponseTemp[NBinShift], &mPmtResponseGlobalRingA1ToA4[0],
179 if (isRing5(detId)) {
180 mPmtResponseTemp = mPmtResponseGlobalRing5;
181 mPmtResponseTemp.erase(mPmtResponseTemp.begin(), mPmtResponseTemp.begin() + abs(NBinShift));
183 mPmtResponseTemp = mPmtResponseGlobalRingA1ToA4;
184 mPmtResponseTemp.erase(mPmtResponseTemp.begin(), mPmtResponseTemp.begin() + abs(NBinShift));
190 for (
int ir = 0;
ir <
int(mPmtResponseTemp.size() / mNTimeBinsPerBC);
ir++) {
191 auto bcCache = getBCCache(cachedIR[
ir]);
193 for (
int iBin = 0; iBin < mNTimeBinsPerBC; iBin++) {
194 (*bcCache).mPmtChargeVsTime[detId][iBin] += (mPmtResponseTemp[
ir * mNTimeBinsPerBC + iBin] * mipFraction);
199 for (
int ir = 0;
ir < nCachedIR;
ir++) {
201 auto bcCache = getBCCache(cachedIR[
ir]);
202 (*bcCache).labels.emplace_back(parID, mEventId, mSrcId, detId);
208 std::vector<o2::fv0::ChannelData>& digitsCh,
209 std::vector<o2::fv0::DetTrigInput>& digitsTrig,
213 while (!mCache.empty()) {
214 auto const&
bc = mCache.front();
216 storeBC(
bc, digitsBC, digitsCh, digitsTrig, labels);
224void Digitizer::storeBC(
const BCCache&
bc,
225 std::vector<o2::fv0::Digit>& digitsBC,
226 std::vector<o2::fv0::ChannelData>& digitsCh,
227 std::vector<o2::fv0::DetTrigInput>& digitsTrig,
231 size_t const nBC = digitsBC.size();
232 size_t const first = digitsCh.size();
233 int8_t nTotFiredCells = 0;
234 int8_t nTrgFiredCells = 0;
235 int totalChargeAllRing = 0;
237 double nSignalInner = 0;
238 double nSignalOuter = 0;
241 mLastBCCache.
clear();
242 mCfdStartIndex.fill(0);
247 double cfdWithOffset = SimulateTimeCfd(mCfdStartIndex[iPmt], mLastBCCache.
mPmtChargeVsTime[iPmt],
bc.mPmtChargeVsTime[iPmt]);
259 if (std::rand() % 2) {
272 digitsCh.emplace_back(iPmt, iCfdZero, iTotalCharge, channelBits);
276 if (std::abs(iCfdZero) < triggerGate) {
279 totalChargeAllRing += iTotalCharge;
290 if (nTotFiredCells < 1) {
293 if (nTrgFiredCells > 0) {
294 avgTime /= nTrgFiredCells;
299 bool isA, isAIn, isAOut, isCen, isSCen;
300 isA = nTrgFiredCells > 0;
301 isAIn = nSignalInner > 0;
302 isAOut = nSignalOuter > 0;
310 const bool unusedBitsInSim =
false;
311 const bool bitDataIsValid =
true;
312 triggers.
setTriggers(isA, isAIn, isAOut, isCen, isSCen, nTrgFiredCells, (int8_t)unusedZero,
313 (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid);
314 digitsBC.emplace_back(
first, nTotFiredCells,
bc, triggers, mEventId - 1);
315 digitsTrig.emplace_back(
bc, isA, isAIn, isAOut, isCen, isSCen);
316 for (
auto const& lbl :
bc.labels) {
325Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot)
const
327 const Float_t epsilon = 0.0001f;
329 if ((fabs(1.0f - p) < epsilon) || nPhot == 0) {
332 const Int_t
n = Int_t(nPhot < 100
333 ? gRandom->Binomial(nPhot, p)
334 : gRandom->Gaus((p * nPhot) + 0.5, TMath::
Sqrt(p * (1. - p) * nPhot)));
338Float_t Digitizer::IntegrateCharge(
const ChannelDigitF& pulse)
const
342 if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) {
343 LOG(fatal) <<
"invalid indicess: chargeInMin=" << chargeIntMin <<
" chargeIntMax=" << chargeIntMax;
346 for (
int iTimeBin = chargeIntMin; iTimeBin < chargeIntMax; iTimeBin++) {
347 totalCharge += pulse[iTimeBin];
352Float_t Digitizer::SimulateTimeCfd(
int& startIndex,
const ChannelDigitF& pulseLast,
const ChannelDigitF& pulse)
const
364 Float_t sigPrev = 5 * pulseLast[mNTimeBinsPerBC - binShift - 1] - pulseLast[mNTimeBinsPerBC - 1];
365 for (Int_t iTimeBin = 0; iTimeBin < mNTimeBinsPerBC; ++iTimeBin) {
366 Float_t const sigCurrent = 5.0f * (iTimeBin >= binShift ? pulse[iTimeBin - binShift] : pulseLast[mNTimeBinsPerBC - binShift + iTimeBin]) - pulse[iTimeBin];
367 if (iTimeBin >= startIndex && std::abs(pulse[iTimeBin]) > cfdThrInCoulomb) {
368 if (sigPrev < 0.0f && sigCurrent >= 0.0f) {
369 timeCfd =
Float_t(iTimeBin) * mBinSize;
371 if (startIndex < mNTimeBinsPerBC) {
374 startIndex -= mNTimeBinsPerBC;
376 if (startIndex > mNTimeBinsPerBC) {
377 LOG(fatal) <<
"CFD dead-time was set to > 25 ns";
382 sigPrev = sigCurrent;
387float Digitizer::getDistFromCellCenter(UInt_t cellId,
double hitx,
double hity)
395 double a = -(
y0 - pCell->
y) / (
x0 - pCell->
x);
397 double c = -(
y0 -
a *
x0);
399 return (
a * hitx +
b * hity +
c) / TMath::Sqrt(
a *
a +
b *
b);
402float Digitizer::getSignalFraction(
float distanceFromXc,
bool isFirstChannel)
405 if (distanceFromXc > 0) {
406 return isFirstChannel ? fraction : (1. - fraction);
408 return isFirstChannel ? (1. - fraction) : fraction;
415 if (mCache.empty() || mCache.back() <
ir) {
416 mCache.emplace_back();
417 auto& cb = mCache.back();
421 if (mCache.front() >
ir) {
422 mCache.emplace_front();
423 auto& cb = mCache.front();
427 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
432 auto cbnew = mCache.emplace(cb);
437 return mCache.front();
443 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
451bool Digitizer::isRing5(
int detID)
#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
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)