CbmRoot
Loading...
Searching...
No Matches
HitMerger2D.cxx
Go to the documentation of this file.
1/* Copyright (C) 2024 Facility for Antiproton and Ion Research in Europe, Darmstadt
2 SPDX-License-Identifier: GPL-3.0-only
3 Authors: Dominik Smith [committer], Alexandru Bercuci */
4
5#include "HitMerger2D.h"
6
8
9#include <functional>
10#include <numeric>
11
12
13using std::endl;
14using std::vector;
15
16namespace cbm::algo::trd
17{
18
19 //_______________________________________________________________________________
20 HitMerger2D::HitMerger2D(HitFinder2DModPar params) : fParams(params) {}
21
22 //_______________________________________________________________________________
23 HitMerger2D::outputType HitMerger2D::operator()(std::vector<inputType>& hitsRow1, std::vector<inputType>& hitsRow2)
24 {
25 std::vector<inputType*> hitData;
26
27 for (auto& elem : hitsRow1) {
28 hitData.push_back(&elem);
29 }
30 for (auto& elem : hitsRow2) {
31 hitData.push_back(&elem);
32 }
33
34 // Sort step below is in principle needed, but wasn't present in original implementation
35 // TO DO: Clarify
36 //std::sort(hitData.begin(), hitData.end(), [](const auto& h0, const auto& h1) { return h0->first.Time() < h1->first.Time(); });
37
38 for (size_t ih = 0; ih < hitData.size(); ih++) {
39 Hit* h0 = &hitData[ih]->first;
40 std::vector<DigiRec>* h0digis = &hitData[ih]->second;
41
42 if (h0->IsUsed()) continue;
43
44 for (size_t jh = ih + 1; jh < hitData.size(); jh++) {
45 Hit* h1 = &hitData[jh]->first;
46 std::vector<DigiRec>* h1digis = &hitData[jh]->second;
47
48 if (h1->IsUsed()) continue;
49
50 // basic check on Time
51 if (h1->Time() < 4000 - h0->Time()) continue; // TODO check with preoper time calibration
52 // skip next hits for being too far (10us) in the future
53 if (h1->Time() > 10000 + h0->Time()) break;
54
55 // basic check on Col
56 if (std::abs(h1->X() - h0->X()) > 2 * fParams.padSizeX) continue;
57
58 // basic check on Row
59 if (std::abs(h1->Y() - h0->Y()) > 2 * fParams.padSizeY) continue;
60
61 // go to topologic checks
62 const int a0 = CheckMerge(h0digis, h1digis);
63 if (a0 == 0) continue;
64
65 auto [hitFlag, hitF] = ProjectDigis(a0 < 0 ? h0digis : h1digis, a0 < 0 ? h1digis : h0digis);
66 // call the working algorithm
67 if (MergeHits(h0, a0, hitF)) {
68 h0->SetRowCross((bool) h1);
69 h1->SetRefId(-1);
70 }
71 }
72 }
73
74 const auto ret1 =
75 std::remove_if(hitsRow1.begin(), hitsRow1.end(), [](const auto& obj) { return obj.first.IsUsed(); });
76 hitsRow1.erase(ret1, hitsRow1.end());
77
78 const auto ret2 =
79 std::remove_if(hitsRow2.begin(), hitsRow2.end(), [](const auto& obj) { return obj.first.IsUsed(); });
80 hitsRow2.erase(ret2, hitsRow2.end());
81
82 return std::make_pair(std::move(hitsRow1), std::move(hitsRow2));
83 }
84
85
86 //_______________________________________________________________________________
87 int HitMerger2D::CheckMerge(std::vector<DigiRec>* cid, std::vector<DigiRec>* cjd)
88 {
94 bool on(kFALSE);
95 int vc[2] = {-1, -1}, vm[2] = {0};
96 double M[2] = {-1., -1.}, S[2] = {0.};
97 vector<DigiRec>::const_iterator jp[2];
98
99 for (int rowId(0); rowId < 2; rowId++) {
100 double rtMax = 0.;
101 double mdMax = 0.;
102 const std::vector<DigiRec>* vcid[2] = {cid, cjd};
103 for (auto id = vcid[rowId]->begin(); id != vcid[rowId]->end(); id++) {
104 int col;
105 GetPadRowCol((*id).GetAddressChannel(), col);
106
107 // mark max position and type
108 const double t = (*id).GetTiltCharge(on);
109 if (on && t > rtMax) {
110 vc[rowId] = col;
111 vm[rowId] = 0;
112 rtMax = t;
113 }
114 const double r = (*id).GetRectCharge(on);
115 if (on && r > rtMax) {
116 vc[rowId] = col;
117 vm[rowId] = 1;
118 rtMax = r;
119 }
120
121 double m = 0.;
122 double d = 0.;
123 if (!rowId) { // compute TR pairs on the bottom row
124 m = 0.5 * (t + r);
125 d = r - t;
126 }
127 else { // compute RT pairs on the upper row
128 auto jd = std::next(id);
129 double T = 0;
130 if (jd != vcid[rowId]->end()) T = (*jd).GetTiltCharge(on);
131 m = 0.5 * (r + T);
132 d = r - T;
133 }
134 if (std::abs(m) > 0.) d = 1.e2 * d / m;
135 if (m > mdMax) {
136 mdMax = m;
137 M[rowId] = m;
138 S[rowId] = d;
139 jp[rowId] = id;
140 }
141 }
142 }
143 int rowMax = M[0] > M[1] ? 0 : 1;
144
145 // basic check on col of the max signal
146 const int dc = vc[1] - vc[0];
147 if (dc < 0 || dc > 1) return 0;
148
149 // special care for both tilt maxima : the TT case
150 // recalculate values on the min row on neighbor column
151 if (!vm[0] && !vm[1]) {
152 if (rowMax == 0) { // modify r=1
153 double r = 0.;
154 double T = 0.;
155 if (M[1] >= 0) {
156 if (jp[1] != cjd->end()) jp[1]++;
157 if (jp[1] != cjd->end()) {
158 r = (*jp[1]).GetRectCharge(on);
159 jp[1]++;
160 if (jp[1] != cjd->end()) T = (*jp[1]).GetTiltCharge(on);
161 }
162 }
163 M[1] = 0.5 * (r + T);
164 S[1] = r - T;
165 }
166 else { // modify r=0
167 double r = 0.;
168 double t = 0.;
169 if (M[0] >= 0) {
170 if (jp[0] != cid->begin()) jp[0]--;
171 if (jp[0] != cid->begin()) {
172 r = (*jp[0]).GetRectCharge(on);
173 t = (*jp[0]).GetTiltCharge(on);
174 }
175 }
176 M[0] = 0.5 * (t + r);
177 S[0] = r - t;
178 }
179 }
180 rowMax = M[0] > M[1] ? 0 : 1;
181
182 // Build the ratio of the charge
183 const float mM = M[rowMax ? 0 : 1] / M[rowMax];
184 const float mS = std::abs(S[rowMax]), mM_l[3] = {0.15, 0.5, 1}, mM_r[3] = {0, 0.28, 1}, mS_r[3] = {43, 27, 20};
185 float dSdM[2], S0[2];
186
187 for (int i(0); i < 2; i++) {
188 dSdM[i] = (mS_r[i + 1] - mS_r[i]) / (mM_r[i + 1] - mM_r[i]);
189 S0[i] = mS_r[i] - dSdM[i] * mM_r[i];
190 }
191 const int irange = mM < mM_r[1] ? 0 : 1;
192 if (mS > S0[irange] + dSdM[irange] * mM) return 0;
193
194 for (int ia(0); ia < 3; ia++) {
195 if (mM < mM_l[ia]) return (rowMax ? 1 : -1) * (3 - ia);
196 }
197 return 0;
198 }
199
200
201 //_______________________________________________________________________________
203 {
204 int n0(hitF.fSignal.size() - 2);
205 auto [dx, dy] = hitF.GetDxDy(n0);
206
207 int typ(hitF.GetHitClass());
208 // get position correction [pw]
209 double xcorr = hitF.GetXcorr(dx, typ, 1) / fParams.padSizeX, xcorrBias(xcorr);
210 if (hitF.IsBiasX()) {
211 typ = hitF.GetHitRcClass(a0);
212 int xmap = hitF.vyM & 0xff;
213 switch (n0) {
214 case 4:
215 if (dx < 0)
216 xcorrBias += (hitF.IsBiasXleft() ? -0.12 : 0.176);
217 else
218 xcorrBias += (xmap == 53 || xmap == 80 || xmap == 113 || xmap == 117 ? -0.176 : 0.12);
219 break;
220 case 5:
221 case 7:
222 if (typ < 0) break;
223 if (xmap == 50 || xmap == 58 || xmap == 146 || xmap == 154) {
224 if (typ == 2)
225 xcorr += 0.0813;
226 else if (typ == 3) {
227 xcorr -= 0.0813;
228 typ = 2;
229 }
230 dx -= xcorr;
231 hitF.RecenterXoffset(dx);
232 xcorrBias = hitF.GetXcorr(dx, typ, 2) / fParams.padSizeX;
233 }
234 else {
235 dx -= xcorr;
236 hitF.RecenterXoffset(dx);
237 if (typ < 2)
238 xcorrBias = hitF.GetXcorr(dx, typ, 2) / fParams.padSizeX;
239 else if (typ == 2)
240 xcorrBias = 0.12;
241 else if (typ == 3)
242 xcorrBias = -0.12;
243 }
244 break;
245 default:
246 if (typ < 0)
247 break;
248 else if (typ == 2)
249 xcorr += 0.0813;
250 else if (typ == 3) {
251 xcorr -= 0.0813;
252 typ = 2;
253 }
254 dx -= xcorr;
255 hitF.RecenterXoffset(dx);
256 xcorrBias = hitF.GetXcorr(dx, typ, 2) / fParams.padSizeX;
257 break;
258 }
259 }
260 else {
261 if (typ) xcorrBias += (dx < 0 ? 1 : -1) * 0.0293;
262 }
263 std::tie(dx, dy) = hitF.CorrectPosition(dx, dy, xcorrBias, fParams.padSizeX, fParams.padSizeY);
264
265 double edx(1), edy(1), edt(60), time(-21), tdrift(100), e(200);
266 CalibrateHit(h, dx, dy, edx, edy, edt, time, tdrift, e, hitF);
267
268 return kTRUE;
269 }
270
271 //_______________________________________________________________________________
272 void HitMerger2D::CalibrateHit(Hit* h, const double dx, const double dy, const double edx, const double edy,
273 const double edt, const double time, const double tdrift, const double eloss,
274 const HitFactory2D& hitF)
275 {
276 const ROOT::Math::XYZVector& local_pad = fParams.rowPar[hitF.vrM].padPar[hitF.vcM].pos;
277 const ROOT::Math::XYZVector local(local_pad.X() + dx, local_pad.Y() + dy, local_pad.Z());
278 const ROOT::Math::XYZVector global = fParams.translation + fParams.rotation(local);
279 h->SetX(global.X());
280 h->SetY(global.Y());
281 h->SetZ(global.Z());
282 h->SetDx(edx);
283 h->SetDy(edy);
284 h->SetDz(0.);
285 h->SetDxy(0.);
286 h->SetTime(fClk * (hitF.vt0 + time) - tdrift + 30.29 + fHitTimeOff, edt);
287 h->SetELoss(eloss);
288 h->SetClassType();
289 h->SetMaxType(hitF.IsMaxTilt());
290 h->SetOverFlow(hitF.HasOvf());
291 }
292
293
294 //_______________________________________________________________________________
295 std::pair<int, HitFactory2D> HitMerger2D::ProjectDigis(std::vector<DigiRec>* cid, std::vector<DigiRec>* cjd)
296 {
302 if (cid->empty()) {
303 L_(debug) << "TrdModuleRec2D::ProjectDigis : Request cl id " << cid << " not found.";
304 return std::make_pair(0, HitFactory2D(0));
305 }
306 HitFactory2D hitF(fParams.rowPar[0].padPar.size());
307
308 std::vector<HitFactory2D::signal>& hitSig = hitF.fSignal;
309 hitF.reset();
310
311 bool on(0); // flag signal transmition
312 int n0(0), nsr(0), // no of signals in the cluster (all/rect)
313 NR(0), // no of rect signals/channel in case of RC
314 NT(0), // no of tilt signals/channel in case of RC
315 ovf(1); // over-flow flag for at least one of the digis
316 Char_t dt0(0); // cluster time offset wrt arbitrary t0
317 double err, // final error parametrization for signal
318 xc(-0.5), // running signal-pad position
319 max(0.); // max signal
320 const double re = 100.; // rect signal
321 const double te = 100.; // tilt signal
322
323 int j(0), col(-1), col0(0), col1(0), step(0), row1;
324
325 // check integrity of input
326 if (cid == nullptr || cjd == nullptr || cid->empty() || cjd->empty()) {
327 L_(debug) << "TrdModuleRec2D::ProjectDigis : Requested cluster not found.";
328 return std::make_pair(0, HitFactory2D(0));
329 }
330
331 vector<DigiRec>::const_iterator i1 = cjd->begin();
332
333 for (auto i = cid->begin(); i != cid->end(); i++, j++) {
334 const DigiRec* dg = &(*i);
335 const DigiRec* dg0 = nullptr;
336
337 // initialize
338 if (col < 0) {
339 hitF.vrM = GetPadRowCol(dg->GetAddressChannel(), col);
340 hitF.vt0 = dg->GetTimeDAQ(); // set arbitrary t0 to avoid double digis loop
341 }
342 GetPadRowCol(dg->GetAddressChannel(), col0);
343 int nt = 0; // no of tilt signals/channel in case of RC
344 int nr = 0; // no of rect signals/channel in case of RC
345
346 // read calibrated signals
347 double t = dg->GetTiltCharge(on);
348 if (on) nt = 1;
349 double r = dg->GetRectCharge(on);
350 if (on) nr = 1;
351 // look for matching neighbor digis in case of pad row cross
352 if ((dg0 = (i1 != cjd->end()) ? &(*i1) : nullptr)) {
353 row1 = GetPadRowCol(dg0->GetAddressChannel(), col1);
354 if (!step) step = hitF.vrM - row1;
355 if (col1 == col0) {
356 r += dg0->GetRectCharge(on);
357 if (on) nr++;
358 }
359 else
360 dg0 = nullptr;
361 }
362 if (step == 1 && i1 != cjd->begin()) {
363 const auto dg1 = &(*(i1 - 1));
364 GetPadRowCol(dg1->GetAddressChannel(), col1);
365 if (col1 == col0 - 1) {
366 t += dg1->GetTiltCharge(on);
367 if (on) nt++;
368 }
369 }
370 if (step == -1 && i1 != cjd->end() && i1 + 1 != cjd->end()) {
371 const auto dg1 = &(*(i1 + 1));
372 GetPadRowCol(dg1->GetAddressChannel(), col1);
373 if (col1 == col0 + 1) {
374 t += dg1->GetTiltCharge(on);
375 if (on) nt++;
376 }
377 }
378 if (dg0) i1++;
379
380 //TO DO: two blocks below might be mergable
381 // process tilt signal/time
382 char ddt = dg->GetTiltTime() - hitF.vt0; // signal time offset wrt prompt
383 if (ddt < dt0) dt0 = ddt;
384 if (abs(t) > 0) {
385 if (nt > 1) t *= 0.5;
386 err = te * (nt > 1 ? 0.707 : 1);
387 if (dg->HasTiltOvf()) {
388 ovf = -1;
389 err = 150.;
390 }
391 if (t > max) {
392 max = t;
393 hitF.vcM = j;
394 hitF.SetMaxTilt(1);
395 hitF.viM = hitSig.size();
396 }
397 }
398 else
399 err = 300.;
400
401 hitSig.emplace_back(t, err, ddt, xc, 0.035);
402 xc += 0.5;
403
404 // process rect signal/time
405 ddt = dg->GetRectTime() - hitF.vt0; // signal time offset wrt prompt
406 if (ddt < dt0) dt0 = ddt;
407 if (abs(r) > 0) {
408 nsr++;
409 if (nr > 1) r *= 0.5;
410 err = re * (nr > 1 ? 0.707 : 1);
411 if (dg->HasRectOvf()) {
412 ovf = -1;
413 err = 150.;
414 }
415 if (r > max) {
416 max = r;
417 hitF.vcM = j;
418 hitF.SetMaxTilt(0);
419 hitF.viM = hitSig.size();
420 }
421 }
422 else
423 err = 300.;
424
425 hitSig.emplace_back(r, err, ddt, xc, 0.);
426 xc += 0.5;
427 NR += nr;
428 NT += nt;
429 } // for (auto i = cid->begin(); i != cid->end(); i++, j++)
430
431
432 //TO DO: two blocks below might be mergable
433 // add front and back anchor points if needed
434 if (std::abs(hitSig[0].s) > 1.e-3) {
435 xc = hitSig[0].x;
436 char ddt = hitSig[0].t;
437 hitSig.emplace(hitSig.begin(), 0., 300., ddt, xc - 0.5, 0.);
438 hitF.viM++;
439 }
440 int n(hitSig.size() - 1);
441 if (std::abs(hitSig[n].s) > 1.e-3) {
442 xc = hitSig[n].x + 0.5;
443 char ddt = hitSig[n].t;
444 hitSig.emplace_back(0., 300., ddt, xc, 0.035);
445 }
446
447 n0 = hitSig.size() - 2;
448 // compute cluster asymmetry
449 int nR = n0 + 1 - hitF.viM;
450 if (nR == hitF.viM) {
451 hitF.SetSymmHit(1);
452 if (hitSig.size() % 2) {
453 double LS(0.), RS(0.);
454 for (UChar_t idx(0); idx < hitF.viM; idx++)
455 LS += hitSig[idx].s;
456 for (uint idx(hitF.viM + 1); idx < hitSig.size(); idx++)
457 RS += hitSig[idx].s;
458 hitF.SetLeftSgn(LS < RS ? 0 : 1);
459 }
460 }
461 else {
462 hitF.SetSymmHit(0);
463 if (hitF.viM < nR)
464 hitF.SetLeftHit(0);
465 else if (hitF.viM > nR)
466 hitF.SetLeftHit(1);
467 }
468 // recenter time and space profile
469 hitF.vt0 += dt0;
470 for (auto& sig : hitSig) {
471 sig.t -= dt0;
472 sig.x -= hitF.vcM;
473 }
474 hitF.vcM += col;
475
476 // check if all signals have same significance
477 const int nmissX = 2 * nsr - NR;
478 if (nmissX) {
479 hitF.SetBiasX(1);
480 for (UChar_t idx(1); idx < hitF.viM; idx++) {
481 if (hitSig[idx].xe > 0.) continue; // select rect signals
482 if (hitSig[idx].se > re * 0.8) hitF.SetBiasXleft(1);
483 }
484 if (hitSig[hitF.viM].xe <= 0. && hitSig[hitF.viM].se > re * 0.8) hitF.SetBiasXmid(1);
485 for (UChar_t idx(hitF.viM + 1); idx < hitSig.size() - 1; idx++) {
486 if (hitSig[idx].xe > 0.) continue; // select rect signals
487 if (hitSig[idx].se > re * 0.8) hitF.SetBiasXright(1);
488 }
489 }
490 else {
491 hitF.SetBiasX(0);
492 }
493
494 const int nmissY = 2 * n0 - 2 * nsr - NT;
495 if (nmissY) {
496 hitF.SetBiasY();
497 for (UChar_t idx(1); idx < hitF.viM; idx++) {
498 if (hitSig[idx].xe > 0. && hitSig[idx].se > te * 0.8) hitF.SetBiasYleft(1); // select tilt signals
499 }
500 if (hitSig[hitF.viM].xe > 0. && hitSig[hitF.viM].se > te * 0.8) hitF.SetBiasYmid(1);
501 for (UChar_t idx(hitF.viM + 1); idx < hitSig.size() - 1; idx++) {
502 if (hitSig[idx].xe > 0. && hitSig[idx].se > te * 0.8) hitF.SetBiasYright(1); // select tilt signals
503 }
504 }
505 else {
506 hitF.SetBiasY(0);
507 }
508
509 if (ovf < 0) hitF.SetOvf();
510 return std::make_pair(ovf * (hitSig.size() - 2), hitF);
511 }
512
513 int HitMerger2D::GetPadRowCol(int address, int& c)
514 {
515 c = address % fParams.rowPar[0].padPar.size();
516 return address / fParams.rowPar[0].padPar.size();
517 }
518
519} // namespace cbm::algo::trd
#define L_(level)
friend fscal max(fscal x, fscal y)
Data class with information on a STS local track.
uint64_t GetTimeDAQ() const
Getter for global DAQ time [clk]. Differs for each ASIC. In FASP case DAQ time is already stored in f...
Definition CbmTrdDigi.h:158
int32_t GetAddressChannel() const
Getter read-out id.
bool HasRectOvf() const
Definition DigiRec.h:58
double GetTiltCharge(bool &on) const
Return calibrated tilt signal.
Definition DigiRec.h:41
double GetTiltTime() const
Return calibrated tilt time [ns].
Definition DigiRec.h:43
bool HasTiltOvf() const
Definition DigiRec.h:59
double GetRectCharge(bool &on) const
Return calibrated rect signal.
Definition DigiRec.h:46
double GetRectTime() const
Return calibrated rect time [ns].
Definition DigiRec.h:48
void SetBiasXmid(bool set=1)
std::pair< double, double > GetDxDy(const int n0)
void SetBiasYright(bool set=1)
std::pair< double, double > CorrectPosition(double dx, double dy, const double xcorr, const double padSizeX, const double padSizeY)
Shift graph representation to [-0.5, 0.5].
void SetBiasYleft(bool set=1)
void SetMaxTilt(bool set=1)
void SetBiasYmid(bool set=1)
void SetSymmHit(bool set=1)
void SetBiasXright(bool set=1)
uint16_t vyM
index of maximum signal in the projection
std::vector< signal > fSignal
void SetLeftSgn(bool set=1)
uint8_t vcM
start time of current hit [clk]
int GetHitRcClass(int a0) const
Hit classification wrt signal bias.
void SetBiasXleft(bool set=1)
int GetHitClass() const
Hit classification wrt center pad.
double GetXcorr(double dx, int typ, int cls=0) const
void SetLeftHit(bool set=1)
HitFinder2DModPar fParams
Parameter container.
Definition HitMerger2D.h:82
void CalibrateHit(Hit *h, const double dx, const double dy, const double edx, const double edy, const double edt, const double time, const double tdrift, const double eloss, const HitFactory2D &hitF)
bool MergeHits(Hit *h, int a0, HitFactory2D &hitF)
Algorithm for hit merging.
std::pair< std::vector< inputType >, std::vector< inputType > > outputType
Definition HitMerger2D.h:43
outputType operator()(std::vector< inputType > &hitsRow1, std::vector< inputType > &hitsRow2)
Steering routine for building hits.
std::pair< int, HitFactory2D > ProjectDigis(std::vector< DigiRec > *cid, std::vector< DigiRec > *cjd)
HitMerger2D()
Default constructor.
Definition HitMerger2D.h:46
int GetPadRowCol(int address, int &c)
Addressing ASIC on module based on id.
int CheckMerge(std::vector< DigiRec > *cid, std::vector< DigiRec > *cjd)
Implement topologic cuts for hit merging.
A light-weight TRD hit class for online reconstruction, based on CbmTrdHit. .
void SetRowCross(bool set=true)
Mark hit reconstructed between pad rows.
ROOT::Math::Rotation3D rotation
std::vector< HitFinder2DRowPar > rowPar
ROOT::Math::XYZVector translation