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 while (digiIt->first->GetSide() == std::next(digiIt)->first->GetSide()) { // Not one Digi of each end!
91 digiIt++;
92 if (2 > std::distance(digiIt, storDigi.end())) {
93 break;
94 }
95 }
96 if (2 > std::distance(digiIt, storDigi.end())) {
97 break;
98 }
99
100 // 2 Digis = both sides present
101 cell = &fParams.fChanPar[chan].cell;
102 const CbmTofDigi* xDigiA = digiIt->first;
103 const CbmTofDigi* xDigiB = std::next(digiIt)->first;
104
105 // use local coordinates, (0,0,0) is in the center of counter ?
106 ROOT::Math::XYZVector pos(((-(double) numChan / 2. + (double) chan) + 0.5) * cell->sizeX, 0., 0.);
107
108 const double timeDif = xDigiA->GetTime() - xDigiB->GetTime();
109
110 pos.SetY(fParams.fSigVel * timeDif * 0.5); // A is the top side, B is the bottom side
111 if (xDigiA->GetSide() != 1.) {
112 pos.SetY(-pos.Y());
113 } // B is the bottom side, A is the top side
114
115 while (std::distance(digiIt, storDigi.end()) > 2 && std::abs(pos.Y()) > cell->sizeY * fParams.fPosYMaxScal) {
116
117 const CbmTofDigi* xDigiC = std::next(digiIt, 2)->first;
118
119 const double timeDifN = (xDigiC->GetSide() == xDigiA->GetSide()) ? xDigiC->GetTime() - xDigiB->GetTime()
120 : xDigiC->GetTime() - xDigiA->GetTime();
121
122 double posYN = fParams.fSigVel * timeDifN * 0.5;
123 if (xDigiC->GetSide() != 1.) {
124 posYN *= -1.;
125 }
126
127 if (std::abs(posYN) >= std::abs(pos.Y())) {
128 break;
129 }
130 pos.SetY(posYN);
131
132 if (xDigiC->GetSide() == xDigiA->GetSide()) {
133 xDigiA = xDigiC;
134 }
135 else {
136 xDigiB = xDigiC;
137 }
138 digiIt++;
139 }
140
141 if (std::abs(pos.Y()) > cell->sizeY * fParams.fPosYMaxScal) { // remove both digis
142 digiIt++;
143 continue;
144 }
145 // The "Strip" time is the mean time between each end
146 const double time = 0.5 * (xDigiA->GetTime() + xDigiB->GetTime());
147
148 // Weight is the total charge => sum of both ends ToT
149 const double totSum = xDigiA->GetTot() + xDigiB->GetTot();
150
151 // Now check if a hit/cluster is already started
152 if (0 < cluster.numChan()) {
153 // a cluster is already started => check distance in space/time
154 // For simplicity, just check along strip direction for now
155 // and break cluster when a not fired strip is found
156 if (!(std::abs(time - lastTime) < fParams.fdMaxTimeDist && lastChan == chan - 1
157 && std::abs(pos.Y() - lastPosY) < fParams.fdMaxSpaceDist)) {
158
159 cluster.normalize(fParams.fTimeRes);
160 cluster.finalize(*cell, fParams);
161 clustersOut.push_back(cluster);
162 cluster.reset();
163 }
164 }
165 cluster.add(pos, time, totSum, totSum);
166 digiIndRef.push_back(digiIt->second);
167 digiIndRef.push_back(std::next(digiIt)->second);
168 digiIt += 2;
169
170 lastChan = chan;
171 lastPosY = pos.Y();
172 lastTime = time;
173 AddNextChan(input, lastChan, cluster, clustersOut, digiIndRef, &lastChanPos);
174 } // while( 1 < storDigi.size() )
175
176 // Apply subtraction such that chanSize constains hit count per channel
177 chanSizes.back() = clustersOut.size() - chanSizes.back();
178
179 // In rare cases, a single digi remains and is deleted here.
180 storDigi.clear();
181 } // for( int32_t chan = 0; chan < iNbCh; chan++ )
182
183 // Now check if another hit/cluster is started
184 // and save it if it's the case.
185 if (0 < cluster.numChan()) {
186 cluster.normalize(fParams.fTimeRes);
187 cluster.finalize(*cell, fParams);
188 clustersOut.push_back(cluster);
189 chanSizes.back()++;
190 }
191 return result;
192 }
193
194 bool Clusterizer::AddNextChan(std::vector<inputType>& input, int32_t lastChan, Hit& cluster,
195 std::vector<Hit>& clustersOut, std::vector<int32_t>& digiIndRef,
196 std::vector<inputType::iterator>* lastChanPos)
197 {
198 //D.Smith 25.8.23: Why are "C" digis (position "2") not considered here?
199
200 //const int32_t iDetId = CbmTofAddress::GetUniqueAddress(iSm, iRpc, 0, 0, iSmType); //Kept for reference
201 size_t numChan = fParams.fChanPar.size();
202 int32_t chan = lastChan + 1;
203
204 while (fParams.fDeadStrips & (1 << chan)) {
205 chan++;
206 if ((size_t) chan >= numChan) {
207 return false;
208 }
209 }
210 if ((size_t) chan == numChan) {
211 return false;
212 }
213
214 inputType& storDigi = input[chan];
215 if (0 == storDigi.size()) {
216 return false;
217 }
218
219 const double clusterTime = cluster.hitTime / cluster.weightsSum;
220 const TofCell& cell = fParams.fChanPar[chan].cell;
221
222 //Get starting position in buffer
223 auto i1 = (lastChanPos == nullptr) ? storDigi.begin() : (*lastChanPos)[chan];
224
225 //Find first digi in buffer that can possibly be part of the cluster.
226 //Relies on time-order to function properly.
227 i1 = std::lower_bound(i1, storDigi.end(), clusterTime - fParams.fdMaxTimeDist,
228 [this, cell](const auto& obj, double val) {
229 return obj.first->GetTime() + cell.sizeY * fParams.fPosYMaxScal / fParams.fSigVel < val;
230 });
231
232 //Store last position in input channels to avoid unnecessary checks.
233 //Relies on time-order to function properly. Can only be used in first-level AddNextChan()
234 //calls, as time-order is not guaranteed in nested calls.
235 if (lastChanPos) {
236 (*lastChanPos)[chan] = i1;
237 }
238
239 for (; i1 < storDigi.end() - 1; i1++) {
240
241 const CbmTofDigi* xDigiA = i1->first;
242 if (xDigiA->GetTime() > clusterTime + fParams.fdMaxTimeDist) {
243 break;
244 }
245 auto i2 = i1;
246
247 while (++i2 < storDigi.end()) {
248
249 const CbmTofDigi* xDigiB = i2->first;
250 if (xDigiA->GetSide() == xDigiB->GetSide()) {
251 continue;
252 }
253
254 const double time = 0.5 * (xDigiA->GetTime() + xDigiB->GetTime());
255
256 //Continue if digis are in the past of cluster time
257 if (time <= clusterTime - fParams.fdMaxTimeDist) {
258 continue;
259 }
260
261 //Break if digis are in the future of cluster time
262 if (time >= clusterTime + fParams.fdMaxTimeDist) {
263 break;
264 }
265
266 const double timeDif = xDigiA->GetTime() - xDigiB->GetTime();
267 double posY = fParams.fSigVel * timeDif * 0.5;
268
269 //Break if position is outside of the detector
270 if (std::abs(posY) > cell.sizeY * fParams.fPosYMaxScal) {
271 break;
272 }
273
274 if (1 != xDigiA->GetSide()) {
275 posY *= -1.;
276 }
277
278 if (std::abs(posY - cluster.hitPos.Y() / cluster.weightsSum) >= fParams.fdMaxSpaceDist) {
279 continue;
280 }
281
282 // append digi pair to current cluster
283 const double posX = ((-(double) numChan / 2. + chan) + 0.5) * cell.sizeX;
284 const double totSum = xDigiA->GetTot() + xDigiB->GetTot();
285
286 ROOT::Math::XYZVector pos(posX, posY, 0.);
287 cluster.add(pos, time, totSum, totSum);
288 digiIndRef.push_back(i1->second);
289 digiIndRef.push_back(i2->second);
290
291 // remove digis at positions i1 and i2 from pool in efficient way (replaces two vector::erase calls).
292 std::move(i1 + 1, i2, i1);
293 std::move(i2 + 1, storDigi.end(), i2 - 1);
294 storDigi.resize(storDigi.size() - 2);
295
296 if (AddNextChan(input, chan, cluster, clustersOut, digiIndRef)) {
297 return true; // signal hit was already added
298 }
299 i1 = storDigi.end(); // jump to end of outer loop
300
301 break;
302 }
303 }
304 TofCell detcell = fParams.fChanPar[0].cell; //D.Smith 17.8.23: This is equivalent to using iDetId, see below
305 //D.Smith 10.8.23: Why pass iDetId here and not iChId?
306 cluster.normalize(fParams.fTimeRes);
307 cluster.finalize(detcell, fParams);
308 clustersOut.push_back(cluster);
309 cluster.reset();
310 return true;
311 }
312
313} // namespace cbm::algo::tof
Data class for expanded digital TOF information.
Definition CbmTofDigi.h:47
double GetSide() const
Channel Side.
Definition CbmTofDigi.h:161
double GetChannel() const
Channel .
Definition CbmTofDigi.h:157
double GetTime() const
Inherited from CbmDigi.
Definition CbmTofDigi.h:132
double GetTot() const
Alias for GetCharge.
Definition CbmTofDigi.h:141
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)