Skip to content

Commit c036069

Browse files
authored
FDD: Optimization of the Digitizer (#3047)
* Optimization of the Digitizer (applying techniques already done for FV0)
1 parent 54f0de8 commit c036069

3 files changed

Lines changed: 157 additions & 80 deletions

File tree

Detectors/FIT/FDD/simulation/include/FDDSimulation/DigitizationParameters.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,24 @@
1414
namespace o2::fdd
1515
{
1616
struct DigitizationParameters {
17-
const UShort_t mNchannels = 16;
18-
const UShort_t mNtriggers = 5;
19-
const Float_t mIntTimeRes = 0.4;
20-
const Float_t mPhotoCathodeEfficiency = 0.18;
21-
const Float_t mLightYield = 0.01;
22-
const Float_t mPmGain = 1e6;
23-
const Float_t mChargePerADC = 0.6e-12;
24-
const Float_t mPMTransitTime = 6.0; // PM response time (corresponds to 1.9 ns rise time)
25-
const Float_t mPMTransparency = 0.25; // Transparency of the first dynode of the PM
26-
const Float_t mPMNbOfSecElec = 6.0; // Number of secondary electrons emitted from first dynode (per ph.e.)
27-
const Float_t mShapeAlpha = -0.445;
28-
const Float_t mShapeN = 2.65;
29-
const Float_t mShapeSigma = 3.25;
30-
//const Float_t mPedestal = 0;
31-
const Float_t mTimeShiftCFD = 1.42;
17+
static constexpr UShort_t mNchannels = 16;
18+
static constexpr UShort_t mNtriggers = 5;
19+
static constexpr Float_t mIntTimeRes = 0.4;
20+
static constexpr Float_t mPhotoCathodeEfficiency = 0.18;
21+
static constexpr Float_t mLightYield = 0.01;
22+
static constexpr Float_t mPmGain = 1e6;
23+
static constexpr Float_t mChargePerADC = 0.6e-12;
24+
static constexpr Float_t mPMTransitTime = 6.0; // PM response time (corresponds to 1.9 ns rise time)
25+
static constexpr Float_t mPMTransparency = 0.25; // Transparency of the first dynode of the PM
26+
static constexpr Float_t mPMNbOfSecElec = 6.0; // Number of secondary electrons emitted from first dynode (per ph.e.)
27+
static constexpr Float_t mShapeAlpha = -0.445;
28+
static constexpr Float_t mShapeN = 2.65;
29+
static constexpr Float_t mShapeSigma = 3.25;
30+
//static constexpr Float_t mPedestal = 0;
31+
static constexpr Float_t mTimeShiftCFD = 1.42;
32+
static constexpr int mPheRRSize = 1e5; // size of random ring to be used inside photoelectron loop
33+
static constexpr int mHitRRSize = 1e4; // size of random ring to be used inside hit loop
34+
static constexpr int mNResponseTables = 9; // number of PMT response tables
3235
};
3336
} // namespace o2::fdd
3437
#endif

Detectors/FIT/FDD/simulation/include/FDDSimulation/Digitizer.h

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include "SimulationDataFormat/MCTruthContainer.h"
1919
#include "SimulationDataFormat/MCCompLabel.h"
2020
#include "FDDSimulation/DigitizationParameters.h"
21+
#include "MathUtils/RandomRing.h"
2122
#include "MathUtils/CachingTF1.h"
2223

2324
namespace o2
@@ -26,14 +27,19 @@ namespace fdd
2627
{
2728
class Digitizer
2829
{
30+
31+
private:
32+
typedef math_utils::RandomRing<float_v::size() * DigitizationParameters::mPheRRSize> HitRandomRingType;
33+
typedef math_utils::RandomRing<float_v::size() * DigitizationParameters::mHitRRSize> PheRandomRingType;
34+
2935
public:
30-
Digitizer(const DigitizationParameters& params, Int_t mode = 0) : mMode(mode), parameters(params), mTime(16) { init(); };
36+
Digitizer(const DigitizationParameters& params, Int_t mode = 0) : mEventTime(0), mIntRecord(), mEventID(-1), mSrcID(-1), mMCLabels(), parameters(params), mTime(), mRndScintDelay(HitRandomRingType::RandomType::CustomTF1), mRndGainVar(PheRandomRingType::RandomType::CustomTF1), mRndSignalShape(PheRandomRingType::RandomType::CustomTF1), mPMResponseTables() { init(); };
3137
~Digitizer() = default;
3238

3339
void process(const std::vector<o2::fdd::Hit>* hits, o2::fdd::Digit* digit);
3440

3541
void initParameters();
36-
void SetEventTime(double value) { mEventTime = value; }
42+
void SetEventTime(long value) { mEventTime = value; }
3743
void SetEventID(Int_t id) { mEventID = id; }
3844
void SetSrcID(Int_t id) { mSrcID = id; }
3945
void SetInteractionRecord(uint16_t bc, uint32_t orbit)
@@ -50,33 +56,41 @@ class Digitizer
5056
void SetTriggers(o2::fdd::Digit* digit);
5157
Int_t SimulateLightYield(Int_t pmt, Int_t nPhot);
5258
Float_t SimulateTimeCFD(Int_t channel);
53-
Double_t PMResponse(Double_t* x, Double_t* par);
54-
Double_t SinglePhESpectrum(Double_t* x, Double_t* par);
5559

5660
void init();
5761
void finish();
5862

5963
void setMCLabels(o2::dataformats::MCTruthContainer<o2::fdd::MCLabel>* mclb) { mMCLabels = mclb; }
6064

6165
private:
62-
Int_t mMode; //triggered or continuos
63-
o2::InteractionRecord mIntRecord; // Interaction record (orbit, bc)
64-
Int_t mEventID;
65-
Int_t mSrcID; // signal, background or QED
66-
Double_t mEventTime; // timestamp
66+
long mEventTime; // TF (run) timestamp
67+
InteractionRecord mIntRecord; // Interaction record (orbit, bc) -> InteractionTimeRecord
68+
Int_t mEventID; // ID of the current event
69+
Int_t mSrcID; // signal, background or QED
6770

6871
DigitizationParameters parameters;
6972
o2::dataformats::MCTruthContainer<o2::fdd::MCLabel>* mMCLabels = nullptr;
7073

71-
std::vector<std::vector<Float_t>> mTime; // Charge time series aka analogue signal pulse from PM
72-
std::vector<Float_t> mTimeCFD; // Time series for CFD measurement
73-
UInt_t mNBins; // Number of bins in pulse series
74-
Float_t mBinSize; // Time width of the pulse bin - HPTDC resolution
75-
std::unique_ptr<o2::base::CachingTF1> mPMResponse; // function which describes the PM time response
76-
std::unique_ptr<o2::base::CachingTF1> mSinglePhESpectrum; // function which describes the single ph.e. PM response
77-
std::unique_ptr<o2::base::CachingTF1> mSignalShape;
74+
std::array<std::vector<Float_t>, DigitizationParameters::mNchannels> mTime; // Charge time series aka analogue signal pulse from PM
75+
std::vector<Float_t> mTimeCFD; // Time series for CFD measurement
76+
UInt_t mNBins; // Number of bins in pulse series
77+
Float_t mBinSize; // Time width of the pulse bin - HPTDC resolution
78+
Float_t mPmtTimeIntegral;
79+
80+
// Random rings
81+
HitRandomRingType mRndScintDelay;
82+
PheRandomRingType mRndGainVar;
83+
PheRandomRingType mRndSignalShape;
84+
85+
// 8 tables starting at different sub-bin positions, i.e, [-4:4] / 8 * mBinSize
86+
// wit each table containg values for start + [-2:2:mBinSize] * DigitizationParameters::mPmtTransitTime
87+
std::array<std::vector<Float_t>, DigitizationParameters::mNResponseTables> mPMResponseTables;
88+
89+
static Double_t PMResponse(Double_t x);
90+
static Double_t PMResponse(Double_t* x, Double_t*);
91+
static Double_t SinglePhESpectrum(Double_t* x, Double_t* par);
7892

79-
ClassDefNV(Digitizer, 2);
93+
ClassDefNV(Digitizer, 3);
8094
};
8195
} // namespace fdd
8296
} // namespace o2

Detectors/FIT/FDD/simulation/src/Digitizer.cxx

Lines changed: 108 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,11 @@
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;
2222
using namespace o2::fdd;
2323

2424
ClassImp(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-
//_____________________________________________________________________________
148146
void Digitizer::SetTriggers(o2::fdd::Digit* digit)
149147
{
150148
}
@@ -156,21 +154,83 @@ void Digitizer::initParameters()
156154
//_______________________________________________________________________
157155
void 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
//_______________________________________________________________________
176205
void 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

Comments
 (0)