Project
Loading...
Searching...
No Matches
DEDigitizer.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <algorithm>
15#include <cmath>
16#include <limits>
17
19#include <TGeoGlobalMagField.h>
20#include "Field/MagneticField.h"
21
23std::pair<o2::InteractionRecord, uint8_t> time2ROFtime(const o2::InteractionRecord& time)
24{
25 auto bc = static_cast<uint8_t>(time.bc % 4);
26 return std::make_pair(o2::InteractionRecord(time.bc - bc, time.orbit), bc);
27}
28
29//_________________________________________________________________________________________________
30
31namespace o2::mch
32{
33//_________________________________________________________________________________________________
35 : mDeId{deId},
36 mResponse{deId < 300 ? Station::Type1 : Station::Type2345},
37 mTransformation{transformation},
38 mSegmentation{mch::mapping::segmentation(deId)},
39 mRandom{random},
40 mMinChargeDist{DigitizerParam::Instance().minChargeMean, DigitizerParam::Instance().minChargeSigma},
41 mTimeDist{0., DigitizerParam::Instance().timeSigma},
42 mNoiseDist{0., DigitizerParam::Instance().noiseSigma},
43 mNoiseOnlyDist{DigitizerParam::Instance().noiseOnlyMean, DigitizerParam::Instance().noiseOnlySigma},
44 mNofNoisyPadsDist{DigitizerParam::Instance().noiseOnlyProba * mSegmentation.nofPads()},
45 mPadIdDist{0, mSegmentation.nofPads() - 1},
46 mBCDist{0, 3},
47 mFirstTFOrbit{0},
48 mSignals(mSegmentation.nofPads())
49{
50}
51
52void DEDigitizer::processHit(const Hit& hit, const InteractionRecord& collisionTime, int evID, int srcID)
53{
54 MCCompLabel label(hit.GetTrackID(), evID, srcID);
55
56 // convert energy to charge
57 auto charge = mResponse.etocharge(hit.GetEnergyLoss());
58 auto chargeCorr = mResponse.chargeCorr();
59 auto chargeBending = chargeCorr * charge;
60 auto chargeNonBending = charge / chargeCorr;
61
62 // local position of the charge distribution
63 auto exitPoint = hit.exitPoint();
64 auto entrancePoint = hit.entrancePoint();
67 mTransformation.MasterToLocal(exitPoint, lexit);
68 mTransformation.MasterToLocal(entrancePoint, lentrance);
69
70 auto hitlengthZ = lentrance.Z() - lexit.Z();
71 auto pitch = mResponse.getPitch();
72
74
75 if (abs(hitlengthZ) > pitch * 1.99) {
76 lpos.SetXYZ((lexit.X() + lentrance.X()) / 2., (lexit.Y() + lentrance.Y()) / 2., (lexit.Z() + lentrance.Z()) / 2.);
77 } else {
78 lpos.SetXYZ(lexit.X(), // take Bragg peak coordinates assuming electron drift parallel to z
79 lexit.Y(), // take Bragg peak coordinates assuming electron drift parallel to z
80 0.0); // take wire position global coordinate negative
81 }
82 auto localX = mResponse.getAnod(lpos.X());
83 auto localY = lpos.Y();
84
85 // calculate angle between track and wire assuming wire perpendicular to z-axis
86 // take global coordinates to avoid issues with rotations of detection elements
87 // neglect rotation of chambers w.r.t. beam
88 auto thetawire = atan((exitPoint.Y() - entrancePoint.Y()) / (entrancePoint.Z() - exitPoint.Z()));
89
90 // local b-field
91 double b[3] = {0., 0., 0.};
92 double x[3] = {entrancePoint.X(), entrancePoint.Y(), entrancePoint.Z()};
93 if (TGeoGlobalMagField::Instance()->GetField()) {
94 TGeoGlobalMagField::Instance()->Field(x, b);
95 } else {
96 LOG(fatal) << "no b field in MCH DEDigitizer";
97 }
98
99 // calculate track betagamma
100 // assume beta = 1 and mass of charged muon to calculate track betagamma from particle energy
101 auto betagamma = hit.eTot() / 0.1056583745;
102 auto yAngleEffect = mResponse.inclandbfield(thetawire, betagamma, b[0]);
103 localY += yAngleEffect;
104
105 // borders of charge integration area
106 auto dxy = mResponse.getSigmaIntegration() * mResponse.getChargeSpread();
107 auto xMin = localX - dxy;
108 auto xMax = localX + dxy;
109 auto yMin = localY - dxy;
110 auto yMax = localY + dxy;
111
112 // loop over all pads within the defined bounding box to compute the charge on each of them
113 mSegmentation.forEachPadInArea(xMin, yMin, xMax, yMax, [&](int padid) {
114 auto dx = mSegmentation.padSizeX(padid) * 0.5;
115 auto dy = mSegmentation.padSizeY(padid) * 0.5;
116 auto xPad = mSegmentation.padPositionX(padid) - localX;
117 auto yPad = mSegmentation.padPositionY(padid) - localY;
118 auto q = mResponse.chargePadfraction(xPad - dx, xPad + dx, yPad - dy, yPad + dy);
119 if (mResponse.isAboveThreshold(q)) {
120 q *= mSegmentation.isBendingPad(padid) ? chargeBending : chargeNonBending;
121 if (q > 0.f) {
122 addSignal(padid, collisionTime, q, label);
123 }
124 }
125 });
126}
127
128void DEDigitizer::addNoise(const InteractionRecord& firstIR, const InteractionRecord& lastIR)
129{
130 if (mNofNoisyPadsDist.mean() > 0.) {
131 auto firstROF = time2ROFtime(firstIR);
132 auto lastROF = time2ROFtime(lastIR);
133 for (auto ir = firstROF.first; ir <= lastROF.first; ir += 4) {
134 int nofNoisyPads = mNofNoisyPadsDist(mRandom);
135 for (auto i = 0; i < nofNoisyPads; ++i) {
136 addNoise(mPadIdDist(mRandom), ir);
137 }
138 }
139 }
140}
141
142size_t DEDigitizer::digitize(std::map<InteractionRecord, DigitsAndLabels>& irDigitsAndLabels)
143{
144 size_t nPileup = 0;
145 for (int padid = 0; padid < mSignals.size(); ++padid) {
146 auto& signals = mSignals[padid];
147
148 // add time dispersion to physical signal (noise-only signal is already randomly distributed)
149 if (mTimeDist.stddev() > 0.f) {
150 for (auto& signal : signals) {
151 if (!signal.labels.front().isNoise()) {
152 addTimeDispersion(signal);
153 }
154 }
155 }
156
157 // sort signals in time (needed to handle pileup)
158 if (DigitizerParam::Instance().handlePileup) {
159 std::sort(signals.begin(), signals.end(), [](const Signal& s1, const Signal& s2) {
160 return s1.rofIR < s2.rofIR || (s1.rofIR == s2.rofIR && s1.bcInROF < s2.bcInROF);
161 });
162 }
163
164 DigitsAndLabels* previousDigitsAndLabels = nullptr;
165 auto previousDigitBCStart = std::numeric_limits<int64_t>::min();
166 auto previousDigitBCEnd = std::numeric_limits<int64_t>::min();
167 float previousRawCharge = 0.f;
168 for (auto& signal : signals) {
169
170 auto rawCharge = signal.charge;
171 auto nSamples = mResponse.nSamples(rawCharge);
172
173 // add noise to physical signal and reject it if it is below threshold
174 // (not applied to noise-only signals, which are noise above threshold by definition)
175 if (!signal.labels.back().isNoise()) {
176 addNoise(signal, nSamples);
177 if (!isAboveThreshold(signal.charge)) {
178 continue;
179 }
180 }
181
182 // create a digit or add the signal to the previous one in case of overlap and if requested.
183 // a correct handling of pileup would require a complete simulation of the electronic signal
184 auto digitBCStart = signal.rofIR.toLong();
185 auto digitBCEnd = digitBCStart + 4 * nSamples - 1;
186 if (DigitizerParam::Instance().handlePileup && digitBCStart <= previousDigitBCEnd + 8) {
187 rawCharge += previousRawCharge;
188 auto minNSamples = (std::max(previousDigitBCEnd, digitBCEnd) + 1 - previousDigitBCStart) / 4;
189 nSamples = std::max(static_cast<uint32_t>(minNSamples), mResponse.nSamples(rawCharge));
190 appendLastDigit(previousDigitsAndLabels, signal, nSamples);
191 previousDigitBCEnd = previousDigitBCStart + 4 * nSamples - 1;
192 previousRawCharge = rawCharge;
193 ++nPileup;
194 } else {
195 previousDigitsAndLabels = addNewDigit(irDigitsAndLabels, padid, signal, nSamples);
196 previousDigitBCStart = digitBCStart;
197 previousDigitBCEnd = digitBCEnd;
198 previousRawCharge = rawCharge;
199 }
200 }
201 }
202
203 return nPileup;
204}
205
206void DEDigitizer::clear()
207{
208 for (auto& signals : mSignals) {
209 signals.clear();
210 }
211}
212
213void DEDigitizer::addSignal(int padid, const InteractionRecord& collisionTime, float charge, const MCCompLabel& label)
214{
215 // convert collision time to ROF time
216 auto rofTime = time2ROFtime(collisionTime);
217
218 // search if we already have a signal for that pad in that ROF
219 auto& signals = mSignals[padid];
220 auto itSignal = std::find_if(signals.begin(), signals.end(),
221 [&rofTime](const Signal& s) { return s.rofIR == rofTime.first; });
222
223 if (itSignal != signals.end()) {
224 // merge with the existing signal
225 itSignal->bcInROF = std::min(itSignal->bcInROF, rofTime.second);
226 itSignal->charge += charge;
227 itSignal->labels.push_back(label);
228 } else {
229 // otherwise create a new signal
230 signals.emplace_back(rofTime.first, rofTime.second, charge, label);
231 }
232}
233
234void DEDigitizer::addNoise(int padid, const InteractionRecord& rofIR)
235{
236 // search if we already have a signal for that pad in that ROF
237 auto& signals = mSignals[padid];
238 auto itSignal = std::find_if(signals.begin(), signals.end(), [&rofIR](const Signal& s) { return s.rofIR == rofIR; });
239
240 // add noise-only signal only if no signal found
241 if (itSignal == signals.end()) {
242 auto bc = static_cast<uint8_t>(mBCDist(mRandom));
243 auto charge = (mNoiseOnlyDist.stddev() > 0.f) ? mNoiseOnlyDist(mRandom) : mNoiseOnlyDist.mean();
244 if (charge > 0.f) {
245 signals.emplace_back(rofIR, bc, charge, MCCompLabel(true));
246 }
247 }
248}
249
250void DEDigitizer::addNoise(Signal& signal, uint32_t nSamples)
251{
252 if (mNoiseDist.stddev() > 0.f) {
253 signal.charge += mNoiseDist(mRandom) * std::sqrt(nSamples);
254 }
255}
256
257void DEDigitizer::addTimeDispersion(Signal& signal)
258{
259 auto time = signal.rofIR.toLong() + signal.bcInROF + std::llround(mTimeDist(mRandom));
260 // the time must be positive
261 if (time < 0) {
262 time = 0;
263 }
265 signal.rofIR = ir;
266 signal.bcInROF = bc;
267}
268
269bool DEDigitizer::isAboveThreshold(float charge)
270{
271 if (charge > 0.f) {
272 if (mMinChargeDist.stddev() > 0.f) {
273 return charge > mMinChargeDist(mRandom);
274 }
275 return charge > mMinChargeDist.mean();
276 }
277 return false;
278}
279
280DEDigitizer::DigitsAndLabels* DEDigitizer::addNewDigit(std::map<InteractionRecord, DigitsAndLabels>& irDigitsAndLabels,
281 int padid, const Signal& signal, uint32_t nSamples) const
282{
283 uint32_t adc = std::round(signal.charge);
284 auto time = signal.rofIR.differenceInBC({0, mFirstTFOrbit});
285 nSamples = std::min(nSamples, 0x3FFU); // the number of samples must fit within 10 bits
286 bool saturated = false;
287 // the charge sum must fit within 20 bits
288 // FIXME: we should better handle charge saturation here
289 if (adc > 0xFFFFFU) {
290 adc = 0xFFFFFU;
291 saturated = true;
292 }
293
294 auto& digitsAndLabels = irDigitsAndLabels[signal.rofIR];
295 digitsAndLabels.first.emplace_back(mDeId, padid, adc, time, nSamples, saturated);
296 digitsAndLabels.second.addElements(digitsAndLabels.first.size() - 1, signal.labels);
297
298 return &digitsAndLabels;
299}
300
301void DEDigitizer::appendLastDigit(DigitsAndLabels* digitsAndLabels, const Signal& signal, uint32_t nSamples) const
302{
303 auto& lastDigit = digitsAndLabels->first.back();
304 uint32_t adc = lastDigit.getADC() + std::round(signal.charge);
305
306 lastDigit.setADC(std::min(adc, 0xFFFFFU)); // the charge sum must fit within 20 bits
307 lastDigit.setNofSamples(std::min(nSamples, 0x3FFU)); // the number of samples must fit within 10 bits
308 lastDigit.setSaturated(adc > 0xFFFFFU); // FIXME: we should better handle charge saturation here
309 digitsAndLabels->second.addElements(digitsAndLabels->first.size() - 1, signal.labels);
310}
311
312} // namespace o2::mch
std::pair< o2::InteractionRecord, uint8_t > time2ROFtime(const o2::InteractionRecord &time)
Convert collision time to ROF time (ROF duration = 4 BC)
int16_t charge
Definition RawEventData.h:5
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
int32_t i
float & yMax
Definition of the MagF class.
int GetTrackID() const
Definition BaseHits.h:30
V GetEnergyLoss() const
Definition BaseHits.h:103
void MasterToLocal(const Point3D< T > &mst, Point3D< T > &loc) const
Definition Cartesian.h:231
DEDigitizer(int deId, math_utils::Transform3D transformation, std::mt19937 &random)
void processHit(const Hit &hit, const InteractionRecord &collisionTime, int evID, int srcID)
std::pair< std::vector< Digit >, dataformats::MCLabelContainer > DigitsAndLabels
Definition DEDigitizer.h:36
float eTot() const
Definition Hit.h:39
math_utils::Point3D< float > exitPoint() const
Definition Hit.h:34
math_utils::Point3D< float > entrancePoint() const
Definition Hit.h:33
float getSigmaIntegration() const
Definition Response.h:37
float chargePadfraction(float xmin, float xmax, float ymin, float ymax) const
Definition Response.h:55
float inclandbfield(float thetawire, float betagamma, float bx) const
compute deteriation of y-resolution due to track inclination and B-field
Definition Response.cxx:92
float getAnod(float x) const
return wire coordinate closest to x
Definition Response.cxx:69
bool isAboveThreshold(float charge) const
Definition Response.h:38
float getPitch() const
Definition Response.h:36
float chargeCorr() const
return a randomized charge correlation between cathodes
Definition Response.cxx:77
float etocharge(float edepos) const
Definition Response.cxx:51
float getChargeSpread() const
Definition Response.h:35
double padPositionX(int dePadIndex) const
double padSizeY(int dePadIndex) const
double padPositionY(int dePadIndex) const
void forEachPadInArea(double xmin, double ymin, double xmax, double ymax, CALLABLE &&func) const
double padSizeX(int dePadIndex) const
GLint GLenum GLint x
Definition glcorearb.h:403
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat GLfloat s1
Definition glcorearb.h:5034
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLuint GLsizei const GLchar * label
Definition glcorearb.h:2519
uint8_t itsSharedClusterMap uint8_t
@ Signal
A message which is created every time a SIGUSR1 is received.
static InteractionRecord long2IR(int64_t l)
auto transformation
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
ArrayADC adc