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* hit = static_cast<sndScifiHit*>(digiHits[0]);
139 int refStation = hit->GetStation();
140 bool refOrientation = 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 sndScifiHit *hit;
323 TIter hitIterator(&supportArray);
324
325 if (method == 0) {
326
327 if ((selection_parameters.find("bins_x") == selection_parameters.end()) ||
328 (selection_parameters.find("min_x") == selection_parameters.end()) ||
329 (selection_parameters.find("max_x") == selection_parameters.end()) ||
330 (selection_parameters.find("time_lower_range") == selection_parameters.end())) {
331 LOG(FATAL) << "In order to use method 0 please provide the correct selection_parameters. Consider the default "
332 "= {{\"bins_x\", 52.}, {\"min_x\", 0.}, {\"max_x\", 26.}, {\"time_lower_range\", "
333 "1E9/(2*ShipUnit::snd_freq/ShipUnit::hertz)}, {\"time_upper_range\", "
334 "1.2E9/(ShipUnit::snd_freq/ShipUnit::hertz)}}";
335 }
336
337 // This is overwriting the previous arrays with the newest one
338 for (auto station : ROOT::MakeSeq(1, ScifiStations + 1)) {
339 for (auto orientation : {false, true}) {
340
341 auto supportArray = selectScifiHits(digiHits, station, orientation, selection_parameters, true, isMC);
342 for (auto *p : *supportArray) {
343 auto *hit = dynamic_cast<sndScifiHit *>(p);
344 if (hit->isValid()) {
345 new ((*filteredHits)[filteredHitsIndex++]) sndScifiHit(*hit);
346 }
347 }
348 }
349 }
350 } else {
351 LOG(error) << "Please provide a valid time filter method from:";
352 LOG(error) << "(0): Events within \\mp time_lower_range time_upper_range of the peak of the time distribution "
353 "for Scifi Hits within each station and orientation";
354 LOG(FATAL) << "selection_parameters = {bins_x, min_x, max_x, time_lower_range, time_upper_range}.";
355 }
356 return filteredHits;
357}
358
359std::unique_ptr<TClonesArray>
360snd::analysis_tools::filterScifiHits(const TClonesArray &digiHits, int method, std::string setup, bool isMC)
361{
362
363 std::map<std::string, float> selection_parameters;
364
365 if (method == 0) {
366
367 selection_parameters["bins_x"] = 52.0;
368 selection_parameters["min_x"] = 0.0;
369 selection_parameters["max_x"] = 26.0;
370 selection_parameters["time_lower_range"] = 1E9 / (2 * ShipUnit::snd_freq / ShipUnit::hertz);
371 selection_parameters["time_upper_range"] = 1.2E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
372
373 } else {
374 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
375 }
376
377 return filterScifiHits(digiHits, selection_parameters, method, setup, isMC);
378}
379
380// Caculate the number of the SiPM channel in the whole station by inputing its number in the
381// sndScifiHit->GetChannelID() format
383{
384
385 int ref_matAux = reference_SiPM % 100000;
386 int ref_mat = ref_matAux / 10000;
387 int ref_arrayAux = reference_SiPM % 10000;
388 int ref_array = ref_arrayAux / 1000;
389 int ref_channel = reference_SiPM % 1000;
390 int referenceChannel = ref_mat * 4 * 128 + ref_array * 128 + ref_channel;
391
392 return referenceChannel;
393}
394
395int snd::analysis_tools::densityScifi(int reference_SiPM, const TClonesArray &digiHits, int radius, int min_hit_density,
396 bool min_check)
397{
398
399 int hit_density = 0;
400
401 bool orientation = false;
402 if (int(reference_SiPM / 100000) % 10 == 1) {
403 orientation = true;
404 }
405 int ref_station = reference_SiPM / 1000000;
406 int referenceChannel = calculateSiPMNumber(reference_SiPM);
407
408 for (auto *p : digiHits) {
409 auto *hit = dynamic_cast<sndScifiHit *>(p);
410 if (!validateHit(hit, ref_station, orientation)) {
411 continue;
412 }
413 int hitChannel = calculateSiPMNumber(hit->GetChannelID());
414 if (radius == -1) {
415 hit_density++;
416 } else {
417 if (hitChannel > referenceChannel + radius) {
418 break;
419 }
420 if (abs(referenceChannel - hitChannel) <= radius) {
421 hit_density++;
422 }
423 }
424
425 if (min_check && (hit_density >= min_hit_density)) {
426 break;
427 }
428 }
429
430 return hit_density;
431}
432
433// Perform a density check for a specific station and orientation (dafault is horizontal)
434bool snd::analysis_tools::densityCheck(const TClonesArray &digiHits, int radius, int min_hit_density, int station,
435 bool orientation)
436{
437
438 if (digiHits.GetEntries() <= 0) {
439 return false;
440 }
441
442 if (radius <= 0) {
443 LOG(FATAL) << "Radius<=0. Please provide a radius bigger than 0.";
444 return false;
445 }
446
447 if (min_hit_density < 0) {
448 LOG(FATAL) << "Min_hit_density < 0. Please provide a min_hit_density >= 0.";
449 return false;
450 }
451
452 if (min_hit_density > 2 * radius) {
453 LOG(warning) << "Warning! Radius of density check does not allow for the required minimum density!";
454 return false;
455 }
456
457 // Creating a vector that stores the fired SiPM channels (should already be ordered but we sort
458 // afterwards to make sure)
459 std::vector<int> fired_channels{};
460
461 // Only fill the set with hits from the same station and with the same orientation as given in
462 // argument (false==horizontal and true==vertical)
463 for (auto *p : digiHits) {
464 auto *hit = dynamic_cast<sndScifiHit *>(p);
465 if (!validateHit(hit, station, orientation)) {
466 continue;
467 }
468 fired_channels.push_back(calculateSiPMNumber(hit->GetChannelID()));
469 }
470
471 int n_fired_channels = fired_channels.size();
472
473 if (n_fired_channels < min_hit_density) {
474 return false;
475 }
476
477 // Looping over the ordered hits, checking whether within an interval of "min_hit_density" the
478 // difference between the channel IDs is smaller than "radius". If so, then we have the required
479 // density. Else, we check the next combination until we meet the criterion, or end the loop,
480 // returning false
481 std::sort(fired_channels.begin(), fired_channels.end());
482 for (int i = 0; i < n_fired_channels - min_hit_density; i++) {
483
484 if (fired_channels[i + min_hit_density - 1] - fired_channels[i] <= radius * 2) {
485 return true;
486 }
487 }
488 return false;
489}
490
491// Retrieve the target block where the shower starts
492// Method 0 checks for a minimum number of hits in a specific range (2*radius) in a station to
493// consider that a shower as started before said station. User must provide a map
494//"selection_parameters" with at least 2 elements "radius" and "min_hit_density"
495// In order to only require 1 orientation to pass the criterion, "selection_parameters" may also
496// include "orientation", which can be 0. and 1. for the horizontal and vertical orientations respec.
497int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits,
498 const std::map<std::string, float> &selection_parameters, int method,
499 std::string setup)
500{
501
502 int totalScifiStations = 5;
503 if (setup == "H8") {
504 totalScifiStations = 4;
505 } else {
506 LOG(info) << "\"TI18\" setup will be used by default, please provide \"H8\" for the Testbeam setup.";
507 }
508
509 // There is always 1 more Scifi Station than a target block. As such, showerStart == totalScifiStations
510 // means that the shower did not start developing in the target, before the last Scifi Station
511 int showerStart = totalScifiStations;
512
513 if (method == 0) {
514
515 if ((selection_parameters.find("radius") == selection_parameters.end()) ||
516 (selection_parameters.find("min_hit_density") == selection_parameters.end())) {
517 LOG(FATAL)
518 << "Argument of select_parameters is incorrect. Please provide a map with the arguments \"radius\" and "
519 "\"min_hit_density\". Consider using the default values of {{\"radius\",64}, {\"min_hit_density\",36}}.";
520 }
521
522 for (int scifiStation = 1; scifiStation <= totalScifiStations; scifiStation++) {
523
524 // For each ScifiStation we check whether we see clusters with the desired parameters
525 // By default, we require passing the check on both the horizontal and vertical mats
526 // In case selection_parameters["orientation"] was provided, we set the NOT CHOSEN orientation
527 // as true by default, so as to only need to pass the chosen orientation check
528 bool horizontalCheck = false;
529 bool verticalCheck = false;
530 if (selection_parameters.find("orientation") != selection_parameters.end()) {
531 if (int(selection_parameters.at("orientation")) == 0) {
532 verticalCheck = true;
533 }
534 if (int(selection_parameters.at("orientation")) == 1) {
535 horizontalCheck = true;
536 }
537 }
538 horizontalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
539 int(selection_parameters.at("min_hit_density")), scifiStation, false) ||
540 horizontalCheck);
541 verticalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
542 int(selection_parameters.at("min_hit_density")), scifiStation, true) ||
543 verticalCheck);
544 if ((horizontalCheck) && (verticalCheck)) {
545 showerStart = scifiStation - 1;
546 return showerStart;
547 }
548 }
549 }
550
551 return showerStart;
552}
553
554int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits, int method, std::string setup)
555{
556
557 if (method != 0) {
558 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
559 }
560
561 std::map<std::string, float> selection_parameters = {{"radius", 64.}, {"min_hit_density", 36.}};
562
563 return showerInteractionWall(digiHits, selection_parameters, method, setup);
564}
565
566double computeMean(const std::vector<double>& values)
567{
568 double sum = std::accumulate(values.begin(), values.end(), 0.0);
569 double mean = sum / values.size();
570 return mean;
571}
572
573std::pair<double, double>
574snd::analysis_tools::findCentreOfGravityPerStation(const TClonesArray* digiHits, int station, Scifi* ScifiDet)
575{
576 if (!digiHits) {
577 LOG(ERROR) << "Error: digiHits is null in findCentreOfGravityPerStation";
578 }
579
580 auto [x_positions, y_positions] = snd::analysis_tools::hitPositionVectorsPerStation(digiHits, station, ScifiDet);
581
582 if (x_positions.empty()) {
583 LOG(ERROR) << "Error: No hits enter.";
584 }
585 double meanX = computeMean(x_positions);
586 if (y_positions.empty()) {
587 LOG(ERROR) << "Error: No hits enter.";
588 }
589 double meanY = computeMean(y_positions);
590 return {meanX, meanY};
591}
592
593std::pair<std::vector<double>,std::vector<double>> snd::analysis_tools::hitPositionVectorsPerStation(const TClonesArray *digiHits, int station, Scifi* ScifiDet){
594 /*This function returns the hit vector position for the digiHits in both orientations
595 Arguments:
596 digiHits: A TClonesArray containing the hits in the SciFi detector.
597 station: The station number for which to retrieve the hit positions.
598 ScifiDet: A pointer to the Scifi detector object to retrieve SiPM positions.
599 Returns:
600 A pair of vectors containing the x and y positions of the hits in the specified station.
601 If no hits are found, it returns empty vectors and logs an error message.
602 */
603
604 if (!digiHits) {
605 LOG(ERROR) << "Error: digiHits is null";
606 }
607 std::vector<double> x_positions;
608 std::vector<double> y_positions;
609
610 TVector3 A, B;
611 for (auto* hit : ROOT::RRangeCast<sndScifiHit*, false, decltype(*digiHits)>(*digiHits)) {
612 if (!hit || !hit->isValid()) {
613 continue;
614 }
615 if (hit->GetStation() != station) {
616 continue;
617 }
618 ScifiDet->GetSiPMPosition(hit->GetDetectorID(), A, B);
619 if (hit->isVertical()) {
620 x_positions.push_back((A.X() + B.X()) * 0.5);
621 }
622 else {
623 y_positions.push_back((A.Y() + B.Y()) * 0.5);
624 }
625 }
626 if (x_positions.empty()) {
627 LOG(ERROR) << "Error: No hits enter.";
628 }
629 if (y_positions.empty()) {
630 LOG(ERROR) << "Error: No hits enter.";
631 }
632 return {x_positions, y_positions};
633}
634
635
636double hitWeightComputation(std::vector<double> hit_position) {
637 /* This function returns the summation of the hit weights
638 where a hit weight is the number of neighbouring hits within 1 cm position (default width).
639
640 Arguments:
641 hit_position: A vector containing the positions of the hits in a specific SciFi station.
642
643 Returns:
644 The sum of the weights of the hits in the vector.
645 Returns 0 if the vector is empty or if the sum of weights is 0.
646 */
647
648 if (hit_position.empty()) {
649 LOG(INFO) << "Warning: The hit position vector is empty." << std::endl;
650 return 0; // Return 0 if the vector is empty
651 }
652
653 int n_hits = hit_position.size();
654 double width = 1.0; // Width around the hit to consider as a neighbour, in cm
655 std::vector<double> weights; // Vector to store the weights of each hit
656
657 // Sorting the hits for efficient neighbour counting
658 std::sort(hit_position.begin(), hit_position.end());
659
660 // Initialize pointers for the sliding window.
661 // Both pointers will only move forward (or stay put) across the outer loop iterations.
662 int left_ptr = 0;
663 int right_ptr = 0;
664
665 // Loop through each hit position to calculate its neighbour count
666 for (int i = 0; i < n_hits; i++) {
667 double specific_hit = hit_position[i];
668
669 // Move right_ptr forward to include all hits within (specific_hit + width).
670 // It will point to the first element *just outside* the right boundary of the window.
671 while (right_ptr < n_hits && hit_position[right_ptr] <= specific_hit + width) {
672 right_ptr++;
673 }
674
675 // Move left_ptr forward to exclude all hits *less than* (specific_hit - width).
676 // It will point to the first element *just inside* or at the left boundary of the window.
677 while (left_ptr < n_hits && hit_position[left_ptr] < specific_hit - width) {
678 left_ptr++;
679 }
680
681 // Calculate neighbouring hits within the window:
682 // The number of hits in the current window is (right_ptr - left_ptr).
683 // This count includes the 'specific_hit' itself.
684 double count_in_window = right_ptr - left_ptr;
685
686 // Subtract 1 to exclude the 'specific_hit' itself from the neighbour count.
687 // We must ensure that 'specific_hit' (hit_position[i]) is actually within the window
688 // defined by left_ptr and right_ptr. Since the array is sorted and we are iterating
689 // through 'i', hit_position[i] will always be between hit_position[left_ptr] and
690 // hit_position[right_ptr-1] (if left_ptr <= i < right_ptr).
691 double neighbour_no_of_hits = count_in_window - 1;
692
693 // Ensure the neighbour count is non-negative
694 if (neighbour_no_of_hits < 0) {
695 neighbour_no_of_hits = 0;
696 }
697
698 weights.push_back(neighbour_no_of_hits);
699 }
700
701 // Calculate the sum of all computed weights
702 double sum_weights = std::accumulate(weights.begin(), weights.end(), 0.0);
703
704 return sum_weights;
705}
706
707std::pair<double,double> snd::analysis_tools::hitDensityPerStation(const TClonesArray *digiHits, int station, Scifi* ScifiDet){
708 /*
709 This function returns the hit density which is described as the summation of the hit weights
710 where a hit weigth is the number of neighbouring hits within 1 cm postion (default width)
711 arguments: hit position vector : vector ontaining the positions of the hits in a specific SciFi station
712 returns: the sum of the weights of the hits in the vector
713 If the vector is empty, it returns 0.
714 If the sum of weights is 0, it returns 0.
715 */
716 std::pair<std::vector<double>, std::vector<double>> hit_position_vec = hitPositionVectorsPerStation(digiHits, station, ScifiDet);
717 std::vector<double> hit_position_x = hit_position_vec.first; // Assuming we are interested in X positions
718 std::vector<double> hit_position_y = hit_position_vec.second; // Assuming we are interested in Y positions
719
720 if (hit_position_x.size()==0 && hit_position_y.size()==0) {
721 LOG(INFO)<< "Warning: The hit position vector is empty." << std::endl;
722 return {0.0,0.0}; // Check if the vector is empty
723 }
724
725 double sum_weights_x = hitWeightComputation(hit_position_x);
726 double sum_weights_y = hitWeightComputation(hit_position_y);
727
728 return {sum_weights_x, sum_weights_y};
729}
730
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)