CbmRoot
Loading...
Searching...
No Matches
tof/Clusterizer.cxx
Go to the documentation of this file.
1/* Copyright (C) 2022 Facility for Antiproton and Ion Research in Europe, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Dominik Smith [committer], Pierre-Alain Loizeau */
4
5#include "Clusterizer.h"
6
7// TOF Classes and includes
8#include "CbmTofDigi.h"
9
10// C++ Classes and includes
11#include <algorithm>
12#include <iomanip>
13#include <iostream>
14#include <map>
15
16namespace cbm::algo::tof
17{
18
19 Clusterizer::resultType Clusterizer::operator()(const std::vector<std::pair<CbmTofDigi, int32_t>>& digisIn)
20 {
21 std::vector<inputType> input = chanSortDigis(digisIn);
22 return buildClusters(input);
23 }
24
25 std::vector<Clusterizer::inputType>
26 Clusterizer::chanSortDigis(const std::vector<std::pair<CbmTofDigi, int32_t>>& digisIn)
27 {
28 std::vector<inputType> result(fParams.fChanPar.size()); //[nbCh][nDigis]
29
30 // Bucket-sort by channel
31 for (size_t iDigi = 0; iDigi < digisIn.size(); iDigi++) {
32 const CbmTofDigi* pDigi = &digisIn[iDigi].first;
33 const int32_t index = digisIn[iDigi].second;
34 const double chan = pDigi->GetChannel();
35 result[chan].emplace_back(pDigi, index);
36 }
37
39 //for (size_t chan = 0; chan < fParams.fChanPar.size(); chan++) {
40 // std::sort(
41 // result[chan].begin(), result[chan].end(),
42 // [](const std::pair<const CbmTofDigi*, int32_t>& a, const std::pair<const CbmTofDigi*, int32_t>& b) -> bool {
43 // return a.first->GetTime() < b.first->GetTime();
44 // });
45 //}
46 return result;
47 }
48
49 //Iterator-based version. Faster than index-based version.
51 {
52 // Hit variables
53 Hit cluster;
54
55 // Output variables
56 resultType result;
57 auto& [clustersOut, chanSizes, chanAddresses, digiIndRef] = result;
58
59 // Reference cell of a cluster
60 TofCell* cell = nullptr;
61
62 // Last Channel Temp variables
63 int32_t lastChan = -1;
64 double lastPosY = 0.0;
65 double lastTime = 0.0;
66
67 const size_t numChan = fParams.fChanPar.size();
68
69 //Store last position in input channels to avoid unnecessary checks in AddNextChan().
70 std::vector<inputType::iterator> lastChanPos;
71 for (size_t chan = 0; chan < numChan; chan++) {
72 lastChanPos.push_back(input[chan].begin());
73 }
74
75 for (int32_t chan = 0; (size_t) chan < numChan; chan++) {
76
77 // Set partition vectors
78 chanSizes.push_back(clustersOut.size());
79 chanAddresses.push_back(fParams.fChanPar[chan].address);
80
81 // skip over dead channels
82 if (fParams.fDeadStrips & (1 << chan)) {
83 chanSizes.back() = 0;
84 continue;
85 }
86 inputType& storDigi = input[chan];
87 auto digiIt = storDigi.begin();
88
89 while (1 < std::distance(digiIt, storDigi.end())) {
90
91 while (digiIt->first->GetSide() == std::next(digiIt)->first->GetSide()) { // Not one Digi of each end!
92 digiIt++;
93 if (2 > std::distance(digiIt, storDigi.end())) {
94 break;
95 }
96 }
97 if (2 > std::distance(digiIt, storDigi.end())) {
98 break;
99 }
100
101 // 2 Digis = both sides present
102 cell = &fParams.fChanPar[chan].cell;
103 const CbmTofDigi* xDigiA = digiIt->first;
104 const CbmTofDigi* xDigiB = std::next(digiIt)->first;
105
106 // use local coordinates, (0,0,0) is in the center of counter ?
107 ROOT::Math::XYZVector pos(((-(double) numChan / 2. + (double) chan) + 0.5) * cell->sizeX, 0., 0.);
108
109 const double timeDif = xDigiA->GetTime() - xDigiB->GetTime();
110
111 pos.SetY(fParams.fSigVel * timeDif * 0.5); // A is the top side, B is the bottom side
112 if (xDigiA->GetSide() != 1.) {
113 pos.SetY(-pos.Y());
114 } // B is the bottom side, A is the top side
115
116 while (std::distance(digiIt, storDigi.end()) > 2 && std::abs(pos.Y()) > cell->sizeY * fParams.fPosYMaxScal) {
117
118 const CbmTofDigi* xDigiC = std::next(digiIt, 2)->first;
119
120 const double timeDifN = (xDigiC->GetSide() == xDigiA->GetSide()) ? xDigiC->GetTime() - xDigiB->GetTime()
121 : xDigiC->GetTime() - xDigiA->GetTime();
122
123 double posYN = fParams.fSigVel * timeDifN * 0.5;
124 if (xDigiC->GetSide() != 1.) {
125 posYN *= -1.;
126 }
127
128 if (std::abs(posYN) >= std::abs(pos.Y())) {
129 break;
130 }
131 pos.SetY(posYN);
132
133 if (xDigiC->GetSide() == xDigiA->GetSide()) {
134 xDigiA = xDigiC;
135 }
136 else {
137 xDigiB = xDigiC;
138 }
139 digiIt++;
140 }
141
142 if (std::abs(pos.Y()) > cell->sizeY * fParams.fPosYMaxScal) { // remove both digis
143 digiIt++;
144 continue;
145 }
146 // The "Strip" time is the mean time between each end
147 const double time = 0.5 * (xDigiA->GetTime() + xDigiB->GetTime());
148
149 // Weight is the total charge => sum of both ends ToT
150 const double totSum = xDigiA->GetTot() + xDigiB->GetTot();
151
152 // Now check if a hit/cluster is already started
153 if (0 < cluster.numChan()) {
154 // a cluster is already started => check distance in space/time
155 // For simplicity, just check along strip direction for now
156 // and break cluster when a not fired strip is found
157 if (!(std::abs(time - lastTime) < fParams.fdMaxTimeDist && lastChan == chan - 1
158 && std::abs(pos.Y() - lastPosY) < fParams.fdMaxSpaceDist)) {
159
160 cluster.normalize(fParams.fTimeRes);
161 cluster.finalize(*cell, fParams);
162 clustersOut.push_back(cluster);
163 cluster.reset();
164 }
165 }
166 cluster.add(pos, time, totSum, totSum);
167 digiIndRef.push_back(digiIt->second);
168 digiIndRef.push_back(std::next(digiIt)->second);
169 digiIt += 2;
170
171 lastChan = chan;
172 lastPosY = pos.Y();
173 lastTime = time;
174 AddNextChan(input, lastChan, cluster, clustersOut, digiIndRef, &lastChanPos);
175 } // while( 1 < storDigi.size() )
176
177 // Apply subtraction such that chanSize constains hit count per channel
178 chanSizes.back() = clustersOut.size() - chanSizes.back();
179
180 // In rare cases, a single digi remains and is deleted here.
181 storDigi.clear();
182 } // for( int32_t chan = 0; chan < iNbCh; chan++ )
183
184 // Now check if another hit/cluster is started
185 // and save it if it's the case.
186 if (0 < cluster.numChan()) {
187 cluster.normalize(fParams.fTimeRes);
188 cluster.finalize(*cell, fParams);
189 clustersOut.push_back(cluster);
190 chanSizes.back()++;
191 }
192 return result;
193 }
194
195 bool Clusterizer::AddNextChan(std::vector<inputType>& input, int32_t lastChan, Hit& cluster,
196 std::vector<Hit>& clustersOut, std::vector<int32_t>& digiIndRef,
197 std::vector<inputType::iterator>* lastChanPos)
198 {
199 //D.Smith 25.8.23: Why are "C" digis (position "2") not considered here?
200
201 //const int32_t iDetId = CbmTofAddress::GetUniqueAddress(iSm, iRpc, 0, 0, iSmType); //Kept for reference
202 size_t numChan = fParams.fChanPar.size();
203 int32_t chan = lastChan + 1;
204
205 while (fParams.fDeadStrips & (1 << chan)) {
206 chan++;
207 if ((size_t) chan >= numChan) {
208 return false;
209 }
210 }
211 if ((size_t) chan == numChan) {
212 return false;
213 }
214
215 inputType& storDigi = input[chan];
216 if (0 == storDigi.size()) {
217 return false;
218 }
219
220 const double clusterTime = cluster.hitTime / cluster.weightsSum;
221 const TofCell& cell = fParams.fChanPar[chan].cell;
222
223 //Get starting position in buffer
224 auto i1 = (lastChanPos == nullptr) ? storDigi.begin() : (*lastChanPos)[chan];
225
226 //Find first digi in buffer that can possibly be part of the cluster.
227 //Relies on time-order to function properly.
228 i1 = std::lower_bound(i1, storDigi.end(), clusterTime - fParams.fdMaxTimeDist,
229 [this, cell](const auto& obj, double val) {
230 return obj.first->GetTime() + cell.sizeY * fParams.fPosYMaxScal / fParams.fSigVel < val;
231 });
232
233 //Store last position in input channels to avoid unnecessary checks.
234 //Relies on time-order to function properly. Can only be used in first-level AddNextChan()
235 //calls, as time-order is not guaranteed in nested calls.
236 if (lastChanPos) {
237 (*lastChanPos)[chan] = i1;
238 }
239
240 for (; i1 < storDigi.end() - 1; i1++) {
241
242 const CbmTofDigi* xDigiA = i1->first;
243 if (xDigiA->GetTime() > clusterTime + fParams.fdMaxTimeDist) {
244 break;
245 }
246 auto i2 = i1;
247
248 while (++i2 < storDigi.end()) {
249
250 const CbmTofDigi* xDigiB = i2->first;
251 if (xDigiA->GetSide() == xDigiB->GetSide()) {
252 continue;
253 }
254
255 const double time = 0.5 * (xDigiA->GetTime() + xDigiB->GetTime());
256
257 //Continue if digis are in the past of cluster time
258 if (time <= clusterTime - fParams.fdMaxTimeDist) {
259 continue;
260 }
261
262 //Break if digis are in the future of cluster time
263 if (time >= clusterTime + fParams.fdMaxTimeDist) {
264 break;
265 }
266
267 const double timeDif = xDigiA->GetTime() - xDigiB->GetTime();
268 double posY = fParams.fSigVel * timeDif * 0.5;
269
270 //Break if position is outside of the detector
271 if (std::abs(posY) > cell.sizeY * fParams.fPosYMaxScal) {
272 break;
273 }
274
275 if (1 != xDigiA->GetSide()) {
276 posY *= -1.;
277 }
278
279 if (std::abs(posY - cluster.hitPos.Y() / cluster.weightsSum) >= fParams.fdMaxSpaceDist) {
280 continue;
281 }
282
283 // append digi pair to current cluster
284 const double posX = ((-(double) numChan / 2. + chan) + 0.5) * cell.sizeX;
285 const double totSum = xDigiA->GetTot() + xDigiB->GetTot();
286
287 ROOT::Math::XYZVector pos(posX, posY, 0.);
288 cluster.add(pos, time, totSum, totSum);
289 digiIndRef.push_back(i1->second);
290 digiIndRef.push_back(i2->second);
291
292 // remove digis at positions i1 and i2 from pool in efficient way (replaces two vector::erase calls).
293 std::move(i1 + 1, i2, i1);
294 std::move(i2 + 1, storDigi.end(), i2 - 1);
295 storDigi.resize(storDigi.size() - 2);
296
297 if (AddNextChan(input, chan, cluster, clustersOut, digiIndRef)) {
298 return true; // signal hit was already added
299 }
300 i1 = storDigi.end(); // jump to end of outer loop
301
302 break;
303 }
304 }
305 TofCell detcell = fParams.fChanPar[0].cell; //D.Smith 17.8.23: This is equivalent to using iDetId, see below
306 //D.Smith 10.8.23: Why pass iDetId here and not iChId?
307 cluster.normalize(fParams.fTimeRes);
308 cluster.finalize(detcell, fParams);
309 clustersOut.push_back(cluster);
310 cluster.reset();
311 return true;
312 }
313
314} // namespace cbm::algo::tof
Data class for expanded digital TOF information.
Definition CbmTofDigi.h:47
double GetSide() const
Channel Side.
Definition CbmTofDigi.h:160
double GetChannel() const
Channel .
Definition CbmTofDigi.h:156
double GetTime() const
Inherited from CbmDigi.
Definition CbmTofDigi.h:131
double GetTot() const
Alias for GetCharge.
Definition CbmTofDigi.h:140
std::vector< inputType > chanSortDigis(const std::vector< std::pair< CbmTofDigi, int32_t > > &digisIn)
resultType operator()(const std::vector< std::pair< CbmTofDigi, int32_t > > &digisIn)
Build clusters out of ToF Digis and store the resulting info in a TofHit.
bool AddNextChan(std::vector< inputType > &input, int32_t iLastChan, Hit &cluster, std::vector< Hit > &clustersOut, std::vector< int32_t > &digiIndRef, std::vector< inputType::iterator > *lastChanPos=nullptr)
std::tuple< std::vector< Hit >, std::vector< size_t >, std::vector< u32 >, std::vector< int32_t > > resultType
ClusterizerRpcPar fParams
Parameter container.
std::vector< std::pair< const CbmTofDigi *, int32_t > > inputType
resultType buildClusters(std::vector< inputType > &input)
std::vector< ClusterizerChanPar > fChanPar
void finalize(const TofCell &trafoCell, const ClusterizerRpcPar &par)
int32_t numChan() const
ROOT::Math::XYZVector hitPos
void add(ROOT::Math::XYZVector pos, double time, double timeErr, double weight)
void normalize(double timeErr)