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;
241 int totalChargeInnerRing = 0;
242 int totalChargeOuterRing = 0;
244 double nSignalInner = 0;
245 double nSignalOuter = 0;
248 mLastBCCache.
clear();
249 mCfdStartIndex.fill(0);
254 double cfdWithOffset = SimulateTimeCfd(mCfdStartIndex[iPmt], mLastBCCache.
mPmtChargeVsTime[iPmt],
bc.mPmtChargeVsTime[iPmt]);
266 if (std::rand() % 2) {
279 digitsCh.emplace_back(iPmt, iCfdZero, iTotalCharge, channelBits);
283 if (std::abs(iCfdZero) < triggerGate) {
286 totalChargeAllRing += iTotalCharge;
290 totalChargeInnerRing += iTotalCharge;
293 totalChargeOuterRing += iTotalCharge;
299 if (nTotFiredCells < 1) {
302 if (nTrgFiredCells > 0) {
303 avgTime /= nTrgFiredCells;
308 bool isA, isNchannels, isAIn, isAOut, isTotalCharge;
309 isA = nTrgFiredCells > 0;
321 const bool unusedBitsInSim =
false;
322 const bool bitDataIsValid =
true;
323 triggers.
setTriggers(isA, isAIn, isAOut, isTotalCharge, isNchannels, nTrgFiredCells, (int8_t)unusedZero,
324 (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid);
325 digitsBC.emplace_back(
first, nTotFiredCells,
bc, triggers, mEventId - 1);
326 digitsTrig.emplace_back(
bc, isA, isAIn, isAOut, isTotalCharge, isNchannels);
328 labels.addElement(nBC, lbl);
336Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot)
const
338 const Float_t epsilon = 0.0001f;
340 if ((fabs(1.0f - p) < epsilon) || nPhot == 0) {
343 const Int_t
n = Int_t(nPhot < 100
344 ? gRandom->Binomial(nPhot, p)
345 : gRandom->Gaus((
p * nPhot) + 0.5, TMath::
Sqrt(
p * (1. -
p) * nPhot)));
349Float_t Digitizer::IntegrateCharge(
const ChannelDigitF& pulse)
const
353 if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) {
354 LOG(fatal) <<
"invalid indicess: chargeInMin=" << chargeIntMin <<
" chargeIntMax=" << chargeIntMax;
357 for (
int iTimeBin = chargeIntMin; iTimeBin < chargeIntMax; iTimeBin++) {
358 totalCharge += pulse[iTimeBin];
363Float_t Digitizer::SimulateTimeCfd(
int& startIndex,
const ChannelDigitF& pulseLast,
const ChannelDigitF& pulse)
const
375 Float_t sigPrev = 5 * pulseLast[mNTimeBinsPerBC - binShift - 1] - pulseLast[mNTimeBinsPerBC - 1];
376 for (Int_t iTimeBin = 0; iTimeBin < mNTimeBinsPerBC; ++iTimeBin) {
377 Float_t const sigCurrent = 5.0f * (iTimeBin >= binShift ? pulse[iTimeBin - binShift] : pulseLast[mNTimeBinsPerBC - binShift + iTimeBin]) - pulse[iTimeBin];
378 if (iTimeBin >= startIndex && std::abs(pulse[iTimeBin]) > cfdThrInCoulomb) {
379 if (sigPrev < 0.0f && sigCurrent >= 0.0f) {
380 timeCfd =
Float_t(iTimeBin) * mBinSize;
382 if (startIndex < mNTimeBinsPerBC) {
385 startIndex -= mNTimeBinsPerBC;
387 if (startIndex > mNTimeBinsPerBC) {
388 LOG(fatal) <<
"CFD dead-time was set to > 25 ns";
393 sigPrev = sigCurrent;
398float Digitizer::getDistFromCellCenter(UInt_t cellId,
double hitx,
double hity)
406 double a = -(
y0 - pCell->
y) / (
x0 - pCell->
x);
408 double c = -(
y0 -
a *
x0);
410 return (
a * hitx +
b * hity +
c) / TMath::Sqrt(
a *
a +
b *
b);
413float Digitizer::getSignalFraction(
float distanceFromXc,
bool isFirstChannel)
416 if (distanceFromXc > 0) {
417 return isFirstChannel ? fraction : (1. - fraction);
419 return isFirstChannel ? (1. - fraction) : fraction;
426 if (mCache.empty() || mCache.back() <
ir) {
427 mCache.emplace_back();
428 auto& cb = mCache.back();
432 if (mCache.front() >
ir) {
433 mCache.emplace_front();
434 auto& cb = mCache.front();
438 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
443 auto cbnew = mCache.emplace(cb);
448 return mCache.front();
454 for (
auto cb = mCache.begin(); cb != mCache.end(); cb++) {
462bool 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)