SND@LHC Software
Loading...
Searching...
No Matches
sndSciFiTools.cxx
Go to the documentation of this file.
1#include "sndSciFiTools.h"
2
3#include <numeric>
4#include <algorithm>
5
6#include "TH1F.h"
7#include "FairLogger.h"
8#include "TClonesArray.h"
9#include "sndScifiHit.h"
10#include "ROOT/TSeq.hxx"
11#include "Scifi.h"
12#include "TROOT.h"
13#include "ROOT/RRangeCast.hxx"
14
15void snd::analysis_tools::getSciFiHitsPerStation(const TClonesArray *digiHits, std::vector<int> &horizontal_hits,
16 std::vector<int> &vertical_hits)
17{
18
19 // Clear hits per plane vectors
20 std::fill(horizontal_hits.begin(), horizontal_hits.end(), 0);
21 std::fill(vertical_hits.begin(), vertical_hits.end(), 0);
22
23 // Add valid hits to hits per plane vectors
24 sndScifiHit *hit;
25 TIter hitIterator(digiHits);
26
27 while ((hit = dynamic_cast<sndScifiHit *>(hitIterator.Next()))) {
28 if (hit->isValid()) {
29 int station = hit->GetStation();
30 if (hit->isVertical()) {
31 vertical_hits[station - 1]++;
32 } else {
33 horizontal_hits[station - 1]++;
34 }
35 }
36 }
37}
38
39int snd::analysis_tools::getTotalSciFiHits(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits)
40{
41 return std::accumulate(horizontal_hits.begin(), horizontal_hits.end(),
42 std::accumulate(vertical_hits.begin(), vertical_hits.end(), 0));
43}
44
45int snd::analysis_tools::getTotalSciFiHits(const TClonesArray *digiHits)
46{
47 std::vector<int> horizontal_hits = std::vector<int>(5);
48 std::vector<int> vertical_hits = std::vector<int>(5);
49
50 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
51
52 return getTotalSciFiHits(horizontal_hits, vertical_hits);
53}
54
55std::vector<float>
56snd::analysis_tools::getFractionalHitsPerScifiPlane(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits)
57{
58
59 int total_hits = getTotalSciFiHits(horizontal_hits, vertical_hits);
60
61 std::vector<float> fractional_hits_per_station = std::vector<float>(horizontal_hits.size());
62
63 std::transform(horizontal_hits.begin(), horizontal_hits.end(), vertical_hits.begin(),
64 fractional_hits_per_station.begin(),
65 [&total_hits](const auto &hor, const auto &ver) { return ((float)hor + ver) / total_hits; });
66
67 return fractional_hits_per_station;
68}
69
70std::vector<float> snd::analysis_tools::getFractionalHitsPerScifiPlane(const TClonesArray *digiHits)
71{
72 std::vector<int> horizontal_hits = std::vector<int>(5);
73 std::vector<int> vertical_hits = std::vector<int>(5);
74
75 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
76
77 return getFractionalHitsPerScifiPlane(horizontal_hits, vertical_hits);
78}
79
80int snd::analysis_tools::findScifiStation(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits,
81 float threshold)
82{
83
84 std::vector<float> frac = getFractionalHitsPerScifiPlane(horizontal_hits, vertical_hits);
85
86 std::vector<float> frac_sum = std::vector<float>(frac.size());
87
88 std::partial_sum(frac.begin(), frac.end(), frac_sum.begin());
89
90 std::vector<float>::iterator station =
91 std::find_if(frac_sum.begin(), frac_sum.end(), [&threshold](const auto &f) { return f > threshold; });
92
93 return station - frac_sum.begin() + 1;
94}
95
96int snd::analysis_tools::findScifiStation(const TClonesArray *digiHits, float threshold)
97{
98 std::vector<int> horizontal_hits = std::vector<int>(5);
99 std::vector<int> vertical_hits = std::vector<int>(5);
100
101 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
102
103 return findScifiStation(horizontal_hits, vertical_hits, threshold);
104}
105
106// Auxiliar function to check whether hits are valid or not and whether they are within the same
107// station and orientation as the reference hit we are comparing it to
108bool validateHit(sndScifiHit *aHit, int ref_station, bool ref_orientation)
109{
110
111 if (!(aHit->isValid())) {
112 return false;
113 }
114 if (aHit->GetStation() != ref_station) {
115 return false;
116 }
117 if (aHit->isVertical() != ref_orientation) {
118 return false;
119 }
120
121 return true;
122}
123
124// Getting the max for the ScifiHits timing distribution in order to select hits within \pm 3ns
125// min_x and max_x should be given in ns
126// The code automatically filters for hits within the same station and orientation as the first hit
127float snd::analysis_tools::peakScifiTiming(const TClonesArray &digiHits, int bins, float min_x, float max_x, bool isMC)
128{
129
130 if (digiHits.GetEntries() <= 0) {
131 LOG(warning) << "digiHits has no valid SciFi Hits and as such no maximum for the timing distribution.";
132 return -1.;
133 }
134
135 TH1F ScifiTiming("Timing", "Scifi Timing", bins, min_x, max_x);
136
137 Scifi *ScifiDet = dynamic_cast<Scifi*> (gROOT->GetListOfGlobals()->FindObject("Scifi") );
138 auto* first_hit = static_cast<sndScifiHit*>(digiHits[0]);
139 int refStation = first_hit->GetStation();
140 bool refOrientation = first_hit->isVertical();
141 float hitTime = -1.0;
142 float timeConversion = 1.;
143 if (!isMC) {
144 timeConversion = 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
145 }
146
147 for (auto *p : digiHits) {
148 auto *hit = dynamic_cast<sndScifiHit *>(p);
149 if (!validateHit(hit, refStation, refOrientation)) {
150 continue;
151 }
152 hitTime = hit->GetTime() * timeConversion;
153 if (!isMC){
154 int id_hit = hit ->GetDetectorID();
155 hitTime = ScifiDet->GetCorrectedTime(id_hit, hitTime, 0);
156 }
157 if (hitTime < min_x || hitTime > max_x) {
158 continue;
159 }
160 ScifiTiming.Fill(hitTime);
161 hitTime = -1.0;
162 }
163
164 float peakTiming = (ScifiTiming.GetMaximumBin() - 0.5) * (max_x - min_x) / bins + min_x;
165
166 return peakTiming;
167}
168
169// Getting all the Scifi hits for a specific station and orientation
170std::unique_ptr<TClonesArray>
171snd::analysis_tools::getScifiHits(const TClonesArray &digiHits, int station, bool orientation)
172{
173
174 auto selectedHits = std::make_unique<TClonesArray>("sndScifiHit");
175
176 int i = 0;
177 for (auto *p : digiHits) {
178 auto *hit = dynamic_cast<sndScifiHit *>(p);
179 if (!validateHit(hit, station, orientation)) {
180 continue;
181 }
182 new ((*selectedHits)[i++]) sndScifiHit(*hit);
183 }
184
185 return selectedHits;
186}
187
188// Getting all the Scifi hits for a specific station and orientation, taking into account a filter
189// of \pm range around the peak of the timing distribution for said Scifi plane
190// selection_parameters is the number of bins for the histogram, min_x, max_x and time window for
191// the Scifi hits in ns
192// make_selection == true allows you to first perform a selection of the relevant hits, and only
193// afterwards apply the filter. This is recomended for when the selection has not been made
194// previously
195std::unique_ptr<TClonesArray> snd::analysis_tools::selectScifiHits(const TClonesArray &digiHits, int station,
196 bool orientation, int bins_x, float min_x,
197 float max_x, float time_lower_range,
198 float time_upper_range, bool make_selection,
199 bool isMC)
200{
201
202 if (bins_x < 1) {
203 LOG(FATAL) << "bins_x in selection_parameters cannot be <1. Consider using the default value of 52 instead.";
204 }
205 if (min_x > max_x) {
206 LOG(warning) << "In selection_parameters min_x > max_x. Values will be swapped.";
207 float aux_float = min_x;
208 min_x = max_x;
209 max_x = aux_float;
210 }
211 if (min_x < 0.0) {
212 LOG(warning) << "In selection_parameters min_x < 0.0. Consider using the default value of 0.0.";
213 }
214 if (max_x < 0.0) {
215 LOG(FATAL) << "In selection_parameters max_x < 0.0. Consider using the default value of 26.0.";
216 }
217 if (time_lower_range <= 0.0) {
218 LOG(FATAL) << "In selection_parameters time_lower_range <= 0.0. Value should always be positive, in ns.";
219 }
220 if (time_upper_range <= 0.0) {
221 LOG(FATAL) << "In selection_parameters time_upper_range <= 0.0. Value should always be positive, in ns.";
222 }
223
224 auto filteredHits = std::make_unique<TClonesArray>("sndScifiHit", digiHits.GetEntries());
225
226 float peakTiming = -1.0;
227
228 float timeConversion = 1.;
229 if (!isMC) {
230 timeConversion = 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
231 }
232
233 if (make_selection) {
234
235 auto selectedHits = getScifiHits(digiHits, station, orientation);
236
237 peakTiming = peakScifiTiming(*selectedHits, bins_x, min_x, max_x, isMC);
238
239 int i = 0;
240 for (auto *p : *selectedHits) {
241 auto *hit = dynamic_cast<sndScifiHit *>(p);
242 if (!validateHit(hit, station, orientation)) {
243 continue;
244 }
245 if ((peakTiming - time_lower_range > hit->GetTime() * timeConversion) ||
246 (hit->GetTime() * timeConversion > peakTiming + time_upper_range)) {
247 continue;
248 }
249 new ((*filteredHits)[i++]) sndScifiHit(*hit);
250 }
251
252 } else {
253 // Does not create selectedHits and just uses digiHits (not unique_ptr)
254
255 peakTiming = peakScifiTiming(digiHits, bins_x, min_x, max_x, isMC);
256
257 int i = 0;
258 for (auto *p : digiHits) {
259 auto *hit = dynamic_cast<sndScifiHit *>(p);
260 if (!validateHit(hit, station, orientation)) {
261 continue;
262 }
263 if ((peakTiming - time_lower_range > hit->GetTime() * timeConversion) ||
264 (hit->GetTime() * timeConversion > peakTiming + time_upper_range)) {
265 continue;
266 }
267 new ((*filteredHits)[i++]) sndScifiHit(*hit);
268 }
269 }
270
271 return filteredHits;
272}
273
274// Takes a map with 4 or 5 arguments as an input {"bins_x", "min_x", "max_x", "time_lower_range",
275// "time_upper_range"}
276// User may foreit "time_upper_range" and function will assume a symmetric time interval
277std::unique_ptr<TClonesArray>
278snd::analysis_tools::selectScifiHits(const TClonesArray &digiHits, int station, bool orientation,
279 const std::map<std::string, float> &selection_parameters, bool make_selection,
280 bool isMC)
281{
282
283 if ((selection_parameters.find("bins_x") == selection_parameters.end()) ||
284 (selection_parameters.find("min_x") == selection_parameters.end()) ||
285 (selection_parameters.find("max_x") == selection_parameters.end()) ||
286 (selection_parameters.find("time_lower_range") == selection_parameters.end())) {
287 LOG(FATAL) << "In order to use method 0 please provide the correct selection_parameters. Consider the default = "
288 "{{\"bins_x\", 52.}, {\"min_x\", 0.}, {\"max_x\", 26.}, {\"time_lower_range\", "
289 "1E9/(2*ShipUnit::snd_freq/ShipUnit::hertz))}, {\"time_upper_range\", "
290 "2E9/(ShipUnit::snd_freq/ShipUnit::hertz)}}";
291 }
292
293 float time_upper_range = -1.;
294
295 if (selection_parameters.find("time_upper_range") == selection_parameters.end()) {
296 time_upper_range = selection_parameters.at("time_lower_range");
297 } else {
298 time_upper_range = selection_parameters.at("time_upper_range");
299 }
300
301 return selectScifiHits(digiHits, station, orientation, int(selection_parameters.at("bins_x")),
302 selection_parameters.at("min_x"), selection_parameters.at("max_x"),
303 selection_parameters.at("time_lower_range"), time_upper_range, make_selection,
304 isMC);
305}
306
307std::unique_ptr<TClonesArray>
308snd::analysis_tools::filterScifiHits(const TClonesArray &digiHits,
309 const std::map<std::string, float> &selection_parameters, int method,
310 std::string setup, bool isMC)
311{
312 TClonesArray supportArray("sndScifiHit", 0);
313 auto filteredHits = std::make_unique<TClonesArray>("sndScifiHit", digiHits.GetEntries());
314 int filteredHitsIndex = 0;
315 int ScifiStations = 5;
316 if (setup == "H8") {
317 ScifiStations = 4;
318 } else {
319 LOG(info) << "\"TI18\" setup will be used by default, please provide \"H8\" for the Testbeam setup.";
320 }
321
322 TIter hitIterator(&supportArray);
323
324 if (method == 0) {
325
326 if ((selection_parameters.find("bins_x") == selection_parameters.end()) ||
327 (selection_parameters.find("min_x") == selection_parameters.end()) ||
328 (selection_parameters.find("max_x") == selection_parameters.end()) ||
329 (selection_parameters.find("time_lower_range") == selection_parameters.end())) {
330 LOG(FATAL) << "In order to use method 0 please provide the correct selection_parameters. Consider the default "
331 "= {{\"bins_x\", 52.}, {\"min_x\", 0.}, {\"max_x\", 26.}, {\"time_lower_range\", "
332 "1E9/(2*ShipUnit::snd_freq/ShipUnit::hertz)}, {\"time_upper_range\", "
333 "1.2E9/(ShipUnit::snd_freq/ShipUnit::hertz)}}";
334 }
335
336 // This is overwriting the previous arrays with the newest one
337 for (auto station : ROOT::MakeSeq(1, ScifiStations + 1)) {
338 for (auto orientation : {false, true}) {
339
340 auto supportArray_p = selectScifiHits(digiHits, station, orientation, selection_parameters, true, isMC);
341 for (auto *p : *supportArray_p) {
342 auto *hit = dynamic_cast<sndScifiHit *>(p);
343 if (hit->isValid()) {
344 new ((*filteredHits)[filteredHitsIndex++]) sndScifiHit(*hit);
345 }
346 }
347 }
348 }
349 } else {
350 LOG(error) << "Please provide a valid time filter method from:";
351 LOG(error) << "(0): Events within \\mp time_lower_range time_upper_range of the peak of the time distribution "
352 "for Scifi Hits within each station and orientation";
353 LOG(FATAL) << "selection_parameters = {bins_x, min_x, max_x, time_lower_range, time_upper_range}.";
354 }
355 return filteredHits;
356}
357
358std::unique_ptr<TClonesArray>
359snd::analysis_tools::filterScifiHits(const TClonesArray &digiHits, int method, std::string setup, bool isMC)
360{
361
362 std::map<std::string, float> selection_parameters;
363
364 if (method == 0) {
365
366 selection_parameters["bins_x"] = 52.0;
367 selection_parameters["min_x"] = 0.0;
368 selection_parameters["max_x"] = 26.0;
369 selection_parameters["time_lower_range"] = 1E9 / (2 * ShipUnit::snd_freq / ShipUnit::hertz);
370 selection_parameters["time_upper_range"] = 1.2E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
371
372 } else {
373 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
374 }
375
376 return filterScifiHits(digiHits, selection_parameters, method, setup, isMC);
377}
378
379// Caculate the number of the SiPM channel in the whole station by inputing its number in the
380// sndScifiHit->GetChannelID() format
382{
383
384 int ref_matAux = reference_SiPM % 100000;
385 int ref_mat = ref_matAux / 10000;
386 int ref_arrayAux = reference_SiPM % 10000;
387 int ref_array = ref_arrayAux / 1000;
388 int ref_channel = reference_SiPM % 1000;
389 int referenceChannel = ref_mat * 4 * 128 + ref_array * 128 + ref_channel;
390
391 return referenceChannel;
392}
393
394int snd::analysis_tools::densityScifi(int reference_SiPM, const TClonesArray &digiHits, int radius, int min_hit_density,
395 bool min_check)
396{
397
398 int hit_density = 0;
399
400 bool orientation = false;
401 if (int(reference_SiPM / 100000) % 10 == 1) {
402 orientation = true;
403 }
404 int ref_station = reference_SiPM / 1000000;
405 int referenceChannel = calculateSiPMNumber(reference_SiPM);
406
407 for (auto *p : digiHits) {
408 auto *hit = dynamic_cast<sndScifiHit *>(p);
409 if (!validateHit(hit, ref_station, orientation)) {
410 continue;
411 }
412 int hitChannel = calculateSiPMNumber(hit->GetChannelID());
413 if (radius == -1) {
414 hit_density++;
415 } else {
416 if (hitChannel > referenceChannel + radius) {
417 break;
418 }
419 if (abs(referenceChannel - hitChannel) <= radius) {
420 hit_density++;
421 }
422 }
423
424 if (min_check && (hit_density >= min_hit_density)) {
425 break;
426 }
427 }
428
429 return hit_density;
430}
431
432// Perform a density check for a specific station and orientation (dafault is horizontal)
433bool snd::analysis_tools::densityCheck(const TClonesArray &digiHits, int radius, int min_hit_density, int station,
434 bool orientation)
435{
436
437 if (digiHits.GetEntries() <= 0) {
438 return false;
439 }
440
441 if (radius <= 0) {
442 LOG(FATAL) << "Radius<=0. Please provide a radius bigger than 0.";
443 return false;
444 }
445
446 if (min_hit_density < 0) {
447 LOG(FATAL) << "Min_hit_density < 0. Please provide a min_hit_density >= 0.";
448 return false;
449 }
450
451 if (min_hit_density > 2 * radius) {
452 LOG(warning) << "Warning! Radius of density check does not allow for the required minimum density!";
453 return false;
454 }
455
456 // Creating a vector that stores the fired SiPM channels (should already be ordered but we sort
457 // afterwards to make sure)
458 std::vector<int> fired_channels{};
459
460 // Only fill the set with hits from the same station and with the same orientation as given in
461 // argument (false==horizontal and true==vertical)
462 for (auto *p : digiHits) {
463 auto *hit = dynamic_cast<sndScifiHit *>(p);
464 if (!validateHit(hit, station, orientation)) {
465 continue;
466 }
467 fired_channels.push_back(calculateSiPMNumber(hit->GetChannelID()));
468 }
469
470 int n_fired_channels = fired_channels.size();
471
472 if (n_fired_channels < min_hit_density) {
473 return false;
474 }
475
476 // Looping over the ordered hits, checking whether within an interval of "min_hit_density" the
477 // difference between the channel IDs is smaller than "radius". If so, then we have the required
478 // density. Else, we check the next combination until we meet the criterion, or end the loop,
479 // returning false
480 std::sort(fired_channels.begin(), fired_channels.end());
481 for (int i = 0; i < n_fired_channels - min_hit_density; i++) {
482
483 if (fired_channels[i + min_hit_density - 1] - fired_channels[i] <= radius * 2) {
484 return true;
485 }
486 }
487 return false;
488}
489
490// Retrieve the target block where the shower starts
491// Method 0 checks for a minimum number of hits in a specific range (2*radius) in a station to
492// consider that a shower as started before said station. User must provide a map
493//"selection_parameters" with at least 2 elements "radius" and "min_hit_density"
494// In order to only require 1 orientation to pass the criterion, "selection_parameters" may also
495// include "orientation", which can be 0. and 1. for the horizontal and vertical orientations respec.
496int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits,
497 const std::map<std::string, float> &selection_parameters, int method,
498 std::string setup)
499{
500
501 int totalScifiStations = 5;
502 if (setup == "H8") {
503 totalScifiStations = 4;
504 } else {
505 LOG(info) << "\"TI18\" setup will be used by default, please provide \"H8\" for the Testbeam setup.";
506 }
507
508 // There is always 1 more Scifi Station than a target block. As such, showerStart == totalScifiStations
509 // means that the shower did not start developing in the target, before the last Scifi Station
510 int showerStart = totalScifiStations;
511
512 if (method == 0) {
513
514 if ((selection_parameters.find("radius") == selection_parameters.end()) ||
515 (selection_parameters.find("min_hit_density") == selection_parameters.end())) {
516 LOG(FATAL)
517 << "Argument of select_parameters is incorrect. Please provide a map with the arguments \"radius\" and "
518 "\"min_hit_density\". Consider using the default values of {{\"radius\",64}, {\"min_hit_density\",36}}.";
519 }
520
521 for (int scifiStation = 1; scifiStation <= totalScifiStations; scifiStation++) {
522
523 // For each ScifiStation we check whether we see clusters with the desired parameters
524 // By default, we require passing the check on both the horizontal and vertical mats
525 // In case selection_parameters["orientation"] was provided, we set the NOT CHOSEN orientation
526 // as true by default, so as to only need to pass the chosen orientation check
527 bool horizontalCheck = false;
528 bool verticalCheck = false;
529 if (selection_parameters.find("orientation") != selection_parameters.end()) {
530 if (int(selection_parameters.at("orientation")) == 0) {
531 verticalCheck = true;
532 }
533 if (int(selection_parameters.at("orientation")) == 1) {
534 horizontalCheck = true;
535 }
536 }
537 horizontalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
538 int(selection_parameters.at("min_hit_density")), scifiStation, false) ||
539 horizontalCheck);
540 verticalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
541 int(selection_parameters.at("min_hit_density")), scifiStation, true) ||
542 verticalCheck);
543 if ((horizontalCheck) && (verticalCheck)) {
544 showerStart = scifiStation - 1;
545 return showerStart;
546 }
547 }
548 }
549
550 return showerStart;
551}
552
553int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits, int method, std::string setup)
554{
555
556 if (method != 0) {
557 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
558 }
559
560 std::map<std::string, float> selection_parameters = {{"radius", 64.}, {"min_hit_density", 36.}};
561
562 return showerInteractionWall(digiHits, selection_parameters, method, setup);
563}
564
565double computeMean(const std::vector<double>& values)
566{
567 double sum = std::accumulate(values.begin(), values.end(), 0.0);
568 double mean = sum / values.size();
569 return mean;
570}
571
572std::pair<double, double>
573snd::analysis_tools::findCentreOfGravityPerStation(const TClonesArray* digiHits, int station, Scifi* ScifiDet)
574{
575 if (!digiHits) {
576 LOG(ERROR) << "Error: digiHits is null in findCentreOfGravityPerStation";
577 }
578
579 auto [x_positions, y_positions] = snd::analysis_tools::hitPositionVectorsPerStation(digiHits, station, ScifiDet);
580
581 if (x_positions.empty()) {
582 LOG(ERROR) << "Error: No hits enter.";
583 }
584 double meanX = computeMean(x_positions);
585 if (y_positions.empty()) {
586 LOG(ERROR) << "Error: No hits enter.";
587 }
588 double meanY = computeMean(y_positions);
589 return {meanX, meanY};
590}
591
592std::pair<std::vector<double>,std::vector<double>> snd::analysis_tools::hitPositionVectorsPerStation(const TClonesArray *digiHits, int station, Scifi* ScifiDet){
593 /*This function returns the hit vector position for the digiHits in both orientations
594 Arguments:
595 digiHits: A TClonesArray containing the hits in the SciFi detector.
596 station: The station number for which to retrieve the hit positions.
597 ScifiDet: A pointer to the Scifi detector object to retrieve SiPM positions.
598 Returns:
599 A pair of vectors containing the x and y positions of the hits in the specified station.
600 If no hits are found, it returns empty vectors and logs an error message.
601 */
602
603 if (!digiHits) {
604 LOG(ERROR) << "Error: digiHits is null";
605 }
606 std::vector<double> x_positions;
607 std::vector<double> y_positions;
608
609 TVector3 A, B;
610 for (auto* hit : ROOT::RRangeCast<sndScifiHit*, false, decltype(*digiHits)>(*digiHits)) {
611 if (!hit || !hit->isValid()) {
612 continue;
613 }
614 if (hit->GetStation() != station) {
615 continue;
616 }
617 ScifiDet->GetSiPMPosition(hit->GetDetectorID(), A, B);
618 if (hit->isVertical()) {
619 x_positions.push_back((A.X() + B.X()) * 0.5);
620 }
621 else {
622 y_positions.push_back((A.Y() + B.Y()) * 0.5);
623 }
624 }
625 if (x_positions.empty()) {
626 LOG(ERROR) << "Error: No hits enter.";
627 }
628 if (y_positions.empty()) {
629 LOG(ERROR) << "Error: No hits enter.";
630 }
631 return {x_positions, y_positions};
632}
633
634
635double hitWeightComputation(std::vector<double> hit_position) {
636 /* This function returns the summation of the hit weights
637 where a hit weight is the number of neighbouring hits within 1 cm position (default width).
638
639 Arguments:
640 hit_position: A vector containing the positions of the hits in a specific SciFi station.
641
642 Returns:
643 The sum of the weights of the hits in the vector.
644 Returns 0 if the vector is empty or if the sum of weights is 0.
645 */
646
647 if (hit_position.empty()) {
648 LOG(INFO) << "Warning: The hit position vector is empty." << std::endl;
649 return 0; // Return 0 if the vector is empty
650 }
651
652 int n_hits = hit_position.size();
653 double width = 1.0; // Width around the hit to consider as a neighbour, in cm
654 std::vector<double> weights; // Vector to store the weights of each hit
655
656 // Sorting the hits for efficient neighbour counting
657 std::sort(hit_position.begin(), hit_position.end());
658
659 // Initialize pointers for the sliding window.
660 // Both pointers will only move forward (or stay put) across the outer loop iterations.
661 int left_ptr = 0;
662 int right_ptr = 0;
663
664 // Loop through each hit position to calculate its neighbour count
665 for (int i = 0; i < n_hits; i++) {
666 double specific_hit = hit_position[i];
667
668 // Move right_ptr forward to include all hits within (specific_hit + width).
669 // It will point to the first element *just outside* the right boundary of the window.
670 while (right_ptr < n_hits && hit_position[right_ptr] <= specific_hit + width) {
671 right_ptr++;
672 }
673
674 // Move left_ptr forward to exclude all hits *less than* (specific_hit - width).
675 // It will point to the first element *just inside* or at the left boundary of the window.
676 while (left_ptr < n_hits && hit_position[left_ptr] < specific_hit - width) {
677 left_ptr++;
678 }
679
680 // Calculate neighbouring hits within the window:
681 // The number of hits in the current window is (right_ptr - left_ptr).
682 // This count includes the 'specific_hit' itself.
683 double count_in_window = right_ptr - left_ptr;
684
685 // Subtract 1 to exclude the 'specific_hit' itself from the neighbour count.
686 // We must ensure that 'specific_hit' (hit_position[i]) is actually within the window
687 // defined by left_ptr and right_ptr. Since the array is sorted and we are iterating
688 // through 'i', hit_position[i] will always be between hit_position[left_ptr] and
689 // hit_position[right_ptr-1] (if left_ptr <= i < right_ptr).
690 double neighbour_no_of_hits = count_in_window - 1;
691
692 // Ensure the neighbour count is non-negative
693 if (neighbour_no_of_hits < 0) {
694 neighbour_no_of_hits = 0;
695 }
696
697 weights.push_back(neighbour_no_of_hits);
698 }
699
700 // Calculate the sum of all computed weights
701 double sum_weights = std::accumulate(weights.begin(), weights.end(), 0.0);
702
703 return sum_weights;
704}
705
706std::pair<double,double> snd::analysis_tools::hitDensityPerStation(const TClonesArray *digiHits, int station, Scifi* ScifiDet){
707 /*
708 This function returns the hit density which is described as the summation of the hit weights
709 where a hit weigth is the number of neighbouring hits within 1 cm postion (default width)
710 arguments: hit position vector : vector ontaining the positions of the hits in a specific SciFi station
711 returns: the sum of the weights of the hits in the vector
712 If the vector is empty, it returns 0.
713 If the sum of weights is 0, it returns 0.
714 */
715 std::pair<std::vector<double>, std::vector<double>> hit_position_vec = hitPositionVectorsPerStation(digiHits, station, ScifiDet);
716 std::vector<double> hit_position_x = hit_position_vec.first; // Assuming we are interested in X positions
717 std::vector<double> hit_position_y = hit_position_vec.second; // Assuming we are interested in Y positions
718
719 if (hit_position_x.size()==0 && hit_position_y.size()==0) {
720 LOG(INFO)<< "Warning: The hit position vector is empty." << std::endl;
721 return {0.0,0.0}; // Check if the vector is empty
722 }
723
724 double sum_weights_x = hitWeightComputation(hit_position_x);
725 double sum_weights_y = hitWeightComputation(hit_position_y);
726
727 return {sum_weights_x, sum_weights_y};
728}
729
Definition Scifi.h:20
void GetSiPMPosition(Int_t SiPMChan, TVector3 &A, TVector3 &B)
Definition Scifi.cxx:727
Double_t GetCorrectedTime(Int_t fDetectorID, Double_t rawTime, Double_t L)
Definition Scifi.cxx:614
Float_t GetTime(Int_t nChannel=0)
Definition SndlhcHit.cxx:32
Int_t GetStation()
Definition sndScifiHit.h:31
bool isValid() const
Definition sndScifiHit.h:30
bool isVertical()
Definition sndScifiHit.h:32
int calculateSiPMNumber(int reference_SiPM)
int findScifiStation(std::vector< int > &horizontal_hits, std::vector< int > &vertical_hits, float threshold)
float peakScifiTiming(const TClonesArray &digiHits, int bins, float min_x, float max_x, bool isMC=false)
int showerInteractionWall(const TClonesArray &digiHits, const std::map< std::string, float > &selection_parameters, int method=0, std::string setup="TI18")
void getSciFiHitsPerStation(const TClonesArray *digiHits, std::vector< int > &horizontal_hits, std::vector< int > &vertical_hits)
std::unique_ptr< TClonesArray > filterScifiHits(const TClonesArray &digiHits, const std::map< std::string, float > &selection_parameters, int method=0, std::string setup="TI18", bool isMC=false)
std::pair< std::vector< double >, std::vector< double > > hitPositionVectorsPerStation(const TClonesArray *digiHits, int station, Scifi *ScifiDet)
std::unique_ptr< TClonesArray > getScifiHits(const TClonesArray &digiHits, int station, bool orientation)
int densityScifi(int reference_SiPM, const TClonesArray &digiHits, int radius, int min_hit_density, bool min_check)
std::pair< double, double > findCentreOfGravityPerStation(const TClonesArray *digiHits, int station, Scifi *ScifiDet)
bool densityCheck(const TClonesArray &digiHits, int radius=64, int min_hit_density=36, int station=1, bool orientation=false)
std::vector< float > getFractionalHitsPerScifiPlane(std::vector< int > &horizontal_hits, std::vector< int > &vertical_hits)
std::unique_ptr< TClonesArray > selectScifiHits(const TClonesArray &digiHits, int station, bool orientation, int bins_x=52, float min_x=0.0, float max_x=26.0, float time_lower_range=1E9/(2 *ShipUnit::snd_freq/ShipUnit::hertz), float time_upper_range=1.2E9/(ShipUnit::snd_freq/ShipUnit::hertz), bool make_selection=true, bool isMC=false)
std::pair< double, double > hitDensityPerStation(const TClonesArray *digiHits, int station, Scifi *ScifiDet)
int getTotalSciFiHits(std::vector< int > &horizontal_hits, std::vector< int > &vertical_hits)
bool validateHit(sndScifiHit *aHit, int ref_station, bool ref_orientation)
double computeMean(const std::vector< double > &values)
double hitWeightComputation(std::vector< double > hit_position)