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
12void snd::analysis_tools::getSciFiHitsPerStation(const TClonesArray *digiHits, std::vector<int> &horizontal_hits,
13 std::vector<int> &vertical_hits)
14{
15
16 // Clear hits per plane vectors
17 std::fill(horizontal_hits.begin(), horizontal_hits.end(), 0);
18 std::fill(vertical_hits.begin(), vertical_hits.end(), 0);
19
20 // Add valid hits to hits per plane vectors
21 sndScifiHit *hit;
22 TIter hitIterator(digiHits);
23
24 while (hit = dynamic_cast<sndScifiHit *>(hitIterator.Next())) {
25 if (hit->isValid()) {
26 int station = hit->GetStation();
27 if (hit->isVertical()) {
28 vertical_hits[station - 1]++;
29 } else {
30 horizontal_hits[station - 1]++;
31 }
32 }
33 }
34}
35
36int snd::analysis_tools::getTotalSciFiHits(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits)
37{
38 return std::accumulate(horizontal_hits.begin(), horizontal_hits.end(),
39 std::accumulate(vertical_hits.begin(), vertical_hits.end(), 0));
40}
41
42int snd::analysis_tools::getTotalSciFiHits(const TClonesArray *digiHits)
43{
44 std::vector<int> horizontal_hits = std::vector<int>(5);
45 std::vector<int> vertical_hits = std::vector<int>(5);
46
47 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
48
49 return getTotalSciFiHits(horizontal_hits, vertical_hits);
50}
51
52std::vector<float>
53snd::analysis_tools::getFractionalHitsPerScifiPlane(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits)
54{
55
56 int total_hits = getTotalSciFiHits(horizontal_hits, vertical_hits);
57
58 std::vector<float> fractional_hits_per_station = std::vector<float>(horizontal_hits.size());
59
60 std::transform(horizontal_hits.begin(), horizontal_hits.end(), vertical_hits.begin(),
61 fractional_hits_per_station.begin(),
62 [&total_hits](const auto &hor, const auto &ver) { return ((float)hor + ver) / total_hits; });
63
64 return fractional_hits_per_station;
65}
66
67std::vector<float> snd::analysis_tools::getFractionalHitsPerScifiPlane(const TClonesArray *digiHits)
68{
69 std::vector<int> horizontal_hits = std::vector<int>(5);
70 std::vector<int> vertical_hits = std::vector<int>(5);
71
72 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
73
74 return getFractionalHitsPerScifiPlane(horizontal_hits, vertical_hits);
75}
76
77int snd::analysis_tools::findScifiStation(std::vector<int> &horizontal_hits, std::vector<int> &vertical_hits,
78 float threshold)
79{
80
81 std::vector<float> frac = getFractionalHitsPerScifiPlane(horizontal_hits, vertical_hits);
82
83 std::vector<float> frac_sum = std::vector<float>(frac.size());
84
85 std::partial_sum(frac.begin(), frac.end(), frac_sum.begin());
86
87 std::vector<float>::iterator station =
88 std::find_if(frac_sum.begin(), frac_sum.end(), [&threshold](const auto &f) { return f > threshold; });
89
90 return station - frac_sum.begin() + 1;
91}
92
93int snd::analysis_tools::findScifiStation(const TClonesArray *digiHits, float threshold)
94{
95 std::vector<int> horizontal_hits = std::vector<int>(5);
96 std::vector<int> vertical_hits = std::vector<int>(5);
97
98 getSciFiHitsPerStation(digiHits, horizontal_hits, vertical_hits);
99
100 return findScifiStation(horizontal_hits, vertical_hits, threshold);
101}
102
103// Auxiliar function to check whether hits are valid or not and whether they are within the same
104// station and orientation as the reference hit we are comparing it to
105bool validateHit(sndScifiHit *aHit, int ref_station, bool ref_orientation)
106{
107
108 if (!(aHit->isValid())) {
109 return false;
110 }
111 if (aHit->GetStation() != ref_station) {
112 return false;
113 }
114 if (aHit->isVertical() != ref_orientation) {
115 return false;
116 }
117
118 return true;
119}
120
121// Getting the max for the ScifiHits timing distribution in order to select hits within \pm 3ns
122// min_x and max_x should be given in ns
123// The code automatically filters for hits within the same station and orientation as the first hit
124float snd::analysis_tools::peakScifiTiming(const TClonesArray &digiHits, int bins, float min_x, float max_x)
125{
126
127 if (digiHits.GetEntries() <= 0) {
128 LOG(warning) << "digiHits has no valid SciFi Hits and as such no maximum for the timing distribution.";
129 return -1.;
130 }
131
132 TH1F ScifiTiming("Timing", "Scifi Timing", bins, min_x, max_x);
133
134 int refStation = ((sndScifiHit *)digiHits.At(0))->GetStation();
135 bool refOrientation = ((sndScifiHit *)digiHits.At(0))->isVertical();
136 float hitTime = -1.0;
137
138 for (auto *p : digiHits) {
139 auto *hit = dynamic_cast<sndScifiHit *>(p);
140 if (!validateHit(hit, refStation, refOrientation)) {
141 continue;
142 }
143 hitTime = hit->GetTime() * 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
144 if (hitTime < min_x || hitTime > max_x) {
145 continue;
146 }
147 ScifiTiming.Fill(hitTime);
148 hitTime = -1.0;
149 }
150
151 float peakTiming = (ScifiTiming.GetMaximumBin() - 0.5) * (max_x - min_x) / bins + min_x;
152
153 return peakTiming;
154}
155
156// Getting all the Scifi hits for a specific station and orientation
157std::unique_ptr<TClonesArray>
158snd::analysis_tools::getScifiHits(const TClonesArray &digiHits, int station, bool orientation)
159{
160
161 auto selectedHits = std::make_unique<TClonesArray>("sndScifiHit");
162
163 int i = 0;
164 for (auto *p : digiHits) {
165 auto *hit = dynamic_cast<sndScifiHit *>(p);
166 if (!validateHit(hit, station, orientation)) {
167 continue;
168 }
169 new ((*selectedHits)[i++]) sndScifiHit(*hit);
170 }
171
172 return selectedHits;
173}
174
175// Getting all the Scifi hits for a specific station and orientation, taking into account a filter
176// of \pm range around the peak of the timing distribution for said Scifi plane
177// selection_parameters is the number of bins for the histogram, min_x, max_x and time window for
178// the Scifi hits in ns
179// make_selection == true allows you to first perform a selection of the relevant hits, and only
180// afterwards apply the filter. This is recomended for when the selection has not been made
181// previously
182std::unique_ptr<TClonesArray> snd::analysis_tools::selectScifiHits(const TClonesArray &digiHits, int station,
183 bool orientation, int bins_x, float min_x,
184 float max_x, float time_lower_range,
185 float time_upper_range, bool make_selection)
186{
187
188 if (bins_x < 1) {
189 LOG(FATAL) << "bins_x in selection_parameters cannot be <1. Consider using the default value of 52 instead.";
190 }
191 if (min_x > max_x) {
192 LOG(warning) << "In selection_parameters min_x > max_x. Values will be swapped.";
193 float aux_float = min_x;
194 min_x = max_x;
195 max_x = aux_float;
196 }
197 if (min_x < 0.0) {
198 LOG(warning) << "In selection_parameters min_x < 0.0. Consider using the default value of 0.0.";
199 }
200 if (max_x < 0.0) {
201 LOG(FATAL) << "In selection_parameters max_x < 0.0. Consider using the default value of 26.0.";
202 }
203 if (time_lower_range <= 0.0) {
204 LOG(FATAL) << "In selection_parameters time_lower_range <= 0.0. Value should always be positive, in ns.";
205 }
206 if (time_upper_range <= 0.0) {
207 LOG(FATAL) << "In selection_parameters time_upper_range <= 0.0. Value should always be positive, in ns.";
208 }
209
210 auto filteredHits = std::make_unique<TClonesArray>("sndScifiHit", digiHits.GetEntries());
211
212 float peakTiming = -1.0;
213
214 if (make_selection) {
215
216 auto selectedHits = getScifiHits(digiHits, station, orientation);
217
218 peakTiming = peakScifiTiming(*selectedHits, bins_x, min_x, max_x);
219
220 int i = 0;
221 for (auto *p : *selectedHits) {
222 auto *hit = dynamic_cast<sndScifiHit *>(p);
223 if (!validateHit(hit, station, orientation)) {
224 continue;
225 }
226 if ((peakTiming - time_lower_range > hit->GetTime() * 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz)) ||
227 (hit->GetTime() * 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz) > peakTiming + time_upper_range)) {
228 continue;
229 }
230 new ((*filteredHits)[i++]) sndScifiHit(*hit);
231 }
232
233 } else {
234 // Does not create selectedHits and just uses digiHits (not unique_ptr)
235
236 peakTiming = peakScifiTiming(digiHits, bins_x, min_x, max_x);
237
238 int i = 0;
239 for (auto *p : digiHits) {
240 auto *hit = dynamic_cast<sndScifiHit *>(p);
241 if (!validateHit(hit, station, orientation)) {
242 continue;
243 }
244 if ((peakTiming - time_lower_range > hit->GetTime() * 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz)) ||
245 (hit->GetTime() * 1E9 / (ShipUnit::snd_freq / ShipUnit::hertz) > peakTiming + time_upper_range)) {
246 continue;
247 }
248 new ((*filteredHits)[i++]) sndScifiHit(*hit);
249 }
250 }
251
252 return filteredHits;
253}
254
255// Takes a map with 4 or 5 arguments as an input {"bins_x", "min_x", "max_x", "time_lower_range",
256// "time_upper_range"}
257// User may foreit "time_upper_range" and function will assume a symmetric time interval
258std::unique_ptr<TClonesArray>
259snd::analysis_tools::selectScifiHits(const TClonesArray &digiHits, int station, bool orientation,
260 const std::map<std::string, float> &selection_parameters, bool make_selection)
261{
262
263 if ((selection_parameters.find("bins_x") == selection_parameters.end()) ||
264 (selection_parameters.find("min_x") == selection_parameters.end()) ||
265 (selection_parameters.find("max_x") == selection_parameters.end()) ||
266 (selection_parameters.find("time_lower_range") == selection_parameters.end())) {
267 LOG(FATAL) << "In order to use method 0 please provide the correct selection_parameters. Consider the default = "
268 "{{\"bins_x\", 52.}, {\"min_x\", 0.}, {\"max_x\", 26.}, {\"time_lower_range\", "
269 "1E9/(2*ShipUnit::snd_freq/ShipUnit::hertz))}, {\"time_upper_range\", "
270 "2E9/(ShipUnit::snd_freq/ShipUnit::hertz)}}";
271 }
272
273 float time_upper_range = -1.;
274
275 if (selection_parameters.find("time_upper_range") == selection_parameters.end()) {
276 time_upper_range = selection_parameters.at("time_lower_range");
277 } else {
278 time_upper_range = selection_parameters.at("time_upper_range");
279 }
280
281 return selectScifiHits(digiHits, station, orientation, int(selection_parameters.at("bins_x")),
282 selection_parameters.at("min_x"), selection_parameters.at("max_x"),
283 selection_parameters.at("time_lower_range"), time_upper_range, make_selection);
284}
285
286std::unique_ptr<TClonesArray>
287snd::analysis_tools::filterScifiHits(const TClonesArray &digiHits,
288 const std::map<std::string, float> &selection_parameters, int method,
289 std::string setup)
290{
291 TClonesArray supportArray("sndScifiHit", 0);
292 auto filteredHits = std::make_unique<TClonesArray>("sndScifiHit", digiHits.GetEntries());
293 int filteredHitsIndex = 0;
294 int ScifiStations = 5;
295 if (setup == "H8") {
296 ScifiStations = 4;
297 } else {
298 LOG(info) << "\"TI18\" setup will be used by default, please provide \"H8\" for the Testbeam setup.";
299 }
300
301 sndScifiHit *hit;
302 TIter hitIterator(&supportArray);
303
304 if (method == 0) {
305
306 if ((selection_parameters.find("bins_x") == selection_parameters.end()) ||
307 (selection_parameters.find("min_x") == selection_parameters.end()) ||
308 (selection_parameters.find("max_x") == selection_parameters.end()) ||
309 (selection_parameters.find("time_lower_range") == selection_parameters.end())) {
310 LOG(FATAL) << "In order to use method 0 please provide the correct selection_parameters. Consider the default "
311 "= {{\"bins_x\", 52.}, {\"min_x\", 0.}, {\"max_x\", 26.}, {\"time_lower_range\", "
312 "1E9/(2*ShipUnit::snd_freq/ShipUnit::hertz)}, {\"time_upper_range\", "
313 "2E9/(ShipUnit::snd_freq/ShipUnit::hertz)}}";
314 }
315
316 // This is overwriting the previous arrays with the newest one
317 for (auto station : ROOT::MakeSeq(1, ScifiStations + 1)) {
318 for (auto orientation : {false, true}) {
319
320 auto supportArray = selectScifiHits(digiHits, station, orientation, selection_parameters, true);
321 for (auto *p : *supportArray) {
322 auto *hit = dynamic_cast<sndScifiHit *>(p);
323 if (hit->isValid()) {
324 new ((*filteredHits)[filteredHitsIndex++]) sndScifiHit(*hit);
325 }
326 }
327 }
328 }
329 } else {
330 LOG(error) << "Please provide a valid time filter method from:";
331 LOG(error) << "(0): Events within \\mp time_lower_range time_upper_range of the peak of the time distribution "
332 "for Scifi Hits within each station and orientation";
333 LOG(FATAL) << "selection_parameters = {bins_x, min_x, max_x, time_lower_range, time_upper_range}.";
334 }
335 return filteredHits;
336}
337
338std::unique_ptr<TClonesArray>
339snd::analysis_tools::filterScifiHits(const TClonesArray &digiHits, int method, std::string setup)
340{
341
342 std::map<std::string, float> selection_parameters;
343
344 if (method == 0) {
345
346 selection_parameters["bins_x"] = 52.0;
347 selection_parameters["min_x"] = 0.0;
348 selection_parameters["max_x"] = 26.0;
349 selection_parameters["time_lower_range"] = 1E9 / (2 * ShipUnit::snd_freq / ShipUnit::hertz);
350 selection_parameters["time_upper_range"] = 2E9 / (ShipUnit::snd_freq / ShipUnit::hertz);
351
352 } else {
353 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
354 }
355
356 return filterScifiHits(digiHits, selection_parameters, method, setup);
357}
358
359// Caculate the number of the SiPM channel in the whole station by inputing its number in the
360// sndScifiHit->GetChannelID() format
362{
363
364 int ref_matAux = reference_SiPM % 100000;
365 int ref_mat = ref_matAux / 10000;
366 int ref_arrayAux = reference_SiPM % 10000;
367 int ref_array = ref_arrayAux / 1000;
368 int ref_channel = reference_SiPM % 1000;
369 int referenceChannel = ref_mat * 4 * 128 + ref_array * 128 + ref_channel;
370
371 return referenceChannel;
372}
373
374int snd::analysis_tools::densityScifi(int reference_SiPM, const TClonesArray &digiHits, int radius, int min_hit_density,
375 bool min_check)
376{
377
378 int hit_density = 0;
379
380 bool orientation = false;
381 if (int(reference_SiPM / 100000) % 10 == 1) {
382 orientation = true;
383 }
384 int ref_station = reference_SiPM / 1000000;
385 int referenceChannel = calculateSiPMNumber(reference_SiPM);
386
387 for (auto *p : digiHits) {
388 auto *hit = dynamic_cast<sndScifiHit *>(p);
389 if (!validateHit(hit, ref_station, orientation)) {
390 continue;
391 }
392 int hitChannel = calculateSiPMNumber(hit->GetChannelID());
393 if (radius == -1) {
394 hit_density++;
395 } else {
396 if (hitChannel > referenceChannel + radius) {
397 break;
398 }
399 if (abs(referenceChannel - hitChannel) <= radius) {
400 hit_density++;
401 }
402 }
403
404 if (min_check && (hit_density >= min_hit_density)) {
405 break;
406 }
407 }
408
409 return hit_density;
410}
411
412// Perform a density check for a specific station and orientation (dafault is horizontal)
413bool snd::analysis_tools::densityCheck(const TClonesArray &digiHits, int radius, int min_hit_density, int station,
414 bool orientation)
415{
416
417 if (digiHits.GetEntries() <= 0) {
418 return false;
419 }
420
421 if (radius <= 0) {
422 LOG(FATAL) << "Radius<=0. Please provide a radius bigger than 0.";
423 return false;
424 }
425
426 if (min_hit_density < 0) {
427 LOG(FATAL) << "Min_hit_density < 0. Please provide a min_hit_density >= 0.";
428 return false;
429 }
430
431 if (min_hit_density > 2 * radius) {
432 LOG(warning) << "Warning! Radius of density check does not allow for the required minimum density!";
433 return false;
434 }
435
436 // Creating a vector that stores the fired SiPM channels (should already be ordered but we sort
437 // afterwards to make sure)
438 std::vector<int> fired_channels{};
439
440 // Only fill the set with hits from the same station and with the same orientation as given in
441 // argument (false==horizontal and true==vertical)
442 for (auto *p : digiHits) {
443 auto *hit = dynamic_cast<sndScifiHit *>(p);
444 if (!validateHit(hit, station, orientation)) {
445 continue;
446 }
447 fired_channels.push_back(calculateSiPMNumber(hit->GetChannelID()));
448 }
449
450 int n_fired_channels = fired_channels.size();
451
452 if (n_fired_channels < min_hit_density) {
453 return false;
454 }
455
456 // Looping over the ordered hits, checking whether within an interval of "min_hit_density" the
457 // difference between the channel IDs is smaller than "radius". If so, then we have the required
458 // density. Else, we check the next combination until we meet the criterion, or end the loop,
459 // returning false
460 std::sort(fired_channels.begin(), fired_channels.end());
461 for (int i = 0; i < n_fired_channels - min_hit_density; i++) {
462
463 if (fired_channels[i + min_hit_density - 1] - fired_channels[i] <= radius * 2) {
464 return true;
465 }
466 }
467 return false;
468}
469
470// Retrieve the target block where the shower starts
471// Method 0 checks for a minimum number of hits in a specific range (2*radius) in a station to
472// consider that a shower as started before said station. User must provide a map
473//"selection_parameters" with at least 2 elements "radius" and "min_hit_density"
474// In order to only require 1 orientation to pass the criterion, "selection_parameters" may also
475// include "orientation", which can be 0. and 1. for the horizontal and vertical orientations respec.
476int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits,
477 const std::map<std::string, float> &selection_parameters, int method,
478 std::string setup)
479{
480
481 int totalScifiStations = 5;
482 if (setup == "H8") {
483 totalScifiStations = 4;
484 } else {
485 LOG(info) << "\"TI18\" setup will be used by default, please provide \"H8\" for the Testbeam setup.";
486 }
487
488 // There is always 1 more Scifi Station than a target block. As such, showerStart == totalScifiStations
489 // means that the shower did not start developing in the target, before the last Scifi Station
490 int showerStart = totalScifiStations;
491
492 if (method == 0) {
493
494 if ((selection_parameters.find("radius") == selection_parameters.end()) ||
495 (selection_parameters.find("min_hit_density") == selection_parameters.end())) {
496 LOG(FATAL)
497 << "Argument of select_parameters is incorrect. Please provide a map with the arguments \"radius\" and "
498 "\"min_hit_density\". Consider using the default values of {{\"radius\",64}, {\"min_hit_density\",36}}.";
499 }
500
501 for (int scifiStation = 1; scifiStation <= totalScifiStations; scifiStation++) {
502
503 // For each ScifiStation we check whether we see clusters with the desired parameters
504 // By default, we require passing the check on both the horizontal and vertical mats
505 // In case selection_parameters["orientation"] was provided, we set the NOT CHOSEN orientation
506 // as true by default, so as to only need to pass the chosen orientation check
507 bool horizontalCheck = false;
508 bool verticalCheck = false;
509 if (selection_parameters.find("orientation") != selection_parameters.end()) {
510 if (int(selection_parameters.at("orientation")) == 0) {
511 verticalCheck = true;
512 }
513 if (int(selection_parameters.at("orientation")) == 1) {
514 horizontalCheck = true;
515 }
516 }
517 horizontalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
518 int(selection_parameters.at("min_hit_density")), scifiStation, false) ||
519 horizontalCheck);
520 verticalCheck = (densityCheck(digiHits, int(selection_parameters.at("radius")),
521 int(selection_parameters.at("min_hit_density")), scifiStation, true) ||
522 verticalCheck);
523 if ((horizontalCheck) && (verticalCheck)) {
524 showerStart = scifiStation - 1;
525 return showerStart;
526 }
527 }
528 }
529
530 return showerStart;
531}
532
533int snd::analysis_tools::showerInteractionWall(const TClonesArray &digiHits, int method, std::string setup)
534{
535
536 if (method != 0) {
537 LOG(FATAL) << "Please use method=0. No other methods implemented so far.";
538 }
539
540 std::map<std::string, float> selection_parameters = {{"radius", 64.}, {"min_hit_density", 36.}};
541
542 return showerInteractionWall(digiHits, selection_parameters, method, setup);
543}
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)
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)
float peakScifiTiming(const TClonesArray &digiHits, int bins, float min_x, float max_x)
std::unique_ptr< TClonesArray > filterScifiHits(const TClonesArray &digiHits, const std::map< std::string, float > &selection_parameters, int method=0, std::string setup="TI18")
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)
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)
int getTotalSciFiHits(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), float time_upper_range=2E9/(ShipUnit::snd_freq/ShipUnit::hertz), bool make_selection=true)
bool validateHit(sndScifiHit *aHit, int ref_station, bool ref_orientation)