1414
1515#include " TMath.h"
1616#include " TRandom.h"
17- #include " MathUtils/CachingTF1.h"
1817#include < algorithm>
1918#include < cassert>
2019#include < iostream>
2120
21+ using namespace o2 ::math_utils;
2222using namespace o2 ::fdd;
2323
2424ClassImp (Digitizer);
@@ -39,9 +39,11 @@ void Digitizer::process(const std::vector<o2::fdd::Hit>* hits, o2::fdd::Digit* d
3939 for (int i = 0 ; i < parameters.mNchannels ; ++i)
4040 channel_data.emplace_back (o2::fdd::ChannelData{i, o2::InteractionRecord::DummyTime, -1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 });
4141 }
42+
43+ auto const roundVc = [&](int i) -> int {
44+ return (i / Vc::float_v::Size) * Vc::float_v::Size;
45+ };
4246 Int_t parent = -10 ;
43- Float_t integral = mPMResponse ->Integral (-parameters.mPMTransitTime , 2 . * parameters.mPMTransitTime );
44- Float_t meansPhE = mSinglePhESpectrum ->Mean (0 , 20 );
4547 for (Int_t i = 0 ; i < parameters.mNchannels ; i++)
4648 std::fill (mTime [i].begin (), mTime [i].end (), 0 );
4749
@@ -60,22 +62,43 @@ void Digitizer::process(const std::vector<o2::fdd::Hit>* hits, o2::fdd::Digit* d
6062 Int_t pmt = hit.GetDetectorID ();
6163 Int_t nPhE = SimulateLightYield (pmt, hit.GetNphot ());
6264
63- Float_t dt_scintillator = gRandom -> Gaus ( 0 , parameters. mIntTimeRes );
65+ Float_t dt_scintillator = mRndScintDelay . getNextValue ( );
6466 Float_t t = dt_scintillator + hit.GetTime ();
67+ // LOG(INFO) << "Nphot = " << hit.GetNphot() << " time =" << hit.GetTime();
68+ // LOG(INFO) << "NphE = " << nPhE;
69+ Float_t charge = TMath::Qe () * parameters.mPmGain * mBinSize / mPmtTimeIntegral ;
70+
71+ auto & analogSignal = mTime [pmt];
6572
66- // LOG(INFO) << "Nphot = "<<hit.GetNphot()<<" time ="<<hit.GetTime()<<FairLogger::endl;
67- // LOG(INFO) << "NphE = " << nPhE << FairLogger::endl;
68- Float_t charge = TMath::Qe () * parameters.mPmGain * mBinSize / integral;
6973 for (Int_t iPhE = 0 ; iPhE < nPhE; ++iPhE) {
70- Float_t tPhE = t + mSignalShape -> GetRandom ( 0 , mBinSize * Float_t ( mNBins ) );
74+ Float_t tPhE = t + mRndSignalShape . getNextValue ( );
7175 // LOG(INFO) <<"t = "<<t<<"tPhE = "<<tPhE;
72- Float_t gainVar = mSinglePhESpectrum ->GetRandom (0 , 20 ) / meansPhE;
73- Int_t firstBin = TMath::Max ((UInt_t)0 , (UInt_t)((tPhE - parameters.mPMTransitTime ) / mBinSize ));
74- Int_t lastBin = TMath::Min (mNBins - 1 , (UInt_t)((tPhE + 2 . * parameters.mPMTransitTime ) / mBinSize ));
75- // LOG(INFO) << "firstBin = "<<firstBin<<" lastbin "<<lastBin<<FairLogger::endl;
76- for (Int_t iBin = firstBin; iBin <= lastBin; ++iBin) {
77- Float_t tempT = mBinSize * (0.5 + iBin) - tPhE;
78- mTime [pmt][iBin] += gainVar * charge * mPMResponse ->Eval (tempT);
76+ Int_t const firstBin = roundVc (TMath::Max ((Int_t)0 , (Int_t)((tPhE - parameters.mPMTransitTime ) / mBinSize )));
77+ Int_t const lastBin = TMath::Min ((Int_t)mNBins - 1 , (Int_t)((tPhE + 2 . * parameters.mPMTransitTime ) / mBinSize ));
78+ // LOG(INFO) << "firstBin = "<<firstBin<<" lastbin "<<lastBin;
79+ Float_t const tempT = mBinSize * (0 .5f + firstBin) - tPhE;
80+ Float_t* p = analogSignal.data () + firstBin;
81+ long iStart = std::lround ((tempT + 2 .0f * parameters.mPMTransitTime ) / mBinSize );
82+ float const offset = tempT + 2 .0f * parameters.mPMTransitTime - Float_t (iStart) * mBinSize ;
83+ long const iOffset = std::lround (offset / mBinSize * Float_t (parameters.mNResponseTables - 1 ));
84+ if (iStart < 0 ) { // this should not happen
85+ LOG (ERROR ) << " FDDDigitizer: table lookup failure" ;
86+ }
87+ iStart = roundVc (std::max (long (0 ), iStart));
88+
89+ Vc::float_v workVc;
90+ Vc::float_v pmtVc;
91+ Float_t const * q = mPMResponseTables [parameters.mNResponseTables / 2 + iOffset].data () + iStart;
92+ Float_t const * qEnd = &mPMResponseTables [parameters.mNResponseTables / 2 + iOffset].back ();
93+ for (Int_t i = firstBin, iEnd = roundVc (lastBin); q < qEnd && i < iEnd; i += Vc::float_v::Size) {
94+ pmtVc.load (q);
95+ q += Vc::float_v::Size;
96+ Vc::prefetchForOneRead (q);
97+ workVc.load (p);
98+ workVc += mRndGainVar .getNextValueVc () * charge * pmtVc;
99+ workVc.store (p);
100+ p += Vc::float_v::Size;
101+ Vc::prefetchForOneRead (p);
79102 }
80103 }
81104 // MCLabels
@@ -93,7 +116,7 @@ void Digitizer::process(const std::vector<o2::fdd::Hit>* hits, o2::fdd::Digit* d
93116 channel_data[ipmt].mTime = SimulateTimeCFD (ipmt);
94117 for (Int_t iBin = 0 ; iBin < mNBins ; ++iBin)
95118 channel_data[ipmt].mChargeADC += mTime [ipmt][iBin] / parameters.mChargePerADC ;
96- // LOG(INFO) << "ADC " << channel_data[ipmt].mChargeADC << " Time " << channel_data[ipmt].mTime << FairLogger::endl ;
119+ // LOG(INFO) << "ADC " << channel_data[ipmt].mChargeADC << " Time " << channel_data[ipmt].mTime;
97120 }
98121}
99122// _____________________________________________________________________________
@@ -120,31 +143,6 @@ Float_t Digitizer::SimulateTimeCFD(Int_t channel)
120143 return timeCFD;
121144}
122145// _____________________________________________________________________________
123- Int_t Digitizer::SimulateLightYield (Int_t pmt, Int_t nPhot)
124- {
125- const Float_t p = parameters.mLightYield * parameters.mPhotoCathodeEfficiency ;
126- if (p == 1 .0f || nPhot == 0 )
127- return nPhot;
128- const Int_t n = Int_t (nPhot < 100 ? gRandom ->Binomial (nPhot, p) : gRandom ->Gaus (p * nPhot + 0.5 , TMath::Sqrt (p * (1 - p) * nPhot)));
129- return n;
130- }
131- // _____________________________________________________________________________
132- Double_t Digitizer::PMResponse (Double_t* x, Double_t*)
133- {
134- // this function describes the PM time response to a single photoelectron
135- Double_t y = x[0 ] + parameters.mPMTransitTime ;
136- return y * y * TMath::Exp (-y * y / (parameters.mPMTransitTime * parameters.mPMTransitTime ));
137- }
138- // _____________________________________________________________________________
139- Double_t Digitizer::SinglePhESpectrum (Double_t* x, Double_t*)
140- {
141- // this function describes the PM amplitude response to a single photoelectron
142- Double_t y = x[0 ];
143- if (y < 0 )
144- return 0 ;
145- return (TMath::Poisson (y, parameters.mPMNbOfSecElec ) + parameters.mPMTransparency * TMath::Poisson (y, 1.0 ));
146- }
147- // _____________________________________________________________________________
148146void Digitizer::SetTriggers (o2::fdd::Digit* digit)
149147{
150148}
@@ -156,21 +154,83 @@ void Digitizer::initParameters()
156154// _______________________________________________________________________
157155void Digitizer::init ()
158156{
157+ mEventTime = 0 ;
159158
160159 mNBins = 2000 ; // Will be computed using detector set-up from CDB
161160 mBinSize = 25.0 / 256.0 ; // Will be set-up from CDB
162161 for (Int_t i = 0 ; i < parameters.mNchannels ; i++)
163162 mTime [i].resize (mNBins );
164163 mTimeCFD .resize (mNBins );
165164
166- if (!mPMResponse )
167- mPMResponse = std::make_unique<o2::base::CachingTF1>(" mPMResponse" , this , &Digitizer::PMResponse, -parameters.mPMTransitTime , 2 . * parameters.mPMTransitTime , 0 );
168- if (!mSinglePhESpectrum )
169- mSinglePhESpectrum = std::make_unique<o2::base::CachingTF1>(" mSinglePhESpectrum" , this , &Digitizer::SinglePhESpectrum, 0 , 20 , 0 );
170- if (!mSignalShape ) {
171- mSignalShape = std::make_unique<o2::base::CachingTF1>(" mSignalShape" , " crystalball" , 0 , 300 );
172- mSignalShape ->SetParameters (1 , parameters.mShapeSigma , parameters.mShapeSigma , parameters.mShapeAlpha , parameters.mShapeN );
165+ auto const roundVc = [&](int i) -> int {
166+ return (i / Vc::float_v::Size) * Vc::float_v::Size;
167+ };
168+ // set up PMT response tables
169+ Float_t offset = -0 .5f * mBinSize ; // offset \in [-0.5..0.5] * mBinSize
170+ Int_t const nBins = roundVc (std::lround (4 .0f * parameters.mPMTransitTime / mBinSize ));
171+ for (auto & table : mPMResponseTables ) {
172+ table.resize (nBins);
173+ Float_t t = -2 .0f * parameters.mPMTransitTime + offset; // t \in offset + [-2 2] * DP::mPmtTransitTime
174+ for (Int_t j = 0 ; j < nBins; ++j) {
175+ table[j] = Digitizer::PMResponse (t);
176+ t += mBinSize ;
177+ }
178+ offset += mBinSize / Float_t (parameters.mNResponseTables - 1 );
173179 }
180+
181+ TF1 scintDelayFn (" fScintDelay" , " gaus" , -6 .0f * parameters.mIntTimeRes , +6 .0f * parameters.mIntTimeRes );
182+ scintDelayFn.SetParameters (1 , 0 , parameters.mIntTimeRes );
183+ mRndScintDelay .initialize (scintDelayFn);
184+
185+ // Initialize function describing the PMT time response
186+ TF1 pmtResponseFn (" mPmtResponseFn" , &Digitizer::PMResponse, -1 .0f * parameters.mPMTransitTime , +2 .0f * parameters.mPMTransitTime , 0 );
187+ pmtResponseFn.SetNpx (100 );
188+ mPmtTimeIntegral = pmtResponseFn.Integral (-1 .0f * parameters.mPMTransitTime , +2 .0f * parameters.mPMTransitTime );
189+
190+ // Initialize function describing PMT response to the single photoelectron
191+ TF1 singlePhESpectrumFn (" mSinglePhESpectrum" ,
192+ &Digitizer::SinglePhESpectrum, 0 , 30 , 0 );
193+ Float_t const meansPhE = singlePhESpectrumFn.Mean (0 , 30 );
194+ mRndGainVar .initialize ([&]() -> float {
195+ return singlePhESpectrumFn.GetRandom (0 , 30 ) / meansPhE;
196+ });
197+
198+ TF1 signalShapeFn (" signalShape" , " crystalball" , 0 , 300 );
199+ signalShapeFn.SetParameters (1 , parameters.mShapeSigma , parameters.mShapeSigma , parameters.mShapeAlpha , parameters.mShapeN );
200+ mRndSignalShape .initialize ([&]() -> float {
201+ return signalShapeFn.GetRandom (0 , mBinSize * Float_t (mNBins ));
202+ });
174203}
175204// _______________________________________________________________________
176205void Digitizer::finish () {}
206+
207+ // _____________________________________________________________________________
208+ Int_t Digitizer::SimulateLightYield (Int_t pmt, Int_t nPhot)
209+ {
210+ const Float_t p = parameters.mLightYield * parameters.mPhotoCathodeEfficiency ;
211+ if (p == 1 .0f || nPhot == 0 )
212+ return nPhot;
213+ const Int_t n = Int_t (nPhot < 100 ? gRandom ->Binomial (nPhot, p) : gRandom ->Gaus (p * nPhot + 0.5 , TMath::Sqrt (p * (1 - p) * nPhot)));
214+ return n;
215+ }
216+ // _____________________________________________________________________________
217+ Double_t Digitizer::PMResponse (Double_t* x, Double_t*)
218+ {
219+ return Digitizer::PMResponse (x[0 ]);
220+ }
221+ // _____________________________________________________________________________
222+ Double_t Digitizer::PMResponse (Double_t x)
223+ {
224+ // this function describes the PM time response to a single photoelectron
225+ Double_t y = x + DigitizationParameters::mPMTransitTime ;
226+ return y * y * TMath::Exp (-y * y / (DigitizationParameters::mPMTransitTime * DigitizationParameters::mPMTransitTime ));
227+ }
228+ // _____________________________________________________________________________
229+ Double_t Digitizer::SinglePhESpectrum (Double_t* x, Double_t*)
230+ {
231+ // this function describes the PM amplitude response to a single photoelectron
232+ Double_t y = x[0 ];
233+ if (y < 0 )
234+ return 0 ;
235+ return (TMath::Poisson (y, DigitizationParameters::mPMNbOfSecElec ) + DigitizationParameters::mPMTransparency * TMath::Poisson (y, 1.0 ));
236+ }
0 commit comments