modified: TrackRecon.C bookkeeping + introducing CO2 dependent Eloss + diagnostic plots to figure out teh slope correection for the PC model_invert

new file:   eloss_calculations/alpha_lookup_20MeV_3pc_350Torr.dat
	new file:   eloss_calculations/alpha_lookup_20MeV_4pc_350Torr.dat
	new file:   eloss_calculations/aluminum_lookup_80MeV_3pc_350Torr.dat
	new file:   eloss_calculations/aluminum_lookup_80MeV_4pc_350Torr.dat
	new file:   eloss_calculations/fluorine_lookup_70MeV_3pc_350Torr.dat
	new file:   eloss_calculations/fluorine_lookup_70MeV_4pc_350Torr.dat
	new file:   eloss_calculations/oxygen_lookup_70MeV_3pc_350Torr.dat
	new file:   eloss_calculations/oxygen_lookup_70MeV_4pc_350Torr.dat
	new file:   eloss_calculations/proton_lookup_20MeV_3pc_350Torr.dat
	new file:   eloss_calculations/proton_lookup_20MeV_4pc_350Torr.dat
	modified:   run_tr.sh book keeping
	new file:   scan_slope_runs.sh   diagnostic plots to figure out the slope correection for the PC model_invert
	new file:   scratch/plot_slope_scan.C  diagnostic plots to figure out the slope correection for the PC model_invert
This commit is contained in:
Vignesh Sitaraman 2026-06-09 17:37:22 -04:00
parent 948c6e6ea2
commit 8dd6526221
14 changed files with 899084 additions and 47 deletions

View File

@ -39,9 +39,9 @@ Int_t colors[40] = {
#include <algorithm>
bool process_alpha_proton_scattering = false;
bool doMiscHistograms = true;
bool doPCSX3ClusterAnalysis = false;
bool doPCQQQClusterAnalysis = false;
bool doMiscHistograms = false;
bool doPCSX3ClusterAnalysis = true;
bool doPCQQQClusterAnalysis = true;
bool doOldAnalysis = false;
bool do27AlapAnalysis = false;
double source_vertex = 53; // 53
@ -169,7 +169,7 @@ void TrackRecon::Begin(TTree * /*tree*/)
source_vertex = (double)std::atof(std::string(getenv("source_vertex")).c_str());
if (getenv("CO2percent"))
CO2percent = (double)std::atof((getenv("CO2percent")));
CO2percent = std::atoi(getenv("CO2percent"));
std::cout << "CO2 percent set to " << CO2percent << std::endl;
pwinstance.ConstructGeo();
@ -266,12 +266,29 @@ void TrackRecon::Begin(TTree * /*tree*/)
// ------------- ELOSS Correction read in from tables -------------
if (CO2percent == 4)
if (dataset == "17F" && CO2percent == 3)
{
MeV_to_cm = new TGraph("eloss_calculations/alpha_lookup_20MeV_3pc.dat", "%lf %*lf %lf");
MeV_to_cm_p = new TGraph("eloss_calculations/proton_lookup_20MeV_3pc.dat", "%lf %*lf %lf");
MeV_to_cm_27Al = new TGraph("eloss_calculations/aluminum_lookup_80MeV_3pc.dat", "%lf %*lf %lf");
MeV_to_cm_17F = new TGraph("eloss_calculations/fluorine_lookup_70MeV_3pc.dat", "%lf %*lf %lf");
// MeV_to_cm = new TGraph("eloss_calculations/alpha_lookup_20MeV_3pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_p = new TGraph("eloss_calculations/proton_lookup_20MeV_3pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_17F = new TGraph("eloss_calculations/fluorine_lookup_70MeV_3pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_27Al = new TGraph("eloss_calculations/aluminum_lookup_80MeV_3pc_350Torr.dat", "%lf %*lf %lf");
}
else if (dataset == "17F" && CO2percent == 4)
{
MeV_to_cm = new TGraph("eloss_calculations/alpha_lookup_20MeV_4pc.dat", "%lf %*lf %lf");
MeV_to_cm_p = new TGraph("eloss_calculations/proton_lookup_20MeV_4pc.dat", "%lf %*lf %lf");
MeV_to_cm_27Al = new TGraph("eloss_calculations/aluminum_lookup_80MeV_4pc.dat", "%lf %*lf %lf");
MeV_to_cm_17F = new TGraph("eloss_calculations/fluorine_lookup_70MeV_4pc.dat", "%lf %*lf %lf");
// MeV_to_cm = new TGraph("eloss_calculations/alpha_lookup_20MeV_4pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_p = new TGraph("eloss_calculations/proton_lookup_20MeV_4pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_17F = new TGraph("eloss_calculations/fluorine_lookup_70MeV_4pc_350Torr.dat", "%lf %*lf %lf");
// MeV_to_cm_27Al = new TGraph("eloss_calculations/aluminum_lookup_80MeV_4pc_350Torr.dat", "%lf %*lf %lf");
}
else
{
@ -1068,13 +1085,13 @@ Bool_t TrackRecon::Process(Long64_t entry)
double beam_path_length = TMath::Abs(vertex_recon - z_entrance) * 0.1;
#ifdef AL_BEAM
double beam_energy_at_vertex = cm_to_MeV_27Al->Eval(MeV_to_cm_27Al->Eval(72.0) - beam_path_length);
double beam_energy_at_vertex = cm_to_MeV_27Al->Eval(MeV_to_cm_27Al->Eval(65.195) - beam_path_length); // 72MeV is the energy of the 27Al beam right ebfore teh chamber
Kinematics apkin_p(mass_27Al, mass_4He, mass_1H, mass_30Si, beam_energy_at_vertex / mass_27Al);
Kinematics apkin_a(mass_27Al, mass_4He, mass_4He, mass_30Si, beam_energy_at_vertex / mass_27Al);
#endif
#ifdef F_BEAM
double beam_energy_at_vertex = cm_to_MeV_17F->Eval(MeV_to_cm_17F->Eval(65.0) - beam_path_length);
double beam_energy_at_vertex = cm_to_MeV_17F->Eval(MeV_to_cm_17F->Eval(60.741) - beam_path_length); // 67.8 MeV iws the energy of the 17F beam right before the chamber
Kinematics apkin_p(mass_17F, mass_4He, mass_1H, mass_20Ne, beam_energy_at_vertex / mass_17F);
Kinematics apkin_a(mass_17F, mass_4He, mass_4He, mass_20Ne, beam_energy_at_vertex / mass_17F);
#endif
@ -1233,8 +1250,14 @@ void protonAlphaHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events,
// Sidetrack for a(p,p)
std::string aplabel = "a(p,p)";
Kinematics apkin_p(1.008664916, 4.002603254, 1.008664916, 4.002603254, 7.0); // m3 is proton
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, 7.0); // m3 is alpha
double initial_energy = 7.0;
if (dataset == "27Al")
initial_energy = 6.79;
if (dataset == "17F")
initial_energy = 6.78;
Kinematics apkin_p(1.008664916, 4.002603254, 1.008664916, 4.002603254, initial_energy); // m3 is proton
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, initial_energy); // m3 is alpha
for (auto qqqevent : QQQ_Events)
{
@ -1397,6 +1420,7 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, s
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
TVector3 vector_closest_to_z_sx3 = x1 + t_minimum * v;
plotter->Fill1D("VertexReconZ_SX3" + std::to_string(PCSX3TimeCut), 600, -1300, 1300, vector_closest_to_z_sx3.Z(), "hPCZSX3");
plotter->Fill1D("VertexReconZ_SX3", 600, -1300, 1300, vector_closest_to_z_sx3.Z());
plotter->Fill2D("VertexReconXY_SX3" + std::to_string(PCSX3TimeCut), 100, -100, 100, 100, -100, 100, vector_closest_to_z_sx3.X(), vector_closest_to_z_sx3.Y(), "hPCZSX3");
plotter->Fill2D("pcz_vs_time", 2000, 0, 2000, 600, -200, 200, pcevent.Time1 * 1e-9, pcevent.pos.Z()); // x-axis is all Si det, y-axis is PC anode+cathode only
@ -1419,7 +1443,10 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, s
plotter->Fill1D("VertexRecon_pczfix", 800, -300, 300, r_rhoMin_fix.Z());
plotter->Fill1D("pczfix_A1C2_1d_sx3", 600, -200, 200, pcz_fix);
plotter->Fill2D("pczfix_vs_sx3pczguess_A1C2", 600, -200, 200, 600, -200, 200, pczguess, pcz_fix);
plotter->Fill2D("pcz_vs_sx3pczguess_A1C2_strip" + std::to_string(sx3event.ch2), 300, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z());
plotter->Fill2D("pczfix_residual_vs_pczguess_A1C2", 600, -200, 200, 200, -100, 100, pczguess, pcz_fix - pczguess);
plotter->Fill2D("pczfix_residual_vs_phi_A1C2", 200, 0, 6.28, 200, -100, 100, r_rhoMin_fix.Phi(), pcz_fix - pczguess);
plotter->Fill1D("pczfix-sx3pczguess_A1C2", 200, -100, 100, pcz_fix - pczguess);
plotter->Fill2D("pczfix_vs_sx3pczguess_A1C2_strip" + std::to_string(sx3event.ch2), 300, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z());
double sinTheta_customV = TMath::Sin((sx3event.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Theta());
plotter->Fill2D("dE3_E_CathodeSX3_A1C2_TC" + std::to_string(PCSX3TimeCut) + "_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2 * sinTheta_customV);
@ -2034,9 +2061,15 @@ void miscHistograms_oneWire(HistPlotter *plotter, std::vector<Event> QQQ_Events,
{
// consider the 'proton-like' QQQ branch seen in a,p data
TRandom3 rand;
rand.SetSeed(); // random seed set
// Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, 6.34); // m3 is alpha, 6.411 MeV is 7.0 MeV proton energy after havar+mylar+kapton+100mm 4He gas (molar mass 5.3, 250 torr)
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, 6.79); // m3 is alpha, 6.79 MeV is 7.0 MeV proton energy after kapton+100mm 4He gas (molar mass 5.2, 250 torr)
rand.SetSeed(); // random seed setW
double initial_energy = 7.0;
if (dataset == "27Al") /// m3 is alpha, 6.79 MeV is 7.0 MeV proton energy after kapton+100mm 4He gas (molar mass 5.6, 250 torr)
initial_energy = 6.79;
if (dataset == "17F")
initial_energy = 6.78; // m3 is alpha, 6.79 MeV is 7.0 MeV proton energy after kapton+100mm 4He gas (molar mass 5.6, 350 torr)
// initial_energy = 6.32; // m3 is alpha, 6.411 MeV is 7.0 MeV proton energy after havar+mylar+kapton+100mm 4He gas (molar mass 5.3, 250 torr)
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, initial_energy);
for (auto qqqevent : QQQ_Events)
{
if (qqqevent.Energy1 < 0.6)
@ -2126,7 +2159,7 @@ void protonMiscHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events, s
if (dataset == "27Al")
initial_energy = 6.79; // m3 is alpha, 6.79 MeV is 7.0 MeV proton energy after kapton+100mm 4He gas (molar mass 5.2, 250 torr)
if (dataset == "17F")
initial_energy = 6.34; // m3 is alpha, 6.411 MeV is 7.0 MeV proton energy after Havar+kapton+100mm 4He gas (molar mass 5.2, 250 torr)
initial_energy = 6.32; // m3 is alpha, 6.411 MeV is 7.0 MeV proton energy after Havar+kapton+100mm 4He gas (molar mass 5.2, 250 torr)
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, initial_energy); // m3 is alpha

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -11,8 +11,8 @@ root -q -l -b -e '.L TrackRecon.C++O'
process_run() {
local wrun=$(printf "%03d" "$1")
local prefix="${PREFIX:-Run_}"
local outdir="${OUT_DIR:-output}" # Defaults to 'output' if OUT_DIR is not set
local out="$Output_{outdir}/results_run${wrun}.root"
local outdir="${OUT_DIR:-Output_default}"
local out="${outdir}/results_run${wrun}.root"
# Ensure the directory exists so ROOT doesn't fail silently
mkdir -p "$outdir"
@ -27,16 +27,16 @@ process_run() {
fi
}
export -f process_run
export -f process_run
export CO2percent=3
# --- Block 1: 27Al Source Runs No Gas (1-8) ---
if [[ 1 -eq 0 ]]; then
export DATASET="27Al"
export PREFIX="Run_"
export OUT_DIR="av"
export OUT_DIR="Output_av"
echo "Starting parallel processing for 27Al runs..."
rm -f p_output/all.root
rm -f ${OUT_DIR}/all.root
parallel --bar -j 6 process_run ::: {1..8}
fi
@ -45,81 +45,75 @@ fi
if [[ 1 -eq 0 ]]; then
export DATASET="27Al"
export PREFIX="Run_"
export timecut_low=400.0
export source_vertex=53.44
export OUT_DIR="a"
export OUT_DIR="Output_a"
echo "Processing 27Al alpha+gas runs..."
parallel --bar -j 6 process_run ::: 9 12
export source_vertex=-5.36; export timecut_low=12.0; export timecut_high=119.0; process_run 9 "$slope"
export source_vertex=53.44; export timecut_low=400.0; process_run 12 "$slope"
unset timecut_low
unset timecut_high
fi
# --- Block 3: 27Al Protons+Gas Runs (15, 17-22) ---
if [[ 1 -eq 1 ]]; then
if [[ 1 -eq 0 ]]; then
export DATASET="27Al"
export PREFIX="Run_"
export OUT_DIR="p"
export OUT_DIR="Output_p"
export source_vertex=-200.0 # Source on the entrance window
echo "Starting parallel processing for 27Al proton runs..."
# rm -f p_output/all.root
parallel --bar -j 6 process_run ::: 15 {17..22}
hadd -j 4 -k p_output/Al_protons.root p_output/results_run0{17..22}.root
hadd -j 4 -k ${OUT_DIR}/Al_protons.root ${OUT_DIR}/results_run0{17..22}.root
fi
# --- Block 4: 17F Source Runs (5-14) ---
if [[ 1 -eq 0 ]]; then
export DATASET="17F"
export PREFIX="Source_"
export OUT_DIR="av"
export OUT_DIR="Output_av"
echo "Starting parallel processing for 17F source runs..."
# rm -f p_output/all.root
parallel --bar -j 6 process_run ::: {5..13}
fi
# --- Block 5: 17F Alpha Run with Gas (18-21) ---
if [[ 1 -eq 0 ]]; then
if [[ 1 -eq 1 ]]; then
export DATASET="17F"
export PREFIX="SourceRun_"
export OUT_DIR="a"
export OUT_DIR="Output_a"
echo "Processing 17F alpha runs with dynamic source vertices..."
# Running sequentially since the source_vertex variable changes per run
export source_vertex=53.44; process_run 18
# export source_vertex=53.44; process_run 18
export source_vertex=14.24; process_run 19
export source_vertex=-24.96; process_run 20
export source_vertex=-73.96; process_run 21
# export source_vertex=-24.96; process_run 20
# export source_vertex=-73.96; process_run 21
fi
# --- Block 6: 17F Proton Data ---
if [[ 1 -eq 1 ]]; then
if [[ 1 -eq 0 ]]; then
export DATASET="17F"
export PREFIX="ProtonRun_"
export OUT_DIR="p"
export OUT_DIR="Output_p"
export source_vertex=-57.28
parallel --bar -j 6 process_run ::: {44..48} #3CO2
hadd -j 4 -k p_output/3pc.root p_output/results_run0{44..48}.root
# parallel --bar -j 6 process_run ::: {44..48} #3% CO2
# hadd -j 4 -k ${OUT_DIR}/3pc.root ${OUT_DIR}/results_run0{44..48}.root
export CO2percent=4
parallel --bar -j 6 process_run ::: {38..42} #4% CO2
hadd -j 4 -k p_output/4pc.root p_output/results_run0{38..42}.root
exit
hadd -j 4 -k ${OUT_DIR}/4pc.root ${OUT_DIR}/results_run0{38..42}.root
fi
# ==========================================
# Cleanup Environment Variables
# ==========================================
unset flipa
unset flipc
unset anode_offset
unset cathode_offset
unset source_vertex # Fixed typo here
unset source_vertex
unset DATASET
unset OUT_DIR
unset PREFIX
unset flip180
unset CO2percent
unset timecut_low
unset timecut_high
unset CO2percent
echo "Script execution finished."

56
scan_slope_runs.sh Normal file
View File

@ -0,0 +1,56 @@
#!/bin/bash
# Scans model_invert slope from 0.44 to 0.56 (step 0.02).
# For each slope: patches PC_StepLadder_Correction.h, recompiles,
# runs all source runs, saves results under slope_scan/run<NNN>/slope_<val>.root
set -e
CORRECTION_H="Armory/PC_StepLadder_Correction.h"
SCAN_DIR="slope_scan"
process_run() {
local wrun=$(printf "%03d" "$1")
local slope="$2"
local outdir="${SCAN_DIR}/run${wrun}"
mkdir -p "$outdir"
local out="${outdir}/slope_${slope}.root"
root -q -l -b -x "../ANASEN_analysis/data/${DATASET}_Data/${PREFIX}${wrun}_mapped.root" \
-e "tree->Process(\"TrackRecon.C+\", \"${out}\")" > /dev/null 2>&1
[ -f "$out" ] && echo " run $wrun slope $slope OK" || echo " run $wrun slope $slope FAILED"
}
export -f process_run
export SCAN_DIR
for slope_x100 in 40 42 44 46 48 50 52 54 56 58 60 62 64 66; do
# for slope_x100 in 58 60 62 64 66; do
slope=$(awk "BEGIN{printf \"%.2f\", $slope_x100/100}")
echo "=== slope=${slope} ==="
sed -i "s/double slope = [0-9.]*/double slope = ${slope}/" "$CORRECTION_H"
echo " Compiling..."
root -q -l -b -e '.L TrackRecon.C++O' 2>/dev/null
# 27Al alpha+gas runs (9, 12, 13)
export DATASET="27Al" PREFIX="Run_"
echo " 27Al runs 9 12 13..."
# export source_vertex=53.44; export timecut_low=400.0; process_run 12 "$slope"
export source_vertex=-5.36; export timecut_low=12.0; export timecut_high=120.0; process_run 9 "$slope"
unset timecut_low
unset timecut_high
# 17F alpha runs with per-run source vertices
export DATASET="17F" PREFIX="SourceRun_"
echo " 17F runs 18-21..."
export source_vertex=53.44; process_run 18 "$slope"
export source_vertex=14.24; process_run 19 "$slope"
export source_vertex=-24.96; process_run 20 "$slope"
export source_vertex=-73.96; process_run 21 "$slope"
echo " done"
done
sed -i "s/double slope = [0-9.]*/double slope = 0.60/" "$CORRECTION_H"
echo "Restored slope = 0.60"
echo ""
echo "All done. Now run:"
echo " root -l 'scratch/plot_slope_scan.C'"

346
scratch/plot_slope_scan.C Normal file
View File

@ -0,0 +1,346 @@
#include <TFile.h>
#include <TH1.h>
#include <TF1.h>
#include <TFitResult.h>
#include <TGraph.h>
#include <TMultiGraph.h>
#include <TCanvas.h>
#include <TLegend.h>
#include <TLine.h>
#include <TString.h>
void plot_slope_scan()
{
// =========================================================================
// --- Configuration ---
// =========================================================================
// Set to true to inspect each Gaussian fit. Double-click the canvas to advance.
bool checkFits = false;
gStyle->SetOptStat(0);
double slopes[] = {0.40, 0.42, 0.44, 0.46, 0.48, 0.50, 0.52, 0.54, 0.56, 0.58, 0.60, 0.62, 0.64, 0.66};
const int N = 14; // Updated to 14 to match the size of your slopes array
// int runs[] = {9, 12, 18, 19, 20, 21};
// const int NRUNS = 6;
// int runs[] = {9, 12};
// const int NRUNS = 2;
int runs[] = {18, 19, 20, 21};
const int NRUNS = 4;
Int_t runColors[6] = {kRed + 1, kBlue + 2, kGreen + 2, kOrange + 1, kViolet + 2, kCyan + 2};
// --- Initialize Graphs ---
TGraph *gStdDevRun[NRUNS];
TGraph *gMeanRun[NRUNS];
for (int ir = 0; ir < NRUNS; ir++)
{
gStdDevRun[ir] = new TGraph(N);
gMeanRun[ir] = new TGraph(N);
}
TGraph *gStdDevSum = new TGraph(N);
TGraph *gMeanSum = new TGraph(N);
TH1 *h1[NRUNS][N];
// --- Load Data ---
for (int ir = 0; ir < NRUNS; ir++)
{
for (int i = 0; i < N; i++)
{
h1[ir][i] = nullptr;
TString path = TString::Format("slope_scan/run%03d/slope_%.2f.root", runs[ir], slopes[i]);
TFile *f = TFile::Open(path);
if (!f || f->IsZombie())
continue;
TH1 *t1 = (TH1 *)f->Get("pczfix-sx3pczguess_A1C2");
if (t1)
{
t1->SetDirectory(nullptr);
h1[ir][i] = t1;
}
f->Close();
}
}
// =========================================================================
// --- Calculate Mean & StdDev with Bounded Gaussian Fit ---
// =========================================================================
std::vector<int> nPtsRun(NRUNS, 0);
int nPtsSum = 0;
double fitWindow = 15.0; // Window around the peak to fit (mm)
TCanvas *cDebug = nullptr;
if (checkFits)
{
cDebug = new TCanvas("cDebug", "Fit Diagnostic Debugger", 0, 0, 800, 600);
}
for (int i = 0; i < N; i++)
{
TH1 *hsum = nullptr;
for (int ir = 0; ir < NRUNS; ir++)
{
if (!h1[ir][i] || h1[ir][i]->GetEntries() <= 0)
continue;
// 1. Find center of the highest peak
int maxBin = h1[ir][i]->GetMaximumBin();
double peakX = h1[ir][i]->GetXaxis()->GetBinCenter(maxBin);
// 2. Define the fit
TF1 *gfit = new TF1("gfit", "gaus", peakX - fitWindow, peakX + fitWindow);
// 3. Fit Conditionally (Draw if checking, otherwise run quietly in background)
TString fitOpt = checkFits ? "RQS" : "RQS0";
if (checkFits)
cDebug->cd(); // Ensure we draw on the debug canvas
TFitResultPtr fitRes = h1[ir][i]->Fit(gfit, fitOpt);
// --- DIAGNOSTIC PAUSE ---
if (checkFits)
{
h1[ir][i]->SetTitle(TString::Format("Run %d | Slope %.2f", runs[ir], slopes[i]));
h1[ir][i]->GetXaxis()->SetRangeUser(peakX - 50, peakX + 50); // Zoom in X
// Scale Y-axis to the maximum of this specific zoomed window + 20% headroom
h1[ir][i]->SetMaximum(h1[ir][i]->GetMaximum() * 1.2);
h1[ir][i]->Draw();
cDebug->Modified();
cDebug->Update();
printf("Visual Check: Run %d | Slope %.2f. Double-click canvas to continue...\n", runs[ir], slopes[i]);
while (cDebug->WaitPrimitive())
;
}
double mean, stddev;
if (fitRes >= 0)
{ // If fit succeeds
mean = gfit->GetParameter(1);
stddev = gfit->GetParameter(2);
}
else
{ // Fallback to raw stats if fit fails
mean = h1[ir][i]->GetMean();
stddev = h1[ir][i]->GetStdDev();
}
gStdDevRun[ir]->SetPoint(nPtsRun[ir], slopes[i], stddev);
gMeanRun[ir]->SetPoint(nPtsRun[ir]++, slopes[i], mean);
delete gfit;
// Build the cumulative sum histogram
if (!hsum)
hsum = (TH1 *)h1[ir][i]->Clone(TString::Format("hsum_%.2f", slopes[i]));
else
hsum->Add(h1[ir][i]);
}
// --- Process Summed Histogram ---
if (hsum && hsum->GetEntries() > 0)
{
int maxBinSum = hsum->GetMaximumBin();
double peakXSum = hsum->GetXaxis()->GetBinCenter(maxBinSum);
TF1 *gfitSum = new TF1("gfitSum", "gaus", peakXSum - fitWindow, peakXSum + fitWindow);
TString fitOptSum = checkFits ? "RQS" : "RQS0";
if (checkFits)
cDebug->cd();
TFitResultPtr fitResSum = hsum->Fit(gfitSum, fitOptSum);
// --- DIAGNOSTIC PAUSE FOR SUM ---
if (checkFits)
{
hsum->SetTitle(TString::Format("SUMMED RUNS | Slope %.2f", slopes[i]));
hsum->GetXaxis()->SetRangeUser(peakXSum - 50, peakXSum + 50);
// Scale Y-axis for the sum + 20% headroom
hsum->SetMaximum(hsum->GetMaximum() * 1.2);
hsum->Draw();
cDebug->Modified();
cDebug->Update();
printf("Visual Check: SUMMED | Slope %.2f. Double-click canvas to continue...\n", slopes[i]);
while (cDebug->WaitPrimitive())
;
}
// if (fitResSum >= 0)
// {
// gStdDevSum->SetPoint(nPtsSum, slopes[i], gfitSum->GetParameter(2));
// gMeanSum->SetPoint(nPtsSum++, slopes[i], gfitSum->GetParameter(1));
// }
// else
// {
// gStdDevSum->SetPoint(nPtsSum, slopes[i], hsum->GetStdDev());
// gMeanSum->SetPoint(nPtsSum++, slopes[i], hsum->GetMean());
// }
// delete gfitSum;
// delete hsum;
}
}
if (checkFits && cDebug)
{
delete cDebug; // Clean up debug canvas before plotting final results
}
// =========================================================================
// ---- Canvas 1: StdDev and Mean vs Slope
// =========================================================================
TCanvas *c1 = new TCanvas("c_stats", "1D Residual Stats vs Slope", 0, 0, 1600, 600);
c1->Divide(2, 1);
c1->cd(1);
gPad->SetGrid(1, 1);
TLegend *leg1 = new TLegend(0.78, 0.45, 0.97, 0.88);
leg1->SetBorderSize(1);
TMultiGraph *mg1 = new TMultiGraph("mg_stddev", "StdDev(pczfix - sx3pczguess) vs Slope;Slope;StdDev (mm)");
for (int ir = 0; ir < NRUNS; ir++)
{
if (nPtsRun[ir] == 0)
continue;
gStdDevRun[ir]->SetLineColor(runColors[ir]);
gStdDevRun[ir]->SetMarkerColor(runColors[ir]);
gStdDevRun[ir]->SetLineWidth(2);
gStdDevRun[ir]->SetMarkerStyle(20 + ir);
mg1->Add(gStdDevRun[ir], "PL");
leg1->AddEntry(gStdDevRun[ir], TString::Format("run %d", runs[ir]), "lp");
}
if (nPtsSum > 0)
{
gStdDevSum->SetLineColor(kBlack);
gStdDevSum->SetLineWidth(3);
gStdDevSum->SetLineStyle(kDashed);
gStdDevSum->SetMarkerStyle(29);
mg1->Add(gStdDevSum, "PL");
leg1->AddEntry(gStdDevSum, "sum", "lp");
}
mg1->Draw("A");
leg1->Draw();
double bestSlope = 0, bestStdDev = 1e9;
for (int i = 0; i < gStdDevRun[0]->GetN(); i++)
{
double x, y;
gStdDevRun[0]->GetPoint(i, x, y);
if (y > 0 && y < bestStdDev)
{
bestStdDev = y;
bestSlope = x;
}
}
TLine *lb = new TLine(bestSlope, mg1->GetHistogram()->GetMinimum(),
bestSlope, mg1->GetHistogram()->GetMaximum());
lb->SetLineColor(kRed);
lb->SetLineStyle(kDashed);
lb->Draw();
printf(">>> Best slope (Run %d) = %.2f StdDev=%.2f mm\n", runs[0], bestSlope, bestStdDev);
c1->cd(2);
gPad->SetGrid(1, 1);
TLegend *leg2 = new TLegend(0.78, 0.45, 0.97, 0.88);
leg2->SetBorderSize(1);
TMultiGraph *mg2 = new TMultiGraph("mg_mean", "Mean(pczfix - sx3pczguess) vs Slope;Slope;Mean Offset (mm)");
for (int ir = 0; ir < NRUNS; ir++)
{
if (nPtsRun[ir] == 0)
continue;
gMeanRun[ir]->SetLineColor(runColors[ir]);
gMeanRun[ir]->SetMarkerColor(runColors[ir]);
gMeanRun[ir]->SetLineWidth(2);
gMeanRun[ir]->SetMarkerStyle(20 + ir);
mg2->Add(gMeanRun[ir], "PL");
leg2->AddEntry(gMeanRun[ir], TString::Format("run %d", runs[ir]), "lp");
}
if (nPtsSum > 0)
{
gMeanSum->SetLineColor(kBlack);
gMeanSum->SetLineWidth(3);
gMeanSum->SetLineStyle(kDashed);
gMeanSum->SetMarkerStyle(29);
mg2->Add(gMeanSum, "PL");
leg2->AddEntry(gMeanSum, "sum", "lp");
}
mg2->Draw("A");
TLine *lz = new TLine(slopes[0], 0, slopes[N - 1], 0);
lz->SetLineColor(kGray + 2);
lz->SetLineStyle(kDotted);
lz->Draw();
leg2->Draw();
c1->SaveAs("slope_scan_1d_metric.png");
// =========================================================================
// ---- Canvas 2: Per-slope pads, overlaying the 1D Residual distributions
// =========================================================================
TCanvas *c2 = new TCanvas("c_perslope_1d", "Per-slope 1D Residuals: all runs overlaid", 0, 100, 1800, 1200);
c2->Divide(4, 4);
for (int i = 0; i < N; i++)
{
c2->cd(i + 1);
gPad->SetGrid(1, 1);
TLegend *legS = new TLegend(0.62, 0.55, 0.88, 0.88);
legS->SetBorderSize(0);
// --- STEP A: Pre-scan to find the highest Y value across all runs for this slope ---
double maxY = 0;
for (int ir = 0; ir < NRUNS; ir++)
{
if (!h1[ir][i] || h1[ir][i]->GetEntries() <= 0)
continue;
// Set X range first so GetMaximum() doesn't accidentally grab a tail far away
h1[ir][i]->GetXaxis()->SetRangeUser(-50, 50);
if (h1[ir][i]->GetMaximum() > maxY)
{
maxY = h1[ir][i]->GetMaximum();
}
}
// --- STEP B: Draw with the dynamically scaled Y-axis ---
bool first = true;
for (int ir = 0; ir < NRUNS; ir++)
{
if (!h1[ir][i] || h1[ir][i]->GetEntries() <= 0)
continue;
h1[ir][i]->SetLineColor(runColors[ir]);
h1[ir][i]->SetLineWidth(2);
h1[ir][i]->SetTitle(TString::Format("slope=%.2f;Residual (mm);Counts", slopes[i]));
if (first)
{
// Set the pad's Y-axis using the global max + 15% buffer so the legend fits cleanly
h1[ir][i]->SetMaximum(maxY * 1.15);
h1[ir][i]->Draw("HIST");
first = false;
}
else
{
h1[ir][i]->Draw("HIST SAME");
}
legS->AddEntry(h1[ir][i], TString::Format("run %d", runs[ir]), "l");
}
if (!first)
legS->Draw();
}
c2->SaveAs("slope_scan_perslope_1d.png");
c2->Modified();
c2->Update();
while (gPad->WaitPrimitive())
;
}