modified: TrackRecon.C aded a2c1 and a2c2 toplogies just to see waht tehy look like

modified:   run_27Al.sh
	modified:   run_tr.sh
	new file:   scratch/RunwiseTrend.C diagnostics to see variationwith run
	modified:   slope_intercept_results_17F.dat
This commit is contained in:
Vignesh Sitaraman 2026-09-14 11:00:08 -04:00
parent bbf00ad3c6
commit 7ec86569fb
5 changed files with 431 additions and 183 deletions

View File

@ -43,8 +43,8 @@ Int_t colors[40] = {
bool process_alpha_proton_scattering = false, bool process_alpha_proton_scattering = false,
doMiscHistograms = true, doMiscHistograms = true,
doRawHistos = false, doRawHistos = false,
doPCSX3ClusterAnalysis = false, doPCSX3ClusterAnalysis = true,
doPCQQQClusterAnalysis = false, doPCQQQClusterAnalysis = true,
doOldAnalysis = false, doOldAnalysis = false,
BenchMark = true, BenchMark = true,
onewire_analysis = true, onewire_analysis = true,
@ -2428,13 +2428,18 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
continue; continue;
double sx3theta = TMath::ATan2(sx3event.pos.Perp(), sx3event.pos.Z() - source_vertex); // true per-strip rho, not nominal 88 double sx3theta = TMath::ATan2(sx3event.pos.Perp(), sx3event.pos.Z() - source_vertex); // true per-strip rho, not nominal 88
double pczguess = 37.0 / TMath::Tan(sx3theta) + source_vertex; double pczguess = 37.0 / TMath::Tan(sx3theta) + source_vertex;
plotter->Fill1D("Benchmark_SX3_VertexZ_trueA1C0", 800, -400, 400, vtx0.Z(), "A1C0True_SX3"); // Split by anode-cluster wire count: 1 wire -> genuine A1C0, 2 wires ->
plotter->Fill1D("Benchmark_SX3_VertexZ_trueA1C0_Hybrid", 800, -400, 400, vtx1.Z(), "A1C0True_SX3"); // genuine A2C0. Same pseudowire math for both (a2c0_wirePos == a1c0_wirePos);
plotter->Fill1D("Benchmark_SX3_VertexZ_trueA1C0_Hybrid_TC" + std::to_string(PCSX3TimeCut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx1.Z(), "A1C0True_SX3"); // the tag keeps the two topologies in separate histograms.
plotter->Fill2D("Benchmark_SX3_VertexXY_trueA1C0_Hybrid", 200, -100, 100, 200, -100, 100, vtx1.X(), vtx1.Y(), "A1C0True_SX3"); const std::string t0 = (aCl.size() == 2) ? "trueA2C0" : "trueA1C0";
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C0_Hybrid", 600, -200, 200, pc_hybrid.Z(), "A1C0True_SX3"); const std::string b0 = (aCl.size() == 2) ? "A2C0True_SX3" : "A1C0True_SX3";
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C0_Hybrid_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pc_hybrid.Z(), "A1C0True_SX3"); plotter->Fill1D("Benchmark_SX3_VertexZ_" + t0, 800, -400, 400, vtx0.Z(), b0);
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C0_Hybrid_minus_sx3pczguess", 400, -100, 100, pc_hybrid.Z() - pczguess, "A1C0True_SX3"); plotter->Fill1D("Benchmark_SX3_VertexZ_" + t0 + "_Hybrid", 800, -400, 400, vtx1.Z(), b0);
plotter->Fill1D("Benchmark_SX3_VertexZ_" + t0 + "_Hybrid_TC" + std::to_string(PCSX3TimeCut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx1.Z(), b0);
plotter->Fill2D("Benchmark_SX3_VertexXY_" + t0 + "_Hybrid", 200, -100, 100, 200, -100, 100, vtx1.X(), vtx1.Y(), b0);
plotter->Fill1D("Benchmark_SX3_PCZ_" + t0 + "_Hybrid", 600, -200, 200, pc_hybrid.Z(), b0);
plotter->Fill2D("Benchmark_SX3_PCZ_" + t0 + "_Hybrid_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pc_hybrid.Z(), b0);
plotter->Fill1D("Benchmark_SX3_PCZ_" + t0 + "_Hybrid_minus_sx3pczguess", 400, -100, 100, pc_hybrid.Z() - pczguess, b0);
} }
} }
@ -2635,6 +2640,7 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
{ {
const auto &aCl = aClusters.front(); const auto &aCl = aClusters.front();
const auto &cCl = cClusters.front(); const auto &cCl = cClusters.front();
const std::string A = (aCl.size() == 2) ? "A2" : "A1";
const std::string benchBranch = "Benchmark_SX3"; const std::string benchBranch = "Benchmark_SX3";
auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint) auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint)
{ {
@ -2729,47 +2735,48 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
if (phicut && PCSX3TimeCut) if (phicut && PCSX3TimeCut)
{ {
if (pcevent.multi1 == 1 && pcevent.multi2 == 2) if (pcevent.multi2 == 2)
{ {
fillSuite("A1C2", pcz_ref, vtx_ref, benchBranch); fillSuite(A + "C2", pcz_ref, vtx_ref, benchBranch);
{ {
double phi_deg = sx3event.pos.Phi() * 180.0 / M_PI; double phi_deg = sx3event.pos.Phi() * 180.0 / M_PI;
double vz_resid = vtx_ref.Z() - source_vertex; double vz_resid = vtx_ref.Z() - source_vertex;
plotter->Fill2D("Diag_SX3_A1C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset"); plotter->Fill2D("Diag_SX3_" + A + "C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset");
plotter->Fill2D("Diag_SX3_A1C2_vtxXY", 200, -15, 15, 200, -15, 15, vtx_ref.X(), vtx_ref.Y(), "Diag_XYoffset"); plotter->Fill2D("Diag_SX3_" + A + "C2_vtxXY", 200, -15, 15, 200, -15, 15, vtx_ref.X(), vtx_ref.Y(), "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset");
plotter->Fill2D("Diag_SX3_A1C2_T_vs_vtxX", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.X(), "Diag_XYoffset"); plotter->Fill2D("Diag_SX3_" + A + "C2_T_vs_vtxX", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.X(), "Diag_XYoffset");
plotter->Fill2D("Diag_SX3_A1C2_T_vs_vtxY", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.Y(), "Diag_XYoffset"); plotter->Fill2D("Diag_SX3_" + A + "C2_T_vs_vtxY", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.Y(), "Diag_XYoffset");
} }
doA1C1("A1C1", sx3event.pos, false); doA1C1(A + "C1", sx3event.pos, false);
doAnodeOnly("A1C0", sx3event.pos.Phi(), sx3event.pos, false); doAnodeOnly(A + "C0", sx3event.pos.Phi(), sx3event.pos, false);
doA1C1("A1C1_Hyb", smeared_sx3_pos); doA1C1(A + "C1_Hyb", smeared_sx3_pos);
doAnodeOnly("A1C0_Hyb", smeared_phi, smeared_sx3_pos); doAnodeOnly(A + "C0_Hyb", smeared_phi, smeared_sx3_pos);
doA1C1Model("A1C1_Cfrac", sx3event.pos); doA1C1Model(A + "C1_Cfrac", sx3event.pos);
{ {
double pcz_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, sx3event.pos.Phi()).Z(); double pcz_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, sx3event.pos.Phi()).Z();
double theta_ref = (sx3event.pos - beamAxisPoint(vtx_ref.Z())).Theta() * 180. / M_PI; double theta_ref = (sx3event.pos - beamAxisPoint(vtx_ref.Z())).Theta() * 180. / M_PI;
plotter->Fill2D("Benchmark_SX3_PCZ_A1C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_PCZ_" + A + "C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_SX3_ref");
plotter->Fill2D("Benchmark_PCZ_A1C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_AnodeOnly"); plotter->Fill2D("Benchmark_PCZ_" + A + "C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_AnodeOnly");
double phi_deg_a = sx3event.pos.Phi() * 180.0 / M_PI; double phi_deg_a = sx3event.pos.Phi() * 180.0 / M_PI;
plotter->Fill2D("Diag_SX3_A1C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset"); plotter->Fill2D("Diag_SX3_" + A + "C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset");
} }
if (a1c1Good && cfrac >= 0.0) if (a1c1Good && cfrac >= 0.0)
{ {
plotter->Fill1D("Benchmark_SX3_A1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_SX3_ref"); const std::string Esuf = (A == "A2") ? "_A2" : ""; // keep a1 energy-vs-cell names byte-identical
plotter->Fill2D("Benchmark_SX3_A1C1_cfrac_vs_ref", 400, -200, 200, 220, -0.05, 1.05, pcz_ref, cfrac, "Benchmark_SX3_ref"); plotter->Fill1D("Benchmark_SX3_" + A + "C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_SX3_ref");
plotter->Fill2D("Benchmark_SX3_A1C1_cfrac_vs_sx3pczguess", 400, -200, 200, 220, -0.05, 1.05, pczguess, cfrac, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfrac_vs_ref", 400, -200, 200, 220, -0.05, 1.05, pcz_ref, cfrac, "Benchmark_SX3_ref");
plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfrac_vs_sx3pczguess", 400, -200, 200, 220, -0.05, 1.05, pczguess, cfrac, "Benchmark_SX3_ref");
static const double zg[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998}; static const double zg[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
double zp = xo_a1c1.Z(); double zp = xo_a1c1.Z();
auto fillCfracS = [&](const char *name, double truth) auto fillCfracS = [&](const std::string &name, double truth)
{ {
double sgn = (truth >= zp) ? 1.0 : -1.0; double sgn = (truth >= zp) ? 1.0 : -1.0;
double znb = (sgn > 0) ? 1.0e30 : -1.0e30; double znb = (sgn > 0) ? 1.0e30 : -1.0e30;
@ -2784,8 +2791,8 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
plotter->Fill2D(name, 240, -1.2, 1.2, 220, -0.05, 1.05, plotter->Fill2D(name, 240, -1.2, 1.2, 220, -0.05, 1.05,
(truth - zp) / TMath::Abs(znb - zp), cfrac, "Benchmark_SX3_ref"); (truth - zp) / TMath::Abs(znb - zp), cfrac, "Benchmark_SX3_ref");
}; };
fillCfracS("Benchmark_SX3_A1C1_cfrac_vs_s", pcz_ref); fillCfracS("Benchmark_SX3_" + A + "C1_cfrac_vs_s", pcz_ref);
fillCfracS("Benchmark_SX3_A1C1_cfrac_vs_s_sx3pczguess", pczguess); fillCfracS("Benchmark_SX3_" + A + "C1_cfrac_vs_s_sx3pczguess", pczguess);
for (int i = 0; i < 7; ++i) for (int i = 0; i < 7; ++i)
{ {
@ -2794,14 +2801,14 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
double zc = 0.5 * (zg[i] + zg[i + 1]); double zc = 0.5 * (zg[i] + zg[i + 1]);
double half = 0.5 * (zg[i] - zg[i + 1]); double half = 0.5 * (zg[i] - zg[i + 1]);
if (half > 0.0) if (half > 0.0)
plotter->Fill2D("Benchmark_SX3_A1C1_cfrac_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, cfrac, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfrac_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, cfrac, "Benchmark_SX3_ref");
break; break;
} }
} }
plotter->Fill2D("Benchmark_SX3_A1C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "Benchmark_SX3_ref");
if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0) if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0)
plotter->Fill2D("Benchmark_SX3_A1C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "Benchmark_SX3_ref");
{ {
A1C1PickedSol sm = a1c1_solve_pick(cfrac, xo_a1c1.Z(), sx3event.pos, xo_a1c1.X(), xo_a1c1.Y(), A1C1PickedSol sm = a1c1_solve_pick(cfrac, xo_a1c1.Z(), sx3event.pos, xo_a1c1.X(), xo_a1c1.Y(),
@ -2817,24 +2824,24 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
if (cell_truth >= 0) if (cell_truth >= 0)
{ {
bool wrong = (sm_cell != cell_truth); bool wrong = (sm_cell != cell_truth);
plotter->Fill2D("Benchmark_SX3_A1C1_cellsel_confusion", 7, 0, 7, 7, 0, 7, cell_truth + 0.5, sm_cell + 0.5, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cellsel_confusion", 7, 0, 7, 7, 0, 7, cell_truth + 0.5, sm_cell + 0.5, "Benchmark_SX3_ref");
plotter->Fill1D("Benchmark_SX3_A1C1_cellsel_misclass", 2, 0, 2, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref"); plotter->Fill1D("Benchmark_SX3_" + A + "C1_cellsel_misclass", 2, 0, 2, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref");
plotter->Fill2D("Benchmark_SX3_A1C1_cellsel_misclass_vs_cell", 7, 0, 7, 2, 0, 2, cell_truth + 0.5, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cellsel_misclass_vs_cell", 7, 0, 7, 2, 0, 2, cell_truth + 0.5, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref");
double zc = 0.5 * (a1c1_zg[cell_truth] + a1c1_zg[cell_truth + 1]); double zc = 0.5 * (a1c1_zg[cell_truth] + a1c1_zg[cell_truth + 1]);
double half = 0.5 * (a1c1_zg[cell_truth] - a1c1_zg[cell_truth + 1]); double half = 0.5 * (a1c1_zg[cell_truth] - a1c1_zg[cell_truth + 1]);
plotter->Fill2D("AnodeEnergy_vs_CellSX3", 120, 0, 1.2, 800, 0, 40000, 1 - TMath::Abs(pcz_ref - zc) / half, pcevent.Energy1); plotter->Fill2D("AnodeEnergy_vs_CellSX3" + Esuf, 120, 0, 1.2, 800, 0, 40000, 1 - TMath::Abs(pcz_ref - zc) / half, pcevent.Energy1);
plotter->Fill2D("CathodeEnergy_vs_CellSX3", 120, 0, 1.2, 800, 0, 40000, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2); plotter->Fill2D("CathodeEnergy_vs_CellSX3" + Esuf, 120, 0, 1.2, 800, 0, 40000, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2);
plotter->Fill2D("FracEnergy_vs_CellSX3", 120, 0, 1.2, 800, 0, 10, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2 / pcevent.Energy1); plotter->Fill2D("FracEnergy_vs_CellSX3" + Esuf, 120, 0, 1.2, 800, 0, 10, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2 / pcevent.Energy1);
if (half > 0.0) if (half > 0.0)
{ {
plotter->Fill2D("Benchmark_SX3_A1C1_cellsel_misclass_vs_fold", 120, 0, 1.2, 2, 0, 2, TMath::Abs(pcz_ref - zc) / half, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cellsel_misclass_vs_fold", 120, 0, 1.2, 2, 0, 2, TMath::Abs(pcz_ref - zc) / half, wrong ? 1.0 : 0.0, "Benchmark_SX3_ref");
plotter->Fill2D("Benchmark_SX3_A1C1_cfracUsed_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, sm.sol.cfrac_used, "Benchmark_SX3_ref"); plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfracUsed_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, sm.sol.cfrac_used, "Benchmark_SX3_ref");
if (aSumE_bm > 0.0) if (aSumE_bm > 0.0)
{ {
plotter->Fill2D("Benchmark_SX3_A1C1_cfracUsed_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, plotter->Fill2D("Benchmark_SX3_" + A + "C1_cfracUsed_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05,
aSumE_bm, sm.sol.cfrac_used, "Benchmark_SX3_ref"); aSumE_bm, sm.sol.cfrac_used, "Benchmark_SX3_ref");
} }
} }
@ -2843,13 +2850,15 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
} }
} }
else if (pcevent.multi1 == 1 && pcevent.multi2 == 1 && a1c1Good) else if (pcevent.multi2 == 1 && a1c1Good)
{ {
const std::string bTrue = A + "C1True_SX3"; // per-topology folder (A1C1True_SX3 / A2C1True_SX3)
const std::string fTrue = "Benchmark_SX3_true" + A + "C1"; // failreason/band diagnostics folder
double pcz_raw = xo_a1c1.Z(); double pcz_raw = xo_a1c1.Z();
TVector3 vtx_raw = vertexFrom(sx3event.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw)); TVector3 vtx_raw = vertexFrom(sx3event.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw));
fillSuite("trueA1C1", pcz_raw, vtx_raw, "A1C1True_SX3"); fillSuite("true" + A + "C1", pcz_raw, vtx_raw, bTrue);
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C1_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pcz_raw, "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_PCZ_true" + A + "C1_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pcz_raw, bTrue);
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C1_minus_sx3pczguess", 400, -100, 100, pcz_raw - pczguess, "A1C1True_SX3"); plotter->Fill1D("Benchmark_SX3_PCZ_true" + A + "C1_minus_sx3pczguess", 400, -100, 100, pcz_raw - pczguess, bTrue);
if (cfrac >= 0.0) if (cfrac >= 0.0)
{ {
@ -2860,22 +2869,22 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
double f = best.f; double f = best.f;
double pcz_cf = best.pcz; double pcz_cf = best.pcz;
bool valid = (picked.side_status != 2); bool valid = (picked.side_status != 2);
plotter->Fill1D("Benchmark_SX3_trueA1C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, "A1C1True_SX3"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, bTrue);
TVector3 vtx_cf = vertexFrom(sx3event.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_cf)); TVector3 vtx_cf = vertexFrom(sx3event.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_cf));
fillSuite(valid ? "trueA1C1_Cfrac" : "trueA1C1_Cfrac_invalid", pcz_cf, vtx_cf, "A1C1True_SX3"); fillSuite(valid ? "true" + A + "C1_Cfrac" : "true" + A + "C1_Cfrac_invalid", pcz_cf, vtx_cf, bTrue);
plotter->Fill1D("Benchmark_SX3_trueA1C1_cfrac", 220, -0.05, 1.05, cfrac, "A1C1True_SX3"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_cfrac", 220, -0.05, 1.05, cfrac, bTrue);
plotter->Fill2D("Benchmark_SX3_trueA1C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_true" + A + "C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, bTrue);
if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0) if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0)
plotter->Fill2D("Benchmark_SX3_trueA1C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_true" + A + "C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), bTrue);
plotter->Fill2D("Benchmark_SX3_trueA1C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_true" + A + "C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, bTrue);
plotter->Fill2D("Benchmark_SX3_trueA1C1_f_vs_cell", 7, 0, 7, 260, -1.5, 2.5, cell + 0.5, f, "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_true" + A + "C1_f_vs_cell", 7, 0, 7, 260, -1.5, 2.5, cell + 0.5, f, bTrue);
plotter->Fill1D("Benchmark_SX3_trueA1C1_f", 260, -1.5, 2.5, f, "A1C1True_SX3"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_f", 260, -1.5, 2.5, f, bTrue);
plotter->Fill1D("Benchmark_SX3_trueA1C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, "A1C1True_SX3"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, bTrue);
plotter->Fill1D("Benchmark_SX3_VertexZ_trueA1C1_Cfrac_status" + std::to_string(picked.side_status), plotter->Fill1D("Benchmark_SX3_VertexZ_true" + A + "C1_Cfrac_status" + std::to_string(picked.side_status),
800, -400, 400, vtx_cf.Z(), "A1C1True_SX3"); 800, -400, 400, vtx_cf.Z(), bTrue);
plotter->Fill1D("Benchmark_SX3_trueA1C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, fTrue);
int reason; int reason;
if (cell < 0 || cell > 6 || a1c1_k_cell[cell] <= 0.0) if (cell < 0 || cell > 6 || a1c1_k_cell[cell] <= 0.0)
reason = 5; reason = 5;
@ -2887,16 +2896,16 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
reason = 2; reason = 2;
else else
reason = 0; reason = 0;
plotter->Fill1D("Benchmark_SX3_trueA1C1_failreason", 6, 0, 6, reason + 0.5, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_failreason", 6, 0, 6, reason + 0.5, fTrue);
if (valid) if (valid)
plotter->Fill1D("Benchmark_SX3_trueA1C1_validreason", 3, 0, 3, reason + 0.5, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_validreason", 3, 0, 3, reason + 0.5, fTrue);
plotter->Fill1D("Benchmark_SX3_trueA1C1_band", 2, 0, 2, picked.sol.band + 0.5, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_band", 2, 0, 2, picked.sol.band + 0.5, fTrue);
if (valid) if (valid)
plotter->Fill1D("Benchmark_SX3_trueA1C1_band_valid", 2, 0, 2, picked.sol.band + 0.5, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_true" + A + "C1_band_valid", 2, 0, 2, picked.sol.band + 0.5, fTrue);
if (valid) if (valid)
{ {
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C1_Cfrac_minus_sx3pczguess_DIAG", 400, -100, 100, pcz_cf - pczguess, "Benchmark_SX3_trueA1C1"); plotter->Fill1D("Benchmark_SX3_PCZ_true" + A + "C1_Cfrac_minus_sx3pczguess_DIAG", 400, -100, 100, pcz_cf - pczguess, fTrue);
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C1_Cfrac_vs_sx3pczguess_DIAG", 400, -200, 200, 400, -200, 200, pczguess, pcz_cf, "Benchmark_SX3_trueA1C1"); plotter->Fill2D("Benchmark_SX3_PCZ_true" + A + "C1_Cfrac_vs_sx3pczguess_DIAG", 400, -200, 200, 400, -200, 200, pczguess, pcz_cf, fTrue);
} }
} }
@ -2905,8 +2914,8 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
TVector3 vtx0 = vertexFrom(sx3event.pos, pc); TVector3 vtx0 = vertexFrom(sx3event.pos, pc);
if (vtx0.Perp() <= 6.0 && vtx0.Z() >= z_entrance) if (vtx0.Perp() <= 6.0 && vtx0.Z() >= z_entrance)
{ {
fillSuite("A1C1asA1C0", pc.Z(), vtx0, "A1C1True_SX3"); fillSuite(A + "C1as" + A + "C0", pc.Z(), vtx0, bTrue);
plotter->Fill2D("Benchmark_SX3_PCZ_A1C1asA1C0_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pc.Z(), "A1C1True_SX3"); plotter->Fill2D("Benchmark_SX3_PCZ_" + A + "C1as" + A + "C0_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pc.Z(), bTrue);
} }
} }
} }
@ -2949,13 +2958,17 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
continue; continue;
double qqqTheta = (qqqevent.pos - beamAxisPoint(source_vertex)).Theta(); double qqqTheta = (qqqevent.pos - beamAxisPoint(source_vertex)).Theta();
double pcz_guess_37 = 37. / TMath::Tan(qqqTheta) + source_vertex; double pcz_guess_37 = 37. / TMath::Tan(qqqTheta) + source_vertex;
plotter->Fill1D("Benchmark_QQQ_VertexZ_trueA1C0", 800, -400, 400, vtx0.Z(), "A1C0True_QQQ"); // Split by anode-cluster wire count: 1 wire -> trueA1C0, 2 wires -> trueA2C0
plotter->Fill1D("Benchmark_QQQ_VertexZ_trueA1C0_Hybrid", 800, -400, 400, vtx1.Z(), "A1C0True_QQQ"); // (same pseudowire math; tag keeps the two topologies in separate histograms).
plotter->Fill1D("Benchmark_QQQ_VertexZ_trueA1C0_Hybrid_TC" + std::to_string(timecut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx1.Z(), "A1C0True_QQQ"); const std::string t0 = (aCl.size() == 2) ? "trueA2C0" : "trueA1C0";
plotter->Fill2D("Benchmark_QQQ_VertexXY_trueA1C0_Hybrid", 200, -100, 100, 200, -100, 100, vtx1.X(), vtx1.Y(), "A1C0True_QQQ"); const std::string b0 = (aCl.size() == 2) ? "A2C0True_QQQ" : "A1C0True_QQQ";
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C0_Hybrid", 600, -200, 200, pc_hybrid.Z(), "A1C0True_QQQ"); plotter->Fill1D("Benchmark_QQQ_VertexZ_" + t0, 800, -400, 400, vtx0.Z(), b0);
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C0_Hybrid_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_37, pc_hybrid.Z(), "A1C0True_QQQ"); plotter->Fill1D("Benchmark_QQQ_VertexZ_" + t0 + "_Hybrid", 800, -400, 400, vtx1.Z(), b0);
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C0_Hybrid_minus_qqqpczguess", 400, -100, 100, pc_hybrid.Z() - pcz_guess_37, "A1C0True_QQQ"); plotter->Fill1D("Benchmark_QQQ_VertexZ_" + t0 + "_Hybrid_TC" + std::to_string(timecut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx1.Z(), b0);
plotter->Fill2D("Benchmark_QQQ_VertexXY_" + t0 + "_Hybrid", 200, -100, 100, 200, -100, 100, vtx1.X(), vtx1.Y(), b0);
plotter->Fill1D("Benchmark_QQQ_PCZ_" + t0 + "_Hybrid", 600, -200, 200, pc_hybrid.Z(), b0);
plotter->Fill2D("Benchmark_QQQ_PCZ_" + t0 + "_Hybrid_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_37, pc_hybrid.Z(), b0);
plotter->Fill1D("Benchmark_QQQ_PCZ_" + t0 + "_Hybrid_minus_qqqpczguess", 400, -100, 100, pc_hybrid.Z() - pcz_guess_37, b0);
} }
} }
@ -3139,6 +3152,10 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
{ {
const auto &aCl = aClusters.front(); const auto &aCl = aClusters.front();
const auto &cCl = cClusters.front(); const auto &cCl = cClusters.front();
// Anode-wire-count topology prefix (see the SX3 twin above): "A1" for a
// 1-wire anode cluster, "A2" for a 2-wire one. Carried in every
// histogram name so a2 events book separately from a1.
const std::string A = (aCl.size() == 2) ? "A2" : "A1";
const std::string benchBranch = "Benchmark_QQQ"; const std::string benchBranch = "Benchmark_QQQ";
auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint) auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint)
@ -3233,48 +3250,49 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
if (phicut && timecut) if (phicut && timecut)
{ {
if (pcevent.multi1 == 1 && pcevent.multi2 == 2) if (pcevent.multi2 == 2)
{ {
fillSuite("A1C2", pcz_ref, vtx_ref, benchBranch); const std::string Esuf = (A == "A2") ? "_A2" : ""; // keep a1 energy-vs-cell names byte-identical
fillSuite(A + "C2", pcz_ref, vtx_ref, benchBranch);
{ {
double phi_deg = qqqevent.pos.Phi() * 180.0 / M_PI; double phi_deg = qqqevent.pos.Phi() * 180.0 / M_PI;
double vz_resid = vtx_ref.Z() - source_vertex; double vz_resid = vtx_ref.Z() - source_vertex;
plotter->Fill2D("Diag_QQQ_A1C2_vtxZ_resid_vs_phi", 180, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C2_vtxZ_resid_vs_phi", 180, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C2_vtxZ_resid_vs_phi", 90, -180, 180, 400, -100, 100, phi_deg, vz_resid, "Diag_XYoffset");
plotter->Fill2D("Diag_QQQ_A1C2_vtxXY", 200, -15, 15, 200, -15, 15, vtx_ref.X(), vtx_ref.Y(), "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C2_vtxXY", 200, -15, 15, 200, -15, 15, vtx_ref.X(), vtx_ref.Y(), "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset");
plotter->Fill2D("Diag_QQQ_A1C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C2_time_vs_phi", 2000, 0, 2000, 90, -180, 180, pcevent.Time1 * 1e-9, phi_deg, "Diag_XYoffset");
plotter->Fill2D("Diag_QQQ_A1C2_T_vs_vtxX", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.X(), "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C2_T_vs_vtxX", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.X(), "Diag_XYoffset");
plotter->Fill2D("Diag_QQQ_A1C2_T_vs_vtxY", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.Y(), "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C2_T_vs_vtxY", 2000, 0, 2000, 200, -15, 15, pcevent.Time1 * 1e-9, vtx_ref.Y(), "Diag_XYoffset");
} }
doA1C1("A1C1", qqqevent.pos, false); doA1C1(A + "C1", qqqevent.pos, false);
doAnodeOnly("A1C0", qqqevent.pos.Phi(), qqqevent.pos, false); doAnodeOnly(A + "C0", qqqevent.pos.Phi(), qqqevent.pos, false);
doA1C1("A1C1_Hyb", smeared_qqq_pos); doA1C1(A + "C1_Hyb", smeared_qqq_pos);
doAnodeOnly("A1C0_Hyb", smeared_phi, smeared_qqq_pos); doAnodeOnly(A + "C0_Hyb", smeared_phi, smeared_qqq_pos);
doA1C1Model("A1C1_Cfrac", qqqevent.pos); doA1C1Model(A + "C1_Cfrac", qqqevent.pos);
{ {
double pcz_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, qqqevent.pos.Phi()).Z(); double pcz_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, qqqevent.pos.Phi()).Z();
double theta_ref = (qqqevent.pos - beamAxisPoint(vtx_ref.Z())).Theta() * 180. / M_PI; double theta_ref = (qqqevent.pos - beamAxisPoint(vtx_ref.Z())).Theta() * 180. / M_PI;
plotter->Fill2D("Benchmark_QQQ_PCZ_A1C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_PCZ_" + A + "C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_QQQ_ref");
plotter->Fill2D("Benchmark_PCZ_A1C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_AnodeOnly"); plotter->Fill2D("Benchmark_PCZ_" + A + "C0_minus_ref_vs_theta", 180, 0, 180, 400, -200, 200, theta_ref, pcz_a1c0 - pcz_ref, "Benchmark_AnodeOnly");
double phi_deg_a = qqqevent.pos.Phi() * 180.0 / M_PI; double phi_deg_a = qqqevent.pos.Phi() * 180.0 / M_PI;
plotter->Fill2D("Diag_QQQ_A1C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset"); plotter->Fill2D("Diag_QQQ_" + A + "C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset");
plotter->Fill2D("Diag_Combined_A1C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset"); plotter->Fill2D("Diag_Combined_" + A + "C0_zresid_vs_phi", 90, -180, 180, 200, -100, 100, phi_deg_a, pcz_a1c0 - pcz_ref, "Diag_XYoffset");
} }
if (a1c1Good && cfrac >= 0.0) if (a1c1Good && cfrac >= 0.0)
{ {
plotter->Fill1D("Benchmark_QQQ_A1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_QQQ_ref"); plotter->Fill1D("Benchmark_QQQ_" + A + "C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_QQQ_ref");
plotter->Fill2D("Benchmark_QQQ_A1C1_cfrac_vs_ref", 400, -200, 200, 220, -0.05, 1.05, pcz_ref, cfrac, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfrac_vs_ref", 400, -200, 200, 220, -0.05, 1.05, pcz_ref, cfrac, "Benchmark_QQQ_ref");
plotter->Fill2D("Benchmark_QQQ_A1C1_cfrac_vs_qqqpczguess", 400, -200, 200, 220, -0.05, 1.05, pcz_guess_37, cfrac, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfrac_vs_qqqpczguess", 400, -200, 200, 220, -0.05, 1.05, pcz_guess_37, cfrac, "Benchmark_QQQ_ref");
static const double zg[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998}; static const double zg[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
double zp = xo_a1c1.Z(); double zp = xo_a1c1.Z();
auto fillCfracS = [&](const char *name, double truth) auto fillCfracS = [&](const std::string &name, double truth)
{ {
double sgn = (truth >= zp) ? 1.0 : -1.0; double sgn = (truth >= zp) ? 1.0 : -1.0;
double znb = (sgn > 0) ? 1.0e30 : -1.0e30; double znb = (sgn > 0) ? 1.0e30 : -1.0e30;
@ -3288,8 +3306,8 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
if (TMath::Abs(znb) < 1e8 && TMath::Abs(znb - zp) > 0.0) if (TMath::Abs(znb) < 1e8 && TMath::Abs(znb - zp) > 0.0)
plotter->Fill2D(name, 240, -1.2, 1.2, 220, -0.05, 1.05, (truth - zp) / TMath::Abs(znb - zp), cfrac, "Benchmark_QQQ_ref"); plotter->Fill2D(name, 240, -1.2, 1.2, 220, -0.05, 1.05, (truth - zp) / TMath::Abs(znb - zp), cfrac, "Benchmark_QQQ_ref");
}; };
fillCfracS("Benchmark_QQQ_A1C1_cfrac_vs_s", pcz_ref); fillCfracS("Benchmark_QQQ_" + A + "C1_cfrac_vs_s", pcz_ref);
fillCfracS("Benchmark_QQQ_A1C1_cfrac_vs_s_qqqpczguess", pcz_guess_37); fillCfracS("Benchmark_QQQ_" + A + "C1_cfrac_vs_s_qqqpczguess", pcz_guess_37);
for (int i = 0; i < 7; ++i) for (int i = 0; i < 7; ++i)
{ {
@ -3298,14 +3316,14 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
double zc = 0.5 * (zg[i] + zg[i + 1]); double zc = 0.5 * (zg[i] + zg[i + 1]);
double half = 0.5 * (zg[i] - zg[i + 1]); double half = 0.5 * (zg[i] - zg[i + 1]);
if (half > 0.0) if (half > 0.0)
plotter->Fill2D("Benchmark_QQQ_A1C1_cfrac_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, cfrac, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfrac_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, cfrac, "Benchmark_QQQ_ref");
break; break;
} }
} }
plotter->Fill2D("Benchmark_QQQ_A1C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "Benchmark_QQQ_ref");
if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0) if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0)
plotter->Fill2D("Benchmark_QQQ_A1C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "Benchmark_QQQ_ref");
{ {
A1C1PickedSol sm = a1c1_solve_pick(cfrac, xo_a1c1.Z(), qqqevent.pos, xo_a1c1.X(), xo_a1c1.Y(), A1C1PickedSol sm = a1c1_solve_pick(cfrac, xo_a1c1.Z(), qqqevent.pos, xo_a1c1.X(), xo_a1c1.Y(),
@ -3321,24 +3339,24 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
if (cell_truth >= 0) if (cell_truth >= 0)
{ {
bool wrong = (sm_cell != cell_truth); bool wrong = (sm_cell != cell_truth);
plotter->Fill2D("Benchmark_QQQ_A1C1_cellsel_confusion", 7, 0, 7, 7, 0, 7, cell_truth + 0.5, sm_cell + 0.5, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cellsel_confusion", 7, 0, 7, 7, 0, 7, cell_truth + 0.5, sm_cell + 0.5, "Benchmark_QQQ_ref");
plotter->Fill1D("Benchmark_QQQ_A1C1_cellsel_misclass", 2, 0, 2, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref"); plotter->Fill1D("Benchmark_QQQ_" + A + "C1_cellsel_misclass", 2, 0, 2, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref");
plotter->Fill2D("Benchmark_QQQ_A1C1_cellsel_misclass_vs_cell", 7, 0, 7, 2, 0, 2, cell_truth + 0.5, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cellsel_misclass_vs_cell", 7, 0, 7, 2, 0, 2, cell_truth + 0.5, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref");
double zc = 0.5 * (a1c1_zg[cell_truth] + a1c1_zg[cell_truth + 1]); double zc = 0.5 * (a1c1_zg[cell_truth] + a1c1_zg[cell_truth + 1]);
double half = 0.5 * (a1c1_zg[cell_truth] - a1c1_zg[cell_truth + 1]); double half = 0.5 * (a1c1_zg[cell_truth] - a1c1_zg[cell_truth + 1]);
plotter->Fill2D("AnodeEnergy_vs_CellQQQ", 120, 0, 1.2, 800, 0, 40000, 1 - TMath::Abs(pcz_ref - zc) / half, pcevent.Energy1); plotter->Fill2D("AnodeEnergy_vs_CellQQQ" + Esuf, 120, 0, 1.2, 800, 0, 40000, 1 - TMath::Abs(pcz_ref - zc) / half, pcevent.Energy1);
plotter->Fill2D("CathodeEnergy_vs_CellQQQ", 120, 0, 1.2, 800, 0, 40000, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2); plotter->Fill2D("CathodeEnergy_vs_CellQQQ" + Esuf, 120, 0, 1.2, 800, 0, 40000, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2);
plotter->Fill2D("FracEnergy_vs_CellQQQ", 120, 0, 1.2, 1200, 0, 20, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2 / pcevent.Energy1); plotter->Fill2D("FracEnergy_vs_CellQQQ" + Esuf, 120, 0, 1.2, 1200, 0, 20, TMath::Abs(pcz_ref - zc) / half, pcevent.Energy2 / pcevent.Energy1);
if (half > 0.0) if (half > 0.0)
{ {
plotter->Fill2D("Benchmark_QQQ_A1C1_cellsel_misclass_vs_fold", 120, 0, 1.2, 2, 0, 2, TMath::Abs(pcz_ref - zc) / half, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cellsel_misclass_vs_fold", 120, 0, 1.2, 2, 0, 2, TMath::Abs(pcz_ref - zc) / half, wrong ? 1.0 : 0.0, "Benchmark_QQQ_ref");
plotter->Fill2D("Benchmark_QQQ_A1C1_cfracUsed_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, sm.sol.cfrac_used, "Benchmark_QQQ_ref"); plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfracUsed_vs_fold", 120, 0, 1.2, 220, -0.05, 1.05, TMath::Abs(pcz_ref - zc) / half, sm.sol.cfrac_used, "Benchmark_QQQ_ref");
if (aSumE_bm > 0.0) if (aSumE_bm > 0.0)
{ {
plotter->Fill2D("Benchmark_QQQ_A1C1_cfracUsed_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, plotter->Fill2D("Benchmark_QQQ_" + A + "C1_cfracUsed_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05,
aSumE_bm, sm.sol.cfrac_used, "Benchmark_QQQ_ref"); aSumE_bm, sm.sol.cfrac_used, "Benchmark_QQQ_ref");
} }
} }
@ -3348,13 +3366,15 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
} }
} }
else if (pcevent.multi1 >= 1 && pcevent.multi2 == 1 && a1c1Good) else if (pcevent.multi2 == 1 && a1c1Good)
{ {
const std::string bTrue = A + "C1True_QQQ"; // fillSuite folder (A1C1True_QQQ / A2C1True_QQQ)
const std::string fTrue = "Benchmark_QQQ_true" + A + "C1"; // diagnostics folder
double pcz_raw = xo_a1c1.Z(); double pcz_raw = xo_a1c1.Z();
TVector3 vtx_raw = vertexFrom(qqqevent.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw)); TVector3 vtx_raw = vertexFrom(qqqevent.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw));
fillSuite("trueA1C1", pcz_raw, vtx_raw, "A1C1True_QQQ"); fillSuite("true" + A + "C1", pcz_raw, vtx_raw, bTrue);
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C1_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_raw, "Benchmark_QQQ_trueA1C1"); plotter->Fill2D("Benchmark_QQQ_PCZ_true" + A + "C1_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_raw, fTrue);
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C1_minus_qqqpczguess", 400, -100, 100, pcz_raw - pcz_guess_int, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_PCZ_true" + A + "C1_minus_qqqpczguess", 400, -100, 100, pcz_raw - pcz_guess_int, fTrue);
if (cfrac >= 0.0) if (cfrac >= 0.0)
{ {
@ -3366,18 +3386,18 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
double pcz_cf = best.pcz; double pcz_cf = best.pcz;
bool valid = (picked.side_status != 2); bool valid = (picked.side_status != 2);
bool cfrac_valid = (valid && best.inband && best.pitchok); bool cfrac_valid = (valid && best.inband && best.pitchok);
plotter->Fill1D("Benchmark_QQQ_trueA1C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_sideStatus", 4, -1, 3, picked.side_status + 0.5, fTrue);
TVector3 vtx_cf = vertexFrom(qqqevent.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_cf)); TVector3 vtx_cf = vertexFrom(qqqevent.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_cf));
fillSuite(valid ? "trueA1C1_Cfrac" : "trueA1C1_Cfrac_invalid", pcz_cf, vtx_cf, "A1C1True_QQQ"); fillSuite(valid ? "true" + A + "C1_Cfrac" : "true" + A + "C1_Cfrac_invalid", pcz_cf, vtx_cf, bTrue);
plotter->Fill1D("Benchmark_QQQ_trueA1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_cfrac", 220, -0.05, 1.05, cfrac, fTrue);
plotter->Fill2D("Benchmark_QQQ_trueA1C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, "Benchmark_QQQ_trueA1C1"); plotter->Fill2D("Benchmark_QQQ_true" + A + "C1_cfrac_vs_anodeE", 400, 0, 40000, 220, -0.05, 1.05, aSumE_bm, cfrac, fTrue);
if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0) if (aSumE_bm > 0.0 && cfrac > 0.0 && cfrac < 1.0)
plotter->Fill2D("Benchmark_QQQ_trueA1C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0, plotter->Fill2D("Benchmark_QQQ_true" + A + "C1_r_vs_invAnodeE", 200, 0, 0.0004, 200, 0, 2.0,
1.0 / aSumE_bm, cfrac / (1.0 - cfrac), "Benchmark_QQQ_trueA1C1"); 1.0 / aSumE_bm, cfrac / (1.0 - cfrac), fTrue);
plotter->Fill2D("Benchmark_QQQ_trueA1C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, "Benchmark_QQQ_trueA1C1"); plotter->Fill2D("Benchmark_QQQ_true" + A + "C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, fTrue);
plotter->Fill1D("Benchmark_QQQ_trueA1C1_f", 260, -1.5, 2.5, f, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_f", 260, -1.5, 2.5, f, fTrue);
plotter->Fill1D("Benchmark_QQQ_trueA1C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, fTrue);
int reason; int reason;
if (cell < 0 || cell > 6 || a1c1_k_cell[cell] <= 0.0) if (cell < 0 || cell > 6 || a1c1_k_cell[cell] <= 0.0)
reason = 5; reason = 5;
@ -3389,16 +3409,16 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
reason = 2; reason = 2;
else else
reason = 0; reason = 0;
plotter->Fill1D("Benchmark_QQQ_trueA1C1_failreason", 6, 0, 6, reason + 0.5, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_failreason", 6, 0, 6, reason + 0.5, fTrue);
if (valid) if (valid)
plotter->Fill1D("Benchmark_QQQ_trueA1C1_validreason", 3, 0, 3, reason + 0.5, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_validreason", 3, 0, 3, reason + 0.5, fTrue);
plotter->Fill1D("Benchmark_QQQ_trueA1C1_band", 2, 0, 2, picked.sol.band + 0.5, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_band", 2, 0, 2, picked.sol.band + 0.5, fTrue);
if (valid) if (valid)
plotter->Fill1D("Benchmark_QQQ_trueA1C1_band_valid", 2, 0, 2, picked.sol.band + 0.5, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_true" + A + "C1_band_valid", 2, 0, 2, picked.sol.band + 0.5, fTrue);
if (valid) if (valid)
{ {
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C1_Cfrac_minus_qqqpczguess_DIAG", 400, -100, 100, pcz_cf - pcz_guess_int, "Benchmark_QQQ_trueA1C1"); plotter->Fill1D("Benchmark_QQQ_PCZ_true" + A + "C1_Cfrac_minus_qqqpczguess_DIAG", 400, -100, 100, pcz_cf - pcz_guess_int, fTrue);
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C1_Cfrac_vs_qqqpczguess_DIAG", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_cf, "Benchmark_QQQ_trueA1C1"); plotter->Fill2D("Benchmark_QQQ_PCZ_true" + A + "C1_Cfrac_vs_qqqpczguess_DIAG", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_cf, fTrue);
} }
} }
@ -3407,8 +3427,8 @@ void PCQQQClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
TVector3 vtx0 = vertexFrom(qqqevent.pos, pc); TVector3 vtx0 = vertexFrom(qqqevent.pos, pc);
if (vtx0.Perp() <= 6.0 && vtx0.Z() >= z_entrance) if (vtx0.Perp() <= 6.0 && vtx0.Z() >= z_entrance)
{ {
fillSuite("A1C1asA1C0", pc.Z(), vtx0, "A1C1True_QQQ"); fillSuite(A + "C1as" + A + "C0", pc.Z(), vtx0, bTrue);
plotter->Fill2D("Benchmark_QQQ_PCZ_A1C1asA1C0_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pc.Z(), "A1C1True_QQQ"); plotter->Fill2D("Benchmark_QQQ_PCZ_" + A + "C1as" + A + "C0_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pc.Z(), bTrue);
} }
} }
} }
@ -3732,9 +3752,14 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill2D(rx + "_EKin_vs_ESi" + ejtag + t + sfx, 400, 0, initial_energy * 1.5, 800, 0, 10, ebeam_kin, sievent.Energy1, pmlabel); plotter->Fill2D(rx + "_EKin_vs_ESi" + ejtag + t + sfx, 400, 0, initial_energy * 1.5, 800, 0, 10, ebeam_kin, sievent.Energy1, pmlabel);
} }
}; };
std::string topo1 = (multi2 == 2) ? "a1c2fix" : (multi2 == 1) ? "a1c1" std::string topo1;
: (multi1 == 2 ? "a2c0" : "a1c0"); if (multi2 == 2)
std::string methodGroup = (multi2 == 1 || multi2 == 2) ? "a1c1c2" : ""; topo1 = (multi1 == 2) ? "a2c2" : "a1c2fix";
else if (multi2 == 1)
topo1 = (multi1 == 2) ? "a2c1" : "a1c1";
else
topo1 = (multi1 == 2) ? "a2c0" : "a1c0";
std::string methodGroup = (multi2 == 1 || multi2 == 2) ? (multi1 == 2 ? "a2c1c2" : "a1c1c2") : "";
forEachTier(topo1, "", methodGroup, plot_with_tag); forEachTier(topo1, "", methodGroup, plot_with_tag);
// Gas segmentation validation (dEgas family), uniform for every // Gas segmentation validation (dEgas family), uniform for every
@ -3769,10 +3794,13 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
fillHypothesis(true); fillHypothesis(true);
}; };
// --- a1c1/a1c2, from PC_Events (unchanged from before) --- // --- a1c1/a1c2 (multi1==1) and a2c1/a2c2 (multi1==2), from PC_Events. The
// 2-anode-wire topologies reuse the same cathode Z-math as their
// single-anode twins (a1c2_zfix for c2, dither for c1); they differ only in
// the topology tag reconstructAndFill books them under. ---
for (const auto &pcevent : PC_Events) for (const auto &pcevent : PC_Events)
{ {
if (!(pcevent.multi1 == 1 && (pcevent.multi2 == 1 || pcevent.multi2 == 2))) if (!((pcevent.multi1 == 1 || pcevent.multi1 == 2) && (pcevent.multi2 == 1 || pcevent.multi2 == 2)))
continue; continue;
bool phicut = TMath::Abs(sievent.pos.DeltaPhi(pcevent.pos)) <= phi_win; bool phicut = TMath::Abs(sievent.pos.DeltaPhi(pcevent.pos)) <= phi_win;
@ -3805,8 +3833,9 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
} }
// --- a1c1 charge-division diagnostics -- independent of particle ID, // --- a1c1 charge-division diagnostics -- independent of particle ID,
// filled for every a1c1 hit (matches reaction_ax_core's a1c1_cfrac). --- // filled for every a1c1 hit (matches reaction_ax_core's a1c1_cfrac).
if (pcevent.multi2 == 1) // Single-anode-wire only: a2c1 must not merge into these a1c1 histograms. ---
if (pcevent.multi1 == 1 && pcevent.multi2 == 1)
{ {
double ac0 = pcevent.Energy1 + pcevent.Energy2; double ac0 = pcevent.Energy1 + pcevent.Energy2;
double cfrac0 = (ac0 > 0.0) ? pcevent.Energy2 / ac0 : -1.0; double cfrac0 = (ac0 > 0.0) ? pcevent.Energy2 / ac0 : -1.0;
@ -3819,8 +3848,8 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
// --- a1c1 Z-reconstruction-method comparison (dither vs. cfrac-pick), // --- a1c1 Z-reconstruction-method comparison (dither vs. cfrac-pick),
// alpha-tagged events only -- unambiguous 2-body kinematics without // alpha-tagged events only -- unambiguous 2-body kinematics without
// needing the Z itself to already be right. --- // needing the Z itself to already be right. Single-anode-wire only. ---
if (pcevent.multi2 == 1 && pid == SiPcPid::kAlpha) if (pcevent.multi1 == 1 && pcevent.multi2 == 1 && pid == SiPcPid::kAlpha)
{ {
const std::string wcat = a1c1_missing_neighbor(pcevent.Anodech, pcevent.Cathodech) ? "_missingw" : "_true1w"; const std::string wcat = a1c1_missing_neighbor(pcevent.Anodech, pcevent.Cathodech) ? "_missingw" : "_true1w";
const std::string cmplbl = misclabel + "_a1c1cmp"; const std::string cmplbl = misclabel + "_a1c1cmp";
@ -4100,13 +4129,7 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
if (beam_energy_at_vertex > 4.0 && beam_energy_at_vertex <= 12.0) if (beam_energy_at_vertex > 4.0 && beam_energy_at_vertex <= 12.0)
{ {
const std::string qtag = "_q" + std::to_string(sievent.ch1 / 16);
plotter->Fill2D(rx + "_EKin-ETrack2235keV_vs_phi" + ejtag + t + sfx, 45, -180, 180, 600, -20, 40, phi * 180 / M_PI, ebeam_kin_2235keV - beam_energy_at_vertex, pmlabel); plotter->Fill2D(rx + "_EKin-ETrack2235keV_vs_phi" + ejtag + t + sfx, 45, -180, 180, 600, -20, 40, phi * 180 / M_PI, ebeam_kin_2235keV - beam_energy_at_vertex, pmlabel);
plotter->Fill2D(rx + "_2235keV_Ex_vs_X" + qtag + ejtag + sfx, 100, -100, 120, 800, -6, 15, sievent.pos.X(), Ex, "qqq" + pmlabel);
plotter->Fill2D(rx + "_2235keV_Ex_vs_Y" + qtag + ejtag + sfx, 100, -100, 120, 800, -6, 15, sievent.pos.Y(), Ex, "qqq" + pmlabel);
plotter->Fill2D(rx + "_2235keV_Ex_vs_phi" + qtag + ejtag + sfx, 45, -180, 180, 600, -6, 15, phi * 180 / M_PI, Ex, "qqq" + pmlabel);
plotter->Fill2D(rx + "_2235keV_Ex_vs_rho" + qtag + ejtag + sfx, 60, 0, 120, 600, -6, 15, sievent.pos.Perp(), Ex, "qqq" + pmlabel);
plotter->Fill1D(rx + "_2235keV_Ex_from" + qtag + ejtag + sfx, 800, -10, 10, Ex, "qqq" + pmlabel);
} }
if (ejtag == "_p" && rx == "m27Alax") if (ejtag == "_p" && rx == "m27Alax")
{ {
@ -4162,13 +4185,6 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill2D(rx + "_Ex_vs_dT" + ejtag + sfx, 500, -2000, 2000, 600, -10, 20, (sievent.Time1 - pcevent.Time1), Ex, pmlabel); plotter->Fill2D(rx + "_Ex_vs_dT" + ejtag + sfx, 500, -2000, 2000, 600, -10, 20, (sievent.Time1 - pcevent.Time1), Ex, pmlabel);
plotter->Fill2D(rx + "_dEgasCalib_vs_dT" + ejtag + sfx, 500, -2000, 2000, 800, 0, 0.6, (sievent.Time1 - pcevent.Time1), anodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalib_vs_dT" + ejtag + sfx, 500, -2000, 2000, 800, 0, 0.6, (sievent.Time1 - pcevent.Time1), anodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_dEgasCalibCathode_vs_dT" + ejtag + sfx, 500, -2000, 2000, 800, 0, 0.6, (sievent.Time1 - pcevent.Time1), cathodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalibCathode_vs_dT" + ejtag + sfx, 500, -2000, 2000, 800, 0, 0.6, (sievent.Time1 - pcevent.Time1), cathodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_AnodeID" + ejtag + sfx, 24, 0.5, 25.5, 600, -6, 15, pcevent.Anodech, Ex, "qqq" + pmlabel);
plotter->Fill2D(rx + "_Ex_vs_CathodeID" + ejtag + sfx, 24, 0.5, 25.5, 600, -6, 15, pcevent.Cathodech, Ex, "qqq" + pmlabel);
if (beam_energy_at_vertex > 4.0 && beam_energy_at_vertex <= 12.0)
{
plotter->Fill2D(rx + "_2235keV_Ex_vs_AnodeID" + ejtag + sfx, 24, 0.5, 25.5, 600, -6, 15, pcevent.Anodech, Ex, "qqq" + pmlabel);
plotter->Fill2D(rx + "_2235keV_Ex_vs_CathodeID" + ejtag + sfx, 24, 0.5, 25.5, 600, -6, 15, pcevent.Cathodech, Ex, "qqq" + pmlabel);
}
} }
if (dt_rf_mcp > -900000000) if (dt_rf_mcp > -900000000)
@ -4222,7 +4238,11 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
for (const auto &pcevent : PC_Events) for (const auto &pcevent : PC_Events)
{ {
if (!(pcevent.multi1 == 1 && (pcevent.multi2 == 1 || pcevent.multi2 == 2))) // multi1==1 -> a1c1/a1c2; multi1==2 -> a2c1/a2c2. The 2-anode-wire
// topologies reuse the same cathode Z-math (a1c1_cfrac_pcz / a1c2_zfix)
// as their single-anode twins, applied to the same crossover Z -- they
// differ only in the topology tag they're booked under.
if (!((pcevent.multi1 == 1 || pcevent.multi1 == 2) && (pcevent.multi2 == 1 || pcevent.multi2 == 2)))
continue; continue;
// phi + time gate, matching protonAlphaElastic_core's equivalent a1c1/a1c2 // phi + time gate, matching protonAlphaElastic_core's equivalent a1c1/a1c2
// dispatch loop -- this branch previously gated on phi only. // dispatch loop -- this branch previously gated on phi only.
@ -4237,11 +4257,19 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
? pcEnergySlope[24 + pcevent.Cathodech] * pcevent.Energy2 ? pcEnergySlope[24 + pcevent.Cathodech] * pcevent.Energy2
: -1.0; : -1.0;
if (pcevent.multi2 == 1) // A1C1 const bool isA2 = (pcevent.multi1 == 2);
const std::string mg = isA2 ? "a2c1c2" : "a1c1c2";
if (pcevent.multi2 == 1) // A1C1 / A2C1
{ {
bool a1c1_inband = false; bool a1c1_inband = false;
double pcz_fix = a1c1_cfrac_pcz(pcevent, sievent.pos, a1c1_inband); double pcz_fix = a1c1_cfrac_pcz(pcevent, sievent.pos, a1c1_inband);
// Charge-fraction diagnostics are the single-anode-wire (a1c1) model's;
// keep them booked as a1c1 only, so a2c1 hits don't silently merge into
// the a1c1_cfrac histograms via HistPlotter's name-only keying.
if (!isA2)
{
double ac = pcevent.Energy1 + pcevent.Energy2; double ac = pcevent.Energy1 + pcevent.Energy2;
double cfrac = (ac > 0.0) ? pcevent.Energy2 / ac : -1.0; double cfrac = (ac > 0.0) ? pcevent.Energy2 / ac : -1.0;
if (cfrac >= 0.0) if (cfrac >= 0.0)
@ -4251,15 +4279,18 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill2D(rx + "_a1c1_cfrac_vs_anodeE" + sfx, 400, 0, 40000, 220, -0.05, 1.05, pcevent.Energy1, cfrac, pmlabel); plotter->Fill2D(rx + "_a1c1_cfrac_vs_anodeE" + sfx, 400, 0, 40000, 220, -0.05, 1.05, pcevent.Energy1, cfrac, pmlabel);
plotter->Fill1D(rx + "_a1c1_cfrac_inband" + sfx, 220, -0.05, 1.05, a1c1_inband ? cfrac : -1.0, pmlabel); plotter->Fill1D(rx + "_a1c1_cfrac_inband" + sfx, 220, -0.05, 1.05, a1c1_inband ? cfrac : -1.0, pmlabel);
} }
reconstructAndFill(pcz_fix, pcevent.pos, pcevent.Energy1, pcevent.Energy2, anodeE_MeV, cathodeE_MeV,
"a1c1", a1c1_inband ? "a1c1_inband" : "", pcevent.Anodech, "a1c1c2");
} }
else // A1C2 (multi2 == 2)
std::string t1 = isA2 ? "a2c1" : "a1c1";
std::string t2 = a1c1_inband ? (isA2 ? "a2c1_inband" : "a1c1_inband") : "";
reconstructAndFill(pcz_fix, pcevent.pos, pcevent.Energy1, pcevent.Energy2, anodeE_MeV, cathodeE_MeV,
t1, t2, pcevent.Anodech, mg);
}
else // A1C2 / A2C2 (multi2 == 2)
{ {
double pcz_fix = a1c2_zfix(pcevent.pos.Z()); double pcz_fix = a1c2_zfix(pcevent.pos.Z());
reconstructAndFill(pcz_fix, pcevent.pos, pcevent.Energy1, pcevent.Energy2, anodeE_MeV, cathodeE_MeV, reconstructAndFill(pcz_fix, pcevent.pos, pcevent.Energy1, pcevent.Energy2, anodeE_MeV, cathodeE_MeV,
"a1c2fix", "", pcevent.Anodech, "a1c1c2"); isA2 ? "a2c2" : "a1c2fix", "", pcevent.Anodech, mg);
} }
} }

View File

@ -9,7 +9,7 @@ export CO2percent=3
export pressure_in_torr=250 export pressure_in_torr=250
export CATHODE_GAIN=3.0 export CATHODE_GAIN=3.0
export source_vertex=-200.0 export source_vertex=-200.0
export DEDX_SCALE=0.89 export DEDX_SCALE=0.87
# export BEAM_AXIS_Z0=-200 # export BEAM_AXIS_Z0=-200
export BEAM_AXIS_X=0.0 export BEAM_AXIS_X=0.0
@ -42,12 +42,12 @@ process_run() {
export -f process_run export -f process_run
for x in -5 -3 0 # for x in -5 -3
do # do
BEAM_AXIS_X=$x # BEAM_AXIS_X=$x
for y in 0 3 5 7 # for y in 3 5 7
do # do
BEAM_AXIS_Y=$y # BEAM_AXIS_Y=$y
# Define and create a clean directory name BEFORE running parallel tasks # Define and create a clean directory name BEFORE running parallel tasks
CURRENT_OUT_DIR="Output_27Al" CURRENT_OUT_DIR="Output_27Al"
@ -74,8 +74,8 @@ do
echo "Merging files..." echo "Merging files..."
# Fixed: Safely merge using the clean directory variable (added -f to overwrite if re-running) # Fixed: Safely merge using the clean directory variable (added -f to overwrite if re-running)
hadd -k -f -j 4 "${CURRENT_OUT_DIR}/Output_27Al.root" "${CURRENT_OUT_DIR}/results_run"*.root hadd -k -f -j 4 "${CURRENT_OUT_DIR}/Output_27Al.root" "${CURRENT_OUT_DIR}/results_run"*.root
done # done
done # done
# Cleanup # Cleanup
unset DATASET unset DATASET

View File

@ -34,7 +34,7 @@ export pressure_in_torr=250
export CO2percent=3 export CO2percent=3
# --- Block 1: 27Al Source Runs No Gas (1-8) --- # --- Block 1: 27Al Source Runs No Gas (1-8) ---
if [[ 1 -eq 1 ]]; then if [[ 1 -eq 0 ]]; then
export DATASET="27Al" export DATASET="27Al"
export PREFIX="Run_" export PREFIX="Run_"
export OUT_DIR="Output_av" export OUT_DIR="Output_av"
@ -56,7 +56,7 @@ if [[ 1 -eq 0 ]]; then
fi fi
# --- Block 3: 27Al Alpha+Gas Runs (9, 12) --- # --- Block 3: 27Al Alpha+Gas Runs (9, 12) ---
if [[ 1 -eq 0 ]]; then if [[ 1 -eq 1 ]]; then
export DATASET="27Al" export DATASET="27Al"
export PREFIX="Run_" export PREFIX="Run_"
export OUT_DIR="Output_a" export OUT_DIR="Output_a"
@ -64,7 +64,7 @@ if [[ 1 -eq 0 ]]; then
export pressure_in_torr=350 export pressure_in_torr=350
rm -f ${OUT_DIR}/all.root rm -f ${OUT_DIR}/all.root
echo "Processing 27Al alpha+gas runs..." echo "Processing 27Al alpha+gas runs..."
# export source_vertex=-5.36; export timecut_low=12.0; export timecut_high=119.0; process_run 9 "$slope" export source_vertex=-5.36; export timecut_low=12.0; export timecut_high=119.0; process_run 9 "$slope"
unset timecut_high unset timecut_high
export source_vertex=53.44; export timecut_low=400.0; process_run 12 "$slope" export source_vertex=53.44; export timecut_low=400.0; process_run 12 "$slope"
unset Gain unset Gain
@ -75,7 +75,7 @@ if [[ 1 -eq 0 ]]; then
fi fi
# --- Block 4: 17F Alpha+Gas Runs (18-21) --- # --- Block 4: 17F Alpha+Gas Runs (18-21) ---
if [[ 1 -eq 0 ]]; then if [[ 1 -eq 1 ]]; then
export DATASET="17F" export DATASET="17F"
export PREFIX="SourceRun_" export PREFIX="SourceRun_"
export OUT_DIR="Output_a" export OUT_DIR="Output_a"
@ -87,7 +87,7 @@ if [[ 1 -eq 0 ]]; then
export source_vertex=-24.96; process_run 20 export source_vertex=-24.96; process_run 20
export source_vertex=-73.96; process_run 21 export source_vertex=-73.96; process_run 21
hadd -j 4 -k ${OUT_DIR}/all.root ${OUT_DIR}/results_run*.root hadd -j 4 -k ${OUT_DIR}/all.root ${OUT_DIR}/results_run*.root
# exit exit
fi fi
# --- Block 5: 27Al Protons+Gas Runs (15, 17-22) --- # --- Block 5: 27Al Protons+Gas Runs (15, 17-22) ---
@ -111,7 +111,7 @@ if [[ 1 -eq 1 ]]; then
fi fi
# --- Block 6: 17F Proton Data --- # --- Block 6: 17F Proton Data ---
if [[ 1 -eq 0 ]]; then if [[ 1 -eq 1 ]]; then
export DATASET="17F" export DATASET="17F"
export PREFIX="ProtonRun_" export PREFIX="ProtonRun_"
export OUT_DIR="Output_p" export OUT_DIR="Output_p"

217
scratch/RunwiseTrend.C Normal file
View File

@ -0,0 +1,217 @@
// =============================================================================
// RunwiseTrend.C
//
// Track any TrackRecon.C histogram-derived quantity as a function of run
// number, to separate a DRIFTING condition (gas pressure, beam energy, beam
// steering, detector gain) from a FIXED analysis systematic.
//
// No reprocessing needed. run_27Al.sh / run_17F.sh already write one file per
// run -- "<OUT_DIR>/results_run###.root" -- and only hadd them afterwards, so
// the per-run files are sitting next to the merged one. (Note they are wiped
// by the "rm -f ${OUT_DIR}/*.root" at the top of the next run, so they reflect
// the most recent processing pass only.)
//
// Not to be confused with RunTimeSummary.C, which reads the *mapped* files for
// a single raw timing histogram. This one reads TrackRecon output.
//
// Usage:
// // does the Ex-vs-vertexZ slope drift across the experiment?
// root -l -b -q 'scratch/RunwiseTrend.C("Output_27Al",
// "m27Alax_VertexReconZ_vs_Ex_p_a1c2fix_qqq","slope",-300,0)'
//
// // mean Ex per run
// root -l -b -q 'scratch/RunwiseTrend.C("Output_27Al",
// "m27Alax_Ex_vs_theta_p_a1c2fix_qqq","mean")'
//
// // beam position per run, from one crossing plane
// root -l -b -q 'scratch/RunwiseTrend.C("Output_27Al",
// "beamAxis_all_crossY_z007","mean")'
//
// modes: "mean" <y> of a TH2 (or <x> of a TH1), error on the mean
// "slope" ProfileX of a TH2, fitted pol1 over [xLo,xHi] -- the slope
// "rms" spread rather than centre
// "entries" statistics per run, as a sanity/normalisation check
//
// The headline output is the fit to a CONSTANT. chi2/ndf near 1 means the
// quantity is stable run to run and any effect is a fixed systematic;
// chi2/ndf >> 1 means something genuinely changed during the experiment.
// Compare the scatter of the points against the typical error bar -- that
// ratio is the same "is this real" question the beam-axis plane medians pose,
// and it is answered the same way.
// =============================================================================
#include <TFile.h>
#include <TH1.h>
#include <TH2.h>
#include <TProfile.h>
#include <TF1.h>
#include <TGraphErrors.h>
#include <TCanvas.h>
#include <TKey.h>
#include <TDirectory.h>
#include <TSystem.h>
#include <TSystemDirectory.h>
#include <TSystemFile.h>
#include <TList.h>
#include <TString.h>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
#include <cctype>
// HistPlotter buries objects in per-tier folders, so search recursively.
static TH1 *findHist(TDirectory *d, const TString &name)
{
if (TObject *o = d->Get(name.Data()))
if (o->InheritsFrom(TH1::Class()))
return (TH1 *)o;
TIter next(d->GetListOfKeys());
while (TKey *k = (TKey *)next())
{
TObject *o = k->ReadObj();
if (o && o->InheritsFrom(TDirectory::Class()))
if (TH1 *h = findHist((TDirectory *)o, name))
return h;
}
return nullptr;
}
// results_run024.root -> 24 ; -1 if the name does not match
static int runNumberOf(const TString &fname)
{
Ssiz_t i = fname.Index("results_run");
if (i == kNPOS)
return -1;
TString digits;
for (Ssiz_t j = i + 11; j < fname.Length() && isdigit(fname[j]); ++j)
digits += fname[j];
return digits.Length() ? digits.Atoi() : -1;
}
void RunwiseTrend(const char *dir,
const char *histName,
const char *mode = "mean",
double xLo = 0.0, double xHi = 0.0,
double minEntries = 200.0,
const char *outPng = "")
{
TString m(mode);
// --- collect and sort the per-run files ---
std::vector<std::pair<int, TString>> files;
TSystemDirectory sd("sd", dir);
if (TList *l = sd.GetListOfFiles())
{
TIter next(l);
while (TSystemFile *sf = (TSystemFile *)next())
{
TString fn = sf->GetName();
if (sf->IsDirectory() || !fn.EndsWith(".root"))
continue;
int r = runNumberOf(fn);
if (r >= 0)
files.push_back({r, TString(dir) + "/" + fn});
}
}
std::sort(files.begin(), files.end());
if (files.empty())
{
std::cerr << "ERROR: no results_run*.root under " << dir << "\n"
<< " (the next run_*.sh wipes them -- they only exist for\n"
<< " the most recent processing pass)\n";
return;
}
std::cout << "\n=========== " << histName << " [" << m << "] ===========\n"
<< dir << " " << files.size() << " runs\n\n"
<< " run value +/- N\n";
TGraphErrors *g = new TGraphErrors();
int np = 0;
std::vector<double> vals, errs;
for (auto &fr : files)
{
TFile *f = TFile::Open(fr.second, "READ");
if (!f || f->IsZombie())
continue;
TH1 *h = findHist(f, histName);
if (!h) { f->Close(); continue; }
double v = 0.0, e = 0.0, n = h->GetEntries();
bool ok = (n >= minEntries);
if (ok && m == "entries") { v = n; e = std::sqrt(n); }
else if (ok && m == "slope")
{
TH2 *h2 = dynamic_cast<TH2 *>(h);
if (!h2) { std::cerr << " (slope needs a TH2)\n"; f->Close(); return; }
TProfile *p = h2->ProfileX(Form("p_%d", fr.first));
p->SetDirectory(0);
TF1 fit("fit", "pol1", (xLo == xHi ? p->GetXaxis()->GetXmin() : xLo),
(xLo == xHi ? p->GetXaxis()->GetXmax() : xHi));
if (p->Fit(&fit, "QNR") == 0) { v = fit.GetParameter(1); e = fit.GetParError(1); }
else ok = false;
delete p;
}
else if (ok)
{
TH2 *h2 = dynamic_cast<TH2 *>(h);
TH1 *proj = h2 ? h2->ProjectionY(Form("py_%d", fr.first)) : h;
if (m == "rms") { v = proj->GetRMS(); e = proj->GetRMSError(); }
else { v = proj->GetMean(); e = proj->GetMeanError(); }
if (h2) delete proj;
}
if (ok)
{
printf(" %4d %10.5f %9.5f %9.0f\n", fr.first, v, e, n);
g->SetPoint(np, fr.first, v);
g->SetPointError(np, 0.0, e);
vals.push_back(v); errs.push_back(e);
++np;
}
else
printf(" %4d (skipped -- %.0f entries)\n", fr.first, n);
f->Close();
}
if (np < 2) { std::cout << "\nonly " << np << " usable runs.\n"; return; }
// --- is the run-to-run scatter real, or just statistics? ---
TF1 c("c", "pol0");
g->Fit(&c, "QN");
double mean = c.GetParameter(0), emean = c.GetParError(0);
double chi2 = c.GetChisquare();
int ndf = c.GetNDF();
double sum = 0, sum2 = 0, esum = 0;
for (int i = 0; i < np; ++i) { sum += vals[i]; sum2 += vals[i] * vals[i]; esum += errs[i]; }
double rms = std::sqrt(std::max(0.0, sum2 / np - (sum / np) * (sum / np)));
double etyp = esum / np;
std::cout << "\n constant fit : " << mean << " +/- " << emean << "\n"
<< " chi2/ndf : " << (ndf > 0 ? chi2 / ndf : 0.0) << " over " << np << " runs\n"
<< " scatter : RMS " << rms << " vs typical error " << etyp
<< " (ratio " << (etyp > 0 ? rms / etyp : 0.0) << ")\n\n"
<< (ndf > 0 && chi2 / ndf > 2.0
? " => scatter exceeds statistics: something changed DURING the\n"
" experiment (gas pressure, beam energy, steering, gains).\n"
" A single run-averaged constant will not describe it.\n"
: " => consistent with a constant: no run-to-run drift at this\n"
" precision, so any effect here is a FIXED systematic\n"
" (tables, geometry, beamE0, z_entrance) rather than a drift.\n");
if (TString(outPng).Length())
{
TCanvas *cv = new TCanvas("cv", "", 1200, 700);
g->SetTitle(Form("%s [%s];run number;%s", histName, mode, mode));
g->SetMarkerStyle(20);
g->Draw("AP");
cv->SaveAs(outPng);
std::cout << " saved: " << outPng << "\n";
}
}

View File

@ -8,7 +8,7 @@
6 0.853919 6.23079 6 0.853919 6.23079
7 0.945588 -9.54044 7 0.945588 -9.54044
8 0.884454 -11.8262 8 0.884454 -11.8262
9 0 0 9 0.922662 -2.32259
10 0.903053 9.28069 10 0.903053 9.28069
11 0.914653 9.87642 11 0.914653 9.87642
12 0 0 12 0 0