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//_________________________________________________________________________________________________
34DEDigitizer::DEDigitizer(int deId, math_utils::Transform3D transformation, std::mt19937& random)
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();
65 math_utils::Point3D<float> lexit{};
66 math_utils::Point3D<float> lentrance{};
67 mTransformation.MasterToLocal(exitPoint, lexit);
68 mTransformation.MasterToLocal(entrancePoint, lentrance);
69
70 auto hitlengthZ = lentrance.Z() - lexit.Z();
71 auto pitch = mResponse.getPitch();
72
73 math_utils::Point3D<float> lpos{};
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
unsigned int uint32_t
int32_t i
int32_t nSamples
Timeframe settings Average distance in cm of events merged into Randomize distance around average distance of merged events Simulate correct bunch interactions instead of placing only the average number of events A value[n] sets TFlen for[n] collisions in Number of bunches per trainsort Number of bunch trains Instantaneous interaction rate Do not simulate border Total number of collisions to be placed in the interior of all time Do not select random but walk over array of events in stride steps h Event generator settings h configStandalone Use CPU for GPU Force usage of the specified GPU device no CPU fallback Continue processing after an error s
configStandalone Reconstruction rec_tpc multiplier for the combined cluster track error during track following area in cm for the search of for z only used if decrease removal by when above this local occupancy rowx Shift of TPC revert cluster flag edge bit distance to edge exceeds this ZOffset for secondary multiple of radius max P of electron K BB bands crossing max P of electron p BB bands crossing max P of electron d BB bands crossing max P of electron t BB bands crossing min accepted dEdxMaxTPC of the track Additive extra cluster error for Y2 if edge flag set Additive extra cluster error for Y2 if single set Additive extra cluster error for Y2 if or single set Additive extra cluster error for Z2 if or single set Extra errors to add to track when crossing depending on addErrorsCECrossing Inner portion of y range in sector that is not used in searching for extrapolated track candidates factor2YS for track merging factor2K for track merging Margin in cm of Y position when rejecting edge clusters based on uncorrected track Y Max fraction of shared clusters for tracklet Except pad from cluster finding if total number of charges in a fragment is above this Except pad from cluster finding if number of consecutive charges in a fragment is above this Threshold where a timebin is considered saturated
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
constexpr std::array< std::string_view, BasicOp::Conditional+1 > mapping
a map between BasicOp and tokens in string expressions
@ 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