10 #include <klm/simulation/ScintillatorSimulator.h>
13 #include <klm/bklm/geometry/GeometryPar.h>
14 #include <klm/eklm/geometry/GeometryData.h>
17 #include <framework/dataobjects/EventMetaData.h>
18 #include <framework/datastore/StoreObjPtr.h>
19 #include <framework/gearbox/Unit.h>
20 #include <framework/logging/Logger.h>
33 static const char MemErr[] =
"Memory allocation error.";
72 double digitizationInitialTime,
bool debug) :
76 m_DigitizationInitialTime(digitizationInitialTime),
78 m_FPGAStat(c_ScintillatorFirmwareNoSignal),
88 double time, attenuationTime;
118 time = samplingTime * i;
120 exp(-digPar->getPEAttenuationFrequency() * time) * attenuationTime /
136 free(m_amplitudeDirect);
137 free(m_amplitudeReflected);
139 free(m_ADCAmplitude);
140 free(m_SignalTimeDependence);
141 free(m_SignalTimeDependenceDiff);
142 free(m_Photoelectrons);
143 free(m_PhotoelectronIndex);
144 free(m_PhotoelectronIndex2);
161 for (
int i = 0; i < m_DigPar->getNDigitizations(); i++) {
163 m_amplitudeDirect[i] = 0;
164 m_amplitudeReflected[i] = 0;
171 const std::multimap<KLMChannelNumber, const BKLMSimHit*>::iterator& firstHit,
172 const std::multimap<KLMChannelNumber, const BKLMSimHit*>::iterator& end)
174 m_stripName =
"strip_" + std::to_string(firstHit->first);
179 geoPar->
findModule(hit->getSection(), hit->getSector(), hit->getLayer());
181 2.0 * (hit->isPhiReadout() ?
182 module->getPhiScintHalfLength(hit->getStrip()) :
183 module->getZScintHalfLength(hit->getStrip()));
184 std::vector<const BKLMSimHit*> hits;
185 for (std::multimap<KLMChannelNumber, const BKLMSimHit*>::iterator it = firstHit;
187 hits.push_back(it->second);
188 std::sort(hits.begin(), hits.end(), compareBKLMSimHits);
189 for (std::vector<const BKLMSimHit*>::iterator it = hits.begin();
190 it != hits.end(); ++it) {
192 m_Energy = m_Energy + hit->getEnergyDeposit();
194 double nPhotons = hit->getEnergyDeposit() * m_DigPar->getNPEperMeV();
196 double sipmDistance = hit->getPropagationTime() *
197 m_DigPar->getFiberLightSpeed();
198 double time = hit->getTime() + hit->getPropagationTime();
200 m_MCTime = hit->getTime();
203 if (hit->getTime() < m_MCTime)
204 m_MCTime = hit->getTime();
205 if (time < m_SiPMMCTime)
208 int generatedPhotons = gRandom->Poisson(nPhotons);
209 generatePhotoelectrons(stripLength, sipmDistance, generatedPhotons,
210 hit->getTime(),
false);
211 if (m_DigPar->getMirrorReflectiveIndex() > 0) {
212 generatedPhotons = gRandom->Poisson(nPhotons);
213 generatePhotoelectrons(stripLength, sipmDistance, generatedPhotons,
214 hit->getTime(),
true);
221 const std::multimap<KLMChannelNumber, const EKLMSimHit*>::iterator& firstHit,
222 const std::multimap<KLMChannelNumber, const EKLMSimHit*>::iterator& end)
224 m_stripName =
"strip_" + std::to_string(firstHit->first);
228 hit->getStrip()) / CLHEP::mm *
Unit::mm;
229 std::vector<const EKLMSimHit*> hits;
230 for (std::multimap<KLMChannelNumber, const EKLMSimHit*>::iterator it = firstHit;
232 hits.push_back(it->second);
233 std::sort(hits.begin(), hits.end(), compareEKLMSimHits);
234 for (std::vector<const EKLMSimHit*>::iterator it = hits.begin();
235 it != hits.end(); ++it) {
237 m_Energy = m_Energy + hit->getEnergyDeposit();
239 double nPhotons = hit->getEnergyDeposit() * m_DigPar->getNPEperMeV();
241 double sipmDistance = 0.5 * stripLength - hit->getLocalPosition().x();
242 double time = hit->getTime() +
243 sipmDistance / m_DigPar->getFiberLightSpeed();
247 m_MCTime = time < m_MCTime ? time : m_MCTime;
248 int generatedPhotons = gRandom->Poisson(nPhotons);
249 generatePhotoelectrons(stripLength, sipmDistance, generatedPhotons,
250 hit->getTime(),
false);
251 if (m_DigPar->getMirrorReflectiveIndex() > 0) {
252 generatedPhotons = gRandom->Poisson(nPhotons);
253 generatePhotoelectrons(stripLength, sipmDistance, generatedPhotons,
254 hit->getTime(),
true);
263 fillSiPMOutput(m_amplitudeDirect,
true,
false);
264 fillSiPMOutput(m_amplitudeReflected,
false,
true);
265 for (
int i = 0; i < m_DigPar->getNDigitizations(); i++)
266 m_amplitude[i] = m_amplitudeDirect[i] + m_amplitudeReflected[i];
268 fillSiPMOutput(m_amplitude,
true,
true);
270 if (m_DigPar->getMeanSiPMNoise() > 0)
271 addRandomSiPMNoise();
273 m_FPGAStat = m_fitter->fit(m_ADCAmplitude, m_Threshold, &m_FPGAFit);
274 if (m_FPGAStat != c_ScintillatorFirmwareSuccessfulFit)
285 for (i = 0; i < m_DigPar->getNDigitizations(); i++)
286 m_amplitude[i] = m_amplitude[i] +
287 gRandom->Poisson(m_DigPar->getMeanSiPMNoise());
292 int* currentIndexArray, *newIndexArray, *tmpIndexArray;
293 int i, i1, i2, i1Max, i2Max, j, mergeSize;
294 currentIndexArray = m_PhotoelectronIndex;
295 newIndexArray = m_PhotoelectronIndex2;
297 while (mergeSize < nPhotoelectrons) {
298 for (i = 0; i < nPhotoelectrons; i = i + 2 * mergeSize) {
302 if (i2 > nPhotoelectrons)
303 i2 = nPhotoelectrons;
305 i2Max = i2 + mergeSize;
306 if (i2Max > nPhotoelectrons)
307 i2Max = nPhotoelectrons;
308 while (i1 < i1Max || i2 < i2Max) {
311 if (m_Photoelectrons[currentIndexArray[i1]].bin <
312 m_Photoelectrons[currentIndexArray[i2]].bin) {
313 newIndexArray[j] = currentIndexArray[i1];
316 newIndexArray[j] = currentIndexArray[i2];
320 newIndexArray[j] = currentIndexArray[i1];
324 newIndexArray[j] = currentIndexArray[i2];
330 tmpIndexArray = currentIndexArray;
331 currentIndexArray = newIndexArray;
332 newIndexArray = tmpIndexArray;
333 mergeSize = mergeSize * 2;
335 return currentIndexArray;
339 double stripLen,
double distSiPM,
int nPhotons,
double timeShift,
342 const double samplingTime = m_DigPar->getADCSamplingTDCPeriods() *
343 m_Time->getTDCPeriod();
344 const double maxHitTime = m_DigPar->getNDigitizations() * samplingTime;
347 double hitTime, deExcitationTime, cosTheta, hitDist, selection;
348 double inverseLightSpeed, inverseAttenuationLength;
349 inverseLightSpeed = 1.0 / m_DigPar->getFiberLightSpeed();
350 inverseAttenuationLength = 1.0 / m_DigPar->getAttenuationLength();
352 for (i = 0; i < nPhotons; i++) {
353 if (m_npe >= m_PhotoelectronBufferSize)
354 reallocPhotoElectronBuffers(m_npe + 100);
355 cosTheta = gRandom->Uniform(m_DigPar->getMinCosTheta(), 1);
357 hitDist = distSiPM / cosTheta;
359 hitDist = (2.0 * stripLen - distSiPM) / cosTheta;
361 selection = gRandom->Uniform();
362 if (selection > exp(-hitDist * inverseAttenuationLength))
366 selection = gRandom->Uniform();
367 if (selection > m_DigPar->getMirrorReflectiveIndex())
371 gRandom->Exp(m_DigPar->getScintillatorDeExcitationTime()) +
372 gRandom->Exp(m_DigPar->getFiberDeExcitationTime());
373 hitTime = hitDist * inverseLightSpeed + deExcitationTime +
374 timeShift - m_DigitizationInitialTime;
375 if (hitTime >= maxHitTime)
378 m_Photoelectrons[m_npe].bin = floor(hitTime / samplingTime);
380 m_Photoelectrons[m_npe].bin = -1;
381 m_Photoelectrons[m_npe].expTime =
382 exp(m_DigPar->getPEAttenuationFrequency() * hitTime);
383 m_Photoelectrons[m_npe].isReflected = isReflected;
384 m_PhotoelectronIndex[m_npe] = m_npe;
453 double attenuationTime, sig, expSum;
455 int ind1, ind2, ind3;
459 attenuationTime = 1.0 / m_DigPar->getPEAttenuationFrequency();
460 indexArray = sortPhotoelectrons(m_npe);
465 bin = m_Photoelectrons[indexArray[ind1]].bin;
470 if (bin != m_Photoelectrons[indexArray[ind2]].bin)
474 for (ind3 = ind1; ind3 != ind2; ind3++) {
475 if (m_Photoelectrons[indexArray[ind3]].isReflected && !useReflected)
477 if (!m_Photoelectrons[indexArray[ind3]].isReflected && !useDirect)
480 sig = attenuationTime - m_Photoelectrons[indexArray[ind3]].expTime *
481 m_SignalTimeDependence[bin + 1];
482 hist[bin] = hist[bin] + sig;
484 expSum = expSum + m_Photoelectrons[indexArray[ind3]].expTime;
487 maxBin = m_DigPar->getNDigitizations() - 1;
489 maxBin = m_Photoelectrons[indexArray[ind2]].bin;
490 for (i = bin + 1; i <= maxBin; i++) {
491 sig = m_SignalTimeDependenceDiff[i] * expSum;
492 hist[i] = hist[i] + sig;
505 if (m_Pedestal == 0 || m_PhotoelectronAmplitude == 0)
506 B2FATAL(
"Incorrect EKLM ADC simulation parameters.");
507 for (i = 0; i < m_DigPar->getNDigitizations(); i++) {
508 amp = m_Pedestal - m_PhotoelectronAmplitude * m_amplitude[i];
509 if (amp < m_DigPar->getADCSaturation())
510 amp = m_DigPar->getADCSaturation();
511 m_ADCAmplitude[i] = floor(amp);
528 intg = m_FPGAFit.getAmplitude();
529 return intg * m_DigPar->getPEAttenuationFrequency() /
530 m_PhotoelectronAmplitude;
548 TFile* hfile =
nullptr;
549 TH1D* histAmplitudeDirect =
nullptr;
550 TH1D* histAmplitudeReflected =
nullptr;
551 TH1D* histAmplitude =
nullptr;
552 TH1D* histADCAmplitude =
nullptr;
554 histAmplitudeDirect =
555 new TH1D(
"histAmplitudeDirect", m_stripName.c_str(),
556 m_DigPar->getNDigitizations(), 0, m_histRange);
557 histAmplitudeReflected =
558 new TH1D(
"histAmplitudeReflected", m_stripName.c_str(),
559 m_DigPar->getNDigitizations(), 0, m_histRange);
561 new TH1D(
"histAmplitude", m_stripName.c_str(),
562 m_DigPar->getNDigitizations(), 0, m_histRange);
564 new TH1D(
"histADCAmplitude", m_stripName.c_str(),
565 m_DigPar->getNDigitizations(), 0, m_histRange);
566 }
catch (std::bad_alloc& ba) {
569 for (i = 0; i < m_DigPar->getNDigitizations(); i++) {
570 histAmplitudeDirect->SetBinContent(i + 1, m_amplitudeDirect[i]);
571 histAmplitudeReflected->SetBinContent(i + 1, m_amplitudeReflected[i]);
572 histAmplitude->SetBinContent(i + 1, m_amplitude[i]);
573 histADCAmplitude->SetBinContent(i + 1, m_ADCAmplitude[i]);
575 str = std::string(
"experiment_") + std::to_string(event->getExperiment()) +
576 "_run_" + std::to_string(event->getRun()) +
"_event_" +
577 std::to_string(event->getEvent()) +
"_" + m_stripName +
".root";
579 hfile =
new TFile(str.c_str(),
"NEW");
580 }
catch (std::bad_alloc& ba) {
583 hfile->Append(histAmplitudeDirect);
584 hfile->Append(histAmplitudeReflected);
585 hfile->Append(histAmplitude);
586 hfile->Append(histADCAmplitude);
Store one simulation hit as a ROOT object.
double getEnergyDeposit() const
Get energy deposition.
float getEnergyDeposit() const
Get energy deposit.
Class EKLMSimHit stores information on particular Geant step; using information from TrackID and Pare...
static const GeometryData & Instance(enum DataSource dataSource=c_Database, const GearDir *gearDir=nullptr)
Instantiation.
double getStripLength(int strip) const
Get strip length.
Class to store KLM scintillator simulation parameters in the database.
float getADCPedestal() const
Get ADC pedestal.
float getPEAttenuationFrequency() const
Get attenuation frequency of a single photoelectron pulse.
int getADCThreshold() const
Get ADC readout corresponding to saturation.
float getADCPEAmplitude() const
Get ADC photoelectron amplitude.
int getADCSamplingTDCPeriods() const
Get ADC sampling time in TDC periods.
int getNDigitizations() const
Get number of digitizations (points) in one sample.
int getThreshold() const
Get threshold.
float getPhotoelectronAmplitude() const
Get photoelectron amplitude.
float getPedestal() const
Get pedestal.
FPGA fit simulation data.
double getTDCPeriod() const
Get TDC period.
void setFEEData(const KLMScintillatorFEEData *FEEData)
Set FEE data.
int * m_PhotoelectronIndex2
Buffer for photoelectron indices.
float * m_amplitudeReflected
Analog amplitude (reflected).
void debugOutput()
Debug output (signal and fit result histograms).
enum ScintillatorFirmwareFitStatus getFitStatus() const
Get fit status.
const KLMTime * m_Time
Time.
double * m_SignalTimeDependence
Buffer for signal time dependence calculation.
void generatePhotoelectrons(double stripLen, double distSiPM, int nPhotons, double timeShift, bool isReflected)
Generate photoelectrons.
double * m_SignalTimeDependenceDiff
Buffer for signal time dependence calculation.
float * m_amplitudeDirect
Analog amplitude (direct).
void addRandomSiPMNoise()
Add random noise to the signal (amplitude-dependend).
void simulateADC()
Simulate ADC (create digital signal from analog),.
~ScintillatorSimulator()
Destructor.
void prepareSimulation()
Prepare simulation.
void simulate(const std::multimap< KLMChannelNumber, const BKLMSimHit * >::iterator &firstHit, const std::multimap< KLMChannelNumber, const BKLMSimHit * >::iterator &end)
Simulate BKLM strip.
double getEnergy()
Get total energy deposited in the strip (sum over ssimulation hits).
double m_histRange
Time range, (number of digitizations) * (ADC sampling time).
double m_Pedestal
Pedestal.
void reallocPhotoElectronBuffers(int size)
Reallocate photoelectron buffers.
double getNPhotoelectrons()
Get number of photoelectrons (fit result).
struct Photoelectron * m_Photoelectrons
Buffer for photoelectron data.
KLMScintillatorFirmwareFitResult * getFPGAFit()
Get fit data.
int * m_ADCAmplitude
Digital amplitude.
void performSimulation()
Perform common simulation stage.
double m_PhotoelectronAmplitude
Photoelectron amplitude.
int getNGeneratedPhotoelectrons()
Get generated number of photoelectrons.
int * m_PhotoelectronIndex
Buffer for photoelectron indices.
float * m_amplitude
Analog amplitude.
void fillSiPMOutput(float *hist, bool useDirect, bool useReflected)
Fill SiPM output.
int m_Threshold
Threshold.
int * sortPhotoelectrons(int nPhotoelectrons)
Sort photoelectrons.
int m_PhotoelectronBufferSize
Size of photoelectron data buffer.
ScintillatorSimulator(const KLMScintillatorDigitizationParameters *digPar, ScintillatorFirmware *fitter, double digitizationInitialTime, bool debug)
Constructor.
const KLMScintillatorDigitizationParameters * m_DigPar
Parameters.
Type-safe access to single objects in the data store.
static const double mm
[millimeters]
Provides BKLM geometry parameters for simulation, reconstruction etc (from Gearbox or DataBase)
const Module * findModule(int section, int sector, int layer) const
Get the pointer to the definition of a module.
static GeometryPar * instance(void)
Static method to get a reference to the singleton GeometryPar instance.
Define the geometry of a BKLM module Each sector [octant] contains Modules.
Abstract base class for different kinds of events.