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
13#include "TSystem.h"
14#include "TGTableLayout.h"
15#include "TCanvas.h"
16#include "TLinearFitter.h"
17#include "TF1.h"
18#include "TSpectrum.h"
19#include "TPolyMarker.h"
20#include "TObject.h"
21#include "TFrame.h"
22#include "TVirtualX.h"
23
24#include "TChannel.h"
25#include "GRootCommands.h"
26#include "combinations.h"
27#include "Globals.h"
28
29std::map<GPeak*, std::tuple<double, double, double, double>> Match(std::vector<GPeak*> peaks, std::vector<std::tuple<double, double, double, double>> sources, TSourceTab* sourceTab)
30{
31 /// This function tries to match a list of found peaks (channels) to a list of provided peaks (energies).
32 /// 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.
33
34 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << "Matching " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl; }
35
36 std::map<GPeak*, std::tuple<double, double, double, double>> result;
37 std::sort(peaks.begin(), peaks.end());
38 std::sort(sources.begin(), sources.end());
39
40 auto maxSize = peaks.size();
41 if(sources.size() > maxSize) { maxSize = sources.size(); }
42
43 // Peaks are the fitted points.
44 // source are the known values
45
46 TLinearFitter fitter(1, "1 ++ x");
47
48 // intermediate vectors and map
49 std::vector<double> peakValues(peaks.size());
50 for(size_t i = 0; i < peaks.size(); ++i) { peakValues[i] = peaks[i]->Centroid(); }
51 std::vector<double> sourceValues(sources.size());
52 for(size_t i = 0; i < sources.size(); ++i) { sourceValues[i] = std::get<0>(sources[i]); }
53 std::map<double, double> tmpMap;
54
55 for(size_t num_data_points = peakValues.size(); num_data_points > 0; num_data_points--) {
56 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << num_data_points << " data points:" << std::endl; }
57 double best_chi2 = DBL_MAX;
58 int iteration = 0;
59 for(auto peak_values : combinations(peakValues, num_data_points)) {
60 // Add a (0,0) point to the calibration.
61 peak_values.push_back(0);
62 for(auto source_values : combinations(sourceValues, num_data_points)) {
63 source_values.push_back(0);
64
65 if(peakValues.size() > 3) {
66 double pratio = peak_values.front() / peak_values.at(peak_values.size() - 2);
67 double sratio = source_values.front() / source_values.at(source_values.size() - 2);
68 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "ratio: " << pratio << " - " << sratio << " = " << std::abs(pratio - sratio) << " "; }
69 if(std::abs(pratio - sratio) > 0.02) {
70 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "skipping" << std::endl; }
71 continue;
72 }
73 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << std::endl; }
74 }
75
76 fitter.ClearPoints();
77 fitter.AssignData(source_values.size(), 1, peak_values.data(), source_values.data());
78 fitter.Eval();
79
80 if(fitter.GetChisquare() < best_chi2) {
81 tmpMap.clear();
82 for(size_t i = 0; i < num_data_points; i++) {
83 tmpMap[peak_values[i]] = source_values[i];
84 }
85 best_chi2 = fitter.GetChisquare();
86 }
87 }
88 sourceTab->Status(Form("%zu/%zu - %zu - %d", num_data_points, peakValues.size(), maxSize, iteration), 1);
89 ++iteration;
90 if(iteration >= TSourceCalibration::MaxIterations()) { break; }
91 }
92
93 // Remove one peak value from the best fit, make sure that we reproduce (0,0) intercept.
94 if(tmpMap.size() > 2) {
95 std::vector<double> peak_values;
96 std::vector<double> source_values;
97 for(auto& item : tmpMap) {
98 peak_values.push_back(item.first);
99 source_values.push_back(item.second);
100 }
101
102 for(size_t skipped_point = 0; skipped_point < source_values.size(); skipped_point++) {
103 std::swap(peak_values[skipped_point], peak_values.back());
104 std::swap(source_values[skipped_point], source_values.back());
105
106 fitter.ClearPoints();
107 fitter.AssignData(source_values.size() - 1, 1, peak_values.data(), source_values.data());
108 fitter.Eval();
109
110 if(std::abs(fitter.GetParameter(0)) > 10) {
112 std::cout << fitter.GetParameter(0) << " too big, clearing map with " << tmpMap.size() << " points: ";
113 for(auto iter : tmpMap) { std::cout << iter.first << " - " << iter.second << "; "; }
114 std::cout << std::endl;
115 }
116 tmpMap.clear();
117 break;
118 }
119
120 std::swap(peak_values[skipped_point], peak_values.back());
121 std::swap(source_values[skipped_point], source_values.back());
122 }
123 }
124
125 // copy all values from the vectors to the result map
126 if(!tmpMap.empty()) {
127 for(auto iter : tmpMap) {
128 result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](auto& item) { return iter.first == item->Centroid(); }))] =
129 *(std::find_if(sources.begin(), sources.end(), [&iter](auto& item) { return iter.second == std::get<0>(item); }));
130 // apparently c++14 is needed to use auto in a lambda so for now we spell it out
131 //result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](std::tuple<double, double, double, double>& item) { return iter.first == std::get<0>(item); }))] =
132 // *(std::find_if(sources.begin(), sources.end(), [&iter](std::tuple<double, double, double, double>& item) { return iter.second == std::get<0>(item); }));
133 }
135 std::cout << "Matched " << num_data_points << " data points from " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl;
136 std::cout << "Returning map with " << result.size() << " points: ";
137 for(auto iter : result) { std::cout << iter.first->Centroid() << " - " << std::get<0>(iter.second) << "; "; }
138 std::cout << std::endl;
139 }
140 break;
141 }
142 sourceTab->Status(Form("%zu/%zu - %zu", num_data_points, peakValues.size(), maxSize), 1);
143 }
144
145 return result;
146}
147
148std::map<GPeak*, std::tuple<double, double, double, double>> SmartMatch(std::vector<GPeak*> peaks, std::vector<std::tuple<double, double, double, double>> sources, TSourceTab* sourceTab)
149{
150 /// This function tries to match a list of found peaks (channels) to a list of provided peaks (energies).
151 /// It does so in slightly smarter way than the brute force method `Match`, by taking the reported intensity of the source peaks into account.
152
153 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << "\"Smart\" matching " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl; }
155 for(size_t i = 0; i < peaks.size() || i < sources.size(); ++i) {
156 std::cout << i << ".: " << std::setw(8);
157 if(i < peaks.size()) {
158 std::cout << peaks[i]->Centroid();
159 } else {
160 std::cout << " ";
161 }
162 std::cout << " - " << std::setw(8);
163 if(i < sources.size()) {
164 std::cout << std::get<0>(sources[i]);
165 } else {
166 std::cout << " ";
167 }
168 std::cout << std::endl;
169 }
170 }
171
172 std::map<GPeak*, std::tuple<double, double, double, double>> result;
173 std::sort(peaks.begin(), peaks.end(), [](const GPeak* a, const GPeak* b) { return a->Centroid() < b->Centroid(); });
174 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); });
175
176 auto maxSize = peaks.size();
177 if(sources.size() > maxSize) { maxSize = sources.size(); }
178
179 // Peaks are the fitted points.
180 // source are the known values
181
182 TLinearFitter fitter(1, "1 ++ x");
183
184 // intermediate vectors and map
185 std::vector<double> peakValues(peaks.size());
186 for(size_t i = 0; i < peaks.size(); ++i) { peakValues[i] = peaks[i]->Centroid(); }
187 std::vector<double> sourceValues(sources.size());
188 std::map<double, double> tmpMap;
189
190 for(size_t num_data_points = std::min(peakValues.size(), sourceValues.size()); num_data_points > 0; num_data_points--) {
191 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << num_data_points << " data points:" << std::endl; }
192 double best_chi2 = DBL_MAX;
193 int iteration = 0;
194 for(auto peak_values : combinations(peakValues, num_data_points)) {
195 // Add a (0,0) point to the calibration.
196 peak_values.push_back(0);
197 // instead of going through all possible combinations of the peaks with the source energies
198 // we pick the num_data_points most intense lines and try them
199 // we don't do the same with the peaks as there might be an intense background peak in the data (511 etc.)
200 sourceValues.resize(num_data_points);
201 for(size_t i = 0; i < sourceValues.size(); ++i) { sourceValues[i] = std::get<0>(sources[i]); }
202 std::sort(sourceValues.begin(), sourceValues.end());
203 sourceValues.push_back(0);
204
206 for(size_t i = 0; i < peak_values.size(); ++i) {
207 std::cout << i << ".: " << std::setw(8) << peak_values[i] << " - " << std::setw(8) << sourceValues[i] << std::endl;
208 }
209 }
210 if(peakValues.size() > 3) {
211 double pratio = peak_values.front() / peak_values.at(peak_values.size() - 2);
212 double sratio = sourceValues.front() / sourceValues.at(sourceValues.size() - 2);
213 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "ratio: " << pratio << " - " << sratio << " = " << std::abs(pratio - sratio) << std::endl; }
214 if(std::abs(pratio - sratio) > 0.02) {
215 if(TSourceCalibration::VerboseLevel() > EVerbosity::kAll) { std::cout << "skipping" << std::endl; }
216 continue;
217 }
218 }
219
220 fitter.ClearPoints();
221 fitter.AssignData(sourceValues.size(), 1, peak_values.data(), sourceValues.data());
222 fitter.Eval();
223
224 if(fitter.GetChisquare() < best_chi2) {
225 tmpMap.clear();
226 for(size_t i = 0; i < num_data_points; i++) {
227 tmpMap[peak_values[i]] = sourceValues[i];
228 }
229 best_chi2 = fitter.GetChisquare();
230 }
231 sourceTab->Status(Form("%zu/%zu - %zu - %d", num_data_points, peakValues.size(), maxSize, iteration), 1);
232 ++iteration;
233 if(iteration >= TSourceCalibration::MaxIterations()) { break; }
234 }
235
236 // Remove one peak value from the best fit, make sure that we reproduce (0,0) intercept.
237 if(tmpMap.size() > 2) {
238 std::vector<double> peak_values;
239 std::vector<double> source_values;
240 for(auto& item : tmpMap) {
241 peak_values.push_back(item.first);
242 source_values.push_back(item.second);
243 }
244
245 for(size_t skipped_point = 0; skipped_point < source_values.size(); skipped_point++) {
246 std::swap(peak_values[skipped_point], peak_values.back());
247 std::swap(source_values[skipped_point], source_values.back());
248
249 fitter.ClearPoints();
250 fitter.AssignData(source_values.size() - 1, 1, peak_values.data(), source_values.data());
251 fitter.Eval();
252
253 if(std::abs(fitter.GetParameter(0)) > 10) {
255 std::cout << fitter.GetParameter(0) << " too big an offset, clearing map with " << tmpMap.size() << " points: ";
256 for(auto iter : tmpMap) { std::cout << iter.first << " - " << iter.second << "; "; }
257 std::cout << std::endl;
258 }
259 tmpMap.clear();
260 break;
261 }
262
263 std::swap(peak_values[skipped_point], peak_values.back());
264 std::swap(source_values[skipped_point], source_values.back());
265 }
266 }
267
268 // copy all values from the vectors to the result map
269 if(!tmpMap.empty()) {
270 // apparently c++14 is needed to use auto in a lambda so for now we spell it out
271 // for(auto it : tmpMap) result[*(std::find_if(peaks.begin(), peaks.end(), [&it] (auto& item) { return it.first == std::get<0>(item); }))] =
272 // *(std::find_if(sources.begin(), sources.end(), [&it] (auto& item) { return it.second == std::get<0>(item); }));
273 for(auto iter : tmpMap) {
274 result[*(std::find_if(peaks.begin(), peaks.end(), [&iter](auto& item) { return iter.first == item->Centroid(); }))] =
275 *(std::find_if(sources.begin(), sources.end(), [&iter](auto& item) { return iter.second == std::get<0>(item); }));
276 }
278 std::cout << "Smart matched " << num_data_points << " data points from " << peaks.size() << " peaks with " << sources.size() << " source energies" << std::endl;
279 std::cout << "Returning map with " << result.size() << " points: ";
280 for(auto iter : result) { std::cout << iter.first->Centroid() << " - " << std::get<0>(iter.second) << "; "; }
281 std::cout << std::endl;
282 }
283 break;
284 }
285 sourceTab->Status(Form("%zu/%zu - %zu", num_data_points, peakValues.size(), maxSize), 1);
286 }
287
288 return result;
289}
290
291double Polynomial(double* x, double* par) // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay, readability-non-const-parameter)
292{
293 double result = par[1];
294 for(int i = 1; i <= par[0]; ++i) {
295 result += par[i + 1] * TMath::Power(x[0], i);
296 }
297 return result;
298}
299
300bool FilledBin(TH2* matrix, const int& bin)
301{
302 return (matrix->Integral(bin, bin, 1, matrix->GetNbinsY()) > 1000);
303}
304
305//////////////////////////////////////// TSourceTab ////////////////////////////////////////
306TSourceTab::TSourceTab(TChannelTab* parent, TGCompositeFrame* frame, GH1D* projection, const double& sigma, const double& threshold, const double& peakRatio, std::vector<std::tuple<double, double, double, double>> sourceEnergy)
307 : fParent(parent), fSourceFrame(frame), fProjection(projection), fSigma(sigma), fThreshold(threshold), fPeakRatio(peakRatio), fSourceEnergy(std::move(sourceEnergy))
308{
311}
312
314{
315 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)
316 fParent = rhs.fParent;
320
322 fData = rhs.fData;
323 fFwhm = rhs.fFwhm;
324 fSigma = rhs.fSigma;
327 fPeaks.clear();
328}
329
331{
332 for(auto* peak : fPeaks) {
333 delete peak;
334 }
335 fPeaks.clear();
336 delete fProjectionCanvas;
337 delete fSourceStatusBar;
338}
339
341{
342 // frame with canvas and status bar
343 fProjectionCanvas = new TRootEmbeddedCanvas(Form("ProjectionCanvas%s", fProjection->GetName()), fSourceFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
344
345 fSourceFrame->AddFrame(fProjectionCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
346
348 std::array<int, 3> parts = {35, 50, 15};
349 fSourceStatusBar->SetParts(parts.data(), parts.size());
350
351 fSourceFrame->AddFrame(fSourceStatusBar, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
352}
353
355{
356 fProjectionCanvas->Connect("ProcessedEvent(Event_t*)", "TSourceTab", this, "ProjectionStatus(Event_t*)");
357 fProjectionCanvas->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", "TSourceTab", this, "ProjectionStatus(Int_t,Int_t,Int_t,TObject*)");
358}
359
361{
362 fProjectionCanvas->Disconnect("ProcessedEvent(Event_t*)", this, "ProjectionStatus(Event_t*)");
363 fProjectionCanvas->GetCanvas()->Disconnect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", this, "ProjectionStatus(Int_t,Int_t,Int_t,TObject*)");
364}
365
367{
368 // enum EGEventType {
369 // kGKeyPress = 0, kKeyRelease, = 1, kButtonPress = 2, kButtonRelease = 3,
370 // kMotionNotify = 4, kEnterNotify = 5, kLeaveNotify = 6, kFocusIn = 7, kFocusOut = 8,
371 // kExpose = 9, kConfigureNotify = 10, kMapNotify = 11, kUnmapNotify = 12, kDestroyNotify = 13,
372 // kClientMessage = 14, kSelectionClear = 15, kSelectionRequest = 16, kSelectionNotify = 17,
373 // kColormapNotify = 18, kButtonDoubleClick = 19, kOtherEvent = 20
374 // };
376 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)
377 if(event->fType == kClientMessage) {
378 std::cout << "Client message: " << event->fUser[0] << ", " << event->fUser[1] << ", " << event->fUser[2] << std::endl;
379 }
380 }
381 if(event->fType == kEnterNotify) {
383 std::cout << "Entered source tab => changing gPad from " << gPad->GetName();
384 }
385 gPad = fProjectionCanvas->GetCanvas();
387 std::cout << " to " << gPad->GetName() << std::endl;
388 }
389 }
390}
391
392void TSourceTab::ProjectionStatus(Int_t event, Int_t px, Int_t py, TObject* selected)
393{
394 // 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
395 // kButton1 = left mouse button, kButton2 = right mouse button
396 // enum EEventType {
397 // kNoEvent = 0,
398 // kButton1Down = 1, kButton2Down = 2, kButton3Down = 3, kKeyDown = 4,
399 // kWheelUp = 5, kWheelDown = 6, kButton1Shift = 7, kButton1ShiftMotion = 8,
400 // kButton1Up = 11, kButton2Up = 12, kButton3Up = 13, kKeyUp = 14,
401 // kButton1Motion = 21, kButton2Motion = 22, kButton3Motion = 23, kKeyPress = 24,
402 // kArrowKeyPress = 25, kArrowKeyRelease = 26,
403 // kButton1Locate = 41, kButton2Locate = 42, kButton3Locate = 43, kESC = 27,
404 // kMouseMotion = 51, kMouseEnter = 52, kMouseLeave = 53,
405 // kButton1Double = 61, kButton2Double = 62, kButton3Double = 63
406 //};
407 Status(selected->GetName(), 0);
408 Status(selected->GetObjectInfo(px, py), 1);
409 //auto key = static_cast<char>(px);
410 switch(event) {
411 case kButton1Down:
412 if(selected == fProjection) {
413 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; }
414 auto* polym = static_cast<TPolyMarker*>(fProjection->GetListOfFunctions()->FindObject("TPolyMarker"));
415 if(polym == nullptr) {
416 std::cerr << "No peaks defined yet?" << std::endl;
417 return;
418 }
419 polym->SetNextPoint(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px), fProjectionCanvas->GetCanvas()->AbsPixeltoX(py));
420 double range = 4 * fSigma; // * fProjection->GetXaxis()->GetBinWidth(1);
421 GPeak* peak = PhotoPeakFit(fProjection, fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) - range, fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) + range, "qretryfit");
422 //fPeakFitter.SetRange(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) - range, fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) + range);
423 //auto peak = new TRWPeak(fProjectionCanvas->GetCanvas()->AbsPixeltoX(px));
424 //fPeakFitter.AddPeak(peak);
425 //fPeakFitter.Fit(fProjection, "qretryfit");
426 if(peak->Area() > 0) {
427 fPeaks.push_back(peak);
428 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "Fitted peak " << fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) - range << " - " << fProjectionCanvas->GetCanvas()->AbsPixeltoX(px) + range << " -> centroid " << peak->Centroid() << std::endl; }
429 } else {
430 std::cout << "Ignoring peak at " << peak->Centroid() << " with negative area " << peak->Area() << std::endl;
431 }
432 fProjection->GetListOfFunctions()->Remove(peak);
433 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)
434
435 //// redo matching of found peaks to source energies
436 //std::vector<std::tuple<double, double, double, double>> peaks;
437 //for(auto* foundPeak : fPeaks) {
438 // peaks.emplace_back(foundPeak->Centroid(), foundPeak->CentroidErr(), foundPeak->Area(), foundPeak->AreaErr());
439 //}
440
441 auto map = Match(fPeaks, fSourceEnergy, this);
442 Add(map);
443
444 // update status
445 Status(Form("%d/%d", static_cast<int>(fData->GetN()), static_cast<int>(fPeaks.size())), 2);
446
447 // update data and re-calibrate
451 }
452 break;
453 case kArrowKeyPress:
454 //Move1DHistogram(px, fProjection);
455 //fProjectionCanvas->GetCanvas()->Modified();
456 case kArrowKeyRelease:
457 break;
458 case kKeyDown:
459 case kKeyPress:
460 //switch(key) {
461 // case 'u':
462 // fProjection->GetXaxis()->UnZoom();
463 // fProjection->GetYaxis()->UnZoom();
464 // break;
465 // case 'U':
466 // fProjection->GetYaxis()->UnZoom();
467 // break;
468 // default:
469 // std::cout << "Key press '" << key << "' not recognized!" << std::endl;
470 // break;
471 //}
472 case kKeyUp:
473 break;
474 case kButton1Motion:
475 break;
476 case kButton1Up:
477 break;
478 case kMouseMotion:
479 break;
480 case kMouseEnter:
481 break;
482 case kMouseLeave:
483 break;
484 default:
486 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;
487 }
488 break;
489 }
490}
491
493{
494 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "UpdateRegions: clearing " << fRegions.size() << " regions" << std::endl; }
495 fRegions.clear();
497 std::cout << "projection has " << fProjection->ListOfRegions()->GetEntries() << " regions:" << std::endl;
498 fProjection->ListOfRegions()->Print();
499 }
501 for(auto* obj : *(fProjection->ListOfRegions())) {
502 if(obj->InheritsFrom(TRegion::Class())) {
503 auto* region = static_cast<TRegion*>(obj);
504 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; }
505 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { region->Print(); }
506 fRegions.emplace_back(std::min(region->GetX1(), region->GetX2()), std::max(region->GetX1(), region->GetX2()));
508 std::cout << obj->GetName() << " is not a TRegion but a " << obj->ClassName() << std::endl;
509 }
510 }
511}
512
513void TSourceTab::FindPeaks(const double& sigma, const double& threshold, const double& peakRatio, const bool& force, const bool& fast)
514{
515 /// This functions finds the peaks in the histogram, fits them, and adds the fits to the list of peaks.
516 /// This list is then used to find all peaks that lie on a straight line.
517
518 if(fPeakRatio != peakRatio) {
519 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DCYAN << __PRETTY_FUNCTION__ << ": updating peak ratio from " << fPeakRatio << " to " << peakRatio << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
520 fPeakRatio = peakRatio;
521 }
522 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)
523
524 if(fPeaks.empty() || fData == nullptr || sigma != fSigma || threshold != fThreshold || force) {
526 std::cout << __PRETTY_FUNCTION__ << ": # peaks " << fPeaks.size() << ", sigma (" << sigma << "/" << fSigma << "), or threshold (" << threshold << "/" << fThreshold << ") have changed?" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
527 }
528 fSigma = sigma;
529 fThreshold = threshold;
530 fPeaks.clear();
531 // Remove all associated functions from projection.
532 // These are the poly markers, fits, and the pave stat
534 TList* functions = fProjection->GetListOfFunctions();
535 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetName() << " before clearing" << std::endl;
536 for(auto* obj : *functions) {
537 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
538 }
539 }
540 fProjection->GetListOfFunctions()->Clear();
541 TSpectrum spectrum;
542 int nofPeaks = 0;
543 std::vector<double> peakPos;
545 if(fRegions.empty()) {
546 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "No regions found, using whole spectrum" << std::endl; }
547 spectrum.Search(fProjection, fSigma, "", fThreshold);
548 nofPeaks = spectrum.GetNPeaks();
549 if(nofPeaks > fPeakRatio * static_cast<double>(fSourceEnergy.size())) {
550 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Reducing # of peaks from " << nofPeaks; }
551 nofPeaks = static_cast<int>(fPeakRatio * static_cast<double>(fSourceEnergy.size()));
552 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << " to " << nofPeaks << std::endl; }
553 }
554 peakPos.insert(peakPos.end(), spectrum.GetPositionX(), spectrum.GetPositionX() + nofPeaks);
555 } else {
556 for(auto& region : fRegions) {
557 fProjection->GetXaxis()->SetRangeUser(region.first, region.second);
558 spectrum.Search(fProjection, fSigma, "", fThreshold);
559 int tmpNofPeaks = spectrum.GetNPeaks();
560 if(tmpNofPeaks > fPeakRatio * static_cast<double>(fSourceEnergy.size())) {
561 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Reducing # of peaks from " << tmpNofPeaks; }
562 tmpNofPeaks = static_cast<int>(fPeakRatio * static_cast<double>(fSourceEnergy.size()));
563 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << " to " << tmpNofPeaks << std::endl; }
564 }
565 peakPos.insert(peakPos.end(), spectrum.GetPositionX(), spectrum.GetPositionX() + tmpNofPeaks);
566 nofPeaks += spectrum.GetNPeaks();
567 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)
568 }
569 fProjection->GetXaxis()->UnZoom();
570 }
571 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)
572 //fPeakFitter.RemoveAllPeaks();
573 for(int i = 0; i < nofPeaks; i++) {
574 double range = 4 * fSigma * fProjection->GetXaxis()->GetBinWidth(1);
575 // 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
576 GPeak* peak = nullptr;
577 if(TSourceCalibration::LogFile().empty()) {
578 TRedirect redirect("/dev/null");
579 peak = PhotoPeakFit(fProjection, peakPos[i] - range, peakPos[i] + range, "qretryfit");
580 } else {
581 peak = PhotoPeakFit(fProjection, peakPos[i] - range, peakPos[i] + range, "retryfit");
582 }
583 if(peak->Area() > 0) {
584 fPeaks.push_back(peak);
586 std::cout << "Fitted peak " << peakPos[i] - range << " - " << peakPos[i] + range << " -> centroid " << peak->Centroid() << ", area " << peak->Area() << std::endl;
587 }
589 std::cout << "Ignoring peak at " << peak->Centroid() << " with negative area " << peak->Area() << std::endl;
590 }
591 //fProjection->GetListOfFunctions()->Remove(peak);
592 }
593 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": added " << fPeaks.size() << " peaks" << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
594 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)
595
596 fProjectionCanvas->GetCanvas()->cd();
597 fProjection->Draw(); // seems like hist + samefunc does not work. Could use hist + loop over list of functions (with same)
598
599 //// get a list of peaks positions and areas
600 //std::vector<std::tuple<double, double, double, double>> peaks;
601 //for(auto* peak : fPeaks) {
602 // peaks.emplace_back(peak->Centroid(), peak->CentroidErr(), peak->Area(), peak->AreaErr());
603 //}
604
605 if(fast) {
606 auto map = SmartMatch(fPeaks, fSourceEnergy, this);
607 Add(map);
608 } else {
609 auto map = Match(fPeaks, fSourceEnergy, this);
610 Add(map);
611 }
612
613 // update status
614 Status(Form("%d/%d", static_cast<int>(fData->GetN()), nofPeaks), 2);
615 }
617 std::cout << __PRETTY_FUNCTION__ << ": found " << fData->GetN() << " peaks"; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
618 Double_t* x = fData->GetX();
619 Double_t* y = fData->GetY();
620 for(int i = 0; i < fData->GetN(); ++i) {
621 std::cout << " - " << x[i] << ", " << y[i];
622 }
623 std::cout << std::endl;
624 }
625}
626
627void TSourceTab::Add(std::map<GPeak*, std::tuple<double, double, double, double>> map)
628{
630 std::cout << DCYAN << "Adding map with " << map.size() << " data points, fData = " << fData << std::endl;
631 }
632 if(fData == nullptr) {
633 fData = new TGraphErrors(map.size());
634 } else {
635 fData->Set(map.size());
636 }
637 fData->SetLineColor(2);
638 fData->SetMarkerColor(2);
639 if(fFwhm == nullptr) {
640 fFwhm = new TGraphErrors(map.size());
641 } else {
642 fFwhm->Set(map.size());
643 }
644 fFwhm->SetLineColor(4);
645 fFwhm->SetMarkerColor(4);
646 int i = 0;
647 for(auto iter = map.begin(); iter != map.end();) {
648 // more readable variable names
649 auto peakPos = iter->first->Centroid();
650 auto peakPosErr = iter->first->CentroidErr();
651 auto peakArea = iter->first->Area();
652 auto peakAreaErr = iter->first->AreaErr();
653 auto fwhm = iter->first->FWHM();
654 auto fwhmErr = iter->first->FWHMErr();
655 auto energy = std::get<0>(iter->second);
656 auto energyErr = std::get<1>(iter->second);
657 // drop this peak if the uncertainties in area or position are too large
658 if(peakPosErr > 0.1 * peakPos || peakAreaErr > peakArea || std::isnan(peakPosErr) || std::isnan(peakAreaErr)) {
660 std::cout << "Dropping peak with position " << peakPos << " +- " << peakPosErr << ", area " << peakArea << " +- " << peakAreaErr << ", energy " << energy << std::endl;
661 }
662 iter = map.erase(iter);
664 std::cout << "Erasing peak returned iterator " << std::distance(map.begin(), iter) << std::endl;
665 }
666 } else {
667 fData->SetPoint(i, peakPos, energy);
668 fData->SetPointError(i, peakPosErr, energyErr);
669 fFwhm->SetPoint(i, peakPos, fwhm);
670 fFwhm->SetPointError(i, peakPosErr, fwhmErr);
672 std::cout << "Using peak with position " << peakPos << " +- " << peakPosErr << ", area " << peakArea << " +- " << peakAreaErr << ", energy " << energy << std::endl;
673 }
674 ++iter;
675 ++i;
676 }
677 }
679 std::cout << "Accepted " << map.size() << " peaks" << std::endl;
680 }
681 // if we dropped a peak, we need to resize the graph, if we haven't dropped any this doesn't do anything
682 fData->Set(map.size());
683 fFwhm->Set(map.size());
684 // split poly marker into those peaks that were used, and those that weren't
685 TList* functions = fProjection->GetListOfFunctions();
687 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetTitle() << std::endl;
688 for(auto* obj : *functions) {
689 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
690 }
691 }
692 auto* polym = static_cast<TPolyMarker*>(functions->FindObject("TPolyMarker"));
693 if(polym != nullptr) {
694 double* oldX = polym->GetX();
695 double* oldY = polym->GetY();
696 int size = polym->GetN();
697 std::vector<double> newX;
698 std::vector<double> newY;
699 std::vector<double> unusedX;
700 std::vector<double> unusedY;
701 for(i = 0; i < size; ++i) {
702 bool used = false;
703 for(auto point : map) {
704 if(TMath::Abs(oldX[i] - point.first->Centroid()) < fSigma) {
705 newX.push_back(oldX[i]);
706 newY.push_back(oldY[i]);
707 used = true;
708 break;
709 }
710 }
711 if(!used) {
712 unusedX.push_back(oldX[i]);
713 unusedY.push_back(oldY[i]);
714 }
715 }
716 polym->SetPolyMarker(newX.size(), newX.data(), newY.data());
717 auto* unusedMarkers = new TPolyMarker(unusedX.size(), unusedX.data(), unusedY.data());
718 unusedMarkers->SetMarkerStyle(23); // triangle down
719 unusedMarkers->SetMarkerColor(16); // light grey
720 functions->Add(unusedMarkers);
722 std::cout << fProjection->GetTitle() << ": " << size << " peaks founds total, " << polym->GetN() << " used, and " << unusedMarkers->GetN() << " unused" << std::endl;
724 std::cout << "Used: ";
725 for(size_t m = 0; m < newX.size(); ++m) { std::cout << newX[m] << " - " << newY[m] << ";"; }
726 std::cout << std::endl;
727 std::cout << "Unused: ";
728 for(size_t m = 0; m < unusedX.size(); ++m) { std::cout << unusedX[m] << " - " << unusedY[m] << ";"; }
729 std::cout << std::endl;
730 }
732 std::cout << functions->GetEntries() << " functions in projection " << fProjection->GetTitle() << std::endl;
733 for(auto* obj : *functions) {
734 std::cout << obj->GetName() << "/" << obj->GetTitle() << " - " << obj->ClassName() << std::endl;
735 }
736 }
737 }
738 }
739
740 // remove fit functions for unused peaks
741 TIter iter(functions);
742 TObject* item = nullptr;
743 while((item = iter.Next()) != nullptr) {
744 if(item->IsA() == TF1::Class() || item->IsA() == GPeak::Class()) { // if the item is a TF1 or GPeak we see if we can find the centroid in the map of used peaks
745 double centroid = 0.;
746 if(item->IsA() == TF1::Class()) {
747 centroid = static_cast<TF1*>(item)->GetParameter(1);
748 } else {
749 centroid = static_cast<GPeak*>(item)->Centroid();
750 }
751 bool found = false;
752 for(auto point : map) {
753 if(TMath::Abs(centroid - point.first->Centroid()) < fSigma) {
754 found = true;
755 break;
756 }
757 }
758 // remove the TF1 if it wasn't found in the map
759 if(!found) {
760 functions->Remove(item);
761 }
762 }
763 }
764}
765
766void TSourceTab::RemovePoint(Int_t px, Int_t py)
767{
769 std::cout << DCYAN << __PRETTY_FUNCTION__ << ": px " << px << ", py " << py << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
770 }
771 // we removed a point of the calibration graph at px, py, where px is the channel and py is the energy
772 // so we try and find a peak with its centroid close to px
773 for(auto it = fPeaks.begin(); it != fPeaks.end(); ++it) {
774 if(TMath::Abs((*it)->Centroid() - px) < fSigma) {
776 std::cout << DCYAN << "Erasing peak " << TMath::Abs((*it)->Centroid() - px) << " < " << fSigma << std::endl;
777 (*it)->Print();
778 }
779 fPeaks.erase(it);
780 break;
781 }
782 }
783 // remove the fit function of this peak
784 for(auto* item : *(fProjection->GetListOfFunctions())) {
785 if(item->IsA() == TF1::Class() || item->IsA() == GPeak::Class()) { // if the item is a TF1 or GPeak we see if we can find the centroid in the map of used peaks
786 double centroid = 0.;
787 if(item->IsA() == TF1::Class()) {
788 centroid = static_cast<TF1*>(item)->GetParameter(1);
789 } else {
790 centroid = static_cast<GPeak*>(item)->Centroid();
791 }
792 if(TMath::Abs(centroid - px) < fSigma) {
794 std::cout << DCYAN << "Removing function " << TMath::Abs(centroid - px) << " < " << fSigma << std::endl;
795 item->Print();
796 }
797 fProjection->GetListOfFunctions()->Remove(item);
798 break;
799 }
800 }
801 // we also need to move the marker of this peak to the poly-marker of unused peaks (16 is light gray aka the color of the unused markers)
802 if(item->IsA() == TPolyMarker::Class()) { item->Print(); } // && static_cast<TPolyMarker*>(item)->GetMarkerColor() != 16) {
803 //auto* polym = static_cast<TPolyMarker*>(item);
804 //double* oldX = polym->GetX();
805 //double* oldY = polym->GetY();
806 //int size = polym->GetN();
807 //std::vector<double> newX;
808 //std::vector<double> newY;
809 //std::vector<double> unusedX;
810 //std::vector<double> unusedY;
811 //for(i = 0; i < size; ++i) {
812 // if(TMath::Abs(oldX[i] - px) < fSigma) {
813 // unusedX.push_back(oldX[i]);
814 // unusedY.push_back(oldY[i]);
815 // } else {
816 // newX.push_back(oldX[i]);
817 // newY.push_back(oldY[i]);
818 // }
819 //}
820 //polym->SetPolyMarker(newX.size(), newX.data(), newY.data());
821 //auto* unusedMarkers = new TPolyMarker(unusedX.size(), unusedX.data(), unusedY.data());
822 //unusedMarkers->SetMarkerStyle(23); // triangle down
823 //unusedMarkers->SetMarkerColor(16); // light grey
824 //functions->Add(unusedMarkers);
825 //}
826 }
827}
828
829void TSourceTab::RemoveResidualPoint(Int_t px, Int_t py)
830{
832 std::cout << DCYAN << __PRETTY_FUNCTION__ << ": px " << px << ", py " << py << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
833 }
834 // we removed a point of the residual graph at px, py, where px is the residual and py is the energy
835}
836
837void TSourceTab::Status(const char* status, int position)
838{
839 fSourceStatusBar->SetText(status, position);
840 fSourceStatusBar->MapWindow();
841 fSourceFrame->MapSubwindows();
842 gVirtualX->Update();
843}
844
846{
847 std::cout << "TSourceTab frame:" << std::endl;
848 fSourceFrame->Print();
849 std::cout << "TSourceTab canvas:" << std::endl;
850 fProjectionCanvas->Print();
851 std::cout << "TSourceTab status bar:" << std::endl;
852 fSourceStatusBar->Print();
853}
854
855//////////////////////////////////////// TChannelTab ////////////////////////////////////////
856TChannelTab::TChannelTab(TSourceCalibration* parent, std::vector<TNucleus*> nuclei, std::vector<GH1D*> projections, std::string name, TGCompositeFrame* frame, double sigma, double threshold, int degree, std::vector<std::vector<std::tuple<double, double, double, double>>> sourceEnergies, TGHProgressBar* progressBar)
857 : 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), fParent(parent), fNuclei(std::move(nuclei)), fProjections(std::move(projections)), fName(std::move(name)), fSigma(sigma), fThreshold(threshold), fDegree(degree), fSourceEnergies(std::move(sourceEnergies))
858{
860 std::cout << DYELLOW << "========================================" << std::endl;
861 std::cout << __PRETTY_FUNCTION__ << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
862 << " name = " << fName << ", fData = " << fData << std::endl;
863 std::cout << "========================================" << std::endl;
864 }
865
866 fChannelFrame->SetLayoutManager(new TGHorizontalLayout(fChannelFrame));
867
868 fSources.resize(fNuclei.size(), nullptr);
869 //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)
870 for(size_t source = 0; source < fNuclei.size(); ++source) {
871 CreateSourceTab(source);
872 }
873
874 for(auto iter = fSources.begin(); iter != fSources.end(); ++iter) {
875 if(*iter == nullptr) {
876 fSources.erase(iter--); // erase iterator and then go to the element before this one (and then the loop goes to the next one)
877 }
878 }
879
880 fChannelFrame->AddFrame(fSourceTab, new TGLayoutHints(kLHintsLeft | kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
881
882 fCalibrationFrame = fCanvasTab->AddTab("Calibration");
883 fCalibrationFrame->SetLayoutManager(new TGVerticalLayout(fCalibrationFrame));
884 fCalibrationCanvas = new TRootEmbeddedCanvas("ChannelCalibrationCanvas", fCalibrationFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
885 fCalibrationFrame->AddFrame(fCalibrationCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
886 fChannelStatusBar = new TGStatusBar(fCalibrationFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::StatusbarHeight());
887 std::array<int, 3> parts = {25, 35, 40};
888 fChannelStatusBar->SetParts(parts.data(), parts.size());
889 fCalibrationFrame->AddFrame(fChannelStatusBar, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
890 fFwhmFrame = fCanvasTab->AddTab("FWHM");
891 fFwhmFrame->SetLayoutManager(new TGVerticalLayout(fFwhmFrame));
892 fFwhmCanvas = new TRootEmbeddedCanvas("ChannelFwhmCanvas", fFwhmFrame, TSourceCalibration::PanelWidth(), TSourceCalibration::PanelHeight());
893 fFwhmFrame->AddFrame(fFwhmCanvas, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
894 //fChannelFrame->AddFrame(fCalibrationFrame, new TGLayoutHints(kLHintsRight | kLHintsBottom | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
895 fChannelFrame->AddFrame(fCanvasTab, new TGLayoutHints(kLHintsRight | kLHintsTop | kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
896
897 UpdateData();
898
899 fCalibrationCanvas->GetCanvas()->cd();
900 fCalibrationPad = new TPad(Form("cal_%s", fName.c_str()), Form("calibration for %s", fName.c_str()), 0.2, 0., 1., 1.);
901 fCalibrationPad->SetNumber(1);
902 fCalibrationPad->Draw();
903 fCalibrationPad->AddExec("zoom", "TChannelTab::ZoomY()");
904
905 fLegend = new TLegend(0.8, 0.3, 0.95, 0.3 + static_cast<double>(fNuclei.size()) * 0.05); // x1, y1, x2, y2
906
907 fCalibrationCanvas->GetCanvas()->cd();
908 fResidualPad = new TPad(Form("res_%s", fName.c_str()), Form("residual for %s", fName.c_str()), 0.0, 0., 0.2, 1.);
909 fResidualPad->SetNumber(2);
910 fResidualPad->Draw();
911 fResidualPad->AddExec("zoom", "TChannelTab::ZoomY()");
912 Calibrate(); // also creates the residual and chi^2 label
913
914 // get the fwhm graphs and plot them
915 UpdateFwhm();
916 fFwhmCanvas->GetCanvas()->cd();
917 fFwhm->DrawCalibration("*");
918 fLegend->Draw();
919
921 std::cout << DYELLOW << "----------------------------------------" << std::endl;
922 std::cout << __PRETTY_FUNCTION__ << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
923 << " channel " << fName << " done" << std::endl;
924 std::cout << "----------------------------------------" << std::endl;
925 }
926}
927
929{
930 delete fSourceTab;
931 for(auto* channel : fSources) {
932 delete channel;
933 }
934 delete fCalibrationCanvas;
935 delete fChannelStatusBar;
936 delete fCalibrationFrame;
937 delete fData;
938 delete fFwhm;
939 delete fCalibrationPad;
940 delete fLegend;
941 delete fResidualPad;
942 delete fChi2Label;
943 delete fFwhmCanvas;
944 delete fFwhmFrame;
945 delete fCanvasTab;
946 delete fProgressBar;
947}
948
950{
951 if(fProjections[source]->GetEntries() > 1000) {
953 std::cout << DYELLOW << "Creating source tab " << source << ", using fParent " << fParent << std::flush;
954 if(fParent != nullptr) {
955 std::cout << ", peak ratio " << fParent->PeakRatio();
956 }
957 std::cout << std::endl;
958 }
959 fSources[source] = new TSourceTab(this, fSourceTab->AddTab(Form("%s_%s", fNuclei[source]->GetName(), fName.c_str())),
961 fProgressBar->Increment(1);
962 } else {
963 fSources[source] = nullptr;
965 std::cout << DYELLOW << "Skipping projection of source " << source << " = " << fProjections[source]->GetName() << ", only " << fProjections[source]->GetEntries() << " entries" << std::endl;
966 }
967 }
969 std::cout << __PRETTY_FUNCTION__ << " source " << source << " done" << std::endl; // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
970 }
971}
972
974{
975 fSourceTab->Connect("Selected(Int_t)", "TChannelTab", this, "SelectedTab(Int_t)");
976 fCalibrationCanvas->GetCanvas()->Connect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", "TChannelTab", this, "CalibrationStatus(Int_t,Int_t,Int_t,TObject*)");
977 fCalibrationCanvas->Connect("ProcessedEvent(Event_t*)", "TChannelTab", this, "SelectCanvas(Event_t*)");
978 for(auto& source : fSources) {
979 source->MakeConnections();
980 fData->Connect("RemovePoint(const Int_t&, const Int_t&)", "TSourceTab", source, "RemovePoint(const Int_t&, const Int_t&)");
981 fData->Connect("RemoveResidualPoint(const Int_t&, const Int_t&)", "TSourceTab", source, "RemoveResidualPoint(const Int_t&, const Int_t&)");
982 }
983}
984
986{
987 fSourceTab->Disconnect("Selected(Int_t)", this, "SelectedTab(Int_t)");
988 fCalibrationCanvas->GetCanvas()->Disconnect("ProcessedEvent(Int_t,Int_t,Int_t,TObject*)", this, "CalibrationStatus(Int_t,Int_t,Int_t,TObject*)");
989 for(auto& source : fSources) {
990 source->Disconnect();
991 fData->Disconnect("RemovePoint(const Int_t&, const Int_t&)", source, "RemovePoint(const Int_t&, const Int_t&)");
992 fData->Disconnect("RemoveResidualPoint(const Int_t&, const Int_t&)", source, "RemoveResidualPoint(const Int_t&, const Int_t&)");
993 }
994}
995
997{
998 /// Simple function that enables and disables the previous and next buttons depending on which tab was selected.
999 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)
1000 fActiveSourceTab = id;
1001 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << "active source tab " << fActiveSourceTab << RESET_COLOR << std::endl; }
1002 if(fSources[fActiveSourceTab] == nullptr) {
1003 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]" << RESET_COLOR << std::endl;
1004 return;
1005 }
1006 if(fSources[fActiveSourceTab]->ProjectionCanvas() == nullptr) {
1007 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]->ProjectionCanvas()" << RESET_COLOR << std::endl;
1008 return;
1009 }
1010 if(fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas() == nullptr) {
1011 std::cout << "Failed to get fSources[" << fActiveSourceTab << "]->ProjectionCanvas()->GetCanvas()" << RESET_COLOR << std::endl;
1012 return;
1013 }
1014 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "Changing gpad from \"" << gPad->GetName() << "\" to \""; }
1015 gPad = fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas();
1016 fSources[fActiveSourceTab]->ProjectionCanvas()->GetCanvas()->cd();
1017 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << gPad->GetName() << "\"" << RESET_COLOR << std::endl; }
1018}
1019
1020void TChannelTab::SelectCanvas(Event_t* event)
1021{
1022 if(event->fType == kEnterNotify) {
1024 std::cout << "Entered channel tab => changing gPad from " << gPad->GetName();
1025 }
1026 gPad = fCalibrationCanvas->GetCanvas();
1028 std::cout << " to " << gPad->GetName() << std::endl;
1029 }
1030 }
1031}
1032
1033void TChannelTab::CalibrationStatus(Int_t event, Int_t px, Int_t py, TObject* selected)
1034{
1035 // 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
1036 // kButton1 = left mouse button, kButton2 = right mouse button
1037 // enum EEventType {
1038 // kNoEvent = 0,
1039 // kButton1Down = 1, kButton2Down = 2, kButton3Down = 3, kKeyDown = 4,
1040 // kWheelUp = 5, kWheelDown = 6, kButton1Shift = 7, kButton1ShiftMotion = 8,
1041 // kButton1Up = 11, kButton2Up = 12, kButton3Up = 13, kKeyUp = 14,
1042 // kButton1Motion = 21, kButton2Motion = 22, kButton3Motion = 23, kKeyPress = 24,
1043 // kArrowKeyPress = 25, kArrowKeyRelease = 26,
1044 // kButton1Locate = 41, kButton2Locate = 42, kButton3Locate = 43, kESC = 27,
1045 // kMouseMotion = 51, kMouseEnter = 52, kMouseLeave = 53,
1046 // kButton1Double = 61, kButton2Double = 62, kButton3Double = 63
1047 //};
1048 fChannelStatusBar->SetText(selected->GetName(), 0);
1049 if(event == kKeyPress) {
1050 fChannelStatusBar->SetText(Form("%c", static_cast<char>(px)), 1);
1051 } else {
1052 auto* canvas = fCalibrationCanvas->GetCanvas();
1053 if(canvas == nullptr) {
1054 fChannelStatusBar->SetText("canvas nullptr");
1055 return;
1056 }
1057 auto* pad = canvas->GetSelectedPad();
1058 if(pad == nullptr) {
1059 fChannelStatusBar->SetText("pad nullptr");
1060 return;
1061 }
1062 //if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) {
1063 // 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;
1064 //}
1065
1066 fChannelStatusBar->SetText(Form("x = %f, y = %f", pad->AbsPixeltoX(px), pad->AbsPixeltoY(py)), 1);
1067 }
1068}
1069
1071{
1072 /// Copy data from all sources into one graph (which we use for the calib && source < fSources.size()ration).
1073 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)
1074 if(fData == nullptr) {
1075 fData = new TCalibrationGraphSet("ADC channel", "Energy [keV]");
1076 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)
1077 }
1078 fData->Clear();
1079
1080 fData->SetName(Form("data%s", fName.c_str()));
1082 std::cout << "set name of fData using " << fName.c_str() << ": " << fData->GetName() << std::endl;
1083 std::cout << "fData " << fData << ": " << (fData != nullptr ? fData->GetN() : -2) << " data points after creation" << std::endl;
1084 std::cout << "Looping over " << fNuclei.size() << " sources, fSources.size() = " << fSources.size() << std::endl;
1085 }
1086 for(size_t source = 0; source < fNuclei.size() && source < fSources.size(); ++source) {
1087 if(fSources[source]->Data()->GetN() > 0) {
1088 int index = fData->Add(fSources[source]->Data(), fNuclei[source]->GetName());
1089 if(index >= 0) {
1090 fData->SetLineColor(index, static_cast<Color_t>(source + 1)); //+1 for the color so that we start with 1 = black instead of 0 = white
1091 fData->SetMarkerColor(index, static_cast<Color_t>(source + 1));
1092 }
1093 }
1094 }
1096 std::cout << "fData " << fData << ": " << (fData != nullptr ? fData->GetN() : -1) << " data points after adding " << fNuclei.size() << " graphs" << std::endl;
1097 fData->Print();
1098 }
1099}
1100
1102{
1103 /// Copy data from all sources into one graph (which we use for the calib && source < fSources.size()ration).
1104 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)
1105 if(fFwhm == nullptr) {
1106 fFwhm = new TCalibrationGraphSet("ADC channel", "FWHM [ADC channel]");
1107 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)
1108 }
1109 fFwhm->Clear();
1110
1111 fFwhm->SetName(Form("fwhm%s", fName.c_str()));
1113 std::cout << "set name of fFwhm using " << fName.c_str() << ": " << fFwhm->GetName() << std::endl;
1114 std::cout << "fFwhm " << fFwhm << ": " << (fFwhm != nullptr ? fFwhm->GetN() : -2) << " data points after creation" << std::endl;
1115 std::cout << "Looping over " << fNuclei.size() << " sources, fSources.size() = " << fSources.size() << std::endl;
1116 }
1117 for(size_t source = 0; source < fNuclei.size() && source < fSources.size(); ++source) {
1118 if(fSources[source]->Fwhm()->GetN() > 0) {
1119 int index = fFwhm->Add(fSources[source]->Fwhm(), fNuclei[source]->GetName());
1120 if(index >= 0) {
1121 fFwhm->SetLineColor(index, static_cast<Color_t>(source + 1)); //+1 for the color so that we start with 1 = black instead of 0 = white
1122 fFwhm->SetMarkerColor(index, static_cast<Color_t>(source + 1));
1123 }
1124 }
1125 }
1127 std::cout << "fFwhm " << fFwhm << ": " << (fFwhm != nullptr ? fFwhm->GetN() : -1) << " data points after adding " << fNuclei.size() << " graphs" << std::endl;
1128 fFwhm->Print();
1129 }
1130}
1131
1132void TChannelTab::Calibrate(const int& degree, const bool& force)
1133{
1134 if(degree != fDegree || force) {
1136 std::cout << DYELLOW;
1137 if(force) {
1138 std::cout << "forced calibration" << std::endl;
1139 } else {
1140 std::cout << "changed degree of polynomial: " << degree << " != " << fDegree << std::endl;
1141 }
1142 }
1143 fDegree = degree;
1144 Calibrate();
1146 std::cout << degree << " == " << fDegree << " and not forcing (" << force << "): not fitting channel " << fName << std::endl;
1147 }
1148}
1149
1151{
1152 /// This function fit's the final data of the given channel. It requires all other elements to have been created already.
1153 TF1* calibration = new TF1("fitfunction", ::Polynomial, 0., 10000., fDegree + 2);
1154 calibration->FixParameter(0, fDegree);
1155 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DYELLOW << "fData " << fData << ": " << (fData != nullptr ? fData->GetN() : -1) << " data points being fit" << std::endl; }
1156 fData->Fit(calibration, "Q");
1157 TString text = Form("%.6f + %.6f*x", calibration->GetParameter(1), calibration->GetParameter(2));
1158 for(int i = 2; i <= fDegree; ++i) {
1159 text.Append(Form(" + %.6f*x^%d", calibration->GetParameter(i + 1), i));
1160 }
1161 fChannelStatusBar->SetText(text.Data(), 2);
1162 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "re-calculating residuals and clearing fields" << std::endl; }
1163 // re-calculate the residuals
1164 fData->SetResidual(true);
1165
1166 fLegend->Clear();
1167 fCalibrationPad->cd();
1169 fLegend->Draw();
1170 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "set chi2 label" << std::endl; }
1171 // calculate the corners of the chi^2 label from the minimum and maximum x/y-values of the graph
1172 // we position it in the top left corner about 50% of the width and 10% of the height of the graph
1173 double left = fData->GetMinimumX();
1174 double right = left + (fData->GetMaximumX() - left) * 0.5;
1175 double top = fData->GetMaximumY();
1176 double bottom = top - (top - fData->GetMinimumY()) * 0.1;
1177
1178 if(fChi2Label == nullptr) {
1179 fChi2Label = new TPaveText(left, bottom, right, top);
1180 } else {
1181 fChi2Label->Clear();
1182 }
1184 std::cout << "fChi2Label created " << fChi2Label << " (" << left << " - " << right << ", " << bottom << " - " << top << ", from " << fData->GetMinimumX() << "-" << fData->GetMaximumX() << ", " << fData->GetMinimumY() << "-" << fData->GetMaximumY() << ") on gPad " << gPad->GetName() << std::endl;
1185 fData->Print("e");
1186 }
1187 fChi2Label->AddText(Form("#chi^{2}/NDF = %f", calibration->GetChisquare() / calibration->GetNDF()));
1188 fChi2Label->SetFillColor(kWhite);
1189 fChi2Label->Draw();
1191 std::cout << "fChi2Label set to:" << std::endl;
1192 fChi2Label->GetListOfLines()->Print();
1193 std::cout << "Text size " << fChi2Label->GetTextSize() << std::endl;
1194 }
1195
1196 fResidualPad->cd();
1197 fData->DrawResidual("*");
1198
1199 fCalibrationCanvas->GetCanvas()->Modified();
1200
1201 delete calibration;
1202
1203 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)
1204}
1205
1207{
1208 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)
1209 // write the actual parameters of the fit
1210 std::stringstream str;
1211 str << std::hex << fName;
1212 int address = 0;
1213 str >> address;
1214 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Got address " << hex(address, 4) << " from name " << fName << std::endl; }
1215 TF1* calibration = fData->FitFunction();
1216 if(calibration == nullptr) {
1217 std::cout << "Failed to find calibration in fData" << std::endl;
1218 fData->TotalGraph()->GetListOfFunctions()->Print();
1219 return;
1220 }
1221 std::vector<Float_t> parameters;
1222 for(int i = 0; i <= calibration->GetParameter(0); ++i) {
1223 parameters.push_back(static_cast<Float_t>(calibration->GetParameter(i + 1)));
1224 }
1225 TChannel* channel = TChannel::GetChannel(address, false);
1226 if(channel == nullptr) {
1227 std::cerr << "Failed to get channel for address " << hex(address, 4) << std::endl;
1228 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)
1229 return;
1230 }
1231 channel->SetENGCoefficients(parameters);
1232 channel->DestroyEnergyNonlinearity();
1234 double* x = fData->GetX();
1235 double* y = fData->GetY();
1236 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << "Going to add " << fData->GetN() << " points to nonlinearity graph" << std::endl; }
1237 for(int i = 0; i < fData->GetN(); ++i) {
1238 // nonlinearity expects y to be the true source energy minus the calibrated energy of the peak
1239 // the source energy is y, the peak is x
1240 channel->AddEnergyNonlinearityPoint(y[i], y[i] - calibration->Eval(x[i]));
1241 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; }
1242 }
1243 }
1244}
1245
1246void TChannelTab::FindPeaks(const double& sigma, const double& threshold, const double& peakRatio, const bool& force, const bool& fast)
1247{
1249 std::cout << DYELLOW << "Finding peaks in source tab " << fActiveSourceTab << ". Old: " << fSourceTab->GetCurrent() << " = " << fSources[fSourceTab->GetCurrent()] << ", current tab element " << fSourceTab->GetCurrentTab() << " is enabled = " << fSourceTab->GetCurrentTab()->IsEnabled() << std::endl;
1250 fSourceTab->Print();
1251 for(int tab = 0; tab < fSourceTab->GetNumberOfTabs(); ++tab) {
1252 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;
1253 }
1254 }
1255 fSources[fActiveSourceTab]->FindPeaks(sigma, threshold, peakRatio, force, fast);
1256 UpdateData();
1257 UpdateFwhm();
1258}
1259
1260void TChannelTab::Write(TFile* output)
1261{
1262 /// Write graphs to output.
1263 output->cd();
1264 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "writing " << fName << std::endl; }
1265 fData->Write(Form("calGraph_%s", fName.c_str()), TObject::kOverwrite);
1266 fFwhm->Write(Form("fwhm_%s", fName.c_str()), TObject::kOverwrite);
1267 if(TSourceCalibration::VerboseLevel() > EVerbosity::kSubroutines) { std::cout << DYELLOW << "wrote data " << fName << std::endl; }
1268}
1269
1271{
1272 /// 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
1273 // find the histogram in the current pad
1274 TH1* hist1 = nullptr;
1275 for(const auto&& obj : *(gPad->GetListOfPrimitives())) {
1276 if(obj->InheritsFrom(TGraph::Class())) {
1277 hist1 = static_cast<TGraph*>(obj)->GetHistogram();
1278 break;
1279 }
1280 }
1281 if(hist1 == nullptr) {
1282 std::cout << "ZoomX - Failed to find histogram for pad " << gPad->GetName() << std::endl;
1283 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { gPad->GetListOfPrimitives()->Print(); }
1284 return;
1285 }
1286
1287 // extract base name and channel from pad name
1288 std::string padName = gPad->GetName();
1289 std::string baseName = padName.substr(0, 3);
1290 std::string channelName = padName.substr(4);
1291
1292 // find the corresponding partner pad to the selected one
1293 std::string newName;
1294 if(baseName == "cal") {
1295 newName = "res_" + channelName;
1296 } else if(baseName == "res") {
1297 newName = "cal_" + channelName;
1298 } else {
1299 std::cout << "Unknown combination of pad " << gPad->GetName() << " and histogram " << hist1->GetName() << std::endl;
1300 return;
1301 }
1302 auto* newObj = gPad->GetCanvas()->FindObject(newName.c_str());
1303 if(newObj == nullptr) {
1304 std::cout << "Failed to find " << newName << std::endl;
1305 return;
1306 }
1307
1308 if(newObj->IsA() != TPad::Class()) {
1309 std::cout << newObj << " = " << newObj->GetName() << ", " << newObj->GetTitle() << " is a " << newObj->ClassName() << " not a TPad" << std::endl;
1310 return;
1311 }
1312 auto* pad2 = static_cast<TPad*>(newObj);
1313 if(pad2 == nullptr) {
1314 std::cout << "Failed to find partner pad " << newName << std::endl;
1315 return;
1316 }
1317 // find the histogram in the partner pad
1318 TH1* hist2 = nullptr;
1319 for(const auto&& obj : *(pad2->GetListOfPrimitives())) {
1320 if(obj->InheritsFrom(TGraph::Class())) {
1321 hist2 = static_cast<TGraph*>(obj)->GetHistogram();
1322 break;
1323 }
1324 }
1325 if(hist2 == nullptr) {
1326 std::cout << "ZoomX - Failed to find histogram for partner pad " << newName << std::endl;
1327 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { pad2->GetListOfPrimitives()->Print(); }
1328 return;
1329 }
1330
1331 TAxis* axis1 = hist1->GetXaxis();
1332 Int_t binmin = axis1->GetFirst();
1333 Int_t binmax = axis1->GetLast();
1334 Double_t xmin = axis1->GetBinLowEdge(binmin);
1335 Double_t xmax = axis1->GetBinLowEdge(binmax);
1336 TAxis* axis2 = hist2->GetXaxis();
1337 Int_t newmin = axis2->FindBin(xmin);
1338 Int_t newmax = axis2->FindBin(xmax);
1339 axis2->SetRange(newmin, newmax);
1340 pad2->Modified();
1341 pad2->Update();
1342}
1343
1345{
1346 /// 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
1347 // find the histogram in the current pad
1348 TH1* hist1 = nullptr;
1349 for(const auto&& obj : *(gPad->GetListOfPrimitives())) {
1350 if(obj->InheritsFrom(TGraph::Class())) {
1351 hist1 = static_cast<TGraph*>(obj)->GetHistogram();
1352 break;
1353 }
1354 }
1355 if(hist1 == nullptr) {
1356 std::cout << "ZoomY - Failed to find histogram for pad " << gPad->GetName() << std::endl;
1357 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { gPad->GetListOfPrimitives()->Print(); }
1358 return;
1359 }
1360
1361 // extract base name and channel from pad name
1362 std::string padName = gPad->GetName();
1363 std::string baseName = padName.substr(0, 3);
1364 std::string channelName = padName.substr(4);
1365
1366 // find the corresponding partner pad to the selected one
1367 std::string newName;
1368 if(baseName == "cal") {
1369 newName = "res_" + channelName;
1370 } else if(baseName == "res") {
1371 newName = "cal_" + channelName;
1372 } else {
1373 std::cout << "Unknown combination of pad " << gPad->GetName() << " and histogram " << hist1->GetName() << std::endl;
1374 return;
1375 }
1376 auto* newObj = gPad->GetCanvas()->FindObject(newName.c_str());
1377 if(newObj == nullptr) {
1378 std::cout << "Failed to find " << newName << std::endl;
1379 return;
1380 }
1381
1382 if(newObj->IsA() != TPad::Class()) {
1383 std::cout << newObj << " = " << newObj->GetName() << ", " << newObj->GetTitle() << " is a " << newObj->ClassName() << " not a TPad" << std::endl;
1384 return;
1385 }
1386 auto* pad2 = static_cast<TPad*>(newObj);
1387 if(pad2 == nullptr) {
1388 std::cout << "Failed to find partner pad " << newName << std::endl;
1389 return;
1390 }
1391 // find the histogram in the partner pad
1392 TH1* hist2 = nullptr;
1393 for(const auto&& obj : *(pad2->GetListOfPrimitives())) {
1394 if(obj->InheritsFrom(TGraph::Class())) {
1395 hist2 = static_cast<TGraph*>(obj)->GetHistogram();
1396 break;
1397 }
1398 }
1399 if(hist2 == nullptr) {
1400 std::cout << "ZoomY - Failed to find histogram for partner pad " << newName << std::endl;
1401 if(TSourceCalibration::VerboseLevel() > EVerbosity::kLoops) { pad2->GetListOfPrimitives()->Print(); }
1402 return;
1403 }
1404
1405 hist2->SetMinimum(hist1->GetMinimum());
1406 hist2->SetMaximum(hist1->GetMaximum());
1407
1408 pad2->Modified();
1409 pad2->Update();
1410}
1411
1413{
1414 std::cout << "TChannelTab frame:" << std::endl;
1415 fChannelFrame->Print();
1416 std::cout << "TChannelTab canvas:" << std::endl;
1417 fCalibrationCanvas->Print();
1418 std::cout << "TChannelTab status bar:" << std::endl;
1419 //fChannelStatusBar->Print();
1420 for(auto* sourceTab : fSources) {
1421 sourceTab->PrintLayout();
1422 }
1423}
1424
1425//////////////////////////////////////// TSourceCalibration ////////////////////////////////////////
1435
1436TSourceCalibration::TSourceCalibration(double sigma, double threshold, int degree, double peakRatio, int count...)
1437 : TGMainFrame(nullptr, 2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight), fDefaultSigma(sigma), fDefaultThreshold(threshold), fDefaultDegree(degree), fDefaultPeakRatio(peakRatio)
1438{
1439 TH1::AddDirectory(false); // turns off warnings about multiple histograms with the same name because ROOT doesn't manage them anymore
1440
1441 va_list args;
1442 va_start(args, count); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1443 for(int i = 0; i < count; ++i) {
1444 fMatrices.push_back(va_arg(args, TH2*)); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1445 if(fMatrices.back() == nullptr) {
1446 std::cout << "Error, got nullptr as matrix input?" << std::endl;
1447 fMatrices.pop_back();
1448 }
1449 }
1450 va_end(args); // NOLINT(cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1452 std::cout << DGREEN << __PRETTY_FUNCTION__ << ": verbose level " << fVerboseLevel << std::endl // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1453 << "using " << count << "/" << fMatrices.size() << " matrices:" << std::endl;
1454 for(auto* mat : fMatrices) {
1455 std::cout << mat << std::flush << " = " << mat->GetName() << std::endl;
1456 }
1457 }
1458
1459 fOldErrorLevel = gErrorIgnoreLevel;
1460 gErrorIgnoreLevel = kError;
1461 gPrintViaErrorHandler = true; // redirects all printf's to root's normal message system
1462
1463 // check matrices (# of filled bins and bin labels) and resize some vectors for later use
1464 // use the first matrix to get a reference for everything
1465 fNofBins = 0;
1466 std::map<int, int> channelToIndex; // used to be a member, but only used here
1467 for(int bin = 1; bin <= fMatrices[0]->GetNbinsX(); ++bin) {
1468 if(FilledBin(fMatrices[0], bin)) {
1469 fActualChannelId.push_back(fNofBins); // at this point fNofBins is the index at which this projection will end up
1470 fActiveBins.push_back(bin);
1471 channelToIndex[bin] = fNofBins; // this map simply stores which bin ends up at which index
1472 fChannelLabel.push_back(fMatrices[0]->GetXaxis()->GetBinLabel(bin));
1473 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << bin << ". bin: fNofBins " << fNofBins << ", channelToIndex[" << bin << "] " << channelToIndex[bin] << ", fActualChannelId.back() " << fActualChannelId.back() << std::endl; }
1474 ++fNofBins;
1475 } else {
1476 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "skipping bin " << bin << std::endl; }
1477 }
1478 }
1479 // now loop over all other matrices and check vs. the first one
1480 for(size_t i = 1; i < fMatrices.size(); ++i) {
1481 int tmpBins = 0;
1482 for(int bin = 1; bin <= fMatrices[i]->GetNbinsX(); ++bin) {
1483 if(FilledBin(fMatrices[i], bin)) { // good bin in the current matrix
1484 // current index is tmpBins, so we check if the bin agrees with what we have
1485 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
1486 std::ostringstream error;
1487 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;
1488 throw std::invalid_argument(error.str());
1489 }
1490 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
1491 std::ostringstream error;
1492 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;
1493 throw std::invalid_argument(error.str());
1494 }
1495 ++tmpBins;
1496 } else {
1497 if(channelToIndex.find(bin) != channelToIndex.end()) { //found an empty bin that was full in the first matrix
1498 std::ostringstream error;
1499 error << "Mismatch in " << i << ". matrix (" << fMatrices[i]->GetName() << "), bin " << bin << " is empty, but should be " << channelToIndex[bin] << ". filled bin" << std::endl;
1500 throw std::invalid_argument(error.str());
1501 }
1502 }
1503 }
1504 }
1505
1506 if(fVerboseLevel > EVerbosity::kQuiet) { std::cout << fMatrices.size() << " matrices with " << fMatrices[0]->GetNbinsX() << " x-bins, fNofBins " << fNofBins << ", fActualChannelId.size() " << fActualChannelId.size() << std::endl; }
1507
1508 fOutput = new TFile("TSourceCalibration.root", "recreate");
1509 if(!fOutput->IsOpen()) {
1510 throw std::runtime_error("Unable to open output file \"TSourceCalibration.root\"!");
1511 }
1512
1513 TRedirect* redirect = nullptr;
1514 if(!fLogFile.empty()) {
1515 redirect = new TRedirect(fLogFile.c_str());
1516 }
1517
1518 // build the first screen
1521
1522 // Set a name to the main frame
1523 SetWindowName("SourceCalibration");
1524
1525 // Map all subwindows of main frame
1526 MapSubwindows();
1527
1528 // Initialize the layout algorithm
1529 Resize(GetDefaultSize());
1530
1531 // Map main frame
1532 MapWindow();
1533
1534 delete redirect;
1535}
1536
1538{
1539 fOutput->Close();
1540 delete fOutput;
1541
1542 gErrorIgnoreLevel = fOldErrorLevel;
1543}
1544
1546{
1547 /// Build initial interface with histogram <-> source assignment
1548
1549 auto* layoutManager = new TGTableLayout(this, fMatrices.size() + 1, 2, true, 5);
1550 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "created table layout manager with 2 columns, " << fMatrices.size() + 1 << " rows" << std::endl; }
1551 SetLayoutManager(layoutManager);
1552
1553 // The matrices and source selection boxes
1554 size_t i = 0;
1555 for(i = 0; i < fMatrices.size(); ++i) {
1556 fMatrixNames.push_back(new TGLabel(this, fMatrices[i]->GetName()));
1557 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Text height " << fMatrixNames.back()->GetFont()->TextHeight() << std::endl; }
1558 fSource.push_back(nullptr);
1559 fSourceEnergy.emplace_back();
1560 fSourceBox.push_back(new TGComboBox(this, "Select source", kSourceBox + fSourceBox.size()));
1561
1562 int index = 0;
1563#if __cplusplus >= 201703L
1564 // For some reasons getenv("GRSISYS") with strcat does not work (adds the "sub"-path twice).
1565 // Have to do this by copying the getenv result into a c++-string.
1566 if(std::getenv("GRSISYS") == nullptr) {
1567 throw std::runtime_error("Failed to get environment variable $GRSISYS");
1568 }
1569 std::string path(std::getenv("GRSISYS"));
1570 path += "/libraries/TAnalysis/SourceData/";
1571 for(const auto& file : std::filesystem::directory_iterator(path)) {
1572 if(file.is_regular_file() && file.path().extension().compare(".sou") == 0) {
1573 fSourceBox.back()->AddEntry(file.path().stem().c_str(), index);
1574 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << "/" << index << ": Comparing matrix name " << fMatrices[i]->GetName() << " to file name " << file.path().stem().c_str() << std::endl; }
1575 if(std::strstr(fMatrices[i]->GetName(), file.path().stem().c_str()) != nullptr) {
1576 fSourceBox.back()->Select(index);
1577 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1578 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": selected source " << index << std::endl; }
1580 std::cout << "matrix name " << fMatrices[i]->GetName() << " not matching " << file.path().stem().c_str() << std::endl;
1581 }
1582 ++index;
1583 }
1584 }
1585 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": created list with " << index << " sources" << std::endl; }
1586#else
1587 fSourceBox.back()->AddEntry("22Na", index);
1588 if(std::strstr(fMatrices[i]->GetName(), "22Na") != nullptr) {
1589 fSourceBox.back()->Select(index);
1590 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1591 }
1592 ++index;
1593 fSourceBox.back()->AddEntry("56Co", index);
1594 if(std::strstr(fMatrices[i]->GetName(), "56Co") != nullptr) {
1595 fSourceBox.back()->Select(index);
1596 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1597 }
1598 ++index;
1599 fSourceBox.back()->AddEntry("60Co", index);
1600 if(std::strstr(fMatrices[i]->GetName(), "60Co") != nullptr) {
1601 fSourceBox.back()->Select(index);
1602 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1603 }
1604 ++index;
1605 fSourceBox.back()->AddEntry("133Ba", index);
1606 if(std::strstr(fMatrices[i]->GetName(), "133Ba") != nullptr) {
1607 fSourceBox.back()->Select(index);
1608 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1609 }
1610 ++index;
1611 fSourceBox.back()->AddEntry("152Eu", index);
1612 if(std::strstr(fMatrices[i]->GetName(), "152Eu") != nullptr) {
1613 fSourceBox.back()->Select(index);
1614 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1615 }
1616 ++index;
1617 fSourceBox.back()->AddEntry("241Am", index);
1618 if(std::strstr(fMatrices[i]->GetName(), "241Am") != nullptr) {
1619 fSourceBox.back()->Select(index);
1620 SetSource(kSourceBox + fSourceBox.size() - 1, index);
1621 }
1622#endif
1623
1624 fSourceBox.back()->SetMinHeight(fPanelHeight / 2);
1625
1626 fSourceBox.back()->Resize(fSourceboxWidth, fLineHeight);
1627 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; }
1628 AddFrame(fMatrixNames.back(), new TGTableLayoutHints(0, 1, i, i + 1, kLHintsRight | kLHintsCenterY));
1629 AddFrame(fSourceBox.back(), new TGTableLayoutHints(1, 2, i, i + 1, kLHintsLeft | kLHintsCenterY));
1630 }
1631
1632 // The buttons
1633 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "Attaching start button to 0, 2, " << i << ", " << i + 1 << std::endl; }
1634 fStartButton = new TGTextButton(this, "Accept && Continue", kStartButton);
1635 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << "start button " << fStartButton << std::endl; }
1636 AddFrame(fStartButton, new TGTableLayoutHints(0, 2, i, i + 1, kLHintsCenterX | kLHintsCenterY));
1637 Layout();
1638}
1639
1641{
1642 /// Create connections for the histogram <-> source assignment interface
1643
1644 // Connect the selection of the source
1645 for(auto* box : fSourceBox) {
1646 box->Connect("Selected(Int_t, Int_t)", "TSourceCalibration", this, "SetSource(Int_t, Int_t)");
1647 }
1648
1649 //Connect the clicking of buttons
1650 fStartButton->Connect("Clicked()", "TSourceCalibration", this, "Start()");
1651}
1652
1654{
1655 /// Disconnect all signals from histogram <-> source assignment interface
1656 for(auto* box : fSourceBox) {
1657 box->Disconnect("Selected(Int_t, Int_t)", this, "SetSource(Int_t, Int_t)");
1658 }
1659
1660 fStartButton->Disconnect("Clicked()", this, "Start()");
1661}
1662
1664{
1665 HideFrame(element);
1666 RemoveFrame(element);
1667 //delete element;
1668 //element = nullptr;
1669}
1670
1672{
1673 for(auto* name : fMatrixNames) {
1674 DeleteElement(name);
1675 }
1676 fMatrixNames.clear();
1677 for(auto* box : fSourceBox) {
1678 DeleteElement(box);
1679 }
1680 fSourceBox.clear();
1682 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "Deleted start button " << fStartButton << std::endl; }
1683}
1684
1685void TSourceCalibration::SetSource(Int_t windowId, Int_t entryId)
1686{
1687 int index = windowId - kSourceBox;
1688 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)
1689 delete fSource[index];
1690 auto* nucleus = new TNucleus(fSourceBox[index]->GetListBox()->GetEntry(entryId)->GetTitle());
1691 TIter iter(nucleus->GetTransitionList());
1692 fSourceEnergy[index].clear();
1693 while(auto* transition = static_cast<TTransition*>(iter.Next())) {
1694 fSourceEnergy[index].push_back(std::make_tuple(transition->GetEnergy(), transition->GetEnergyUncertainty(), transition->GetIntensity(), transition->GetIntensityUncertainty()));
1695 }
1696 fSource[index] = nucleus;
1697}
1698
1700{
1701 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)
1702 if(fEmitter == fStartButton) {
1703 SecondWindow();
1704 } else if(fEmitter == fAcceptAllButton) {
1705 for(auto& channelTab : fChannelTab) {
1706 if(channelTab == nullptr) { continue; }
1707 channelTab->UpdateChannel();
1708 channelTab->Write(fOutput);
1709 }
1711 std::cout << "Closing window" << std::endl;
1712 CloseWindow();
1713 std::cout << "all done" << std::endl;
1714 exit(0);
1715 }
1716}
1717
1719{
1720 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)
1721 if(fEmitter == nullptr) { // we only want to do this once at the beginning (after fEmitter was initialized to nullptr)
1723 TTimer::SingleShot(fWaitMs, "TSourceCalibration", this, "HandleTimer()");
1724 }
1725}
1726
1728{
1729 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1730 // check that all sources have been set
1731 for(size_t i = 0; i < fSource.size(); ++i) {
1732 if(fSource[i] == nullptr) {
1733 std::cerr << "Source " << i << " not set!" << std::endl;
1734 return;
1735 }
1736 }
1737 // now check that we don't have the same source twice (which wouldn't make sense)
1738 for(size_t i = 0; i < fSource.size(); ++i) {
1739 for(size_t j = i + 1; j < fSource.size(); ++j) {
1740 if(*(fSource[i]) == *(fSource[j])) {
1741 std::cerr << "Duplicate sources: " << i << " - " << fSource[i]->GetName() << " and " << j << " - " << fSource[j]->GetName() << std::endl;
1742 return;
1743 }
1744 }
1745 }
1746
1747 // disconnect signals of first screen and remove all elements
1749 RemoveAll();
1750 DeleteFirst();
1751
1752 SetLayoutManager(new TGVerticalLayout(this));
1753
1754 // create intermediate progress bar
1755 fProgressBar = new TGHProgressBar(this, TGProgressBar::kFancy, fPanelWidth);
1756 fProgressBar->SetRange(0., static_cast<Float_t>(fMatrices.size() * fActiveBins.size()));
1757 fProgressBar->Percent(true);
1758 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; }
1759 AddFrame(fProgressBar, new TGLayoutHints(kLHintsCenterX | kLHintsCenterY | kLHintsExpandX | kLHintsExpandY, 0, 0, 0, 0));
1760
1761 // Map all subwindows of main frame
1762 MapSubwindows();
1763
1764 // Initialize the layout algorithm
1765 Resize(TGDimension(fPanelWidth, fLineHeight));
1766
1767 // Map main frame
1768 MapWindow();
1769
1770 // create second screen and its connections
1773
1774 // remove progress bar
1776
1777 // Map all subwindows of main frame
1778 MapSubwindows();
1779
1780 // Initialize the layout algorithm
1781 Resize(TGDimension(2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight));
1782
1783 // Map main frame
1784 MapWindow();
1785 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)
1786}
1787
1789{
1790 SetLayoutManager(new TGVerticalLayout(this));
1791 fTab = new TGTab(this, 2 * fPanelWidth, fPanelHeight + 2 * fStatusbarHeight);
1792
1793 fChannelTab.resize(fMatrices[0]->GetNbinsX());
1794 for(auto& bin : fActiveBins) {
1795 // create vector with projections of this channel for each source
1796 std::vector<GH1D*> projections(fMatrices.size());
1797 size_t index = 0;
1798 for(auto* matrix : fMatrices) {
1799 projections[index] = new GH1D(matrix->ProjectionY(Form("%s_%s", fSource[index]->GetName(), matrix->GetXaxis()->GetBinLabel(bin)), bin, bin));
1800 ++index;
1801 }
1802 fChannelTab[bin - 1] = new TChannelTab(this, fSource, projections, fMatrices[0]->GetXaxis()->GetBinLabel(bin), fTab->AddTab(fMatrices[0]->GetXaxis()->GetBinLabel(bin)), fDefaultSigma, fDefaultThreshold, fDefaultDegree, fSourceEnergy, fProgressBar);
1803 }
1804
1805 //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; }
1806
1807 //for(int i = 0; i < fChannelTab[0]->SourceTab()->GetNumberOfTabs(); ++i) {
1808 //if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << i << ": " << fChannelTab[0]->SourceTab()->GetTabTab(i)->GetText()->GetString() << std::endl; }
1809 //}
1810
1811 AddFrame(fTab, new TGLayoutHints(kLHintsTop | kLHintsExpandX | kLHintsExpandY, 0, 0, 0, 0));
1812
1813 // bottom frame with navigation button group, text entries, etc.
1814 fBottomFrame = new TGHorizontalFrame(this, 2 * fPanelWidth, TSourceCalibration::StatusbarHeight());
1815
1816 fLeftFrame = new TGVerticalFrame(fBottomFrame, fPanelWidth, fParameterHeight);
1817 fNavigationGroup = new TGHButtonGroup(fLeftFrame, "");
1818 fPreviousButton = new TGTextButton(fNavigationGroup, "Previous");
1819 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << "prev button " << fPreviousButton << std::endl; }
1820 fPreviousButton->SetEnabled(false);
1821 fFindPeaksButton = new TGTextButton(fNavigationGroup, "Find Peaks");
1822 fFindPeaksFastButton = new TGTextButton(fNavigationGroup, "Find Peaks Fast");
1823 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "find peaks button " << fFindPeaksButton << std::endl; }
1824 fCalibrateButton = new TGTextButton(fNavigationGroup, "Calibrate");
1825 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "cal button " << fCalibrateButton << std::endl; }
1826 fDiscardButton = new TGTextButton(fNavigationGroup, "Discard");
1827 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "discard button " << fDiscardButton << std::endl; }
1828 fAcceptButton = new TGTextButton(fNavigationGroup, "Accept");
1829 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "accept button " << fAcceptButton << std::endl; }
1830 fAcceptAllButton = new TGTextButton(fNavigationGroup, "Accept All && Finish");
1831 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "accept all button " << fAcceptAllButton << std::endl; }
1832 fNextButton = new TGTextButton(fNavigationGroup, "Next");
1833 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "next button " << fNextButton << std::endl; }
1834
1835 fLeftFrame->AddFrame(fNavigationGroup, new TGLayoutHints(kLHintsTop | kLHintsExpandX, 2, 2, 2, 2));
1836
1837 fBottomFrame->AddFrame(fLeftFrame, new TGLayoutHints(kLHintsLeft, 2, 2, 2, 2));
1838
1839 fRightFrame = new TGVerticalFrame(fBottomFrame, fPanelWidth, fParameterHeight);
1840 fParameterFrame = new TGGroupFrame(fRightFrame, "Parameters", kHorizontalFrame);
1841 fParameterFrame->SetLayoutManager(new TGMatrixLayout(fParameterFrame, 0, 4, 2));
1842 fSigmaLabel = new TGLabel(fParameterFrame, "Sigma");
1843 fSigmaEntry = new TGNumberEntry(fParameterFrame, fDefaultSigma, fDigitWidth, kSigmaEntry, TGNumberFormat::EStyle::kNESRealTwo, TGNumberFormat::EAttribute::kNEAPositive);
1844 fThresholdLabel = new TGLabel(fParameterFrame, "Threshold");
1845 fThresholdEntry = new TGNumberEntry(fParameterFrame, fDefaultThreshold, fDigitWidth, kThresholdEntry, TGNumberFormat::EStyle::kNESRealThree, TGNumberFormat::EAttribute::kNEAPositive, TGNumberFormat::ELimit::kNELLimitMinMax, 0., 1.);
1846 fDegreeLabel = new TGLabel(fParameterFrame, "Degree of polynomial");
1847 fDegreeEntry = new TGNumberEntry(fParameterFrame, fDefaultDegree, 2, kDegreeEntry, TGNumberFormat::EStyle::kNESInteger, TGNumberFormat::EAttribute::kNEAPositive);
1848 fPeakRatioLabel = new TGLabel(fParameterFrame, "Ratio foundsource peaks");
1849 fPeakRatioEntry = new TGNumberEntry(fParameterFrame, fDefaultPeakRatio, fDigitWidth, kPeakRatioEntry, TGNumberFormat::EStyle::kNESRealTwo, TGNumberFormat::EAttribute::kNEAPositive);
1850 fWriteNonlinearities = new TGCheckButton(fParameterFrame, "Write nonlinearities", kWriteNonlinearities);
1851 fWriteNonlinearities->SetState(kButtonUp);
1852
1853 fParameterFrame->AddFrame(fSigmaLabel);
1854 fParameterFrame->AddFrame(fSigmaEntry);
1857 fParameterFrame->AddFrame(fDegreeLabel);
1858 fParameterFrame->AddFrame(fDegreeEntry);
1862
1863 fRightFrame->AddFrame(fParameterFrame, new TGLayoutHints(kLHintsRight | kLHintsExpandX, 2, 2, 2, 2));
1864
1865 fBottomFrame->AddFrame(fRightFrame, new TGLayoutHints(kLHintsBottom | kLHintsRight, 2, 2, 2, 2));
1866
1867 AddFrame(fBottomFrame, new TGLayoutHints(kLHintsBottom | kLHintsExpandX, 2, 2, 2, 2));
1868
1869 SelectedTab(0);
1870
1871 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << DGREEN << "Second interface done" << std::endl; }
1872}
1873
1875{
1876 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1877 fNavigationGroup->Connect("Clicked(Int_t)", "TSourceCalibration", this, "Navigate(Int_t)");
1878 fTab->Connect("Selected(Int_t)", "TSourceCalibration", this, "SelectedTab(Int_t)");
1879 // we don't need to connect the sigma, threshold, and degree number entries, those are automatically read when we start the calibration
1880 for(auto* sourceTab : fChannelTab) {
1881 if(sourceTab != nullptr) {
1882 sourceTab->MakeConnections();
1883 }
1884 }
1885}
1886
1888{
1889 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1890 fNavigationGroup->Disconnect("Clicked(Int_t)", this, "Navigate(Int_t)");
1891 fTab->Disconnect("Selected(Int_t)", this, "SelectedTab(Int_t)");
1892 for(auto* sourceTab : fChannelTab) {
1893 if(sourceTab != nullptr) {
1894 sourceTab->Disconnect();
1895 }
1896 }
1897}
1898
1900{
1901 // we get the current source tab id and use it to get the channel tab from the right source tab
1902 // 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
1903 // 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
1904 int currentChannelId = fTab->GetCurrent();
1905 int actualChannelId = fActualChannelId[currentChannelId];
1906 int nofTabs = fTab->GetNumberOfTabs();
1907 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)
1908 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; }
1909 switch(id) {
1910 case ENavigate::kPrevious: // previous
1911 fTab->SetTab(currentChannelId - 1);
1912 SelectedTab(currentChannelId - 1);
1913 break;
1914 case ENavigate::kFindPeaks: // find peaks
1915 FindPeaks();
1916 break;
1917 case ENavigate::kFindPeaksFast: // find peaks fast
1918 FindPeaksFast();
1919 break;
1920 case ENavigate::kCalibrate: // calibrate
1921 Calibrate();
1922 break;
1923 case ENavigate::kDiscard: // discard
1924 // select the next (or if we are on the last tab, the previous) tab
1925 if(currentChannelId < nofTabs - 1) {
1926 fTab->SetTab(currentChannelId + 1);
1927 } else {
1928 fTab->SetTab(currentChannelId - 1);
1929 }
1930 // remove the original active tab
1931 fTab->RemoveTab(currentChannelId);
1932 break;
1933 case ENavigate::kAccept: // accept
1934 AcceptChannel(currentChannelId);
1935 break;
1936 case ENavigate::kAcceptAll: // accept all (no argument = -1 = all)
1937 AcceptChannel();
1938 return;
1939 break;
1940 case ENavigate::kNext: // next
1941 fTab->SetTab(currentChannelId + 1);
1942 SelectedTab(currentChannelId + 1);
1943 break;
1944 default:
1945 break;
1946 }
1947 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; }
1948}
1949
1951{
1952 /// Simple function that enables and disables the previous and next buttons depending on which tab was selected.
1953 /// Also sets gPad to the projection of the source selected in this tab.
1954 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << ": id " << id << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
1955 if(id < 0) { return; }
1956 if(id == 0) {
1957 fPreviousButton->SetEnabled(false);
1958 } else {
1959 fPreviousButton->SetEnabled(true);
1960 }
1961
1962 if(id == fTab->GetNumberOfTabs() - 1) {
1963 fNextButton->SetEnabled(false);
1964 } else {
1965 fNextButton->SetEnabled(true);
1966 }
1967
1968 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; }
1969
1970 if(fChannelTab[fActiveBins[id] - 1] == nullptr) {
1971 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]" << RESET_COLOR << std::endl;
1972 return;
1973 }
1974 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab() == nullptr) {
1975 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()" << RESET_COLOR << std::endl;
1976 return;
1977 }
1978 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas() == nullptr) {
1979 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()->ProjectionCanvas()" << RESET_COLOR << std::endl;
1980 return;
1981 }
1982 if(fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas() == nullptr) {
1983 std::cout << "Failed to get fChannelTab[" << fActiveBins[id] - 1 << " = fActiveBins[" << id << "]-1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas()" << RESET_COLOR << std::endl;
1984 return;
1985 }
1986 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << DGREEN << "Changing gpad from \"" << gPad->GetName() << "\" to \""; }
1987 gPad = fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas();
1988 fChannelTab[fActiveBins[id] - 1]->SelectedSourceTab()->ProjectionCanvas()->GetCanvas()->cd();
1989 if(TSourceCalibration::VerboseLevel() > EVerbosity::kBasicFlow) { std::cout << gPad->GetName() << "\"" << RESET_COLOR << std::endl; }
1990}
1991
1992void TSourceCalibration::AcceptChannel(const int& channelId)
1993{
1994 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)
1995
1996 // select the next (or if we are on the last tab, the previous) tab
1997 int nofTabs = fTab->GetNumberOfTabs();
1998 int minChannel = 0;
1999 int maxChannel = nofTabs - 1;
2000 if(channelId >= 0) {
2001 minChannel = channelId;
2002 maxChannel = channelId;
2003 }
2004
2005 // we need to loop backward, because removing the first channel would make the second one the new first and so on
2006 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)
2007 for(int currentChannelId = maxChannel; currentChannelId >= minChannel; --currentChannelId) {
2008 int actualChannelId = fActualChannelId[currentChannelId];
2009 if(fVerboseLevel > EVerbosity::kLoops) { std::cout << __PRETTY_FUNCTION__ << ": currentChannelId " << currentChannelId << ", actualChannelId " << actualChannelId << ", fChannelTab.size() " << fChannelTab.size() << ", fActualChannelId.size() " << fActualChannelId.size() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2010 if(minChannel == maxChannel) { // we don't need to select the tab if we close all
2011 if(currentChannelId < maxChannel) {
2012 fTab->SetTab(currentChannelId + 1);
2013 } else {
2014 fTab->SetTab(currentChannelId - 1);
2015 }
2016 }
2017 // remove the original active tab
2018 fTab->RemoveTab(currentChannelId);
2019 fActualChannelId.erase(fActualChannelId.begin() + currentChannelId);
2020 fActiveBins.erase(fActiveBins.begin() + currentChannelId);
2021 }
2022 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << __PRETTY_FUNCTION__ << ": # of channel tabs " << fActualChannelId.size() << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2023 // 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!)
2024 SelectedTab(fTab->GetCurrent());
2025 // if this was also the last source vector we initiate the last screen
2026 if(fActualChannelId.empty() || fActiveBins.empty()) {
2027 if(fVerboseLevel > EVerbosity::kSubroutines) { std::cout << "last channel tab done - writing files now" << std::endl; }
2029 TTimer::SingleShot(fWaitMs, "TSourceCalibration", this, "HandleTimer()");
2030 }
2031 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2032}
2033
2035{
2036 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2037 if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2038 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->FindPeaks(Sigma(), Threshold(), PeakRatio(), true, false); // force = true, fast = false
2039 Calibrate();
2040 } else {
2041 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)
2042 }
2043 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2044}
2045
2047{
2048 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2049 if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2050 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->FindPeaks(Sigma(), Threshold(), PeakRatio(), true, true); // force = true, fast = true
2051 Calibrate();
2052 } else {
2053 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)
2054 }
2055 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2056}
2057
2059{
2060 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2061 if(fChannelTab[fActiveBins[fTab->GetCurrent()] - 1] != nullptr) {
2062 fChannelTab[fActiveBins[fTab->GetCurrent()] - 1]->Calibrate(Degree(), true);
2063 } else {
2064 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)
2065 }
2066 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << RESET_COLOR << std::flush; }
2067}
2068
2070{
2071 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2072 std::ostringstream fileName;
2073 for(auto* source : fSource) {
2074 fileName << source->GetName();
2075 }
2076 fileName << ".cal";
2077 TChannel::WriteCalFile(fileName.str());
2078 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)
2080}
2081
2083{
2084 if(fVerboseLevel > EVerbosity::kBasicFlow) { std::cout << DGREEN << __PRETTY_FUNCTION__ << std::endl; } // NOLINT(cppcoreguidelines-pro-type-const-cast, cppcoreguidelines-pro-bounds-array-to-pointer-decay)
2085
2086 Print();
2087 if(fBottomFrame != nullptr) { fBottomFrame->Print(); }
2088 if(fLeftFrame != nullptr) { fLeftFrame->Print(); }
2089 if(fRightFrame != nullptr) { fRightFrame->Print(); }
2090 if(fTab != nullptr) { fTab->Print(); }
2091 std::cout << "TSourceCalibration all channel tabs:" << std::endl;
2092 for(auto* channelTab : fChannelTab) {
2093 if(channelTab != nullptr) { channelTab->PrintLayout(); }
2094 }
2095}
#define kArrowKeyRelease
Definition GCanvas.cxx:49
#define kArrowKeyPress
Definition GCanvas.cxx:48
GPeak * PhotoPeakFit(TH1 *, double, double, Option_t *opt="")
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 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
bool FilledBin(TH2 *matrix, const int &bin)
std::map< GPeak *, std::tuple< double, double, double, double > > Match(std::vector< GPeak * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
double Polynomial(double *x, double *par)
std::map< GPeak *, std::tuple< double, double, double, double > > SmartMatch(std::vector< GPeak * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
bool FilledBin(TH2 *matrix, const int &bin)
std::map< GPeak *, std::tuple< double, double, double, double > > Match(std::vector< GPeak * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
double Polynomial(double *x, double *par)
std::map< GPeak *, std::tuple< double, double, double, double > > SmartMatch(std::vector< GPeak * > peaks, std::vector< std::tuple< double, double, double, double > > sources, TSourceTab *sourceTab)
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
Definition GPeak.h:11
Double_t Area() const
Definition GPeak.h:44
Double_t Centroid() const
Definition GPeak.h:42
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)
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
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
std::vector< GH1D * > fProjections
vector of all projections
double fThreshold
the threshold (relative to the largest peak) used in the peak finder
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
TChannelTab(TSourceCalibration *parent, std::vector< TNucleus * > nuclei, std::vector< GH1D * > projections, std::string name, TGCompositeFrame *frame, double sigma, double threshold, int degree, std::vector< std::vector< std::tuple< double, double, double, double > > > sourceEnergies, TGHProgressBar *progressBar)
TPaveText * fChi2Label
TGTab * fSourceTab
tab for sources
void SelectCanvas(Event_t *event)
void Write(TFile *output)
void CalibrationStatus(Int_t event, Int_t px, Int_t py, TObject *selected)
static void ZoomY()
TGCompositeFrame * fCalibrationFrame
frame of tab with calibration
TSourceCalibration * fParent
the parent of this tab
std::vector< std::vector< std::tuple< double, double, double, double > > > fSourceEnergies
vector with source energies and uncertainties
void CreateSourceTab(size_t source)
TGCompositeFrame * fFwhmFrame
frame of tab with fwhm
TRootEmbeddedCanvas * fFwhmCanvas
int fActiveSourceTab
id of the currently active source tab
std::string fName
name of this tab
void FindPeaks(const double &sigma, const double &threshold, const double &peakRatio, const bool &force=false, const bool &fast=true)
int fDegree
degree of polynomial function used to calibrate
static void ZoomX()
std::vector< TNucleus * > fNuclei
the source nucleus
void SelectedTab(Int_t id)
TGStatusBar * fChannelStatusBar
TGTab * fCanvasTab
tab for canvases (calibration with residuals, and FWHM)
double fSigma
the sigma used in the peak finder
TGHProgressBar * fProgressBar
TRootEmbeddedCanvas * fCalibrationCanvas
TCalibrationGraphSet * fData
combined data from all sources
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
static std::string LogFile()
static int fStatusbarHeight
Height of status bar (also extra height needed for tabs)
TGVerticalFrame * fRightFrame
static std::string fLogFile
name of log file, if empty no log file is written
TGNumberEntry * fPeakRatioEntry
TGCheckButton * fWriteNonlinearities
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 * fCalibrateButton
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 * fFindPeaksFastButton
double fDefaultPeakRatio
The default ratio between found peaks and peaks in the source (per region).
TGHProgressBar * fProgressBar
TGHButtonGroup * fNavigationGroup
TGNumberEntry * fThresholdEntry
void SetSource(Int_t windowId, Int_t entryId)
TGTextButton * fFindPeaksButton
std::vector< int > fActiveBins
TGTextButton * fDiscardButton
std::vector< TNucleus * > fSource
std::vector< const char * > fChannelLabel
std::vector< TChannelTab * > fChannelTab
TGTextButton * fAcceptButton
TSourceCalibration(double sigma, double threshold, int degree, double peakRatio, int count...)
static int fPanelWidth
Width of one panel.
static int fPanelHeight
Height of one panel.
std::vector< TH2 * > fMatrices
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)
std::vector< TGComboBox * > fSourceBox
std::vector< int > fActualChannelId
int fNofBins
Number of filled bins in first matrix.
TGNumberEntry * fDegreeEntry
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 int fSourceboxWidth
Width of the box to select which source each histogram is from.
TGTextButton * fStartButton
double fDefaultThreshold
The default threshold used for the peak finding algorithm, can be changed later. Co-56 source needs a...
TGTextButton * fNextButton
TGVerticalFrame * fLeftFrame
static EVerbosity fVerboseLevel
Changes verbosity from 0 (quiet) to 4 (very verbose)
TGHorizontalFrame * fBottomFrame
void RemovePoint(Int_t px, Int_t py)
std::vector< GPeak * > fPeaks
void Status(const char *status, int position)
TGraphErrors * fFwhm
TRootEmbeddedCanvas * fProjectionCanvas
TSourceTab(TChannelTab *parent, TGCompositeFrame *frame, GH1D *projection, const double &sigma, const double &threshold, const double &peakRatio, std::vector< std::tuple< double, double, double, double > > sourceEnergy)
void FindPeaks(const double &sigma, const double &threshold, const double &peakRatio, const bool &force=false, const bool &fast=true)
std::vector< std::pair< double, double > > fRegions
void Add(std::map< GPeak *, std::tuple< double, double, double, double > > map)
void ProjectionStatus(Event_t *event)
TGCompositeFrame * fSourceFrame
TChannelTab * fParent
TGraphErrors * fData
void RemoveResidualPoint(Int_t px, Int_t py)
std::vector< std::tuple< double, double, double, double > > fSourceEnergy
void PrintLayout() const
TGStatusBar * fSourceStatusBar