56int main(
int argc,
char** argv)
64 double projGateLow = -1.;
65 double projGateHigh = -1.;
66 double projBgLow = -1.;
67 double projBgHigh = -1.;
68 double timeRandomNorm = -1.;
71 double peakHigh = -1.;
72 std::string baseName =
"AngularCorrelation";
73 std::string bgName = baseName +
"BG";
74 std::string mixedName = baseName +
"Mixed";
75 std::string inputFile;
76 std::string theoryFile;
77 std::string outputFile =
"AngularCorrelations.root";
78 std::string settingsFile;
82 parser.
option(
"h help ?", &help,
true).description(
"Show this help message.");
83 parser.
option(
"projection-low proj-low", &projGateLow,
true).description(
"Low edge of projection gate.");
84 parser.
option(
"projection-high proj-high", &projGateHigh,
true).description(
"High edge of projection gate.");
85 parser.
option(
"background-low bg-low", &projBgLow,
true).description(
"Low edge of background gate (for multiple gates use settings file).");
86 parser.
option(
"background-high bg-high", &projBgHigh,
true).description(
"High edge of background gate (for multiple gates use settings file).");
87 parser.
option(
"time-random-normalization", &timeRandomNorm,
true).description(
"Normalization factor for subtraction of time-random matrix. If negative (default) it will be calculated automatically.");
88 parser.
option(
"base-name", &baseName,
true).description(
"Base name of matrices.").default_value(
"AngularCorrelation");
89 parser.
option(
"background-name", &bgName,
true).description(
"Name of backround matrices.").default_value(
"AngularCorrelationBG");
90 parser.
option(
"mixed-name", &mixedName,
true).description(
"Name of mixed matrices.").default_value(
"AngularCorrelationMixed");
91 parser.
option(
"peak-pos peak", &peakPos,
true).description(
"Peak position (for multiple peaks use settings file).");
92 parser.
option(
"peak-low", &peakLow,
true).description(
"Low edge of peak fit.");
93 parser.
option(
"peak-high", &peakHigh,
true).description(
"High edge of peak fit.");
94 parser.
option(
"settings", &settingsFile,
true).description(
"Settings file with user settings, these do not overwrite anything provided on command line!");
95 parser.
option(
"input", &inputFile,
true).description(
"Input file with gamma-gamma matrices for each angle (coincident, time-random, and event mixed).");
96 parser.
option(
"theory", &theoryFile,
true).description(
"File with simulated z0-, z2-, and z4-graphs.");
97 parser.
option(
"output", &outputFile,
true).description(
"Name of output file, default is \"AngularCorrelations.root\".");
99 parser.
parse(argc, argv,
true);
102 std::cout << parser << std::endl;
107 if(inputFile.empty()) {
108 std::cerr <<
"Need an input file!" << std::endl;
109 std::cout << parser << std::endl;
113 TFile input(inputFile.c_str());
115 if(!input.IsOpen()) {
116 std::cerr <<
"Failed to open input file " << inputFile << std::endl;
117 std::cout << parser << std::endl;
121 auto* settings =
static_cast<TUserSettings*
>(input.Get(
"UserSettings"));
125 if(!settingsFile.empty()) {
126 if(settings ==
nullptr) {
129 settings->ReadSettings(settingsFile);
134 if(settings ==
nullptr || settings->empty()) {
135 std::cerr <<
"Failed to get user settings from input file." << std::endl;
136 std::cout << parser << std::endl;
141 if(projGateLow == -1.) { projGateLow = settings->GetDouble(
"Projection.Low"); }
142 if(projGateHigh == -1.) { projGateHigh = settings->GetDouble(
"Projection.High"); }
143 if(timeRandomNorm == -1.) { timeRandomNorm = settings->GetDouble(
"TimeRandomNormalization"); }
144 if(peakPos == -1.) { peakPos = settings->GetDouble(
"Peak.Position"); }
145 if(peakLow == -1.) { peakLow = settings->GetDouble(
"Peak.Low"); }
146 if(peakHigh == -1.) { peakHigh = settings->GetDouble(
"Peak.High"); }
147 if(baseName ==
"AngularCorrelation") { baseName = settings->GetString(
"Histograms.BaseName",
"AngularCorrelation"); }
148 if(bgName ==
"AngularCorrelationBG") { bgName = settings->GetString(
"Histograms.BackgroundName",
"AngularCorrelationBG"); }
149 if(mixedName ==
"AngularCorrelationMixed") { mixedName = settings->GetString(
"Histograms.MixedName",
"AngularCorrelationMixed"); }
150 if(theoryFile.empty()) {
152 theoryFile = settings->GetString(
"Theory",
true);
153 }
catch(std::exception&) {}
155 if(outputFile ==
"AngularCorrelations.root") { outputFile = settings->GetString(
"Output",
"AngularCorrelations.root"); }
158 std::vector<std::tuple<double, double, double>> bgPeakPos;
159 std::vector<double>
bgLow;
160 std::vector<double>
bgHigh;
162 if(projBgLow == -1. || projBgHigh == -1. || projBgLow >= projBgHigh) {
163 for(
int i = 1;; ++i) {
165 auto low = settings->GetDouble(Form(
"Background.%d.Low", i),
true);
166 auto high = settings->GetDouble(Form(
"Background.%d.High", i),
true);
168 std::cout << i <<
". background gate, low edge not lower than the high edge: " << low <<
" >= " << high << std::endl;
171 bgLow.push_back(low);
173 }
catch(std::out_of_range& e) {
178 bgLow.push_back(projBgLow);
179 bgHigh.push_back(projBgHigh);
183 for(
int i = 1;; ++i) {
185 auto pos = settings->GetDouble(Form(
"Background.Peak.%d.Position", i),
true);
186 if(pos <= peakLow || pos >= peakHigh) {
187 std::cout << i <<
". background peak outside of fit range: " << pos <<
" <= " << peakLow <<
" or " << pos <<
" >= " << peakHigh << std::endl;
191 auto low = settings->GetDouble(Form(
"Background.Peak.%d.Low", i), -1.);
192 auto high = settings->GetDouble(Form(
"Background.Peak.%d.Low", i), -1.);
193 bgPeakPos.push_back(std::make_tuple(pos, low, high));
194 }
catch(std::out_of_range& e) {
202 std::vector<double> peakParameter(6, -2.);
203 std::vector<double> peakParameterLow(6, 0.);
204 std::vector<double> peakParameterHigh(6, -1.);
205 std::vector<double> bgPeakParameter(6, -2.);
206 std::vector<double> bgPeakParameterLow(6, 0.);
207 std::vector<double> bgPeakParameterHigh(6, -1.);
208 for(
size_t i = 0; i < peakParameterLow.size(); ++i) {
209 peakParameter[i] = settings->GetDouble(Form(
"Peak.Parameter.%d",
static_cast<int>(i)), -2.);
210 peakParameterLow[i] = settings->GetDouble(Form(
"Peak.Parameter.%d.Low",
static_cast<int>(i)), 0.);
211 peakParameterHigh[i] = settings->GetDouble(Form(
"Peak.Parameter.%d.High",
static_cast<int>(i)), -1.);
212 bgPeakParameter[i] = settings->GetDouble(Form(
"Background.Peak.Parameter.%d",
static_cast<int>(i)), -2.);
213 bgPeakParameterLow[i] = settings->GetDouble(Form(
"Background.Peak.Parameter.%d.Low",
static_cast<int>(i)), 0.);
214 bgPeakParameterHigh[i] = settings->GetDouble(Form(
"Background.Peak.Parameter.%d.High",
static_cast<int>(i)), -1.);
217 if(peakParameterLow[i] <= peakParameterHigh[i] && (peakParameter[i] < peakParameterLow[i] || peakParameterHigh[i] < peakParameter[i])) {
218 bool output = peakParameter[i] != -2.;
219 if(output) { std::cout <<
"Warning, " << i <<
". peak parameter (" << peakParameter[i] <<
") is out of range " << peakParameterLow[i] <<
" - " << peakParameterHigh[i] <<
", resetting it to "; }
220 peakParameter[i] = (peakParameterHigh[i] + peakParameterLow[i]) / 2.;
221 if(output) { std::cout << peakParameter[i] << std::endl; }
223 if(bgPeakParameterLow[i] <= bgPeakParameterHigh[i] && (bgPeakParameter[i] < bgPeakParameterLow[i] || bgPeakParameterHigh[i] < bgPeakParameter[i])) {
224 bool output = bgPeakParameter[i] != -2.;
225 if(output) { std::cout <<
"Warning, " << i <<
". background peak parameter (" << bgPeakParameter[i] <<
") is out of range " << bgPeakParameterLow[i] <<
" - " << bgPeakParameterHigh[i] <<
", resetting it to "; }
226 bgPeakParameter[i] = (bgPeakParameterHigh[i] + bgPeakParameterLow[i]) / 2.;
227 if(output) { std::cout << bgPeakParameter[i] << std::endl; }
230 if(i == 1 && peakParameterLow[i] == 0. && peakParameterHigh[i] == -1.) {
231 peakParameter[i] = peakPos;
232 peakParameterLow[i] = peakPos;
233 peakParameterHigh[i] = peakPos;
238 std::vector<double> backgroundParameter(4, -2.);
239 std::vector<double> backgroundParameterLow(4, 0.);
240 std::vector<double> backgroundParameterHigh(4, -1.);
241 backgroundParameter[0] = settings->GetDouble(
"Background.Offset", -2.);
242 backgroundParameterLow[0] = settings->GetDouble(
"Background.Offset.Low", 0.);
243 backgroundParameterHigh[0] = settings->GetDouble(
"Background.Offset.High", -1.);
244 backgroundParameter[1] = settings->GetDouble(
"Background.Linear", -2.);
245 backgroundParameterLow[1] = settings->GetDouble(
"Background.Linear.Low", 0.);
246 backgroundParameterHigh[1] = settings->GetDouble(
"Background.Linear.High", -1.);
247 backgroundParameter[2] = settings->GetDouble(
"Background.Quadratic", -2.);
248 backgroundParameterLow[2] = settings->GetDouble(
"Background.Quadratic.Low", 0.);
249 backgroundParameterHigh[2] = settings->GetDouble(
"Background.Quadratic.High", -1.);
250 backgroundParameter[3] = settings->GetDouble(
"Background.Xoffset", -2.);
251 backgroundParameterLow[3] = settings->GetDouble(
"Background.Xoffset.Low", 0.);
252 backgroundParameterHigh[3] = settings->GetDouble(
"Background.Xoffset.High", -1.);
253 for(
size_t i = 0; i < backgroundParameter.size(); ++i) {
254 if(backgroundParameterLow[i] <= backgroundParameterHigh[i] && (backgroundParameter[i] < backgroundParameterLow[i] || backgroundParameterHigh[i] < backgroundParameter[i])) {
255 bool output = backgroundParameter[i] != -2.;
256 if(output) { std::cout <<
"Warning, " << i <<
". background parameter (" << backgroundParameter[i] <<
") is out of range " << backgroundParameterLow[i] <<
" - " << backgroundParameterHigh[i] <<
", resetting it to "; }
257 backgroundParameter[i] = (backgroundParameterHigh[i] + backgroundParameterLow[i]) / 2.;
258 if(output) { std::cout << backgroundParameter[i] << std::endl; }
264 std::vector<int> twoJLow = settings->GetIntVector(
"TwoJ.Low");
265 std::vector<int> twoJMiddle = settings->GetIntVector(
"TwoJ.Middle");
266 std::vector<int> twoJHigh = settings->GetIntVector(
"TwoJ.High");
269 double confidenceLevel = settings->GetDouble(
"ConfidenceLevel", 1.535);
272 if(projGateLow >= projGateHigh) {
273 std::cerr <<
"Need a projection gate with a low edge that is smaller than the high edge, " << projGateLow <<
" >= " << projGateHigh << std::endl;
274 std::cout << parser << std::endl;
278 if(peakLow >= peakHigh) {
279 std::cerr <<
"Need a fit range with a low edge that is smaller than the high edge, " << peakLow <<
" >= " << peakHigh << std::endl;
280 std::cout << parser << std::endl;
284 if(peakPos >= peakHigh || peakPos <= peakLow) {
285 std::cerr <<
"Need a peak within the fit range, " << peakPos <<
" not within " << peakLow <<
" - " << peakHigh << std::endl;
286 std::cout << parser << std::endl;
290 if(timeRandomNorm <= 0.) {
291 std::cerr <<
"Need a positive normalization factor for time random subtraction" << std::endl;
292 std::cout << parser << std::endl;
299 std::cerr <<
"Background gate information missing, either no low/high edges or a mismatching amount of low and high edges: " <<
bgLow.size() <<
" low edges, and " <<
bgHigh.size() <<
" high edges" << std::endl;
300 std::cout << parser << std::endl;
305 auto* angles =
static_cast<TGriffinAngles*
>(input.Get(
"GriffinAngles"));
307 if(angles ==
nullptr) {
308 std::cerr <<
"Failed to find 'GriffinAngles' in '" << inputFile <<
"'" << std::endl;
309 std::cout << parser << std::endl;
314 auto logFileName = outputFile.substr(0, outputFile.find_last_of(
'.')) +
".log";
315 std::ofstream logFile(logFileName.c_str());
322 TFile output(outputFile.c_str(),
"recreate");
324 auto* rawAngularDistribution =
new TGraphErrors(angles->NumberOfAngles());
325 rawAngularDistribution->SetName(
"RawAngularDistribution");
326 auto* angularDistribution =
new TGraphErrors(angles->NumberOfAngles());
327 angularDistribution->SetName(
"AngularDistribution");
328 auto* mixedAngularDistribution =
new TGraphErrors(angles->NumberOfAngles());
329 mixedAngularDistribution->SetName(
"MixedAngularDistribution");
330 auto* rawChiSquares =
new TGraph(angles->NumberOfAngles());
331 rawChiSquares->SetName(
"RawChiSquares");
332 auto* mixedChiSquares =
new TGraph(angles->NumberOfAngles());
333 mixedChiSquares->SetName(
"MixedChiSquares");
338#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
339 auto* fitDir = output.mkdir(
"fits",
"Projections with fits",
true);
341 auto* fitDir = output.mkdir(
"fits",
"Projections with fits");
344 logFile <<
"# columns are:" << std::endl
345 <<
"#ID p/m centroid +- uncertainty area +- uncertainty FWHM +- uncertainty red. chi^2" << std::endl;
348 for(
int i = 0; i < angles->NumberOfAngles(); ++i) {
350 auto* prompt =
static_cast<TH2*
>(input.Get(Form(
"%s%d", baseName.c_str(), i)));
351 if(prompt ==
nullptr) {
352 std::cerr <<
"Failed to find histogram '" << Form(
"%s%d", baseName.c_str(), i) <<
"', should have " << angles->NumberOfAngles() <<
" angles in total!" << std::endl;
353 std::cout << parser << std::endl;
356 auto* bg =
static_cast<TH2*
>(input.Get(Form(
"%s%d", bgName.c_str(), i)));
358 std::cerr <<
"Failed to find histogram '" << Form(
"%s%d", bgName.c_str(), i) <<
"', should have " << angles->NumberOfAngles() <<
" angles in total!" << std::endl;
359 std::cout << parser << std::endl;
362 auto* mixed =
static_cast<TH2*
>(input.Get(Form(
"%s%d", mixedName.c_str(), i)));
363 if(mixed ==
nullptr) {
364 std::cerr <<
"Failed to find histogram '" << Form(
"%s%d", mixedName.c_str(), i) <<
"', should have " << angles->NumberOfAngles() <<
" angles in total!" << std::endl;
365 std::cout << parser << std::endl;
371 prompt->Add(bg, -timeRandomNorm);
375 auto* proj = prompt->ProjectionX(Form(
"proj%d", i), prompt->GetYaxis()->FindBin(projGateLow), prompt->GetYaxis()->FindBin(projGateHigh));
376 auto* projMixed = mixed->ProjectionX(Form(
"projMixed%d", i), mixed->GetYaxis()->FindBin(projGateLow), mixed->GetYaxis()->FindBin(projGateHigh));
378 auto* projBg = prompt->ProjectionX(Form(
"projBg%d", i), prompt->GetYaxis()->FindBin(
bgLow[0]), prompt->GetYaxis()->FindBin(
bgHigh[0]));
379 auto* projMixedBg = mixed->ProjectionX(Form(
"projMixedBg%d", i), mixed->GetYaxis()->FindBin(
bgLow[0]), mixed->GetYaxis()->FindBin(
bgHigh[0]));
380 double bgGateWidth = prompt->GetYaxis()->FindBin(
bgHigh[0]) - prompt->GetYaxis()->FindBin(
bgLow[0]) + 1;
381 double mixedBgGateWidth = mixed->GetYaxis()->FindBin(
bgHigh[0]) - mixed->GetYaxis()->FindBin(
bgLow[0]) + 1;
382 for(
size_t g = 1; g <
bgLow.size(); ++g) {
383 projBg->Add(prompt->ProjectionX(Form(
"projBg%d", i), prompt->GetYaxis()->FindBin(
bgLow[g]), prompt->GetYaxis()->FindBin(
bgHigh[g])));
384 projMixedBg->Add(mixed->ProjectionX(Form(
"projMixedBg%d", i), mixed->GetYaxis()->FindBin(
bgLow[g]), mixed->GetYaxis()->FindBin(
bgHigh[g])));
385 bgGateWidth += prompt->GetYaxis()->FindBin(
bgHigh[g]) - prompt->GetYaxis()->FindBin(
bgLow[g]) + 1;
386 mixedBgGateWidth += mixed->GetYaxis()->FindBin(
bgHigh[g]) - mixed->GetYaxis()->FindBin(
bgLow[g]) + 1;
390 proj->Add(projBg, -(prompt->GetYaxis()->FindBin(projGateHigh) - prompt->GetYaxis()->FindBin(projGateLow) + 1) / bgGateWidth);
391 projMixed->Add(projMixedBg, -(mixed->GetYaxis()->FindBin(projGateHigh) - mixed->GetYaxis()->FindBin(projGateLow) + 1) / mixedBgGateWidth);
397 for(
size_t p = 0; p < peakParameterLow.size(); ++p) {
398 if(peakParameterLow[p] == peakParameterHigh[p]) {
400 }
else if(peakParameterLow[p] < peakParameterHigh[p]) {
402 peak.
GetFitFunction()->SetParLimits(p, peakParameterLow[p], peakParameterHigh[p]);
406 for(
auto bgPeak : bgPeakPos) {
407 auto* bgP =
new TRWPeak(std::get<0>(bgPeak));
409 if(std::get<1>(bgPeak) != -1. && std::get<2>(bgPeak) != -1. && std::get<1>(bgPeak) < std::get<2>(bgPeak)) {
410 bgP->GetFitFunction()->SetParLimits(1, std::get<1>(bgPeak), std::get<2>(bgPeak));
412 for(
size_t p = 0; p < bgPeakParameterLow.size(); ++p) {
413 if(bgPeakParameterLow[p] == bgPeakParameterHigh[p]) {
414 bgP->GetFitFunction()->FixParameter(p, bgPeakParameter[p]);
415 }
else if(bgPeakParameterLow[p] < bgPeakParameterHigh[p]) {
416 bgP->GetFitFunction()->SetParameter(p, bgPeakParameter[p]);
417 bgP->GetFitFunction()->SetParLimits(p, bgPeakParameterLow[p], bgPeakParameterHigh[p]);
422 for(
size_t p = 0; p < backgroundParameterLow.size(); ++p) {
423 if(backgroundParameterLow[p] == backgroundParameterHigh[p]) {
425 }
else if(backgroundParameterLow[p] < backgroundParameterHigh[p]) {
427 pf.
GetBackground()->SetParLimits(p, backgroundParameterLow[p], backgroundParameterHigh[p]);
432 pf.
Fit(proj,
"qretryfit");
435 logFile << std::setw(2) << i <<
" p "
436 << std::setw(10) << peak.
Centroid() <<
" +- " << std::setw(10) << peak.
CentroidErr() <<
" "
437 << std::setw(10) << peak.
Area() <<
" +- " << std::setw(10) << peak.
AreaErr() <<
" "
438 << std::setw(10) << peak.
FWHM() <<
" +- " << std::setw(10) << peak.
FWHMErr() <<
" "
443 for(
size_t p = 0; p < peakParameterLow.size(); ++p) {
444 if(peakParameterLow[p] == peakParameterHigh[p]) {
446 }
else if(peakParameterLow[p] < peakParameterHigh[p]) {
448 peakMixed.
GetFitFunction()->SetParLimits(p, peakParameterLow[p], peakParameterHigh[p]);
452 for(
auto bgPeak : bgPeakPos) {
453 auto* bgP =
new TRWPeak(std::get<0>(bgPeak));
455 if(std::get<1>(bgPeak) != -1. && std::get<2>(bgPeak) != -1. && std::get<1>(bgPeak) < std::get<2>(bgPeak)) {
456 bgP->GetFitFunction()->SetParLimits(1, std::get<1>(bgPeak), std::get<2>(bgPeak));
458 for(
size_t p = 0; p < bgPeakParameterLow.size(); ++p) {
459 if(bgPeakParameterLow[p] == bgPeakParameterHigh[p]) {
460 bgP->GetFitFunction()->FixParameter(p, bgPeakParameter[p]);
461 }
else if(bgPeakParameterLow[p] < bgPeakParameterHigh[p]) {
462 bgP->GetFitFunction()->SetParameter(p, bgPeakParameter[p]);
463 bgP->GetFitFunction()->SetParLimits(p, bgPeakParameterLow[p], bgPeakParameterHigh[p]);
468 for(
size_t p = 0; p < backgroundParameterLow.size(); ++p) {
469 if(backgroundParameterLow[p] == backgroundParameterHigh[p]) {
470 pfMixed.
GetBackground()->FixParameter(p, backgroundParameter[p]);
471 }
else if(backgroundParameterLow[p] < backgroundParameterHigh[p]) {
472 pfMixed.
GetBackground()->SetParameter(p, backgroundParameter[p]);
473 pfMixed.
GetBackground()->SetParLimits(p, backgroundParameterLow[p], backgroundParameterHigh[p]);
478 pfMixed.
Fit(projMixed,
"qretryfit");
481 logFile << std::setw(2) << i <<
" m "
482 << std::setw(8) << peakMixed.
Centroid() <<
" +- " << std::setw(8) << peakMixed.
CentroidErr() <<
" "
483 << std::setw(8) << peakMixed.
Area() <<
" +- " << std::setw(8) << peakMixed.
AreaErr() <<
" "
484 << std::setw(8) << peakMixed.
FWHM() <<
" +- " << std::setw(8) << peakMixed.
FWHMErr() <<
" "
493 rawAngularDistribution->SetPoint(i, angles->AverageAngle(i), peak.
Area());
494 rawAngularDistribution->SetPointError(i, 0., peak.
AreaErr());
495 mixedAngularDistribution->SetPoint(i, angles->AverageAngle(i), peakMixed.
Area());
496 mixedAngularDistribution->SetPointError(i, 0., peakMixed.
AreaErr());
497 angularDistribution->SetPoint(i, angles->AverageAngle(i), peak.
Area() / peakMixed.
Area());
498 angularDistribution->SetPointError(i, 0., peak.
Area() / peakMixed.
Area() * TMath::Sqrt(TMath::Power(peak.
AreaErr() / peak.
Area(), 2) + TMath::Power(peakMixed.
AreaErr() / peakMixed.
Area(), 2)));
500 rawChiSquares->SetPoint(i, angles->AverageAngle(i), peak.
GetReducedChi2());
501 mixedChiSquares->SetPoint(i, angles->AverageAngle(i), peakMixed.
GetReducedChi2());
503 std::cout <<
"Angle " << std::setw(3) << i <<
" of " << angles->NumberOfAngles() <<
" done\r" << std::flush;
505 std::cout <<
"Fitting of projections done." << std::endl;
508 auto* rawAngularDistributionCorr =
static_cast<TGraphErrors*
>(rawAngularDistribution->Clone(
"RawAngularDistributionCorrected"));
509 for(
int i = 0; i < rawAngularDistributionCorr->GetN(); ++i) {
510#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
511 if(angles->Count(rawAngularDistributionCorr->GetPointX(i)) != 0) {
512 rawAngularDistributionCorr->SetPointY(i, rawAngularDistributionCorr->GetPointY(i) / angles->Count(rawAngularDistributionCorr->GetPointX(i)));
513 rawAngularDistributionCorr->SetPointError(i, rawAngularDistributionCorr->GetErrorX(i), rawAngularDistributionCorr->GetErrorY(i) / angles->Count(rawAngularDistributionCorr->GetPointX(i)));
515 rawAngularDistributionCorr->SetPointY(i, 0);
519 rawAngularDistributionCorr->GetPoint(i, px, py);
520 if(angles->Count(px) != 0) {
521 rawAngularDistributionCorr->SetPoint(i, px, py / angles->Count(px));
522 rawAngularDistributionCorr->SetPointError(i, rawAngularDistributionCorr->GetErrorX(i), rawAngularDistributionCorr->GetErrorY(i) / angles->Count(px));
524 rawAngularDistributionCorr->SetPoint(i, px, 0);
528 auto* mixedAngularDistributionCorr =
static_cast<TGraphErrors*
>(mixedAngularDistribution->Clone(
"MixedAngularDistributionCorrected"));
529 for(
int i = 0; i < mixedAngularDistributionCorr->GetN(); ++i) {
530#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
531 if(angles->Count(mixedAngularDistributionCorr->GetPointX(i)) != 0) {
532 mixedAngularDistributionCorr->SetPointY(i, mixedAngularDistributionCorr->GetPointY(i) / angles->Count(mixedAngularDistributionCorr->GetPointX(i)));
533 mixedAngularDistributionCorr->SetPointError(i, mixedAngularDistributionCorr->GetErrorX(i), mixedAngularDistributionCorr->GetErrorY(i) / angles->Count(mixedAngularDistributionCorr->GetPointX(i)));
535 mixedAngularDistributionCorr->SetPointY(i, 0);
539 mixedAngularDistributionCorr->GetPoint(i, px, py);
540 if(angles->Count(px) != 0) {
541 mixedAngularDistributionCorr->SetPoint(i, px, py / angles->Count(px));
542 mixedAngularDistributionCorr->SetPointError(i, mixedAngularDistributionCorr->GetErrorX(i), mixedAngularDistributionCorr->GetErrorY(i) / angles->Count(px));
544 mixedAngularDistributionCorr->SetPoint(i, px, 0);
558 if(!theoryFile.empty()) {
559 TFile theory(theoryFile.c_str());
561 if(theory.IsOpen()) {
563 auto* z0 =
static_cast<TGraphErrors*
>(theory.Get(
"graph000"));
564 auto* z2 =
static_cast<TGraphErrors*
>(theory.Get(
"graph100"));
565 auto* z4 =
static_cast<TGraphErrors*
>(theory.Get(
"graph010"));
567 if(z0 !=
nullptr && z2 !=
nullptr && z4 !=
nullptr && z0->GetN() == z2->GetN() && z0->GetN() == z4->GetN()) {
571 if(z0->GetN() == angularDistribution->GetN() || ((angles->Grouping() || angles->Folding()) && z0->GetN() == (angles->Addback() ? 49 : 51))) {
573 if(z0->GetN() != angularDistribution->GetN()) {
574 angles->FoldOrGroup(z0, z2, z4);
577 std::vector<TGraph*> spin;
578 std::vector<double> spinLabel;
579 std::vector<std::vector<double>> parameters;
580 logFile << std::endl;
582 if(twoJLow.size() > 1 && twoJMiddle.size() == 1 && twoJHigh.size() == 1) {
583 logFile <<
"# Mixing method, high 2J = " << twoJHigh.at(0) <<
", middle 2J = " << twoJMiddle.at(0) <<
", low 2J = " << twoJLow.at(0) <<
" - " << twoJLow.back() << std::endl;
584 for(
auto twoJ : twoJLow) {
585 parameters.emplace_back();
587 spin.push_back(
MixingMethod(angularDistribution, z0, z2, z4, twoJHigh.at(0), twoJMiddle.at(0), twoJ, parameters.back(), logFile));
588 spinLabel.push_back(twoJ / 2.);
590 }
else if(twoJLow.size() == 1 && twoJMiddle.size() > 1 && twoJHigh.size() == 1) {
591 logFile <<
"# Mixing method, high 2J = " << twoJHigh.at(0) <<
", middle 2J = " << twoJMiddle.at(0) <<
" - " << twoJMiddle.back() <<
", low 2J = " << twoJLow.at(0) << std::endl;
592 for(
auto twoJ : twoJMiddle) {
593 parameters.emplace_back();
595 spin.push_back(
MixingMethod(angularDistribution, z0, z2, z4, twoJHigh.at(0), twoJ, twoJLow.at(0), parameters.back(), logFile));
596 spinLabel.push_back(twoJ / 2.);
598 }
else if(twoJLow.size() == 1 && twoJMiddle.size() == 1 && twoJHigh.size() > 1) {
599 logFile <<
"# Mixing method, high 2J = " << twoJHigh.at(0) <<
" - " << twoJHigh.back() <<
", middle 2J = " << twoJMiddle.at(0) <<
", low 2J = " << twoJLow.at(0) << std::endl;
600 for(
auto twoJ : twoJHigh) {
601 parameters.emplace_back();
603 spin.push_back(
MixingMethod(angularDistribution, z0, z2, z4, twoJ, twoJMiddle.at(0), twoJLow.at(0), parameters.back(), logFile));
604 spinLabel.push_back(twoJ / 2.);
607 logFile <<
"# Mixing method, high 2J = " << twoJHigh.at(0) <<
", middle 2J = " << twoJMiddle.at(0) <<
", low 2J = " << twoJLow.at(0) << std::endl;
608 parameters.emplace_back();
610 spin.push_back(
MixingMethod(angularDistribution, z0, z2, z4, twoJHigh.at(0), twoJMiddle.at(0), twoJLow.at(0), parameters.back(), logFile));
611 spinLabel.push_back(twoJHigh.at(0) / 2.);
615 auto* canvas =
new TCanvas;
618 double min = TMath::MinElement(spin.at(0)->GetN(), spin.at(0)->GetY());
619 double max = TMath::MaxElement(spin.at(0)->GetN(), spin.at(0)->GetY());
620 for(
size_t i = 1; i < spin.size(); ++i) {
621 min = TMath::Min(min, TMath::MinElement(spin.at(i)->GetN(), spin.at(i)->GetY()));
622 max = TMath::Max(max, TMath::MaxElement(spin.at(i)->GetN(), spin.at(i)->GetY()));
624 min = TMath::Min(min, confidenceLevel);
628 for(first = 0; first < spin.size(); ++first) {
629 if(spin[first]->GetN() > 1) {
break; }
632 spin[first]->SetTitle(
"");
633 spin[first]->SetMinimum(0.9 * min);
634 spin[first]->SetMaximum(1.1 * max);
636 for(
size_t i = 0; i < spin.size(); ++i) {
637 spin[i]->SetLineColor(i + 1);
638 spin[i]->SetMarkerColor(i + 1);
639 spin[i]->SetLineWidth(2);
642 spin[first]->Draw(
"ac");
643 for(
size_t i = 0; i < spin.size(); ++i) {
644 if(i == first) {
continue; }
645 if(spin[i]->GetN() > 1) {
652 auto* confidenceLevelLine =
new TLine(-1.5, confidenceLevel, 1.5, confidenceLevel);
654 confidenceLevelLine->Draw();
656#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
657 auto* legend =
new TLegend(0.1, 0.3);
659 auto* legend =
new TLegend(0.7, 0.6, 0.8, 0.9);
661 for(
size_t i = 0; i < spin.size(); ++i) {
662 if(spin[i]->GetN() == 1) {
663 legend->AddEntry(spin[i], Form(
"J = %.1f", spinLabel[i]),
"p");
665 legend->AddEntry(spin[i], Form(
"J = %.1f", spinLabel[i]),
"l");
673 spin[first]->GetHistogram()->GetXaxis()->SetRangeUser(-1.5, 1.5);
674 spin[first]->GetHistogram()->GetXaxis()->SetTitle(
"atan(#delta) [rad]");
675 spin[first]->GetHistogram()->GetXaxis()->CenterTitle();
676 spin[first]->GetHistogram()->GetYaxis()->SetTitle(
"red. #chi^{2}");
677 spin[first]->GetHistogram()->GetYaxis()->CenterTitle();
681 z0->Write(
"graph000");
682 z2->Write(
"graph010");
683 z4->Write(
"graph100");
684 for(
size_t i = 0; i < spin.size(); ++i) {
685 spin[i]->Write(Form(
"spin%d",
static_cast<int>(i)));
687 canvas->Write(
"MixingCanvas");
690 std::vector<TGraphErrors*> spinFit(spin.size(),
new TGraphErrors(angularDistribution->GetN()));
691 auto* x = angularDistribution->GetX();
692 for(
int p = 0; p < angularDistribution->GetN(); ++p) {
693 for(
size_t i = 0; i < spinFit.size(); ++i) {
694 spinFit[i]->SetPoint(p, x[p], parameters[i][0] * ((1. - parameters[i][1] - parameters[i][2]) * z0->Eval(x[p]) + parameters[i][1] * z2->Eval(x[p]) + parameters[i][2] * z4->Eval(x[p])));
695 spinFit[i]->SetPointError(p, 0., TMath::Sqrt(parameters[i][0] * (TMath::Power((1. - parameters[i][1] - parameters[i][2]) *
GetYError(z0, x[p]), 2) + TMath::Power(parameters[i][1] *
GetYError(z2, x[p]), 2) + TMath::Power(parameters[i][2] *
GetYError(z4, x[p]), 2))));
698 for(
size_t i = 0; i < spin.size(); ++i) {
699 spinFit[i]->SetLineColor(i + 1);
700 spinFit[i]->SetMarkerColor(i + 1);
701 spinFit[i]->SetLineWidth(2);
702 spinFit[i]->Write(Form(
"SpinFit%d",
static_cast<int>(i)));
703 output.WriteObject(¶meters[i], Form(
"Parameters%d",
static_cast<int>(i)));
707 auto a2a4Parameters =
A2a4Method(angularDistribution, z0, z2, z4);
708 output.WriteObject(&a2a4Parameters,
"ParametersA2a4Fit");
710 std::cerr <<
"Mismatch between theory and data (" << z0->GetN() <<
" != " << angularDistribution->GetN() <<
"?) or neither grouping and folding are enabled (" << std::boolalpha << angles->Grouping() <<
", " << angles->Folding() <<
") or the ungrouped/folded theory doesn't match the required size for " << (angles->Addback() ?
"addback" :
"singles") <<
" data (" << (angles->Addback() ? 49 : 51) <<
")" << std::endl;
713 std::cerr <<
"Failed to find z0 (" << z0 <<
", \"graph000\"), z2 (" << z2 <<
", \"graph010\"), or z4 (" << z4 <<
", \"graph100\") in " << theoryFile <<
", or they had mismatched sizes" << std::endl;
716 std::cerr <<
"Failed to open " << theoryFile << std::endl;
719 std::cout <<
"No file with simulation results (--theory flag), so we won't produce chi2 vs mixing angle plot." << std::endl;
722 rawAngularDistribution->Write();
723 angularDistribution->Write();
724 mixedAngularDistribution->Write();
725 rawChiSquares->Write();
726 mixedChiSquares->Write();
727 rawAngularDistributionCorr->Write();
728 mixedAngularDistributionCorr->Write();
872TGraph*
MixingMethod(TGraphErrors* data, TGraphErrors* z0, TGraphErrors* z2, TGraphErrors* z4,
int twoJhigh,
int twoJmid,
int twoJlow, std::vector<double>& bestParameters, std::ofstream& logFile)
874 logFile <<
"# high 2J " << twoJhigh <<
", middle 2J " << twoJmid <<
", low 2J " << twoJlow << std::endl;
875 logFile <<
"# a0 a2 a4 red.chi^2" << std::endl;
876 TGraph* result =
nullptr;
877 Ac ac(data, z0, z2, z4);
878 ROOT::Fit::Fitter fitter;
880 fitter.SetFCN(nPar, ac);
881 for(
int i = 0; i < nPar; ++i) {
883 fitter.Config().ParSettings(i) = ROOT::Fit::ParameterSettings(Form(
"a_{%d}", 2 * i), 0.5, 0.0001, -10., 10.);
885 fitter.Config().MinimizerOptions().SetPrintLevel(0);
886 fitter.Config().SetMinimizer(
"Minuit2",
"Migrad");
891 double j1 = 0.5 * twoJhigh;
892 double j2 = 0.5 * twoJmid;
893 double j3 = 0.5 * twoJlow;
897 int l1a = TMath::Abs(twoJhigh - twoJmid) / 2;
898 if(l1a == 0) { l1a = 1; }
903 int l2a = TMath::Abs(twoJmid - twoJlow) / 2;
904 if(l2a == 0) { l2a = 1; }
908 if((twoJhigh == 0 && twoJmid == 0) || (twoJmid == 0 && twoJlow == 0)) {
909 std::cout <<
"!!!!!!!!!!!!!!! ERROR !!!!!!!!!!!!!!!" << std::endl
910 <<
"Can't have gamma transition between J=0 states (high " << twoJhigh <<
", mid " << twoJmid <<
", low " << twoJlow <<
")." << std::endl
911 <<
"Aborting..." << std::endl;
914 if(l1a == TMath::Abs(twoJhigh + twoJmid) / 2) {
921 if(l2a == TMath::Abs(twoJmid + twoJlow) / 2) {
942 double mixingAngle1Minimum = -TMath::Pi() / 2;
943 double mixingAngle1Maximum = TMath::Pi() / 2;
945 double stepSize1 = (mixingAngle1Maximum - mixingAngle1Minimum) / steps1;
947 double mixingAngle2Minimum = -TMath::Pi() / 2;
948 double mixingAngle2Maximum = TMath::Pi() / 2;
950 double stepSize2 = (mixingAngle2Maximum - mixingAngle2Minimum) / steps2;
954 mixingAngle1Minimum = 0;
958 mixingAngle2Minimum = 0;
962 result =
new TGraph(steps1);
963 double minChi2 = 1e6;
964 std::vector<double> bestErrors;
965 double bestMixingAngle1 = 0.;
966 double bestMixingAngle2 = 0.;
967 for(
int i = 0; i < steps1; i++) {
968 double mixangle1 = mixingAngle1Minimum + i * stepSize1;
969 double delta1 = TMath::Tan(mixangle1);
970 for(
int j = 0; j < steps2; j++) {
971 double mixangle2 = mixingAngle2Minimum + j * stepSize2;
972 double delta2 = TMath::Tan(mixangle2);
976 fitter.Config().ParSettings(1).Set(
"a_{2}", a2);
977 fitter.Config().ParSettings(1).Fix();
981 fitter.Config().ParSettings(2).Set(
"a_{4}", a4);
982 fitter.Config().ParSettings(2).Fix();
983 if(!fitter.FitFCN()) {
984 std::cerr << i <<
", " << j <<
": Fit failed using a2 " << a2 <<
", a4 " << a4 << std::endl;
987 auto fitResult = fitter.Result();
989 double chi2 = fitResult.MinFcnValue() / (ac.
Np() - fitResult.NFreeParameters());
992 result->SetPoint(i, mixangle1, chi2);
995 bestParameters = fitResult.Parameters();
996 bestErrors = fitResult.Errors();
997 bestMixingAngle1 = mixangle1;
998 bestMixingAngle2 = mixangle2;
1000 logFile << std::setw(12) << fitResult.Parameter(0) <<
" " << std::setw(10) << a2 <<
" " << std::setw(10) << a4 <<
" " << std::setw(10) << fitResult.MinFcnValue() / (ac.
Np() - fitResult.NFreeParameters()) << std::endl;
1005 auto minIndex = TMath::LocMin(result->GetN(), result->GetY());
1006 double x1 = std::numeric_limits<double>::quiet_NaN();
1007 double x2 = std::numeric_limits<double>::quiet_NaN();
1008 double y1 = std::numeric_limits<double>::quiet_NaN();
1009 double y2 = std::numeric_limits<double>::quiet_NaN();
1010 for(
int i = minIndex; i < result->GetN(); ++i) {
1011 if(result->GetPointY(i) > minChi2 + 1.) {
1012 x1 = result->GetPointX(i);
1013 x2 = result->GetPointX(i - 1);
1014 y1 = result->GetPointY(i);
1015 y2 = result->GetPointY(i - 1);
1019 double upperLimit = std::numeric_limits<double>::quiet_NaN();
1020 if(!std::isnan(x1)) {
1021 upperLimit = x1 - (x2 - x1) / (y2 - y1) * y1 + (x2 - x1) / (y2 - y1) * (minChi2 + 1.);
1023 x1 = std::numeric_limits<double>::quiet_NaN();
1024 for(
int i = minIndex; i >= 0; --i) {
1025 if(result->GetPointY(i) > minChi2 + 1.) {
1026 x1 = result->GetPointX(i);
1027 x2 = result->GetPointX(i + 1);
1028 y1 = result->GetPointY(i);
1029 y2 = result->GetPointY(i + 1);
1033 double lowerLimit = std::numeric_limits<double>::quiet_NaN();
1034 if(!std::isnan(x1)) {
1035 lowerLimit = x1 - (x2 - x1) / (y2 - y1) * y1 + (x2 - x1) / (y2 - y1) * (minChi2 + 1.);
1039 auto* fit =
static_cast<TGraphErrors*
>(z0->Clone(
"fit"));
1040 for(
int i = 0; i < fit->GetN(); ++i) {
1041#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
1042 fit->SetPointY(i, bestParameters[0] * ((1. - bestParameters[1] - bestParameters[2]) * z0->GetPointY(i) + bestParameters[1] * z2->GetPointY(i) + bestParameters[2] * z4->GetPointY(i)));
1044 fit->SetPoint(i, fit->GetX()[i], bestParameters[0] * ((1. - bestParameters[1] - bestParameters[2]) * z0->GetY()[i] + bestParameters[1] * z2->GetY()[i] + bestParameters[2] * z4->GetY()[i]));
1046 fit->SetPointError(i, 0., std::abs(bestParameters[0]) * TMath::Sqrt(TMath::Power((1. - bestParameters[1] - bestParameters[2]) * z0->GetErrorY(i), 2) + TMath::Power(bestParameters[1] * z2->GetErrorY(i), 2) + TMath::Power(bestParameters[2] * z4->GetErrorY(i), 2)));
1049 auto* residual =
static_cast<TGraphErrors*
>(data->Clone(
"residual"));
1050 for(
int i = 0; i < residual->GetN(); ++i) {
1051#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 20, 0)
1052 residual->SetPointY(i, data->GetPointY(i) - fit->GetPointY(i));
1054 residual->SetPoint(i, residual->GetX()[i], data->GetY()[i] - fit->GetY()[i]);
1056 residual->SetPointError(i, 0., TMath::Sqrt(TMath::Power(data->GetErrorY(i), 2) + TMath::Power(fit->GetErrorY(i), 2)));
1059 auto* canvas =
new TCanvas;
1060 TMultiGraph* multiGraph =
nullptr;
1062 if(steps1 == 1 && steps2 == 1) {
1063 std::cout <<
"Analyzed cascade " << twoJhigh / 2. <<
" -> " << twoJmid / 2. <<
" -> " << twoJlow / 2. <<
": red. chi^2 " << std::setw(12) << minChi2 <<
", a0 " << std::setw(12) << bestParameters[0] <<
" +- " << std::setw(12) << bestErrors[0] <<
", a2 " << std::setw(12) << bestParameters[1] <<
", a4 " << std::setw(12) << bestParameters[2] << std::endl;
1064 multiGraph =
PlotCanvas(data, fit, residual, bestParameters, bestErrors, minChi2);
1067 std::cout <<
"Varied mixing angle for cascade " << twoJhigh / 2. <<
" -> " << twoJmid / 2. <<
" -> " << twoJlow / 2. <<
": best red. chi^2 " << std::setw(12) << minChi2 <<
", at mixing angle " << std::setw(12) << bestMixingAngle2 <<
", a0 " << std::setw(12) << bestParameters[0] <<
" +- " << std::setw(12) << bestErrors[0] <<
", a2 " << std::setw(12) << bestParameters[1] <<
", a4 " << std::setw(12) << bestParameters[2] << std::endl;
1068 multiGraph =
PlotCanvas(data, fit, residual, bestParameters, bestErrors, minChi2, Form(
"mixing angle = %f", bestMixingAngle2));
1069 }
else if(steps2 == 1) {
1070 std::cout <<
"Varied mixing angle for cascade " << twoJhigh / 2. <<
" -> " << twoJmid / 2. <<
" -> " << twoJlow / 2. <<
": best red. chi^2 " << std::setw(12) << minChi2 <<
", at mixing angle " << std::setw(12) << bestMixingAngle1 <<
", a0 " << std::setw(12) << bestParameters[0] <<
" +- " << std::setw(12) << bestErrors[0] <<
", a2 " << std::setw(12) << bestParameters[1] <<
", a4 " << std::setw(12) << bestParameters[2] << std::endl;
1071 multiGraph =
PlotCanvas(data, fit, residual, bestParameters, bestErrors, minChi2, Form(
"mixing angle = %f (-%f/+%f)", bestMixingAngle1, bestMixingAngle1 - lowerLimit, upperLimit - bestMixingAngle1));
1073 std::cout <<
"Varied mixing angle for cascade " << twoJhigh / 2. <<
" -> " << twoJmid / 2. <<
" -> " << twoJlow / 2. <<
": best red. chi^2 " << std::setw(12) << minChi2 <<
", at mixing angle " << std::setw(12) << bestMixingAngle1 <<
" / " << std::setw(12) << bestMixingAngle2 <<
", a0 " << std::setw(12) << bestParameters[0] <<
" +- " << std::setw(12) << bestErrors[0] <<
", a2 " << std::setw(12) << bestParameters[1] <<
", a4 " << std::setw(12) << bestParameters[2] << std::endl;
1074 multiGraph =
PlotCanvas(data, fit, residual, bestParameters, bestErrors, minChi2, Form(
"mixing angle = %f/%f", bestMixingAngle1, bestMixingAngle2));
1078 fit->Write(Form(
"BestMixingFit%d_%d_%d", twoJhigh, twoJmid, twoJlow));
1079 residual->Write(Form(
"Residual%d_%d_%d", twoJhigh, twoJmid, twoJlow));
1080 multiGraph->Write(Form(
"FitComparison%d_%d_%d", twoJhigh, twoJmid, twoJlow));
1081 canvas->Write(Form(
"Canvas%d_%d_%d", twoJhigh, twoJmid, twoJlow));