10 #include <svd/modules/svdSimulation/SVDDigitizerModule.h>
11 #include <vxd/geometry/GeoCache.h>
13 #include <framework/dataobjects/EventMetaData.h>
14 #include <framework/logging/Logger.h>
15 #include <framework/gearbox/Unit.h>
16 #include <framework/gearbox/Const.h>
17 #include <framework/datastore/DataStore.h>
18 #include <framework/datastore/StoreArray.h>
19 #include <framework/datastore/StoreObjPtr.h>
20 #include <framework/datastore/RelationArray.h>
21 #include <framework/datastore/RelationIndex.h>
22 #include <mdst/dataobjects/MCParticle.h>
23 #include <svd/dataobjects/SVDTrueHit.h>
24 #include <svd/dataobjects/SVDShaperDigit.h>
25 #include <svd/dataobjects/SVDModeByte.h>
26 #include <svd/dataobjects/SVDEventInfo.h>
33 #include <root/TMath.h>
34 #include <root/TRandom.h>
48 double tree_signal[20];
60 SVDDigitizerModule::SVDDigitizerModule() :
Module(),
61 m_mapping(m_xmlFileName)
91 "Zero suppression cut in sigmas of strip noise",
m_SNAdjacent);
93 "FADC mode: if true, ZS cut is rounded to nearest ADU ",
m_roundZS);
95 "Keep digit if numberOfSamples or more samples are over ZS threshold",
108 "Left edge of event time randomization window, ns",
m_minTimeFrame);
110 "Right edge of event time randomization window, ns",
m_maxTimeFrame);
114 "ROOT Filename for statistics generation. If filename is empty, no statistics will be produced",
119 "Store signals (time/charge/tau) in a tab-delimited file",
131 storeShaperDigits.registerInDataStore();
162 "SVDDigitizer parameters (in default system units, *=cannot be set directly):");
163 B2DEBUG(1,
" DATASTORE COLLECTIONS:");
176 B2DEBUG(1,
" PHYSICS: ");
179 B2DEBUG(1,
" NOISE: ");
180 B2DEBUG(1,
" --> Add Poisson noise " << (
m_applyPoisson ?
"true" :
"false"));
181 B2DEBUG(1,
" --> Add Gaussian noise: " << (
m_applyNoise ?
"true" :
"false"));
182 B2DEBUG(1,
" --> Zero suppression cut" <<
m_SNAdjacent);
183 B2DEBUG(1,
" --> Round ZS cut: " << (
m_roundZS ?
"true" :
"false"));
186 B2DEBUG(1,
" TIMING: ");
191 B2DEBUG(1,
" REPORTING: ");
203 " 'Diffusion' distance, u", 100, -200, 200);
209 "Lorentz angle, electrons", 100, -0.002, 0.002);
212 "Strip signals vs. TrueHits, holes", 100, -400, 400);
213 m_signalDist_u->GetXaxis()->SetTitle(
"U strip position - TrueHit u [um]");
215 "Strip signals vs. TrueHits, electrons", 100, -400, 400);
216 m_signalDist_v->GetXaxis()->SetTitle(
"V strip position - TrueHit v [um]");
237 30, -0.002, -0.0004);
248 m_waveTree =
new TTree(
"waveTree",
"SVD waveforms");
249 m_waveTree->Branch(
"sensor", &tree_vxdID,
"sensor/I");
250 m_waveTree->Branch(
"u_or_v", &tree_uv,
"u_or_v/I");
251 m_waveTree->Branch(
"strip", &tree_strip,
"strip/I");
252 m_waveTree->Branch(
"signal", tree_signal,
"signal[20]/D");
279 B2WARNING(
"No valid SVDFADCMaskedStrip for the requested IoV -> no strips masked");
281 B2WARNING(
"No valid channel mapping -> all APVs will be enabled");
297 for (Sensors::value_type& sensor :
m_sensors) {
298 sensor.second.first.clear();
299 sensor.second.second.clear();
314 unsigned int nSimHits = storeSimHits.
getEntries();
319 for (
unsigned int i = 0; i < nSimHits; ++i) {
335 "Could not find MCParticle which produced SVDSimhit " << i);
341 if (trueRel && trueRel->
weight > 0) {
353 "Sensor Information for Sensor " << sensorID <<
" not found, make sure that the geometry is set up correctly");
371 "Sensor Parameters for Sensor " << sensorID <<
": " << endl
392 "Processing hit " << i <<
" in Sensor " << sensorID <<
", related to MCParticle " <<
m_currentParticle);
414 TVector3 direction = stopPoint - startPoint;
415 double trackLength = direction.Mag();
425 double lastFraction {0};
426 double lastElectrons {0};
428 for (
auto& segment : segments) {
431 const double f = (segment.first + lastFraction) / 2;
432 const double e = segment.second - lastElectrons;
434 std::tie(lastFraction, lastElectrons) = segment;
437 const TVector3 position = startPoint + f * direction;
438 driftCharge(position, e, SVD::SensorInfo::electron);
447 bool have_electrons = (carrierType == SVD::SensorInfo::electron);
449 string carrierName = (have_electrons) ?
"electron" :
"hole";
451 "Drifting " << carriers <<
" " << carrierName <<
"s at position (" << position.x() <<
", " << position.y() <<
", " << position.z()
453 B2DEBUG(30,
"@@@ driftCharge: drifting " << carriers <<
" " << carrierName <<
"s at position (" << position.x() <<
", " <<
454 position.y() <<
", " << position.z()
461 double distanceToPlane = (have_electrons) ?
470 TVector3 mean_pos(position.X(), position.Y(), position.Z() + 0.5 * distanceToPlane);
473 TVector3 v = info.getVelocity(carrierType, mean_pos);
477 double driftTime = distanceToPlane / v.Z();
481 TVector3 center = position + driftTime * v;
482 double mobility = (have_electrons) ?
483 info.getElectronMobility(info.getEField(mean_pos).Mag()) :
484 info.getHoleMobility(info.getEField(mean_pos).Mag());
490 double sigma = std::max(1.0e-4, sqrt(2.0 * D * driftTime));
491 double tanLorentz = (!have_electrons) ? v.X() / v.Z() : v.Y() / v.Z();
494 B2DEBUG(30,
"D = " << D <<
", driftTime = " << driftTime /
Unit::ns <<
" ns");
495 B2DEBUG(30,
"sigma = " << sigma /
Unit::um <<
" um");
496 B2DEBUG(30,
"tan Lorentz = " << tanLorentz);
498 sigma *= sqrt(1.0 + tanLorentz * tanLorentz);
503 int vID = info.getVCellID(center.Y(),
true);
504 int uID = info.getUCellID(center.X(), center.Y(),
true);
505 int seedStrip = (!have_electrons) ? uID : vID;
506 double seedPos = (!have_electrons) ?
507 info.getUCellPosition(seedStrip, vID) :
508 info.getVCellPosition(seedStrip);
509 double geomPitch = (!have_electrons) ? 0.5 * info.getUPitch(center.Y()) : 0.5 * info.getVPitch();
510 int nCells = (!have_electrons) ? info.getUCells() : info.getVCells();
511 std::deque<double> stripCharges;
512 std::deque<double> strips;
513 #define NORMAL_CDF(z) 0.5 * std::erfc( - (z) * 0.707107)
514 double current_pos = (!have_electrons) ? seedPos - center.X() : seedPos - center.Y();
515 double current_strip = seedStrip;
516 double cdf_low = NORMAL_CDF((current_pos - 0.5 * geomPitch) / sigma);
517 double cdf_high = NORMAL_CDF((current_pos + 0.5 * geomPitch) / sigma);
518 double charge = carriers * (cdf_high - cdf_low);
520 B2DEBUG(30,
"geomPitch = " << geomPitch /
Unit::um <<
" um");
521 B2DEBUG(30,
"charge = " << charge <<
" = " << carriers <<
"(carriers) * (" << cdf_high <<
"(cdf_high) - " << cdf_low <<
524 stripCharges.push_back(charge);
525 strips.push_back(current_strip);
526 while (cdf_low > 1.0e-5) {
527 current_pos -= geomPitch;
528 current_strip -= 0.5;
529 double cdf_current = NORMAL_CDF((current_pos - 0.5 * geomPitch) / sigma);
530 charge = carriers * (cdf_low - cdf_current);
531 stripCharges.push_front(charge);
532 strips.push_front(current_strip);
533 cdf_low = cdf_current;
535 current_pos = (!have_electrons) ? seedPos - center.X() : seedPos - center.Y();
536 current_strip = seedStrip;
537 while (cdf_high < 1.0 - 1.0e-5) {
538 current_pos += geomPitch;
539 current_strip += 0.5;
540 double cdf_current = NORMAL_CDF((current_pos + 0.5 * geomPitch) / sigma);
541 charge = carriers * (cdf_current - cdf_high);
542 stripCharges.push_back(charge);
543 strips.push_back(current_strip);
544 cdf_high = cdf_current;
549 int npads = (strips.front() - floor(strips.front()) == 0) ? 4 : 3;
550 for (
int i = 0; i < npads; ++i) {
551 strips.push_front(strips.front() - 0.5);
552 stripCharges.push_front(0);
554 npads = (strips.back() - floor(strips.back()) == 0) ? 4 : 3;
555 for (
int i = 0; i < npads; ++i) {
556 strips.push_back(strips.back() + 0.5);
557 stripCharges.push_back(0);
560 B2DEBUG(30,
" --> charge sharing simulation, # strips = " << strips.size());
561 std::deque<double> readoutCharges;
562 std::deque<int> readoutStrips;
563 for (std::size_t index = 2; index < strips.size() - 2; index += 2) {
564 B2DEBUG(30,
" index = " << index <<
", strip = " << strips[index] <<
", stripCharge = " << stripCharges[index]);
565 int currentStrip =
static_cast<int>(strips[index]);
566 double Cc = (!have_electrons) ? info.getCouplingCapacitanceU(currentStrip) :
567 info.getCouplingCapacitanceV(currentStrip);
568 double Ci = (!have_electrons) ? info.getInterstripCapacitanceU(currentStrip) :
569 info.getInterstripCapacitanceV(currentStrip);
570 double Cb = (!have_electrons) ? info.getBackplaneCapacitanceU(currentStrip) :
571 info.getBackplaneCapacitanceV(currentStrip);
573 double cNeighbour2 = 0.5 * Ci / (Ci + Cb + Cc);
574 double cNeighbour1 = Ci / (2 * Ci + Cb);
575 double cSelf = Cc / (Ci + Cb + Cc);
577 B2DEBUG(30,
" current strip = " << currentStrip);
578 B2DEBUG(30,
" index-2 = " << index - 2 <<
", strip = " << strips[index - 2] <<
", stripCharge = " << stripCharges[index - 2]);
579 B2DEBUG(30,
" index-1 = " << index - 1 <<
", strip = " << strips[index - 1] <<
", stripCharge = " << stripCharges[index - 1]);
580 B2DEBUG(30,
" index = " << index <<
", strip = " << strips[index] <<
", stripCharge = " << stripCharges[index]);
581 B2DEBUG(30,
" index+1 = " << index + 1 <<
", strip = " << strips[index + 1] <<
", stripCharge = " << stripCharges[index + 1]);
582 B2DEBUG(30,
" index+2 = " << index + 2 <<
", strip = " << strips[index + 2] <<
", stripCharge = " << stripCharges[index + 2]);
584 readoutCharges.push_back(cSelf * (
585 cNeighbour2 * stripCharges[index - 2]
586 + cNeighbour1 * stripCharges[index - 1]
587 + stripCharges[index]
588 + cNeighbour1 * stripCharges[index + 1]
589 + cNeighbour2 * stripCharges[index + 2]
591 readoutStrips.push_back(currentStrip);
592 B2DEBUG(30,
" post simulation: " << index <<
", strip = " << currentStrip <<
", readoutCharge = " <<
593 readoutCharges[readoutCharges.size() - 1]);
597 while (readoutStrips.size() > 0 && readoutStrips.front() < 0) {
598 readoutStrips.pop_front();
599 tail += readoutCharges.front();
600 readoutCharges.pop_front();
602 readoutCharges.front() += tail;
604 while (readoutStrips.size() > 0 && readoutStrips.back() > nCells - 1) {
605 readoutStrips.pop_back();
606 tail += readoutCharges.back();
607 readoutCharges.pop_back();
609 readoutCharges.back() += tail;
612 for (
auto& c : readoutCharges)
613 c = (c <= 0) ? 0 : std::max(0.0, gRandom->Gaus(c, std::sqrt(info.c_fanoFactorSi * c)));
618 double d = (!have_electrons) ? seedPos - center.X() : seedPos - center.Y();
619 for (std::size_t index = 0; index < readoutStrips.size(); ++ index) {
620 double dist = d + (readoutStrips[index] - seedStrip) * 2 * geomPitch;
621 histo->Fill(dist /
Unit::um, readoutCharges[index]);
625 double recoveredCharge = 0;
626 for (std::size_t index = 0; index < readoutStrips.size(); index ++) {
629 digits[readoutStrips[index]].add(
m_currentTime + 0.5 * driftTime, readoutCharges[index],
631 recoveredCharge += readoutCharges[index];
632 B2DEBUG(30,
"strip: " << readoutStrips[index] <<
" charge: " << readoutCharges[index]);
634 B2DEBUG(30,
"Digitized " << recoveredCharge <<
" of " << carriers <<
" original carriers.");
642 charge = TMath::NormQuantile(p) * noise;
644 charge += gRandom->Gaus(0., noise);
652 SVDModeByte modeByte = storeSVDEvtInfo->getModeByte();
657 RelationArray relShaperDigitMCParticle(storeShaperDigits, storeMCParticles,
659 RelationArray relShaperDigitTrueHit(storeShaperDigits, storeTrueHits,
663 const double bunchTimeSep = 2 * 1.96516;
665 int bunchXingsSinceAPVstart = 2 * triggerBin + gRandom->Integer(2);
666 double initTime =
m_startSampling - bunchTimeSep * bunchXingsSinceAPVstart;
673 int nAPV25Samples = 0;
676 else if (daqMode == 1)
678 else if (daqMode == 0)
681 B2ERROR(
"daqMode not recongized, check SVDEventInfoSetter parameters");
684 vector<pair<unsigned int, float> > digit_weights;
688 for (Sensors::value_type& sensor :
m_sensors) {
689 int sensorID = sensor.first;
693 double elNoiseU = info.getElectronicNoiseU();
694 double aduEquivalentU = info.getAduEquivalentU();
699 int nU = info.getUCells();
700 int uNoisyStrips = gRandom->Poisson(fraction * nU);
701 for (
short ns = 0; ns < uNoisyStrips; ++ns) {
702 short iStrip = gRandom->Integer(nU);
703 sensor.second.first[iStrip].add(0, -1, 0, 0, 0);
707 for (StripSignals::value_type& stripSignal : sensor.second.first) {
708 short int iStrip = stripSignal.first;
711 vector<double> samples;
713 digit_weights.clear();
717 double pSelect = 1.0 / nAPV25Samples;
718 for (
int iSample = 0; iSample < nAPV25Samples; iSample++) {
719 if (gRandom->Uniform() < pSelect)
720 samples.push_back(
addNoise(-1, elNoiseU));
722 samples.push_back(gRandom->Gaus(0, elNoiseU));
726 for (
int iSample = 0; iSample < nAPV25Samples; iSample ++) {
727 samples.push_back(
addNoise(s(t), elNoiseU));
738 std::transform(samples.begin(), samples.end(), rawSamples.begin(),
739 [&](
double x)->SVDShaperDigit::APVRawSampleType {
740 return SVDShaperDigit::trimToSampleRange(x / aduEquivalentU);
743 auto rawThreshold = charge_thresholdU / aduEquivalentU;
744 if (
m_roundZS) rawThreshold = round(rawThreshold);
745 auto n_over = std::count_if(rawSamples.begin(), rawSamples.end(),
746 std::bind2nd(std::greater<double>(), rawThreshold)
754 if (!
m_map->isAPVinMap(sensorID,
true, iStrip))
continue;
757 int digIndex = storeShaperDigits.
getEntries();
761 if (particles.size() > 0) {
762 relShaperDigitMCParticle.
add(digIndex, particles.begin(), particles.end());
765 if (truehits.size() > 0) {
766 relShaperDigitTrueHit.
add(digIndex, truehits.begin(), truehits.end());
772 double elNoiseV = info.getElectronicNoiseV();
773 double aduEquivalentV = info.getAduEquivalentV();
778 int nV = info.getVCells();
779 int vNoisyStrips = gRandom->Poisson(fraction * nV);
780 for (
short ns = 0; ns < vNoisyStrips; ++ns) {
781 short iStrip = gRandom->Integer(nV);
782 sensor.second.second[iStrip].add(0, -1, 0, 0, 0);
786 for (StripSignals::value_type& stripSignal : sensor.second.second) {
787 short int iStrip = stripSignal.first;
790 vector<double> samples;
792 digit_weights.clear();
796 double pSelect = 1.0 / nAPV25Samples;
797 for (
int iSample = 0; iSample < nAPV25Samples; iSample++) {
798 if (gRandom->Uniform() < pSelect)
799 samples.push_back(
addNoise(-1, elNoiseV));
801 samples.push_back(gRandom->Gaus(0, elNoiseV));
805 for (
int iSample = 0; iSample < nAPV25Samples; iSample ++) {
806 samples.push_back(
addNoise(s(t), elNoiseV));
817 std::transform(samples.begin(), samples.end(), rawSamples.begin(),
818 [&](
double x)->SVDShaperDigit::APVRawSampleType {
819 return SVDShaperDigit::trimToSampleRange(x / aduEquivalentV);
822 auto rawThreshold = charge_thresholdV / aduEquivalentV;
823 if (
m_roundZS) rawThreshold = round(rawThreshold);
824 auto n_over = std::count_if(rawSamples.begin(), rawSamples.end(),
825 std::bind2nd(std::greater<double>(), rawThreshold)
833 if (!
m_map->isAPVinMap(sensorID,
false, iStrip))
continue;
836 int digIndex = storeShaperDigits.
getEntries();
840 if (particles.size() > 0) {
841 relShaperDigitMCParticle.
add(digIndex, particles.begin(), particles.end());
844 if (truehits.size() > 0) {
845 relShaperDigitTrueHit.
add(digIndex, truehits.begin(), truehits.end());
853 for (Sensors::value_type& sensor :
m_sensors) {
854 tree_vxdID = sensor.first;
859 double thresholdU = 3.0 * info.getElectronicNoiseU();
860 for (StripSignals::value_type& stripSignal : sensor.second.first) {
861 tree_strip = stripSignal.first;
864 if (s.getCharge() < thresholdU)
866 for (
int iTime = 0; iTime < 20; ++iTime) {
867 tree_signal[iTime] = s(10 * iTime);
873 double thresholdV = 3.0 * info.getElectronicNoiseV();
874 for (StripSignals::value_type& stripSignal : sensor.second.second) {
875 tree_strip = stripSignal.first;
878 if (s.getCharge() < thresholdV)
880 for (
int iTime = 0; iTime < 20; ++iTime) {
881 tree_signal[iTime] = s(10. * iTime);
891 static size_t recordNo = 0;
892 static const string header(
"Event\tSensor\tSide\tStrip\tContrib\tTime\tCharge\tTau");
893 regex startLine(
"^|\n");
895 if (recordNo == 0) outfile << header << endl;
896 for (Sensors::value_type& sensor :
m_sensors) {
897 VxdID sensorID(sensor.first);
902 double thresholdU = 3.0 * info.getElectronicNoiseU();
903 for (StripSignals::value_type& stripSignal : sensor.second.first) {
904 size_t strip = stripSignal.first;
907 if (s.getCharge() < thresholdU)
910 ostringstream preamble;
912 preamble <<
"$&" << recordNo <<
'\t' << sensorID <<
'\t' << isU <<
'\t' << strip <<
'\t';
913 string signalString = s.toString();
914 signalString.pop_back();
915 string tableString = regex_replace(signalString, startLine, preamble.str());
916 outfile << tableString << endl;
920 double thresholdV = 3.0 * info.getElectronicNoiseV();
921 for (StripSignals::value_type& stripSignal : sensor.second.second) {
922 size_t strip = stripSignal.first;
925 if (s.getCharge() < thresholdV)
928 ostringstream preamble;
930 preamble <<
"$&" << recordNo <<
'\t' << sensorID <<
'\t' << isU <<
'\t' << strip <<
'\t';
931 string signalString = s.toString();
932 signalString.pop_back();
933 string tableString = regex_replace(signalString, startLine, preamble.str());
934 outfile << tableString << endl;