GRSISort "v4.1.1.0"
An extension of the ROOT analysis Framework
Loading...
Searching...
No Matches
GriffinCTFix.cxx
Go to the documentation of this file.
1#include <iostream>
2
3#include "TFile.h"
4#include "TH2.h"
5#include "TF1.h"
6#include "TCutG.h"
7#include "TGraphErrors.h"
8#include "TString.h"
9#include "TObjString.h"
10#include "TObjArray.h"
11#include "TProfile.h"
12
13#include "TChannel.h"
14#include "TGriffin.h"
15#include "TUserSettings.h"
16
17double CrossTalkFit(double* x, double* par) // NOLINT(readability-non-const-parameter)
18{
19 // This function is the linear fit function, but uses the CT coefficients as parameters instead of slope and
20 // intercept
21 double k0 = par[0] / (1. - par[0]);
22 double k1 = par[1] / (1. - par[1]);
23 double energy = par[2];
24
25 double slope = -(1. + k0) / (1. + k1);
26 double intercept = energy * (1. + k0 / (1. + k1));
27
28 return x[0] * slope + intercept;
29}
30
31double* CrossTalkFix(int det, double energy, TFile* inputFile, int minimumCounts)
32{
33 // The outfile is implicit since it was the last file that was open.
34 static double largestCorrection = 0.0;
35
36 // Clear all of the CT calibrations for this detector.
37 for(int i = 0; i < 4; ++i) {
38 TChannel* chan = TChannel::FindChannelByName(Form("GRG%02d%sN00A", det, TGriffin::GetColorFromNumber(i)));
39 if(chan == nullptr) {
40 std::cout << DRED << "Couldn't find a channel for "
41 << Form("GRG%02d%sN00A", det, TGriffin::GetColorFromNumber(i)) << RESET_COLOR << std::endl;
42 continue;
43 }
44 chan->DestroyCTCal();
45 }
46
47 static int largestDet = -1;
48 static int largestCrystal1 = -1;
49 static int largestCrystal2 = -1;
50
51 std::string namebase = Form("det_%d", det);
52
53 // This range seems to be working fairly well since no shift should be larger than say 6 or 7 keV
54 double lowCut = energy - 15;
55 double highCut = energy + 15;
56
57 // create diagonal cut
58 std::array<double, 5> xpts = {lowCut, 0, 0, highCut, lowCut};
59 std::array<double, 5> ypts = {0, lowCut, highCut, 0, 0};
60 TCutG cut("cut", 5, xpts.data(), ypts.data());
61
62 auto* d = new double[16]; // matrix of coefficients
63 auto* eD = new double[16]; // matrix of errors
64
65 for(int crystal1 = 0; crystal1 < 4; crystal1++) {
66 for(int crystal2 = crystal1 + 1; crystal2 < 4; crystal2++) {
67 // Load all of the addback matrices in and put them into a vector of TH2*
68 std::string name = Form("%s_%d_%d", namebase.c_str(), crystal1, crystal2);
69 TH2* mat = dynamic_cast<TH2*>(inputFile->Get(name.c_str()));
70 if(mat == nullptr) {
71 std::cout << "can not find: " << name << std::endl;
72 return nullptr;
73 }
74 std::cout << mat->GetName() << std::endl;
75 int xbins = mat->GetNbinsX();
76 int ybins = mat->GetNbinsY();
77
78 TH2* cmat = dynamic_cast<TH2*>(mat->Clone(Form("%s_clone", mat->GetName())));
79 cmat->Reset();
80
81 // I make a graph out of the "addback line" because I don't like the way TProfile handles the empty bins
82 auto* fitGraph = new TGraphErrors;
83 fitGraph->SetNameTitle(Form("%s_graph", mat->GetName()), "Graph");
84
85 // This loop turns the addback plot and TCut into the TGraphErrors
86 int rejectedBins = 0;
87 for(int i = 1; i <= xbins; i++) {
88 bool insideYet = false;
89 for(int j = 1; j <= ybins; j++) {
90 double xc = mat->GetXaxis()->GetBinCenter(i);
91 double yc = mat->GetYaxis()->GetBinCenter(j);
92 if(cut.IsInside(xc, yc) != 0) {
93 if(!insideYet) {
94 insideYet = true;
95 }
96 cmat->Fill(xc, yc, mat->GetBinContent(i, j));
97 }
98 }
99 cmat->GetXaxis()->SetRange(i, i);
100 // This makes sure that there are at least 4 counts in the "y bin". I'd prefer this to be higher,
101 // but that requires more 60Co statistics. The reason I do this is because RMS and SD of the mean really only
102 // works for us if we have enough counts that the mean is actually a good representation of the true value.
103 // This is something TProfile does not do for us, and seems to skew the result a bit.
104 if(cmat->Integral() > minimumCounts) {
105 fitGraph->SetPoint(fitGraph->GetN(), cmat->GetYaxis()->GetBinCenter(i), cmat->GetMean(2));
106 fitGraph->SetPointError(fitGraph->GetN() - 1, cmat->GetXaxis()->GetBinWidth(i), cmat->GetMeanError(2));
107 } else {
108 ++rejectedBins;
109 }
110 }
111 // unzoom the x-axis
112 cmat->GetXaxis()->SetRange(1, cmat->GetXaxis()->GetNbins());
113 cmat->Write();
114
115 if(rejectedBins > 0) {
116 std::cout << name << ": rejected " << rejectedBins << " bins out of " << xbins << " bins, because the number of counts in the projection of this bin is less than " << minimumCounts << std::endl;
117 }
118
119 if(fitGraph->GetN() <= 0) {
120 std::cout << name << ": graph doesn't have any data points (" << fitGraph->GetN() << "), going to skip fitting it" << std::endl;
121 continue;
122 }
123
124 // This fits the TGraph
125 auto* fpx = new TF1(Form("pxfit_%i_%i_%i", det, crystal1, crystal2), CrossTalkFit, 6, 1167, 3);
126 fpx->SetParameter(0, 0.0001);
127 fpx->SetParameter(1, 0.0001);
128 fpx->SetParameter(2, energy);
129 fpx->FixParameter(2, energy);
130 fitGraph->Fit(fpx);
131 fitGraph->Write();
132 delete fitGraph;
133
134 // create and write profile
135 TProfile* px = cmat->ProfileX();
136 px->Write();
137 // Make a residuals plot
138 TH1* residualPlot = new TH1D(Form("%s_resid", fpx->GetName()), "residuals", 2000, 0, 2000);
139 for(int i = 0; i < residualPlot->GetNbinsX(); ++i) {
140 if(px->GetBinContent(i) != 0) {
141 residualPlot->SetBinContent(i, px->GetBinContent(i) - fpx->Eval(residualPlot->GetBinCenter(i)));
142 }
143 residualPlot->SetBinError(i, px->GetBinError(i));
144 }
145 residualPlot->Write();
146 delete residualPlot;
147 delete cmat;
148
149 // for some reason from here on out crystal1 and crystal2 are used in reverse?
150 // was this maybe because before we used xind and yind instead which were from the tokenized name using entry N-1 (for xind) and N-2 (for yind)?
151 // temporary swapped the two - except for the largest stuff?
152 std::cout << "=====================" << std::endl;
153 std::cout << mat->GetName() << std::endl;
154 std::cout << "d" << crystal1 << crystal2 << " at zero " << (fpx->Eval(energy)) / energy << std::endl;
155 std::cout << "=====================" << std::endl;
156
157 // Fill the parameter matrix with the parameters from the fit.
158 d[crystal2 * 4 + crystal1] = fpx->GetParameter(0);
159 d[crystal1 * 4 + crystal2] = fpx->GetParameter(1);
160 eD[crystal2 * 4 + crystal1] = fpx->GetParError(0);
161 eD[crystal1 * 4 + crystal2] = fpx->GetParError(1);
162
163 // Keep track of the largest correction and output that to screen,
164 // This helps identify problem channels, or mistakes
165 if(fpx->GetParameter(0) > largestCorrection) {
166 largestCorrection = fpx->GetParameter(0);
167 largestDet = det;
168 largestCrystal2 = crystal2;
169 largestCrystal1 = crystal1;
170 }
171 if(fpx->GetParameter(1) > largestCorrection) {
172 largestCorrection = fpx->GetParameter(1);
173 largestDet = det;
174 largestCrystal2 = crystal2;
175 largestCrystal1 = crystal1;
176 }
177 } //for(int crystal2 = crystal1 + 1; crystal2 < 4; crystal2++)
178 } // for(int crystal1 = 0; crystal1 < 4; crystal1++)
179
180 std::cout << " -------------------- " << std::endl;
181 std::cout << " -------------------- " << std::endl;
182 std::cout << std::endl;
183
184 // Set the diagonal elements to 0 since you can't cross-talk yourself
185 // store current precision and set to fixed 10 precision
186 std::streamsize prec = std::cout.precision();
187 std::cout.precision(10);
188 std::cout.setf(std::ios::fixed, std::ios::floatfield);
189 for(int i = 0; i < 4; i++) {
190 for(int j = 0; j < 4; j++) {
191 if(i == j) {
192 d[i * 4 + j] = 0.0000;
193 eD[i * 4 + j] = 0.0000;
194 }
195 // output a matrix to screen
196 std::cout << d[j * 4 + i] << "\t";
197
198 // Time to find the proper channels and build the corrections xind/i is row number
199 TChannel* chan = TChannel::FindChannelByName(Form("GRG%02d%sN00A", det, TGriffin::GetColorFromNumber(j)));
200 if(chan == nullptr) {
201 std::cout << DRED << "Couldn't find a channel for "
202 << Form("GRG%02d%sN00A", det, TGriffin::GetColorFromNumber(j)) << RESET_COLOR << std::endl;
203 continue;
204 }
205 // Writes the coefficient to the found channel above
206 chan->AddCTCoefficient(d[i * 4 + j]);
207 }
208 std::cout << std::endl;
209 }
210 // set precision back to old value and remove fixed flag
211 std::cout.precision(prec);
212 std::cout.unsetf(std::ios::floatfield);
213
214 std::cout << std::endl;
215 std::cout << "Largest correction = " << largestCorrection << " Shift = " << largestCorrection * energy << std::endl;
216 std::cout << "Largest combo, det = " << largestDet << " Crystals = " << largestCrystal1 << ", " << largestCrystal2 << std::endl;
217 std::cout << " -------------------- " << std::endl;
218 std::cout << " -------------------- " << std::endl;
219 return d;
220}
221
222void FixAll(TFile* inputFile, double energy, int minimumCounts, std::vector<int> excludedDetectors)
223{
224 // This function loops over 16 clovers (always does) and by default uses the 1332 keV gamma ray in 60Co
225 for(int d = 1; d <= 16; d++) {
226 if(!excludedDetectors.empty() && std::find(excludedDetectors.begin(), excludedDetectors.end(), d) != excludedDetectors.end()) {
227 std::cout << "Skipping excluded detector " << d << std::endl;
228 continue;
229 }
230 std::cout << "Starting CrossTalkFix for detector " << d << ", using energy " << energy << ", and minimum counts " << minimumCounts << std::endl;
231 CrossTalkFix(d, energy, inputFile, minimumCounts);
232 }
233}
234
235#ifndef __CINT__
236int main(int argc, char** argv)
237{
238 // Do basic file checks
239 if(argc != 3 && argc != 4) {
240 std::cout << "Usage: " << argv[0] << " <matrix file> <cal file> <user settings (optional)>" << std::endl;
241 std::cout << "User settings available: CrossTalkEnergy (double), MinimumCounts (int), and ExcludedDetectors (int vector, 1-16)" << std::endl;
242 return 1;
243 }
244
245 // We need a cal file to find the channels to write the corrections to
246 if(TChannel::ReadCalFile(argv[2]) < 0) {
247 std::cout << "Aborting" << std::endl;
248 exit(1);
249 }
250
251 auto* inputFile = new TFile(argv[1]);
252 if(inputFile == nullptr || !inputFile->IsOpen()) {
253 std::cout << "Failed to open file '" << argv[1] << "'!" << std::endl;
254 return 1;
255 }
256
257 auto* userSettings = new TUserSettings();
258 if(argc == 4) {
259 userSettings->ReadSettings(argv[3]);
260 }
261
262 // Create the output file
263 std::string outputFileName = argv[1];
264 auto lastSlash = outputFileName.find_last_of('/');
265 if(lastSlash != std::string::npos) {
266 outputFileName = std::string("ct_") + outputFileName.substr(lastSlash + 1);
267 } else {
268 outputFileName = std::string("ct_") + std::string(argv[1]);
269 }
270
271 auto* outputFile = new TFile(outputFileName.c_str(), "recreate");
272
273 std::vector<int> excludedDetectors;
274 try {
275 excludedDetectors = userSettings->GetIntVector("ExcludedDetectors", true);
276 } catch(std::out_of_range& e) {
277 }
278
279 FixAll(inputFile, userSettings->GetDouble("CrossTalkEnergy", 1332.), userSettings->GetInt("MinimumCounts", 10), excludedDetectors);
280
281 // This function writes a corrections cal_file which can be loaded in with your normal cal file.
282 TChannel::WriteCTCorrections("ct_correction.cal");
283
284 outputFile->Write();
285 outputFile->Close();
286}
287#endif
#define DRED
Definition Globals.h:18
#define RESET_COLOR
Definition Globals.h:5
int main(int argc, char **argv)
void FixAll(TFile *inputFile, double energy, int minimumCounts, std::vector< int > excludedDetectors)
double * CrossTalkFix(int det, double energy, TFile *inputFile, int minimumCounts)
double CrossTalkFit(double *x, double *par)
TH2D * mat
Definition UserFillObj.h:12
void AddCTCoefficient(double temp)
Definition TChannel.h:213
static TChannel * FindChannelByName(const char *ccName)
Definition TChannel.cxx:511
static Int_t ReadCalFile(std::ifstream &infile)
static void WriteCTCorrections(const std::string &outfilename="")
void DestroyCTCal()
Definition TChannel.cxx:604
static const char * GetColorFromNumber(int number)
Definition TGriffin.cxx:462