GRSISort "v4.0.0.5"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
TGainMatch.cxx
Go to the documentation of this file.
1#include "TGainMatch.h"
2
3#include <algorithm>
4
5#include "Math/MinimizerOptions.h"
6#include "TVirtualFitter.h"
7#include "TFitResult.h"
8
10
12 : TCal(copy), fCoarseRange(fDefaultCoarseRange)
13{
14 std::cout << "THIS ---> " << fCoarseRange << std::endl;
15 copy.Copy(*this);
16}
17
18void TGainMatch::Copy(TObject& obj) const
19{
20 static_cast<TGainMatch&>(obj).fCoarseMatch = fCoarseMatch;
21 static_cast<TGainMatch&>(obj).fCoarseRange = fCoarseRange;
22 TCal::Copy(obj);
23}
24
25void TGainMatch::CalculateGain(Double_t cent1, Double_t cent2, Double_t eng1, Double_t eng2)
26{
27 // Put the peaks in order for ease (if the user put them in the wrong order)
28 // Apparantly there is a TGraph Sort method. Might look into this later.
29 if(eng1 > eng2) {
30 std::swap(eng1, eng2);
31 std::swap(cent1, cent2);
32 }
33
34 SetPoint(0, cent1, eng1);
35 SetPoint(1, cent2, eng2);
36
37 auto* gainFit = new TF1("gain", "pol1");
38
39 TFitResultPtr res = Fit(gainFit, "SC0");
40 SetFitFunction(GetFunction("gain")); // Have to do this because I want to delete gainfit
41 fGainCoeffs[0] = res->Parameter(0);
42 fGainCoeffs[1] = res->Parameter(1);
43
44 delete gainFit;
45}
46
47Bool_t TGainMatch::CoarseMatch(TH1* hist, Int_t chanNum, Double_t energy1, Double_t energy2)
48{
49 // This functions is used to perform a rough gain matching on a 60Co
50 // source by default. This makes gain matching over a wide range much easier to do afterwards
51 // This might have to be changed slightly if someone wants a different type of gaussian fitted
52 // for gain matching purposes.
53 if(hist == nullptr) {
54 return false;
55 }
56
57 fHist = hist;
58
59 // I might want to do a clear of the gainmatching parameters at this point.
60
61 // Check to see that the histogram isn't empty
62 if(hist->GetEntries() < 1) {
63 Error("CoarseMatch", "The histogram is empty");
64 return false;
65 }
66
67 // See if the channel exists. There is no point in finding the gains if we don't have anywhere to write it
69 if(chan == nullptr) {
70 if(chanNum != 9999) {
71 Warning("CoarseMatch", "Channel Number %d does not exist in current memory.", chanNum);
72 }
73 } else {
74 // Set the channel number
75 SetChannel(chan);
76 }
77
78 std::vector<Double_t> engVec; // This is the vector of expected energies
79 // We do coarse gain matching on 60Co. So I have hardcoded the energies for now
80 engVec.push_back(energy1);
81 engVec.push_back(energy2);
82
83 // Sort these in case the peak is returned in the wrong order
84 std::sort(engVec.begin(), engVec.end());
85
86 // Change the histogram range so that the search ignores potential low energy noise.
87 Int_t high = hist->GetXaxis()->GetLast();
88 hist->GetXaxis()->SetRangeUser(100, hist->GetXaxis()->GetBinCenter(high));
89 // Use a TSpectrum to find the two largest peaks in the spectrum
90 auto* spec = new TSpectrum; // This might not have to be allocated
91 Int_t nFound = spec->Search(hist, 2, "", 0.50); // This returns peaks in order of their height in the spectrum.
92
93 // return histogram to proper range
94 hist->GetXaxis()->UnZoom();
95 // If we didn't find two peaks, it is likely we gave it garbage
96 if(nFound < 2) {
97 Error("CoarseMatch", "Did not find enough peaks");
98 delete spec;
99 return false;
100 }
101
102 // We want to store the centroids of the found peaks
103 std::vector<Double_t> foundBin;
104 for(int x = 0; x < 2;
105 x++) { // I have hard-coded this to 2 because I'm assuming the rough match peaks will be by far the largest.
106 foundBin.push_back(spec->GetPositionX()[x]);
107 std::cout << "Found peak at bin " << foundBin[x] << std::endl;
108 }
109 std::sort(foundBin.begin(), foundBin.end());
110
111 // Get Bin Width for use later. I am using the first peak found to set the bin width
112 // If you are using antisymmetric binning... god help you.
113 // Double_t binWidth = hist->GetBinWidth(foundBin[0]);
114
115 // Set the number of data points to 2. In the gain matching graph.
116 Set(2);
117
118 // We now want to create a peak for each one we found (2) and fit them.
119 for(int x = 0; x < 2; x++) {
120 TPeak tmpPeak(foundBin[x], foundBin[x] - fCoarseRange, foundBin[x] + fCoarseRange);
121 tmpPeak.SetName(Form("GM_Cent_%lf", foundBin[x])); // Change the name of the TPeak to know it's origin
122 tmpPeak.SetLineColor(static_cast<Color_t>(2 * x + 2));
123 tmpPeak.Fit(hist, "Q+");
124 tmpPeak.ReleaseParameter(3);
125 tmpPeak.ReleaseParameter(4);
126 tmpPeak.SetLineColor(static_cast<Color_t>(2 * x + 3));
127 tmpPeak.Fit(hist, "Q+");
128 SetPoint(x, tmpPeak.GetParameter("centroid"), engVec[x]);
129 }
130
131 auto* gainFit = new TF1("gain", "pol1");
132
133 TFitResultPtr res = Fit(gainFit, "SC0");
134 SetFitFunction(GetFunction("gain")); // Have to do this because I want to delete gainFit.
135 fGainCoeffs[0] = res->Parameter(0);
136 fGainCoeffs[1] = res->Parameter(1);
137
138 delete gainFit;
139 // We have finished gain matching so let the TGainMatch know that it is a coarse gain
140 fCoarseMatch = true;
141 delete spec;
142 return true;
143}
144
145Bool_t TGainMatch::FineMatchFast(TH1* hist1, TPeak* peak1, TH1* hist2, TPeak* peak2, Int_t channelNum)
146{
147 // You need to pass a TPeak with the centroid and range set to the real energy centroid and ranges.
148 // The function uses the coarse gain parameters to find the peak and gain matches the raw spectrum.
149 // This is more useful as it allows a script to find all of the peaks.
150 if((hist1 == nullptr) || (hist2 == nullptr)) {
151 Error("FineMatchFast", "No histogram being pointed to");
152 return false;
153 }
154
155 // Check to see that the histogram isn't empty
156 if(hist1->GetEntries() < 1 || hist2->GetEntries() < 1) {
157 Error("FineMatchFast", "Histogram is empty");
158 return false;
159 }
160
161 // See if the channel exists. There is no point in finding the gains if we don't have anywhere to write it
162 Double_t gain = 0.;
163 Double_t offset = 0.;
164 TChannel* chan = TChannel::GetChannelByNumber(channelNum);
165 if(chan == nullptr) {
166 if(channelNum != 9999) {
167 Warning("FineMatchFast", "Channel Number %d does not exist in current memory.", channelNum);
168 }
169 if(GetFitFunction() != nullptr) {
170 gain = GetParameter(1);
171 offset = GetParameter(0);
172 } else {
173 Error("Fine Match", "There needs to be a coarse gain set to do a fine gain");
174 return false;
175 }
176 } else {
177 SetChannel(chan);
178 // The first thing we need to do is "un-gain correct" the centroids of the TPeaks.
179 // What we are actually doing is applying the recirpocal gain to the energy of the real peak
180 // to figure out where the centroid of the charge is spectrum is roughly going to be
181 // First read in the rough gain coefficients
182 std::vector<Float_t> roughCoeffs = chan->GetENGCoeff();
183 gain = roughCoeffs.at(1);
184 offset = roughCoeffs.at(0);
185 }
186
187 // The reason I'm using TPeak here is we might want to gain match "TPhotopeaks", or "TElectronPeaks", or
188 //"TCrazyNonGaussian" Peak. All we care about is that it has a centroid.
189 if((peak1 == nullptr) || (peak2 == nullptr)) {
190 Error("FineMatchFast", "No TPeak being pointed to");
191 return false;
192 }
193
194 // Set the channel number
195 Set(2);
196
197 // Find the energy of the peak that we want to use
198 std::array<Double_t, 2> energy = {peak1->GetParameter("centroid"), peak2->GetParameter("centroid")};
199 std::cout << peak1->GetParameter("centroid") << " ENERGIES " << energy[1] << std::endl;
200 // Offsets are very small right now so I'm not including them until they become a problem.
201 peak1->SetParameter("centroid", (energy[0] - offset) / gain);
202 peak2->SetParameter("centroid", (energy[1] - offset) / gain);
203 // Change the range for the fit to be in the gain corrected spectrum
204
205 peak1->SetRange((peak1->GetXmin() - offset) / gain, (peak1->GetXmax() - offset) / gain);
206 peak2->SetRange((peak2->GetXmin() - offset) / gain, (peak2->GetXmax() - offset) / gain);
207
208 // The gains won't be perfect, so we need to search for the peak within a range.
209 hist1->GetXaxis()->SetRangeUser(peak1->GetXmin() - 20., peak1->GetXmax() + 20.);
210 TSpectrum spec;
211 Int_t nFound = spec.Search(hist1, 2, "", 0.3);
212
213 for(int x = 0; x < nFound; x++) {
214 std::cout << spec.GetPositionX()[x] << std::endl;
215 }
216 Double_t closestPeak = 0;
217 Double_t closestDiff = 10000;
218 for(int x = 0; x < nFound; x++) {
219 if(fabs(peak1->GetCentroid() - spec.GetPositionX()[x]) < closestDiff) {
220 closestPeak = spec.GetPositionX()[x];
221 closestDiff = fabs(peak1->GetCentroid() - spec.GetPositionX()[x]);
222 }
223 }
224
225 Double_t rangeWidth = (peak1->GetXmax() - peak1->GetXmin()) / 2.;
226 peak1->SetParameter("centroid", closestPeak);
227 peak1->SetRange(peak1->GetCentroid() - rangeWidth, peak1->GetCentroid() + rangeWidth);
228 std::cout << "Centroid Guess " << peak1->GetCentroid() << std::endl;
229 std::cout << "Range Low " << peak1->GetXmin() << " " << peak1->GetXmax() << std::endl;
230
231 closestPeak = 0;
232 closestDiff = 10000;
233 hist2->GetXaxis()->SetRangeUser(peak2->GetXmin() - 20., peak2->GetXmax() + 20.);
234 TSpectrum spec2;
235 nFound = spec2.Search(hist2, 2, "", 0.3); // Search the next histogram
236 for(int x = 0; x < nFound; x++) {
237 std::cout << spec2.GetPositionX()[x] << std::endl;
238 }
239
240 for(int x = 0; x < nFound; x++) {
241 if(fabs(peak2->GetCentroid() - spec2.GetPositionX()[x]) < closestDiff) {
242 closestPeak = spec2.GetPositionX()[x];
243 closestDiff = fabs(peak2->GetCentroid() - spec2.GetPositionX()[x]);
244 }
245 }
246 Double_t rangeWidth2 = (peak2->GetXmax() - peak2->GetXmin()) / 2.;
247 peak2->SetParameter("centroid", closestPeak);
248 peak2->SetRange(peak2->GetCentroid() - rangeWidth2, peak2->GetCentroid() + rangeWidth2);
249
250 std::cout << "Centroid Guess " << peak2->GetCentroid() << std::endl;
251 std::cout << "Range High " << peak2->GetXmin() << " " << peak2->GetXmax() << std::endl;
252
253 hist1->GetXaxis()->UnZoom();
254 hist2->GetXaxis()->UnZoom();
255 peak1->Fit(hist1, "MS+");
256 peak2->Fit(hist2, "MS+");
257
258 hist1->Draw();
259 peak1->Draw("same");
260 peak2->Draw("same");
261
262 std::array<Double_t, 2> centroid = {peak1->GetCentroid(), peak2->GetCentroid()};
263
264 // Put the peaks in order for ease (if the user put them in the wrong order)
265 // Apparantly there is a TGraph Sort method. Might look into this later.
266 if(energy[0] > energy[1]) {
267 std::swap(energy[0], energy[1]);
268 std::swap(centroid[0], centroid[1]);
269 }
270
271 SetPoint(0, centroid[0], energy[0]);
272 SetPoint(1, centroid[1], energy[1]);
273
274 auto* gainFit = new TF1("gain", "pol1");
275
276 TFitResultPtr res = Fit(gainFit, "SC0");
277 SetFitFunction(GetFunction("gain")); // Have to do this because I want to delete gainFit
278 fGainCoeffs[0] = res->Parameter(0);
279 fGainCoeffs[1] = res->Parameter(1);
280
281 delete gainFit;
282
283 fCoarseMatch = false;
284 return true;
285}
286
287Bool_t TGainMatch::FineMatchFast(TH1* hist, TPeak* peak1, TPeak*, Int_t channelNum)
288{
289 // You need to pass a TPeak with the centroid and range set to the real energy centroid and ranges.
290 // The function uses the coarse gain parameters to find the peak and gain matches the raw spectrum.
291 // This is more useful as it allows a script to find all of the peaks.
292 return FineMatchFast(hist, peak1, hist, peak1, channelNum);
293}
294
295Bool_t TGainMatch::FineMatchFast(TH1* hist, Double_t energy1, Double_t energy2, Int_t channelNum)
296{
297 // Performs fine gain matching on one histogram once we have set the rough energy
298 // coefficients.
299 return FineMatchFast(hist, energy1, hist, energy2, channelNum);
300}
301
302Bool_t TGainMatch::FineMatchFast(TH1* hist1, Double_t energy1, TH1* hist2, Double_t energy2, Int_t channelNum)
303{
304 // Performs fine gain matching on two histograms once we have set the rough energy
305 // coefficients. You use this if you have a two different sources giving your full range
306 // of energy. This histograms should be binned the same. NOT IMPLEMENTED
307
308 // using an automatic range of 10 keV for testing purposes.
309 auto* peak1 = new TPeak(energy1, energy1 - 10.0, energy1 + 10.0);
310 auto* peak2 = new TPeak(energy2, energy2 - 10.0, energy2 + 10.0);
311
312 Bool_t result = FineMatchFast(hist1, peak1, hist2, peak2, channelNum);
313
314 delete peak1;
315 delete peak2;
316 return result;
317 return false;
318}
319
321{
322 if(GetChannel() == nullptr) {
323 Error("WriteToChannel", "No Channel Set");
324 return;
325 }
327 std::cout << "Writing to channel " << GetChannel()->GetNumber() << std::endl;
328 std::cout << "p0 = " << GetParameter(0) << " \t p1 = " << GetParameter(1) << std::endl;
329 // Set the energy parameters based on the fitted gains.
330 GetChannel()->AddENGCoefficient(static_cast<float>(GetParameter(0)));
331 GetChannel()->AddENGCoefficient(static_cast<float>(GetParameter(1)));
332}
333
334void TGainMatch::Print(Option_t*) const
335{
336 std::cout << std::endl;
337 std::cout << "GainMatching: " << std::endl;
338 if(fCoarseMatch) {
339 std::cout << "COARSE" << std::endl;
340 } else {
341 std::cout << "FINE" << std::endl;
342 }
343
344 if(fAligned) {
345 std::cout << "Aligned" << std::endl;
346 } else {
347 std::cout << "NOT Aligned" << std::endl;
348 }
349
350 std::cout << "Gain Coefficients: " << fGainCoeffs[0] << "\t" << fGainCoeffs[1] << std::endl;
351 std::cout << "Align Coefficients: " << fAlignCoeffs[0] << "\t" << fAlignCoeffs[1] << std::endl;
352
353 TCal::Print();
354}
355
356Bool_t TGainMatch::CoarseMatchAll(TCalManager* cm, TH2* mat, Double_t, Double_t)
357{
358 // If you supply this function with a matrix of Channel vs. energy it will automatically slice,
359 // and figure out the coarse gains. I might add a channel range option later
360 std::vector<Int_t> badlist;
361 auto* gm = new TGainMatch;
362 if(cm == nullptr) {
363 gm->Error("CoarseMatchAll", "CalManager Pointer is nullptr");
364 return false;
365 }
366 if(mat == nullptr) {
367 gm->Error("CoarseMatchAll", "TH2 Pointer is nullptr");
368 return false;
369 }
370 // Find the range of channels provided
371 Int_t first_chan = mat->GetXaxis()->GetFirst();
372 Int_t last_chan = mat->GetXaxis()->GetLast();
373 // The first thing we need to do is slice the matrix into it's channel vs energy.
374 for(int chan = first_chan; chan <= last_chan; chan++) {
375 gm->Clear();
376 std::cout << std::endl
377 << "Now fitting channel " << chan - 1 << std::endl;
378 auto* h1 = mat->ProjectionY(Form("Channel%d", chan), chan, chan, "o");
379 std::cout << "BIN WIDTH " << h1->GetXaxis()->GetBinWidth(h1->GetXaxis()->GetFirst() + 1) << std::endl;
380 if(h1->Integral() < 100) {
381 continue;
382 }
383
384 if(!(gm->CoarseMatch(h1, chan - 1))) {
385 badlist.push_back(chan - 1);
386 continue;
387 }
388 gm->SetName(Form("gm_chan_%d", chan - 1));
389 cm->AddToManager(gm, chan - 1);
390 }
391 if(!badlist.empty()) {
392 std::cout << "The following channels did not gain match properly: ";
393 }
394 for(int i : badlist) {
395 std::cout << i << "\t";
396 }
397
398 delete gm;
399
400 return true;
401}
402
403Bool_t TGainMatch::FineMatchFastAll(TCalManager* cm, TH2* mat1, Double_t energy1, TH2* mat2, Double_t energy2)
404{
405
406 // using an automatic range of 10 keV for testing purposes.
407 // We need to include bin width likely?
408 auto* peak1 = new TPeak(energy1, energy1 - 10.0, energy1 + 10.0);
409 auto* peak2 = new TPeak(energy2, energy2 - 10.0, energy2 + 10.0);
410
411 Bool_t result = TGainMatch::FineMatchFastAll(cm, mat1, peak1, mat2, peak2);
412
413 delete peak1;
414 delete peak2;
415 return result;
416}
417
418Bool_t TGainMatch::FineMatchFastAll(TCalManager* cm, TH2* mat, Double_t energy1, Double_t energy2)
419{
420 return TGainMatch::FineMatchFastAll(cm, mat, energy1, mat, energy2);
421}
422
423Bool_t TGainMatch::FineMatchFastAll(TCalManager* cm, TH2* mat1, TPeak* peak1, TH2* mat2, TPeak* peak2)
424{
425 // If you supply this function with a matrix of Channel vs. energy it will automatically slice,
426 // and figure out the fine gains. I might add a channel range option later
427 std::vector<Int_t> badlist;
428 auto* gm = new TGainMatch;
429 if(cm == nullptr) {
430 gm->Error("FineMatchFastAll", "CalManager Pointer is nullptr");
431 return false;
432 }
433 if((mat1 == nullptr) || (mat2 == nullptr)) {
434 gm->Error("FineMatchFastAll", "TH2 Pointer is nullptr");
435 return false;
436 }
437 if((peak1 == nullptr) || (peak2 == nullptr)) {
438 gm->Error("FineMatchFastAll", "No TPeak being pointed to");
439 return false;
440 }
441
442 // Find the range of channels provided, we want to use the largest range provided
443 Int_t first_chan1 = mat1->GetXaxis()->GetFirst();
444 Int_t last_chan1 = mat1->GetXaxis()->GetLast();
445 Int_t first_chan2 = mat2->GetXaxis()->GetFirst();
446 Int_t last_chan2 = mat2->GetXaxis()->GetLast();
447 Int_t first_chan = std::min(first_chan1, first_chan2);
448 Int_t last_chan = std::max(last_chan1, last_chan2);
449 // The first thing we need to do is slice the matrix into it's channel vs energy.
450 for(int chan = first_chan; chan <= last_chan; chan++) {
451 gm->Clear();
452 // Make a copy of the TPeaks so that we can fit each of them
453 // TPeak* copyPeak1 = static_cast<TPeak*>(peak1->Clone());//Clone creates smart pointers so these will be
454 // dealoccated automatically.
455 // TPeak* copyPeak2 = static_cast<TPeak*>(peak2->Clone());
456 auto* copyPeak1 = new TPeak(*peak1);
457 auto* copyPeak2 = new TPeak(*peak2);
458
459 std::cout << std::endl
460 << "Now fitting channel: " << chan - 1 << std::endl;
461 auto* h1 = mat1->ProjectionY(Form("Channel%d_mat1", chan - 1), chan, chan, "o");
462 auto* h2 = mat2->ProjectionY(Form("Channel%d_mat2", chan - 1), chan, chan, "o");
463 if(h1->Integral() < 100 || h2->Integral() < 100) {
464 gm->Warning("FineMatchFastAll", "Empty channel = %d", chan - 1);
465 continue;
466 }
467 if(!(gm->FineMatchFast(h1, copyPeak1, h2, copyPeak2, chan - 1))) {
468 badlist.push_back(chan - 1);
469 continue;
470 }
471 cm->AddToManager(gm);
472
473 delete copyPeak1;
474 delete copyPeak2;
475 }
476 if(!badlist.empty()) {
477 std::cout << "The following channels did not gain match properly: ";
478 }
479 for(int i : badlist) {
480 std::cout << i << "\t";
481 }
482
483 delete gm;
484
485 return true;
486}
487
488Bool_t TGainMatch::FineMatchFastAll(TCalManager* cm, TH2* mat, TPeak* peak1, TPeak* peak2)
489{
490 return TGainMatch::FineMatchFastAll(cm, mat, peak1, mat, peak2);
491}
492
493void TGainMatch::Clear(Option_t*)
494{
495 fCoarseMatch = true;
496 fAligned = false;
497 fAlignCoeffs[0] = 0.0;
498 fAlignCoeffs[1] = 1.0;
499 fGainCoeffs[0] = 0.0;
500 fGainCoeffs[1] = 1.0;
501 TCal::Clear();
502}
503
504Bool_t TGainMatch::Align(TH1* test, TH1* hist, Int_t low_range, Int_t high_range)
505{
506 // Minimizes the chi^2 between the bin contents of the test histogram and the bin contents of the histogram to be
507 // matched'
508 if((test == nullptr) || (hist == nullptr)) {
509 std::cout << "Unassigned histogram" << std::endl;
510 return false;
511 }
512 fHist = hist; // Need this histogram to be seen by ftotal...Don't know how else to do this right now.
513 // TF1* tmpfunc = new
514 // TF1("tmpfunc",this,&TGainMatch::HistCompare,test->GetMinimumStored()+1,test->GetMaximumStored()-1,3);
515
516 /* //Initial Guesses
517
518 TSpectrum stest,shist;
519 TGraph guess_graph;
520 Int_t nFoundtest = stest.Search(test);
521 Int_t nFoundhist = shist.Search(hist);
522 TF1* shift_guess = new TF1("shift_guess","pol1");
523 if(nFoundtest >1 && nFoundhist > 1) {
524 stest.GetPositionX()[0]/shist.GetPositionX()[0];
525 guess_graph.SetPoint(0,shist.GetPositionX()[0],stest.GetPositionX()[0]);
526 guess_graph.SetPoint(1,shist.GetPositionX()[1],stest.GetPositionX()[1]);
527
528 guess_graph.Print();
529
530 TFitResultPtr res = guess_graph.Fit(shift_guess,"SC0");
531 }
532 */
533
534 TF1* tmpfunc = new TF1("tmpfunc", this, &TGainMatch::HistCompare, low_range, high_range, 3);
535 tmpfunc->SetNpx(10000);
536 tmpfunc->SetParameters(1.0, 1.0, 1.0);
537
538 // hist->Sumw2();
539
540 /* for(int i =0; i<hist->GetXaxis()->GetNbins();i++) {
541 // hist->SetBinError(i,TMath::Sqrt(TMath::Sqrt(hist->GetBinContent(i))));
542 hist->SetBinError(i,1./(1.+hist->GetBinContent(i)));
543 }
544 */
545 // ftot->SetParLimits(0,.3*norm,norm);
546
547 // const char* minName = "Minuit2";
548 // const char* algoName = "Scan";
549 // ROOT::Math::MinimizerOptions::SetDefaultMinimizer("TMinuit2","Simplex");
550 // TVirtualFitter::SetDefaultFitter("Fumili"); //USE COMBINATION!!!!
551 ROOT::Math::MinimizerOptions::SetDefaultMinimizer("Minuit2", "Combination");
552 TVirtualFitter::SetPrecision(1.0e-10);
553 TVirtualFitter::SetMaxIterations(10000);
554 TFitResultPtr res = test->Fit("tmpfunc", "RSILV");
555 fAlignCoeffs[0] = res->Parameter(1);
556 fAlignCoeffs[1] = res->Parameter(2);
557 std::cout << "Chi2: " << res->Chi2() / res->Ndf() << std::endl;
558 if(std::abs(res->Parameter(0) - 1.00) > 20.) {
559 return false;
560 }
561 if(std::abs(res->Parameter(2) - 1.00) > 5.) {
562 return false;
563 }
564 fAligned = true;
565 // delete shift_guess;
566 return true;
567}
568
569Bool_t TGainMatch::AlignAll(TCalManager* cm, TH1* hist, TH2* mat, Int_t low_range, Int_t high_range)
570{
571 std::vector<Int_t> badlist;
572 auto* gm = new TGainMatch;
573 if(cm == nullptr) {
574 gm->Error("AlignAll", "CalManager Pointer is nullptr");
575 return false;
576 }
577 if(mat == nullptr) {
578 gm->Error("AlignAll", "TH2 Pointer is nullptr");
579 return false;
580 }
581 if(hist == nullptr) {
582 gm->Error("AlignAll", "TH1 Pointer is nullptr");
583 return false;
584 }
585 // Find the range of channels provided
586 Int_t first_chan = mat->GetXaxis()->GetFirst();
587 // Int_t last_chan = mat->GetXaxis()->GetLast();
588 // The first thing we need to do is slice the matrix into it's channel vs energy.
589 // for(int chan=first_chan; chan<=last_chan;chan++) {
590 for(int chan = first_chan; chan <= 2; chan++) {
591 gm->Clear();
592 std::cout << std::endl
593 << "Now fitting channel: " << chan << std::endl;
594 auto* h1 = mat->ProjectionY(Form("Channel%d", chan), chan + 1, chan + 1, "o");
595 std::cout << "BIN WIDTH " << h1->GetXaxis()->GetBinWidth(h1->GetXaxis()->GetFirst() + 1) << std::endl;
596 if(h1->Integral() < 100) {
597 continue;
598 }
599
600 if(!(gm->Align(hist, h1, low_range, high_range))) {
601 badlist.push_back(chan);
602 continue;
603 }
604 cm->AddToManager(gm);
605 }
606 if(!badlist.empty()) {
607 std::cout << "The following channels did not gain match properly: ";
608 }
609 for(int i : badlist) {
610 std::cout << i << "\t";
611 }
612
613 delete gm;
614
615 return true;
616}
617
618Bool_t TGainMatch::FineMatchAll(TCalManager* cm, TH2* charge_mat, TH2* eng_mat, Int_t testchan, Double_t energy1,
619 Double_t energy2, Int_t low_range, Int_t high_range)
620{
621 // Should be run to correct drifting gains. Requires a previously gain matched spectrum, and ideally a roughly energy
622 // calibrated matrix
623
624 auto* gm = new TGainMatch;
625 if(cm == nullptr) {
626 gm->Error("FineMatchAll", "CalManager Pointer is nullptr");
627 return false;
628 }
629 if((charge_mat == nullptr) || (eng_mat == nullptr)) {
630 gm->Error("FineMatchAll", "TH2 Pointer is nullptr");
631 return false;
632 }
633 auto binwidth = static_cast<Int_t>(0.5 + 1. / eng_mat->GetYaxis()->GetBinWidth(100));
634 eng_mat->RebinY(binwidth);
635
636 std::vector<Int_t> badlist;
637 // Find the range of channels provided, we want to use the largest range provided
638 Int_t first_chan = eng_mat->GetXaxis()->GetFirst();
639 Int_t last_chan = eng_mat->GetXaxis()->GetLast();
640 // The first thing we need to do is slice the matrix into it's channel vs energy.
641 auto* testhist = eng_mat->ProjectionY(Form("Test%d_mat", testchan), testchan + 1, testchan + 1, "o");
642
643 for(int chan = first_chan; chan <= last_chan; chan++) {
644 std::cout << std::endl
645 << "Now fitting channel: " << chan - 1 << std::endl;
646 auto* chargeh = charge_mat->ProjectionY(Form("Charge%d_mat", chan - 1), chan, chan, "o");
647 auto* engh = eng_mat->ProjectionY(Form("Energy%d_mat", chan - 1), chan, chan, "o");
648 if(chargeh->Integral() < 100 || chargeh->Integral() < 100) {
649 gm->Warning("FineMatchAll", "Empty channel = %d", chan - 1);
650 continue;
651 }
652 // Needs to align before matching
653 if(!(gm->FineMatch(engh, testhist, chargeh, energy1, energy2, low_range, high_range, chan - 1))) {
654 badlist.push_back(chan - 1);
655 continue;
656 }
657 cm->AddToManager(gm);
658 }
659 if(!badlist.empty()) {
660 std::cout << "The following channels did not gain match properly: ";
661 }
662 for(int i : badlist) {
663 std::cout << i << "\t";
664 }
665
666 delete testhist;
667 delete gm;
668
669 return true;
670}
671
672Bool_t TGainMatch::FineMatch(TH1* energyHist, TH1* testhist, TH1* chargeHist, Double_t energy1, Double_t energy2,
673 Int_t low_range, Int_t high_range, Int_t channelNum)
674{
675 // Should be run to correct drifting gains. Requires a previously gain matched spectrum, and ideally a roughly energy
676 // calibrated spectrum
677 if(!Align(testhist, energyHist, low_range, high_range)) {
678 return false;
679 }
680 TH1* hist2 = chargeHist; // Cheating for easier modification later
681 if((chargeHist == nullptr) || (testhist == nullptr) || (energyHist == nullptr)) {
682 Error("FineMatch", "No histogram being pointed to");
683 return false;
684 }
685
686 // Check to see that the histogram isn't empty
687 if(chargeHist->GetEntries() < 1 || testhist->GetEntries() < 1 || energyHist->GetEntries() < 1) {
688 Error("FineMatchFast", "Histogram is empty");
689 return false;
690 }
691
692 // See if the channel exists. There is no point in finding the gains if we don't have anywhere to write it
693 Double_t gain = 0.;
694 Double_t offset = 0.;
695 TChannel* chan = TChannel::GetChannelByNumber(channelNum);
696 if(chan == nullptr) {
697 if(channelNum != 9999) {
698 Warning("FineMatch", "Channel Number %d does not exist in current memory.", channelNum);
699 }
700 if(GetFitFunction() != nullptr) {
701 gain = GetParameter(1);
702 offset = GetParameter(0);
703 } else {
704 Error("FineMatch", "There needs to be a coarse gain set to do a fine gain");
705 return false;
706 }
707 } else {
708 SetChannel(chan);
709 // The first thing we need to do is "un-gain correct" the centroids of the TPeaks.
710 // What we are actually doing is applying the recirpocal gain to the energy of the real peak
711 // to figure out where the centroid of the charge is spectrum is roughly going to be
712 // First read in the rough gain coefficients
713 std::vector<Float_t> roughCoeffs = chan->GetENGCoeff();
714 gain = roughCoeffs.at(1);
715 offset = roughCoeffs.at(0);
716 }
717
718 auto* peak1 = new TPeak(energy1, energy1 - 15.0, energy1 + 15.0);
719 auto* peak2 = new TPeak(energy2, energy2 - 15.0, energy2 + 15.0);
720
721 // Set the channel number
722 // Graph()->Set(2);
723 Set(2);
724
725 // Find the energy of the peak that we want to use
726 std::array<Double_t, 2> energy = {peak1->GetParameter("centroid"), peak2->GetParameter("centroid")};
727 std::cout << peak1->GetParameter("centroid") << " ENERGIES " << energy[1] << std::endl;
728 // Offsets are very small right now so I'm not including them until they become a problem.
729 // peak1->SetParameter("centroid",((energy[0]-offset)/gain)*fAlignCoeffs[1] + fAlignCoeffs[0]);
730 // peak2->SetParameter("centroid",((energy[0]-offset)/gain)*fAlignCoeffs[1] + fAlignCoeffs[0]);
731 peak1->SetParameter("centroid", (energy[0] * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain);
732 peak2->SetParameter("centroid", (energy[1] * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain);
733 // Change the range for the fit to be in the gain corrected spectrum
734
735 std::cout << " Should be: " << ((energy[0] * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain) << " from (" << energy[0] << " * " << fAlignCoeffs[1] << " + " << fAlignCoeffs[0] << " - " << offset << ")/" << gain << std::endl;
736 std::cout << " Should be: " << ((energy[1] * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain) << " from (" << energy[1] << " * " << fAlignCoeffs[1] << " + " << fAlignCoeffs[0] << " - " << offset << ")/" << gain << std::endl;
737
738 // peak1->SetRange(((peak1->GetXmin()-offset)/gain)*fAlignCoeffs[1] +
739 // fAlignCoeffs[0],((peak1->GetXmax()-offset)/gain)*fAlignCoeffs[1] + fAlignCoeffs[0]);
740 // peak2->SetRange(((peak2->GetXmin()-offset)/gain)*fAlignCoeffs[1] +
741 // fAlignCoeffs[0],((peak2->GetXmax()-offset)/gain)*fAlignCoeffs[1] + fAlignCoeffs[0]);
742 peak1->SetRange(((peak1->GetXmin() * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain),
743 ((peak1->GetXmax() * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain));
744 peak2->SetRange(((peak2->GetXmin() * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain),
745 ((peak2->GetXmax() * fAlignCoeffs[1] + fAlignCoeffs[0] - offset) / gain));
746
747 // The gains won't be perfect, so we need to search for the peak within a range.
748 TSpectrum spec;
749 chargeHist->GetXaxis()->SetRangeUser(peak1->GetXmin() - 20., peak1->GetXmax() + 20.);
750 Int_t nFound = spec.Search(chargeHist, 2, "", 0.3);
751
752 for(int x = 0; x < nFound; x++) {
753 std::cout << spec.GetPositionX()[x] << std::endl;
754 }
755 Double_t closestPeak = 0;
756 Double_t closestDiff = 10000;
757 for(int x = 0; x < nFound; x++) {
758 if(fabs(peak1->GetCentroid() - spec.GetPositionX()[x]) < closestDiff) {
759 closestPeak = spec.GetPositionX()[x];
760 closestDiff = fabs(peak1->GetCentroid() - spec.GetPositionX()[x]);
761 }
762 }
763
764 Double_t rangeWidth = (peak1->GetXmax() - peak1->GetXmin()) / 2.;
765 peak1->SetParameter("centroid", closestPeak);
766 peak1->SetRange(peak1->GetCentroid() - rangeWidth, peak1->GetCentroid() + rangeWidth);
767 std::cout << "Centroid Guess " << peak1->GetCentroid() << std::endl;
768 std::cout << "Range Low " << peak1->GetXmin() << " " << peak1->GetXmax() << std::endl;
769
770 closestDiff = 10000;
771 TSpectrum spec2;
772 hist2->GetXaxis()->SetRangeUser(peak2->GetXmin() - 20., peak2->GetXmax() + 20.);
773 std::cout << "RANGE: " << peak2->GetXmin() - 20. << " " << peak2->GetXmax() + 20. << std::endl;
774 nFound = spec2.Search(hist2, 2, "", 0.3); // Search the next histogram
775 if(nFound == 0) {
776 std::cout << "RANGE: " << peak2->GetXmin() - 40. << " " << peak2->GetXmax() + 40. << std::endl;
777 nFound = spec2.Search(hist2, 2, "", 0.3); // Search the next histogram
778 }
779
780 for(int x = 0; x < nFound; x++) {
781 std::cout << spec2.GetPositionX()[x] << std::endl;
782 }
783
784 Double_t largestPeak = spec2.GetPositionX()[0];
785 for(int x = 0; x < nFound; x++) {
786 closestDiff = std::min(closestDiff, fabs(peak2->GetCentroid() - spec2.GetPositionX()[x]));
787 }
788 Double_t rangeWidth2 = (peak2->GetXmax() - peak2->GetXmin()) / 2.;
789 peak2->SetParameter("centroid", largestPeak);
790 peak2->SetRange(peak2->GetCentroid() - rangeWidth2, peak2->GetCentroid() + rangeWidth2);
791
792 std::cout << "Centroid Guess " << peak2->GetCentroid() << std::endl;
793 std::cout << "Range High " << peak2->GetXmin() << " " << peak2->GetXmax() << std::endl;
794
795 chargeHist->GetXaxis()->UnZoom();
796 hist2->GetXaxis()->UnZoom();
797 peak1->InitParams(chargeHist);
798 peak2->InitParams(chargeHist);
799 peak1->SetParameter("sigma", TMath::Sqrt(9.0 + 4. * peak1->GetParameter("centroid") / 1000.) / 2.35);
800 peak2->SetParameter("sigma", TMath::Sqrt(9.0 + 4. * peak2->GetParameter("centroid") / 1000.) / 2.35);
801 peak1->Fit(chargeHist, "MSL+");
802 peak2->Fit(hist2, "MSL+");
803
804 chargeHist->Draw();
805 peak1->Draw("same");
806 peak2->Draw("same");
807
808 std::array<Double_t, 2> centroid = {peak1->GetCentroid(), peak2->GetCentroid()};
809
810 // Put the peaks in order for ease (if the user put them in the wrong order)
811 // Apparantly there is a TGraph Sort method. Might look into this later.
812 if(energy[0] > energy[1]) {
813 std::swap(energy[0], energy[1]);
814 std::swap(centroid[0], centroid[1]);
815 }
816
817 SetPoint(0, centroid[0], energy[0]);
818 SetPoint(1, centroid[1], energy[1]);
819
820 auto* gainFit = new TF1("gain", "pol1");
821
822 TFitResultPtr res = Fit(gainFit, "SC0");
823 SetFitFunction(GetFunction("gain")); // Have to do this because I want to delete gainFit
824 fGainCoeffs[0] = res->Parameter(0);
825 fGainCoeffs[1] = res->Parameter(1);
826
827 delete gainFit;
828
829 fCoarseMatch = false;
830 delete peak1;
831 delete peak2;
832 return true;
833}
834
835Double_t TGainMatch::HistCompare(Double_t* x, Double_t* par) // NOLINT(readability-non-const-parameter)
836{
837 Int_t bin = fHist->GetXaxis()->FindBin(x[0] * par[2] + par[1]);
838 Double_t content = fHist->GetBinContent(bin);
839
840 return par[0] * content;
841}
TH2D * mat
Definition UserFillObj.h:12
TH1D * hist
Definition UserFillObj.h:3
Definition TCal.h:31
void Print(Option_t *opt="") const override
Definition TCal.cxx:145
virtual void SetFitFunction(TF1 *func)
Definition TCal.h:47
virtual Double_t GetParameter(size_t parameter) const
Definition TCal.cxx:99
void Clear(Option_t *opt="") override
Definition TCal.cxx:137
Bool_t SetChannel(TChannel *chan)
Definition TCal.cxx:51
void Copy(TObject &obj) const override
Definition TCal.cxx:37
virtual TF1 * GetFitFunction() const
Definition TCal.h:46
TChannel * GetChannel() const
Definition TCal.cxx:121
Bool_t AddToManager(TCal *cal, UInt_t chanNum, Option_t *opt="")
std::vector< Float_t > GetENGCoeff(size_t range=0) const
Definition TChannel.h:189
void DestroyENGCal()
Definition TChannel.cxx:571
int GetNumber() const
Definition TChannel.h:166
static TChannel * GetChannelByNumber(int temp_num)
Definition TChannel.cxx:499
void AddENGCoefficient(Float_t temp, size_t range=0)
Definition TChannel.h:203
std::array< Double_t, 2 > fGainCoeffs
Definition TGainMatch.h:85
Bool_t FineMatchFast(TH1 *hist1, TPeak *peak1, TH1 *hist2, TPeak *peak2, Int_t channelNum=9999)
TH1 * fHist
Definition TGainMatch.h:83
static Double_t fDefaultCoarseRange
Definition TGainMatch.h:89
std::array< Double_t, 2 > fAlignCoeffs
Definition TGainMatch.h:84
void Clear(Option_t *opt="") override
static Bool_t FineMatchFastAll(TCalManager *cm, TH2 *mat, Double_t energy1, Double_t energy2)
Double_t HistCompare(Double_t *x, Double_t *par)
void WriteToChannel() const override
void Copy(TObject &obj) const override
Bool_t CoarseMatch(TH1 *hist, Int_t chanNum=9999, Double_t energy1=1173.228, Double_t energy2=1332.492)
void CalculateGain(Double_t cent1, Double_t cent2, Double_t eng1, Double_t eng2)
Bool_t fCoarseMatch
Definition TGainMatch.h:81
Bool_t Align(TH1 *test, TH1 *hist, Int_t low_range=100, Int_t high_range=600)
static Bool_t CoarseMatchAll(TCalManager *cm, TH2 *mat, Double_t energy1=1173.228, Double_t energy2=1332.492)
static Bool_t AlignAll(TCalManager *cm, TH1 *hist, TH2 *mat, Int_t low_range=100, Int_t high_range=600)
static Bool_t FineMatchAll(TCalManager *cm, TH2 *charge_mat, TH2 *eng_mat, Int_t testchan, Double_t energy1, Double_t energy2, Int_t low_range=100, Int_t high_range=600)
Bool_t FineMatch(TH1 *energyHist, TH1 *testhist, TH1 *chargeHist, Double_t energy1, Double_t energy2, Int_t low_range=100, Int_t high_range=600, Int_t channelNum=9999)
void Print(Option_t *opt="") const override
Double_t fCoarseRange
Definition TGainMatch.h:86
Bool_t fAligned
Definition TGainMatch.h:82
Definition TPeak.h:23
Bool_t Fit(TH1 *fitHist, Option_t *opt="")
Definition TPeak.cxx:196
Double_t GetCentroid() const
Definition TPeak.h:46