epic-drich-beam-test-analysis
ePIC dRICH beam test analysis framework
Loading...
Searching...
No Matches
utility.h
Go to the documentation of this file.
1#pragma once
2
24#include <cstdint>
25#include <optional>
26#include <vector>
27#include <random>
28#include <unordered_set>
29#include <fstream>
30#include <filesystem>
31#include <Math/Functor.h>
32#include <Fit/Fitter.h>
33#include <TEllipse.h>
34#include <TH1.h>
35#include <TH2.h>
36#include <TGraph.h>
37#include <TGraph2D.h>
38#include <TFile.h>
39#include <TTree.h>
40#include <TF1.h>
41#include <TProfile.h>
42#include <Math/Minimizer.h>
43#include <functional>
44#include <iostream>
45#include <mist/mist.h>
46
52inline uint32_t encode_bit(uint8_t active_bit)
53{
54 return (active_bit < 32) ? (1u << active_bit) : 0;
55}
56
62inline uint32_t encode_bits(const std::vector<uint8_t> &active_bits)
63{
64 uint32_t mask = 0;
65 for (uint8_t bit : active_bits)
66 if (bit < 32)
67 mask |= (1u << bit);
68 return mask;
69}
70
76inline uint8_t count_trailing_zeros(uint32_t mask)
77{
78 if (mask == 0)
79 return 32;
80 uint8_t count = 0;
81 while ((mask & 1u) == 0)
82 {
83 mask >>= 1;
84 ++count;
85 }
86 return count;
87}
88
94inline std::vector<uint8_t> decode_bits(uint32_t mask)
95{
96 std::vector<uint8_t> result;
97 result.resize(32, 0);
98 while (mask)
99 {
100 uint8_t bit = count_trailing_zeros(mask);
101 result[bit] = bit;
102 mask &= ~(1u << bit); // clear that bit
103 }
104 return result;
105}
106
111
113inline std::random_device _global_rd_;
114
116inline std::mt19937 _global_gen_(_global_rd_());
117
119inline std::uniform_real_distribution<> _rnd_(0.0, 1.0);
120
122
129
131inline int get_hit_tdc_from_global_tdc_index(int global_tdc_index) { return global_tdc_index % 4; }
132
134inline int get_device_from_global_tdc_index(int global_tdc_index) { return 192 + (global_tdc_index / 1024); }
135
137inline int get_fifo_from_global_tdc_index(int global_tdc_index) { return (global_tdc_index % 1024) / 32; }
138
140inline int get_chip_from_global_tdc_index(int global_tdc_index) { return get_fifo_from_global_tdc_index(global_tdc_index) / 4; }
141
143inline int get_eo_channel_index_from_global_tdc_index(int global_tdc_index) { return ((global_tdc_index % 1024) % (32 * 4)) / 4 + 32 * (get_chip_from_global_tdc_index(global_tdc_index) % 2); }
144
146inline int get_column_from_global_tdc_index(int global_tdc_index) { return ((global_tdc_index % 1024) % 32) / 4; }
147
149inline int get_pixel_from_global_tdc_index(int global_tdc_index) { return ((global_tdc_index % 1024) % 32) % 4; }
150
152inline int get_calib_index_from_global_tdc_index(int global_tdc_index) { return get_hit_tdc_from_global_tdc_index(global_tdc_index) + 4 * get_eo_channel_index_from_global_tdc_index(global_tdc_index) + 128 * get_chip_from_global_tdc_index(global_tdc_index); }
153
155inline int get_device_index_from_global_tdc_index(int global_tdc_index) { return get_eo_channel_index_from_global_tdc_index(global_tdc_index) + 64 * (get_chip_from_global_tdc_index(global_tdc_index) / 2); }
156
158inline int get_pdu_from_global_tdc_index(int global_tdc_index) { return global_tdc_index / 256 + 1; }
159
161inline int get_matrix_from_global_tdc_index(int global_tdc_index) { return (global_tdc_index % 256 / 4) + 1; }
162
171inline uint32_t get_global_index(int device, int chip, int channel, int tdc) { return (device - 192) * 32 * 8 * 4 + chip * 32 * 4 + channel * 4 + tdc; }
172
174
178
180using circle_fit_results = std::array<std::array<float, 2>, 3>;
181
193inline circle_fit_results fit_circle(std::vector<std::array<float, 2>> points, std::array<float, 3> initial_values, bool fix_XY = true, std::vector<int> exclude_points = {{}})
194{
195 circle_fit_results result;
196
197 // Chi2 minimisation for points in a circle
198 auto chi2_function = [&](const double *parameters)
199 {
200 float chi2 = 0;
201 for (int iPnt = 0; iPnt < points.size(); iPnt++)
202 {
203 if (std::find(exclude_points.begin(), exclude_points.end(), iPnt) != exclude_points.end())
204 continue;
205 double delta_x = points[iPnt][0] - parameters[0];
206 double delta_y = points[iPnt][1] - parameters[1];
207 double delta_r = parameters[2] - std::sqrt(delta_x * delta_x + delta_y * delta_y);
208 chi2 += delta_r * delta_r;
209 }
210 return chi2;
211 };
212
213 // wrap chi2 function in a function object for the fit
214 ROOT::Math::Functor fit_function(chi2_function, 3);
215 ROOT::Fit::Fitter fitter;
216
217 // Set initial values and variables names
218 double internal_initial_values[3] = {initial_values[0], initial_values[1], initial_values[2]};
219 fitter.SetFCN(fit_function, internal_initial_values);
220 fitter.Config().ParSettings(0).SetName("x0");
221 fitter.Config().ParSettings(1).SetName("y0");
222 fitter.Config().ParSettings(2).SetName("R");
223 fitter.Config().ParSettings(2).SetLowerLimit(0);
224 if (fix_XY)
225 {
226 fitter.Config().ParSettings(0).Fix();
227 fitter.Config().ParSettings(1).Fix();
228 }
229
230 // Fitting
231 if (!fitter.FitFCN())
232 {
233 // Error("fit_circle", "Fit failed");
234 // return {{{-2., 0.}, {-2., 0.}, {-2., 0.}}};
235 }
236 const ROOT::Fit::FitResult &fit_result = fitter.Result();
237
238 auto iTer = -1;
239 for (auto current_parameter : fit_result.Parameters())
240 {
241 iTer++;
242 result[iTer][0] = current_parameter;
243 result[iTer][1] = fit_result.Errors()[iTer];
244 }
245
246 // Calculate chi2
247 double *test = new double[3];
248 test[0] = result[0][0];
249 test[1] = result[1][0];
250 test[2] = result[2][0];
251 auto myChi2 = chi2_function(test);
252
253 return result;
254}
255
257
261
272inline double logistic(double variable, double amplitude, double center_1, double sigma_1, double center_2, double sigma_2)
273{
274 return amplitude * // Amplitude
275 // (1. / (1. + exp(-(center_2 - center_1) / sigma_1)) - 1. / (1. + exp(-(center_2 - center_1) / sigma_2))) * // Normalisation
276 (1. / (1. + exp(-(variable - center_1) / sigma_1)) - 1. / (1. + exp(-(variable - center_2) / sigma_2))); // Difference of logistics
277}
278
287inline double clip_phi(double phi, double low_bound, double high_bound)
288{
289 double result = phi;
290 while (phi < low_bound || phi >= high_bound)
291 {
292 /* code */
293 }
294 return -1.;
295}
296
309inline double ring_fit_function_sigma_function(double phi, double baseline_sigma, std::vector<std::array<double, 4>> input_values = {})
310{
311 double result = baseline_sigma;
312 for (auto current_logistic : input_values)
313 {
314 result += logistic(phi, current_logistic[0], current_logistic[1] - 0.5 * current_logistic[2], current_logistic[3], current_logistic[1] + 0.5 * current_logistic[2], current_logistic[3]);
315 }
316 return result;
317}
318
328inline double ring_fit_function(std::array<double, 2> input_values, std::array<double, 6> parameters, std::vector<std::array<double, 4>> logistic_input_values = {})
329{
330 // Get input values
331 auto current_radius = input_values[0];
332 auto current_phi = input_values[1];
333
334 // Parameters
335 auto ring_x_center = parameters[0];
336 auto ring_y_center = parameters[1];
337 auto ring_radius = parameters[2];
338 auto ring_sigma = parameters[3];
339 auto ring_photons = parameters[4];
340 auto bkg_level = parameters[5];
341
342 // Signal
343 auto signal = ring_photons * (1. / (2 * TMath::Pi() * ring_radius)) * TMath::Gaus(current_radius, ring_radius, ring_fit_function_sigma_function(current_phi, ring_sigma, logistic_input_values), true);
344 // Background
345 auto background = bkg_level;
346
347 // Return
348 return signal + background;
349}
350
358inline double ring_fit_function_xy(std::array<double, 2> input_values, std::array<double, 6> parameters, std::vector<std::array<double, 4>> logistic_input_values = {})
359{
360 // Get input values
361 auto x_center = input_values[0];
362 auto y_center = input_values[1];
363
364 // Parameters
365 auto ring_x_center = parameters[0];
366 auto ring_y_center = parameters[1];
367
368 // Get radial coordinates
369 auto current_radius = hypot(x_center - ring_x_center, y_center - ring_y_center);
370 auto current_phi = atan2(y_center - ring_y_center, x_center - ring_x_center);
371
372 // Return
373 return ring_fit_function({current_radius, current_phi}, parameters);
374}
375
384inline double ring_fit_function_xy(std::array<double, 2> input_values, const double *parameters, int how_many_logistics = 0)
385{
386 // Convert to internal format
387 std::array<double, 6> array_parameters = {
388 static_cast<double>(parameters[0]),
389 static_cast<double>(parameters[1]),
390 static_cast<double>(parameters[2]),
391 static_cast<double>(parameters[3]),
392 static_cast<double>(parameters[4]),
393 static_cast<double>(parameters[5])};
394
395 std::vector<std::array<double, 4>> logistic_input_values;
396 for (auto i_logistic = 0; i_logistic < how_many_logistics; i_logistic++)
397 logistic_input_values.emplace_back(
398 std::array<double, 4>{parameters[5 + i_logistic * 4 + 0],
399 parameters[5 + i_logistic * 4 + 1],
400 parameters[5 + i_logistic * 4 + 2],
401 parameters[5 + i_logistic * 4 + 3]});
402
403 // Return
404 return ring_fit_function_xy(input_values, array_parameters);
405}
406
408using ring_fit_results = std::array<std::array<double, 2>, 6>;
409
422inline ring_fit_results fit_ring_integral(TH2 *target_histogram, std::array<double, 6> initial_values, bool fix_XY = true)
423{
424 ring_fit_results result;
425
426 // Chi2 minimisation for points in a circle
427 auto chi2_function = [&](const double *parameters)
428 {
429 double chi2 = 0;
430 for (auto x_bin = 1; x_bin <= target_histogram->GetNbinsX(); x_bin++)
431 for (auto y_bin = 1; y_bin <= target_histogram->GetNbinsY(); y_bin++)
432 {
433 // Get bin values
434 double x_center = target_histogram->GetXaxis()->GetBinCenter(x_bin);
435 double y_center = target_histogram->GetYaxis()->GetBinCenter(y_bin);
436 double x_width = target_histogram->GetXaxis()->GetBinWidth(x_bin);
437 double y_width = target_histogram->GetYaxis()->GetBinWidth(y_bin);
438 double z_value = target_histogram->GetBinContent(x_bin, y_bin);
439 double z_error = target_histogram->GetBinError(x_bin, y_bin);
440 // z_error = std::max(z_error, 1.0);
441
442 if (z_value <= 0)
443 continue;
444
445 // Calculate the fit function here
446 double current_function_value = x_width * y_width * ring_fit_function_xy({x_center, y_center}, parameters);
447 chi2 += (z_value - current_function_value) * (z_value - current_function_value) / (z_error * z_error);
448 }
449 return chi2;
450 };
451
452 // wrap chi2 function in a function object for the fit
453 ROOT::Math::Functor fit_function(chi2_function, 6);
454 ROOT::Fit::Fitter fitter;
455
456 // Set initial values and variables names
457 double internal_initial_values[6] = {initial_values[0], initial_values[1], initial_values[2], initial_values[3], initial_values[4], initial_values[5]};
458 fitter.SetFCN(fit_function, internal_initial_values);
459 fitter.Config().ParSettings(0).SetName("centerX");
460 fitter.Config().ParSettings(0).SetLimits(-5.0, 5.0);
461 fitter.Config().ParSettings(1).SetName("centerY");
462 fitter.Config().ParSettings(1).SetLimits(-5.0, 5.0);
463 fitter.Config().ParSettings(2).SetName("ring__R");
464 fitter.Config().ParSettings(2).SetLowerLimit(0);
465 fitter.Config().ParSettings(3).SetName("sigma_R");
466 fitter.Config().ParSettings(3).SetLimits(0., 7.5);
467 fitter.Config().ParSettings(4).SetName("N_gamma");
468 fitter.Config().ParSettings(4).SetLimits(0., 50.);
469 fitter.Config().ParSettings(5).SetName("bkg");
470 fitter.Config().ParSettings(5).SetLowerLimit(0.);
471 if (fix_XY)
472 {
473 fitter.Config().ParSettings(0).Fix();
474 fitter.Config().ParSettings(1).Fix();
475 }
476
477 // Fitting
478 if (!fitter.FitFCN())
479 {
480 const ROOT::Fit::FitResult &fit_result = fitter.Result();
481 Error("fit_circle", "Fit failed");
482 fit_result.Print(std::cout);
483 return {{{-2., 0.}, {-2., 0.}, {-2., 0.}, {0., 0.}, {0., 0.}, {0., 0.}}};
484 }
485 const ROOT::Fit::FitResult &fit_result = fitter.Result();
486 fit_result.Print(std::cout);
487
488 auto iTer = -1;
489 for (auto current_parameter : fit_result.Parameters())
490 {
491 iTer++;
492 mist::logger::debug("par " + fit_result.GetParameterName(iTer) + " : " + std::to_string(current_parameter) + " +- " + std::to_string(fit_result.Errors()[iTer]));
493 if (iTer > 6)
494 continue;
495 result[iTer][0] = current_parameter;
496 result[iTer][1] = fit_result.Errors()[iTer];
497 }
498
499 return result;
500}
501
510inline std::vector<TGraph *> plot_ring_integral(ring_fit_results fit_results, std::vector<float> sigma_values, std::vector<std::array<double, 4>> logistic_input_values = {}, int n_points = 500)
511{
512 std::vector<TGraph *> result;
513 for (auto current_sigma_value : sigma_values)
514 {
515 result.push_back(new TGraph());
516 auto &current_graph = result.back();
517 for (auto i_point = 0; i_point <= n_points; i_point++)
518 {
519 auto current_phi = -TMath::Pi() + 2 * TMath::Pi() * (1. * i_point) / n_points;
520 auto current_x = fit_results[0][0] + (fit_results[2][0] + current_sigma_value * ring_fit_function_sigma_function(current_phi, fit_results[3][0], logistic_input_values)) * cos(current_phi);
521 auto current_y = fit_results[1][0] + (fit_results[2][0] + current_sigma_value * ring_fit_function_sigma_function(current_phi, fit_results[3][0], logistic_input_values)) * sin(current_phi);
522 current_graph->SetPoint(i_point, current_x, current_y);
523 }
524 }
525 return result;
526}
527
529
532
548inline TFile *open_or_build_rootfile(const std::string &filename,
549 std::function<void(std::string, std::string, int, bool, int, std::string, std::string, std::string)> builder,
550 const std::string &data_repository,
551 const std::string &run_name,
552 int max_spill,
553 bool force_rebuild = false)
554{
555 // Try to open the file
556 if (!force_rebuild)
557 {
558 TFile *input_file = TFile::Open(filename.c_str(), "READ");
559 if (input_file && !input_file->IsZombie())
560 return input_file;
561 delete input_file; // Delete if zombie
562 }
563 std::cout << "[WARNING] File '" << filename << "' missing, corrupted or rebuild forced, creating it\n";
564
565 // Re-build file
566 builder(data_repository, run_name, max_spill, force_rebuild, -1, "", "", "");
567
568 TFile *input_file = TFile::Open(filename.c_str(), "READ");
569 if (!input_file || input_file->IsZombie())
570 {
571 std::cerr << "[ERROR] Could not open file '" << filename << "' even after rebuilding\n";
572 delete input_file;
573 return nullptr;
574 }
575 return input_file;
576}
577
579
582
590void inline draw_circle(std::array<float, 3> parameters, int line_colour = kBlack, int line_style = kSolid, int line_width = 1)
591{
592 auto result = new TEllipse(parameters[0], parameters[1], parameters[2]);
593 result->SetFillStyle(0);
594 result->SetLineColor(line_colour);
595 result->SetLineStyle(line_style);
596 result->SetLineWidth(line_width);
597 result->DrawEllipse(parameters[0], parameters[1], parameters[2], 0, 0, 360, 0, "same");
598}
599
600// https://root-forum.cern.ch/t/fitting-a-poisson-distribution-to-a-histogram/12078/4
601// [0] = Normalizing parameter
602// [1] / [2] -> mean (mu)
603// x / [2] -> x
604// Gamma( x / [2] + 1 ) = factorial (x / [2])
605// inline TF1 *fPhotonFitFunction = new TF1("hPhotonFitFunction", "[0]*TMath::Power(([1]/[2]),(x/[2]))*(TMath::Exp(-([1]/[2])))/TMath::Gamma((x/[2])+1)", -1000, 1000);
606
607/*
608// Data structures
609// === Fit results: X, Y, R and errors
610using circle_fit_results = std::array<std::array<float, 2>, 3>;
611// === recodata
612// === === Structure
613struct recodata
614{
615 unsigned short n = 0;
616 float x[1024];
617 float y[1024];
618 float t[1024];
619};
620// === === Load data from tree
621void load_data(TTree *reco_data_tree, recodata &target_data_struct)
622{
623 reco_data_tree->SetBranchAddress("n", &target_data_struct.n);
624 reco_data_tree->SetBranchAddress("x", &target_data_struct.x);
625 reco_data_tree->SetBranchAddress("y", &target_data_struct.y);
626 reco_data_tree->SetBranchAddress("t", &target_data_struct.t);
627}
628// === === Load data from file
629TTree *load_data(std::string filename, recodata &target_data_struct)
630{
631 auto input_file = TFile::Open(filename.c_str());
632 auto input_tree = (TTree *)input_file->Get("recodata");
633 load_data(input_tree, target_data_struct);
634 return input_tree;
635}
636// === Devices
637std::map<std::string, int> devices_name = {
638 {"kc705-192", 192},
639 {"kc705-193", 193},
640 {"kc705-194", 194},
641 {"kc705-195", 195},
642 {"kc705-196", 196},
643 {"kc705-197", 197},
644 {"kc705-198", 198},
645 {"kc705-199", 199},
646 {"kc705-200", 200},
647 {"kc705-201", 201},
648 {"kc705-202", 202},
649 {"kc705-207", 207}};
650std::map<int, int> devices_enum = {
651 {192, 0},
652 {193, 1},
653 {194, 2},
654 {195, 3},
655 {196, 4},
656 {197, 5},
657 {198, 6},
658 {199, 7},
659 {200, 8},
660 {201, 9},
661 {202, 10},
662 {207, 11}};
663
664// General Info
665const float kSiPM_x_dimension = 1.5;
666const float kSiPM_y_dimension = 1.5;
667
668// General utilities
669// === Cartesian to Polar
670inline std::array<float, 2> cartesian_to_polar(std::array<float, 2> target, std::array<float, 2> center_shift = {0., 0.})
671{
672 float x_new_coordinate = target[0] - center_shift[0];
673 float y_new_coordinate = target[1] - center_shift[1];
674 float r_coordinate = TMath::Sqrt(x_new_coordinate * x_new_coordinate + y_new_coordinate * y_new_coordinate);
675 float phi_coordinate = TMath::ATan2(y_new_coordinate, x_new_coordinate);
676 return {r_coordinate, phi_coordinate};
677}
678inline std::array<float, 2> polar_to_cartesian(std::array<float, 2> target, std::array<float, 2> center_shift = {0., 0.})
679{
680 float r_new_coordinate = target[0] - center_shift[0];
681 float phi_new_coordinate = target[1] - center_shift[1];
682 float x_coordinate = target[0] * TMath::Cos(target[1]);
683 float y_coordinate = target[0] * TMath::Sin(target[1]);
684 return {x_coordinate, y_coordinate};
685}
686// === Filler functions
687bool is_within_SiPM(std::array<float, 2> cartesian_coordinates, std::array<float, 2> SiPM_center)
688{
689 bool is_within_SiPM = false;
690 auto x_distance = fabs(cartesian_coordinates[0] - SiPM_center[0]);
691 auto y_distance = fabs(cartesian_coordinates[1] - SiPM_center[1]);
692 if ((x_distance) <= kSiPM_x_dimension && fabs(y_distance) <= kSiPM_y_dimension)
693 is_within_SiPM = true;
694 return is_within_SiPM;
695}
696bool is_within_ring(std::array<float, 2> cartesian_coordinates, std::array<float, 3> ring_parameters, float ring_radius_sigma = 0.)
697{
698 // Improvements:
699 // Make X and Y have different possible values
700 // Check values received for the interval
701 bool is_within_ring = false;
702 auto target_radius = cartesian_to_polar(cartesian_coordinates, {ring_parameters[0], ring_parameters[1]})[0];
703 if ((target_radius >= ring_parameters[2] - ring_radius_sigma) && (target_radius <= ring_parameters[2] + ring_radius_sigma))
704 is_within_ring = true;
705 return is_within_ring;
706}
707template <bool rnd_smearing = true>
708void fill_persistance(TH2F *hTarget, recodata reco_data)
709{
710 for (int iPnt = 0; iPnt < reco_data.n; iPnt++)
711 hTarget->Fill(rnd_smearing ? gRandom->Uniform(reco_data.x[iPnt] - 1.5, reco_data.x[iPnt] + 1.5) : reco_data.x[iPnt], rnd_smearing ? gRandom->Uniform(reco_data.y[iPnt] - 1.5, reco_data.y[iPnt] + 1.5) : reco_data.y[iPnt]);
712}
713template <bool allow_overlap = false>
714void fill_with_SiPM_coverage(TH2F *target, std::array<float, 2> SiPM_center)
715{
716 float x_center_bin = target->GetXaxis()->FindBin(SiPM_center[0]);
717 float y_center_bin = target->GetYaxis()->FindBin(SiPM_center[1]);
718 float x_center_bin_center = target->GetXaxis()->GetBinCenter(x_center_bin);
719 float y_center_bin_center = target->GetYaxis()->GetBinCenter(y_center_bin);
720 float current_center_bin_content = target->GetBinContent(target->FindBin(x_center_bin, y_center_bin));
721
722 // Did we set this SiPM already?
723 // * No overlap is permitted
724 if ((current_center_bin_content > 0) && !allow_overlap)
725 return;
726
727 // Start with know center bin
728 for (auto xBin = 0; xBin < target->GetNbinsX(); xBin++)
729 {
730 // Check at least one fill has been done
731 bool at_least_one_fill = false;
732
733 float current_bin_center_x_high = (float)(target->GetXaxis()->GetBinCenter(x_center_bin + xBin));
734 float current_bin_center_x_low_ = (float)(target->GetXaxis()->GetBinCenter(x_center_bin - xBin));
735 for (auto yBin = 0; yBin < target->GetNbinsY(); yBin++)
736 {
737 float current_bin_center_y_high = (float)(target->GetYaxis()->GetBinCenter(y_center_bin + yBin));
738 float current_bin_center_y_low_ = (float)(target->GetYaxis()->GetBinCenter(y_center_bin - yBin));
739
740 // Set bin contents
741 if (is_within_SiPM({current_bin_center_x_high, current_bin_center_y_high}, SiPM_center))
742 {
743 target->SetBinContent(x_center_bin + xBin, y_center_bin + yBin, 1);
744 at_least_one_fill = true;
745 }
746 if (is_within_SiPM({current_bin_center_x_high, current_bin_center_y_low_}, SiPM_center))
747 {
748 target->SetBinContent(x_center_bin + xBin, y_center_bin - yBin, 1);
749 at_least_one_fill = true;
750 }
751 if (is_within_SiPM({current_bin_center_x_low_, current_bin_center_y_high}, SiPM_center))
752 {
753 target->SetBinContent(x_center_bin - xBin, y_center_bin + yBin, 1);
754 at_least_one_fill = true;
755 }
756 if (is_within_SiPM({current_bin_center_x_low_, current_bin_center_y_low_}, SiPM_center))
757 {
758 target->SetBinContent(x_center_bin - xBin, y_center_bin - yBin, 1);
759 at_least_one_fill = true;
760 }
761
762 // return if we stopped filling
763 if (!at_least_one_fill)
764 break;
765 }
766 if (!is_within_SiPM({current_bin_center_x_high, y_center_bin_center}, SiPM_center) && !is_within_SiPM({current_bin_center_x_low_, y_center_bin_center}, SiPM_center))
767 return;
768 }
769}
770void fill_with_ring_coverage(TH2F *target, std::array<float, 3> ring_parameters, float ring_radius_sigma = 0.)
771{
772 for (auto xBin = 0; xBin < target->GetNbinsX(); xBin++)
773 {
774 float current_bin_center_x = (float)(target->GetXaxis()->GetBinCenter(xBin));
775 for (auto yBin = 0; yBin < target->GetNbinsY(); yBin++)
776 {
777 float current_bin_center_y = float(target->GetYaxis()->GetBinCenter(yBin));
778 if (is_within_ring({current_bin_center_x, current_bin_center_y}, ring_parameters, ring_radius_sigma))
779 {
780 target->SetBinContent(xBin, yBin, 1);
781 }
782 else
783 {
784 target->SetBinContent(xBin, yBin, 0);
785 }
786 }
787 }
788}
789// === Ring finders & fittera
790std::vector<std::tuple<float, float, int>>
791find_peaks(TH1F *original_histo, int bin_span = 3, float cutoff_threshold = 0.33)
792{
793 // Result
794 std::vector<std::tuple<float, float, int>> result;
795
796 // Clone target to manipulate
797 auto target_histo = (TH1F *)(original_histo->Clone("tmp_get_filtered_center"));
798 auto target_maximum = target_histo->GetMaximum();
799
800 // Loop over bins
801 for (auto iBin = 1 + bin_span; iBin <= target_histo->GetNbinsX() - bin_span; iBin++)
802 {
803 auto neighbourhood_maximum = 0;
804 auto current_bin = target_histo->GetBinContent(iBin);
805 for (auto iSpan = 1; iSpan <= bin_span; iSpan++)
806 {
807 auto current_bin_left = target_histo->GetBinContent(iBin - iSpan);
808 auto current_bin_right = target_histo->GetBinContent(iBin + iSpan);
809 if ((neighbourhood_maximum < current_bin_left) || (neighbourhood_maximum < current_bin_right))
810 neighbourhood_maximum = max(current_bin_left, current_bin_right);
811 if (neighbourhood_maximum > current_bin)
812 break;
813 }
814 if ((neighbourhood_maximum < current_bin) && (current_bin > cutoff_threshold * target_maximum))
815 result.push_back({target_histo->GetBinCenter(iBin), target_histo->GetBinContent(iBin), iBin});
816 }
817
818 delete target_histo;
819 return result;
820}
821std::vector<std::vector<std::array<float, 2>>>
822find_rings(TH1F *original_histo_X, TH1F *original_histo_Y, float x_center = -1.5, float y_center = -1.5)
823{
824 // Clone target to manipulate
825 auto target_histo_X = (TH1F *)(original_histo_X->Clone("tmp_find_rings_X"));
826 auto target_histo_Y = (TH1F *)(original_histo_Y->Clone("tmp_find_rings_Y"));
827
828 // Find peaks
829 auto current_peaks_X = find_peaks(target_histo_X);
830 auto current_peaks_Y = find_peaks(target_histo_Y);
831
832 // == Assumption is made that the beam is within the smaller circle
833 auto n_cirles = 0;
834 auto current_circle = 0;
835 std::vector<std::vector<std::array<float, 2>>> circles;
836
837 // First loop on x_findings
838 for (auto current_peak : current_peaks_X)
839 {
840 auto current_X = get<0>(current_peak);
841 if (current_X < 0)
842 {
843 n_cirles++;
844 circles.push_back({{current_X, y_center}});
845 current_circle++;
846 }
847 else
848 {
849 current_circle--;
850 if (current_circle < 0)
851 {
852 current_circle = n_cirles;
853 n_cirles++;
854 circles.insert(circles.begin(), {{current_X, y_center}});
855 continue;
856 }
857 circles[current_circle].push_back({current_X, y_center});
858 }
859 }
860
861 // Second loop on y_findings
862 auto n_left_rings = 0;
863 for (auto current_peak : current_peaks_Y)
864 {
865 auto current_Y = get<0>(current_peak);
866 if (current_Y < 0)
867 n_left_rings++;
868 }
869 if (n_left_rings == n_cirles)
870 {
871 current_circle = 0;
872 for (auto current_peak : current_peaks_Y)
873 {
874 auto current_Y = get<0>(current_peak);
875 if (current_Y < 0)
876 {
877 circles[current_circle].push_back({x_center, current_Y});
878 current_circle++;
879 }
880 else
881 {
882 current_circle--;
883 if (current_circle < 0)
884 {
885 current_circle = n_cirles;
886 n_cirles++;
887 circles.insert(circles.begin(), {{x_center, current_Y}});
888 continue;
889 }
890 circles[current_circle].push_back({x_center, current_Y});
891 }
892 }
893 }
894
895 delete target_histo_X;
896 delete target_histo_Y;
897 std::reverse(circles.begin(), circles.end());
898 return circles;
899}
900std::vector<std::vector<std::array<float, 2>>>
901merge_circles(std::vector<std::vector<std::vector<std::array<float, 2>>>> target_circles)
902{
903 std::vector<std::vector<std::array<float, 2>>> final_circles;
904 for (auto current_circle_array : target_circles)
905 {
906 auto icircle = -1;
907 for (auto current_circle : current_circle_array)
908 {
909 icircle++;
910 if (final_circles.size() < icircle + 1)
911 final_circles.push_back({});
912 for (auto current_point : current_circle)
913 {
914 final_circles[icircle].push_back(current_point);
915 }
916 }
917 }
918 return final_circles;
919}
920template <bool fix_XY = true>
921circle_fit_results
922fit_circle(TGraph *gTarget, std::array<float, 3> initial_values)
923{
924 circle_fit_results result;
925
926 // Chi2 minimisation for points in a circle
927 auto chi2_function = [&](const double *parameters)
928 {
929 float chi2 = 0;
930 for (int iPnt = 0; iPnt < gTarget->GetN(); iPnt++)
931 {
932 double delta_x = gTarget->GetPointX(iPnt) - parameters[0];
933 double delta_y = gTarget->GetPointY(iPnt) - parameters[1];
934 double delta_r = parameters[2] - std::sqrt(delta_x * delta_x + delta_y * delta_y);
935 chi2 += delta_r * delta_r;
936 }
937 return chi2;
938 };
939
940 // wrap chi2 function in a function object for the fit
941 ROOT::Math::Functor fit_function(chi2_function, 3);
942 ROOT::Fit::Fitter fitter;
943
944 // Set initial values and variables names
945 double internal_initial_values[3] = {initial_values[0], initial_values[1], initial_values[2]};
946 fitter.SetFCN(fit_function, internal_initial_values);
947 fitter.Config().ParSettings(0).SetName("x0");
948 fitter.Config().ParSettings(1).SetName("y0");
949 fitter.Config().ParSettings(2).SetName("R");
950 fitter.Config().ParSettings(2).SetLowerLimit(0);
951 if (fix_XY)
952 {
953 fitter.Config().ParSettings(0).Fix();
954 fitter.Config().ParSettings(1).Fix();
955 }
956
957 // Fitting
958 if (!fitter.FitFCN())
959 {
960 // Error("fit_circle", "Fit failed");
961 // return {{{-2., 0.}, {-2., 0.}, {-2., 0.}}};
962 }
963 const ROOT::Fit::FitResult &fit_result = fitter.Result();
964
965 auto iTer = -1;
966 for (auto current_parameter : fit_result.Parameters())
967 {
968 iTer++;
969 result[iTer][0] = current_parameter;
970 result[iTer][1] = fit_result.Errors()[iTer];
971 }
972
973 // Calculate chi2
974 double *test = new double[3];
975 test[0] = result[0][0];
976 test[1] = result[1][0];
977 test[2] = result[2][0];
978 auto myChi2 = chi2_function(test);
979
980 return result;
981}
982template <bool fix_XY = true>
983circle_fit_results
984fit_circles(std::vector<TGraphErrors *> gTargets, std::array<float, 2> center_guess, std::vector<float> radiuses_guesses)
985{
986 // Result
987 circle_fit_results result;
988
989 if (gTargets.size() == 0)
990 return result;
991
992 if (gTargets.size() == 1)
993 return fit_circle<false>(gTargets[0], {center_guess[0], center_guess[1], radiuses_guesses[0]});
994
995 // Chi2 minimisation for points in a circle
996 auto chi2_function = [&](const double *parameters)
997 {
998 float chi2 = 0;
999 auto iGraph = -1;
1000 for (auto current_graph : gTargets)
1001 {
1002 iGraph++;
1003 for (int iPnt = 0; iPnt < current_graph->GetN(); iPnt++)
1004 {
1005 double delta_x = current_graph->GetPointX(iPnt) - parameters[0];
1006 double delta_y = current_graph->GetPointY(iPnt) - parameters[1];
1007 double delta_r = parameters[2 + iGraph] - std::sqrt(delta_x * delta_x + delta_y * delta_y);
1008 chi2 += delta_r * delta_r;
1009 }
1010 }
1011 return chi2;
1012 };
1013
1014 // wrap chi2 function in a function object for the fit
1015 ROOT::Math::Functor fit_function(chi2_function, 2 + gTargets.size());
1016 ROOT::Fit::Fitter fitter;
1017
1018 // Set initial values and variables names
1019 double *internal_initial_values = new double[2 + gTargets.size()];
1020 internal_initial_values[0] = center_guess[0];
1021 internal_initial_values[1] = center_guess[1];
1022 auto iTer = 1;
1023 for (auto current_radius : radiuses_guesses)
1024 {
1025 iTer++;
1026 if (iTer > (2 + gTargets.size()))
1027 break;
1028 internal_initial_values[iTer] = current_radius;
1029 }
1030 fitter.SetFCN(fit_function, internal_initial_values);
1031 fitter.Config().ParSettings(0).SetName("x0");
1032 fitter.Config().ParSettings(1).SetName("y0");
1033
1034 iTer = -1;
1035 for (auto current_radius : radiuses_guesses)
1036 {
1037 iTer++;
1038 if (iTer >= gTargets.size())
1039 break;
1040 fitter.Config().ParSettings(2 + iTer).SetName(Form("R_%i", iTer));
1041 fitter.Config().ParSettings(2 + iTer).SetLowerLimit(0);
1042 }
1043 if (fix_XY)
1044 {
1045 fitter.Config().ParSettings(0).Fix();
1046 fitter.Config().ParSettings(1).Fix();
1047 }
1048
1049 // Fitting
1050 if (!fitter.FitFCN())
1051 {
1052 // Error("fit_circle", "Fit failed");
1053 // return {{{-2., 0.}, {-2., 0.}, {-2., 0.}}};
1054 }
1055 const ROOT::Fit::FitResult &fit_result = fitter.Result();
1056
1057 iTer = -1;
1058 for (auto current_parameter : fit_result.Parameters())
1059 {
1060 iTer++;
1061 result[iTer][0] = current_parameter;
1062 result[iTer][1] = fit_result.Errors()[iTer];
1063 }
1064 return result;
1065}
1066template <bool simultaneous_fit = true>
1067std::vector<circle_fit_results> fit_multiple_rings(std::vector<std::array<TH1F *, 2>> original_histo_s, std::vector<std::array<float, 2>> centers)
1068{
1069 std::vector<std::vector<std::vector<std::array<float, 2>>>> final_circles_array;
1070 auto iTer = -1;
1071 for (auto current_xy_pair : original_histo_s)
1072 {
1073 iTer++;
1074 final_circles_array.push_back(find_rings(current_xy_pair[0], current_xy_pair[1], centers[iTer][0], centers[iTer][1]));
1075 }
1076
1077 auto final_circles = merge_circles(final_circles_array);
1078
1079 std::vector<circle_fit_results> final_rings;
1080 std::vector<TGraphErrors *> gfinal_rings;
1081 auto current_n_circle = -1;
1082 for (auto current_circle : final_circles)
1083 {
1084 current_n_circle++;
1085 gfinal_rings.push_back(new TGraphErrors());
1086 for (auto current_point : current_circle)
1087 {
1088 gfinal_rings[current_n_circle]->SetPoint(gfinal_rings[current_n_circle]->GetN(), current_point[0], current_point[1]);
1089 }
1090 auto circle_fit = fit_circle<false>(gfinal_rings[current_n_circle], {0, 0, 20});
1091 final_rings.push_back(circle_fit);
1092 }
1093
1094 if (simultaneous_fit)
1095 {
1096 auto fit_results = fit_circles<false>(gfinal_rings, {0, 0}, {100, 100, 100, 100, 100});
1097 auto iRing = -1;
1098 for (auto &current_ring : final_rings)
1099 {
1100 iRing++;
1101 current_ring[0][0] = fit_results[0][0];
1102 current_ring[1][0] = fit_results[1][0];
1103 current_ring[0][1] = fit_results[0][1];
1104 current_ring[1][1] = fit_results[1][1];
1105 current_ring[2][0] = fit_results[2 + iRing][0];
1106 current_ring[2][1] = fit_results[2 + iRing][1];
1107 }
1108 }
1109
1110 return final_rings;
1111}
1112template <bool simultaneous_fit = true>
1113std::vector<circle_fit_results> fit_multiple_rings(TH2F *persistance_2D, std::vector<std::array<float, 2>> slices = {{-0., +0.}, {-12., -12.}, {+12., +12.}})
1114{
1115 // Clone target to manipulate
1116 auto target_persistance_2D = (TH2F *)(persistance_2D->Clone("target_persistance_2D"));
1117
1118 // Define utility functions
1119 std::vector<std::array<TH1F *, 2>> original_histo_s;
1120 std::vector<std::array<float, 2>> centers;
1121
1122 // Iteration for requested X-Y slices
1123 auto iTer = -1;
1124 for (auto current_limits : slices)
1125 {
1126 iTer++;
1127
1128 auto x_target_bin = target_persistance_2D->GetXaxis()->FindBin(current_limits[0]);
1129 auto y_target_bin = target_persistance_2D->GetXaxis()->FindBin(current_limits[1]);
1130
1131 auto x_target_bin_center = target_persistance_2D->GetXaxis()->GetBinCenter(x_target_bin);
1132 auto y_target_bin_center = target_persistance_2D->GetYaxis()->GetBinCenter(y_target_bin);
1133
1134 auto hXslice = (TH1F *)(target_persistance_2D->ProjectionX(Form("current_projection_X_%i", iTer), x_target_bin, x_target_bin));
1135 auto hYslice = (TH1F *)(target_persistance_2D->ProjectionY(Form("current_projection_Y_%i", iTer), y_target_bin, y_target_bin));
1136
1137 original_histo_s.push_back({hXslice, hYslice});
1138 centers.push_back({(float)(x_target_bin_center), (float)(y_target_bin_center)});
1139 }
1140
1141 auto result = fit_multiple_rings<simultaneous_fit>(original_histo_s, centers);
1142 for (auto current_histos : original_histo_s)
1143 {
1144 delete current_histos[0];
1145 delete current_histos[1];
1146 }
1147 delete target_persistance_2D;
1148
1149 return result;
1150}
1151// === Graphical helpers
1152TCanvas *get_std_canvas()
1153{
1154 TCanvas *result = new TCanvas("", "", 800, 800);
1155 gStyle->SetOptStat(1110);
1156 result->SetRightMargin(0.15);
1157 result->SetTopMargin(0.15);
1158 result->SetLeftMargin(0.15);
1159 result->SetBottomMargin(0.15);
1160 return result;
1161}
1162template <bool grid_x = true, bool grid_y = true>
1163TCanvas *get_std_canvas_2D(std::string name, std::string title, float nXpixels = 1145, float nYpixels = 1000)
1164{
1165 TCanvas *cResult = new TCanvas(name.c_str(), title.c_str(), nXpixels, nYpixels);
1166 gStyle->SetOptStat(0);
1167 gPad->SetGridx(grid_x);
1168 gPad->SetGridy(grid_y);
1169 gPad->SetRightMargin(0.15);
1170 gPad->SetTopMargin(0.05);
1171 gPad->SetLeftMargin(0.15);
1172 gPad->SetBottomMargin(0.15);
1173 return cResult;
1174}
1175void plot_box(std::array<float, 4> parameters, int line_colour = kBlack, int line_style = kSolid, int line_width = 1)
1176{
1177 auto result = new TBox(parameters[0], parameters[1], parameters[2], parameters[3]);
1178 result->SetFillStyle(0);
1179 result->SetLineColor(line_colour);
1180 result->SetLineStyle(line_style);
1181 result->SetLineWidth(line_width);
1182 result->DrawBox(parameters[0], parameters[1], parameters[2], parameters[3]);
1183}
1184void plot_circle(std::array<float, 3> parameters, int line_colour = kBlack, int line_style = kSolid, int line_width = 1)
1185{
1186 auto result = new TEllipse(parameters[0], parameters[1], parameters[2]);
1187 result->SetFillStyle(0);
1188 result->SetLineColor(line_colour);
1189 result->SetLineStyle(line_style);
1190 result->SetLineWidth(line_width);
1191 result->DrawEllipse(parameters[0], parameters[1], parameters[2], 0, 0, 360, 0, "same");
1192}
1193void plot_circle(std::array<std::array<float, 2>, 3> parameters, std::array<std::array<int, 3>, 3> plot_options)
1194{
1195 std::array<float, 3> reduced_parameters = {parameters[0][0], parameters[1][0], parameters[2][0]};
1196 plot_circle(reduced_parameters, plot_options[0][0], plot_options[0][1], plot_options[0][2]);
1197 reduced_parameters = {parameters[0][0], parameters[1][0], parameters[2][0] + parameters[2][1]};
1198 plot_circle(reduced_parameters, plot_options[1][0], plot_options[1][1], plot_options[1][2]);
1199 reduced_parameters = {parameters[0][0], parameters[1][0], parameters[2][0] - parameters[2][1]};
1200 plot_circle(reduced_parameters, plot_options[1][0], plot_options[1][1], plot_options[1][2]);
1201 std::array<float, 4> box_parameters = {parameters[0][0] - parameters[0][1], parameters[1][0] - parameters[1][1], parameters[0][0] + parameters[0][1], parameters[1][0] + parameters[1][1]};
1202 plot_box(box_parameters, plot_options[2][0], plot_options[2][1], plot_options[2][2]);
1203}
1204TCanvas *plot_check_coordinates(TH2F *hPersistance2D, std::vector<circle_fit_results> found_rings)
1205{
1206 TCanvas *cDrawResult = get_std_canvas_2D("cDrawResult", "cDrawResult", 1145 * 2, 1000);
1207 cDrawResult->Divide(2, 1);
1208 TLatex *lLatex = new TLatex();
1209 cDrawResult->cd(2);
1210 gPad->SetRightMargin(0.15);
1211 gPad->SetTopMargin(0.05);
1212 gPad->SetLeftMargin(0.15);
1213 gPad->SetBottomMargin(0.15);
1214 cDrawResult->cd(1);
1215 gPad->SetRightMargin(0.15);
1216 gPad->SetTopMargin(0.05);
1217 gPad->SetLeftMargin(0.15);
1218 gPad->SetBottomMargin(0.15);
1219 hPersistance2D->Draw("COLZ");
1220 auto iRing = -1;
1221 for (auto current_ring : found_rings)
1222 {
1223 iRing++;
1224
1225 cDrawResult->cd(1);
1226 std::array<std::array<int, 3>, 3> plot_options = {{{kBlack, kSolid, 3}, {kRed, kDashed, 2}, {kRed, kSolid, 2}}};
1227 std::array<std::array<float, 2>, 3> plot_coordinates = {{{current_ring[0][0], current_ring[0][1]}, {current_ring[1][0], current_ring[1][1]}, {current_ring[2][0], current_ring[2][1]}}};
1228 plot_circle(plot_coordinates, plot_options);
1229
1230 cDrawResult->cd(2);
1231
1232 lLatex->SetTextSize(0.045);
1233 lLatex->DrawLatexNDC(0.02, 0.95 + iRing * 0.25, Form("Ring %i", iRing));
1234
1235 lLatex->SetTextSize(0.03);
1236 lLatex->DrawLatexNDC(0.02, 0.91 + iRing * 0.25, Form("Guess (mm):"));
1237 lLatex->DrawLatexNDC(0.18, 0.91 + iRing * 0.25, Form("X_{0} : %.2f#pm%.2f", current_ring[0][0], current_ring[0][1]));
1238 lLatex->DrawLatexNDC(0.36, 0.91 + iRing * 0.25, Form("Y_{0} : %.2f#pm%.2f", current_ring[1][0], current_ring[1][1]));
1239 lLatex->DrawLatexNDC(0.54, 0.91 + iRing * 0.25, Form("R_{0} : %.2f#pm%.2f", current_ring[2][0], current_ring[2][1]));
1240 lLatex->DrawLatexNDC(0.72, 0.91 + iRing * 0.25, Form("#sigma_{R} : %.2f#pm%.2f", 0., 0.));
1241 }
1242 return cDrawResult;
1243}
1244*/
uint8_t count_trailing_zeros(uint32_t mask)
Count trailing zeros (portable C++17)
Definition utility.h:76
int get_pixel_from_global_tdc_index(int global_tdc_index)
Decode the pixel address from a global TDC index.
Definition utility.h:149
std::vector< uint8_t > decode_bits(uint32_t mask)
Decode a 32-bit mask into the indices of set bits.
Definition utility.h:94
double logistic(double variable, double amplitude, double center_1, double sigma_1, double center_2, double sigma_2)
Difference-of-logistic function used to model azimuthal acceptance gaps.
Definition utility.h:272
int get_column_from_global_tdc_index(int global_tdc_index)
Decode the column address from a global TDC index.
Definition utility.h:146
void draw_circle(std::array< float, 3 > parameters, int line_colour=kBlack, int line_style=kSolid, int line_width=1)
Draw a circle on the current ROOT canvas pad.
Definition utility.h:590
std::random_device _global_rd_
Seeding device for _global_gen_.
Definition utility.h:113
int get_device_index_from_global_tdc_index(int global_tdc_index)
Compute the per-device flat index from a global TDC index.
Definition utility.h:155
int get_device_from_global_tdc_index(int global_tdc_index)
Decode the readout device ID (192+) from a global TDC index.
Definition utility.h:134
int get_pdu_from_global_tdc_index(int global_tdc_index)
Decode the PDU number (1-based) from a global TDC index.
Definition utility.h:158
std::array< std::array< double, 2 >, 6 > ring_fit_results
Result type for ring fits: { {x0,σ}, {y0,σ}, {R,σ}, {sigma_R,σ}, {N,σ}, {bkg,σ} }.
Definition utility.h:408
double ring_fit_function(std::array< double, 2 > input_values, std::array< double, 6 > parameters, std::vector< std::array< double, 4 > > logistic_input_values={})
Evaluate the ring signal + flat background model in (R, φ) coordinates.
Definition utility.h:328
TFile * open_or_build_rootfile(const std::string &filename, std::function< void(std::string, std::string, int, bool, int, std::string, std::string, std::string)> builder, const std::string &data_repository, const std::string &run_name, int max_spill, bool force_rebuild=false)
Open a ROOT file for reading, rebuilding it if missing or corrupted.
Definition utility.h:548
ring_fit_results fit_ring_integral(TH2 *target_histogram, std::array< double, 6 > initial_values, bool fix_XY=true)
Fit a 2-D ring model to a histogram using chi-squared minimisation.
Definition utility.h:422
uint32_t encode_bits(const std::vector< uint8_t > &active_bits)
Encode multiple bits into a 32-bit mask.
Definition utility.h:62
int get_eo_channel_index_from_global_tdc_index(int global_tdc_index)
Decode the even/odd channel index from a global TDC index.
Definition utility.h:143
double ring_fit_function_sigma_function(double phi, double baseline_sigma, std::vector< std::array< double, 4 > > input_values={})
Azimuthally-varying ring-width model.
Definition utility.h:309
uint32_t get_global_index(int device, int chip, int channel, int tdc)
Pack device, chip, channel, and TDC sub-index into a global TDC index.
Definition utility.h:171
uint32_t encode_bit(uint8_t active_bit)
Encode a single bit into a 32-bit mask.
Definition utility.h:52
int get_calib_index_from_global_tdc_index(int global_tdc_index)
Compute the calibration look-up index from a global TDC index.
Definition utility.h:152
int get_fifo_from_global_tdc_index(int global_tdc_index)
Decode the FIFO number from a global TDC index.
Definition utility.h:137
int get_chip_from_global_tdc_index(int global_tdc_index)
Decode the chip number from a global TDC index.
Definition utility.h:140
std::vector< TGraph * > plot_ring_integral(ring_fit_results fit_results, std::vector< float > sigma_values, std::vector< std::array< double, 4 > > logistic_input_values={}, int n_points=500)
Generate ring-contour TGraphs at specified sigma levels from a fit result.
Definition utility.h:510
double clip_phi(double phi, double low_bound, double high_bound)
Clip an angle to a given range (placeholder — currently returns -1).
Definition utility.h:287
int get_hit_tdc_from_global_tdc_index(int global_tdc_index)
Extract the TDC sub-index (0–3) from a global TDC index.
Definition utility.h:131
int get_matrix_from_global_tdc_index(int global_tdc_index)
Decode the matrix number (1-based) from a global TDC index.
Definition utility.h:161
std::uniform_real_distribution _rnd_(0.0, 1.0)
Uniform float distribution in [0, 1).
std::array< std::array< float, 2 >, 3 > circle_fit_results
Result type: { {x0,σx0}, {y0,σy0}, {R,σR} }.
Definition utility.h:180
std::mt19937 _global_gen_(_global_rd_())
Mersenne-Twister 19937 engine seeded from _global_rd_.
circle_fit_results fit_circle(std::vector< std::array< float, 2 > > points, std::array< float, 3 > initial_values, bool fix_XY=true, std::vector< int > exclude_points={{}})
Fit a circle to a set of 2-D points.
Definition utility.h:193
double ring_fit_function_xy(std::array< double, 2 > input_values, std::array< double, 6 > parameters, std::vector< std::array< double, 4 > > logistic_input_values={})
Evaluate the ring model in Cartesian (x, y) coordinates.
Definition utility.h:358