Compare commits

...

3 Commits

View File

@ -88,20 +88,8 @@ std::string dataset;
int co2pc = 3; // default to 3% CO2; also selects the Eloss table pc suffix. int co2pc = 3; // default to 3% CO2; also selects the Eloss table pc suffix.
int pressure = 250; // gas pressure (torr) for the Eloss-table filenames; int pressure = 250; // gas pressure (torr) for the Eloss-table filenames;
// overridable via the pressure_in_torr env var. // overridable via the pressure_in_torr env var.
// One analysis-wide RNG. Previously every dithering/smearing site declared its
// own `static TRandom3 x(0)`, and ROOT reads seed 0 as "seed from a TUUID" -- so
// each of the 11 generators picked a fresh stream on every run and the same input
// file produced different dithered histograms each time, making it impossible to
// separate a real change from dither noise. Fixed default seed, overridable via
// RNG_SEED when an independent stream is genuinely wanted.
TRandom3 anasenRandom(4357); TRandom3 anasenRandom(4357);
// Si <-> PC time coincidence. Kept in one place because this gate was previously
// spelled five different ways (`< 0`, `< 150`, `< -200`, `> 150`-reject,
// `!(< 150)`-reject) across 15 sites, which is how a sign inversion went unnoticed.
// One-sided by design: the real coincidence band sits well below zero (see the
// DelT_Vs_*ECal diagnostics), so only the late side needs rejecting.
constexpr double kSiPcDtMax = 150.0; constexpr double kSiPcDtMax = 150.0;
inline bool siPcCoincident(double t_si, double t_pc) inline bool siPcCoincident(double t_si, double t_pc)
{ {
@ -444,7 +432,7 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
bool isQQQ, const std::string &det, double si_ecut, double perp_cut, double phi_win, bool isQQQ, const std::string &det, double si_ecut, double perp_cut, double phi_win,
double initial_energy, const std::string &globaltag = ""); double initial_energy, const std::string &globaltag = "");
void miscHistograms_17Fax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events, void miscHistograms_17Fax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events,
const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag = ""); const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag = "", double dt_rf_mcp = -987654321);
void miscHistograms_27Alax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events, void miscHistograms_27Alax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events,
const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag = ""); const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag = "");
void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events, void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events,
@ -499,6 +487,7 @@ void TrackRecon::Begin(TTree * /*tree*/)
std::string outdir = getenv("OUT_DIR") ? getenv("OUT_DIR") : ""; std::string outdir = getenv("OUT_DIR") ? getenv("OUT_DIR") : "";
ta_foil_run = (outdir == "Output_p"); ta_foil_run = (outdir == "Output_p");
source_run = (outdir == "Output_a"); source_run = (outdir == "Output_a");
if (ta_foil_run && getenv("RUN_NUMBER")) if (ta_foil_run && getenv("RUN_NUMBER"))
{ {
int run_number = std::atoi(getenv("RUN_NUMBER")); int run_number = std::atoi(getenv("RUN_NUMBER"));
@ -1577,16 +1566,37 @@ Bool_t TrackRecon::Process(Long64_t entry)
double ae1 = std::get<1>(aCluster[1]); double ae1 = std::get<1>(aCluster[1]);
double alo = std::min(ae0, ae1); double alo = std::min(ae0, ae1);
double ahi = std::max(ae0, ae1); double ahi = std::max(ae0, ae1);
// Calibrated (MeV) wire energies for the same pair -- same pcEnergySlope
// lookup used everywhere else (e.g. anodeE_MeV in protonAlphaElastic_core).
int wi0 = std::get<0>(aCluster[0]);
int wi1 = std::get<0>(aCluster[1]);
double ae0_MeV = (wi0 >= 0 && wi0 < 24) ? pcEnergySlope[wi0] * ae0 : -1.0;
double ae1_MeV = (wi1 >= 0 && wi1 < 24) ? pcEnergySlope[wi1] * ae1 : -1.0;
if (ahi > 0.0) if (ahi > 0.0)
{ {
double aratio = alo / ahi; double aratio = alo / ahi;
plotter->Fill1D("A2_anode_ratio", 120, 0, 1.2, aratio, "hGMPC"); plotter->Fill1D("A2_anode_ratio_raw", 120, 0, 1.2, aratio, "hGMPC");
// plotter->Fill2D("A2_anode_ratio_vs_sum", 800, 0, 40000, 120, 0, 1.2, ae0 + ae1, aratio, "hGMPC"); // plotter->Fill2D("A2_anode_ratio_vs_sum", 800, 0, 40000, 120, 0, 1.2, ae0 + ae1, aratio, "hGMPC");
plotter->Fill2D("A1_vs_A2", 800, 0, 40000, 800, 0, 40000, ae0, ae1, "hGMPC"); plotter->Fill2D("A1_vs_A2_raw", 800, 0, 40000, 800, 0, 40000, ae0, ae1, "hGMPC");
plotter->Fill2D("A2_anode_ratio_vs_lowerIndex", 24, 0, 24, 120, 0, 1.2, plotter->Fill2D("A2_anode_ratio_vs_lowerIndex", 24, 0, 24, 120, 0, 1.2,
std::min(std::get<0>(aCluster[0]), std::get<0>(aCluster[1])), aratio, "hGMPC"); std::min(std::get<0>(aCluster[0]), std::get<0>(aCluster[1])), aratio, "hGMPC");
} }
// Calibrated equivalents -- guarded independently since a wire can lack
// a valid pcEnergySlope entry even when its raw ADC value is fine.
if (ae0_MeV >= 0.0 && ae1_MeV >= 0.0 && std::max(ae0_MeV, ae1_MeV) > 0.0)
{
double alo_MeV = std::min(ae0_MeV, ae1_MeV);
double ahi_MeV = std::max(ae0_MeV, ae1_MeV);
double aratio_MeV = alo_MeV / ahi_MeV;
plotter->Fill1D("A2_anode_ratio_calib", 120, 0, 1.2, aratio_MeV, "hGMPC");
plotter->Fill2D("A1_vs_A2_calib", 800, 0, 0.6, 800, 0, 0.6, ae0_MeV, ae1_MeV, "hGMPC");
plotter->Fill2D("A2_anode_ratio_calib_vs_lowerIndex", 24, 0, 24, 120, 0, 1.2,
std::min(wi0, wi1), aratio_MeV, "hGMPC");
}
plotter->Fill1D("Raw_A2_AnodeSum", 800, 0, 40000, ae0 + ae1, "hGMPC"); plotter->Fill1D("Raw_A2_AnodeSum", 800, 0, 40000, ae0 + ae1, "hGMPC");
} }
else if (aCluster.size() == 1) else if (aCluster.size() == 1)
@ -1736,6 +1746,19 @@ Bool_t TrackRecon::Process(Long64_t entry)
//////Timing stuff for F data //////Timing stuff for F data
TRandom3 &rnd = anasenRandom; TRandom3 &rnd = anasenRandom;
double dt_rf_mcp_event = -987654321;
{
double ts_rf_tmp = -987654321, ts_mcp_tmp = -987654321;
for (int j = 0; j < misc.multi; j++)
{
if (misc.ch[j] == 3)
ts_rf_tmp = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
if (misc.ch[j] == 4)
ts_mcp_tmp = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
}
if (ts_rf_tmp > -987654321 && ts_mcp_tmp > -987654321)
dt_rf_mcp_event = ts_rf_tmp - ts_mcp_tmp;
}
if (dataset == "17F" && reactiondata) if (dataset == "17F" && reactiondata)
{ {
// misc.ch is a property of the event, not of any Si hit -- filled inside the // misc.ch is a property of the event, not of any Si hit -- filled inside the
@ -1747,6 +1770,7 @@ Bool_t TrackRecon::Process(Long64_t entry)
if (SX3_Events.size()) if (SX3_Events.size())
plotter->Fill1D("channels_misc_sx3", 20, -0.5, 19.5, misc.ch[j], "misc"); plotter->Fill1D("channels_misc_sx3", 20, -0.5, 19.5, misc.ch[j], "misc");
} }
int ctr = 0; int ctr = 0;
for (const auto &qqqevent : QQQ_Events) for (const auto &qqqevent : QQQ_Events)
{ {
@ -1902,7 +1926,7 @@ Bool_t TrackRecon::Process(Long64_t entry)
if (reactiondata) if (reactiondata)
{ {
if (dataset == "17F") if (dataset == "17F")
miscHistograms_17Fax(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters); miscHistograms_17Fax(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters, "", dt_rf_mcp_event);
if (dataset == "27Al") if (dataset == "27Al")
miscHistograms_27Alax(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters); miscHistograms_27Alax(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters);
} }
@ -2362,8 +2386,8 @@ void PCSX3ClusterAnalysis(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
{ {
for (const auto &sx3event : SX3_Events) for (const auto &sx3event : SX3_Events)
{ {
plotter->Fill1D("dt_pcA_sx3B" + std::to_string(sx3event.ch2), 640, -2000, 2000, sx3event.Time1 - pcevent.Time1, "Timing"); // plotter->Fill1D("dt_pcA_sx3B" + std::to_string(sx3event.ch2), 640, -2000, 2000, sx3event.Time1 - pcevent.Time1, "Timing");
plotter->Fill1D("dt_pcC_sx3B" + std::to_string(sx3event.ch2), 640, -2000, 2000, sx3event.Time1 - pcevent.Time2, "Timing"); // plotter->Fill1D("dt_pcC_sx3B" + std::to_string(sx3event.ch2), 640, -2000, 2000, sx3event.Time1 - pcevent.Time2, "Timing");
bool PCASX3TimeCut = siPcCoincident(sx3event.Time1, pcevent.Time1); bool PCASX3TimeCut = siPcCoincident(sx3event.Time1, pcevent.Time1);
bool PCCSX3TimeCut = siPcCoincident(sx3event.Time1, pcevent.Time2); bool PCCSX3TimeCut = siPcCoincident(sx3event.Time1, pcevent.Time2);
@ -3505,6 +3529,25 @@ void TrackRecon::OldAnalysis()
} }
} }
// Every reconstructed point contributes to a fixed set of output tiers:
// always the pooled fill (""), always topo1 (the finest-grained method tag,
// e.g. "a1c1"/"a1c2fix"/"a1c0"/"a2c0"), and optionally topo2 (a variant like
// "a1c1_inband") and methodGroup (a coarser grouping like "a1c1c2"). Shared by
// reaction_ax_core and protonAlphaElastic_core (below) so this tier list only
// has to be spelled out once; relocated here (from just above
// reaction_ax_core) so both can see it.
template <typename FillOneTier>
static void forEachTier(const std::string &topo1, const std::string &topo2,
const std::string &methodGroup, FillOneTier &&fillOneTier)
{
fillOneTier("");
fillOneTier(topo1);
if (!topo2.empty())
fillOneTier(topo2);
if (!methodGroup.empty())
fillOneTier(methodGroup);
}
void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_Events, const std::vector<Event> &PC_Events, void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_Events, const std::vector<Event> &PC_Events,
const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters,
bool isQQQ, const std::string &det, double si_ecut, double perp_cut, double phi_win, bool isQQQ, const std::string &det, double si_ecut, double perp_cut, double phi_win,
@ -3562,27 +3605,45 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill1D(rx + "_pczfix" + ejtag + sfx, 600, -300, 300, pcz_fix, pmlabel); plotter->Fill1D(rx + "_pczfix" + ejtag + sfx, 600, -300, 300, pcz_fix, pmlabel);
plotter->Fill1D(rx + "_VertexReconZ" + ejtag + sfx, 800, -400, 400, vertex_z, pmlabel); plotter->Fill1D(rx + "_VertexReconZ" + ejtag + sfx, 800, -400, 400, vertex_z, pmlabel);
plotter->Fill2D(rx + "_VertexReconXY" + ejtag + sfx, 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), pmlabel); plotter->Fill2D(rx + "_VertexReconXY" + ejtag + sfx, 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ef" + ejtag + sfx, 800, -400, 400, 800, 0, 10, vertex_z, Efix, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ef" + ejtag + "_a" + std::to_string(multi1) + sfx, 800, -400, 400, 800, 0, 20, vertex_z, Efix, pmlabel);
plotter->Fill2D(rx + "_Ef_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, 10, theta * 180 / M_PI, Efix, pmlabel); plotter->Fill2D(rx + "_Ef_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, 10, theta * 180 / M_PI, Efix, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_theta" + ejtag + sfx, 180, 0, 180, 800, -10, 10, theta * 180 / M_PI, Ex, pmlabel); plotter->Fill2D(rx + "_Ex_vs_theta" + ejtag + sfx, 180, 0, 180, 800, -10, 10, theta * 180 / M_PI, Ex, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_phi" + ejtag + sfx, 180, -180, 180, 800, -10, 10, sievent.pos.Phi() * 180 / M_PI, Ex, pmlabel); plotter->Fill2D(rx + "_Ex_vs_phi" + ejtag + sfx, 180, -180, 180, 800, -10, 10, sievent.pos.Phi() * 180 / M_PI, Ex, pmlabel);
plotter->Fill1D(rx + "_Ex_from" + ejtag + sfx, 800, -10, 10, Ex, pmlabel);
if (multi2 == 1)
{
plotter->Fill2D(rx + "_Ef_vs_theta_a1c1" + ejtag + sfx, 180, 0, 180, 800, 0, 10, theta * 180 / M_PI, Efix, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ef_a1c1" + ejtag + sfx, 800, -400, 400, 800, 0, 20, vertex_z, Efix, pmlabel);
}
// Ground-state beam-energy consistency check -- elastic scattering has // Ground-state beam-energy consistency check -- elastic scattering has
// no excited levels, so there's only ever a "ground state" hypothesis // no excited levels, so there's only ever a "ground state" hypothesis
// here, unlike the (a,p) reaction branch's snapped levels. // here, unlike the (a,p) reaction branch's snapped levels.
double m3 = alphaHyp ? mass_4He : mass_1H, m4 = alphaHyp ? mass_1H : mass_4He; double m3 = alphaHyp ? mass_4He : mass_1H, m4 = alphaHyp ? mass_1H : mass_4He;
double theta_deg = (theta * 180 / M_PI); double theta_deg = (theta * 180 / M_PI);
double ebeam_kin = invertBeamEnergyMeV(mass_1H, mass_4He, m3, m4, Efix,theta_deg, 0.0); double ebeam_kin = invertBeamEnergyMeV(mass_1H, mass_4He, m3, m4, Efix, theta_deg, 0.0);
if (ebeam_kin > 0.0)
plotter->Fill2D(rx + "_BeamEnergy_ETrack_vs_EKin" + ejtag + sfx, 800, 0, initial_energy * 1.5, 800, 0, initial_energy * 1.5, beam_energy_at_vertex, ebeam_kin, pmlabel); // Per-topology tiering, matching reaction_ax_core exactly: pooled ("")
plotter->Fill2D(rx + "_EKin_vs_ESi" + ejtag + sfx, 400, 0, initial_energy * 1.5, 800, 0, 10, ebeam_kin, sievent.Energy1, pmlabel); // plus a separate copy per topo1 (a1c1/a1c2fix/a1c0/a2c0) and, for
// a1c1/a1c2, the coarser methodGroup "a1c1c2" grouping them together
// as distinct from a1c0/a2c0. topo2 ("a1c1_inband") is intentionally
// left empty here -- reaction_ax_core's a1c1 uses the cfrac-based Z
// (a1c1_cfrac_pcz) as its PRIMARY reconstruction and inband-ness comes
// straight out of that call; this branch's primary a1c1 Z is still the
// Gaussian dither (see the a1c1cmp comparison block above for where
// cfrac-pick is checked instead), so there's no equivalent inband flag
// on the primary path to tag with. Say the word if you'd rather switch
// a1c1's primary Z to a1c1_cfrac_pcz to close that gap too.
auto plot_with_tag = [&](const std::string &topo)
{
std::string t = topo.empty() ? "" : ("_" + topo);
plotter->Fill1D(rx + "_Ex_from" + ejtag + t + sfx, 800, -10, 10, Ex, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ef" + ejtag + t + sfx, 800, -400, 400, 800, 0, 10, vertex_z, Efix, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ex" + ejtag + t + sfx, 800, -400, 400, 400, -10, 10, vertex_z, Ex, pmlabel);
if (ebeam_kin > 0.0)
{
plotter->Fill2D(rx + "_BeamEnergy_ETrack_vs_EKin" + ejtag + t + sfx, 800, 0, initial_energy * 1.5, 800, 0, initial_energy * 1.5,
beam_energy_at_vertex, ebeam_kin, 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"
: (multi1 == 2 ? "a2c0" : "a1c0");
std::string methodGroup = (multi2 == 1 || multi2 == 2) ? "a1c1c2" : "";
forEachTier(topo1, "", methodGroup, plot_with_tag);
// Gas segmentation validation (dEgas family), uniform for every // Gas segmentation validation (dEgas family), uniform for every
// topology including a1c0/a2c0. // topology including a1c0/a2c0.
@ -3601,9 +3662,9 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill2D(rx + "_dEgasCalib_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, 0.6, theta * 180 / M_PI, anodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalib_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, 0.6, theta * 180 / M_PI, anodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_dEgasCalib_vs_phi" + ejtag + sfx, 100, -200, 200, 800, 0, 0.6, sievent.pos.Phi() * 180 / M_PI, anodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalib_vs_phi" + ejtag + sfx, 100, -200, 200, 800, 0, 0.6, sievent.pos.Phi() * 180 / M_PI, anodeE_MeV, pmlabel);
if (anodeCh >= 0 && anodeCh < 24) if (anodeCh >= 0 && anodeCh < 24)
plotter->Fill2D(rx + "_dEgasCalib_vs_E" + ejtag + sfx + "_anode" + pad2(anodeCh), // plotter->Fill2D(rx + "_dEgasCalib_vs_E" + ejtag + sfx + "_anode" + pad2(anodeCh),
400, 0, 10, 800, 0, 0.6, sievent.Energy1, anodeE_MeV, pmlabel); // 400, 0, 10, 800, 0, 0.6, sievent.Energy1, anodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_dEgasCalib_vs_Ex" + ejtag + sfx, 800, -10, 10, 800, 0, 0.6, Ex, anodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalib_vs_Ex" + ejtag + sfx, 800, -10, 10, 800, 0, 0.6, Ex, anodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_dEgasCalib_vs_Z" + ejtag + sfx, 800, -400, 400, 800, 0, 0.6, vertex_z, anodeE_MeV, pmlabel); plotter->Fill2D(rx + "_dEgasCalib_vs_Z" + ejtag + sfx, 800, -400, 400, 800, 0, 0.6, vertex_z, anodeE_MeV, pmlabel);
plotter->Fill2D(rx + "_dEgasPred_vs_dEgasCalib" + ejtag + sfx, 800, 0, 0.6, 800, 0, 0.6, anodeE_MeV, dE_pred, pmlabel); plotter->Fill2D(rx + "_dEgasPred_vs_dEgasCalib" + ejtag + sfx, 800, 0, 0.6, 800, 0, 0.6, anodeE_MeV, dE_pred, pmlabel);
} }
@ -3753,6 +3814,15 @@ void protonAlphaElastic_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill2D(rx + "_dPhi" + sfx, 100, -200, 200, 100, -200, 200, pc.Phi() * 180 / M_PI, sievent.pos.Phi() * 180 / M_PI, misclabel); plotter->Fill2D(rx + "_dPhi" + sfx, 100, -200, 200, 100, -200, 200, pc.Phi() * 180 / M_PI, sievent.pos.Phi() * 180 / M_PI, misclabel);
plotter->Fill1D(rx + "_dt_Anode" + sfx, 600, -2000, 2000, apTSMaxE - sievent.Time1, misclabel); plotter->Fill1D(rx + "_dt_Anode" + sfx, 600, -2000, 2000, apTSMaxE - sievent.Time1, misclabel);
// Per-topology diagnostics (matches reaction_ax_core's a1c0/a2c0 loop
// exactly): dE_E_Anode, dPhi, and the raw (undithered) wire-lookup Z,
// each tagged by a0tag rather than pooled.
const std::string a0tag = isA2C0 ? "a2c0" : "a1c0";
std::string a0label = misclabel + "_" + a0tag;
plotter->Fill2D(rx + "_dE_E_Anode_" + a0tag + sfx, 400, 0, 10, 800, 0, 40000, sievent.Energy1, apSumE, a0label);
plotter->Fill2D(rx + "_dPhi_" + a0tag + sfx, 100, -200, 200, 100, -200, 200, pc.Phi() * 180 / M_PI, sievent.pos.Phi() * 180 / M_PI, a0label);
plotter->Fill1D(rx + "_rawZ_" + a0tag + sfx, 600, -300, 300, pc.Z(), a0label);
int anodeCh_a0 = std::get<0>(aCl[0]); int anodeCh_a0 = std::get<0>(aCl[0]);
if (anodeCh_a0 < 0 || anodeCh_a0 >= 24) if (anodeCh_a0 < 0 || anodeCh_a0 >= 24)
anodeCh_a0 = -1; anodeCh_a0 = -1;
@ -3814,30 +3884,12 @@ inline double snapToNearestLevel(double ex, const std::vector<double> &levels, d
return best; return best;
} }
// Every reconstructed point contributes to a fixed set of output tiers:
// always the pooled fill (""), always topo1 (the finest-grained method tag,
// e.g. "a1c1"/"a1c2fix"/"a1c0"), and optionally topo2 (a variant like
// "a1c1_inband") and methodGroup (a coarser grouping like "a1c1c2"). Used
// by reaction_ax_core for both its always-on fills and its proton-locus
// gated fills below, so this tier list only has to be spelled out once.
template <typename FillOneTier>
static void forEachTier(const std::string &topo1, const std::string &topo2,
const std::string &methodGroup, FillOneTier &&fillOneTier)
{
fillOneTier("");
fillOneTier(topo1);
if (!topo2.empty())
fillOneTier(topo2);
if (!methodGroup.empty())
fillOneTier(methodGroup);
}
static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_Events, const std::vector<Event> &PC_Events, static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_Events, const std::vector<Event> &PC_Events,
const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, bool isQQQ, const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, bool isQQQ,
const std::string &rx, const std::string &det, double si_ecut, double perp_cut, double phi_win, const std::string &rx, const std::string &det, double si_ecut, double perp_cut, double phi_win,
double dEa_max, double dEc_max, double ef_max, double dEa_max, double dEc_max, double ef_max,
double beamE0, TSpline3 *beam_MeV_to_cm, TSpline3 *beam_cm_to_MeV, double m_beam, double beamE0, TSpline3 *beam_MeV_to_cm, TSpline3 *beam_cm_to_MeV, double m_beam,
const AAEjectileMasses &ej_m, const std::string &globaltag) const AAEjectileMasses &ej_m, const std::string &globaltag, double dt_rf_mcp = -987654321)
{ {
const std::string sfx = "_" + det + globaltag; const std::string sfx = "_" + det + globaltag;
TRandom3 &rand = anasenRandom; TRandom3 &rand = anasenRandom;
@ -3913,6 +3965,7 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
plotter->Fill1D(rx + "_Ex_from" + ejtag + t + sfx, 600, -15, 15, Ex, pmlabel); plotter->Fill1D(rx + "_Ex_from" + ejtag + t + sfx, 600, -15, 15, Ex, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ef" + ejtag + t + sfx, 800, -400, 400, 800, 0, ef_max, vertex_z, Efix, pmlabel); plotter->Fill2D(rx + "_VertexReconZ_vs_Ef" + ejtag + t + sfx, 800, -400, 400, 800, 0, ef_max, vertex_z, Efix, pmlabel);
plotter->Fill2D(rx + "_VertexReconZ_vs_Ex" + ejtag + t + sfx, 800, -400, 400, 800, -20, 20, vertex_z, Ex, pmlabel); plotter->Fill2D(rx + "_VertexReconZ_vs_Ex" + ejtag + t + sfx, 800, -400, 400, 800, -20, 20, vertex_z, Ex, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_theta" + ejtag + t + sfx, 360, 0, 180, 800, -20, 20, theta * 180 / M_PI, Ex, pmlabel);
if (ebeam_kin_MeV > 0.0) if (ebeam_kin_MeV > 0.0)
plotter->Fill2D(rx + "_BeamEnergy_ETrack_vs_EKin" + ejtag + t + sfx, 400, 0, beamE0 * 1.5, 400, 0, beamE0 * 1.5, plotter->Fill2D(rx + "_BeamEnergy_ETrack_vs_EKin" + ejtag + t + sfx, 400, 0, beamE0 * 1.5, 400, 0, beamE0 * 1.5,
@ -3946,8 +3999,9 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
} }
plotter->Fill1D(rx + "_pczfix" + sfx, 600, -300, 300, pcz_fix, pmlabel); plotter->Fill1D(rx + "_pczfix" + sfx, 600, -300, 300, pcz_fix, pmlabel);
plotter->Fill2D(rx + "_Ef_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, ef_max, theta * 180 / M_PI, Efix, pmlabel); plotter->Fill2D(rx + "_Ef_vs_theta" + ejtag + sfx, 100, 0, 180, 800, 0, ef_max, theta * 180 / M_PI, Efix, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_theta" + ejtag + sfx, 360, 0, 180, 800, -20, 20, theta * 180 / M_PI, Ex, pmlabel);
plotter->Fill2D(rx + "_Ex_vs_phi" + ejtag + sfx, 180, -180, 180, 800, -20, 20, phi * 180 / M_PI, Ex, pmlabel); plotter->Fill2D(rx + "_Ex_vs_phi" + ejtag + sfx, 180, -180, 180, 800, -20, 20, phi * 180 / M_PI, Ex, pmlabel);
if (dt_rf_mcp > -900000000)
plotter->Fill2D(rx + "_Ex_vs_TOF_rf_mcp" + ejtag + sfx, 500, -1000, 1000, 800, -20, 20, dt_rf_mcp, Ex, pmlabel);
plotter->Fill1D(rx + "_VertexReconZ" + sfx, 800, -400, 400, vertex_z, pmlabel); plotter->Fill1D(rx + "_VertexReconZ" + sfx, 800, -400, 400, vertex_z, pmlabel);
forEachTier(topo1, topo2, methodGroup, plot_with_tag); forEachTier(topo1, topo2, methodGroup, plot_with_tag);
@ -4090,7 +4144,7 @@ static void reaction_ax_core(HistPlotter *plotter, const std::vector<Event> &Si_
} }
void miscHistograms_17Fax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events, void miscHistograms_17Fax(HistPlotter *plotter, const std::vector<Event> &QQQ_Events, const std::vector<Event> &SX3_Events, const std::vector<Event> &PC_Events,
const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag) const std::vector<std::vector<std::tuple<int, double, double>>> &aClusters, std::string globaltag, double dt_rf_mcp)
{ {
// 17F beam energy at the gas target, after the entrance-window foils: // 17F beam energy at the gas target, after the entrance-window foils:
@ -4099,9 +4153,9 @@ void miscHistograms_17Fax(HistPlotter *plotter, const std::vector<Event> &QQQ_Ev
// 17F(a,a)/(a,d)/(a,p): ejectile + recoil masses per channel. // 17F(a,a)/(a,d)/(a,p): ejectile + recoil masses per channel.
AAEjectileMasses ej17F{mass_4He, mass_17F, mass_2H, mass_19Ne_rec, mass_1H, mass_20Ne}; AAEjectileMasses ej17F{mass_4He, mass_17F, mass_2H, mass_19Ne_rec, mass_1H, mass_20Ne};
reaction_ax_core(plotter, QQQ_Events, PC_Events, aClusters, true, "m17Fax", "qqq", 0.6, 6.0, TMath::Pi() / 4.0, reaction_ax_core(plotter, QQQ_Events, PC_Events, aClusters, true, "m17Fax", "qqq", 0.6, 6.0, TMath::Pi() / 4.0,
30.0, 40000.0, 30.0, ebeam_17F_MeV, MeV_to_cm_17F_spl, cm_to_MeV_17F_spl, mass_17F, ej17F, globaltag); 30.0, 40000.0, 30.0, ebeam_17F_MeV, MeV_to_cm_17F_spl, cm_to_MeV_17F_spl, mass_17F, ej17F, globaltag, dt_rf_mcp);
reaction_ax_core(plotter, SX3_Events, PC_Events, aClusters, false, "m17Fax", "sx3", 1.2, 10.0, TMath::Pi() / 3.0, reaction_ax_core(plotter, SX3_Events, PC_Events, aClusters, false, "m17Fax", "sx3", 1.2, 10.0, TMath::Pi() / 3.0,
30.0, 40000.0, 30.0, ebeam_17F_MeV, MeV_to_cm_17F_spl, cm_to_MeV_17F_spl, mass_17F, ej17F, globaltag); 30.0, 40000.0, 30.0, ebeam_17F_MeV, MeV_to_cm_17F_spl, cm_to_MeV_17F_spl, mass_17F, ej17F, globaltag, dt_rf_mcp);
} }
// 27Al(a,a) excitation functions for BOTH silicon branches (QQQ + SX3), with the // 27Al(a,a) excitation functions for BOTH silicon branches (QQQ + SX3), with the