GRSISort "v4.0.0.5"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
TSourceCalibration.cxx
Go to the documentation of this file.
2
3#include <chrono>
4#include <cstdlib>
5#include <iostream>
6#include <iomanip>
7#include <string>
8#include <cstring>
9#if __cplusplus >= 201703L
10#include <filesystem>
11#endif
12#include <thread>
13
14#include "TSystem.h"
15#include "TGTableLayout.h"
16#include "TCanvas.h"
17#include "TLinearFitter.h"
18#include "TF1.h"
19#include "TSpectrum.h"
20#include "TPolyMarker.h"
21#include "TObject.h"
22#include "TFrame.h"
23#include "TVirtualX.h"
24
25#include "TChannel.h"
26#include "GRootCommands.h"
27#include "combinations.h"
28#include "Globals.h"
29
30std::map<double, std::tuple<double, double, double, double>> RoughCal(std::vector<double> peaks, std::vector<std::tuple<double, double, double, double>> sources, TSourceTab* sourceTab)
31{
32 /// This function tries to match a list of found peaks (channels) to a list of provided peaks (energies).
33 /// It does so in slightly smarter way than the brute force method `Match`, by taking the reported intensity of the source peaks into account.
34
35 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << "\"Smart\" matching " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl; }
37 for(size_t i = 0; i < peaks.size() || i < sources.size(); ++i) {
38 std::cout << i << ".: " << std::setw(8);
39 if(i < peaks.size()) {
40 std::cout << peaks[i];
41 } else {
42 std::cout << " ";
43 }
44 std::cout << " - " << std::setw(8);
45 if(i < sources.size()) {
46 std::cout << std::get<0>(sources[i]);
47 } else {
48 std::cout << " ";
49 }
50 std::cout << std::endl;
51 }
52 }
53
54 std::map<double, std::tuple<double, double, double, double>> result;
55 std::sort(peaks.begin(), peaks.end());
56 std::sort(sources.begin(), sources.end(), [](const std::tuple<double, double, double, double>& a, const std::tuple<double, double, double, double>& b) { return std::get<2>(a) > std::get<2>(b); });
57
58 auto maxSize = peaks.size();
59 if(sources.size() > maxSize) { maxSize = sources.size(); }
60
61 // Peaks are the fitted points.
62 // source are the known values
63
64 TLinearFitter fitter(1, "1 ++ x");
65
66 // intermediate vectors and map
67 std::vector<double> sourceValues(sources.size());
68 std::map<double, double> tmpMap;
69
70 for(size_t num_data_points = std::min(peaks.size(), sourceValues.size()); num_data_points > 0; num_data_points--) {
71 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << num_data_points << " data points:" << std::endl; }
72 double best_chi2 = DBL_MAX;
73 int iteration = 0;
74 for(auto peak_values : combinations(peaks, num_data_points)) {
75 // Add a (0,0) point to the calibration.
76 peak_values.push_back(0);
77 // instead of going through all possible combinations of the peaks with the source energies
78 // we pick the num_data_points most intense lines and try them
79 // we don't do the same with the peaks as there might be an intense background peak in the data (511 etc.)
80 sourceValues.resize(num_data_points);
81 for(size_t i = 0; i < sourceValues.size(); ++i) { sourceValues[i] = std::get<0>(sources[i]); }
82 std::sort(sourceValues.begin(), sourceValues.end());
83 sourceValues.push_back(0);
84
86 for(size_t i = 0; i < peak_values.size(); ++i) {
87 std::cout << i << ".: " << std::setw(8) << peak_values[i] << " - " << std::setw(8) << sourceValues[i] << std::endl;
88 }
89 }
90 if(peaks.size() > 3) {
91 double pratio = peak_values.front() / peak_values.at(peak_values.size() - 2);
92 double sratio = sourceValues.front() / sourceValues.at(sourceValues.size() - 2);
93 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "ratio: " << pratio << " - " << sratio << " = " << std::abs(pratio - sratio) << std::endl; }
94 if(std::abs(pratio - sratio) > 0.02) {
95 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "skipping" << std::endl; }
96 continue;
97 }
98 }
99
100 fitter.ClearPoints();
101 fitter.AssignData(sourceValues.size(), 1, peak_values.data(), sourceValues.data());
102 fitter.Eval();
103
104 if(fitter.GetChisquare() < best_chi2) {
105 tmpMap.clear();
106 for(size_t i = 0; i < num_data_points; i++) {
107 tmpMap[peak_values[i]] = sourceValues[i];
108 }
109 best_chi2 = fitter.GetChisquare();
110 }
111 sourceTab->Status(Form("%zu/%zu - %zu - %d", num_data_points, peaks.size(), maxSize, iteration), 1);
112 ++iteration;
113 if(iteration >= TSourceCalibration::MaxIterations()) { break; }
114 }
115
116 // Remove one peak value from the best fit, make sure that we reproduce (0,0) intercept.
117 if(tmpMap.size() > 2) {
118 std::vector<double> peak_values;
119 std::vector<double> source_values;
120 for(auto& item : tmpMap) {
121 peak_values.push_back(item.first);
122 source_values.push_back(item.second);
123 }
124
125 for(size_t skipped_point = 0; skipped_point < source_values.size(); skipped_point++) {
126 std::swap(peak_values[skipped_point], peak_values.back());
127 std::swap(source_values[skipped_point], source_values.back());
128
129 fitter.ClearPoints();
130 fitter.AssignData(source_values.size() - 1, 1, peak_values.data(), source_values.data());
131 fitter.Eval();
132
133 if(std::abs(fitter.GetParameter(0)) > 10) {
135 std::cout << fitter.GetParameter(0) << " too big an offset, clearing map with " << tmpMap.size() << " points: ";
136 for(auto iter : tmpMap) { std::cout << iter.first << " - " << iter.second << "; "; }
137 std::cout << std::endl;
138 }
139 tmpMap.clear();
140 break;
141 }
142
143 std::swap(peak_values[skipped_point], peak_values.back());
144 std::swap(source_values[skipped_point], source_values.back());
145 }
146 }
147
148 // copy all values from the vectors to the result map
149 if(!tmpMap.empty()) {
150 // apparently c++14 is needed to use auto in a lambda so for now we spell it out
151 // for(auto it : tmpMap) result[*(std::find_if(peaks.begin(), peaks.end(), [&it] (auto& item) { return it.first == std::get<0>(item); }))] =
152 // *(std::find_if(sources.begin(), sources.end(), [&it] (auto& item) { return it.second == std::get<0>(item); }));
153 for(auto iter : tmpMap) {
154 result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](auto& item) { return iter.first == item; }))] =
155 *(std::find_if(sources.begin(), sources.end(), [&iter](auto& item) { return iter.second == std::get<0>(item); }));
156 }
158 std::cout << "Smart matched " << num_data_points << " data points from " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl;
159 std::cout << "Returning map with " << result.size() << " points: ";
160 for(auto iter : result) { std::cout << iter.first << " - " << std::get<0>(iter.second) << "; "; }
161 std::cout << std::endl;
162 }
163 break;
164 }
165 sourceTab->Status(Form("%zu/%zu - %zu", num_data_points, peaks.size(), maxSize), 1);
166 }
167
168 return result;
169}
170
171std::map<TGauss*, std::tuple<double, double, double, double>> Match(std::vector<TGauss*> peaks, std::vector<std::tuple<double, double, double, double>> sources, TSourceTab* sourceTab)
172{
173 /// This function tries to match a list of found peaks (channels) to a list of provided peaks (energies).
174 /// It does so in a brute force fashion where we try all combinations of channels and energies, do a linear fit through them, and keep the one with the best chi square.
175
176 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << "Matching " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl; }
178 std::cout << "peaks:";
179 for(auto* peak : peaks) {
180 std::cout << " " << peak->Centroid();
181 }
182 std::cout << std::endl;
183 std::cout << "energies:";
184 for(auto source : sources) {
185 std::cout << " " << std::get<0>(source);
186 }
187 std::cout << std::endl;
188 }
189
190 std::map<TGauss*, std::tuple<double, double, double, double>> result;
191 std::sort(peaks.begin(), peaks.end(), [](const TGauss* a, const TGauss* b) { return a->Centroid() < b->Centroid(); });
192 std::sort(sources.begin(), sources.end());
193
194 auto maxSize = peaks.size();
195 if(sources.size() > maxSize) { maxSize = sources.size(); }
196
197 // Peaks are the fitted points.
198 // source are the known values
199
200 TLinearFitter fitter(1, "1 ++ x");
201
202 // intermediate vectors and map
203 std::vector<double> peakValues(peaks.size());
204 for(size_t i = 0; i < peaks.size(); ++i) { peakValues[i] = peaks[i]->Centroid(); }
205 std::vector<double> sourceValues(sources.size());
206 for(size_t i = 0; i < sources.size(); ++i) { sourceValues[i] = std::get<0>(sources[i]); }
207 std::map<double, double> tmpMap;
208
209 for(size_t num_data_points = peakValues.size(); num_data_points > 0; num_data_points--) {
210 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << num_data_points << " data points:" << std::endl; }
211 double best_chi2 = DBL_MAX;
212 int iteration = 0;
213 for(auto peak_values : combinations(peakValues, num_data_points)) {
214 // Add a (0,0) point to the calibration.
215 peak_values.push_back(0);
216 for(auto source_values : combinations(sourceValues, num_data_points)) {
217 source_values.push_back(0);
218
219 if(peakValues.size() > 3) {
220 double pratio = peak_values.front() / peak_values.at(peak_values.size() - 2);
221 double sratio = source_values.front() / source_values.at(source_values.size() - 2);
222 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "ratio: " << pratio << " - " << sratio << " = " << std::abs(pratio - sratio) << " "; }
223 if(std::abs(pratio - sratio) > 0.02) {
224 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "skipping" << std::endl; }
225 continue;
226 }
227 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << std::endl; }
228 }
229
230 fitter.ClearPoints();
231 fitter.AssignData(source_values.size(), 1, peak_values.data(), source_values.data());
232 fitter.Eval();
233
234 if(fitter.GetChisquare() < best_chi2) {
235 tmpMap.clear();
236 for(size_t i = 0; i < num_data_points; i++) {
237 tmpMap[peak_values[i]] = source_values[i];
238 }
240 std::cout << "Chi^2 " << fitter.GetChisquare() << " better than " << best_chi2 << ":" << std::endl;
241 for(auto iter : tmpMap) {
242 std::cout << std::setw(10) << iter.first << " - " << iter.second << std::endl;
243 }
244 }
245 best_chi2 = fitter.GetChisquare();
246 }
247 }
248 sourceTab->Status(Form("%zu/%zu - %zu - %d", num_data_points, peakValues.size(), maxSize, iteration), 1);
249 ++iteration;
250 if(iteration >= TSourceCalibration::MaxIterations()) { break; }
251 }
252
253 // Remove one peak value from the best fit, make sure that we reproduce (0,0) intercept.
254 if(tmpMap.size() > 2) {
255 std::vector<double> peak_values;
256 std::vector<double> source_values;
257 for(auto& item : tmpMap) {
258 peak_values.push_back(item.first);
259 source_values.push_back(item.second);
260 }
261
262 for(size_t skipped_point = 0; skipped_point < source_values.size(); skipped_point++) {
263 std::swap(peak_values[skipped_point], peak_values.back());
264 std::swap(source_values[skipped_point], source_values.back());
265
266 fitter.ClearPoints();
267 fitter.AssignData(source_values.size() - 1, 1, peak_values.data(), source_values.data());
268 fitter.Eval();
269
270 if(std::abs(fitter.GetParameter(0)) > 10) {
272 std::cout << fitter.GetParameter(0) << " too big, clearing map with " << tmpMap.size() << " points: ";
273 for(auto iter : tmpMap) { std::cout << iter.first << " - " << iter.second << "; "; }
274 std::cout << std::endl;
275 }
276 tmpMap.clear();
277 break;
278 }
279
280 std::swap(peak_values[skipped_point], peak_values.back());
281 std::swap(source_values[skipped_point], source_values.back());
282 }
283 }
284
285 // copy all values from the vectors to the result map
286 if(!tmpMap.empty()) {
287 for(auto iter : tmpMap) {
288 result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](auto& item) { return iter.first == item->Centroid(); }))] =
289 *(std::find_if(sources.begin(), sources.end(), [&iter](auto& item) { return iter.second == std::get<0>(item); }));
290 // apparently c++14 is needed to use auto in a lambda so for now we spell it out
291 //result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](std::tuple<double, double, double, double>& item) { return iter.first == std::get<0>(item); }))] =
292 // *(std::find_if(sources.begin(), sources.end(), [&iter](std::tuple<double, double, double, double>& item) { return iter.second == std::get<0>(item); }));
293 }
295 std::cout << "Matched " << num_data_points << " data points from " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl;
296 std::cout << "Returning map with " << result.size() << " points: ";
297 for(auto iter : result) { std::cout << iter.first->Centroid() << " - " << std::get<0>(iter.second) << "; "; }
298 std::cout << std::endl;
299 }
300 break;
301 }
302 sourceTab->Status(Form("%zu/%zu - %zu", num_data_points, peakValues.size(), maxSize), 1);
303 }
304
305 return result;
306}
307
308std::map<TGauss*, std::tuple<double, double, double, double>> SmartMatch(std::vector<TGauss*> peaks, std::vector<std::tuple<double, double, double, double>> sources, TSourceTab* sourceTab)
309{
310 /// This function tries to match a list of found peaks (channels) to a list of provided peaks (energies).
311 /// It does so in slightly smarter way than the brute force method `Match`, by taking the reported intensity of the source peaks into account.
312
313 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << "\"Smart\" matching " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl; }
315 for(size_t i = 0; i < peaks.size() || i < sources.size(); ++i) {
316 std::cout << i << ".: " << std::setw(8);
317 if(i < peaks.size()) {
318 std::cout << peaks[i]->Centroid();
319 } else {
320 std::cout << " ";
321 }
322 std::cout << " - " << std::setw(8);
323 if(i < sources.size()) {
324 std::cout << std::get<0>(sources[i]);
325 } else {
326 std::cout << " ";
327 }
328 std::cout << std::endl;
329 }
330 }
331
332 std::map<TGauss*, std::tuple<double, double, double, double>> result;
333 std::sort(peaks.begin(), peaks.end(), [](const TGauss* a, const TGauss* b) { return a->Centroid() < b->Centroid(); });
334 std::sort(sources.begin(), sources.end(), [](const std::tuple<double, double, double, double>& a, const std::tuple<double, double, double, double>& b) { return std::get<2>(a) > std::get<2>(b); });
335
336 auto maxSize = peaks.size();
337 if(sources.size() > maxSize) { maxSize = sources.size(); }
338
339 // Peaks are the fitted points.
340 // source are the known values
341
342 TLinearFitter fitter(1, "1 ++ x");
343
344 // intermediate vectors and map
345 std::vector<double> peakValues(peaks.size());
346 for(size_t i = 0; i < peaks.size(); ++i) { peakValues[i] = peaks[i]->Centroid(); }
347 std::vector<double> sourceValues(sources.size());
348 std::map<double, double> tmpMap;
349
350 for(size_t num_data_points = std::min(peakValues.size(), sourceValues.size()); num_data_points > 0; num_data_points--) {
351 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << num_data_points << " data points:" << std::endl; }
352 double best_chi2 = DBL_MAX;
353 int iteration = 0;
354 for(auto peak_values : combinations(peakValues, num_data_points)) {
355 // Add a (0,0) point to the calibration.
356 peak_values.push_back(0);
357 // instead of going through all possible combinations of the peaks with the source energies
358 // we pick the num_data_points most intense lines and try them
359 // we don't do the same with the peaks as there might be an intense background peak in the data (511 etc.)
360 sourceValues.resize(num_data_points);
361 for(size_t i = 0; i < sourceValues.size(); ++i) { sourceValues[i] = std::get<0>(sources[i]); }
362 std::sort(sourceValues.begin(), sourceValues.end());
363 sourceValues.push_back(0);
364
366 for(size_t i = 0; i < peak_values.size(); ++i) {
367 std::cout << i << ".: " << std::setw(8) << peak_values[i] << " - " << std::setw(8) << sourceValues[i] << std::endl;
368 }
369 }
370 if(peakValues.size() > 3) {
371 double pratio = peak_values.front() / peak_values.at(peak_values.size() - 2);
372 double sratio = sourceValues.front() / sourceValues.at(sourceValues.size() - 2);
373 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "ratio: " << pratio << " - " << sratio << " = " << std::abs(pratio - sratio) << std::endl; }
374 if(std::abs(pratio - sratio) > 0.02) {
375 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "skipping" << std::endl; }
376 continue;
377 }
378 }
379
380 fitter.ClearPoints();
381 fitter.AssignData(sourceValues.size(), 1, peak_values.data(), sourceValues.data());
382 fitter.Eval();
383
384 if(fitter.GetChisquare() < best_chi2) {
385 tmpMap.clear();
386 for(size_t i = 0; i < num_data_points; i++) {
387 tmpMap[peak_values[i]] = sourceValues[i];
388 }
389 best_chi2 = fitter.GetChisquare();
390 }
391 sourceTab->Status(Form("%zu/%zu - %zu - %d", num_data_points, peakValues.size(), maxSize, iteration), 1);
392 ++iteration;
393 if(iteration >= TSourceCalibration::MaxIterations()) { break; }
394 }
395
396 // Remove one peak value from the best fit, make sure that we reproduce (0,0) intercept.
397 if(tmpMap.size() > 2) {
398 std::vector<double> peak_values;
399 std::vector<double> source_values;
400 for(auto& item : tmpMap) {
401 peak_values.push_back(item.first);
402 source_values.push_back(item.second);
403 }
404
405 for(size_t skipped_point = 0; skipped_point < source_values.size(); skipped_point++) {
406 std::swap(peak_values[skipped_point], peak_values.back());
407 std::swap(source_values[skipped_point], source_values.back());
408
409 fitter.ClearPoints();
410 fitter.AssignData(source_values.size() - 1, 1, peak_values.data(), source_values.data());
411 fitter.Eval();
412
413 if(std::abs(fitter.GetParameter(0)) > 10) {
415 std::cout << fitter.GetParameter(0) << " too big an offset, clearing map with " << tmpMap.size() << " points: ";
416 for(auto iter : tmpMap) { std::cout << iter.first << " - " << iter.second << "; "; }
417 std::cout << std::endl;
418 }
419 tmpMap.clear();
420 break;
421 }
422
423 std::swap(peak_values[skipped_point], peak_values.back());
424 std::swap(source_values[skipped_point], source_values.back());
425 }
426 }
427
428 // copy all values from the vectors to the result map
429 if(!tmpMap.empty()) {
430 // apparently c++14 is needed to use auto in a lambda so for now we spell it out
431 // for(auto it : tmpMap) result[*(std::find_if(peaks.begin(), peaks.end(), [&it] (auto& item) { return it.first == std::get<0>(item); }))] =
432 // *(std::find_if(sources.begin(), sources.end(), [&it] (auto& item) { return it.second == std::get<0>(item); }));
433 for(auto iter : tmpMap) {
434 result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](auto& item) { return iter.first == item->Centroid(); }))] =
435 *(std::find_if(sources.begin(), sources.end(), [&iter](auto& item) { return iter.second == std::get<0>(item); }));
436 }
438 std::cout << "Smart matched " << num_data_points << " data points from " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl;
439 std::cout << "Returning map with " << result.size() << " points: ";
440 for(auto iter : result) { std::cout << iter.first->Centroid() << " - " << std::get<0>(iter.second) << "; "; }
441 std::cout << std::endl;
442 }
443 break;
444 }
445 sourceTab->Status(Form("%zu/%zu - %zu", num_data_points, peakValues.size(), maxSize), 1);
446 }
447
448 return result;
449}
450
451double Polynomial(double* x, double* par) // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, readability-non-const-parameter)
452{
453 double result = par[1];
454 for(int i = 1; i <= par[0]; ++i) {
455 result += par[i + 1] * TMath::Power(x[0], i);
456 }
457 return result;
458}
459
460bool FilledBin(TH2* matrix, const int& bin)
461{
462 return (matrix->Integral(bin, bin, 1, matrix->GetNbinsY()) > 1000);
463}
464
465//////////////////////////////////////// TSourceTab ////////////////////////////////////////
466TSourceTab::TSourceTab(TSourceCalibration* sourceCal, TChannelTab* channel, TGCompositeFrame* frame, GH1D* projection, const char* sourceName, std::vector<std::tuple<double, double, double, double>> sourceEnergy)
467 : fSourceCalibration(sourceCal), fChannelTab(channel), fSourceFrame(frame), fProjection(projection), fSourceName(sourceName), fSourceEnergy(std::move(sourceEnergy))
468{
469 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << RESET_COLOR << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
470 // default sorting of tuples is by the first entry, which is the source energy here, so no need for a custom sort condition
471 std::sort(fSourceEnergy.begin(), fSourceEnergy.end());
473 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << "interface built, finding peaks ... " << RESET_COLOR << std::endl; }
474}
475
477{
478 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << RESET_COLOR << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
483
485 fData = rhs.fData;
486 fFwhm = rhs.fFwhm;
487 // ? not sure what to do here, clearing the vectors seems unnecessary since we never filled them
488 // but we also can't just simply copy them, we would need a deep copy, which might not make sense?
489 // does is make sense in general to have assignment constructors for this class?
490 fFits.clear();
491 fBadFits.clear();
492 fPeaks.clear();
493}
494
496{
497 for(auto* fit : fFits) {
498 delete fit;
499 }
500 fFits.clear();
501 for(auto* fit : fBadFits) {
502 delete fit;
503 }
504 fBadFits.clear();
505 for(auto* peak : fPeaks) {
506 delete peak;
507 }
508 fPeaks.clear();
509 delete fSourceFrame;
510 delete fProjectionCanvas;
511 delete fSourceStatusBar;
512 //delete fProjection;
513 //delete fData;
514 //delete fFwhm;
515}
516
518{
519 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << RESET_COLOR << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
520 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Creating unique lock on graphics mutex to build interface " << std::endl; }
521 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
522 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Got unique lock on graphics mutex!" << std::endl; }
523 // frame with canvas and status bar
524 fProjectionCanvas = new TRootEmbeddedCanvas(Form("ProjectionCanvas%s", fProjection->GetName()), fSourceFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
525
526 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Created projection canvas!" << std::endl; }
527
528 fSourceFrame->AddFrame(fProjectionCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
529
530 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Added projection canvas to source frame!" << std::endl; }
531
533 std::array<int, 3> parts = {35, 50, 15};
534 fSourceStatusBar->SetParts(parts.data(), parts.size());
535
536 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Created status bar!" << std::endl; }
537
538 fSourceFrame->AddFrame(fSourceStatusBar, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
539
540 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Releasing unique lock on graphics mutex after building interface" << std::endl; }
541}
542
544{
545 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << RESET_COLOR << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
546 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
547 fProjectionCanvas->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", "TSourceTab", this, "ProjectionStatus(Int_t,Int_t,Int_t,TObject*)");
548 fProjectionCanvas->Connect("ProcessedEvent(Event_t*)", "TSourceTab", this, "ProjectionStatus(Event_t*)");
549}
550
552{
553 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
554 fProjectionCanvas->Disconnect("ProcessedEvent(Event_t*)", this, "ProjectionStatus(Event_t*)");
555 fProjectionCanvas->GetCanvas()->Disconnect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", this, "ProjectionStatus(Int_t,Int_t,Int_t,TObject*)");
556}
557
559{
560 // enum EGEventType {
561 // kGKeyPress = 0, kKeyRelease, = 1, kButtonPress = 2, kButtonRelease = 3,
562 // kMotionNotify = 4, kEnterNotify = 5, kLeaveNotify = 6, kFocusIn = 7, kFocusOut = 8,
563 // kExpose = 9, kConfigureNotify = 10, kMapNotify = 11, kUnmapNotify = 12, kDestroyNotify = 13,
564 // kClientMessage = 14, kSelectionClear = 15, kSelectionRequest = 16, kSelectionNotify = 17,
565 // kColormapNotify = 18, kButtonDoubleClick = 19, kOtherEvent = 20
566 // };
567 //if(TSourceCalibration::VerboseLevel() == EVerbosity::kAll) {
568 // std::cout << __PRETTY_FUNCTION__ << ": code " << event->fCode << ", count " << event->fCount << ", state " << event->fState << ", type " << event->fType << std::endl; // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
569 // if(event->fType == kClientMessage) {
570 // std::cout << "Client message: " << event->fUser[0] << ", " << event->fUser[1] << ", " << event->fUser[2] << std::endl;
571 // }
572 //}
573 if(event->fType == kEnterNotify) {
575 std::cout << "Entered source tab => changing gPad from " << gPad->GetName();
576 }
577 gPad = fProjectionCanvas->GetCanvas();
579 std::cout << " to " << gPad->GetName() << std::endl;
580 }
581 }
582}
583
584void TSourceTab::ProjectionStatus(Int_t event, Int_t px, Int_t py, TObject* selected)
585{
586 // looks like 51 is hovering over the object, 52 is moving the cursor over the object, and 53 is moving the cursort away from the object
587 // kButton1 = left mouse button, kButton2 = right mouse button
588 // enum EEventType {
589 // kNoEvent = 0,
590 // kButton1Down = 1, kButton2Down = 2, kButton3Down = 3, kKeyDown = 4,
591 // kWheelUp = 5, kWheelDown = 6, kButton1Shift = 7, kButton1ShiftMotion = 8,
592 // kButton1Up = 11, kButton2Up = 12, kButton3Up = 13, kKeyUp = 14,
593 // kButton1Motion = 21, kButton2Motion = 22, kButton3Motion = 23, kKeyPress = 24,
594 // kArrowKeyPress = 25, kArrowKeyRelease = 26,
595 // kButton1Locate = 41, kButton2Locate = 42, kButton3Locate = 43, kESC = 27,
596 // kMouseMotion = 51, kMouseEnter = 52, kMouseLeave = 53,
597 // kButton1Double = 61, kButton2Double = 62, kButton3Double = 63
598 //};
599 Status(selected->GetName(), 0);
600 Status(selected->GetObjectInfo(px, py), 1);
601 //auto key = static_cast<char>(px);
602 switch(event) {
603 case kButton1Down:
604 if(selected == fProjection) {
605 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << "Adding new marker at " << px << ", " << py << " (pixel to user: " << fProjectionCanvas->GetCanvas()->PixeltoX(px) << ", " << fProjectionCanvas->GetCanvas()->PixeltoY(py - fProjectionCanvas->GetCanvas()->GetWh()) << ", abs pixel to user: " << fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) << ", " << fProjectionCanvas->GetCanvas()->AbsPixeltoY(py) << ")" << std::endl; }
607 std::cout << fPeaks.size() << " peaks:";
608 for(auto* peak : fPeaks) {
609 std::cout << " " << peak->Centroid();
610 }
611 std::cout << std::endl;
612 }
613 auto* polym = static_cast<TPolyMarker*>(fProjection->GetListOfFunctions()->FindObject("TPolyMarker"));
614 if(polym == nullptr) {
615 std::cerr << "No peaks defined yet?" << std::endl;
616 return;
617 }
618 polym->SetNextPoint(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px), fProjectionCanvas->GetCanvas()->AbsPixeltoX(py));
619 double range = 4 * fSourceCalibration->Sigma(); // * fProjection->GetXaxis()->GetBinWidth(1);
620 auto* pf = new TPeakFitter(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) - range, fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) + range);
621 auto* peak = new TGauss(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px));
622 pf->AddPeak(peak);
623 pf->Fit(fProjection, "qnretryfit");
624 if(peak->Area() > 0) {
625 fPeaks.push_back(peak);
626 fFits.push_back(pf);
627 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Fitted peak " << fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) - range << " - " << fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) + range << " -> centroid " << peak->Centroid() << std::endl; }
628 } else {
629 std::cout << "Ignoring peak at " << peak->Centroid() << " with negative area " << peak->Area() << std::endl;
630 }
631 fProjection->GetListOfFunctions()->Remove(peak);
632 fProjection->Sumw2(false); // turn errors off, makes the histogram look like a histogram when using normal plotting (hist and samefunc doesn't work for some reason)
633
634 std::sort(fPeaks.begin(), fPeaks.end(), [](const TGauss* a, const TGauss* b) { return a->Centroid() < b->Centroid(); });
635
636 auto map = Match(fPeaks, fSourceEnergy, this);
637 Add(map);
638
639 // update status
640 Status(Form("%d/%d", static_cast<int>(fData->GetN()), static_cast<int>(fPeaks.size())), 2);
641
642 // update data and re-calibrate
646 }
647 break;
648 case kArrowKeyPress:
649 //Move1DHistogram(px, fProjection);
650 //fProjectionCanvas->GetCanvas()->Modified();
651 case kArrowKeyRelease:
652 break;
653 case kKeyDown:
654 case kKeyPress:
655 //switch(key) {
656 // case 'u':
657 // fProjection->GetXaxis()->UnZoom();
658 // fProjection->GetYaxis()->UnZoom();
659 // break;
660 // case 'U':
661 // fProjection->GetYaxis()->UnZoom();
662 // break;
663 // default:
664 // std::cout << "Key press '" << key << "' not recognized!" << std::endl;
665 // break;
666 //}
667 case kKeyUp:
668 break;
669 case kButton1Motion:
670 break;
671 case kButton1Up:
672 break;
673 case kMouseMotion:
674 //{
675 // auto* canvas = fProjectionCanvas->GetCanvas();
676 // if(canvas == nullptr) {
677 // Status("canvas nullptr", 0);
678 // break;
679 // }
680 // auto* pad = canvas->GetSelectedPad();
681 // if(pad == nullptr) {
682 // Status("pad nullptr", 0);
683 // break;
684 // }
685
686 // Status(Form("x = %f, y = %f", pad->AbsPixeltoX(px), pad->AbsPixeltoY(py)), 0);
687 //}
688 break;
689 case kMouseEnter:
690 break;
691 case kMouseLeave:
692 break;
693 default:
695 std::cout << "unprocessed event " << event << " with px, py " << px << ", " << py << " selected object is a " << selected->ClassName() << " with name " << selected->GetName() << " and title " << selected->GetTitle() << " object info is " << selected->GetObjectInfo(px, py) << std::endl;
696 }
697 break;
698 }
699}
700
702{
703 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "UpdateRegions: clearing " << fRegions.size() << " regions" << std::endl; }
704 fRegions.clear();
706 std::cout << "projection has " << fProjection->ListOfRegions()->GetEntries() << " regions:" << std::endl;
707 fProjection->ListOfRegions()->Print();
708 }
710 for(auto* obj : *(fProjection->ListOfRegions())) {
711 if(obj->InheritsFrom(TRegion::Class())) {
712 auto* region = static_cast<TRegion*>(obj);
713 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "UpdateRegions: found " << fRegions.size() << ". region: " << std::min(region->GetX1(), region->GetX2()) << " - " << std::max(region->GetX1(), region->GetX2()) << std::endl; }
714 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { region->Print(); }
715 fRegions.emplace_back(std::min(region->GetX1(), region->GetX2()), std::max(region->GetX1(), region->GetX2()));
717 std::cout << obj->GetName() << " is not a TRegion but a " << obj->ClassName() << std::endl;
718 }
719 }
720}
721
723{
724 return TSourceCalibration::AcceptBadFits() || (peak->CentroidErr() < 0.1 * peak->Centroid() && peak->AreaErr() < peak->Area() && !std::isnan(peak->CentroidErr()) && !std::isnan(peak->AreaErr()));
725}
726
728{
729 /// This functions removes all fits named "gauss_total" from the histogram and adds instead the fit functions from all good and bad peaks to it.
730 TList* functions = fProjection->GetListOfFunctions();
732 std::cout << "Updating functions for histogram " << fProjection->GetName() << " by deleting " << functions->GetEntries() << " functions:" << std::endl;
733 for(auto* obj : *functions) {
734 std::cout << obj << ": " << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName();
735 if(obj->IsA() == TF1::Class()) {
736 std::cout << " line color " << static_cast<TF1*>(obj)->GetLineColor();
737 } else {
738 std::cout << " not a TF1";
739 }
740 std::cout << std::endl;
741 }
742 }
743 // remove all old fits (all have the same name/title of "gauss_total")
744 // alternative: clone the peaks with indices in their names, that would also allow us to add a function to replace a single fit
745 TObject* fit = nullptr;
746 while((fit = functions->FindObject("gauss_total")) != nullptr) {
747 functions->Remove(fit);
748 }
749 // add all the fit functions of the good and the bad peaks
750 for(auto* obj : fFits) {
751 functions->Add(obj->GetFitFunction()->Clone("gauss_total"));
752 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Added clone of " << obj->GetFitFunction() << " = " << functions->Last() << std::endl; }
753 }
754 for(auto* obj : fBadFits) {
755 functions->Add(obj->GetFitFunction()->Clone("gauss_total"));
756 }
758 std::cout << "And adding " << functions->GetEntries() << " functions instead:" << std::endl;
759 for(auto* obj : *functions) {
760 std::cout << obj << ": " << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName();
761 if(obj->IsA() == TF1::Class()) {
762 std::cout << " line color " << static_cast<TF1*>(obj)->GetLineColor();
763 } else {
764 std::cout << " not a TF1";
765 }
766 std::cout << std::endl;
767 }
768 }
769 if(!fFits.empty() && fFits.front()->NumberOfPeaks() > 0 && fFits.back()->NumberOfPeaks() > 0) {
770 fProjection->SetTitle(Form("%lu of %lu source peaks used, channel %.0f - %.0f", fFits.size(), fSourceEnergy.size(), fFits.front()->Peak(0)->Centroid(), fFits.back()->Peak(0)->Centroid()));
771 } else {
772 fProjection->SetTitle(Form("%lu of %lu source peaks used, channel ???? - ????", fFits.size(), fSourceEnergy.size()));
773 }
774 fProjectionCanvas->GetCanvas()->Modified();
775}
776
778{
779 fProjectionCanvas->GetCanvas()->cd();
780 fProjection->Draw(); // seems like hist + samefunc does not work. Could use hist + loop over list of functions (with same)
781 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": drew " << fProjection->GetName() << " on " << fProjectionCanvas->GetCanvas()->GetName() << "/" << gPad->GetName() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
782}
783
784void TSourceTab::InitialCalibration(const bool& force)
785{
786 /// This functions finds the peaks in the histogram, fits them, and adds the fits to the list of peaks.
787 /// This list is then used to find all peaks that lie on a straight line.
788
789 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << std::flush << " " << fProjection->GetName() << ": got " << fPeaks.size() << " peaks" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
790
791 if(fPeaks.empty() || fData == nullptr || force) {
793 std::cout << __PRETTY_FUNCTION__ << ": # peaks " << fPeaks.size() << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
794 }
795 fPeaks.clear();
796 fFits.clear();
797 fBadFits.clear();
798 // Remove all associated functions from projection.
799 // These are the poly markers, fits, and the pave stat
801 TList* functions = fProjection->GetListOfFunctions();
802 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetName() << " before clearing" << std::endl;
803 for(auto* obj : *functions) {
804 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
805 }
806 }
807 fProjection->GetListOfFunctions()->Clear();
808 TSpectrum spectrum;
809 int nofPeaks = 0;
810 std::vector<double> peakPos;
812 if(fRegions.empty()) {
813 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "No regions found, using whole spectrum" << std::endl; }
815 nofPeaks = spectrum.GetNPeaks();
816 peakPos.insert(peakPos.end(), spectrum.GetPositionX(), spectrum.GetPositionX() + nofPeaks);
817 } else {
818 for(auto& region : fRegions) {
819 fProjection->GetXaxis()->SetRangeUser(region.first, region.second);
821 int tmpNofPeaks = spectrum.GetNPeaks();
822 peakPos.insert(peakPos.end(), spectrum.GetPositionX(), spectrum.GetPositionX() + tmpNofPeaks);
823 nofPeaks += spectrum.GetNPeaks();
824 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": found " << spectrum.GetNPeaks() << " peaks in region " << region.first << " - " << region.second << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
825 }
826 fProjection->GetXaxis()->UnZoom();
827 }
828 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": found " << nofPeaks << " peaks" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
829
830 auto map = RoughCal(peakPos, fSourceEnergy, this);
831 Add(map);
832
833 // update status
834 Status(Form("%d/%d", static_cast<int>(fData->GetN()), nofPeaks), 2);
835 }
836}
837
838void TSourceTab::FindCalibratedPeaks(const TF1* calibration)
839{
840 /// This functions finds uses the existing calibration to find all peaks from the list of source energies.
841
842 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << std::flush << " " << fProjection->GetName() << ": using calibration " << calibration << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
843
844 if(calibration == nullptr) {
845 std::cerr << __PRETTY_FUNCTION__ << ": provided calibration is nullptr, this function can only be called after an initial calibration has been made!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
846 return;
847 }
848
849 // try and make sure the right canvas is selected as all fits are drawn in the currently selected canvas
850 gPad = fProjectionCanvas->GetCanvas();
851
852 fPeaks.clear();
853 fFits.clear();
854 fBadFits.clear();
855 // Remove all associated functions from projection.
856 // These are the poly markers, fits, and the pave stat
858 std::cout << "calibration has " << calibration->GetNpar() << " parameters (";
859 for(int i = 0; i < calibration->GetNpar(); ++i) {
860 if(i != 0) {
861 std::cout << ", ";
862 }
863 std::cout << i << ": " << calibration->GetParameter(i);
864 }
865 std::cout << "), if linear offset is " << calibration->Eval(0) << " and gain is " << calibration->Eval(1) - calibration->Eval(0) << std::endl;
866 TList* functions = fProjection->GetListOfFunctions();
867 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetName() << " before clearing" << std::endl;
868 for(auto* obj : *functions) {
869 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
870 }
871 }
872 fProjection->GetListOfFunctions()->Clear();
873
874 std::map<TGauss*, std::tuple<double, double, double, double>> map;
875
876 // loop over all source energies and check if we want to use the peak or if it's too weak
877 // for each source energy we calculate the channel it would be at
878 std::vector<std::pair<std::tuple<double, double, double, double>, double>> channelPos;
879 for(auto& tuple : fSourceEnergy) {
880 if(std::get<2>(tuple) < TSourceCalibration::MinIntensity()) {
882 std::cout << "Skipping peak at " << std::get<0>(tuple) << " keV with intensity " << std::get<2>(tuple) << " below required minimum intensity " << TSourceCalibration::MinIntensity() << std::endl;
883 }
884 continue;
885 }
886 channelPos.emplace_back(tuple, calibration->GetX(std::get<0>(tuple)));
888 std::cout << "Set channel position for " << channelPos.size() - 1 << ". peak to " << channelPos.back().second << " channels = " << std::get<0>(channelPos.back().first) << " keV" << std::endl;
889 }
890 }
891 // loop over channel positions and fit the peaks
892 for(size_t i = 0; i < channelPos.size(); ++i) {
894 double highRange = TSourceCalibration::FitRange() * fSourceCalibration->Sigma();
895 // if the range for this peaks overlaps with the previous/next one, reduce the range but not more than half
896 // also check if the previous/next one is above the minimum intensity, because otherwise we would have skipped that one anyway
897 if(i > 0) {
898 if(channelPos[i].second - lowRange < channelPos[i - 1].second + highRange) {
900 std::cout << channelPos[i].second - lowRange << " < " << channelPos[i - 1].second + highRange << ": changing low range from " << lowRange << " to " << std::max(lowRange / 2., (channelPos[i].second - channelPos[i - 1].second) / 2.) << " = std::max(" << lowRange / 2. << ", " << (channelPos[i].second - channelPos[i - 1].second) / 2. << " = (" << channelPos[i].second << " - " << channelPos[i - 1].second << "/2.)" << std::endl;
901 }
902 lowRange = std::max(lowRange / 2., (channelPos[i].second - channelPos[i - 1].second) / 2.);
903 }
904 }
905 if(i + 1 < channelPos.size()) {
906 if(channelPos[i].second + highRange > channelPos[i + 1].second - lowRange) {
908 std::cout << channelPos[i].second + highRange << " > " << channelPos[i + 1].second - lowRange << ": changing high range from " << highRange << " to " << std::max(highRange / 2., (channelPos[i + 1].second - channelPos[i].second) / 2.) << " = std::max(" << highRange / 2. << ", " << (channelPos[i + 1].second - channelPos[i].second) / 2. << " = (" << channelPos[i + 1].second << " - " << channelPos[i].second << "/2.)" << std::endl;
909 }
910 highRange = std::min(highRange / 2., (channelPos[i + 1].second - channelPos[i].second) / 2.);
911 }
912 }
914 std::cout << "Trying to fit peak " << channelPos[i].second << " in range " << channelPos[i].second - lowRange << " - " << channelPos[i].second + highRange << std::endl;
915 }
916 // if we aren't already redirecting the output, we redirect it to /dev/null and do a quiet fit, otherwise we do a normal fit since the output will be redirected to a file
917 auto* pf = new TPeakFitter(channelPos[i].second - lowRange, channelPos[i].second + highRange);
918 auto* peak = new TGauss(channelPos[i].second);
919 pf->AddPeak(peak);
920 if(TSourceCalibration::LogFile().empty()) {
921 TRedirect redirect("/dev/null");
922 pf->Fit(fProjection, "qnretryfit");
923 } else {
924 pf->Fit(fProjection, "nretryfit");
925 }
926 if(peak->Area() > 0) {
927 // check if we accept bad fits (and if it was a bad fit)
928 if(Good(peak, channelPos[i].second - lowRange, channelPos[i].second + highRange)) {
929 fPeaks.push_back(peak);
930 fFits.push_back(pf);
931 map[peak] = channelPos[i].first;
933 std::cout << "Fitted peak " << channelPos[i].second - lowRange << " - " << channelPos[i].second + highRange << " -> centroid " << peak->Centroid() << ", area " << peak->Area() << std::endl;
934 }
935 } else {
936 fBadFits.push_back(pf);
938 std::cout << "We're ignoring bad fits, and this one has position " << peak->Centroid() << " +- " << peak->CentroidErr() << ", area " << peak->Area() << " +- " << peak->AreaErr() << std::endl;
939 }
940 }
942 std::cout << "Ignoring peak at " << peak->Centroid() << " with negative area " << peak->Area() << std::endl;
943 }
944 }
945
946 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": added " << fPeaks.size() << " peaks (" << fSourceEnergy.size() << " source energies, " << channelPos.size() << " channel positions)" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
947 fProjection->Sumw2(false); // turn errors off, makes the histogram look like a histogram when using normal plotting (hist and samefunc doesn't work for some reason)
948
949 std::sort(fFits.begin(), fFits.end(), [](const TPeakFitter* a, const TPeakFitter* b) { return a->GetFitFunction()->GetParameter("centroid_0") < b->GetFitFunction()->GetParameter("centroid_0"); });
950 std::sort(fBadFits.begin(), fBadFits.end(), [](const TPeakFitter* a, const TPeakFitter* b) { return a->GetFitFunction()->GetParameter("centroid_0") < b->GetFitFunction()->GetParameter("centroid_0"); });
951 std::sort(fPeaks.begin(), fPeaks.end(), [](const TGauss* a, const TGauss* b) { return a->Centroid() < b->Centroid(); });
952
954
955 UpdateFits();
956
957 Add(map);
958
960 std::cout << __PRETTY_FUNCTION__ << ": found " << fData->GetN() << " peaks"; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
961 Double_t* x = fData->GetX();
962 Double_t* y = fData->GetY();
963 for(int i = 0; i < fData->GetN(); ++i) {
964 std::cout << "; " << x[i] << " - " << y[i];
965 }
966 std::cout << std::endl;
967 }
968}
969
970void TSourceTab::Add(std::map<double, std::tuple<double, double, double, double>> map)
971{
973 std::cout << DCYAN << "Adding map with " << map.size() << " data points, fData = " << fData << std::endl;
974 }
975 if(fData == nullptr) {
976 fData = new TGraphErrors(map.size());
977 } else {
978 fData->Set(map.size());
979 }
980 fData->SetLineColor(2);
981 fData->SetMarkerColor(2);
982 int i = 0;
983 for(auto iter = map.begin(); iter != map.end();) {
984 // more readable variable names
985 auto peakPos = iter->first;
986 auto energy = std::get<0>(iter->second);
987 fData->SetPoint(i, peakPos, energy);
989 std::cout << "Using peak with position " << peakPos << ", energy " << energy << std::endl;
990 }
991 ++iter;
992 ++i;
993 }
995 std::cout << "Accepted " << map.size() << " peaks" << std::endl;
996 }
997 fData->Sort();
998}
999
1000void TSourceTab::Add(std::map<TGauss*, std::tuple<double, double, double, double>> map)
1001{
1003 std::cout << DCYAN << "Adding map with " << map.size() << " data points, fData = " << fData << std::endl;
1004 }
1005 if(fData == nullptr) {
1006 fData = new TGraphErrors(map.size());
1007 } else {
1008 fData->Set(map.size());
1009 }
1010 fData->SetLineColor(2);
1011 fData->SetMarkerColor(2);
1012 if(fFwhm == nullptr) {
1013 fFwhm = new TGraphErrors(map.size());
1014 } else {
1015 fFwhm->Set(map.size());
1016 }
1017 fFwhm->SetLineColor(4);
1018 fFwhm->SetMarkerColor(4);
1019 int i = 0;
1020 for(auto iter = map.begin(); iter != map.end();) {
1021 // more readable variable names
1022 auto peakPos = iter->first->Centroid();
1023 auto peakPosErr = iter->first->CentroidErr();
1024 auto peakArea = iter->first->Area();
1025 auto peakAreaErr = iter->first->AreaErr();
1026 auto fwhm = iter->first->FWHM();
1027 auto fwhmErr = iter->first->FWHMErr();
1028 auto energy = std::get<0>(iter->second);
1029 auto energyErr = std::get<1>(iter->second);
1030 // drop this peak if the uncertainties in area or position are too large
1031 if(!Good(iter->first)) {
1033 std::cout << "Dropping peak with position " << peakPos << " +- " << peakPosErr << ", area " << peakArea << " +- " << peakAreaErr << ", energy " << energy << std::endl;
1034 }
1035 iter = map.erase(iter);
1037 std::cout << "Erasing peak returned iterator " << std::distance(map.begin(), iter) << std::endl;
1038 }
1039 } else {
1040 fData->SetPoint(i, peakPos, energy);
1041 fData->SetPointError(i, peakPosErr, energyErr);
1042 fFwhm->SetPoint(i, peakPos, fwhm);
1043 fFwhm->SetPointError(i, peakPosErr, fwhmErr);
1045 std::cout << "Using peak with position " << peakPos << " +- " << peakPosErr << ", area " << peakArea << " +- " << peakAreaErr << ", energy " << energy << std::endl;
1046 }
1047 ++iter;
1048 ++i;
1049 }
1050 }
1052 std::cout << "Accepted " << map.size() << " peaks" << std::endl;
1053 }
1054 // if we dropped a peak, we need to resize the graph, if we haven't dropped any this doesn't do anything
1055 fData->Set(map.size());
1056 fFwhm->Set(map.size());
1057 fData->Sort();
1058 fFwhm->Sort();
1059 // split poly marker into those peaks that were used, and those that weren't
1060 TList* functions = fProjection->GetListOfFunctions();
1062 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetName() << std::endl;
1063 for(auto* obj : *functions) {
1064 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
1065 }
1066 }
1067 auto* polym = static_cast<TPolyMarker*>(functions->FindObject("TPolyMarker"));
1068 if(polym != nullptr) {
1069 double* oldX = polym->GetX();
1070 double* oldY = polym->GetY();
1071 int size = polym->GetN();
1072 std::vector<double> newX;
1073 std::vector<double> newY;
1074 std::vector<double> unusedX;
1075 std::vector<double> unusedY;
1076 for(i = 0; i < size; ++i) {
1077 bool used = false;
1078 for(auto point : map) {
1079 if(TMath::Abs(oldX[i] - point.first->Centroid()) < fSourceCalibration->Sigma()) {
1080 newX.push_back(oldX[i]);
1081 newY.push_back(oldY[i]);
1082 used = true;
1083 break;
1084 }
1085 }
1086 if(!used) {
1087 unusedX.push_back(oldX[i]);
1088 unusedY.push_back(oldY[i]);
1089 }
1090 }
1091 polym->SetPolyMarker(newX.size(), newX.data(), newY.data());
1092 auto* unusedMarkers = new TPolyMarker(unusedX.size(), unusedX.data(), unusedY.data());
1093 unusedMarkers->SetMarkerStyle(23); // triangle down
1094 unusedMarkers->SetMarkerColor(16); // light grey
1095 functions->Add(unusedMarkers);
1097 std::cout << fProjection->GetTitle() << ": " << size << " peaks founds total, " << polym->GetN() << " used, and " << unusedMarkers->GetN() << " unused" << std::endl;
1099 std::cout << "Used: ";
1100 for(size_t m = 0; m < newX.size(); ++m) { std::cout << newX[m] << " - " << newY[m] << ";"; }
1101 std::cout << std::endl;
1102 std::cout << "Unused: ";
1103 for(size_t m = 0; m < unusedX.size(); ++m) { std::cout << unusedX[m] << " - " << unusedY[m] << ";"; }
1104 std::cout << std::endl;
1105 }
1107 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetTitle() << std::endl;
1108 for(auto* obj : *functions) {
1109 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
1110 }
1111 }
1112 }
1113 }
1114
1115 // change color of fit functions for unused peaks
1116 TIter iter(functions);
1117 TObject* item = nullptr;
1118 while((item = iter.Next()) != nullptr) {
1119 if(item->IsA() == TF1::Class() || item->IsA() == TGauss::Class()) { // if the item is a TF1 or TGauss we see if we can find the centroid in the map of used peaks
1120 double centroid = 0.;
1121 if(item->IsA() == TF1::Class()) {
1122 centroid = static_cast<TF1*>(item)->GetParameter(1);
1123 } else {
1124 centroid = static_cast<TGauss*>(item)->Centroid();
1125 }
1126 bool found = false;
1127 for(auto point : map) {
1128 if(TMath::Abs(centroid - point.first->Centroid()) < fSourceCalibration->Sigma()) {
1129 found = true;
1130 break;
1131 }
1132 }
1133 // remove the TF1 if it wasn't found in the map
1134 if(!found) {
1135 //functions->Remove(item);
1136 if(item->IsA() == TF1::Class()) {
1137 static_cast<TF1*>(item)->SetLineColor(kGray);
1138 } else {
1139 static_cast<TGauss*>(item)->GetFitFunction()->SetLineColor(kGray);
1140 }
1141 }
1142 }
1143 }
1144}
1145
1146void TSourceTab::ReplacePeak(const size_t& index, const double& channel)
1147{
1148 /// Replace the peak at the index with one centered at channel (calculated from an initial calibration and the source energy of this peak).
1149 /// This replaces the TGauss* in fPeaks, and updates the values of this index in fData and fFwhm.
1151 std::cout << DCYAN << __PRETTY_FUNCTION__ << ": index " << index << ", channel " << channel << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1152 for(size_t i = 0; i < fPeaks.size() && i < fFits.size(); ++i) {
1153 std::cout << i << ": " << fPeaks[i]->Centroid() << " +- " << fPeaks[i]->CentroidErr() << " - " << fFits[i]->GetFitFunction()->GetParameter("centroid_0") << std::endl;
1154 }
1155 }
1156
1157 if(index >= fPeaks.size()) {
1158 std::cerr << "Trying to replace peak at index " << index << " but there are only " << fPeaks.size() << " peaks!" << std::endl;
1159 return;
1160 }
1161
1162 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Replacing old peak at index " << index << ", centroid " << fPeaks.at(index)->Centroid() << " with new peak at channel " << channel << std::endl; }
1163
1164 // calculate the fitting range (low to high) and adjust it so we ignore the old peak
1166 double low = channel - range;
1167 double high = channel + range;
1168 if(fPeaks.at(index)->Centroid() < channel) {
1169 low = (fPeaks.at(index)->Centroid() + channel) / 2.;
1170 } else {
1171 high = (fPeaks.at(index)->Centroid() + channel) / 2.;
1172 }
1173 // if we aren't already redirecting the output, we redirect it to /dev/null and do a quiet fit, otherwise we do a normal fit since the output will be redirected to a file
1174 auto* pf = new TPeakFitter(low, high);
1175 auto* peak = new TGauss(channel);
1176 pf->AddPeak(peak);
1177 if(TSourceCalibration::LogFile().empty()) {
1178 TRedirect redirect("/dev/null");
1179 pf->Fit(fProjection, "retryfit");
1180 } else {
1181 pf->Fit(fProjection, "retryfit");
1182 }
1183 if(peak->Area() > 0) {
1184 if(Good(peak, low, high)) {
1186 std::cout << index << ". peak/fit out of " << fPeaks.size() << "/" << fFits.size() << " peaks/fits: " << std::flush << fPeaks.at(index)->Centroid() << "/" << fFits.at(index)->GetFitFunction()->GetParameter("centroid_0") << std::endl;
1187 std::cout << "Deleting " << index << ". peak " << fPeaks.at(index) << std::flush;
1188 }
1189 peak->SetLineColor(fPeaks.at(index)->GetLineColor());
1190 delete fPeaks.at(index);
1191 fPeaks[index] = peak;
1193 std::cout << " and setting it to " << fPeaks.at(index) << std::endl;
1194 }
1196 std::cout << "Deleting " << index << ". fit " << fFits.at(index) << std::flush;
1197 }
1198 pf->GetFitFunction()->SetLineColor(fFits.at(index)->GetFitFunction()->GetLineColor());
1199 delete fFits.at(index);
1200 fFits[index] = pf;
1202 std::cout << " and setting it to " << fFits.at(index) << std::endl;
1203 }
1204 UpdateFits();
1205 fData->SetPoint(index, peak->Centroid(), fData->GetPointY(index));
1206 fData->SetPointError(index, peak->CentroidErr(), fData->GetErrorY(index));
1207 fFwhm->SetPoint(index, peak->Centroid(), peak->FWHM());
1208 fFwhm->SetPointError(index, peak->CentroidErr(), peak->FWHMErr());
1210 std::cout << "Fitted peak " << low << " - " << high << " -> centroid " << peak->Centroid() << ", area " << peak->Area() << std::endl;
1211 }
1212 } else {
1214 std::cout << "We're ignoring bad fits, and this one has position " << peak->Centroid() << " +- " << peak->CentroidErr() << ", area " << peak->Area() << " +- " << peak->AreaErr() << std::endl;
1215 }
1216 }
1218 std::cout << "Ignoring peak at " << peak->Centroid() << " with negative area " << peak->Area() << std::endl;
1219 }
1220}
1221
1222void TSourceTab::RemovePoint(Int_t oldPoint)
1223{
1225 std::cout << DCYAN << __PRETTY_FUNCTION__ << ": oldPoint " << oldPoint << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1226 }
1227 // first thing to do is check if the graph the point was removed from matches the id of this tab
1229 //auto erasedPeak = fPeaks.at(oldPoint);
1230 fPeaks.erase(fPeaks.begin() + oldPoint);
1231 auto* erasedFit = fFits.at(oldPoint);
1232 fFits.erase(fFits.begin() + oldPoint);
1233 fBadFits.push_back(erasedFit);
1234 fData->RemovePoint(oldPoint);
1235
1236 SetLineColors();
1237
1238 UpdateFits();
1239
1241}
1242
1244{
1245 /// This function sets the line colors of good and bad fits to alternating colors.
1246 /// Red and blue for the good fits, and grey and dark grey for the bad fits.
1247
1248 // set colors of good fits alternating to red or blue (peaks themselves are set to darker versions of those colors)
1249 for(size_t i = 0; i < fPeaks.size() && i < fFits.size(); ++i) {
1250 if(i % 2 == 0) {
1251 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << i << ": setting line colors to (dark) red for peak " << fPeaks[i] << ", fit function " << fFits[i]->GetFitFunction() << std::endl; }
1252 fPeaks[i]->SetLineColor(kRed + 2);
1253 fFits[i]->GetFitFunction()->SetLineColor(kRed);
1254 } else {
1255 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << i << ": setting line colors to (dark) blue for peak " << fPeaks[i] << ", fit function " << fFits[i]->GetFitFunction() << std::endl; }
1256 fPeaks[i]->SetLineColor(kBlue + 2);
1257 fFits[i]->GetFitFunction()->SetLineColor(kBlue);
1258 }
1259 }
1260
1261 // set colors of bad fits alterating to grey and dark grey
1262 for(size_t i = 0; i < fBadFits.size(); ++i) {
1263 if(i % 2 == 0) {
1264 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << i << ": setting line colors to grey for fit function " << fBadFits[i]->GetFitFunction() << std::endl; }
1265 fBadFits[i]->GetFitFunction()->SetLineColor(kGray);
1266 } else {
1267 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << i << ": setting line colors to dark grey for fit function " << fBadFits[i]->GetFitFunction() << std::endl; }
1268 fBadFits[i]->GetFitFunction()->SetLineColor(kGray + 2);
1269 }
1270 }
1271}
1272
1273void TSourceTab::Status(const char* status, int position)
1274{
1275 fSourceStatusBar->SetText(status, position);
1276 fSourceStatusBar->MapWindow();
1277 fSourceFrame->MapSubwindows();
1278 gVirtualX->Update();
1279}
1280
1282{
1283 std::cout << "Projection " << fProjection;
1284 if(fProjection != nullptr) {
1285 std::cout << " = " << fProjection->GetName() << "/" << fProjection->GetTitle();
1286 } else {
1287 std::cout << " is null";
1288 }
1289 std::cout << std::endl;
1290
1291 std::cout << "Data " << fData;
1292 if(fData != nullptr) {
1293 std::cout << " = " << fData->GetName() << "/" << fData->GetTitle();
1294 } else {
1295 std::cout << " is null";
1296 }
1297 std::cout << std::endl;
1298
1299 std::cout << "Fwhm " << fFwhm;
1300 if(fFwhm != nullptr) {
1301 std::cout << " = " << fFwhm->GetName() << "/" << fFwhm->GetTitle();
1302 } else {
1303 std::cout << " is null";
1304 }
1305 std::cout << std::endl;
1306
1307 std::cout << fFits.size() << " good fits:";
1308 for(size_t i = 0; i < fFits.size(); ++i) {
1309 std::cout << " " << std::setw(2) << i << " - " << std::setw(8) << fFits.at(i)->GetFitFunction()->GetParameter("centroid_0");
1310 }
1311 std::cout << std::endl;
1312
1313 std::cout << fBadFits.size() << " bad fits:";
1314 for(size_t i = 0; i < fBadFits.size(); ++i) {
1315 std::cout << " " << std::setw(2) << i << " - " << std::setw(8) << fBadFits.at(i)->GetFitFunction()->GetParameter("centroid_0");
1316 }
1317 std::cout << std::endl;
1318
1319 std::cout << fPeaks.size() << " peaks:";
1320 for(size_t i = 0; i < fPeaks.size(); ++i) {
1321 std::cout << " " << std::setw(2) << i << " - " << std::setw(8) << fPeaks.at(i)->Centroid();
1322 }
1323 std::cout << std::endl;
1324}
1325
1327{
1328 std::cout << "TSourceTab " << fSourceName << " projection canvas:" << std::endl;
1329 fProjectionCanvas->GetCanvas()->Print();
1330 for(auto* obj : *(fProjectionCanvas->GetCanvas()->GetListOfPrimitives())) {
1331 obj->Print();
1332 }
1333}
1334
1336{
1337 std::cout << "TSourceTab frame:" << std::endl;
1338 fSourceFrame->Print();
1339 std::cout << "TSourceTab canvas:" << std::endl;
1340 fProjectionCanvas->Print();
1341 std::cout << "TSourceTab status bar:" << std::endl;
1342 fSourceStatusBar->Print();
1343}
1344
1345//////////////////////////////////////// TChannelTab ////////////////////////////////////////
1346TChannelTab::TChannelTab(TSourceCalibration* sourceCal, std::vector<TNucleus*> nuclei, std::vector<GH1D*> projections, std::string name, TGCompositeFrame* frame, std::vector<std::vector<std::tuple<double, double, double, double>>> sourceEnergies, TGHProgressBar* progressBar)
1347 : fChannelFrame(frame), fSourceTab(new TGTab(frame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight() + 2 * TSourceCalibration::StatusbarHeight())), fCanvasTab(new TGTab(frame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight() + 2 * TSourceCalibration::StatusbarHeight())), fProgressBar(progressBar), fSourceCalibration(sourceCal), fNuclei(std::move(nuclei)), fProjections(std::move(projections)), fName(std::move(name)), fSourceEnergies(std::move(sourceEnergies))
1348{
1350 std::cout << DYELLOW << "========================================" << std::endl;
1351 std::cout << __PRETTY_FUNCTION__ << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1352 << " name = " << fName << ", fData = " << fData << std::endl;
1353 std::cout << "========================================" << std::endl;
1354 }
1355
1357
1359 std::cout << DYELLOW << "----------------------------------------" << std::endl;
1360 std::cout << __PRETTY_FUNCTION__ << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1361 << " channel " << fName << " done" << std::endl;
1362 std::cout << "----------------------------------------" << std::endl;
1363 }
1364}
1365
1367{
1368 delete fChannelFrame;
1369 delete fSourceTab;
1370 for(auto* channel : fSources) {
1371 delete channel;
1372 }
1373 delete fCanvasTab;
1374 delete fCalibrationFrame;
1375 delete fCalibrationCanvas;
1376 delete fResidualPad;
1377 delete fCalibrationPad;
1378 delete fChannelStatusBar;
1379 delete fLegend;
1380 delete fChi2Label;
1381 delete fFwhmFrame;
1382 delete fFwhmCanvas;
1383 // delete all fProjections and all fNuclei?
1384 delete fData;
1385 delete fFwhm;
1386}
1387
1389{
1390 {
1391 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1392 fChannelFrame->SetLayoutManager(new TGHorizontalLayout(fChannelFrame));
1393 }
1394
1395 fSources.resize(fNuclei.size(), nullptr);
1396 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << ": creating channels for bin 1 to " << fMatrix->GetNbinsX() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1397 for(size_t source = 0; source < fNuclei.size(); ++source) {
1398 CreateSourceTab(source);
1399 }
1400
1401 for(auto iter = fSources.begin(); iter != fSources.end(); ++iter) {
1402 if(*iter == nullptr) {
1403 fSources.erase(iter--); // erase iterator and then go to the element before this one (and then the loop goes to the next one)
1404 }
1405 }
1406
1407 {
1408 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1409 fChannelFrame->AddFrame(fSourceTab, new TGLayoutHints(kLHintsLeft | kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1410
1411 fCalibrationFrame = fCanvasTab->AddTab("Calibration");
1412 fCalibrationFrame->SetLayoutManager(new TGVerticalLayout(fCalibrationFrame));
1413 fCalibrationCanvas = new TRootEmbeddedCanvas(Form("CalibrationCanvas%s", fName.c_str()), fCalibrationFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
1414 fCalibrationFrame->AddFrame(fCalibrationCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1416 std::array<int, 3> parts = {25, 35, 40};
1417 fChannelStatusBar->SetParts(parts.data(), parts.size());
1418 fCalibrationFrame->AddFrame(fChannelStatusBar, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
1419 fFwhmFrame = fCanvasTab->AddTab("FWHM");
1420 fFwhmFrame->SetLayoutManager(new TGVerticalLayout(fFwhmFrame));
1421 fFwhmCanvas = new TRootEmbeddedCanvas(Form("FwhmCanvas%s", fName.c_str()), fFwhmFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
1422 fFwhmFrame->AddFrame(fFwhmCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1423 //fChannelFrame->AddFrame(fCalibrationFrame, new TGLayoutHints(kLHintsRight | kLHintsBottom | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1424
1425 fInitFrame = fCanvasTab->AddTab("Initial");
1426 fInitFrame->SetLayoutManager(new TGVerticalLayout(fInitFrame));
1427 fInitCanvas = new TRootEmbeddedCanvas(Form("InitCanvas%s", fName.c_str()), fInitFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
1428 fInitFrame->AddFrame(fInitCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1429
1430 fChannelFrame->AddFrame(fCanvasTab, new TGLayoutHints(kLHintsRight | kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
1431 }
1432
1433 UpdateData();
1434
1435 {
1436 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1437 fCalibrationCanvas->GetCanvas()->cd();
1438 fCalibrationPad = new TPad(Form("cal_%s", fName.c_str()), Form("calibration for %s", fName.c_str()), 0.2, 0., 1., 1.);
1439 fCalibrationPad->SetNumber(1);
1440 fCalibrationPad->Draw();
1441 fCalibrationPad->AddExec("zoom", "TChannelTab::ZoomY()");
1442
1443 fLegend = new TLegend(0.8, 0.3, 0.95, 0.3 + static_cast<double>(fNuclei.size()) * 0.05); // x1, y1, x2, y2
1444
1445 fCalibrationCanvas->GetCanvas()->cd();
1446 fResidualPad = new TPad(Form("res_%s", fName.c_str()), Form("residual for %s", fName.c_str()), 0.0, 0., 0.2, 1.);
1447 fResidualPad->SetNumber(2);
1448 fResidualPad->Draw();
1449 fResidualPad->AddExec("zoom", "TChannelTab::ZoomY()");
1450 }
1451 //Calibrate(); // also creates the residual and chi^2 label
1452
1453 // check whether we want to use this initial calibration to find more peaks
1454 //if(TSourceCalibration::UseCalibratedPeaks()) {
1455 // FindCalibratedPeaks();
1456 //}
1457
1458 // get the fwhm graphs and plot them
1459 UpdateFwhm();
1460 //{
1461 // std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1462 // fFwhmCanvas->GetCanvas()->cd();
1463 // fFwhm->DrawCalibration("*");
1464 // fLegend->Draw();
1465 //}
1466}
1467
1469{
1470 if(fProjections[source]->GetEntries() > 1000) {
1472 std::cout << DYELLOW << "Creating source tab " << source << ", using fSourceCalibration " << fSourceCalibration << std::endl;
1473 }
1474 TGCompositeFrame* tmpTab = nullptr;
1475 {
1476 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Creating unique lock on graphics mutex to add tab" << std::endl; }
1477 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1478 tmpTab = fSourceTab->AddTab(Form("%s_%s", fNuclei[source]->GetName(), fName.c_str()));
1479 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Releasing unique lock on graphics mutex after adding tab" << std::endl; }
1480 }
1481 fSources[source] = new TSourceTab(fSourceCalibration, this, tmpTab, fProjections[source], fNuclei[source]->GetName(), fSourceEnergies[source]);
1482 {
1483 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Creating unique lock on graphics mutex to increment status bar" << std::endl; }
1484 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1485 fProgressBar->Increment(1);
1486 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Releasing unique lock on graphics mutex after incrementing status bar" << std::endl; }
1487 }
1488 } else {
1489 fSources[source] = nullptr;
1491 std::cout << DYELLOW << "Skipping projection of source " << source << " = " << fProjections[source]->GetName() << ", only " << fProjections[source]->GetEntries() << " entries" << std::endl;
1492 }
1493 }
1495 std::cout << __PRETTY_FUNCTION__ << " source " << source << " done" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1496 }
1497}
1498
1500{
1501 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1502 fSourceTab->Connect("Selected(Int_t)", "TChannelTab", this, "SelectedTab(Int_t)");
1503 for(auto* source : fSources) {
1504 source->MakeConnections();
1505 }
1506 fCalibrationCanvas->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", "TChannelTab", this, "CalibrationStatus(Int_t,Int_t,Int_t,TObject*)");
1507 fCalibrationCanvas->Connect("ProcessedEvent(Event_t*)", "TChannelTab", this, "SelectCanvas(Event_t*)");
1508 if(fData != nullptr) {
1510 std::cout << DYELLOW << __PRETTY_FUNCTION__ << ": connecting fData " << fData << ":" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1511 fData->Print();
1512 }
1513 fData->Connect("RemovePoint(Int_t, Int_t)", "TChannelTab", this, "RemovePoint(Int_t, Int_t)");
1514 } else {
1515 std::cerr << DRED << "Failed to create connections for the data graph fData since it hasn't been created yet!" << std::endl;
1516 }
1517}
1518
1520{
1521 //std::unique_lock<std::mutex> graphicsLock(fSourceCalibration->GraphicsMutex());
1522 fSourceTab->Disconnect("Selected(Int_t)", this, "SelectedTab(Int_t)");
1523 for(auto* source : fSources) {
1524 source->Disconnect();
1525 }
1526 fCalibrationCanvas->GetCanvas()->Disconnect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", this, "CalibrationStatus(Int_t,Int_t,Int_t,TObject*)");
1527 fData->Disconnect("RemovePoint(Int_t, Int_t)", this, "RemovePoint(Int_t, Int_t)");
1528}
1529
1531{
1532 /// Simple function that enables and disables the previous and next buttons depending on which tab was selected.
1533 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << __PRETTY_FUNCTION__ << ": id " << id << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1534 fActiveSourceTab = id;
1535 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "active source tab " << fActiveSourceTab << RESET_COLOR << std::endl; }
1536 if(fSources[fActiveSourceTab] == nullptr) {
1537 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]" << RESET_COLOR << std::endl;
1538 return;
1539 }
1540 if(fSources[fActiveSourceTab]->ProjectionCanvas() == nullptr) {
1541 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]->ProjectionCanvas()" << RESET_COLOR << std::endl;
1542 return;
1543 }
1544 if(fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas() == nullptr) {
1545 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]->ProjectionCanvas()->GetCanvas()" << RESET_COLOR << std::endl;
1546 return;
1547 }
1548 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "Changing gpad from \"" << gPad->GetName() << "\" to \""; }
1549 gPad = fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas();
1550 fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas()->cd();
1551 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << gPad->GetName() << "\"" << RESET_COLOR << std::endl; }
1552}
1553
1554void TChannelTab::SelectCanvas(Event_t* event)
1555{
1556 if(event->fType == kEnterNotify) {
1558 std::cout << "Entered channel tab => changing gPad from " << gPad->GetName();
1559 }
1560 gPad = fCalibrationCanvas->GetCanvas();
1562 std::cout << " to " << gPad->GetName() << std::endl;
1563 }
1564 }
1565}
1566
1567void TChannelTab::RemovePoint(Int_t oldGraph, Int_t oldPoint)
1568{
1570 std::cout << DCYAN << __PRETTY_FUNCTION__ << ": oldGraph " << oldGraph << ", oldPoint " << oldPoint << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1571 }
1572 if(0 <= oldGraph && oldGraph < static_cast<int>(fSources.size())) {
1573 if(oldPoint >= 0) {
1574 return fSources[oldGraph]->RemovePoint(oldPoint);
1575 }
1576 std::cout << "Can't remove negative point " << oldPoint << " from graph " << oldGraph << std::endl;
1577 }
1578 std::cout << "Graph the point was removed from was " << oldGraph << ", but we only have " << fSources.size() << " source tabs?" << std::endl;
1579}
1580
1581void TChannelTab::CalibrationStatus(Int_t event, Int_t px, Int_t py, TObject* selected)
1582{
1583 // looks like 51 is hovering over the object, 52 is moving the cursor over the object, and 53 is moving the cursort away from the object
1584 // kButton1 = left mouse button, kButton2 = right mouse button
1585 // enum EEventType {
1586 // kNoEvent = 0,
1587 // kButton1Down = 1, kButton2Down = 2, kButton3Down = 3, kKeyDown = 4,
1588 // kWheelUp = 5, kWheelDown = 6, kButton1Shift = 7, kButton1ShiftMotion = 8,
1589 // kButton1Up = 11, kButton2Up = 12, kButton3Up = 13, kKeyUp = 14,
1590 // kButton1Motion = 21, kButton2Motion = 22, kButton3Motion = 23, kKeyPress = 24,
1591 // kArrowKeyPress = 25, kArrowKeyRelease = 26,
1592 // kButton1Locate = 41, kButton2Locate = 42, kButton3Locate = 43, kESC = 27,
1593 // kMouseMotion = 51, kMouseEnter = 52, kMouseLeave = 53,
1594 // kButton1Double = 61, kButton2Double = 62, kButton3Double = 63
1595 //};
1596 fChannelStatusBar->SetText(selected->GetName(), 0);
1597 if(event == kKeyPress) {
1598 fChannelStatusBar->SetText(Form("%c", static_cast<char>(px)), 1);
1599 } else {
1600 auto* canvas = fCalibrationCanvas->GetCanvas();
1601 if(canvas == nullptr) {
1602 fChannelStatusBar->SetText("canvas nullptr");
1603 return;
1604 }
1605 auto* pad = canvas->GetSelectedPad();
1606 if(pad == nullptr) {
1607 fChannelStatusBar->SetText("pad nullptr");
1608 return;
1609 }
1610 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) {
1611 // std::cout << "px " << px << ", py " << py << "; gPad: px " << gPad->AbsPixeltoX(px) << ", py " << gPad->AbsPixeltoY(py) << "; fCalibrationCanvas: px " << fCalibrationCanvas->GetCanvas()->AbsPixeltoX(px) << ", py " << fCalibrationCanvas->GetCanvas()->AbsPixeltoY(py) << "; fResidualPad: px " << fResidualPad->AbsPixeltoX(px) << ", py " << fResidualPad->AbsPixeltoY(py) << "; fCalibrationPad: px " << fCalibrationPad->AbsPixeltoX(px) << ", py " << fCalibrationPad->AbsPixeltoY(py) << "; pad: px " << pad->AbsPixeltoX(px) << ", py " << pad->AbsPixeltoY(py) << std::endl;
1612 //}
1613
1614 fChannelStatusBar->SetText(Form("x = %f, y = %f", pad->AbsPixeltoX(px), pad->AbsPixeltoY(py)), 1);
1615 }
1616}
1617
1619{
1620 /// Copy data from all sources into one graph (which we use for the calib && source < fSources.size()ration).
1621 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << __PRETTY_FUNCTION__ << " fData = " << fData << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1622 if(fData == nullptr) {
1623 fData = new TCalibrationGraphSet("ADC channel", "Energy [keV]");
1624 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << " created fData = " << fData << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1625 }
1626 fData->Clear();
1627
1628 fData->SetName(Form("data%s", fName.c_str()));
1630 std::cout << "set name of fData using " << fName.c_str() << ": " << fData->GetName() << std::endl;
1631 std::cout << "fData " << fData << ": " << (fData != nullptr ? fData->GetN() : -2) << " data points after creation" << std::endl;
1632 std::cout << "Looping over " << fNuclei.size() << " sources, fSources.size() = " << fSources.size() << std::endl;
1633 }
1634 for(size_t source = 0; source < fNuclei.size() && source < fSources.size(); ++source) {
1635 if(fSources[source]->Data() != nullptr && fSources[source]->Data()->GetN() > 0) {
1636 int index = fData->Add(fSources[source]->Data(), fNuclei[source]->GetName());
1637 if(index >= 0) {
1638 fData->SetLineColor(index, static_cast<Color_t>(source + 1)); //+1 for the color so that we start with 1 = black instead of 0 = white
1639 fData->SetMarkerColor(index, static_cast<Color_t>(source + 1));
1640 }
1641 }
1642 }
1644 std::cout << "fData " << fData << ": " << (fData != nullptr ? fData->GetN() : -1) << " data points after adding " << fNuclei.size() << " graphs" << std::endl;
1645 fData->Print();
1646 }
1647}
1648
1650{
1651 /// Copy data from all sources into one graph (which we use for the calib && source < fSources.size()ration).
1652 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << __PRETTY_FUNCTION__ << " fFwhm = " << fFwhm << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1653 if(fFwhm == nullptr) {
1654 fFwhm = new TCalibrationGraphSet("ADC channel", "FWHM [ADC channel]");
1655 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << " created fFwhm = " << fFwhm << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1656 }
1657 fFwhm->Clear();
1658
1659 fFwhm->SetName(Form("fwhm%s", fName.c_str()));
1661 std::cout << "set name of fFwhm using " << fName.c_str() << ": " << fFwhm->GetName() << std::endl;
1662 std::cout << "fFwhm " << fFwhm << ": " << (fFwhm != nullptr ? fFwhm->GetN() : -2) << " data points after creation" << std::endl;
1663 std::cout << "Looping over " << fNuclei.size() << " sources, fSources.size() = " << fSources.size() << std::endl;
1664 }
1665 for(size_t source = 0; source < fNuclei.size() && source < fSources.size(); ++source) {
1666 if(fSources[source]->Fwhm() != nullptr && fSources[source]->Fwhm()->GetN() > 0) {
1667 int index = fFwhm->Add(fSources[source]->Fwhm(), fNuclei[source]->GetName());
1668 if(index >= 0) {
1669 fFwhm->SetLineColor(index, static_cast<Color_t>(source + 1)); //+1 for the color so that we start with 1 = black instead of 0 = white
1670 fFwhm->SetMarkerColor(index, static_cast<Color_t>(source + 1));
1671 }
1672 }
1673 }
1675 std::cout << "fFwhm " << fFwhm << ": " << (fFwhm != nullptr ? fFwhm->GetN() : -1) << " data points after adding " << fNuclei.size() << " graphs" << std::endl;
1676 fFwhm->Print();
1677 }
1678}
1679
1681{
1682 /// Uses the statis TSourceCalibration::Degree for the degree of the polynomial used to calibrate the data.
1684}
1685
1687{
1688 /// This function fit's the final data of the given channel.
1689 /// It requires all other elements to have been created already.
1690 if(TSourceCalibration::VerboseLevel() >= EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1691 TF1* calibration = new TF1("fitfunction", ::Polynomial, 0., 10000., degree + 2);
1692 calibration->FixParameter(0, degree);
1694 std::cout << DYELLOW << "fData " << fData << ": ";
1695 if(fData != nullptr) {
1696 std::cout << fData->GetN() << " data points being fit, ";
1697 }
1698 double min = 0.;
1699 double max = 0.;
1700 calibration->GetRange(min, max);
1701 std::cout << "range " << min << " - " << max << std::endl;
1702 }
1703 fData->Fit(calibration, "Q");
1704 TString text = Form("%.6f + %.6f*x", calibration->GetParameter(1), calibration->GetParameter(2));
1705 for(int i = 2; i <= degree; ++i) {
1706 text.Append(Form(" + %.6f*x^%d", calibration->GetParameter(i + 1), i));
1707 }
1708 fChannelStatusBar->SetText(text.Data(), 2);
1709 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "re-calculating residuals and clearing fields" << std::endl; }
1710 // re-calculate the residuals
1711 fData->SetResidual(true);
1712
1713 fLegend->Clear();
1714 fCalibrationPad->cd();
1716 fLegend->Draw();
1717 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "set chi2 label" << std::endl; }
1718 // calculate the corners of the chi^2 label from the minimum and maximum x/y-values of the graph
1719 // we position it in the top left corner about 50% of the width and 10% of the height of the graph
1720 if(fChi2Label == nullptr) {
1721 double left = fData->GetMinimumX();
1722 double right = left + (fData->GetMaximumX() - left) * 0.5;
1723 double top = fData->GetMaximumY();
1724 double bottom = top - (top - fData->GetMinimumY()) * 0.1;
1725
1726 fChi2Label = new TPaveText(left, bottom, right, top);
1728 fChi2Label->ConvertNDCtoPad();
1729 std::cout << "fChi2Label created " << fChi2Label << " (" << left << " - " << right << ", " << bottom << " - " << top << ", from " << fData->GetMinimumX() << "-" << fData->GetMaximumX() << ", " << fData->GetMinimumY() << "-" << fData->GetMaximumY() << ") on gPad " << gPad->GetName() << " coordinates: " << fChi2Label->GetX1() << "/" << fChi2Label->GetX1NDC() << " - " << fChi2Label->GetX2() << "/" << fChi2Label->GetX2NDC() << ", " << fChi2Label->GetY1() << "/" << fChi2Label->GetY1NDC() << " - " << fChi2Label->GetY2() << "/" << fChi2Label->GetY2NDC() << std::endl;
1730 fData->Print("e");
1731 }
1732 } else {
1733 fChi2Label->Clear();
1734 }
1735 fChi2Label->AddText(Form("#chi^{2}/NDF = %f", calibration->GetChisquare() / calibration->GetNDF()));
1736 fChi2Label->SetFillColor(kWhite);
1737 fChi2Label->Draw();
1739 std::cout << "fChi2Label set to:" << std::endl;
1740 fChi2Label->GetListOfLines()->Print();
1741 std::cout << "Text size " << fChi2Label->GetTextSize() << std::endl;
1742 }
1743 if(fInfoLabel == nullptr) {
1744 fInfoLabel = new TPaveText(0, 0.95, 1., 1., "NDCNB");
1746 fInfoLabel->ConvertNDCtoPad();
1747 std::cout << "fInfoLabel created " << fInfoLabel << " (" << fInfoLabel->GetX1() << "/" << fInfoLabel->GetX1NDC() << " - " << fInfoLabel->GetX2() << "/" << fInfoLabel->GetX2NDC() << ", " << fInfoLabel->GetY1() << "/" << fInfoLabel->GetY1NDC() << " - " << fInfoLabel->GetY2() << "/" << fInfoLabel->GetY2NDC() << ", from 0, 0.95, 1., 1.) on gPad " << gPad->GetName() << std::endl;
1748 }
1749 } else {
1750 fInfoLabel->Clear();
1752 fInfoLabel->ConvertNDCtoPad();
1753 std::cout << "fInfoLabel using existing " << fInfoLabel << " (" << fInfoLabel->GetX1() << "/" << fInfoLabel->GetX1NDC() << " - " << fInfoLabel->GetX2() << "/" << fInfoLabel->GetX2NDC() << ", " << fInfoLabel->GetY1() << "/" << fInfoLabel->GetY1NDC() << " - " << fInfoLabel->GetY2() << "/" << fInfoLabel->GetY2NDC() << ") on gPad " << gPad->GetName() << std::endl;
1754 }
1755 }
1756 fInfoLabel->AddText(Form("Using %d peaks from %.0f to %.0f keV", fData->TotalGraph()->GetN(), fData->GetMinimumY(), fData->GetMaximumY()));
1757 fInfoLabel->SetFillColor(kWhite);
1758 fInfoLabel->Draw();
1759
1760 fResidualPad->cd();
1761 fData->DrawResidual("*");
1762
1763 fCalibrationCanvas->GetCanvas()->Modified();
1764
1765 fData->FitFunction()->SetRange(0., 10000.);
1767 double min = 0.;
1768 double max = 0.;
1769 calibration->GetRange(min, max);
1770 std::cout << DYELLOW << "calibration has range " << min << " - " << max;
1771 if(fData->FitFunction() != nullptr) {
1772 fData->FitFunction()->GetRange(min, max);
1773 std::cout << ", fit function has range " << min << " - " << max;
1774 }
1775 std::cout << std::endl;
1776 }
1777 delete calibration;
1778
1779 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << " done" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1780}
1781
1782void TChannelTab::RecursiveRemove(const double& maxResidual)
1783{
1784 /// This function goes through the residuals of the calibration and recursively removes each point that is above the maxResidual parameter.
1785 /// The function requires an initial calibration to have residuals available.
1786 /// After each point that is removed, it re-calibrates the data, producing new residuals.
1787
1788 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << __PRETTY_FUNCTION__ << ", max. residual " << maxResidual << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1789
1790 if(fData == nullptr) {
1791 std::cout << "No longer have a data for channel " << Name() << "?" << std::endl;
1792 return;
1793 }
1794
1795 if(fData->TotalResidualGraph() == nullptr) {
1796 std::cout << "No longer have a total residual graph for channel " << Name() << ". Maybe removed all points? Total graph has " << fData->TotalGraph()->GetN() << " points." << std::endl;
1797 return;
1798 }
1799
1800 if(fData->TotalResidualGraph()->GetN() == 0) {
1801 std::cout << "Recursively removed all data points for channel " << Name() << std::endl;
1802 return;
1803 }
1804
1805 // get the maximum residual
1806 auto* maxElement = std::max_element(fData->TotalResidualGraph()->GetX(), fData->TotalResidualGraph()->GetX() + fData->TotalResidualGraph()->GetN(), [](double a, double b) { return std::abs(a) < std::abs(b); });
1807 auto index = std::distance(fData->TotalResidualGraph()->GetX(), maxElement);
1808
1809 if(std::abs(*maxElement) < maxResidual) {
1810 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "largest residual is " << *maxElement << " at position " << index << " and is smaller than max. residual " << maxResidual << ": stopping recursive remove" << std::endl; }
1811 return;
1812 }
1813
1814 // remove the index with the maximum residual
1815 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "std::abs(" << *maxElement << ") > " << maxResidual << ": removing index " << index << "/" << fData->TotalResidualGraph()->GetN() << std::endl; }
1816 fData->RemovePoint(index);
1817
1818 // update the data, and re-calibrate
1819 UpdateData();
1820 UpdateFwhm();
1821 Calibrate();
1822
1823 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "RecursiveRemove(" << maxResidual << ") being called again" << RESET_COLOR << std::endl; }
1824 RecursiveRemove(maxResidual);
1825}
1826
1828{
1829 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1830 // write the actual parameters of the fit
1831 std::stringstream str;
1832 str << std::hex << fName;
1833 int address = 0;
1834 str >> address;
1835 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Got address " << hex(address, 4) << " from name " << fName << std::endl; }
1836 TF1* calibration = fData->FitFunction();
1837 if(calibration == nullptr) {
1838 std::cout << "Failed to find calibration in fData" << std::endl;
1839 fData->TotalGraph()->GetListOfFunctions()->Print();
1840 return;
1841 }
1842 std::vector<Float_t> parameters;
1843 for(int i = 0; i <= calibration->GetParameter(0); ++i) {
1844 parameters.push_back(static_cast<Float_t>(calibration->GetParameter(i + 1)));
1845 }
1846 TChannel* channel = TChannel::GetChannel(address, false);
1847 if(channel == nullptr) {
1848 std::cerr << "Failed to get channel for address " << hex(address, 4) << std::endl;
1849 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << ": done" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1850 return;
1851 }
1852 channel->SetENGCoefficients(parameters);
1853 channel->DestroyEnergyNonlinearity();
1855 double* x = fData->GetX();
1856 double* y = fData->GetY();
1857 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Going to add " << fData->GetN() << " points to nonlinearity graph" << std::endl; }
1858 for(int i = 0; i < fData->GetN(); ++i) {
1859 // nonlinearity expects y to be the true source energy minus the calibrated energy of the peak
1860 // the source energy is y, the peak is x
1861 channel->AddEnergyNonlinearityPoint(y[i], y[i] - calibration->Eval(x[i]));
1862 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { std::cout << "Added " << channel->GetEnergyNonlinearity().GetN() << ". point " << y[i] << ", " << y[i] - calibration->Eval(x[i]) << " = " << y[i] << " - " << calibration->Eval(x[i]) << std::endl; }
1863 }
1864 }
1865}
1866
1867void TChannelTab::Initialize(const bool& force)
1868{
1869 // loop through sources and get rough calibration for each
1870 for(auto* source : fSources) {
1872 std::cout << DYELLOW << "Rough calibration in source tab:" << std::endl;
1873 source->Print();
1874 }
1875 source->InitialCalibration(force);
1876 fProgressBar->Increment(1);
1877 }
1878 // get this rough calibration data and calibrate with linear calibration
1879 UpdateData();
1880 Calibrate(1);
1881 // copy the data to the initial data and draw it
1882 fInit = static_cast<TCalibrationGraphSet*>(fData->Clone(Form("init%s", fName.c_str())));
1884 std::cout << "Cloned data " << fData->GetName() << " to " << fInit->GetName() << ":" << std::endl;
1885 fInit->Print();
1886 }
1887 auto* oldPad = gPad;
1888 fInitCanvas->GetCanvas()->cd();
1889 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Switched from " << oldPad->GetName() << " to " << gPad->GetName() << std::endl; }
1891 // calculate the corners of the label from the minimum and maximum x/y-values of the graph
1892 // we position it in the top left corner about 50% of the width and 10% of the height of the graph
1893 if(fCalLabel == nullptr) {
1894 double left = fInit->GetMinimumX();
1895 double right = left + (fInit->GetMaximumX() - left) * 0.5;
1896 double top = fInit->GetMaximumY();
1897 double bottom = top - (top - fInit->GetMinimumY()) * 0.1;
1898
1899 fCalLabel = new TPaveText(left, bottom, right, top);
1901 std::cout << "fCalLabel created " << fCalLabel << " (" << left << " - " << right << ", " << bottom << " - " << top << ", from " << fInit->GetMinimumX() << "-" << fInit->GetMaximumX() << ", " << fInit->GetMinimumY() << "-" << fInit->GetMaximumY() << ") on gPad " << gPad->GetName() << std::endl;
1902 }
1903 } else {
1904 fCalLabel->Clear();
1905 }
1906 std::stringstream str;
1907 if(fInit->FitFunction()->GetNpar() == 3) {
1908 str << "Fit function: " << fInit->FitFunction()->GetParameter(1) << " + " << fInit->FitFunction()->GetParameter(2) << " x";
1909 } else {
1910 str << "Unknown fit function with " << fInit->FitFunction()->GetNpar() << " parameters";
1911 }
1913 std::cout << "Created label text \"" << str.str() << "\"" << std::endl;
1914 }
1915
1916 fCalLabel->AddText(str.str().c_str());
1917 fCalLabel->SetFillColor(kWhite);
1918 fCalLabel->Draw();
1919 oldPad->cd();
1920 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Switched back to " << gPad->GetName() << std::endl; }
1921}
1922
1924{
1926 std::cout << DYELLOW << "Finding calibrated peaks in source tab " << fActiveSourceTab << ". Old: " << fSourceTab->GetCurrent() << " = " << fSources[fSourceTab->GetCurrent()] << ", current tab element " << fSourceTab->GetCurrentTab() << " is enabled = " << fSourceTab->GetCurrentTab()->IsEnabled() << std::endl;
1927 fSourceTab->Print();
1928 for(int tab = 0; tab < fSourceTab->GetNumberOfTabs(); ++tab) {
1929 std::cout << "Tab " << tab << " = " << fSourceTab->GetTabTab(tab) << " = " << fSourceTab->GetTabTab(tab)->GetText()->GetString() << (fSourceTab->GetTabTab(tab)->IsActive() ? " is active" : " is inactive") << (fSourceTab->GetTabTab(tab)->IsEnabled() ? " is enabled" : " is not enabled") << std::endl;
1930 }
1931 }
1932 fSources[fActiveSourceTab]->FindCalibratedPeaks(fData->FitFunction());
1933 UpdateData();
1934 UpdateFwhm();
1936 std::cout << DYELLOW << "Done finding calibrated peaks in source tab " << fActiveSourceTab << ". Old: " << fSourceTab->GetCurrent() << " = " << fSources[fSourceTab->GetCurrent()] << ", current tab element " << fSourceTab->GetCurrentTab() << " is enabled = " << fSourceTab->GetCurrentTab()->IsEnabled() << std::endl;
1937 }
1938}
1939
1941{
1943 std::cout << DYELLOW << "Finding calibrated peaks in all source tabs" << std::endl;
1944 }
1945 for(auto* source : fSources) {
1947 std::cout << DYELLOW << "Finding calibrated peaks in source tab " << source->SourceName() << std::endl;
1948 }
1949 source->FindCalibratedPeaks(fData->FitFunction());
1950 }
1951 UpdateData();
1952 UpdateFwhm();
1954 std::cout << DYELLOW << "Done finding calibrated peaks in all source tabs" << std::endl;
1955 }
1956}
1957
1959{
1960 fFwhmCanvas->GetCanvas()->cd();
1961 fFwhm->DrawCalibration("*");
1962 fLegend->Draw();
1963 for(auto* source : fSources) {
1964 source->Draw();
1965 }
1966}
1967
1968void TChannelTab::Write(TFile* output)
1969{
1970 /// Write graphs to output.
1971 output->cd();
1972 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "writing " << fName << std::endl; }
1973 fData->Write(Form("calGraph_%s", fName.c_str()), TObject::kOverwrite);
1974 fFwhm->Write(Form("fwhm_%s", fName.c_str()), TObject::kOverwrite);
1975 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "wrote data " << fName << std::endl; }
1976}
1977
1979{
1980 /// finds corresponding graph (res_xxx for cal_xxx and vice versa) and sets it's x-axis range to be the same as the selected graphs
1981 // find the histogram in the current pad
1982 TH1* hist1 = nullptr;
1983 for(const auto&& obj : *(gPad->GetListOfPrimitives())) {
1984 if(obj->InheritsFrom(TGraph::Class())) {
1985 hist1 = static_cast<TGraph*>(obj)->GetHistogram();
1986 break;
1987 }
1988 }
1989 if(hist1 == nullptr) {
1990 std::cout << "ZoomX - Failed to find histogram for pad " << gPad->GetName() << std::endl;
1991 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { gPad->GetListOfPrimitives()->Print(); }
1992 return;
1993 }
1994
1995 // extract base name and channel from pad name
1996 std::string padName = gPad->GetName();
1997 std::string baseName = padName.substr(0, 3);
1998 std::string channelName = padName.substr(4);
1999
2000 // find the corresponding partner pad to the selected one
2001 std::string newName;
2002 if(baseName == "cal") {
2003 newName = "res_" + channelName;
2004 } else if(baseName == "res") {
2005 newName = "cal_" + channelName;
2006 } else {
2007 std::cout << "Unknown combination of pad " << gPad->GetName() << " and histogram " << hist1->GetName() << std::endl;
2008 return;
2009 }
2010 auto* newObj = gPad->GetCanvas()->FindObject(newName.c_str());
2011 if(newObj == nullptr) {
2012 std::cout << "Failed to find " << newName << std::endl;
2013 return;
2014 }
2015
2016 if(newObj->IsA() != TPad::Class()) {
2017 std::cout << newObj << " = " << newObj->GetName() << ", " << newObj->GetTitle() << " is a " << newObj->ClassName() << " not a TPad" << std::endl;
2018 return;
2019 }
2020 auto* pad2 = static_cast<TPad*>(newObj);
2021 if(pad2 == nullptr) {
2022 std::cout << "Failed to find partner pad " << newName << std::endl;
2023 return;
2024 }
2025 // find the histogram in the partner pad
2026 TH1* hist2 = nullptr;
2027 for(const auto&& obj : *(pad2->GetListOfPrimitives())) {
2028 if(obj->InheritsFrom(TGraph::Class())) {
2029 hist2 = static_cast<TGraph*>(obj)->GetHistogram();
2030 break;
2031 }
2032 }
2033 if(hist2 == nullptr) {
2034 std::cout << "ZoomX - Failed to find histogram for partner pad " << newName << std::endl;
2035 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { pad2->GetListOfPrimitives()->Print(); }
2036 return;
2037 }
2038
2039 TAxis* axis1 = hist1->GetXaxis();
2040 Int_t binmin = axis1->GetFirst();
2041 Int_t binmax = axis1->GetLast();
2042 Double_t xmin = axis1->GetBinLowEdge(binmin);
2043 Double_t xmax = axis1->GetBinLowEdge(binmax);
2044 TAxis* axis2 = hist2->GetXaxis();
2045 Int_t newmin = axis2->FindBin(xmin);
2046 Int_t newmax = axis2->FindBin(xmax);
2047 axis2->SetRange(newmin, newmax);
2048 pad2->Modified();
2049 pad2->Update();
2050}
2051
2053{
2054 /// finds corresponding graph (res_xxx for cal_xxx and vice versa) and sets it's y-axis range to be the same as the selected graphs
2055 // find the histogram in the current pad
2056 TH1* hist1 = nullptr;
2057 for(const auto&& obj : *(gPad->GetListOfPrimitives())) {
2058 if(obj->InheritsFrom(TGraph::Class())) {
2059 hist1 = static_cast<TGraph*>(obj)->GetHistogram();
2060 break;
2061 }
2062 }
2063 if(hist1 == nullptr) {
2064 std::cout << "ZoomY - Failed to find histogram for pad " << gPad->GetName() << std::endl;
2065 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { gPad->GetListOfPrimitives()->Print(); }
2066 return;
2067 }
2068
2069 // extract base name and channel from pad name
2070 std::string padName = gPad->GetName();
2071 std::string baseName = padName.substr(0, 3);
2072 std::string channelName = padName.substr(4);
2073
2074 // find the corresponding partner pad to the selected one
2075 std::string newName;
2076 if(baseName == "cal") {
2077 newName = "res_" + channelName;
2078 } else if(baseName == "res") {
2079 newName = "cal_" + channelName;
2080 } else {
2081 std::cout << "Unknown combination of pad " << gPad->GetName() << " and histogram " << hist1->GetName() << std::endl;
2082 return;
2083 }
2084 auto* newObj = gPad->GetCanvas()->FindObject(newName.c_str());
2085 if(newObj == nullptr) {
2086 std::cout << "Failed to find " << newName << std::endl;
2087 return;
2088 }
2089
2090 if(newObj->IsA() != TPad::Class()) {
2091 std::cout << newObj << " = " << newObj->GetName() << ", " << newObj->GetTitle() << " is a " << newObj->ClassName() << " not a TPad" << std::endl;
2092 return;
2093 }
2094 auto* pad2 = static_cast<TPad*>(newObj);
2095 if(pad2 == nullptr) {
2096 std::cout << "Failed to find partner pad " << newName << std::endl;
2097 return;
2098 }
2099 // find the histogram in the partner pad
2100 TH1* hist2 = nullptr;
2101 for(const auto&& obj : *(pad2->GetListOfPrimitives())) {
2102 if(obj->InheritsFrom(TGraph::Class())) {
2103 hist2 = static_cast<TGraph*>(obj)->GetHistogram();
2104 break;
2105 }
2106 }
2107 if(hist2 == nullptr) {
2108 std::cout << "ZoomY - Failed to find histogram for partner pad " << newName << std::endl;
2109 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { pad2->GetListOfPrimitives()->Print(); }
2110 return;
2111 }
2112
2113 hist2->SetMinimum(hist1->GetMinimum());
2114 hist2->SetMaximum(hist1->GetMaximum());
2115
2116 pad2->Modified();
2117 pad2->Update();
2118}
2119
2121{
2122 std::cout << "TChannelTab " << Name() << " calibration canvas:" << std::endl;
2123 fCalibrationCanvas->GetCanvas()->Print();
2124 for(auto* obj : *(fCalibrationCanvas->GetCanvas()->GetListOfPrimitives())) {
2125 obj->Print();
2126 }
2127 std::cout << "TChannelTab fwhm canvas:" << std::endl;
2128 fFwhmCanvas->GetCanvas()->Print();
2129 for(auto* obj : *(fFwhmCanvas->GetCanvas()->GetListOfPrimitives())) {
2130 obj->Print();
2131 }
2132 for(auto* sourceTab : fSources) {
2133 sourceTab->PrintCanvases();
2134 }
2135}
2136
2138{
2139 std::cout << "TChannelTab frame:" << std::endl;
2140 fChannelFrame->Print();
2141 std::cout << "TChannelTab canvas:" << std::endl;
2142 fCalibrationCanvas->Print();
2143 std::cout << "TChannelTab status bar:" << std::endl;
2144 //fChannelStatusBar->Print();
2145 for(auto* sourceTab : fSources) {
2146 sourceTab->PrintLayout();
2147 }
2148}
2149
2150//////////////////////////////////////// TSourceCalibration ////////////////////////////////////////
2162bool TSourceCalibration::fFast = false;
2166std::vector<int> TSourceCalibration::fBadBins;
2167
2168TSourceCalibration::TSourceCalibration(double sigma, double threshold, int degree, int count...)
2169 : TGMainFrame(nullptr, 2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight), fDefaultSigma(sigma), fDefaultThreshold(threshold), fDefaultDegree(degree)
2170{
2171 TH1::AddDirectory(false); // turns off warnings about multiple histograms with the same name because ROOT doesn't manage them anymore
2172
2173 va_list args;
2174 va_start(args, count); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2175 for(int i = 0; i < count; ++i) {
2176 fMatrices.push_back(va_arg(args, TH2*)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2177 if(fMatrices.back() == nullptr) {
2178 std::cout << "Error, got nullptr as matrix input?" << std::endl;
2179 fMatrices.pop_back();
2180 }
2181 }
2182 va_end(args); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2184 std::cout << DGREEN << __PRETTY_FUNCTION__ << ": verbose level " << fVerboseLevel << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2185 << "using " << count << "/" << fMatrices.size() << " matrices:" << std::endl;
2186 for(auto* mat : fMatrices) {
2187 std::cout << mat << std::flush << " = " << mat->GetName() << std::endl;
2188 }
2189 }
2190
2191 fOldErrorLevel = gErrorIgnoreLevel;
2192 gErrorIgnoreLevel = kError;
2193 gPrintViaErrorHandler = true; // redirects all printf's to root's normal message system
2194
2195 if(!fLogFile.empty()) {
2196 fRedirect = new TRedirect(fLogFile.c_str());
2197 std::cout << "Starting redirect to " << fLogFile << std::endl;
2198 }
2199
2200 // check matrices (# of filled bins and bin labels) and resize some vectors for later use
2201 // use the first matrix to get a reference for everything
2202 fNofBins = 0;
2203 std::map<int, int> channelToIndex; // used to be a member, but only used here
2204 for(int bin = 1; bin <= fMatrices[0]->GetNbinsX(); ++bin) {
2205 if(FilledBin(fMatrices[0], bin) && std::find(fBadBins.begin(), fBadBins.end(), bin) == fBadBins.end()) {
2206 fActualChannelId.push_back(fNofBins); // at this point fNofBins is the index at which this projection will end up
2207 fActiveBins.push_back(bin);
2208 channelToIndex[bin] = fNofBins; // this map simply stores which bin ends up at which index
2209 fChannelLabel.push_back(fMatrices[0]->GetXaxis()->GetBinLabel(bin));
2210 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << bin << ". bin: fNofBins " << fNofBins << ", channelToIndex[" << bin << "] " << channelToIndex[bin] << ", fActualChannelId.back() " << fActualChannelId.back() << std::endl; }
2211 ++fNofBins;
2212 } else {
2213 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "skipping bin " << bin << std::endl; }
2214 }
2215 }
2216 // now loop over all other matrices and check vs. the first one
2217 for(size_t i = 1; i < fMatrices.size(); ++i) {
2218 int tmpBins = 0;
2219 for(int bin = 1; bin <= fMatrices[i]->GetNbinsX(); ++bin) {
2220 if(FilledBin(fMatrices[i], bin)) { // good bin in the current matrix
2221 // current index is tmpBins, so we check if the bin agrees with what we have
2222 if(channelToIndex.find(bin) == channelToIndex.end() || tmpBins != channelToIndex[bin]) { // found a full bin, but the corresponding bin in the first matrix isn't full or a different bin
2223 std::ostringstream error;
2224 error << "Mismatch in " << i << ". matrix (" << fMatrices[i]->GetName() << "), bin " << bin << " is " << tmpBins << ". filled bin, but should be " << (channelToIndex.find(bin) == channelToIndex.end() ? "not filled" : Form("%d", channelToIndex[bin])) << std::endl;
2225 throw std::invalid_argument(error.str());
2226 }
2227 if(strcmp(fMatrices[0]->GetXaxis()->GetBinLabel(bin), fMatrices[i]->GetXaxis()->GetBinLabel(bin)) != 0) { // bin is full and matches the bin in the first matrix so we check the labels
2228 std::ostringstream error;
2229 error << i << ". matrix, " << bin << ". bin: label (" << fMatrices[i]->GetXaxis()->GetBinLabel(bin) << ") doesn't match bin label of the first matrix (" << fMatrices[0]->GetXaxis()->GetBinLabel(bin) << ")" << std::endl;
2230 throw std::invalid_argument(error.str());
2231 }
2232 ++tmpBins;
2233 } else {
2234 if(channelToIndex.find(bin) != channelToIndex.end()) { //found an empty bin that was full in the first matrix
2235 std::ostringstream error;
2236 error << "Mismatch in " << i << ". matrix (" << fMatrices[i]->GetName() << "), bin " << bin << " is empty, but should be " << channelToIndex[bin] << ". filled bin" << std::endl;
2237 throw std::invalid_argument(error.str());
2238 }
2239 }
2240 }
2241 }
2242
2243 if(fVerboseLevel > EVerbosity::kQuiet) { std::cout << fMatrices.size() << " matrices with " << fMatrices[0]->GetNbinsX() << " x-bins, fNofBins " << fNofBins << ", fActualChannelId.size() " << fActualChannelId.size() << std::endl; }
2244
2245 fOutput = new TFile("TSourceCalibration.root", "recreate");
2246 if(!fOutput->IsOpen()) {
2247 throw std::runtime_error("Unable to open output file \"TSourceCalibration.root\"!");
2248 }
2249
2250 // build the first screen
2253
2254 // Set a name to the main frame
2255 SetWindowName("SourceCalibration");
2256
2257 // Map all subwindows of main frame
2258 MapSubwindows();
2259
2260 // Initialize the layout algorithm
2261 Resize(GetDefaultSize());
2262
2263 // Map main frame
2264 MapWindow();
2265}
2266
2268{
2269 fOutput->Close();
2270 delete fOutput;
2271
2272 gErrorIgnoreLevel = fOldErrorLevel;
2273
2274 delete fRedirect;
2275}
2276
2278{
2279 /// Build initial interface with histogram <-> source assignment
2280
2281 auto* layoutManager = new TGTableLayout(this, fMatrices.size() + 1, 2, true, 5);
2282 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "created table layout manager with 2 columns, " << fMatrices.size() + 1 << " rows" << std::endl; }
2283 SetLayoutManager(layoutManager);
2284
2285 // The matrices and source selection boxes
2286 size_t i = 0;
2287 for(i = 0; i < fMatrices.size(); ++i) {
2288 fMatrixNames.push_back(new TGLabel(this, fMatrices[i]->GetName()));
2289 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Text height " << fMatrixNames.back()->GetFont()->TextHeight() << std::endl; }
2290 fSource.push_back(nullptr);
2291 fSourceEnergy.emplace_back();
2292 fSourceBox.push_back(new TGComboBox(this, "Select source", kSourceBox + fSourceBox.size()));
2293
2294 int index = 0;
2295#if __cplusplus >= 201703L
2296 // For some reasons getenv("GRSISYS") with strcat does not work (adds the "sub"-path twice).
2297 // Have to do this by copying the getenv result into a c++-string.
2298 if(std::getenv("GRSISYS") == nullptr) {
2299 throw std::runtime_error("Failed to get environment variable $GRSISYS");
2300 }
2301 std::string path(std::getenv("GRSISYS"));
2302 path += "/libraries/TAnalysis/SourceData/";
2303 for(const auto& file : std::filesystem::directory_iterator(path)) {
2304 if(file.is_regular_file() && file.path().extension().compare(".sou") == 0) {
2305 fSourceBox.back()->AddEntry(file.path().stem().c_str(), index);
2306 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << "/" << index << ": Comparing matrix name " << fMatrices[i]->GetName() << " to file name " << file.path().stem().c_str() << std::endl; }
2307 if(std::strstr(fMatrices[i]->GetName(), file.path().stem().c_str()) != nullptr) {
2308 fSourceBox.back()->Select(index);
2309 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2310 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": selected source " << index << std::endl; }
2312 std::cout << "matrix name " << fMatrices[i]->GetName() << " not matching " << file.path().stem().c_str() << std::endl;
2313 }
2314 ++index;
2315 }
2316 }
2317 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": created list with " << index << " sources" << std::endl; }
2318#else
2319 fSourceBox.back()->AddEntry("22Na", index);
2320 if(std::strstr(fMatrices[i]->GetName(), "22Na") != nullptr) {
2321 fSourceBox.back()->Select(index);
2322 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2323 }
2324 ++index;
2325 fSourceBox.back()->AddEntry("56Co", index);
2326 if(std::strstr(fMatrices[i]->GetName(), "56Co") != nullptr) {
2327 fSourceBox.back()->Select(index);
2328 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2329 }
2330 ++index;
2331 fSourceBox.back()->AddEntry("60Co", index);
2332 if(std::strstr(fMatrices[i]->GetName(), "60Co") != nullptr) {
2333 fSourceBox.back()->Select(index);
2334 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2335 }
2336 ++index;
2337 fSourceBox.back()->AddEntry("133Ba", index);
2338 if(std::strstr(fMatrices[i]->GetName(), "133Ba") != nullptr) {
2339 fSourceBox.back()->Select(index);
2340 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2341 }
2342 ++index;
2343 fSourceBox.back()->AddEntry("152Eu", index);
2344 if(std::strstr(fMatrices[i]->GetName(), "152Eu") != nullptr) {
2345 fSourceBox.back()->Select(index);
2346 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2347 }
2348 ++index;
2349 fSourceBox.back()->AddEntry("241Am", index);
2350 if(std::strstr(fMatrices[i]->GetName(), "241Am") != nullptr) {
2351 fSourceBox.back()->Select(index);
2352 SetSource(kSourceBox + fSourceBox.size() - 1, index);
2353 }
2354#endif
2355
2356 fSourceBox.back()->SetMinHeight(fPanelHeight / 2);
2357
2358 fSourceBox.back()->Resize(fSourceboxWidth, fLineHeight);
2359 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Attaching " << i << ". label to 0, 1, " << i << ", " << i + 1 << ", and box to 1, 2, " << i << ", " << i + 1 << std::endl; }
2360 AddFrame(fMatrixNames.back(), new TGTableLayoutHints(0, 1, i, i + 1, kLHintsRight | kLHintsCenterY));
2361 AddFrame(fSourceBox.back(), new TGTableLayoutHints(1, 2, i, i + 1, kLHintsLeft | kLHintsCenterY));
2362 }
2363
2364 // The buttons
2365 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "Attaching start button to 0, 2, " << i << ", " << i + 1 << std::endl; }
2366 fStartButton = new TGTextButton(this, "Accept && Continue", kStartButton);
2367 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "start button " << fStartButton << std::endl; }
2368 AddFrame(fStartButton, new TGTableLayoutHints(0, 2, i, i + 1, kLHintsCenterX | kLHintsCenterY));
2369 Layout();
2370}
2371
2373{
2374 /// Create connections for the histogram <-> source assignment interface
2375
2376 // Connect the selection of the source
2377 for(auto* box : fSourceBox) {
2378 box->Connect("Selected(Int_t, Int_t)", "TSourceCalibration", this, "SetSource(Int_t, Int_t)");
2379 }
2380
2381 //Connect the clicking of buttons
2382 fStartButton->Connect("Clicked()", "TSourceCalibration", this, "Start()");
2383}
2384
2386{
2387 /// Disconnect all signals from histogram <-> source assignment interface
2388 for(auto* box : fSourceBox) {
2389 box->Disconnect("Selected(Int_t, Int_t)", this, "SetSource(Int_t, Int_t)");
2390 }
2391
2392 fStartButton->Disconnect("Clicked()", this, "Start()");
2393}
2394
2396{
2397 HideFrame(element);
2398 RemoveFrame(element);
2399 delete element;
2400 element = nullptr;
2401}
2402
2404{
2405 for(auto* name : fMatrixNames) {
2406 DeleteElement(name);
2407 }
2408 fMatrixNames.clear();
2409 for(auto* box : fSourceBox) {
2410 DeleteElement(box);
2411 }
2412 fSourceBox.clear();
2414 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Deleted start button " << fStartButton << std::endl; }
2415}
2416
2417void TSourceCalibration::SetSource(Int_t windowId, Int_t entryId)
2418{
2419 int index = windowId - kSourceBox;
2420 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << ": windowId " << windowId << ", entryId " << entryId << " => " << index << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2421 delete fSource[index];
2422 auto* nucleus = new TNucleus(fSourceBox[index]->GetListBox()->GetEntry(entryId)->GetTitle());
2423 TIter iter(nucleus->GetTransitionList());
2424 fSourceEnergy[index].clear();
2425 while(auto* transition = static_cast<TTransition*>(iter.Next())) {
2426 fSourceEnergy[index].push_back(std::make_tuple(transition->GetEnergy(), transition->GetEnergyUncertainty(), transition->GetIntensity(), transition->GetIntensityUncertainty()));
2427 }
2428 fSource[index] = nucleus;
2429}
2430
2432{
2433 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << ": fEmitter " << fEmitter << ", fStartButton " << fStartButton << ", fAcceptAllButton " << fAcceptAllButton << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2434 if(fEmitter == fStartButton) {
2435 SecondWindow();
2436 } else if(fEmitter == fAcceptAllButton) {
2437 for(auto& channelTab : fChannelTab) {
2438 if(channelTab == nullptr) { continue; }
2439 channelTab->UpdateChannel();
2440 channelTab->Write(fOutput);
2441 }
2443 std::cout << "Closing window" << std::endl;
2444 CloseWindow();
2445 std::cout << "all done" << std::endl;
2446 exit(0);
2447 }
2448}
2449
2451{
2452 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << ": fEmitter " << fEmitter << ", fStartButton " << fStartButton << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2453 if(fEmitter == nullptr) { // we only want to do this once at the beginning (after fEmitter was initialized to nullptr)
2455 TTimer::SingleShot(fWaitMs, "TSourceCalibration", this, "HandleTimer()");
2456 }
2457}
2458
2460{
2461 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2462 // check that all sources have been set
2463 for(size_t i = 0; i < fSource.size(); ++i) {
2464 if(fSource[i] == nullptr) {
2465 std::cerr << "Source " << i << " not set!" << std::endl;
2466 return;
2467 }
2468 }
2469 // now check that we don't have the same source twice (which wouldn't make sense)
2470 for(size_t i = 0; i < fSource.size(); ++i) {
2471 for(size_t j = i + 1; j < fSource.size(); ++j) {
2472 if(*(fSource[i]) == *(fSource[j])) {
2473 std::cerr << "Duplicate sources: " << i << " - " << fSource[i]->GetName() << " and " << j << " - " << fSource[j]->GetName() << std::endl;
2474 return;
2475 }
2476 }
2477 }
2478
2479 // disconnect signals of first screen and remove all elements
2481 RemoveAll();
2482 DeleteFirst();
2483
2484 SetLayoutManager(new TGVerticalLayout(this));
2485
2486 // create intermediate progress bar
2487 fProgressBar = new TGHProgressBar(this, TGProgressBar::kFancy, fPanelWidth);
2488 fProgressBar->SetBarColor("lightblue");
2489 fProgressBar->SetRange(0., static_cast<Float_t>(fMatrices.size() * fActiveBins.size()));
2490 fProgressBar->ShowPosition(true, false, "Creating source tabs: %.0f");
2491 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Set range of progress bar to 0. - " << fProgressBar->GetMax() << " = " << fMatrices.size() * fActiveBins.size() << " = " << fMatrices.size() << "*" << fActiveBins.size() << std::endl; }
2492 AddFrame(fProgressBar, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandX | kLHintsExpandY, 0, 0, 0, 0));
2493
2494 // Map all subwindows of main frame
2495 MapSubwindows();
2496
2497 // Initialize the layout algorithm
2498 Resize(TGDimension(fPanelWidth, fLineHeight));
2499
2500 // Map main frame
2501 MapWindow();
2502
2503 // create second screen and its connections
2506
2507 // remove progress bar
2509
2510 // Map all subwindows of main frame
2511 MapSubwindows();
2512
2513 // Initialize the layout algorithm
2514 Resize(TGDimension(2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight));
2515
2516 // Map main frame
2517 MapWindow();
2518 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << __PRETTY_FUNCTION__ << " done" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2519}
2520
2522{
2523 SetLayoutManager(new TGVerticalLayout(this));
2524 fTab = new TGTab(this, 2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight);
2525
2526 fChannelTab.resize(fMatrices[0]->GetNbinsX());
2527 for(auto& bin : fActiveBins) {
2528 // create vector with projections of this channel for each source
2529 std::vector<GH1D*> projections(fMatrices.size());
2530 size_t index = 0;
2531 for(auto* matrix : fMatrices) {
2532 projections[index] = new GH1D(matrix->ProjectionY(Form("%s_%s", fSource[index]->GetName(), matrix->GetXaxis()->GetBinLabel(bin)), bin, bin));
2533 ++index;
2534 }
2535 auto* frame = fTab->AddTab(fMatrices[0]->GetXaxis()->GetBinLabel(bin));
2536 // create a new tab in a new thread
2537 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << "Creating channel for bin " << bin << std::endl; }
2538 fChannelTab[bin - 1] = new TChannelTab(this, fSource, projections, fMatrices[0]->GetXaxis()->GetBinLabel(bin), frame, fSourceEnergy, fProgressBar);
2539 //// right now the lambda uses copies of all variables, not sure if that is neccesary, or if we only need that for projections?
2540 //fFutures.emplace(std::make_pair(bin - 1, std::async(std::launch::async, [=] {
2541 // std::cout << "creating channel tab with these arguments: " << this << ", fSource vector of size " << fSource.size() << ", projection vector of size " << projections.size() << ", " << fMatrices[0]->GetXaxis()->GetBinLabel(bin) << ", " << frame << ", " << fDefaultSigma << ", " << fDefaultThreshold << ", " << fDefaultDegree << ", " << fSourceEnergy.size() << " source energy vectors, " << fProgressBar << "."<< std::endl;
2542 // auto* newTab = new TChannelTab(this, fSource, projections, fMatrices[0]->GetXaxis()->GetBinLabel(bin), frame, fDefaultSigma, fDefaultThreshold, fDefaultDegree, fSourceEnergy, fProgressBar);
2543 // std::cout << "created new tab " << newTab << std::endl;
2544 // return newTab;
2545 // })));
2546 //// if we have too many threads running, get the result of the first one (this means waiting on that thread to finish) and assign the results
2547 //if(fFutures.size() >= fNumberOfThreads) {
2548 // if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << fFutures.size() << " threads running, waiting for thread #" << fFutures.front().first << " to finish before starting next, max. # threads " << fNumberOfThreads << std::endl; }
2549 // auto tmp = fFutures.front().second.get();
2550 // fChannelTab[fFutures.front().first] = tmp;
2551 // fFutures.pop();
2552 //}
2553 }
2554 // wait for all remaining threads to finish
2555 //while(!fFutures.empty()) {
2556 // if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << fFutures.size() << " threads left, waiting for thread #" << fFutures.front().first << " to finish before starting next, max. # threads " << fNumberOfThreads << std::endl; }
2557 // auto tmp = fFutures.front().second.get();
2558 // fChannelTab[fFutures.front().first] = tmp;
2559 // fFutures.pop();
2560 //}
2561
2562 //if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << fMatrices.size() << " matrices with " << fMatrices[0]->GetNbinsX() << " x-bins, fNofBins " << fNofBins << ", fActualChannelId.size() " << fActualChannelId.size() << ", fChannelTab[0]->SourceTab()->GetNumberOfTabs() " << fChannelTab[0]->SourceTab()->GetNumberOfTabs() << std::endl; }
2563
2564 //for(int i = 0; i < fChannelTab[0]->SourceTab()->GetNumberOfTabs(); ++i) {
2565 //if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": " << fChannelTab[0]->SourceTab()->GetTabTab(i)->GetText()->GetString() << std::endl; }
2566 //}
2567
2568 AddFrame(fTab, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 0, 0, 0, 0));
2569
2570 // bottom frame with navigation button group, text entries, etc.
2571 fBottomFrame = new TGHorizontalFrame(this, 2 * fPanelWidth, TSourceCalibration::StatusbarHeight());
2572
2573 fLeftFrame = new TGVerticalFrame(fBottomFrame, fPanelWidth, fParameterHeight);
2574 fNavigationGroup = new TGHButtonGroup(fLeftFrame, "Navigation");
2575 fPreviousButton = new TGTextButton(fNavigationGroup, "&Previous");
2576 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << "prev button " << fPreviousButton << std::endl; }
2577 fPreviousButton->SetEnabled(false);
2578 fDiscardButton = new TGTextButton(fNavigationGroup, "&Discard");
2579 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "discard button " << fDiscardButton << std::endl; }
2580 fAcceptButton = new TGTextButton(fNavigationGroup, "&Accept");
2581 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "accept button " << fAcceptButton << std::endl; }
2582 fAcceptAllButton = new TGTextButton(fNavigationGroup, "Accept All && Finish");
2583 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "accept all button " << fAcceptAllButton << std::endl; }
2584 fWriteButton = new TGTextButton(fNavigationGroup, "&Write");
2585 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "write button " << fWriteButton << std::endl; }
2586 fNextButton = new TGTextButton(fNavigationGroup, "&Next");
2587 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "next button " << fNextButton << std::endl; }
2588
2589 fLeftFrame->AddFrame(fNavigationGroup, new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 2, 2));
2590
2591 fFittingGroup = new TGHButtonGroup(fLeftFrame, "Fitting/Calibrating");
2592 fFindSourcePeaksButton = new TGTextButton(fFittingGroup, "&Find Source Peaks");
2593 fFindChannelPeaksButton = new TGTextButton(fFittingGroup, "Find Channel Peaks");
2594 fFindAllPeaksButton = new TGTextButton(fFittingGroup, "Find All Peaks");
2595 fCalibrateButton = new TGTextButton(fFittingGroup, "&Calibrate");
2596 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "cal button " << fCalibrateButton << std::endl; }
2597 fRecursiveRemoveButton = new TGTextButton(fFittingGroup, "Recursive Remove");
2598 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "remove button " << fRecursiveRemoveButton << std::endl; }
2599
2600 fLeftFrame->AddFrame(fFittingGroup, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
2601
2602 fBottomFrame->AddFrame(fLeftFrame, new TGLayoutHints(kLHintsLeft, 2, 2, 2, 2));
2603
2604 fRightFrame = new TGVerticalFrame(fBottomFrame, fPanelWidth, fParameterHeight);
2605 fParameterFrame = new TGGroupFrame(fRightFrame, "Parameters", kHorizontalFrame);
2606 fParameterFrame->SetLayoutManager(new TGMatrixLayout(fParameterFrame, 0, 4, 2));
2607 fSigmaLabel = new TGLabel(fParameterFrame, "Sigma");
2608 fSigmaEntry = new TGNumberEntry(fParameterFrame, fDefaultSigma, fDigitWidth, kSigmaEntry, TGNumberFormat::EStyle::kNESRealTwo, TGNumberFormat::EAttribute::kNEAPositive);
2609 fThresholdLabel = new TGLabel(fParameterFrame, "Threshold");
2610 fThresholdEntry = new TGNumberEntry(fParameterFrame, fDefaultThreshold, fDigitWidth, kThresholdEntry, TGNumberFormat::EStyle::kNESRealThree, TGNumberFormat::EAttribute::kNEAPositive, TGNumberFormat::ELimit::kNELLimitMinMax, 0., 1.);
2611 fDegreeLabel = new TGLabel(fParameterFrame, "Degree of polynomial");
2612 fDegreeEntry = new TGNumberEntry(fParameterFrame, fDefaultDegree, 2, kDegreeEntry, TGNumberFormat::EStyle::kNESInteger, TGNumberFormat::EAttribute::kNEAPositive);
2613 fMaxResidualLabel = new TGLabel(fParameterFrame, "Max. residual (in keV)");
2614 fMaxResidualEntry = new TGNumberEntry(fParameterFrame, fDefaultMaxResidual, fDigitWidth, kMaxResidualEntry, TGNumberFormat::EStyle::kNESRealTwo, TGNumberFormat::EAttribute::kNEAPositive);
2615
2616 fParameterFrame->AddFrame(fSigmaLabel);
2617 fParameterFrame->AddFrame(fSigmaEntry);
2620 fParameterFrame->AddFrame(fDegreeLabel);
2621 fParameterFrame->AddFrame(fDegreeEntry);
2624
2625 fRightFrame->AddFrame(fParameterFrame, new TGLayoutHints(kLHintsTop | kLHintsRight | kLHintsExpandX, 2, 2, 2, 2));
2626
2627 fSettingsFrame = new TGGroupFrame(fRightFrame, "Settings", kHorizontalFrame);
2628 fSettingsFrame->SetLayoutManager(new TGMatrixLayout(fSettingsFrame, 0, 2, 2));
2629 fApplyToAll = new TGCheckButton(fSettingsFrame, "Apply to all channels", kApplyToAll);
2630 fApplyToAll->SetState(kButtonUp);
2631 fSettingsFrame->AddFrame(fApplyToAll);
2632 fWriteNonlinearities = new TGCheckButton(fSettingsFrame, "Write nonlinearities", kWriteNonlinearities);
2633 fWriteNonlinearities->SetState(kButtonUp);
2635
2636 fRightFrame->AddFrame(fSettingsFrame, new TGLayoutHints(kLHintsBottom | kLHintsRight | kLHintsExpandX, 2, 2, 2, 2));
2637
2638 fBottomFrame->AddFrame(fRightFrame, new TGLayoutHints(kLHintsBottom | kLHintsRight, 2, 2, 2, 2));
2639
2640 AddFrame(fBottomFrame, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
2641
2642 SelectedTab(0);
2643
2644 fProgressBar->Reset();
2645 fProgressBar->ShowPosition(true, false, "Finding peaks for tab #%.0f");
2646 for(auto* channelTab : fChannelTab) {
2647 if(channelTab != nullptr) {
2649 for(int i = 0; i < 20; ++i) { std::cout << std::endl; }
2650 for(int i = 0; i < 5; ++i) {
2651 std::cout << "================================================================================" << std::endl;
2652 }
2653 for(int i = 0; i < 20; ++i) { std::cout << std::endl; }
2654 }
2655 channelTab->Initialize(true);
2656 channelTab->FindAllCalibratedPeaks();
2657 channelTab->Calibrate();
2658 channelTab->Draw();
2660 channelTab->PrintCanvases();
2661 }
2662 }
2663 }
2664
2665 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << "Second interface done" << std::endl; }
2666}
2667
2669{
2670 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2671 fNavigationGroup->Connect("Clicked(Int_t)", "TSourceCalibration", this, "Navigate(Int_t)");
2672 fFittingGroup->Connect("Clicked(Int_t)", "TSourceCalibration", this, "Fitting(Int_t)");
2673 fTab->Connect("Selected(Int_t)", "TSourceCalibration", this, "SelectedTab(Int_t)");
2674 // we don't need to connect the sigma, threshold, and degree number entries, those are automatically read when we start the calibration
2675 for(auto* channelTab : fChannelTab) {
2676 if(channelTab != nullptr) {
2677 channelTab->MakeConnections();
2678 }
2679 }
2680}
2681
2683{
2684 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2685 fNavigationGroup->Disconnect("Clicked(Int_t)", this, "Navigate(Int_t)");
2686 fFittingGroup->Disconnect("Clicked(Int_t)", this, "Fitting(Int_t)");
2687 fTab->Disconnect("Selected(Int_t)", this, "SelectedTab(Int_t)");
2688 for(auto* channelTab : fChannelTab) {
2689 if(channelTab != nullptr) {
2690 channelTab->Disconnect();
2691 }
2692 }
2693}
2694
2696{
2697 // we get the current source tab id and use it to get the channel tab from the right source tab
2698 // since the current id only refers to the position within the open tabs we need to keep track of the actual id it relates to
2699 // for this we created a vector with all ids at the beginning and now remove the position correspoding to the current id when we remove the tab
2700 int currentChannelId = fTab->GetCurrent();
2701 int actualChannelId = fActualChannelId[currentChannelId];
2702 int nofTabs = fTab->GetNumberOfTabs();
2703 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": id " << id << ", channel tab id " << currentChannelId << ", actual channel tab id " << actualChannelId << ", # of tabs " << nofTabs << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2704 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Before: active source tab of tab " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Name() << ", #" << fTab->GetCurrent() << " = bin " << fActiveBins[fTab->GetCurrent()] << ": " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->ActiveSourceTab() << std::endl; }
2705 switch(id) {
2706 case ENavigate::kPrevious: // previous
2707 fTab->SetTab(currentChannelId - 1);
2708 SelectedTab(currentChannelId - 1);
2709 break;
2710 case ENavigate::kDiscard: // discard
2711 // select the next (or if we are on the last tab, the previous) tab
2712 if(currentChannelId < nofTabs - 1) {
2713 fTab->SetTab(currentChannelId + 1);
2714 } else {
2715 fTab->SetTab(currentChannelId - 1);
2716 }
2717 // remove the original active tab
2718 fTab->RemoveTab(currentChannelId);
2719 break;
2720 case ENavigate::kAccept: // accept
2721 AcceptChannel(currentChannelId);
2722 break;
2723 case ENavigate::kAcceptAll: // accept all (no argument = -1 = all)
2724 AcceptChannel();
2725 return;
2726 case ENavigate::kWrite: // write
2728 break;
2729 case ENavigate::kNext: // next
2730 fTab->SetTab(currentChannelId + 1);
2731 SelectedTab(currentChannelId + 1);
2732 break;
2733 default:
2734 break;
2735 }
2736 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "After: active source tab of tab " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Name() << ", #" << fTab->GetCurrent() << " = bin " << fActiveBins[fTab->GetCurrent()] << ": " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->ActiveSourceTab() << std::endl; }
2737}
2738
2740{
2741 // we get the current source tab id and use it to get the channel tab from the right source tab
2742 // since the current id only refers to the position within the open tabs we need to keep track of the actual id it relates to
2743 // for this we created a vector with all ids at the beginning and now remove the position correspoding to the current id when we remove the tab
2744 int currentChannelId = fTab->GetCurrent();
2745 int actualChannelId = fActualChannelId[currentChannelId];
2746 int nofTabs = fTab->GetNumberOfTabs();
2747 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": id " << id << ", channel tab id " << currentChannelId << ", actual channel tab id " << actualChannelId << ", # of tabs " << nofTabs << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2748 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Before: active source tab of tab " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Name() << ", #" << fTab->GetCurrent() << " = bin " << fActiveBins[fTab->GetCurrent()] << ": " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->ActiveSourceTab() << std::endl; }
2749 switch(id) {
2750 case EFitting::kFindSourcePeaks: // find peaks for selected source
2752 break;
2753 case EFitting::kFindChannelPeaks: // find peaks for selected channel
2755 break;
2756 case EFitting::kFindAllPeaks: // find all peaks
2757 FindAllPeaks();
2758 break;
2759 case EFitting::kCalibrate: // calibrate
2760 Calibrate();
2761 break;
2762 case EFitting::kRecursiveRemove: // recursively remove outliers
2764 break;
2765 default:
2766 break;
2767 }
2768 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "After: active source tab of tab " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Name() << ", #" << fTab->GetCurrent() << " = bin " << fActiveBins[fTab->GetCurrent()] << ": " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->ActiveSourceTab() << std::endl; }
2769}
2770
2772{
2773 /// Simple function that enables and disables the previous and next buttons depending on which tab was selected.
2774 /// Also sets gPad to the projection of the source selected in this tab.
2775 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": id " << id << ", nofTabs " << fTab->GetNumberOfTabs() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2776 if(id < 0) { return; }
2777 if(id == 0) {
2778 fPreviousButton->SetEnabled(false);
2779 } else {
2780 fPreviousButton->SetEnabled(true);
2781 }
2782
2783 if(id >= fTab->GetNumberOfTabs() - 1) {
2784 fNextButton->SetEnabled(false);
2785 } else {
2786 fNextButton->SetEnabled(true);
2787 }
2788
2789 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "active source tab of tab " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Name() << ", #" << fTab->GetCurrent() << " = bin " << fActiveBins[fTab->GetCurrent()] << ": " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->ActiveSourceTab() << RESET_COLOR << std::endl; }
2790
2791 if(fChannelTab[fActiveBins[id] - 1] == nullptr) {
2792 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]" << RESET_COLOR << std::endl;
2793 return;
2794 }
2795 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab() == nullptr) {
2796 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()" << RESET_COLOR << std::endl;
2797 return;
2798 }
2799 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas() == nullptr) {
2800 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()->ProjectionCanvas()" << RESET_COLOR << std::endl;
2801 return;
2802 }
2803 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas() == nullptr) {
2804 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas()" << RESET_COLOR << std::endl;
2805 return;
2806 }
2807 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "Changing gpad from \"" << gPad->GetName() << "\" to \""; }
2808 gPad = fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas();
2809 fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas()->cd();
2810 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << gPad->GetName() << "\"" << RESET_COLOR << std::endl; }
2811}
2812
2813void TSourceCalibration::AcceptChannel(const int& channelId)
2814{
2815 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": channelId " << channelId << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2816
2817 // select the next (or if we are on the last tab, the previous) tab
2818 int nofTabs = fTab->GetNumberOfTabs();
2819 int minChannel = 0;
2820 int maxChannel = nofTabs - 1;
2821 if(channelId >= 0) {
2822 minChannel = channelId;
2823 maxChannel = channelId;
2824 }
2825
2826 // we need to loop backward, because removing the first channel would make the second one the new first and so on
2827 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": accepting channels " << maxChannel << " to " << minChannel << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2828 for(int currentChannelId = maxChannel; currentChannelId >= minChannel; --currentChannelId) {
2829 int actualChannelId = fActualChannelId[currentChannelId];
2830 if(fVerboseLevel > EVerbosity::kLoops) { std::cout << __PRETTY_FUNCTION__ << ": currentChannelId " << currentChannelId << ", actualChannelId " << actualChannelId << ", fChannelTab.size() " << fChannelTab.size() << ", fActualChannelId.size() " << fActualChannelId.size() << ", nofTabs " << nofTabs << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2831
2832 if(minChannel == maxChannel) { // we don't need to select the tab if we close all
2833 if(currentChannelId < nofTabs) {
2834 fTab->SetTab(currentChannelId + 1);
2835 if(fVerboseLevel > EVerbosity::kLoops) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": set tab to " << currentChannelId + 1 << " = " << fTab->GetCurrent() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2836 } else {
2837 fTab->SetTab(currentChannelId - 1);
2838 if(fVerboseLevel > EVerbosity::kLoops) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": set tab to " << currentChannelId - 1 << " = " << fTab->GetCurrent() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2839 }
2840 }
2841
2842 // remove the original active tab
2843 fTab->RemoveTab(currentChannelId); // this sets the selected tab to zero if the deleted tab is the currently selected one, which is why we do it now (after we've selected a new tab)
2844 fActualChannelId.erase(fActualChannelId.begin() + currentChannelId);
2845 fActiveBins.erase(fActiveBins.begin() + currentChannelId);
2846 }
2847 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": # of channel tabs " << fActualChannelId.size() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2848 // check if this changes which buttons are enabled or not (not using the variables from the beginning of this block because they might have changed by now!)
2849 SelectedTab(fTab->GetCurrent());
2850 // if this was also the last source vector we initiate the last screen
2851 if(fActualChannelId.empty() || fActiveBins.empty()) {
2852 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "last channel tab done - writing files now" << std::endl; }
2854 TTimer::SingleShot(fWaitMs, "TSourceCalibration", this, "HandleTimer()");
2855 }
2856 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2857}
2858
2860{
2861 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2862 if(fApplyToAll->IsDown()) {
2863 auto* window = CreateProgressbar("Finding current source peaks in channel #%.0f");
2864
2865 for(auto& bin : fActiveBins) {
2866 if(fChannelTab[bin - 1] != nullptr) {
2867 fChannelTab[bin - 1]->FindCalibratedPeaks();
2868 fChannelTab[bin - 1]->Calibrate();
2869 }
2870 fProgressBar->Increment(1);
2871 }
2872 window->CloseWindow();
2873 delete fProgressBar;
2874 } else if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2875 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->FindCalibratedPeaks();
2876 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Calibrate();
2877 } else {
2878 std::cout << __PRETTY_FUNCTION__ << ": fChannelTab[" << fActiveBins[fTab->GetCurrent()] << " = fActiveBins[" << fTab->GetCurrent() << "]] is a nullptr (fApplyToAll is " << (fApplyToAll->IsDown() ? "down" : "up") << ")!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2879 }
2880 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2881}
2882
2884{
2885 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2886 if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2887 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->FindAllCalibratedPeaks();
2888 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Calibrate();
2889 } else {
2890 std::cout << __PRETTY_FUNCTION__ << ": fChannelTab[" << fActiveBins[fTab->GetCurrent()] << " = fActiveBins[" << fTab->GetCurrent() << "]] is a nullptr!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2891 }
2892 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2893}
2894
2896{
2897 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2898 auto* window = CreateProgressbar("Finding all source peaks for channel #%.0f");
2899
2900 for(auto& bin : fActiveBins) {
2901 if(fChannelTab[bin - 1] != nullptr) {
2902 fChannelTab[bin - 1]->FindAllCalibratedPeaks();
2903 fChannelTab[bin - 1]->Calibrate();
2904 } else {
2905 std::cout << __PRETTY_FUNCTION__ << ": fChannelTab[" << bin << "] is a nullptr!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2906 }
2907 fProgressBar->Increment(1);
2908 }
2909 window->CloseWindow();
2910 delete fProgressBar;
2911 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2912}
2913
2915{
2916 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2917 if(fApplyToAll->IsDown()) {
2918 auto* window = CreateProgressbar("Calibrating channel #%.0f");
2919
2920 for(auto& bin : fActiveBins) {
2921 if(fChannelTab[bin - 1] != nullptr) {
2922 fChannelTab[bin - 1]->Calibrate();
2923 }
2924 fProgressBar->Increment(1);
2925 }
2926 window->CloseWindow();
2927 delete fProgressBar;
2928 } else if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2929 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Calibrate();
2930 } else {
2931 std::cout << __PRETTY_FUNCTION__ << ": fChannelTab[" << fActiveBins[fTab->GetCurrent()] << " = fActiveBins[" << fTab->GetCurrent() << "]] is a nullptr (fApplyToAll is " << (fApplyToAll->IsDown() ? "down" : "up") << ")!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2932 }
2933 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2934}
2935
2937{
2938 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2939 if(fApplyToAll->IsDown()) {
2940 auto* window = CreateProgressbar("Recursively removing peaks for channel #%.0f");
2941
2942 for(auto& bin : fActiveBins) {
2943 if(fChannelTab[bin - 1] != nullptr) {
2944 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "Starting recursive remove for bin " << bin << ", fChannelTab[bin - 1] " << fChannelTab[bin - 1] << std::endl; }
2945 fChannelTab[bin - 1]->RecursiveRemove(MaxResidual());
2946 }
2947 fProgressBar->Increment(1);
2948 }
2949 window->CloseWindow();
2950 delete fProgressBar;
2951 } else if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2952 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "Starting recursive remove for bin " << fActiveBins[fTab->GetCurrent() - 1] << ", fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] " << fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] << std::endl; }
2953 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->RecursiveRemove(MaxResidual());
2954 } else {
2955 std::cout << __PRETTY_FUNCTION__ << ": fChannelTab[" << fActiveBins[fTab->GetCurrent()] << " = fActiveBins[" << fTab->GetCurrent() << "]] is a nullptr (fApplyToAll is " << (fApplyToAll->IsDown() ? "down" : "up") << ")!" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2956 }
2957 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2958}
2959
2961{
2962 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2963 std::ostringstream fileName;
2964 for(auto* source : fSource) {
2965 fileName << source->GetName();
2966 }
2967 fileName << ".cal";
2968 TChannel::WriteCalFile(fileName.str());
2969 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": wrote " << fileName.str() << " with " << TChannel::GetNumberOfChannels() << " channels" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2971}
2972
2973TGTransientFrame* TSourceCalibration::CreateProgressbar(const char* format)
2974{
2975 auto* window = new TGTransientFrame(gClient->GetRoot(), this, fPanelWidth, fStatusbarHeight);
2976 fProgressBar = new TGHProgressBar(window, TGProgressBar::kFancy, fPanelWidth);
2977 fProgressBar->SetBarColor("lightblue");
2978 fProgressBar->SetRange(0., static_cast<Float_t>(fActiveBins.size()));
2979 fProgressBar->ShowPosition(true, false, format);
2980 window->AddFrame(fProgressBar, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandX | kLHintsExpandY, 0, 0, 0, 0));
2981 window->CenterOnParent();
2982 window->MapSubwindows();
2983 window->MapWindow();
2984 window->Resize(window->GetDefaultSize());
2985
2986 return window;
2987}
2988
2990{
2991 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2992
2993 Print();
2994 if(fBottomFrame != nullptr) { fBottomFrame->Print(); }
2995 if(fLeftFrame != nullptr) { fLeftFrame->Print(); }
2996 if(fRightFrame != nullptr) { fRightFrame->Print(); }
2997 if(fTab != nullptr) { fTab->Print(); }
2998 std::cout << "TSourceCalibration all " << fChannelTab.size() << " channel tabs:" << std::endl;
2999 for(auto* channelTab : fChannelTab) {
3000 if(channelTab != nullptr) { channelTab->PrintLayout(); }
3001 }
3002}
3003
3005{
3006 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
3007
3008 for(auto* channelTab : fChannelTab) {
3009 if(channelTab != nullptr) { channelTab->PrintCanvases(); }
3010 }
3011}
#define kArrowKeyRelease
Definition GCanvas.cxx:49
#define kArrowKeyPress
Definition GCanvas.cxx:48
EVerbosity
Definition Globals.h:143
@ kQuiet
Definition Globals.h:144
@ kSubroutines
Definition Globals.h:146
@ kLoops
Definition Globals.h:147
@ kAll
Definition Globals.h:148
@ kBasicFlow
Definition Globals.h:145
#define DYELLOW
Definition Globals.h:16
#define DRED
Definition Globals.h:18
#define DGREEN
Definition Globals.h:17
#define DCYAN
Definition Globals.h:21
std::string hex(T val, int width=-1)
Definition Globals.h:129
#define RESET_COLOR
Definition Globals.h:5
TH2D * mat
Definition UserFillObj.h:12
Definition GH1D.h:17
TList * ListOfRegions()
Definition GH1D.h:68
void Print(Option_t *opt="") const override
Definition GH1D.cxx:95
void Clear(Option_t *opt="") override
Definition GH1D.cxx:89
void Draw(Option_t *opt="") override
Definition GH1D.cxx:108
void DrawResidual(Option_t *opt="", TLegend *legend=nullptr)
int GetN()
Returns GetN(), i.e. number of points of the total graph.
void SetMarkerColor(int index, Color_t color)
bool SetResidual(const bool &force=false)
TGraphErrors * TotalResidualGraph()
double GetMaximumY() const
Return maximum y-value.
void SetLineColor(int index, Color_t color)
double * GetY()
Returns an array of y-values of the total graph.
void Print(Option_t *opt="") const override
void Fit(TF1 *function, Option_t *opt="")
Fits the provided function to the total graph.
double GetMinimumY() const
Return minimum y-value.
double GetMinimumX() const
Return minimum x-value.
TGraphErrors * TotalGraph()
double GetMaximumX() const
Return maximum x-value.
int Add(TGraphErrors *, const std::string &label)
Add new graph to set, using the label when creating legends during plotting.
void DrawCalibration(Option_t *opt="", TLegend *legend=nullptr)
void Clear(Option_t *option="") override
Int_t RemovePoint(const Int_t &px, const Int_t &py)
double * GetX()
Returns an array of x-values of the total graph.
TF1 * FitFunction()
Gets the calibration from the total graph (might be nullptr!).
static void WriteCalFile(const std::string &outfilename="")
Definition TChannel.cxx:992
TGraph GetEnergyNonlinearity() const
Definition TChannel.h:198
static TChannel * GetChannel(unsigned int temp_address, bool warn=false)
Definition TChannel.cxx:459
void SetENGCoefficients(const std::vector< Float_t > &tmp, size_t range=0)
Definition TChannel.h:225
void DestroyEnergyNonlinearity()
Definition TChannel.cxx:593
void AddEnergyNonlinearityPoint(double x, double y)
Definition TChannel.h:215
static size_t GetNumberOfChannels()
Definition TChannel.h:68
TSourceCalibration * fSourceCalibration
the parent of this tab
std::vector< GH1D * > fProjections
vector of all projections
void PrintLayout() const
TGCompositeFrame * fChannelFrame
main frame of this tab
std::vector< TSourceTab * > fSources
tabs for all sources
TCalibrationGraphSet * fFwhm
combined fwhm from all sources
TGraphErrors * Data(int channelId) const
TPaveText * fChi2Label
TGTab * fSourceTab
tab for sources
void SelectCanvas(Event_t *event)
void Write(TFile *output)
TRootEmbeddedCanvas * fInitCanvas
TChannelTab(TSourceCalibration *sourceCal, std::vector< TNucleus * > nuclei, std::vector< GH1D * > projections, std::string name, TGCompositeFrame *frame, std::vector< std::vector< std::tuple< double, double, double, double > > > sourceEnergies, TGHProgressBar *progressBar)
void CalibrationStatus(Int_t event, Int_t px, Int_t py, TObject *selected)
void Initialize(const bool &force)
static void ZoomY()
void RemovePoint(Int_t oldGraph, Int_t oldPoint)
TGCompositeFrame * fCalibrationFrame
frame of tab with calibration
std::vector< std::vector< std::tuple< double, double, double, double > > > fSourceEnergies
vector with source energies and uncertainties
std::string Name() const
void CreateSourceTab(size_t source)
TGCompositeFrame * fFwhmFrame
frame of tab with fwhm
TCalibrationGraphSet * fInit
combined initial data from all sources
TRootEmbeddedCanvas * fFwhmCanvas
int fActiveSourceTab
id of the currently active source tab
std::string fName
name of this tab
TPaveText * fInfoLabel
static void ZoomX()
std::vector< TNucleus * > fNuclei
the source nucleus
void SelectedTab(Int_t id)
TGStatusBar * fChannelStatusBar
void RecursiveRemove(const double &maxResidual)
TGTab * fCanvasTab
tab for canvases (calibration with residuals, and FWHM)
TPaveText * fCalLabel
TGCompositeFrame * fInitFrame
frame of tab with initial calibration
void PrintCanvases() const
TGHProgressBar * fProgressBar
TRootEmbeddedCanvas * fCalibrationCanvas
TCalibrationGraphSet * fData
combined data from all sources
Double_t CentroidErr() const override
Definition TGauss.cxx:63
void Centroid(const Double_t &centroid) override
Definition TGauss.cxx:11
void SetLineColor(Color_t color)
Definition TSinglePeak.h:97
Double_t AreaErr() const
Definition TSinglePeak.h:58
Double_t Area() const
Definition TSinglePeak.h:57
unsigned int fLineHeight
Height of text boxes and progress bar.
int fWaitMs
How many milliseconds we wait before we process the navigation input (to avoid double triggers?...
TGTextButton * fPreviousButton
TRedirect * fRedirect
redirect, created in constructor and destroyed in destructor if a log file name has been provided
static std::string LogFile()
static int fStatusbarHeight
Height of status bar (also extra height needed for tabs)
TGVerticalFrame * fRightFrame
TGTransientFrame * CreateProgressbar(const char *format)
static std::string fLogFile
name of log file, if empty no log file is written
TSourceCalibration(double sigma, double threshold, int degree, int count...)
TGCheckButton * fWriteNonlinearities
TGNumberEntry * fMaxResidualEntry
int fDefaultDegree
The default degree of the polynomial used for calibrating, can be changed later.
static EVerbosity VerboseLevel()
TGGroupFrame * fParameterFrame
std::vector< TGLabel * > fMatrixNames
TGTextButton * fFindSourcePeaksButton
TGTextButton * fCalibrateButton
static double MinIntensity()
TGTextButton * fAcceptAllButton
void AcceptChannel(const int &channelId=-1)
TGNumberEntry * fSigmaEntry
double fDefaultSigma
The default sigma used for the peak finding algorithm, can be changed later.
static int fMaxIterations
Maximum iterations over combinations in Match and SmartMatch.
TGTextButton * fFindAllPeaksButton
TGHProgressBar * fProgressBar
TGHButtonGroup * fNavigationGroup
TGNumberEntry * fThresholdEntry
void SetSource(Int_t windowId, Int_t entryId)
std::vector< int > fActiveBins
TGTextButton * fDiscardButton
std::vector< TNucleus * > fSource
static bool fFast
Do we use the fast peak finding method on startup or not.
std::vector< const char * > fChannelLabel
std::vector< TChannelTab * > fChannelTab
static bool fAcceptBadFits
Do we accept peaks where the fit was bad (position or area uncertainties too large or not numbers)
TGTextButton * fAcceptButton
static int fPanelWidth
Width of one panel.
static size_t fNumberOfThreads
Maximum number of threads to run while creating the channel tabs.
double fDefaultMaxResidual
The default maximum residual accepted when trying to iteratively find peaks.
static int fPanelHeight
Height of one panel.
TGTextButton * fRecursiveRemoveButton
std::vector< TH2 * > fMatrices
static bool fUseCalibratedPeaks
Do we use the initial calibration to find more peaks in the source spectra?
int fOldErrorLevel
Used to store old value of gErrorIgnoreLevel (set to kError for the scope of the class)
static int fDigitWidth
Number of digits used for parameter entries (if they are floating point)
TGHButtonGroup * fFittingGroup
TGTextButton * fWriteButton
std::vector< TGComboBox * > fSourceBox
std::vector< int > fActualChannelId
int fNofBins
Number of filled bins in first matrix.
TGTextButton * fFindChannelPeaksButton
TGNumberEntry * fDegreeEntry
static double fMinIntensity
Minimum intensity of source peaks to be considered when trying to find them in the spectrum.
void DeleteElement(TGFrame *element)
static int fParameterHeight
Height of the frame for the parameters.
std::vector< std::vector< std::tuple< double, double, double, double > > > fSourceEnergy
vector to hold source energy, energy uncertainty, intensity, and intensity uncertainty
static bool AcceptBadFits()
static std::vector< int > fBadBins
Bins of the 2D matrix to be ignored even if there are enough counts in them.
static int fSourceboxWidth
Width of the box to select which source each histogram is from.
static double FitRange()
TGTextButton * fStartButton
double fDefaultThreshold
The default threshold used for the peak finding algorithm, can be changed later. Co-56 source needs a...
TGCheckButton * fApplyToAll
TGTextButton * fNextButton
TGVerticalFrame * fLeftFrame
static EVerbosity fVerboseLevel
Changes verbosity from kQuiet to kAll.
TGGroupFrame * fSettingsFrame
TGHorizontalFrame * fBottomFrame
static int fFitRange
range in sigma used for fitting peaks (peak position - range to peas position + range)
void InitialCalibration(const bool &force)
void Status(const char *status, int position)
void ReplacePeak(const size_t &index, const double &channel)
const char * fSourceName
TGraphErrors * fFwhm
bool Good(TGauss *peak)
TRootEmbeddedCanvas * fProjectionCanvas
std::vector< std::pair< double, double > > fRegions
void RemovePoint(Int_t oldPoint)
TChannelTab * fChannelTab
void ProjectionStatus(Event_t *event)
TSourceTab(TSourceCalibration *sourceCal, TChannelTab *channel, TGCompositeFrame *frame, GH1D *projection, const char *sourceName, std::vector< std::tuple< double, double, double, double > > sourceEnergy)
TGCompositeFrame * fSourceFrame
TGraphErrors * fData
std::vector< TPeakFitter * > fBadFits
all bad fits (centroid err > 10%, area err > 100%, or either of them not a number at all)
std::vector< std::tuple< double, double, double, double > > fSourceEnergy
gamma rays from the source, with their energies, uncertainties in the energies, intensities,...
std::vector< TPeakFitter * > fFits
all good fits
void Add(std::map< double, std::tuple< double, double, double, double > > map)
TSourceCalibration * fSourceCalibration
void PrintLayout() const
std::vector< TGauss * > fPeaks
all peaks that have been fitted and are good
void FindCalibratedPeaks(const TF1 *calibration)
TGStatusBar * fSourceStatusBar
void PrintCanvases() const
std::map< TGauss *, std::tuple< double, double, double, double > > SmartMatch(std::vector< TGauss * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
std::map< TGauss *, std::tuple< double, double, double, double > > Match(std::vector< TGauss * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
bool FilledBin(TH2 *matrix, const int &bin)
std::map< double, std::tuple< double, double, double, double > > RoughCal(std::vector< double > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
double Polynomial(double *x, double *par)