modified: fit_a1c1_cfrac.C included a reference free method for gain amtching cell modified: slope_intercept_results_17F.dat put in value for the missing wire gains based on other wires with similar profiles. The variation among them is minute but the vairation from one is consitent
3187 lines
169 KiB
C
3187 lines
169 KiB
C
#define TrackRecon_cxx
|
|
|
|
#define RAW_HISTOS
|
|
// #define VTX_GATES
|
|
// #define AL_BEAM
|
|
// #define F_BEAM
|
|
// #define nA_Analysis
|
|
|
|
Int_t colors[40] = {
|
|
kBlack, kRed, kGreen, kBlue, kYellow, kMagenta, kCyan, kOrange,
|
|
kSpring, kTeal, kAzure, kViolet, kPink, kGray, kWhite,
|
|
kRed + 2, kGreen + 2, kBlue + 2, kYellow + 2, kMagenta + 2, kCyan + 2, kOrange + 2,
|
|
kSpring + 2, kTeal + 2, kAzure + 2, kViolet + 2, kPink + 2,
|
|
kRed - 7, kGreen - 7, kBlue - 7, kYellow - 7, kMagenta - 7, kCyan - 7, kOrange - 7,
|
|
kSpring - 7, kTeal - 7, kAzure - 7, kViolet - 7, kPink - 7, kGray + 2};
|
|
|
|
#include "TrackRecon.h"
|
|
#include "Armory/ClassPW.h"
|
|
#include "Armory/HistPlotter.h"
|
|
#include "Armory/SX3Geom.h"
|
|
#include "Armory/PC_StepLadder_Correction.h"
|
|
#include "Armory/Kinematics.h"
|
|
#include <TH2.h>
|
|
#include <TF1.h>
|
|
#include <TStyle.h>
|
|
#include <TCanvas.h>
|
|
#include <TMath.h>
|
|
#include <TBranch.h>
|
|
#include <TVector3.h>
|
|
#include <TRandom3.h>
|
|
|
|
#include <fstream>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <vector>
|
|
#include <array>
|
|
#include <map>
|
|
#include <utility>
|
|
#include <algorithm>
|
|
|
|
bool process_alpha_proton_scattering = false;
|
|
bool doMiscHistograms = true;
|
|
bool doPCSX3ClusterAnalysis = true;
|
|
bool doPCQQQClusterAnalysis = true;
|
|
bool doOldAnalysis = true;
|
|
bool do27AlapAnalysis = false;
|
|
bool BenchMark = true;
|
|
double source_vertex = 53; // 53
|
|
const double qqq_z = 105.0;
|
|
double z_entrance = -174.3 - 9.7 - 100.0;
|
|
const double anode_gain = 1.5146e-5; // channels --> MeV
|
|
double dither_sigma = 8.0;
|
|
double dither_sigma_c0 = 16.0;
|
|
double Gain = 1;
|
|
std::string dataset;
|
|
int CO2percent;
|
|
bool reactiondata = false;
|
|
|
|
TF1 pcfix_func("func", model_invert, -200, 200);
|
|
|
|
// --- A1C1 linear centre-fold model: cfrac = cfmin[cell] + k[cell]*fold ----------
|
|
// fold = |z - z_cellcentre| / halfcell in [0,1]. The raw cfrac pedestal floats
|
|
// with the arbitrary anode/cathode gain, so cfmin/k are dataset-specific: fit
|
|
// them OFFLINE with fit_a1c1_cfrac.C on prebuilt data and paste the per-cell
|
|
// results below; Begin() selects the active set by dataset.
|
|
const double a1c1_zg[8] = {147.998, 101.946, 59.7634, 19.6965, -19.6965, -59.7634, -101.946, -147.998};
|
|
|
|
static const double a1c1_cfmin_17F[7] = {0.557612, 0.400000, 0.400000, 0.342771, 0.400000, 0.682479, 0.400000};
|
|
static const double a1c1_k_17F[7] = {0.0688469, 0.075000, 0.075000, 0.160131, 0.075000, 0.273423, 0.075000};
|
|
static const double a1c1_cfmin_27Al[7] = {0.18, 0.18, 0.18, 0.18, 0.18, 0.18, 0.18}; // TODO: optimise on 27Al data
|
|
static const double a1c1_k_27Al[7] = {0.06, 0.06, 0.06, 0.06, 0.06, 0.06, 0.06};
|
|
|
|
// active per-cell set, populated by dataset in Begin()
|
|
double a1c1_cfmin_cell[7] = {0.40, 0.40, 0.40, 0.40, 0.40, 0.40, 0.40};
|
|
double a1c1_k_cell[7] = {0.075, 0.075, 0.075, 0.075, 0.075, 0.075, 0.075};
|
|
|
|
// --- Dead / missing PC wires -------------------------------------------------
|
|
// Some channels are unresponsive in a run (the white vertical gaps in
|
|
// PC_Index_Vs_Energy). A genuine two-wire track that straddles a dead wire
|
|
// collapses to a single fired wire: a "pseudo-1-wire" event. We still treat it
|
|
// as A1C1, but flag it so the excitation function can be split three ways:
|
|
// _all : every A1C1 event (unchanged from before)
|
|
// _true1w : neither neighbour of the fired anode/cathode is dead (genuine single)
|
|
// _missingw : a neighbouring wire is dead (suspected masked two-wire event)
|
|
// Index 0-23 within EACH plane; 1 = dead. Read the dead channels off the gaps
|
|
// in PC_Index_Vs_Energy (anode = index 0-23, cathode = index 24-47 -> w = idx-24)
|
|
// and fill the per-dataset arrays. Default all-alive => everything is _true1w
|
|
// and _missingw stays empty (no behaviour change until channels are entered).
|
|
|
|
// static std::vector<int> a1c1_dead_anode_17F = {}; // 1 can be recovered
|
|
// static std::vector<int> a1c1_dead_cathode_17F = {}; // 0,13,15 gain-matched to 0
|
|
// static std::vector<int> a1c1_dead_anode_27Al = {};
|
|
// static std::vector<int> a1c1_dead_cathode_27Al = {};
|
|
|
|
static std::vector<int> a1c1_dead_anode_17F = {9, 12}; // 1 can be recovered
|
|
static std::vector<int> a1c1_dead_cathode_17F = {}; // 0,13,15 can be recovered
|
|
static std::vector<int> a1c1_dead_anode_27Al = {0, 9, 12, 19};
|
|
static std::vector<int> a1c1_dead_cathode_27Al = {13};
|
|
std::vector<int> *a1c1_dead_anode = &a1c1_dead_anode_17F; // active set, chosen in Begin()
|
|
std::vector<int> *a1c1_dead_cathode = &a1c1_dead_cathode_17F;
|
|
|
|
// True if a neighbouring wire (index +/-1) of the fired anode OR cathode is
|
|
// dead -- i.e. this single-wire event may actually be a masked two-wire one.
|
|
inline bool a1c1_missing_neighbor(int awire, int cwire)
|
|
{
|
|
auto deadAdj = [](const std::vector<int> &dead, int w)
|
|
{
|
|
if (w < 0 || w >= 24)
|
|
return false;
|
|
auto isDead = [&](int x)
|
|
{ return std::find(dead.begin(), dead.end(), x) != dead.end(); };
|
|
return isDead(w - 1) || isDead(w + 1);
|
|
};
|
|
return deadAdj(*a1c1_dead_anode, awire) || deadAdj(*a1c1_dead_cathode, cwire);
|
|
}
|
|
|
|
// --- A1C1 LOW BAND (incomplete charge integration) -----------------------------
|
|
// 17F shows a second, parallel cfrac band (~0.10 -> 0.15 over the fold) where the
|
|
// cathode charge is only partially integrated. It still tracks z (real physics),
|
|
// so it gets its own per-cell cfmin/k. Events with cfrac below a1c1_cfrac_split
|
|
// are reconstructed with this set instead of being rejected. a1c1_cfrac_split<=0
|
|
// disables the low band (e.g. 27Al, which shows no second band).
|
|
static const double a1c1_cfmin2_17F[7] = {0.10, 0.10, 0.10, 0.10, 0.10, 0.10, 0.10};
|
|
static const double a1c1_k2_17F[7] = {0.05, 0.05, 0.05, 0.05, 0.05, 0.05, 0.05};
|
|
static const double a1c1_cfmin2_27Al[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // no low band
|
|
static const double a1c1_k2_27Al[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
|
|
|
double a1c1_cfmin2_cell[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
|
double a1c1_k2_cell[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
|
|
double a1c1_cfrac_split = 0.0; // cfrac below this uses the low band; <=0 disables
|
|
double a1c1_missing_fmax = 2.0; // f-ceiling when a neighbouring wire is dead (1.0 = no extension)
|
|
|
|
// Sub-cell A1C1 z from cfrac (linear centre-fold). zf = crossover z (fired
|
|
// cathode, on the grid), z_a1c0 = anode-only z (side reference). Anchors on the
|
|
// fired wire, folds about the adjacent cell centre, inverts cfrac = cfmin+k*fold
|
|
// per cell. Picks the LOW band (band=1) when cfrac < a1c1_cfrac_split, else the
|
|
// MAIN band (band=0). Callers apply their own acceptance via the returned flags:
|
|
// inband : f in [0,1] (cfrac within the calibrated band)
|
|
// pitchok : |pcz - zf| <= pitch (consistent with the fired wire)
|
|
struct A1C1Sol
|
|
{
|
|
double pcz; // raw inversion (can fall outside the cell if f<0 or f>1)
|
|
double pcz_clamped; // f clamped to [0,1] -> always inside the cell (no events lost)
|
|
double f;
|
|
int cell;
|
|
double pitch;
|
|
bool inband;
|
|
bool pitchok;
|
|
int band;
|
|
};
|
|
inline A1C1Sol a1c1_solve(double cfrac, double zf, double z_a1c0, int cwire = -1, int awire = -1)
|
|
{
|
|
// Result structure.
|
|
// By default return the fired-wire position (zf) and mark the solution invalid.
|
|
A1C1Sol s{zf, zf, 0.0, 0, 0.0, false, false, 0};
|
|
// Two independent calibrations are supported:
|
|
// band 0 : nominal integration
|
|
// band 1 : low-cfrac / incomplete-integration events
|
|
const double *cfmin = a1c1_cfmin_cell;
|
|
const double *kk = a1c1_k_cell;
|
|
if (a1c1_cfrac_split > 0.0 && cfrac >= 0.0 && cfrac < a1c1_cfrac_split)
|
|
{
|
|
cfmin = a1c1_cfmin2_cell;
|
|
kk = a1c1_k2_cell;
|
|
s.band = 1;
|
|
}
|
|
|
|
// Identify the fired cathode wire from the measured cathode position zf
|
|
// which is assumed to lie on one side of the calibrated cathode wire positions a1c1_zg[].
|
|
|
|
int wf = 0;
|
|
for (int i = 1; i < 8; ++i)
|
|
if (TMath::Abs(a1c1_zg[i] - zf) < TMath::Abs(a1c1_zg[wf] - zf))
|
|
wf = i;
|
|
|
|
// Determine the side of the fired cathode wire the event occurred onfrom teh anode only recon z_a1c0
|
|
// Cells are indexed: wire0 --- cell0 --- wire1 --- cell1 --- ... --- wire7 giving seven physical drift cells.
|
|
|
|
int cell = (z_a1c0 >= a1c1_zg[wf]) ? (wf - 1) : wf;
|
|
if (cell < 0)
|
|
cell = 0;
|
|
if (cell > 6)
|
|
cell = 6;
|
|
s.cell = cell;
|
|
|
|
// Cell geometry.
|
|
// zc : cell centre half : half-cell width pitch: full wire-to-wire spacing
|
|
|
|
double zc = 0.5 * (a1c1_zg[cell] + a1c1_zg[cell + 1]);
|
|
double half = 0.5 * (a1c1_zg[cell] - a1c1_zg[cell + 1]);
|
|
s.pitch = a1c1_zg[cell] - a1c1_zg[cell + 1];
|
|
if (half <= 0.0 || kk[cell] <= 0.0)
|
|
return s;
|
|
|
|
// Convert cfrac into a normalized position coordinate f, with f = 0 -> cell centre and f = 1 -> fired wire
|
|
// Values outside [0,1] indicate an event outside the calibrated cfrac band.
|
|
|
|
s.f = (cfrac - cfmin[cell]) / kk[cell];
|
|
// Determine whether the fired wire is above or below the cell centre.
|
|
// The sign maps increasing f toward the appropriate cathode wire.
|
|
double sgn = (a1c1_zg[wf] >= zc) ? +1.0 : -1.0;
|
|
s.pcz = zc + sgn * s.f * half;
|
|
// A dead neighbouring wire means the fired wire also collects the charge it
|
|
// would normally share, so cfrac (hence f) runs past the usual cfmin+k ceiling.
|
|
// Lift the upper limit toward the dead wire's cell so these events land there
|
|
// instead of being pinned to the fired-wire edge (f=1).
|
|
double fmax = a1c1_missing_neighbor(awire, cwire) ? a1c1_missing_fmax : 1.0;
|
|
double fc = (s.f < 0.0) ? 0.0 : (s.f > fmax ? fmax : s.f);
|
|
s.pcz_clamped = zc + sgn * fc * half;
|
|
s.inband = (s.f >= 0.0 && s.f <= fmax);
|
|
// Consistency check:reconstructed position should remain within one cell pitch of
|
|
// originally fired cathode wire.
|
|
s.pitchok = (TMath::Abs(s.pcz - zf) <= s.pitch);
|
|
return s;
|
|
}
|
|
|
|
TGraph *MeV_to_cm = NULL, *cm_to_MeV = NULL;
|
|
TGraph *MeV_to_cm_p = NULL, *cm_to_MeVp = NULL;
|
|
TGraph *MeV_to_cm_27Al = NULL, *cm_to_MeV_27Al = NULL;
|
|
TGraph *MeV_to_cm_17F = NULL, *cm_to_MeV_17F = NULL;
|
|
|
|
// declaring masses for kinematics calculations
|
|
double mass_27Al = 26.981538;
|
|
double mass_4He = 4.002603;
|
|
double mass_1H = 1.007825;
|
|
double mass_30Si = 29.973770;
|
|
double mass_17F = 17.002095;
|
|
double mass_20Ne = 19.992440;
|
|
|
|
/*
|
|
double z_to_crossover_rho(double z)
|
|
{
|
|
return 9.20645e-5 * z * z + 34.1973;
|
|
}
|
|
|
|
double z_to_crossover_rho_cathode(double z)
|
|
{
|
|
return 9.20645e-5 * z * z + 34.1973;
|
|
}
|
|
*/
|
|
|
|
// new Parabola for 4wire shift
|
|
double z_to_crossover_rho(double z)
|
|
{
|
|
return 1.65896E-4 * z * z + 4.61626E-8 * z + 32.067;
|
|
}
|
|
|
|
// Global instances
|
|
PW pwinstance;
|
|
TVector3 hitPos;
|
|
double qqqenergy, qqqtimestamp;
|
|
class Event
|
|
{
|
|
public:
|
|
Event(TVector3 p, double e1, double e2, double t1, double t2) : pos(p), Energy1(e1), Energy2(e2), Time1(t1), Time2(t2) {}
|
|
Event(TVector3 p, double e1, double e2, double t1, double t2, int c1, int c2) : pos(p), Energy1(e1), Energy2(e2), Time1(t1), Time2(t2), ch1(c1), ch2(c2) {}
|
|
// Event(TVector3 p, double e1, double e2, double t1, double t2, int c1, int c2, int m1, int m2) : pos(p), Energy1(e1), Energy2(e2), Time1(t1), Time2(t2), ch1(c1), ch2(c2), multi1(m1), multi2(m2) {}
|
|
Event(TVector3 p, double e1, double e2, double t1, double t2, int a, int c, int c1, int c2) : pos(p), Energy1(e1), Energy2(e2), Time1(t1), Time2(t2), Anodech(a), Cathodech(c), ch1(c1), ch2(c2) {}
|
|
|
|
TVector3 pos;
|
|
int ch1 = -1; // int(ch1/16) gives qqq id, ch1%16 gives ring#
|
|
int ch2 = -1; // int(ch2/16) gives qqq id, ch2%16 gives wedge#
|
|
double Energy1 = -1; // Front for QQQ, Anode for PC
|
|
double Energy2 = -1; // Back for QQQ, Cathode for PC
|
|
double Time1 = -1;
|
|
double Time2 = -1;
|
|
int Anodech = -1;
|
|
int Cathodech = -1;
|
|
|
|
// misc elements;
|
|
int multi1 = -1, multi2 = -1;
|
|
};
|
|
|
|
// Calibration globals
|
|
const int MAX_QQQ = 4;
|
|
const int MAX_RING = 16;
|
|
const int MAX_WEDGE = 16;
|
|
double qqqGain[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
|
bool qqqGainValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
|
double qqqCalib[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{0}}};
|
|
bool qqqCalibValid[MAX_QQQ][MAX_RING][MAX_WEDGE] = {{{false}}};
|
|
|
|
double sx3BackGain[24][4][4] = {{{1.}}};
|
|
double sx3FrontGain[24][4] = {{1.}};
|
|
double sx3FrontOffset[24][4] = {{0.}};
|
|
double sx3RightGain[24][4] = {{1.}};
|
|
|
|
// PC Arrays
|
|
double pcSlope[48];
|
|
double pcIntercept[48];
|
|
|
|
HistPlotter *plotter;
|
|
|
|
bool HitNonZero;
|
|
bool sx3ecut;
|
|
bool qqqEcut;
|
|
|
|
bool PCQQQTimeCut = false;
|
|
bool PCSX3TimeCut = false, PCASX3TimeCut = false, PCCSX3TimeCut = false;
|
|
double anodeT = -99999, cathodeT = 99999;
|
|
int anodeIndex = -1, cathodeIndex = -1;
|
|
|
|
void protonAlphaHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events);
|
|
void miscHistograms_oneWire(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<std::vector<std::tuple<int, double, double>>> aClusters);
|
|
void protonMiscHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events);
|
|
void protonMiscHistograms_sx3(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events);
|
|
void PCSX3ClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, 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>>> &cClusters);
|
|
void PCQQQClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, 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>>> &cClusters);
|
|
|
|
void TrackRecon::Begin(TTree * /*tree*/)
|
|
{
|
|
pcfix_func.SetNpx(100000);
|
|
///// ---------Set Environment Variables--------- /////
|
|
TString option = GetOption();
|
|
if (option != "")
|
|
plotter = new HistPlotter(option.Data(), "TFILE");
|
|
else
|
|
plotter = new HistPlotter("Analyzer_SX3.root", "TFILE");
|
|
|
|
if (getenv("reactiondata"))
|
|
{
|
|
reactiondata = std::atoi(getenv("reactiondata"));
|
|
std::cout << "Analyzing dataset as reactiondata" << std::endl;
|
|
}
|
|
|
|
if (getenv("DATASET"))
|
|
dataset = std::string(getenv("DATASET"));
|
|
if (getenv("source_vertex"))
|
|
source_vertex = (double)std::atof(std::string(getenv("source_vertex")).c_str());
|
|
|
|
if (getenv("CO2percent"))
|
|
CO2percent = std::atoi(getenv("CO2percent"));
|
|
std::cout << "CO2 percent set to " << CO2percent << std::endl;
|
|
|
|
if (getenv("source_vertex"))
|
|
source_vertex = (double)std::atof(std::string(getenv("source_vertex")).c_str());
|
|
|
|
if (getenv("DITHER_SIGMA"))
|
|
{
|
|
dither_sigma = std::atof(getenv("DITHER_SIGMA"));
|
|
dither_sigma_c0 = dither_sigma;
|
|
std::cout << "Dither Sigma set to " << dither_sigma << " mm" << std::endl;
|
|
}
|
|
|
|
if (getenv("Gain"))
|
|
Gain = std::atof(getenv("Gain"));
|
|
|
|
// --- A1C1 per-cell linear centre-fold constants: select the static, offline-
|
|
// optimised set for this dataset (cfmin floats with the arbitrary gain, so the
|
|
// values are dataset-specific). Re-fit them with fit_a1c1_cfrac.C on prebuilt
|
|
// data and paste into the a1c1_{cfmin,k}_<dataset> arrays above. ---
|
|
const double *cfmin_src = a1c1_cfmin_17F;
|
|
const double *k_src = a1c1_k_17F;
|
|
const double *cfmin2_src = a1c1_cfmin2_17F;
|
|
const double *k2_src = a1c1_k2_17F;
|
|
a1c1_cfrac_split = 0.25; // 17F: split off the incomplete-integration low band
|
|
a1c1_dead_anode = &a1c1_dead_anode_17F;
|
|
a1c1_dead_cathode = &a1c1_dead_cathode_17F;
|
|
if (dataset == "27Al")
|
|
{
|
|
cfmin_src = a1c1_cfmin_27Al;
|
|
k_src = a1c1_k_27Al;
|
|
cfmin2_src = a1c1_cfmin2_27Al;
|
|
k2_src = a1c1_k2_27Al;
|
|
a1c1_cfrac_split = 0.0; // 27Al: no second band, low band disabled
|
|
a1c1_dead_anode = &a1c1_dead_anode_27Al;
|
|
a1c1_dead_cathode = &a1c1_dead_cathode_27Al;
|
|
}
|
|
for (int i = 0; i < 7; ++i)
|
|
{
|
|
a1c1_cfmin_cell[i] = cfmin_src[i];
|
|
a1c1_k_cell[i] = k_src[i];
|
|
a1c1_cfmin2_cell[i] = cfmin2_src[i];
|
|
a1c1_k2_cell[i] = k2_src[i];
|
|
}
|
|
std::cout << "A1C1 per-cell constants: using static " << (dataset.empty() ? "(default 17F)" : dataset)
|
|
<< " set; low-band split cfrac<" << a1c1_cfrac_split << std::endl;
|
|
|
|
pwinstance.ConstructGeo();
|
|
|
|
for (int i = 0; i < 48; i++)
|
|
{
|
|
pcSlope[i] = 1.0;
|
|
pcIntercept[i] = 0.0;
|
|
}
|
|
|
|
// ------------Load PC Calibrations-------------- ///
|
|
std::ifstream inputFile("slope_intercept_results_" + dataset + ".dat");
|
|
if (inputFile.is_open())
|
|
{
|
|
std::string line;
|
|
int index;
|
|
double slope, intercept;
|
|
while (std::getline(inputFile, line))
|
|
{
|
|
std::stringstream ss(line);
|
|
ss >> index >> slope >> intercept;
|
|
if (index >= 0 && index <= 47)
|
|
{
|
|
pcSlope[index] = slope;
|
|
pcIntercept[index] = intercept;
|
|
}
|
|
}
|
|
inputFile.close();
|
|
}
|
|
else
|
|
{
|
|
std::cerr << "Error opening slope_intercept.dat" << std::endl;
|
|
}
|
|
|
|
// ------------Load QQQ Calibrations-------------- ///
|
|
{
|
|
std::string filename = "qqq_GainMatch.dat";
|
|
std::ifstream infile(filename);
|
|
if (infile.is_open())
|
|
{
|
|
int det, ring, wedge;
|
|
double gainw, gainr;
|
|
while (infile >> det >> wedge >> ring >> gainw >> gainr)
|
|
{
|
|
qqqGain[det][wedge][ring] = gainw;
|
|
qqqGainValid[det][wedge][ring] = (gainw > 0);
|
|
// std::cout << "QQQ Gain Loaded: Det " << det << " Ring " << ring << " Wedge " << wedge << " GainW " << gainw << " GainR " << gainr << std::endl;
|
|
}
|
|
infile.close();
|
|
}
|
|
}
|
|
|
|
{
|
|
std::string filename = "qqq_Calib.dat";
|
|
std::ifstream infile(filename);
|
|
if (infile.is_open())
|
|
{
|
|
int det, ring, wedge;
|
|
double slope;
|
|
while (infile >> det >> wedge >> ring >> slope)
|
|
{
|
|
qqqCalib[det][wedge][ring] = slope;
|
|
qqqCalibValid[det][wedge][ring] = (slope > 0);
|
|
// std::cout << "QQQ Calib Loaded: Det " << det << " Ring " << ring << " Wedge " << wedge << " Slope " << slope << std::endl;
|
|
}
|
|
infile.close();
|
|
}
|
|
}
|
|
|
|
// ------------Load SX3 Calibrations--------------- ///
|
|
{
|
|
std::ifstream infile("sx3cal/" + dataset + "/backgains.dat");
|
|
std::string temp;
|
|
int backpos, frontpos, clkpos;
|
|
if (infile.is_open())
|
|
while (infile >> clkpos >> temp >> frontpos >> temp >> backpos >> sx3BackGain[clkpos][frontpos][backpos])
|
|
; // std::cout << sx3BackGain[clkpos][frontpos][backpos] << std::endl;
|
|
infile.close();
|
|
|
|
infile.open("sx3cal/" + dataset + "/frontgains.dat");
|
|
if (infile.is_open())
|
|
while (infile >> clkpos >> temp >> temp >> frontpos >> sx3FrontOffset[clkpos][frontpos] >> sx3FrontGain[clkpos][frontpos])
|
|
; // std::cout << sx3FrontOffset[clkpos][frontpos] << " " << sx3FrontGain[clkpos][frontpos] << std::endl;
|
|
infile.close();
|
|
|
|
infile.open("sx3cal/" + dataset + "/rightgains.dat");
|
|
if (infile.is_open())
|
|
while (infile >> clkpos >> frontpos >> temp >> sx3RightGain[clkpos][frontpos])
|
|
{
|
|
sx3RightGain[clkpos][frontpos] = TMath::Abs(sx3RightGain[clkpos][frontpos]);
|
|
}
|
|
infile.close();
|
|
}
|
|
|
|
// ------------- ELOSS Correction read in from tables -------------
|
|
|
|
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
|
|
{
|
|
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");
|
|
}
|
|
cm_to_MeV = new TGraph(MeV_to_cm->GetN(), MeV_to_cm->GetY(), MeV_to_cm->GetX());
|
|
cm_to_MeVp = new TGraph(MeV_to_cm_p->GetN(), MeV_to_cm_p->GetY(), MeV_to_cm_p->GetX());
|
|
cm_to_MeV_27Al = new TGraph(MeV_to_cm_27Al->GetN(), MeV_to_cm_27Al->GetY(), MeV_to_cm_27Al->GetX());
|
|
cm_to_MeV_17F = new TGraph(MeV_to_cm_17F->GetN(), MeV_to_cm_17F->GetY(), MeV_to_cm_17F->GetX());
|
|
}
|
|
|
|
Bool_t TrackRecon::Process(Long64_t entry)
|
|
{
|
|
hitPos.Clear();
|
|
qqqenergy = -1;
|
|
qqqtimestamp = -1;
|
|
HitNonZero = false;
|
|
PCQQQTimeCut = false;
|
|
PCSX3TimeCut = false;
|
|
PCASX3TimeCut = false;
|
|
PCCSX3TimeCut = false;
|
|
anodeT = -99999;
|
|
cathodeT = 99999;
|
|
anodeIndex = -1;
|
|
cathodeIndex = -1;
|
|
bool qqq1000cut = false;
|
|
b_sx3Multi->GetEntry(entry);
|
|
b_sx3ID->GetEntry(entry);
|
|
b_sx3Ch->GetEntry(entry);
|
|
b_sx3E->GetEntry(entry);
|
|
b_sx3T->GetEntry(entry);
|
|
b_qqqMulti->GetEntry(entry);
|
|
b_qqqID->GetEntry(entry);
|
|
b_qqqCh->GetEntry(entry);
|
|
b_qqqE->GetEntry(entry);
|
|
b_qqqT->GetEntry(entry);
|
|
b_pcMulti->GetEntry(entry);
|
|
b_pcID->GetEntry(entry);
|
|
b_pcCh->GetEntry(entry);
|
|
b_pcE->GetEntry(entry);
|
|
b_pcT->GetEntry(entry);
|
|
if (dataset == "17F" && reactiondata)
|
|
{
|
|
b_miscMulti->GetEntry(entry);
|
|
b_miscID->GetEntry(entry);
|
|
b_miscCh->GetEntry(entry);
|
|
b_miscE->GetEntry(entry);
|
|
b_miscT->GetEntry(entry);
|
|
b_miscTf->GetEntry(entry);
|
|
}
|
|
double timecut_low = getenv("timecut_low") ? std::atof(getenv("timecut_low")) : 0;
|
|
double timecut_high = getenv("timecut_high") ? std::atof(getenv("timecut_high")) : 1e15;
|
|
|
|
if (pc.multi > 0)
|
|
{
|
|
for (int i = 0; i < pc.multi; i++)
|
|
{
|
|
if (pc.t[i] * 1e-9 < timecut_high && pc.t[i] * 1e-9 >= timecut_low)
|
|
{
|
|
// good, keep it moving
|
|
}
|
|
else
|
|
{
|
|
return kTRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
sx3.CalIndex();
|
|
qqq.CalIndex();
|
|
pc.CalIndex();
|
|
|
|
std::vector<Event> SX3_Events;
|
|
if (sx3.multi > 1)
|
|
{
|
|
std::array<sx3det, 24> Fsx3;
|
|
// std::cout << "-----" << std::endl;
|
|
bool found_upstream_sx3 = 0;
|
|
for (int i = 0; i < sx3.multi; i++)
|
|
{
|
|
int id = sx3.id[i];
|
|
if (id >= 12)
|
|
continue;
|
|
if (sx3.ch[i] >= 8)
|
|
{
|
|
int sx3ch = sx3.ch[i] - 8;
|
|
sx3ch = (sx3ch + 3) % 4;
|
|
if (id >= 12)
|
|
{
|
|
found_upstream_sx3 = 1;
|
|
// std::cout << Form("f%d(",id) << sx3ch << "," << sx3.e[i] << ") " << std::flush;
|
|
}
|
|
// if(sx3ch==0 || sx3ch==3) continue;
|
|
double value = sx3.e[i];
|
|
int gch = sx3.id[i] * 4 + (sx3.ch[i] - 8);
|
|
if (id < 12)
|
|
Fsx3.at(id).fillevent("BACK", sx3ch, value);
|
|
Fsx3.at(id).ts = static_cast<double>(sx3.t[i]);
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("sx3backs_all_raw", 100, 0, 100, 800, 0, 4096, gch, sx3.e[i]);
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
int sx3ch = sx3.ch[i] / 2;
|
|
double value = sx3.e[i];
|
|
if (id >= 12)
|
|
{
|
|
found_upstream_sx3 = 1;
|
|
// std::cout << Form("b%d(",id) << sx3ch << "," << value << ") " << std::flush;
|
|
}
|
|
if (sx3.ch[i] % 2 == 0)
|
|
{
|
|
Fsx3.at(id).fillevent("FRONT_L", sx3ch, value * sx3RightGain[id][sx3ch]);
|
|
}
|
|
else
|
|
{
|
|
Fsx3.at(id).fillevent("FRONT_R", sx3ch, value);
|
|
}
|
|
}
|
|
} // end for (i in sx3.multi)
|
|
// if(found_upstream_sx3) std::cout << std::endl;
|
|
|
|
for (int id = 0; id < 24; id++)
|
|
{
|
|
// std::cout << id << " " << Fsx3.at(id).valid_front_chans.size() << " " << Fsx3.at(id).valid_back_chans.size() << std::endl;;
|
|
try
|
|
{
|
|
Fsx3.at(id).validate();
|
|
}
|
|
catch (std::exception exc)
|
|
{
|
|
std::cout << "oops! anyway " << std::endl;
|
|
continue;
|
|
}
|
|
auto det = Fsx3.at(id);
|
|
bool no_charge_sharing_strict = det.valid_front_chans.size() == 1 && det.valid_back_chans.size() == 1;
|
|
if (det.valid)
|
|
{
|
|
// std::cout << det.frontEL << " " << det.frontEL*sx3RightGain[id][det.stripF] << std::endl;
|
|
// plotter->Fill2D("be_vs_x_sx3_id_"+std::to_string(id)+"_f"+std::to_string(det.stripF)+"_b"+std::to_string(det.stripB),200,-1,1,800,0,8192,det.frontX,det.backE,"evsx");
|
|
// plotter->Fill2D("unmatched_be_vs_x_sx3_id_" + std::to_string(id), 200, -1, 1, 800, 0, 4096, det.frontX, det.backE, "evsx");
|
|
// plotter->Fill2D("unmatched_be_vs_x_sx3", 200, -1, 1, 800, 0, 4096, det.frontX, det.backE, "evsx");
|
|
// plotter->Fill2D("matched_be_vs_x_sx3", 200, -60, 60, 800, 0, 8192, det.frontX * sx3FrontGain[id][det.stripF] + sx3FrontOffset[id][det.stripF], det.backE * sx3BackGain[id][det.stripF][det.stripB], "evsx");
|
|
// plotter->Fill2D("matched_be_vs_x_sx3_id_" + std::to_string(id), 200, -60, 60, 800, 0, 8192, det.frontX * sx3FrontGain[id][det.stripF] + sx3FrontOffset[id][det.stripF], det.backE * sx3BackGain[id][det.stripF][det.stripB], "evsx");
|
|
|
|
// plotter->Fill2D("matched_be_vs_x_sx3_id_" + std::to_string(id) + "_f" + std::to_string(det.stripF), 200, -60, 60, 800, 0, 8192,
|
|
// det.frontX * sx3FrontGain[id][det.stripF] + sx3FrontOffset[id][det.stripF], det.backE * sx3BackGain[id][det.stripF][det.stripB], "evsx_matched");
|
|
// plotter->Fill2D("fe_vs_x_sx3_id_"+std::to_string(id)+"_f"+std::to_string(det.stripF)+"_"+std::to_string(det.stripB),200,-1,1,800,0,4096,det.frontX,det.backE,"evsx");
|
|
// plotter->Fill2D("l_vs_r_sx3_id_" + std::to_string(id) + "_f" + std::to_string(det.stripF), 800, 0, 4096, 800, 0, 4096, det.frontEL, det.frontER, "l_vs_r");
|
|
}
|
|
if (det.valid && (id == 9 || id == 7 || id == 1 || id == 3) && det.stripF != DEFAULT_NULL && det.stripB != DEFAULT_NULL)
|
|
{
|
|
double z = det.frontX * sx3FrontGain[id][det.stripF] + sx3FrontOffset[id][det.stripF];
|
|
z = z + (75.0 / 2.0) - 3.0; // convert local sx3z to detector global coordinate system as indicated by measurements.
|
|
// Note that this will be different for the upstream barrel, when it gets implemented
|
|
double backE = det.backE * sx3BackGain[id][det.stripF][det.stripB];
|
|
// if(backE<2000) continue;
|
|
det.stripF = 3 - det.stripF;
|
|
|
|
double alpha_n = TMath::ATan2((2 * det.stripF - 3) * 40.30, 8.0 * 88.0 * TMath::Cos(15.0 * M_PI / 180.0)) * 180. / M_PI; // angle subtended w.r.t the radial perpendicular bisector of each sx3
|
|
double beta_n = 15.0 + alpha_n; // how much to add per strip to the starting position? this is the angle w.r.t an edge of the sx3, the above values run as (-10.08deg, -3.39deg, 3.39deg, 10.08deg)
|
|
double phi_n = ((-id + 0.5) * 30 + beta_n);
|
|
phi_n += 45;
|
|
double rho_at_strip = 88.0 / TMath::Cos(alpha_n * M_PI / 180.0); //*TMath::Cos(15.0*M_PI/180.0) if the edge-length is 88mm
|
|
|
|
// if(getenv("flip180")) {
|
|
// if(std::string(getenv("flip180"))=="1") {
|
|
// if(dataset=="17F")
|
|
// phi_n+=180;//run 37 in 17F-->
|
|
// }
|
|
//}
|
|
phi_n *= M_PI / 180.; // starting-position phi + strip contribution
|
|
// Event sx3ev(TVector3(88.0*TMath::Cos(phi_n),88.0*TMath::Sin(phi_n),z),backE*0.001,-1,det.ts,-1,det.stripB+4*id,det.stripF+4*id);
|
|
Event sx3ev(TVector3(rho_at_strip * TMath::Cos(phi_n), rho_at_strip * TMath::Sin(phi_n), z), backE * 0.001, -1, det.ts, -1, det.stripB + 4 * id, det.stripF + 4 * id);
|
|
SX3_Events.push_back(sx3ev);
|
|
plotter->Fill2D("sx3backs_gm", 100, 0, 100, 800, 0, 8192, det.stripB + 4 * id, backE);
|
|
plotter->Fill1D("sx3backs_calib", 800, 0, 8192, backE);
|
|
|
|
// plotter->Fill2D("SX3CartesianPlot", 200, -100, 100, 200, -100, 100, 88.0*TMath::Cos(phi_n),88.0*TMath::Sin(phi_n), "hCalSX3");
|
|
plotter->Fill2D("SX3CartesianPlot" + std::to_string(id), 200, -100, 100, 200, -100, 100, 88.0 * TMath::Cos(phi_n), 88.0 * TMath::Sin(phi_n), "hCalSX3");
|
|
}
|
|
#ifdef RAW_HISTOS
|
|
if (det.valid && det.stripF != DEFAULT_NULL && det.stripB != DEFAULT_NULL)
|
|
{
|
|
plotter->Fill2D("sx3backs_raw", 100, 0, 100, 800, 0, 8192, det.stripB + 4 * id, det.backE);
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
// return kTRUE;
|
|
// QQQ Processing
|
|
|
|
int qqqCount = 0;
|
|
int qqqAdjCh = 0;
|
|
// REMOVE WHEN RERUNNING USING THE NEW CALIBRATION FILE
|
|
std::vector<Event> QQQ_Events, PC_Events;
|
|
std::vector<Event> QQQ_Events_Raw, PC_Events_Raw;
|
|
std::vector<Event> QQQ_Events2; // clustering done
|
|
|
|
// Check for muliplt hits in the same QQQ channel
|
|
|
|
// -------Check Start ------
|
|
|
|
std::unordered_map<int, std::tuple<int, int, double, double>> qvecr[4], qvecw[4];
|
|
if (qqq.multi > 1)
|
|
{
|
|
// if(qqq.multi>=3) std::cout << "-----" << std::endl;
|
|
for (int i = 0; i < qqq.multi; i++)
|
|
{
|
|
if (qqq.ch[i] / 16)
|
|
{
|
|
if (qvecr[qqq.id[i]].find(qqq.ch[i]) != qvecr[qqq.id[i]].end())
|
|
std::cout << "mayday!" << std::endl;
|
|
qvecr[qqq.id[i]][qqq.ch[i]] = std::tuple(qqq.id[i], qqq.ch[i], qqq.e[i], qqq.t[i]);
|
|
}
|
|
else
|
|
{
|
|
if (qvecw[qqq.id[i]].find(qqq.ch[i]) != qvecw[qqq.id[i]].end())
|
|
std::cout << "mayday!" << std::endl;
|
|
qvecw[qqq.id[i]][qqq.ch[i]] = std::tuple(qqq.id[i], qqq.ch[i], qqq.e[i], qqq.t[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// -------Check End ------
|
|
|
|
bool PCAQQQTimeCut = false;
|
|
bool PCCQQQTimeCut = false;
|
|
for (int i = 0; i < qqq.multi; i++)
|
|
{
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("QQQ_Index_Vs_Energy", 16 * 8, 0, 16 * 8, 2000, 0, 8000, qqq.index[i], qqq.e[i], "hRawQQQ");
|
|
|
|
for (int j = 0; j < qqq.multi; j++)
|
|
{
|
|
if (j == i)
|
|
continue;
|
|
plotter->Fill2D("QQQ_Coincidence_Matrix", 16 * 8, 0, 16 * 8, 16 * 8, 0, 16 * 8, qqq.index[i], qqq.index[j], "hRawQQQ");
|
|
}
|
|
|
|
for (int k = 0; k < pc.multi; k++)
|
|
{
|
|
if (pc.index[k] < 24 && pc.e[k] > 10)
|
|
{
|
|
plotter->Fill2D("QQQ_Vs_Anode_Energy", 400, 0, 4000, 1000, 0, 16000, qqq.e[i], pc.e[k], "hRawQQQ");
|
|
plotter->Fill2D("QQQ_Vs_PC_Index", 16 * 8, 0, 16 * 8, 24, 0, 24, qqq.index[i], pc.index[k], "hRawQQQ");
|
|
}
|
|
else if (pc.index[k] >= 24 && pc.e[k] > 10)
|
|
{
|
|
plotter->Fill2D("QQQ_Vs_Cathode_Energy", 400, 0, 4000, 1000, 0, 16000, qqq.e[i], pc.e[k], "hRawQQQ");
|
|
}
|
|
}
|
|
#endif
|
|
for (int j = i + 1; j < qqq.multi; j++)
|
|
{
|
|
if (qqq.id[i] == qqq.id[j])
|
|
{
|
|
qqqCount++;
|
|
|
|
int chWedge = -1;
|
|
int chRing = -1;
|
|
double eWedge = 0.0;
|
|
double eWedgeMeV = 0.0;
|
|
double eRing = 0.0;
|
|
double eRingMeV = 0.0;
|
|
double tRing = 0.0;
|
|
double tWedge = 0.0;
|
|
|
|
if (qqq.ch[i] < 16 && qqq.ch[j] >= 16 && qqqGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16])
|
|
{
|
|
chWedge = qqq.ch[i];
|
|
eWedge = qqq.e[i] * qqqGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
|
chRing = qqq.ch[j] - 16;
|
|
eRing = qqq.e[j];
|
|
tRing = static_cast<double>(qqq.t[j]);
|
|
tWedge = static_cast<double>(qqq.t[i]);
|
|
}
|
|
else if (qqq.ch[j] < 16 && qqq.ch[i] >= 16 && qqqGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16])
|
|
{
|
|
chWedge = qqq.ch[j];
|
|
eWedge = qqq.e[j] * qqqGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16];
|
|
chRing = qqq.ch[i] - 16;
|
|
eRing = qqq.e[i];
|
|
tRing = static_cast<double>(qqq.t[i]);
|
|
tWedge = static_cast<double>(qqq.t[j]);
|
|
}
|
|
else
|
|
continue;
|
|
|
|
plotter->Fill1D("Wedgetime_Vs_Ringtime", 100, -1000, 1000, tWedge - tRing, "hTiming");
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("RingE_vs_Index", 16 * 4, 0, 16 * 4, 1000, 0, 16000, chRing + qqq.id[i] * 16, eRing, "hRawQQQ");
|
|
plotter->Fill2D("WedgeE_vs_Index", 16 * 4, 0, 16 * 4, 1000, 0, 16000, chWedge + qqq.id[i] * 16, eWedge, "hRawQQQ");
|
|
#endif
|
|
|
|
if (qqqCalibValid[qqq.id[i]][chWedge][chRing])
|
|
{
|
|
eWedgeMeV = eWedge * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
|
eRingMeV = eRing * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
|
|
|
if (eRingMeV / eWedgeMeV > 3.0 || eRingMeV / eWedgeMeV < 1.0 / 3.0)
|
|
continue;
|
|
// if(eRingMeV<1.2 || eWedgeMeV<1.2) continue;
|
|
|
|
// double theta = 2 * TMath::Pi() * (-qqq.id[i] * 16 + (15 - chWedge) + 0.5) / (16 * 4);
|
|
|
|
double theta = (M_PI / 180.) * (-90 * qqq.id[i] + (87. / 16.) * ((15 - chWedge) + 0.5) + 3.0);
|
|
double rho = 50. + (50. / 16.) * (chRing + 0.5); //"?"
|
|
// z used to be 75+30+23=128
|
|
// we found a 12mm shift towards the vertex later --> 116
|
|
Event qqqevent(TVector3(rho * TMath::Cos(theta), rho * TMath::Sin(theta), qqq_z), eRingMeV, eWedgeMeV, tRing, tWedge, chRing + qqq.id[i] * 16, chWedge + qqq.id[i] * 16);
|
|
Event qqqeventr(TVector3(rho * TMath::Cos(theta), rho * TMath::Sin(theta), qqq_z), eRing, eWedge, tRing, tWedge, chRing + qqq.id[i] * 16, chWedge + qqq.id[i] * 16);
|
|
|
|
QQQ_Events.push_back(qqqevent);
|
|
QQQ_Events_Raw.push_back(qqqeventr);
|
|
plotter->Fill2D("WedgeE_Vs_RingECal_selected", 1000, 0, 10, 1000, 0, 10, eWedgeMeV, eRingMeV, "hCalQQQ");
|
|
|
|
plotter->Fill1D("QQQECal", 2048, 0, 10, eRingMeV);
|
|
plotter->Fill1D("QQQECal", 2048, 0, 10, eWedgeMeV);
|
|
|
|
const int channelsPerDetector = MAX_RING + MAX_WEDGE;
|
|
int globalRingChannel = chRing + (qqq.id[i] * channelsPerDetector);
|
|
int globalWedgeChannel = chWedge + (qqq.id[i] * channelsPerDetector) + MAX_RING;
|
|
|
|
// Fill the histograms
|
|
plotter->Fill2D("QQQ_CalibratedE_vs_Ch", 128, 0, 128, 1000, 0, 20, globalRingChannel, eRingMeV, "hCalQQQ");
|
|
plotter->Fill2D("QQQ_CalibratedE_vs_Ch", 128, 0, 128, 1000, 0, 20, globalWedgeChannel, eWedgeMeV, "hCalQQQ");
|
|
|
|
plotter->Fill2D("QQQCartesianPlot", 200, -100, 100, 200, -100, 100, rho * TMath::Cos(theta), rho * TMath::Sin(theta), "hCalQQQ");
|
|
plotter->Fill2D("QQQCartesianPlot" + std::to_string(qqq.id[i]), 200, -100, 100, 200, -100, 100, rho * TMath::Cos(theta), rho * TMath::Sin(theta), "hCalQQQ");
|
|
plotter->Fill2D("PC_XY_Projection_QQQ" + std::to_string(qqq.id[i]), 400, -100, 100, 400, -100, 100, rho * TMath::Cos(theta), rho * TMath::Sin(theta), "hPCQQQ");
|
|
}
|
|
else
|
|
continue;
|
|
|
|
for (int k = 0; k < pc.multi; k++)
|
|
{
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("RingCh_vs_Anode_Index", 16 * 4, 0, 16 * 4, 24, 0, 24, chRing + qqq.id[i] * 16, pc.index[k], "hRawQQQ");
|
|
plotter->Fill2D("WedgeCh_vs_Anode_Index", 16 * 4, 0, 16 * 4, 24, 0, 24, chWedge + qqq.id[i] * 16, pc.index[k], "hRawQQQ");
|
|
plotter->Fill2D("WedgeCh_vs_Anode_Index" + std::to_string(qqq.id[i]), 16 * 4, 0, 16 * 4, 24, 0, 24, chWedge + qqq.id[i] * 16, pc.index[k], "hRawQQQ");
|
|
plotter->Fill2D("RingCh_vs_Cathode_Index", 16 * 4, 0, 16 * 4, 24, 24, 48, chRing + qqq.id[i] * 16, pc.index[k], "hRawQQQ");
|
|
plotter->Fill2D("WedgeCh_vs_Cathode_Index", 16 * 4, 0, 16 * 4, 24, 24, 48, chWedge + qqq.id[i] * 16, pc.index[k], "hRawQQQ");
|
|
#endif
|
|
if (pc.index[k] < 24 && pc.e[k] > 10)
|
|
{
|
|
plotter->Fill2D("Timing_Difference_QQQ_PC", 500, -2000, 2000, 16, 0, 16, tRing - static_cast<double>(pc.t[k]), chRing, "hTiming");
|
|
plotter->Fill2D("DelT_Vs_QQQRingECal", 500, -2000, 2000, 1000, 0, 10, tRing - static_cast<double>(pc.t[k]), eRingMeV, "hTiming");
|
|
// if (tRing - static_cast<double>(pc.t[k]) < -150) // proton tests, 27Al
|
|
if (tRing - static_cast<double>(pc.t[k]) < -150) // proton tests, 27Al
|
|
{
|
|
PCAQQQTimeCut = true;
|
|
plotter->Fill2D("CalibratedQQQEvsPCE_R", 1000, 0, 10, 2000, 0, 30000, eRingMeV, pc.e[k], "hPCQQQ");
|
|
plotter->Fill2D("CalibratedQQQEvsPCE_W", 1000, 0, 10, 2000, 0, 30000, eWedgeMeV, pc.e[k], "hPCQQQ");
|
|
}
|
|
}
|
|
|
|
if (pc.index[k] >= 24 && pc.e[k] > 10)
|
|
{
|
|
if (tRing - static_cast<double>(pc.t[k]) < -200)
|
|
PCCQQQTimeCut = true;
|
|
// if (tRing - static_cast<double>(pc.t[k]) > 200) PCCQQQTimeCut = true;
|
|
plotter->Fill2D("Timing_Difference_QQQ_PC_Cathode", 500, -2000, 2000, 16, 0, 16, tRing - static_cast<double>(pc.t[k]), chRing, "hTiming");
|
|
}
|
|
} // end of pc k loop
|
|
|
|
if (!HitNonZero)
|
|
{
|
|
// double theta = -TMath::Pi() / 2 + 2 * TMath::Pi() / 16 / 4. * (qqq.id[i] * 16 + chWedge + 0.5);
|
|
// double rho = 50. + (50. / 16.) * (chRing + 0.5); //"?"
|
|
double theta = 2 * TMath::Pi() * (-qqq.id[i] * 16 + (15 - chWedge) + 0.5) / (16 * 4);
|
|
double rho = 50. + (50. / 16.) * (chRing + 0.5); //"?"
|
|
double x = rho * TMath::Cos(theta);
|
|
double y = rho * TMath::Sin(theta);
|
|
hitPos.SetXYZ(x, y, qqq_z);
|
|
qqqenergy = eRingMeV;
|
|
qqqtimestamp = tRing;
|
|
HitNonZero = true;
|
|
}
|
|
} // if j==i
|
|
} // j loop end
|
|
} // i loop end
|
|
|
|
PCQQQTimeCut = PCAQQQTimeCut && PCCQQQTimeCut;
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill1D("QQQ_Multiplicity", 10, 0, 10, qqqCount, "hRawQQQ");
|
|
#endif
|
|
aWireEvents.clear();
|
|
aWireEvents.reserve(24);
|
|
cWireEvents.clear();
|
|
cWireEvents.reserve(24);
|
|
// PC Gain Matching and Filling
|
|
for (int i = 0; i < pc.multi; i++)
|
|
{
|
|
// std::cout << pc.index[i] << " " << pc.e[i] << " " << std::endl;
|
|
#ifdef RAW_HISTOS
|
|
if (pc.e[i] > 10)
|
|
{
|
|
plotter->Fill2D("PC_Index_Vs_Energy", 48, 0, 48, 2000, 0, 30000, pc.index[i], static_cast<double>(pc.e[i]), "hRawPC");
|
|
}
|
|
#endif
|
|
|
|
if (pc.index[i] < 48)
|
|
{
|
|
pc.e[i] = pcSlope[pc.index[i]] * pc.e[i] + pcIntercept[pc.index[i]];
|
|
plotter->Fill2D("PC_Index_VS_GainMatched_Energy", 48, 0, 48, 2000, 0, 30000, pc.index[i], pc.e[i], "hGMPC");
|
|
}
|
|
if (pc.e[i] > 5)
|
|
{
|
|
if (pc.index[i] < 24)
|
|
{
|
|
anodeT = static_cast<double>(pc.t[i]);
|
|
anodeIndex = pc.index[i];
|
|
aWireEvents[pc.index[i]] = std::tuple(pc.index[i], pc.e[i], static_cast<double>(pc.t[i]));
|
|
}
|
|
else
|
|
{
|
|
cathodeT = static_cast<double>(pc.t[i]);
|
|
cathodeIndex = pc.index[i] - 24;
|
|
cWireEvents[pc.index[i] - 24] = std::tuple(pc.index[i] - 24, pc.e[i], static_cast<double>(pc.t[i]));
|
|
}
|
|
}
|
|
|
|
if (anodeT != -99999 && cathodeT != 99999)
|
|
{
|
|
for (int j = 0; j < qqq.multi; j++)
|
|
{
|
|
plotter->Fill1D("PC_Time_qqq", 200, -2000, 2000, anodeT - cathodeT, "hTiming");
|
|
plotter->Fill2D("PC_Time_Vs_QQQ_ch", 200, -2000, 2000, 16 * 8, 0, 16 * 8, anodeT - cathodeT, qqq.ch[j], "hTiming");
|
|
plotter->Fill2D("PC_Time_vs_AIndex_qqq", 200, -2000, 2000, 24, 0, 24, anodeT - cathodeT, anodeIndex, "hTiming");
|
|
plotter->Fill2D("PC_Time_vs_CIndex_qqq", 200, -2000, 2000, 24, 0, 24, anodeT - cathodeT, cathodeIndex, "hTiming");
|
|
// plotter->Fill1D("PC_Time_A" + std::to_string(anodeIndex) + "_C" + std::to_string(cathodeIndex), 200, -1000, 1000, anodeT - cathodeT, "TimingPC");
|
|
}
|
|
|
|
for (int j = 0; j < sx3.multi; j++)
|
|
{
|
|
plotter->Fill1D("PC_Time_sx3", 200, -2000, 2000, anodeT - cathodeT, "hTiming");
|
|
// plotter->Fill2D("PC_Time_Vs_SX3_ch", 200, -2000, 2000, 16 * 8, 0, 16 * 8, anodeT - cathodeT, sx3.ch[j], "hTiming");
|
|
plotter->Fill2D("PC_Time_vs_AIndex_sx3", 200, -2000, 2000, 24, 0, 24, anodeT - cathodeT, anodeIndex, "hTiming");
|
|
plotter->Fill2D("PC_Time_vs_CIndex_sx3", 200, -2000, 2000, 24, 0, 24, anodeT - cathodeT, cathodeIndex, "hTiming");
|
|
}
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
bool TCC = sx3event.Time1 - cathodeT < 0;
|
|
bool TCA = sx3event.Time1 - anodeT < 0;
|
|
// plotter->Fill2D("sx3_z_phi_awire"+std::to_string(anodeIndex)+"_TC"+std::to_string(TCA), 400,-100,100, 200, -200,200,sx3event.pos.Z(), sx3event.pos.Phi()*180/M_PI );
|
|
// plotter->Fill2D("sx3_z_phi_cwire"+std::to_string(cathodeIndex)+"_TC"+std::to_string(TCC), 400,-100,100, 200, -200,200,sx3event.pos.Z(), sx3event.pos.Phi()*180/M_PI );
|
|
}
|
|
|
|
plotter->Fill1D("PC_Time", 200, -2000, 2000, anodeT - cathodeT, "hTiming");
|
|
}
|
|
|
|
for (int j = i + 1; j < pc.multi; j++)
|
|
{
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("PC_Coincidence_Matrix", 48, 0, 48, 48, 0, 48, pc.index[i], pc.index[j], "hRawPC");
|
|
plotter->Fill2D("PC_Coincidence_Matrix_anodeMinusCathode_lt_-200_" + std::to_string(anodeT - cathodeT < -200), 48, 0, 48, 48, 0, 48, pc.index[i], pc.index[j], "hRawPC");
|
|
#endif
|
|
plotter->Fill2D("Anode_V_Anode", 24, 0, 24, 24, 0, 24, pc.index[i], pc.index[j], "hGMPC");
|
|
}
|
|
}
|
|
anodeHits.clear();
|
|
cathodeHits.clear();
|
|
corrcatMax.clear();
|
|
|
|
for (int i = 0; i < pc.multi; i++)
|
|
{
|
|
// if (pc.e[i] > 100)
|
|
{
|
|
if (pc.index[i] < 24)
|
|
{
|
|
anodeHits.push_back(std::pair<int, double>(pc.index[i], pc.e[i]));
|
|
}
|
|
else if (pc.index[i] >= 24)
|
|
{
|
|
cathodeHits.push_back(std::pair<int, double>(pc.index[i] - 24, pc.e[i]));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::sort(anodeHits.begin(), anodeHits.end(), [](std::pair<int, double> a, std::pair<int, double> b)
|
|
{ return a.first < b.first; });
|
|
|
|
std::sort(cathodeHits.begin(), cathodeHits.end(), [](std::pair<int, double> a, std::pair<int, double> b)
|
|
{ return a.first < b.first; });
|
|
|
|
// clusters = collection of (collection of wires) where each wire is (index, energy, timestamp)
|
|
std::vector<std::vector<std::tuple<int, double, double>>> aClusters = pwinstance.Make_Clusters(aWireEvents);
|
|
std::vector<std::vector<std::tuple<int, double, double>>> cClusters = pwinstance.Make_Clusters(cWireEvents);
|
|
|
|
std::vector<std::pair<double, double>> sumE_AC;
|
|
for (auto aCluster : aClusters)
|
|
{
|
|
for (auto cCluster : cClusters)
|
|
{
|
|
if (aCluster.size() == 0)
|
|
continue;
|
|
if (cCluster.size() == 0)
|
|
continue;
|
|
// both have at least 1, here. Keep the a1, c1 events
|
|
auto [crossover, alpha, apSumE, cpSumE, apMaxE, cpMaxE, apTSMaxE, cpTSMaxE] = pwinstance.FindCrossoverProperties(aCluster, cCluster);
|
|
if (alpha != 9999999 && apSumE != -1)
|
|
{
|
|
// Event PCEvent(crossover,apMaxE,cpMaxE,apTSMaxE,cpTSMaxE);
|
|
// Event PCEvent(crossover,apSumE,cpSumE,apTSMaxE,cpTSMaxE);
|
|
Event PCEvent(crossover, apSumE, cpMaxE, apTSMaxE, cpTSMaxE); // run12 shows cathode-max and anode-sum provide best dE signals.
|
|
// std::cout << apSumE << " " << crossover.Perp() << " " << apMaxE << " " << apTSMaxE << std::endl;
|
|
PCEvent.multi1 = aCluster.size();
|
|
PCEvent.multi2 = cCluster.size();
|
|
PCEvent.Anodech = std::get<0>(aCluster[0]);
|
|
PCEvent.Cathodech = std::get<0>(cCluster[0]);
|
|
PC_Events.push_back(PCEvent);
|
|
sumE_AC.push_back(std::pair(apSumE, cpSumE));
|
|
}
|
|
else
|
|
{
|
|
; // std::cout << "AAAA " << std::endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
//////Timing stuff for F data
|
|
|
|
TRandom3 rnd;
|
|
rnd.SetSeed(); // random seed set
|
|
if (dataset == "17F" && reactiondata)
|
|
{
|
|
int ctr = 0;
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
double ts_rf = -987654321;
|
|
double ts_needle = -987654321;
|
|
double ts_mcp = -987654321;
|
|
double ts_qqq = static_cast<double>(qqqevent.Time1) + (rnd.Uniform(16.0) - 8.0);
|
|
bool found_rf = false;
|
|
bool found_mcp = false;
|
|
bool found_needle = false;
|
|
bool qqq_inner_ring = (qqqevent.ch1 % 16) < 8;
|
|
for (int j = 0; j < misc.multi; j++)
|
|
{
|
|
plotter->Fill1D("channels_misc_qqq", 20, 0, 20, misc.ch[j], "misc");
|
|
if (misc.ch[j] == 2)
|
|
{ // Needle
|
|
plotter->Fill2D("needle_vs_qqqE", 800, 0, 16384, 800, 0, 10, misc.e[j], qqqevent.Energy1, "misc");
|
|
ts_needle = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_needle = 1;
|
|
plotter->Fill1D("dt_qqq_needle", 800, -2000, 2000, ts_qqq - ts_needle, "misc");
|
|
}
|
|
if (misc.ch[j] == 3)
|
|
{ // RF
|
|
ts_rf = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_rf = 1;
|
|
plotter->Fill1D("dt_qqq_rf_innerring" + std::to_string(qqq_inner_ring), 800, -2000, 2000, ts_qqq - ts_rf, "misc");
|
|
}
|
|
if (misc.ch[j] == 4)
|
|
{ // mcp
|
|
ts_mcp = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_mcp = 1;
|
|
plotter->Fill1D("dt_qqq_mcp_innerring" + std::to_string(qqq_inner_ring), 800, -2000, 2000, ts_qqq - ts_mcp, "misc");
|
|
}
|
|
}
|
|
if (found_rf && found_mcp)
|
|
{
|
|
if (ctr == 0)
|
|
plotter->Fill1D("dt_rf_mcp_qqq_innerring" + std::to_string(qqq_inner_ring), 500, -1000, 1000, ts_rf - ts_mcp, "misc");
|
|
double dt_rf_mcp = ts_rf - ts_mcp;
|
|
double dt_qqq_rf = ts_qqq - ts_rf;
|
|
double dt_qqq_mcp = ts_qqq - ts_mcp;
|
|
plotter->Fill2D("dt(qqq,rf)_vs_(rf,mcp)_innerring" + std::to_string(qqq_inner_ring), 640, -2000, 2000, 640, -2000, 2000, dt_qqq_rf, dt_rf_mcp, "misc");
|
|
plotter->Fill2D("dt_(qqq,mcp)_vs_(qqq,rf)_innerring" + std::to_string(qqq_inner_ring), 640, -1400, 2000, 640, -2000, 2000, dt_qqq_mcp, dt_qqq_rf, "misc");
|
|
plotter->Fill2D("dt_(qqq,mcp)_vs_(rf,mcp)_innerring" + std::to_string(qqq_inner_ring), 640, -1400, -600, 640, -2000, 2000, dt_qqq_mcp, dt_rf_mcp, "misc");
|
|
}
|
|
ctr += 1;
|
|
}
|
|
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
double ts_rf = -987654321;
|
|
double ts_needle = -987654321;
|
|
double ts_mcp = -987654321;
|
|
double ts_sx3 = static_cast<double>(sx3event.Time1) + (rnd.Uniform(16.0) - 8.0);
|
|
bool found_rf = false;
|
|
bool found_mcp = false;
|
|
bool found_needle = false;
|
|
for (int j = 0; j < misc.multi; j++)
|
|
{
|
|
plotter->Fill1D("channels_misc_sx3", 20, 0, 20, misc.ch[j], "misc");
|
|
if (misc.ch[j] == 2)
|
|
{ // Needle
|
|
plotter->Fill2D("needle_vs_sx3E", 800, 0, 16384, 800, 0, 10, misc.e[j], sx3event.Energy1, "misc");
|
|
ts_needle = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_needle = 1;
|
|
plotter->Fill1D("dt_sx3_needle", 800, -2000, 2000, ts_sx3 - ts_needle, "misc");
|
|
}
|
|
if (misc.ch[j] == 3)
|
|
{ // RF
|
|
ts_rf = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_rf = 1;
|
|
plotter->Fill1D("dt_sx3_rf", 800, -2000, 2000, ts_sx3 - ts_rf, "misc");
|
|
}
|
|
if (misc.ch[j] == 4)
|
|
{ // mcp
|
|
ts_mcp = static_cast<double>(misc.t[j]) + static_cast<double>(misc.tf[j]);
|
|
found_mcp = 1;
|
|
plotter->Fill1D("dt_sx3_mcp", 800, -2000, 2000, ts_sx3 - ts_mcp, "misc");
|
|
}
|
|
}
|
|
if (found_rf && found_mcp)
|
|
{
|
|
if (ctr == 0)
|
|
plotter->Fill1D("dt_rf_mcp_sx3", 500, -1000, 1000, ts_rf - ts_mcp, "misc");
|
|
double dt_rf_mcp = ts_rf - ts_mcp;
|
|
double dt_sx3_rf = ts_sx3 - ts_rf;
|
|
double dt_sx3_mcp = ts_sx3 - ts_mcp;
|
|
plotter->Fill2D("dt(sx3,rf)_vs_(rf,mcp)", 640, -2000, 2000, 640, -2000, 2000, dt_sx3_rf, dt_rf_mcp, "misc");
|
|
plotter->Fill2D("dt_(sx3,mcp)_vs_(sx3,rf)", 640, -1400, 2000, 640, -2000, 2000, dt_sx3_mcp, dt_sx3_rf, "misc");
|
|
plotter->Fill2D("dt_(sx3,mcp)_vs_(rf,mcp)", 640, -1400, -600, 640, -2000, 2000, dt_sx3_mcp, dt_rf_mcp, "misc");
|
|
}
|
|
ctr += 1;
|
|
}
|
|
}
|
|
|
|
if (process_alpha_proton_scattering)
|
|
{
|
|
protonAlphaHistograms(plotter, QQQ_Events, SX3_Events, PC_Events);
|
|
// return kTRUE;
|
|
} // end if(process_alpha_proton_scattering)
|
|
|
|
if (doMiscHistograms)
|
|
{
|
|
miscHistograms_oneWire(plotter, QQQ_Events, aClusters);
|
|
protonMiscHistograms_sx3(plotter, QQQ_Events, SX3_Events, PC_Events);
|
|
protonMiscHistograms(plotter, QQQ_Events, SX3_Events, PC_Events);
|
|
// return kTRUE;
|
|
}
|
|
|
|
#ifdef RAW_HISTOS
|
|
if (QQQ_Events.size() && PC_Events.size())
|
|
plotter->Fill2D("PCEv_vs_QQQEv", 20, 0, 20, 20, 0, 20, QQQ_Events.size(), PC_Events.size());
|
|
|
|
plotter->Fill2D("ac_vs_cc", 20, 0, 20, 20, 0, 20, aClusters.size(), cClusters.size(), "wiremult");
|
|
for (auto cluster : aClusters)
|
|
{
|
|
plotter->Fill1D("aClusters" + std::to_string(aClusters.size()), 20, -5, 15, cluster.size(), "wiremult");
|
|
}
|
|
for (auto cluster : cClusters)
|
|
{
|
|
plotter->Fill1D("cClusters" + std::to_string(cClusters.size()), 20, -5, 15, cluster.size(), "wiremult");
|
|
}
|
|
|
|
if (cClusters.size() && aClusters.size())
|
|
{
|
|
plotter->Fill2D("ac_vs_cc_ign0", 20, 0, 20, 20, 0, 20, aClusters.size(), cClusters.size(), "wiremult");
|
|
}
|
|
#endif
|
|
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
for (int i = 0; i < 24; i++)
|
|
{
|
|
if (aWireEvents.find(i) != aWireEvents.end())
|
|
{
|
|
auto awire = aWireEvents[i];
|
|
if (sx3event.Time1 - (double)std::get<2>(awire) < 150)
|
|
{
|
|
// plotter->Fill2D("sx3_z_phi2_awire"+std::to_string(std::get<0>(awire)), 400,-100,100, 100, -200,200,sx3event.pos.Z(), sx3event.pos.Phi()*180/M_PI );
|
|
// plotter->Fill2D("sx3_z_strip#_awire"+std::to_string(std::get<0>(awire)), 400,-100,100, 100, -50,50,sx3event.pos.Z(), sx3event.ch2);
|
|
plotter->Fill2D("onewire_dEa_Esx3_TC1_fullev" + std::to_string(PC_Events.size() > 0), 400, 0, 10, 800, 0, 40000, sx3event.Energy1, std::get<1>(awire), "1wire");
|
|
plotter->Fill2D("onewire_aNum_sx3Phi_TC1_fullev" + std::to_string(PC_Events.size() > 0), 24, 0, 24, 120, -360, 360, i, sx3event.pos.Phi() * 180. / M_PI, "1wire");
|
|
}
|
|
}
|
|
|
|
if (cWireEvents.find(i) != cWireEvents.end())
|
|
{
|
|
auto cwire = cWireEvents[i];
|
|
if (sx3event.Time1 - (double)std::get<2>(cwire) < 150)
|
|
{
|
|
// plotter->Fill2D("sx3_z_phi2_cwire"+std::to_string(std::get<0>(cwire)),400,-100,100, 100, -200,200,sx3event.pos.Z(), sx3event.pos.Phi()*180/M_PI );
|
|
// plotter->Fill2D("sx3_z_strip#_cwire"+std::to_string(std::get<0>(cwire)),400,-100,100, 100, -50,50,sx3event.pos.Z(), sx3event.ch2 );
|
|
plotter->Fill2D("onewire_dEc_Esx3_fullev" + std::to_string(PC_Events.size() > 0), 400, 0, 10, 800, 0, 40000, sx3event.Energy1, std::get<1>(cwire), "1wire");
|
|
plotter->Fill2D("onewire_cNum_sx3Phi_TC1_fullev" + std::to_string(PC_Events.size() > 0), 24, 0, 24, 120, -360, 360, i, sx3event.pos.Phi() * 180. / M_PI, "1wire");
|
|
}
|
|
}
|
|
} // for 'i' loop
|
|
|
|
for (const auto acluster : aClusters)
|
|
{
|
|
auto [apwire, apSumE, apMaxE, apTSMaxE] = pwinstance.GetPseudoWire(acluster, "ANODE");
|
|
int a_number = acluster.size();
|
|
TVector3 pc_closest = pwinstance.getClosestWirePosAtWirePhi(apwire, sx3event.pos.Phi());
|
|
if (sx3event.Time1 - apTSMaxE < 150)
|
|
{
|
|
plotter->Fill2D("dEa_interp_Esx3_TC1_ignC" + std::to_string(acluster.size()), 400, 0, 10, 800, 0, 40000, sx3event.Energy1, apSumE, "ainterp_noc");
|
|
plotter->Fill2D("aPhi_interp_sx3Phi_TC1_ignC" + std::to_string(acluster.size()), 120, -360, 360, 120, -360, 360, pc_closest.Phi() * 180. / M_PI, sx3event.pos.Phi() * 180. / M_PI, "ainterp_noc");
|
|
plotter->Fill2D("aZ_interp_sx3Z_TC1_ignC" + std::to_string(acluster.size()), 400, -200, 200, 300, -100, 200, pc_closest.Z(), sx3event.pos.Z(), "ainterp_noc");
|
|
TVector3 x2(pc_closest), x1(sx3event.pos);
|
|
TVector3 v = x2 - x1;
|
|
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_axis = x1 + t_minimum * v;
|
|
plotter->Fill2D("vertexZ_interp_sx3Z_TC1_ignC" + std::to_string(acluster.size()), 400, -200, 200, 300, -100, 200, vector_closest_to_axis.Z(), sx3event.pos.Z(), "ainterp_noc");
|
|
plotter->Fill2D("vertexXY_interp_TC1_ignC" + std::to_string(acluster.size()), 200, -100, 100, 200, -100, 100, vector_closest_to_axis.X(), vector_closest_to_axis.Y(), "ainterp_noc");
|
|
}
|
|
}
|
|
}
|
|
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
for (int i = 0; i < 24; i++)
|
|
{
|
|
if (aWireEvents.find(i) != aWireEvents.end())
|
|
{
|
|
auto awire = aWireEvents[i];
|
|
if (qqqevent.Time1 - (double)std::get<2>(awire) < 150)
|
|
{
|
|
plotter->Fill2D("onewire_dEa_Eqqq_TC1_fullev" + std::to_string(PC_Events.size() > 0), 400, 0, 10, 800, 0, 40000, qqqevent.Energy1, std::get<1>(awire), "1wire");
|
|
plotter->Fill2D("onewire_aNum_QQQPhi_TC1_fullev" + std::to_string(PC_Events.size() > 0), 24, 0, 24, 120, -360, 360, i, qqqevent.pos.Phi() * 180. / M_PI, "1wire");
|
|
}
|
|
}
|
|
|
|
if (cWireEvents.find(i) != cWireEvents.end())
|
|
{
|
|
auto cwire = cWireEvents[i];
|
|
if (qqqevent.Time1 - (double)std::get<2>(cwire) < 150)
|
|
{
|
|
plotter->Fill2D("onewire_dEc_Eqqq_TC1_fullev" + std::to_string(PC_Events.size() > 0), 400, 0, 10, 800, 0, 40000, qqqevent.Energy1, std::get<1>(cwire), "1wire");
|
|
plotter->Fill2D("onewire_cNum_QQQPhi_TC1_fullev" + std::to_string(PC_Events.size() > 0), 24, 0, 24, 120, -360, 360, i, qqqevent.pos.Phi() * 180. / M_PI, "1wire");
|
|
}
|
|
}
|
|
} // for 'i' loop
|
|
}
|
|
|
|
if (doPCSX3ClusterAnalysis)
|
|
{
|
|
PCSX3ClusterAnalysis(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters, cClusters);
|
|
}
|
|
if (doPCQQQClusterAnalysis)
|
|
{
|
|
PCQQQClusterAnalysis(plotter, QQQ_Events, SX3_Events, PC_Events, aClusters, cClusters);
|
|
}
|
|
|
|
///////////////////nA analysis using pseudo-wire (GetPseudoWire + getClosestWirePosAtWirePhi)///////////////////
|
|
#ifdef nA_Analysis
|
|
if (aClusters.size() > 0)
|
|
{
|
|
// ---------------------------------------------------------
|
|
// PROTON LOOP (SX3 BARREL)
|
|
// ---------------------------------------------------------
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
// Pick the anode cluster closest in phi to this SX3 hit
|
|
const std::vector<std::tuple<int, double, double>> *bestCluster = &aClusters[0];
|
|
double bestDphi = 9999.0;
|
|
|
|
for (const auto &acluster : aClusters)
|
|
{
|
|
auto [pw, sumE, maxE, tsMax] = pwinstance.GetPseudoWire(acluster, "ANODE");
|
|
TVector3 pos = pwinstance.getClosestWirePosAtWirePhi(pw, sx3event.pos.Phi());
|
|
double dphi = TMath::Abs(TVector2::Phi_mpi_pi(sx3event.pos.Phi() - pos.Phi()));
|
|
if (dphi < bestDphi)
|
|
{
|
|
bestDphi = dphi;
|
|
bestCluster = &acluster;
|
|
}
|
|
}
|
|
|
|
// Extract the virtual wire specifically for the best cluster
|
|
auto [apwire, apSumE, apMaxE, apTSMaxE] = pwinstance.GetPseudoWire(*bestCluster, "ANODE");
|
|
std::string nA_label = std::to_string(bestCluster->size()) + "A";
|
|
|
|
TVector3 pcz_intersect = pwinstance.getClosestWirePosAtWirePhi(apwire, sx3event.pos.Phi());
|
|
|
|
double deltaRho = sx3event.pos.Perp() - pcz_intersect.Perp();
|
|
double deltaZ = sx3event.pos.Z() - pcz_intersect.Z();
|
|
double vertex_recon = sx3event.pos.Z() - sx3event.pos.Perp() * (deltaZ / deltaRho);
|
|
|
|
std::string vtx_gate = "";
|
|
|
|
#ifdef VTX_GATE
|
|
if (vertex_recon >= -176.0 && vertex_recon < -100.0)
|
|
vtx_gate = "_Z[-176_to_-100]";
|
|
else if (vertex_recon >= -100.0 && vertex_recon < -50.0)
|
|
vtx_gate = "_Z[-100_to_-50]";
|
|
else if (vertex_recon >= -50.0 && vertex_recon < 0.0)
|
|
vtx_gate = "_Z[-50_to_0]";
|
|
else if (vertex_recon >= 0.0 && vertex_recon < 50.0)
|
|
vtx_gate = "_Z[0_to_50]";
|
|
else if (vertex_recon >= 50.0 && vertex_recon < 100.0)
|
|
vtx_gate = "_Z[50_to_100]";
|
|
else if (vertex_recon >= 100.0 && vertex_recon < 176.0)
|
|
vtx_gate = "_Z[100_to_176]";
|
|
#endif
|
|
|
|
// *0.1 converts mm to cm for the Eloss
|
|
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(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(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
|
|
|
|
double path_length = (sx3event.pos - TVector3(0, 0, vertex_recon)).Mag() * 0.1;
|
|
double sx3Efix = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(sx3event.Energy1) - path_length);
|
|
double sx3Efixalpha = cm_to_MeV->Eval(MeV_to_cm->Eval(sx3event.Energy1) - path_length);
|
|
|
|
double theta_recon = (sx3event.pos - TVector3(0, 0, vertex_recon)).Theta();
|
|
double sinTheta = TMath::Sin(theta_recon);
|
|
|
|
// Fill standard un-gated plots
|
|
plotter->Fill1D(nA_label + "_vertex_recon_SX3", 400, -200, 200, vertex_recon, nA_label);
|
|
plotter->Fill1D(nA_label + "_vertex_recon", 400, -200, 200, vertex_recon, nA_label);
|
|
|
|
plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_SX3", 800, 0, 30, 800, 0, 30000, sx3Efix, apSumE * sinTheta, nA_label);
|
|
plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_SX3_alpha", 800, 0, 30, 800, 0, 30000, sx3Efixalpha, apSumE * sinTheta, nA_label);
|
|
plotter->Fill2D(nA_label + "_sx3_E_vs_theta_raw_SX3", 180, 0, 180, 400, 0, 30, theta_recon * 180. / M_PI, sx3event.Energy1, nA_label);
|
|
plotter->Fill2D(nA_label + "_sx3_E_vs_theta_corr_SX3", 180, 0, 180, 400, 0, 30, theta_recon * 180. / M_PI, sx3Efix, nA_label);
|
|
|
|
#if defined(F_BEAM) || defined(AL_BEAM)
|
|
double Ex_from_proton = apkin_p.getExc(sx3Efix, theta_recon * 180. / M_PI);
|
|
double Ex_from_alpha = apkin_a.getExc(sx3Efixalpha, theta_recon * 180. / M_PI);
|
|
plotter->Fill1D(nA_label + "_Ex_from_protons_SX3", 1200, -30, 30, Ex_from_proton, nA_label);
|
|
plotter->Fill1D(nA_label + "_Ex_from_alphas_SX3", 1200, -30, 30, Ex_from_alpha, nA_label);
|
|
#endif
|
|
|
|
// Fill Gated Plots
|
|
// if (vtx_gate != "")
|
|
// {
|
|
// plotter->Fill2D("dE_Ecorr_Anode_SX3" + vtx_gate, 400, 0, 30, 800, 0, 40000, sx3Efix, apSumE * sinTheta, nA_label);
|
|
// plotter->Fill2D("dE_Ecorr_Anode_SX3_alpha" + vtx_gate, 400, 0, 30, 800, 0, 40000, sx3Efixalpha, apSumE * sinTheta, nA_label);
|
|
// plotter->Fill1D("twisted_pcz_recon_SX3" + vtx_gate, 600, -300, 300, pcz_intersect.Z(), nA_label);
|
|
// plotter->Fill1D("twisted_vertex_recon_SX3" + vtx_gate, 600, -300, 300, vertex_recon, nA_label);
|
|
// plotter->Fill1D("Ex_from_protons_SX3" + vtx_gate, 1200, -30, 30, Ex_from_proton, nA_label);
|
|
// plotter->Fill1D("Ex_from_alphas_SX3" + vtx_gate, 1200, -30, 30, Ex_from_alpha, nA_label);
|
|
// }
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// PROTON LOOP (QQQ ENDCAP)
|
|
// ---------------------------------------------------------
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
const std::vector<std::tuple<int, double, double>> *bestCluster = nullptr;
|
|
double bestDphi = 9999.0;
|
|
|
|
for (const auto &acluster : aClusters)
|
|
{
|
|
auto [apw, sumE, maxE, tsMax] = pwinstance.GetPseudoWire(acluster, "ANODE");
|
|
TVector3 pcPos = pwinstance.getClosestWirePosAtWirePhi(apw, qqqevent.pos.Phi());
|
|
double dphi = TMath::Abs(TVector2::Phi_mpi_pi(qqqevent.pos.Phi() - pcPos.Phi()));
|
|
if (dphi < bestDphi)
|
|
{
|
|
bestDphi = dphi;
|
|
bestCluster = &acluster;
|
|
}
|
|
}
|
|
if (!bestCluster)
|
|
continue;
|
|
|
|
// Extract the virtual wire specifically for the best cluster
|
|
auto [apwire, apSumE, apMaxE, apTSMaxE] = pwinstance.GetPseudoWire(*bestCluster, "ANODE");
|
|
std::string nA_label = std::to_string(bestCluster->size()) + "A";
|
|
|
|
TVector3 pcz_intersect = pwinstance.getClosestWirePosAtWirePhi(apwire, qqqevent.pos.Phi());
|
|
|
|
double deltaRho = qqqevent.pos.Perp() - pcz_intersect.Perp();
|
|
double deltaZ = qqqevent.pos.Z() - pcz_intersect.Z();
|
|
double vertex_recon = qqqevent.pos.Z() - qqqevent.pos.Perp() * (deltaZ / deltaRho);
|
|
|
|
std::string vtx_gate = "";
|
|
|
|
#ifdef VTX_GATE
|
|
if (vertex_recon >= -176.0 && vertex_recon < -100.0)
|
|
vtx_gate = "_Z[-176_to_-100]";
|
|
else if (vertex_recon >= -100.0 && vertex_recon < -50.0)
|
|
vtx_gate = "_Z[-100_to_-50]";
|
|
else if (vertex_recon >= -50.0 && vertex_recon < 0.0)
|
|
vtx_gate = "_Z[-50_to_0]";
|
|
else if (vertex_recon >= 0.0 && vertex_recon < 50.0)
|
|
vtx_gate = "_Z[0_to_50]";
|
|
else if (vertex_recon >= 50.0 && vertex_recon < 100.0)
|
|
vtx_gate = "_Z[50_to_100]";
|
|
else if (vertex_recon >= 100.0 && vertex_recon < 176.0)
|
|
vtx_gate = "_Z[100_to_176]";
|
|
#endif
|
|
|
|
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);
|
|
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);
|
|
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
|
|
|
|
// Energy Loss Correction
|
|
double path_length = (qqqevent.pos - TVector3(0, 0, vertex_recon)).Mag() * 0.1;
|
|
double qqqEfix = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(qqqevent.Energy1) - path_length);
|
|
double qqqEfixalpha = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy2) - path_length);
|
|
|
|
double theta_recon = (qqqevent.pos - TVector3(0, 0, vertex_recon)).Theta();
|
|
double sinTheta = TMath::Sin(theta_recon);
|
|
|
|
// Fill standard un-gated plots
|
|
plotter->Fill1D(nA_label + "_vertex_recon_QQQ", 400, -200, 200, vertex_recon, nA_label);
|
|
plotter->Fill1D(nA_label + "_vertex_recon", 400, -200, 200, vertex_recon, nA_label);
|
|
plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_QQQ", 800, 0, 30, 800, 0, 30000, qqqEfix, apSumE * sinTheta, nA_label);
|
|
plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_QQQ_alpha", 800, 0, 30, 800, 0, 30000, qqqEfixalpha, apSumE * sinTheta, nA_label);
|
|
plotter->Fill2D(nA_label + "_qqq_E_vs_theta_raw_QQQ", 180, 0, 180, 400, 0, 30, theta_recon * 180. / M_PI, qqqevent.Energy1, nA_label);
|
|
plotter->Fill2D(nA_label + "_qqq_E_vs_theta_corr_QQQ", 180, 0, 180, 400, 0, 30, theta_recon * 180. / M_PI, qqqEfix, nA_label);
|
|
|
|
#if defined(F_BEAM) || defined(AL_BEAM)
|
|
double Ex_from_alpha = apkin_a.getExc(qqqEfixalpha, theta_recon * 180. / M_PI);
|
|
plotter->Fill1D(nA_label + "_Ex_from_alphas_QQQ", 1200, -30, 30, Ex_from_alpha, nA_label);
|
|
plotter->Fill1D(nA_label + "_Ex_from_protons_QQQ", 1200, -30, 30, Ex_from_proton, nA_label);
|
|
#endif
|
|
|
|
// Fill Gated Plots
|
|
// if (vtx_gate != "")
|
|
// {
|
|
// plotter->Fill1D(nA_label + "_twisted_pcz_recon_QQQ" + vtx_gate, 600, -300, 300, pcz_intersect.Z(), nA_label);
|
|
// plotter->Fill1D(nA_label + "_twisted_vertex_recon_QQQ" + vtx_gate, 600, -300, 300, vertex_recon, nA_label);
|
|
// plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_QQQ" + vtx_gate, 400, 0, 30, 800, 0, 40000, qqqEfix, apSumE * sinTheta, nA_label);
|
|
// plotter->Fill2D(nA_label + "_dE_Ecorr_Anode_QQQ_alpha" + vtx_gate, 400, 0, 30, 800, 0, 40000, qqqEfixalpha, apSumE * sinTheta, nA_label);
|
|
// plotter->Fill1D(nA_label + "_Ex_from_alphas_QQQ" + vtx_gate, 1200, -30, 30, Ex_from_alpha, nA_label);
|
|
// plotter->Fill1D(nA_label + "_Ex_from_protons_QQQ" + vtx_gate, 1200, -30, 30, Ex_from_proton, nA_label);
|
|
// }
|
|
}
|
|
}
|
|
#endif
|
|
if (doOldAnalysis)
|
|
OldAnalysis();
|
|
return kTRUE;
|
|
}
|
|
|
|
void TrackRecon::Terminate()
|
|
{
|
|
plotter->FlushToDisk(10);
|
|
/* can1->Modified();
|
|
can1->Update();
|
|
can2->Modified();
|
|
can2->Update();
|
|
while(can1->WaitPrimitive());
|
|
while(can2->WaitPrimitive());*/
|
|
}
|
|
|
|
void protonAlphaHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events)
|
|
{
|
|
|
|
// Sidetrack for a(p,p)
|
|
std::string aplabel = "a(p,p)";
|
|
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)
|
|
{
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
plotter->Fill1D("ap_qqq_sx3_dt", 800, -2000, 2000, qqqevent.Time1 - sx3event.Time1, aplabel);
|
|
if (TMath::Abs(qqqevent.Time1 - sx3event.Time1) > 300)
|
|
continue;
|
|
// sx3event.pos.SetZ(sx3event.pos.Z()+5.0);
|
|
plotter->Fill1D("ap_qqq_sx3_dt_timecut", 800, -2000, 2000, qqqevent.Time1 - sx3event.Time1, aplabel);
|
|
plotter->Fill1D("ap_qqq_sx3_dphi", 180, -360, 360, qqqevent.pos.Phi() * 180 / M_PI - sx3event.pos.Phi() * 180 / M_PI, aplabel);
|
|
plotter->Fill2D("ap_qqq_sx3_dphi_vs_qqqphi", 180, -360, 360, 180, -360, 360, qqqevent.pos.Phi() * 180 / M_PI - sx3event.pos.Phi() * 180 / M_PI, qqqevent.pos.Phi() * 180 / M_PI, aplabel);
|
|
plotter->Fill2D("ap_qqq_sx3_matrix", 400, 0, 10, 400, 0, 10, qqqevent.Energy1, sx3event.Energy1, aplabel);
|
|
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
|
|
double pcz_fix = pcfix_func.Eval(pcevent.pos.Z()) - 5.0;
|
|
TVector3 x2f(pcevent.pos.X(), pcevent.pos.Y(), pcz_fix);
|
|
TVector3 x1(qqqevent.pos);
|
|
TVector3 v = x2f - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
double vertex_z = r_rhoMin_fix.Z();
|
|
double theta_q = (qqqevent.pos - TVector3(0, 0, vertex_z)).Theta();
|
|
// double theta_q = (qqqevent.pos - r_rhoMin_fix).Theta();
|
|
double sinTheta_customV = TMath::Sin(theta_q);
|
|
double theta_s = (sx3event.pos - TVector3(0, 0, vertex_z)).Theta();
|
|
// double theta_s = (sx3event.pos - r_rhoMin_fix).Theta();
|
|
double sinTheta_s = TMath::Sin(theta_s);
|
|
// if(vertex_z<0 || vertex_z>100) continue;
|
|
|
|
// double sinTheta = TMath::Sin((qqqevent.pos - pcevent.pos).Theta());
|
|
// plotter->Fill2D("sinTheta2_vs_sinTheta",80,-2,2,80,-2,2,sinTheta,sinTheta_customV,aplabel);
|
|
|
|
plotter->Fill2D("ap_dE_E_Anodesx3B", 400, 0, 10, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1, aplabel);
|
|
plotter->Fill2D("ap_dE_E_Cathodesx3B", 400, 0, 10, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2, aplabel);
|
|
plotter->Fill2D("ap_dE_E_AnodeQQQ", 400, 0, 10, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1, aplabel);
|
|
plotter->Fill2D("ap_dE_E_CathodeQQQ", 400, 0, 10, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy2, aplabel);
|
|
plotter->Fill2D("ap_dE3_E_AnodeQQQ", 400, 0, 10, 400, 0, 40000, qqqevent.Energy1, pcevent.Energy1 * sinTheta_customV, aplabel);
|
|
plotter->Fill2D("ap_dE3_E_CathodeQQQ", 400, 0, 10, 400, 0, 10000, qqqevent.Energy1, pcevent.Energy2 * sinTheta_customV, aplabel);
|
|
|
|
plotter->Fill2D("ap_dPhi_QQQ_PC", 180, -360, 360, 180, -360, 360, pcevent.pos.Phi() * 180 / M_PI, qqqevent.pos.Phi() * 180 / M_PI, aplabel);
|
|
plotter->Fill2D("ap_dPhi_SX3_PC", 180, -360, 360, 180, -360, 360, pcevent.pos.Phi() * 180 / M_PI, sx3event.pos.Phi() * 180 / M_PI, aplabel);
|
|
plotter->Fill1D("ap_dt_Anode_QQQ", 600, -2000, 2000, pcevent.Time1 - qqqevent.Time1, aplabel);
|
|
plotter->Fill1D("ap_dt_Cathode_QQQ", 600, -2000, 2000, pcevent.Time2 - qqqevent.Time1, aplabel);
|
|
plotter->Fill1D("ap_dt_Anode_SX3", 600, -2000, 2000, pcevent.Time1 - sx3event.Time1, aplabel);
|
|
plotter->Fill1D("ap_dt_Cathode_SX3", 600, -2000, 2000, pcevent.Time2 - sx3event.Time1, aplabel);
|
|
plotter->Fill1D("ap_pczfix", 600, -300, 300, pcz_fix, aplabel);
|
|
plotter->Fill1D("ap_pcz", 600, -300, 300, pcevent.pos.Z(), aplabel);
|
|
|
|
double path_length_q = (qqqevent.pos - TVector3(0, 0, vertex_z)).Mag() * 0.1;
|
|
double path_length_s = (sx3event.pos - TVector3(0, 0, vertex_z)).Mag() * 0.1;
|
|
// double path_length_q = (qqqevent.pos-r_rhoMin_fix).Mag()*0.1;
|
|
// double path_length_s = (sx3event.pos-r_rhoMin_fix).Mag()*0.1;
|
|
|
|
// We know that alphas predominantly are detected in QQQs, and protons in SX3s, and that protons don't leave much of a trace in dE layer.
|
|
// Using the estimated path lengths, we correct alpha eloss in qqq, and protons in sx3. The result should (hopefully be) vertex independent.
|
|
|
|
double qqqEfix = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy1) - path_length_q);
|
|
double sx3Efix = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(sx3event.Energy1) - path_length_s);
|
|
// plotter->Fill2D("qqqEf_sx3E_matrix_all",400,0,10,400,0,10,qqqEfix,sx3event.Energy1,aplabel);
|
|
plotter->Fill2D("ap_qqqEf_sx3Ef_matrix", 400, 0, 10, 400, 0, 10, qqqEfix, sx3Efix, aplabel);
|
|
|
|
plotter->Fill2D("ap_Ef_vs_theta_qqq", 100, 0, 180, 400, 0, 10, theta_q * 180 / M_PI, qqqEfix, aplabel);
|
|
plotter->Fill2D("ap_Ef_vs_theta_sx3", 100, 0, 180, 400, 0, 10, theta_s * 180 / M_PI, sx3Efix, aplabel);
|
|
plotter->Fill2D("ap_theta_vs_theta_qqq_sx3", 100, 0, 180, 100, 0, 180, theta_q * 180 / M_PI, theta_s * 180 / M_PI, aplabel);
|
|
plotter->Fill1D("ap_VertexReconZ", 400, -200, 200, vertex_z, aplabel);
|
|
plotter->Fill2D("ap_VertexReconXY", 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), aplabel);
|
|
plotter->Fill1D("ap_Ex_from_protons", 200, -10, 10, apkin_p.getExc(sx3Efix, theta_s * 180 / M_PI), aplabel);
|
|
plotter->Fill1D("ap_Ex_from_alpha", 200, -10, 10, apkin_a.getExc(qqqEfix, theta_q * 180 / M_PI), aplabel);
|
|
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
{ // one-anode, two-cathode events, as originally intended
|
|
// std::cout << "Test" << std::endl;
|
|
plotter->Fill1D("ap_VertexReconZ_a1c2", 400, -200, 200, vertex_z, aplabel);
|
|
plotter->Fill2D("ap_VertexReconXY_a1c2", 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), aplabel);
|
|
plotter->Fill2D("ap_theta_vs_theta_qqq_sx3_a1c2", 100, 0, 180, 100, 0, 180, theta_q * 180 / M_PI, theta_s * 180 / M_PI, aplabel);
|
|
plotter->Fill2D("ap_Ef_vs_theta_qqq_a1c2", 100, 0, 180, 400, 0, 10, theta_q * 180 / M_PI, qqqEfix, aplabel);
|
|
plotter->Fill1D("ap_Ex_from_protons_a1c2", 200, -10, 10, apkin_p.getExc(sx3Efix, theta_s * 180 / M_PI), aplabel);
|
|
plotter->Fill1D("ap_Ex_from_alpha_a1c2", 200, -10, 10, apkin_a.getExc(qqqEfix, theta_q * 180 / M_PI), aplabel);
|
|
|
|
// std::cout << apkin_p.getExc(sx3Efix,theta_s*180/M_PI) << " " << apkin_a.getExc(qqqEfix,theta_q*180/M_PI)<< std::endl;
|
|
plotter->Fill2D("ap_Ef_vs_theta_sx3_a1c2", 100, 0, 180, 400, 0, 10, theta_s * 180 / M_PI, sx3Efix, aplabel);
|
|
|
|
// plotter->Fill2D("qqqEf_sx3E_matrix",400,0,10,400,0,10,qqqEfix,sx3event.Energy1,aplabel);
|
|
plotter->Fill2D("ap_qqq_sx3_matrix_a1c2", 400, 0, 10, 400, 0, 10, qqqevent.Energy1, sx3event.Energy1, aplabel);
|
|
plotter->Fill2D("ap_qqqEf_sx3Ef_matrix_a1c2", 400, 0, 10, 400, 0, 10, qqqEfix, sx3Efix, aplabel);
|
|
// std::cout << sx3event.Energy1 << " " << path_length_s << " " << sx3Efix << std::endl;
|
|
|
|
// plotter->Fill2D("dE3_Ef_AnodeQQQ_a1c2",400,0,10,400,0,40000,qqqEfix,pcevent.Energy1*sinTheta_customV,aplabel);
|
|
// plotter->Fill2D("dE3_Ef_CathodeQQQ_a1c2",400,0,10,400,0,10000,qqqEfix,pcevent.Energy2*sinTheta_customV,aplabel);
|
|
|
|
} // end if(a1c2) loop
|
|
} // end PC_Events for loop
|
|
|
|
} // end SX3_Events for loop
|
|
} // end QQQ_Events for loop, end sidetrack a(p,p)
|
|
|
|
return;
|
|
}
|
|
|
|
void PCSX3ClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, 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>>> &cClusters)
|
|
{
|
|
|
|
static TRandom3 rand(0);
|
|
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
bool PCSX3TimeCut = false;
|
|
bool PCASX3TimeCut = false;
|
|
bool PCCSX3TimeCut = false;
|
|
for (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_pcC_sx3B" + std::to_string(sx3event.ch2), 640, -2000, 2000, sx3event.Time1 - pcevent.Time2, "Timing");
|
|
if (sx3event.Time1 - pcevent.Time1 < 0) //-150 for alphas
|
|
PCASX3TimeCut = 1;
|
|
if (sx3event.Time1 - pcevent.Time2 < 0) //-200 for alphas
|
|
PCCSX3TimeCut = 1;
|
|
PCSX3TimeCut = PCASX3TimeCut && PCCSX3TimeCut;
|
|
|
|
bool phicut = sx3event.pos.Phi() <= pcevent.pos.Phi() + TMath::Pi() / 4. && sx3event.pos.Phi() >= pcevent.pos.Phi() - TMath::Pi() / 4.;
|
|
|
|
plotter->Fill1D("dt_pcA_sx3B", 640, -2000, 2000, sx3event.Time1 - pcevent.Time1, "Timing");
|
|
plotter->Fill1D("dt_pcC_sx3B", 640, -2000, 2000, sx3event.Time1 - pcevent.Time2, "Timing");
|
|
plotter->Fill2D("dt_pcA_vs_sx3RE", 640, -2000, 2000, 400, 0, 30, sx3event.Time1 - pcevent.Time1, sx3event.Energy1, "Timing");
|
|
plotter->Fill2D("dE_E_Anodesx3B", 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
plotter->Fill2D("dE_E_Cathodesx3B", 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
plotter->Fill2D("dE_E_Anodesx3B_a1c2", 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
plotter->Fill2D("dE_E_Cathodesx3B_a1c2", 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
if (pcevent.multi1 == 2 && pcevent.multi2 == 1)
|
|
plotter->Fill2D("dE_E_Anodesx3B_a2c1", 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
if (pcevent.multi1 == 2 && pcevent.multi2 == 1)
|
|
plotter->Fill2D("dE_E_Cathodesx3B_a2c1", 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
plotter->Fill2D("sx3phi_vs_pcphi" + std::to_string(sx3event.Time1 - pcevent.Time1 < -150), 100, -360, 360, 100, -360, 360, sx3event.pos.Phi() * 180 / M_PI, pcevent.pos.Phi() * 180 / M_PI, "Kinematics_Angles");
|
|
if (PCSX3TimeCut)
|
|
{
|
|
plotter->Fill1D("dt_pcA_sx3B_timecut", 640, -2000, 2000, sx3event.Time1 - pcevent.Time1, "Timing");
|
|
plotter->Fill1D("dt_pcC_sx3B_timecut", 640, -2000, 2000, sx3event.Time1 - pcevent.Time2, "Timing");
|
|
plotter->Fill2D("xyplot_sx3" + std::to_string(sx3event.ch2 / 4), 100, -100, 100, 100, -100, 100, sx3event.pos.X(), sx3event.pos.Y(), "Vertex_Reconstruction");
|
|
plotter->Fill2D("xyplot_sx3" + std::to_string(sx3event.ch2 / 4), 100, -100, 100, 100, -100, 100, pcevent.pos.X(), pcevent.pos.Y(), "Vertex_Reconstruction");
|
|
plotter->Fill2D("pcz_vs_pcphi_TimeCut", 600, -200, 200, 120, -360, 360, pcevent.pos.Z(), pcevent.pos.Phi() * 180 / M_PI, "Z_Reconstruction");
|
|
}
|
|
|
|
double sx3rho = 88.0;
|
|
double sx3z = sx3event.pos.Z();
|
|
double pcz = pcevent.pos.Z();
|
|
double calcsx3theta = TMath::ATan2(sx3rho - z_to_crossover_rho(pcz), sx3z - pcz);
|
|
plotter->Fill2D("dE2_E_Anodesx3B", 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1 * TMath::Sin(calcsx3theta), "PID_dE_E");
|
|
plotter->Fill2D("dE2_E_Cathodesx3B", 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2 * TMath::Sin(calcsx3theta), "PID_dE_E");
|
|
|
|
double sx3theta = TMath::ATan2(sx3rho, sx3z - source_vertex);
|
|
double pczguess = 37.0 / TMath::Tan(sx3theta) + source_vertex;
|
|
double pcz_guess_int = z_to_crossover_rho(pcevent.pos.Z()) / TMath::Tan(sx3theta) + source_vertex;
|
|
double sinTheta = TMath::Sin(sx3theta);
|
|
|
|
TVector3 x2(pcevent.pos), x1(sx3event.pos);
|
|
TVector3 v = x2 - x1;
|
|
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(), "Vertex_Reconstruction");
|
|
plotter->Fill1D("VertexReconZ_SX3", 600, -1300, 1300, vector_closest_to_z_sx3.Z(), "Vertex_Reconstruction");
|
|
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(), "Vertex_Reconstruction");
|
|
|
|
plotter->Fill2D("pcz_vs_time", 2000, 0, 2000, 600, -200, 200, pcevent.Time1 * 1e-9, pcevent.pos.Z(), "Timing");
|
|
plotter->Fill2D("pcphi_vs_time", 2000, 0, 2000, 180, -360, 360, pcevent.Time1 * 1e-9, pcevent.pos.Phi() * 180. / M_PI, "Timing");
|
|
plotter->Fill2D("sx3phi_vs_time", 2000, 0, 2000, 180, -360, 360, pcevent.Time1 * 1e-9, sx3event.pos.Phi() * 180. / M_PI, "Timing");
|
|
|
|
plotter->Fill2D("pcz_vs_sx3pczguess", 600, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
{
|
|
plotter->Fill2D("pcz_vs_sx3pczguess_A1C2", 600, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
double pcz_fix = pcfix_func.Eval(pcevent.pos.Z());
|
|
|
|
TVector3 x2f(pcevent.pos.X(), pcevent.pos.Y(), pcz_fix);
|
|
TVector3 v = x2f - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
plotter->Fill1D("VertexRecon_pczfix_sx3", 800, -300, 300, r_rhoMin_fix.Z(), "Vertex_Reconstruction");
|
|
plotter->Fill1D("VertexRecon_pczfix", 800, -300, 300, r_rhoMin_fix.Z(), "Vertex_Reconstruction");
|
|
plotter->Fill1D("pczfix_A1C2_1d_sx3", 600, -200, 200, pcz_fix, "Z_Reconstruction");
|
|
plotter->Fill2D("pczfix_vs_sx3pczguess_A1C2", 600, -200, 200, 600, -200, 200, pczguess, pcz_fix, "Z_Reconstruction");
|
|
plotter->Fill2D("pczfix_vs_sx3pczguess_int_A1C2", 600, -200, 200, 600, -200, 200, pcz_guess_int, pcz_fix, "Z_Reconstruction");
|
|
plotter->Fill2D("pczguess_vs_int", 600, -200, 200, 600, -200, 200, pcz_guess_int, pczguess, "Z_Reconstruction");
|
|
plotter->Fill1D("pczguess_vs_int_residualsx3", 200, -50, 50, pcz_guess_int - pczguess, "Residuals");
|
|
plotter->Fill2D("pczfix_residual_vs_pczguess_A1C2", 600, -200, 200, 200, -100, 100, pczguess, pcz_fix - pczguess, "Residuals");
|
|
plotter->Fill2D("pczfix_residual_vs_phi_A1C2", 200, 0, 6.28, 200, -100, 100, r_rhoMin_fix.Phi(), pcz_fix - pczguess, "Residuals");
|
|
plotter->Fill2D("pczguess_vs_int_residual_vs_phi_A1C2", 200, 0, 6.28, 200, -100, 100, r_rhoMin_fix.Phi(), pcz_guess_int - pczguess, "Residuals");
|
|
plotter->Fill1D("pczfix-sx3pczguess_A1C2", 200, -100, 100, pcz_fix - pczguess, "Residuals");
|
|
plotter->Fill2D("pczfix_vs_sx3pczguess_A1C2_strip" + std::to_string(sx3event.ch2), 300, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
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, "PID_dE_E");
|
|
plotter->Fill2D("dE3_E_AnodeSX3_A1C2_TC" + std::to_string(PCSX3TimeCut) + "_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1 * sinTheta_customV, "PID_dE_E");
|
|
|
|
if (TMath::Abs(r_rhoMin_fix.Z()) < 200.0)
|
|
{
|
|
plotter->Fill2D("dE3_E_AnodeSX3B_A1C2_(vertex_fix_z/100)=" + std::to_string(floor(r_rhoMin_fix.Z() / 100.0)), 400, 0, 30, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1 * sinTheta_customV, "PID_dE_E");
|
|
plotter->Fill2D("dE3_E_CathodeSX3B_A1C2_(vertex_fix_z/100)=" + std::to_string(floor(r_rhoMin_fix.Z() / 100.0)), 400, 0, 30, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2 * sinTheta_customV, "PID_dE_E");
|
|
}
|
|
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 3)
|
|
{
|
|
plotter->Fill2D("pcz_vs_sx3pczguess_A1C3", 600, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
plotter->Fill2D("pcz_vs_sx3pczguess_A1C3_strip" + std::to_string(sx3event.ch2), 300, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
}
|
|
|
|
plotter->Fill2D("pcz_vs_sx3pczguess_int", 600, -200, 200, 600, -200, 200, pcz_guess_int, pcevent.pos.Z(), "Z_Reconstruction");
|
|
plotter->Fill2D("pcz_vs_sx3pczguess_strip" + std::to_string(sx3event.ch2), 300, -200, 200, 600, -200, 200, pczguess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
bool sx3PhiCut = (TMath::Abs(sx3event.pos.Phi() - pcevent.pos.Phi()) < 45.0 * M_PI / 180.);
|
|
|
|
plotter->Fill1D("pcz_sx3Coinc_phiCut" + std::to_string(sx3PhiCut) + "_TC" + std::to_string(PCSX3TimeCut), 300, 0, 200, sx3z, "Z_Reconstruction");
|
|
plotter->Fill2D("pcz_vs_sx3z_phiCut" + std::to_string(sx3PhiCut) + "_TC" + std::to_string(PCSX3TimeCut), 300, 0, 200, 600, -400, 400, sx3z, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
plotter->Fill2D("sx3E_vs_sx3z", 400, 0, 30, 300, 0, 200, sx3event.Energy1, sx3z, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("pcdEA_vs_sx3z", 800, 0, 20000, 300, 0, 200, pcevent.Energy1, sx3z, "Kinematics_Angles");
|
|
plotter->Fill2D("pcdEC_vs_sx3z", 800, 0, 20000, 300, 0, 200, pcevent.Energy2, sx3z, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("pcdEA_vs_sx3z" + std::to_string(sx3event.ch2), 800, 0, 20000, 300, 0, 200, pcevent.Energy1, sx3z, "Kinematics_Angles");
|
|
plotter->Fill2D("pcdEC_vs_sx3z" + std::to_string(sx3event.ch2), 800, 0, 20000, 300, 0, 200, pcevent.Energy2, sx3z, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("pcdE2A_vs_sx3z", 800, 0, 20000, 300, 0, 200, pcevent.Energy1 * sinTheta, sx3z, "Kinematics_Angles");
|
|
plotter->Fill2D("pcdE2C_vs_sx3z", 800, 0, 20000, 300, 0, 200, pcevent.Energy2 * sinTheta, sx3z, "Kinematics_Angles");
|
|
plotter->Fill2D("phi_vs_stripnum", 180, -180, 180, 48, 0, 48, pcevent.pos.Phi() * 180. / M_PI, sx3event.ch2, "Kinematics_Angles");
|
|
plotter->Fill2D("E_theta_AnodeSX3", 400, -20, 180, 300, 0, 15, sx3theta * 180 / M_PI, sx3event.Energy1, "Kinematics_Angles");
|
|
}
|
|
if (PCSX3TimeCut)
|
|
{
|
|
plotter->Fill1D("PCZ_sx3", 800, -200, 200, pcevent.pos.Z(), "Z_Reconstruction");
|
|
}
|
|
|
|
//-----------------------Benchmarking Method for Source Runs (SX3)------------------------//
|
|
if (BenchMark && aClusters.size() == 1 && cClusters.size() == 1)
|
|
{
|
|
const auto &aCl = aClusters.front();
|
|
const auto &cCl = cClusters.front();
|
|
|
|
auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint)
|
|
{
|
|
TVector3 vf = pcpoint - si;
|
|
double tm = -1.0 * (si.X() * vf.X() + si.Y() * vf.Y()) / (vf.X() * vf.X() + vf.Y() * vf.Y());
|
|
return TVector3(si + tm * vf);
|
|
};
|
|
|
|
auto fillSuite = [&](const std::string &tag, double pcz_method, const TVector3 &vtx)
|
|
{
|
|
plotter->Fill1D("Benchmark_SX3_VertexZ_" + tag, 800, -400, 400, vtx.Z(), "Benchmark_SX3");
|
|
plotter->Fill1D("Benchmark_SX3_VertexZ_" + tag + "_TC" + std::to_string(PCSX3TimeCut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx.Z(), "Benchmark_SX3");
|
|
plotter->Fill2D("Benchmark_SX3_VertexXY_" + tag, 200, -100, 100, 200, -100, 100, vtx.X(), vtx.Y(), "Benchmark_SX3");
|
|
plotter->Fill1D("Benchmark_SX3_PCZ_" + tag, 600, -200, 200, pcz_method, "Benchmark_SX3");
|
|
};
|
|
|
|
auto fillVsRef = [&](const std::string &tag, double pcz_method, const TVector3 &vtx, double pcz_ref, const TVector3 &vtx_ref)
|
|
{
|
|
plotter->Fill2D("Benchmark_SX3_PCZ_" + tag + "_vs_ref", 400, -200, 200, 400, -200, 200, pcz_ref, pcz_method, "Benchmark_SX3_ref");
|
|
plotter->Fill1D("Benchmark_SX3_PCZ_" + tag + "_minus_ref", 400, -100, 100, pcz_method - pcz_ref, "Benchmark_SX3_ref");
|
|
plotter->Fill2D("Benchmark_SX3_PCZ_" + tag + "_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pcz_method, "Benchmark_SX3_ref");
|
|
plotter->Fill1D("Benchmark_SX3_PCZ_" + tag + "_minus_sx3pczguess", 400, -100, 100, pcz_method - pczguess, "Benchmark_SX3_ref");
|
|
};
|
|
|
|
double pcz_ref = pcfix_func.Eval(pcevent.pos.Z());
|
|
TVector3 vtx_ref = vertexFrom(sx3event.pos, TVector3(pcevent.pos.X(), pcevent.pos.Y(), pcz_ref));
|
|
|
|
auto pw_tuple = pwinstance.GetPseudoWire(aCl, "ANODE");
|
|
std::pair<TVector3, TVector3> apwire_bm = std::get<0>(pw_tuple);
|
|
|
|
auto cMaxWire = *std::max_element(cCl.begin(), cCl.end(), [](const auto &a, const auto &b)
|
|
{ return std::get<1>(a) < std::get<1>(b); });
|
|
std::vector<std::tuple<int, double, double>> cOne = {cMaxWire};
|
|
|
|
auto xo_tuple = pwinstance.FindCrossoverProperties(aCl, cOne);
|
|
TVector3 xo_a1c1 = std::get<0>(xo_tuple);
|
|
double alpha_a1c1 = std::get<1>(xo_tuple);
|
|
bool a1c1Good = (alpha_a1c1 != 9999999 && std::get<2>(xo_tuple) != -1);
|
|
|
|
// --- A1C1 charge fraction (single max-E cathode vs anode, pseudo-wire sums) ---
|
|
double aSumE_bm = std::get<1>(pw_tuple);
|
|
double cSumE_bm = std::get<1>(cMaxWire); // Extract the energy directly!
|
|
double ac_sum = aSumE_bm + cSumE_bm;
|
|
double cfrac = (ac_sum > 0.0) ? cSumE_bm / ac_sum : -1.0;
|
|
|
|
// Cmax/anode vs Si phi for ALL events reaching this block (not just the
|
|
// A1C2 subset). Cmax = single max-E cathode wire (cSumE_bm), anode =
|
|
// anode pseudo-wire sum. Ratio is unbounded above (arbitrary cathode gains).
|
|
if (aSumE_bm > 0.0)
|
|
plotter->Fill2D("Benchmark_SX3_CmaxOverAnode_vs_phi", 180, -180, 180, 250, 0, 5,
|
|
sx3event.pos.Phi() * 180. / M_PI, cSumE_bm / aSumE_bm, "Benchmark_SX3_ref");
|
|
|
|
// Smearing setup
|
|
double sx3_phi_pitch = 6.5 * (M_PI / 180.0);
|
|
double smeared_phi = sx3event.pos.Phi() + rand.Uniform(-sx3_phi_pitch / 2.0, sx3_phi_pitch / 2.0);
|
|
TVector3 smeared_sx3_pos(sx3event.pos.Perp() * TMath::Cos(smeared_phi), sx3event.pos.Perp() * TMath::Sin(smeared_phi), sx3event.pos.Z());
|
|
|
|
auto doA1C1 = [&](const std::string &tag, const TVector3 &si_point, bool dither, bool hybrid)
|
|
{
|
|
if (!a1c1Good)
|
|
return;
|
|
double pcz = dither ? rand.Gaus(xo_a1c1.Z(), dither_sigma) : xo_a1c1.Z();
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz));
|
|
fillSuite(tag, pcz, vtx);
|
|
fillVsRef(tag, pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
auto doAnodeOnly = [&](const std::string &tag, double phi_use, const TVector3 &si_point, bool dither, bool hybrid)
|
|
{
|
|
TVector3 pc = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, phi_use);
|
|
TVector3 vtx0 = vertexFrom(si_point, pc);
|
|
if (!(vtx0.Perp() <= 6.0 && vtx0.Z() >= -173.6))
|
|
return;
|
|
double pcz = dither ? rand.Gaus(pc.Z(), dither_sigma) : pc.Z();
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(pc.X(), pc.Y(), pcz));
|
|
fillSuite(tag, pcz, vtx);
|
|
fillVsRef(tag, pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
// --- A1C1 with the cfrac sub-cell model (linear centre-fold) ---
|
|
// Anchored on the fired cathode (xo_a1c1.Z()); cell from the anode z;
|
|
// |offset| from cfrac. REJECT (no fill) when out of band or inconsistent.
|
|
auto doA1C1Model = [&](const std::string &tag, const TVector3 &si_point)
|
|
{
|
|
if (!a1c1Good || cfrac < 0.0)
|
|
return;
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, si_point.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, xo_a1c1.Z(), z_a1c0, std::get<0>(cMaxWire));
|
|
if (!(s.inband && s.pitchok))
|
|
return;
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(xo_a1c1.X(), xo_a1c1.Y(), s.pcz));
|
|
fillSuite(tag, s.pcz, vtx);
|
|
fillVsRef(tag, s.pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
if (phicut && PCSX3TimeCut)
|
|
{
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
{
|
|
fillSuite("A1C2", pcz_ref, vtx_ref);
|
|
doA1C1("A1C1", sx3event.pos, true, false);
|
|
doAnodeOnly("A1C0", sx3event.pos.Phi(), sx3event.pos, true, false);
|
|
doA1C1("A1C1_Si", smeared_sx3_pos, false, false);
|
|
doAnodeOnly("A1C0_Si", smeared_phi, smeared_sx3_pos, false, false);
|
|
doA1C1("A1C1_Hyb", smeared_sx3_pos, true, true);
|
|
doAnodeOnly("A1C0_Hyb", smeared_phi, smeared_sx3_pos, true, true);
|
|
|
|
// cfrac-model-corrected A1C1, directly comparable to A1C1 / A1C2
|
|
doA1C1Model("A1C1_Cfrac", sx3event.pos);
|
|
|
|
// --- A1C1 charge-fraction diagnostics ---
|
|
if (a1c1Good && cfrac >= 0.0)
|
|
{
|
|
plotter->Fill1D("Benchmark_SX3_A1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_SX3_ref");
|
|
plotter->Fill2D("Benchmark_SX3_A1C1_cfrac_vs_ref", 400, -200, 200, 220, -0.05, 1.05,
|
|
pcz_ref, 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");
|
|
|
|
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();
|
|
auto fillCfracS = [&](const char *name, double truth)
|
|
{
|
|
double sgn = (truth >= zp) ? 1.0 : -1.0;
|
|
double znb = (sgn > 0) ? 1.0e30 : -1.0e30;
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
if (sgn > 0 && zg[i] > zp + 1e-6 && zg[i] < znb)
|
|
znb = zg[i];
|
|
if (sgn < 0 && zg[i] < zp - 1e-6 && zg[i] > znb)
|
|
znb = zg[i];
|
|
}
|
|
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_SX3_ref");
|
|
};
|
|
fillCfracS("Benchmark_SX3_A1C1_cfrac_vs_s", pcz_ref);
|
|
fillCfracS("Benchmark_SX3_A1C1_cfrac_vs_s_sx3pczguess", pczguess);
|
|
|
|
// folded about the cell CENTRE: f = |ref - z_center|/halfcell in [0,1]
|
|
for (int i = 0; i < 7; ++i)
|
|
{
|
|
if (pcz_ref <= zg[i] && pcz_ref > zg[i + 1])
|
|
{
|
|
double zc = 0.5 * (zg[i] + zg[i + 1]);
|
|
double half = 0.5 * (zg[i] - zg[i + 1]);
|
|
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");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- TRUE A1C1: genuine one-anode/one-cathode events. There is no
|
|
// A1C2 crossover reference for these, so compare only to the Si
|
|
// geometric guess (pczguess). xo_a1c1 is the real measured crossover. ----
|
|
else if (pcevent.multi1 == 1 && pcevent.multi2 == 1 && a1c1Good)
|
|
{
|
|
double pcz_raw = xo_a1c1.Z();
|
|
TVector3 vtx_raw = vertexFrom(sx3event.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw));
|
|
fillSuite("trueA1C1", pcz_raw, vtx_raw);
|
|
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C1_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pcz_raw, "Benchmark_SX3_trueA1C1");
|
|
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C1_minus_sx3pczguess", 400, -100, 100, pcz_raw - pczguess, "Benchmark_SX3_trueA1C1");
|
|
|
|
// cfrac sub-cell reconstruction, anchored on the FIRED CATHODE.
|
|
// Vertex-independent (no pczguess): the cell is the one adjacent to
|
|
// the cathode that fired (zf), the side is taken from the anode z,
|
|
// and the hit must land within one cell pitch of zf to be valid.
|
|
if (cfrac >= 0.0)
|
|
{
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, sx3event.pos.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, xo_a1c1.Z(), z_a1c0, std::get<0>(cMaxWire));
|
|
int cell = s.cell;
|
|
double f = s.f;
|
|
double pcz_cf = s.pcz;
|
|
bool valid = s.pitchok; // accept if consistent with the fired wire
|
|
|
|
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);
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_SX3_trueA1C1");
|
|
// reference-free per-cell cfrac (cell from geometry, no A1C2 ref): the
|
|
// offline fitter reads per-cell edges/percentiles to gain-match cfmin/k.
|
|
plotter->Fill2D("Benchmark_SX3_trueA1C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, "Benchmark_SX3_trueA1C1");
|
|
plotter->Fill2D("Benchmark_SX3_trueA1C1_f_vs_cell", 7, 0, 7, 260, -1.5, 2.5, cell + 0.5, f, "Benchmark_SX3_trueA1C1");
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_f", 260, -1.5, 2.5, f, "Benchmark_SX3_trueA1C1");
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, "Benchmark_SX3_trueA1C1");
|
|
// failure-reason breakdown (why the cfrac estimate is / isn't usable):
|
|
// 0 valid & f in [0,1] ideal, inside the calibrated band
|
|
// 1 valid & f in [-1,0) below band but within the pitch tolerance
|
|
// 2 valid & f in (1,3] above band but within the pitch tolerance
|
|
// 3 invalid: f < -1 cfrac far below band (cfmin too high / cathode low?)
|
|
// 4 invalid: f > 3 cfrac far above band (cfmin too low / cathode high?)
|
|
// 5 cell k<=0 cell not autocalibrated
|
|
int reason;
|
|
if (a1c1_k_cell[cell] <= 0.0)
|
|
reason = 5;
|
|
else if (!valid)
|
|
reason = (f < 0.0) ? 3 : 4;
|
|
else if (f < 0.0)
|
|
reason = 1;
|
|
else if (f > 1.0)
|
|
reason = 2;
|
|
else
|
|
reason = 0;
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_failreason", 6, 0, 6, reason + 0.5, "Benchmark_SX3_trueA1C1");
|
|
// valid-only breakdown (reason is 0/1/2 here): how clean the accepted
|
|
// sample is -- 0 ideal (f in [0,1]), 1 below-band, 2 above-band marginal.
|
|
if (valid)
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_validreason", 3, 0, 3, reason + 0.5, "Benchmark_SX3_trueA1C1");
|
|
// which cfrac band reconstructed this event: 0 = main, 1 = low (incomplete integration)
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_band", 2, 0, 2, s.band + 0.5, "Benchmark_SX3_trueA1C1");
|
|
if (valid)
|
|
plotter->Fill1D("Benchmark_SX3_trueA1C1_band_valid", 2, 0, 2, s.band + 0.5, "Benchmark_SX3_trueA1C1");
|
|
// source-run-only diagnostics: pczguess is meaningful only when the vertex is fixed
|
|
if (valid)
|
|
{
|
|
plotter->Fill1D("Benchmark_SX3_PCZ_trueA1C1_Cfrac_minus_sx3pczguess_DIAG", 400, -100, 100, pcz_cf - pczguess, "Benchmark_SX3_trueA1C1");
|
|
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C1_Cfrac_vs_sx3pczguess_DIAG", 400, -200, 200, 400, -200, 200, pczguess, pcz_cf, "Benchmark_SX3_trueA1C1");
|
|
}
|
|
}
|
|
|
|
// anode-only (A1C0) estimate for the same events
|
|
{
|
|
TVector3 pc = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, sx3event.pos.Phi());
|
|
TVector3 vtx0 = vertexFrom(sx3event.pos, pc);
|
|
if (vtx0.Perp() <= 6.0 && vtx0.Z() >= -173.6)
|
|
{
|
|
fillSuite("trueA1C0", pc.Z(), vtx0);
|
|
plotter->Fill2D("Benchmark_SX3_PCZ_trueA1C0_vs_sx3pczguess", 400, -200, 200, 400, -200, 200, pczguess, pc.Z(), "Benchmark_SX3_trueA1C1");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void PCQQQClusterAnalysis(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, 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>>> &cClusters)
|
|
{
|
|
static TRandom3 rand(0);
|
|
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
plotter->Fill1D("dt_pcA_qqqR", 640, -2000, 2000, qqqevent.Time1 - pcevent.Time1, "Timing");
|
|
plotter->Fill2D("dt_pcA_qqqR_vs_qqqRE", 640, -2000, 2000, 400, 0, 30, qqqevent.Time1 - pcevent.Time1, qqqevent.Energy1, "Timing");
|
|
plotter->Fill1D("dt_pcC_qqqW", 640, -2000, 2000, qqqevent.Time2 - pcevent.Time2, "Timing");
|
|
plotter->Fill2D("phiPC_vs_phiQQQ", 180, -360, 360, 180, -360, 360, qqqevent.pos.Phi() * 180 / M_PI, pcevent.pos.Phi() * 180 / M_PI, "Kinematics_Angles");
|
|
double sinTheta = TMath::Sin((qqqevent.pos - TVector3(0, 0, source_vertex)).Theta());
|
|
|
|
TVector3 x2(pcevent.pos);
|
|
TVector3 x1(qqqevent.pos);
|
|
TVector3 v = x2 - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin = x1 + t_minimum * v;
|
|
|
|
bool timecut = (qqqevent.Time1 - pcevent.Time1 < 150);
|
|
bool lowercut_cath = pcevent.Energy2 * sinTheta < 1 && (qqqevent.Energy2 < 5.0 || qqqevent.Energy1 < 5.0);
|
|
bool phicut = qqqevent.pos.Phi() <= pcevent.pos.Phi() + TMath::Pi() / 4. && qqqevent.pos.Phi() >= pcevent.pos.Phi() - TMath::Pi() / 4.;
|
|
|
|
if (lowercut_cath && phicut)
|
|
{
|
|
plotter->Fill1D("dt_pcA_qqqR_pidlow_PC1", 640, -2000, 2000, qqqevent.Time1 - pcevent.Time1, "Timing");
|
|
plotter->Fill2D("dt_pcA_qqqR_vs_qqqRE_pidlow_PC1", 640, -2000, 2000, 400, 0, 30, qqqevent.Time1 - pcevent.Time1, qqqevent.Energy1, "Timing");
|
|
plotter->Fill1D("dt_pcC_qqqW_pidlow_PC1", 640, -2000, 2000, qqqevent.Time2 - pcevent.Time2, "Timing");
|
|
}
|
|
if (timecut)
|
|
{
|
|
plotter->Fill2D("dE_E_AnodeQQQR", 400, 0, 30, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
plotter->Fill2D("dE_E_CathodeQQQR", 400, 0, 30, 800, 0, 10000, qqqevent.Energy2, pcevent.Energy2, "PID_dE_E");
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
plotter->Fill2D("dE_E_AnodeQQQR_a1c2", 400, 0, 30, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
plotter->Fill2D("dE_E_CathodeQQQR_a1c2", 400, 0, 30, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
if (pcevent.multi1 == 2 && pcevent.multi2 == 1)
|
|
plotter->Fill2D("dE_E_AnodeQQQR_a2c1", 400, 0, 30, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1, "PID_dE_E");
|
|
if (pcevent.multi1 == 2 && pcevent.multi2 == 1)
|
|
plotter->Fill2D("dE_E_CathodeQQQR_a2c1", 400, 0, 30, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
|
|
if (phicut)
|
|
{
|
|
plotter->Fill2D("dE2_E_AnodeQQQR_TC1PC1_pidlow" + std::to_string(lowercut_cath), 400, 0, 30, 800, 0, 4000, qqqevent.Energy1, pcevent.Energy1 * sinTheta, "PID_dE_E");
|
|
plotter->Fill2D("dE2_E_CathodeQQQW_TC1PC1_pidlow" + std::to_string(lowercut_cath), 400, 0, 30, 800, 0, 1000, qqqevent.Energy2, pcevent.Energy2 * sinTheta, "PID_dE_E");
|
|
plotter->Fill2D("E_theta_zoomin_AnodeQQQR_TC1PC1_pidlow" + std::to_string(lowercut_cath), 60, 0, 30, 300, 0, 15, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, qqqevent.Energy1, "Kinematics_Angles");
|
|
}
|
|
|
|
plotter->Fill2D("dE2_E_AnodeQQQR_TC1_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 4000, qqqevent.Energy1, pcevent.Energy1 * sinTheta, "PID_dE_E");
|
|
plotter->Fill2D("dE2_E_CathodeQQQR_TC1_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 1000, qqqevent.Energy2, pcevent.Energy2 * sinTheta, "PID_dE_E");
|
|
plotter->Fill2D("dEC_vs_dEA_TC1_PC" + std::to_string(phicut), 800, 0, 40000, 800, 0, 10000, pcevent.Energy1, pcevent.Energy2, "PID_dE_E");
|
|
plotter->Fill2D("qqqphi_vs_time", 2000, 0, 2000, 180, -360, 360, pcevent.Time1 * 1e-9, qqqevent.pos.Phi() * 180. / M_PI, "Timing");
|
|
|
|
plotter->Fill1D("dt_pcA_qqqR_timecut", 640, -2000, 2000, qqqevent.Time1 - pcevent.Time1, "Timing");
|
|
plotter->Fill1D("dt_pcC_qqqW_timecut", 640, -2000, 2000, qqqevent.Time2 - pcevent.Time2, "Timing");
|
|
plotter->Fill2D("dE_theta_AnodeQQQR", 90, 0, 90, 400, 0, 20000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy1, "Kinematics_Angles");
|
|
plotter->Fill2D("dE2_theta_AnodeQQQR_zoomin", 60, 0, 30, 400, 0, 5000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy1 * sinTheta, "Kinematics_Angles");
|
|
plotter->Fill2D("dE2_theta_AnodeQQQR", 90, 0, 90, 400, 0, 20000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy1 * sinTheta, "Kinematics_Angles");
|
|
plotter->Fill2D("phiPC_vs_phiQQQ_TimeCut", 180, -360, 360, 180, -360, 360, qqqevent.pos.Phi() * 180 / M_PI, pcevent.pos.Phi() * 180 / M_PI, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("Etot2_theta_AnodeQQQR", 75, 0, 90, 300, 0, 15, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, qqqevent.Energy1 + pcevent.Energy1 * anode_gain * sinTheta, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("dE_theta_CathodeQQQR", 75, 0, 90, 800, 0, 10000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy2, "Kinematics_Angles");
|
|
plotter->Fill2D("dE2_theta_CathodeQQQR", 75, 0, 90, 800, 0, 10000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy2 * sinTheta, "Kinematics_Angles");
|
|
plotter->Fill2D("dE2_theta_CathodeQQQR_zoomin", 60, 0, 30, 800, 0, 3000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, pcevent.Energy2 * sinTheta, "Kinematics_Angles");
|
|
|
|
plotter->Fill2D("dE_phi_AnodeQQQR", 100, -180, 180, 800, 0, 40000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Phi() * 180 / M_PI, pcevent.Energy1, "Kinematics_Angles");
|
|
plotter->Fill2D("dE_phi_CathodeQQQR", 100, -180, 180, 800, 0, 10000, (qqqevent.pos - TVector3(0, 0, source_vertex)).Phi() * 180 / M_PI, pcevent.Energy2, "Kinematics_Angles");
|
|
plotter->Fill1D("PCZ", 800, -200, 200, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
double pcz_guess_37 = 37. / TMath::Tan((qqqevent.pos - TVector3(0, 0, source_vertex)).Theta()) + source_vertex;
|
|
plotter->Fill2D("pczguess_vs_pc_37", 180, 0, 200, 150, 0, 200, pcz_guess_37, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
double pcz_guess_42 = 42. / TMath::Tan((qqqevent.pos - TVector3(0, 0, source_vertex)).Theta()) + source_vertex;
|
|
plotter->Fill2D("pczguess_vs_pc_42", 180, 0, 200, 150, 0, 200, pcz_guess_42, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
double pcz_guess_int = z_to_crossover_rho(pcevent.pos.Z()) / TMath::Tan((qqqevent.pos - TVector3(0, 0, source_vertex)).Theta()) + source_vertex;
|
|
plotter->Fill2D("pczguess_vs_pc_int", 400, -200, 200, 600, -400, 400, pcz_guess_int, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
{
|
|
double pcz_fix = pcfix_func.Eval(pcevent.pos.Z());
|
|
TVector3 x2f(pcevent.pos.X(), pcevent.pos.Y(), pcz_fix);
|
|
TVector3 v = x2f - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
|
|
double sinTheta_customV = TMath::Sin((qqqevent.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Theta());
|
|
plotter->Fill2D("dE3_E_CathodeQQQW_A1C2_TC1_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 10000, qqqevent.Energy2, pcevent.Energy2 * sinTheta_customV, "PID_dE_E");
|
|
plotter->Fill2D("dE3_E_AnodeQQQR_A1C2_TC1_PC" + std::to_string(phicut), 400, 0, 30, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy1 * sinTheta_customV, "PID_dE_E");
|
|
|
|
plotter->Fill1D("VertexRecon_pczfix_qqq", 800, -300, 300, r_rhoMin_fix.Z(), "Vertex_Reconstruction");
|
|
plotter->Fill1D("VertexRecon_pczfix", 800, -300, 300, r_rhoMin_fix.Z(), "Vertex_Reconstruction");
|
|
plotter->Fill1D("VertexRecon_pczfix_qqq_PC" + std::to_string(phicut) + "_pidlow" + std::to_string(lowercut_cath), 800, -400, 400, r_rhoMin_fix.Z(), "Vertex_Reconstruction");
|
|
|
|
if (TMath::Abs(r_rhoMin_fix.Z()) < 200.0)
|
|
{
|
|
plotter->Fill2D("dE3_E_AnodeQQQR_A1C2_(vertex_fix_z/100)=" + std::to_string(floor(r_rhoMin_fix.Z() / 100.0)), 400, 0, 30, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1 * sinTheta_customV, "PID_dE_E");
|
|
plotter->Fill2D("dE3_E_CathodeQQQR_A1C2_(vertex_fix_z/100)=" + std::to_string(floor(r_rhoMin_fix.Z() / 100.0)), 400, 0, 30, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy2 * sinTheta_customV, "PID_dE_E");
|
|
}
|
|
|
|
plotter->Fill1D("pczfix_A1C2_1d_qqq", 600, -200, 200, pcz_fix, "Z_Reconstruction");
|
|
plotter->Fill2D("pczfix_vs_qqqpczguess_A1C2", 600, -200, 200, 600, -200, 200, pcz_guess_int, pcz_fix, "Z_Reconstruction");
|
|
plotter->Fill2D("pczguess_vs_pc_int_A1C2", 400, -200, 200, 600, -400, 400, pcz_guess_int, pcevent.pos.Z(), "Z_Reconstruction");
|
|
plotter->Fill2D("pczfix_residual_vs_pczguess_A1C2", 600, -200, 200, 200, -100, 100, pcz_guess_37, pcz_fix - pcz_guess_37, "Residuals");
|
|
plotter->Fill2D("pczfix_residual_vs_phi_A1C2", 200, 0, 6.28, 200, -100, 100, r_rhoMin_fix.Phi(), pcz_fix - pcz_guess_37, "Residuals");
|
|
plotter->Fill1D("pczfix-qqqpczguess_A1C2", 200, -100, 100, pcz_fix - pcz_guess_37, "Residuals");
|
|
plotter->Fill1D("pczfix-qqqpczint_A1C2", 200, -100, 100, pcz_fix - pcz_guess_int, "Residuals");
|
|
plotter->Fill1D("pczguess_vs_int_residualsqqq", 200, -50, 50, pcz_guess_int - pcz_guess_37, "Residuals");
|
|
|
|
double path_length = (qqqevent.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Mag() * 0.1;
|
|
double qqqEfix = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy1) - path_length);
|
|
double qqqEfix_p = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(qqqevent.Energy1) - path_length);
|
|
|
|
plotter->Fill2D("E_thetaf_AnodeQQQR_TC1_PC" + std::to_string(phicut), 180, 0, 180, 600, 0, 15, (qqqevent.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Theta() * 180 / M_PI, qqqevent.Energy1, "Kinematics_Angles");
|
|
if (lowercut_cath)
|
|
plotter->Fill2D("Ef_thetaf_AnodeQQQR_TC1_PC" + std::to_string(phicut) + "_pidlow" + std::to_string(lowercut_cath), 180, 0, 180, 600, 0, 15, (qqqevent.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Theta() * 180 / M_PI, qqqEfix_p, "Kinematics_Angles");
|
|
else
|
|
{
|
|
std::string zcut = "_" + std::to_string((TMath::Abs(r_rhoMin_fix.Z()) < 180));
|
|
plotter->Fill2D("Ef_thetaf_AnodeQQQR_TC1_PC" + std::to_string(phicut) + "_pidlow" + std::to_string(lowercut_cath) + zcut, 180, 0, 180, 600, 0, 15, (qqqevent.pos - TVector3(0, 0, r_rhoMin_fix.Z())).Theta() * 180 / M_PI, qqqEfix, "Kinematics_Angles");
|
|
}
|
|
|
|
plotter->Fill2D("dE3_Ef_AnodeQQQR_TC1" + std::to_string(phicut) + "_pidlow" + std::to_string(lowercut_cath), 600, 0, 15, 800, 0, 40000, qqqEfix, pcevent.Energy1 * sinTheta_customV, "PID_dE_E");
|
|
plotter->Fill2D("dE3_Ef_CathodeQQQR_TC1PC" + std::to_string(phicut) + "_pidlow" + std::to_string(lowercut_cath), 600, 0, 15, 800, 0, 10000, qqqEfix, pcevent.Energy2 * sinTheta_customV, "PID_dE_E");
|
|
}
|
|
|
|
//-----------------------Benchmarking Method for Source Runs (QQQ)------------------------//
|
|
if (BenchMark && aClusters.size() == 1 && cClusters.size() == 1)
|
|
{
|
|
const auto &aCl = aClusters.front();
|
|
const auto &cCl = cClusters.front();
|
|
|
|
auto vertexFrom = [](const TVector3 &si, const TVector3 &pcpoint)
|
|
{
|
|
TVector3 vf = pcpoint - si;
|
|
double tm = -1.0 * (si.X() * vf.X() + si.Y() * vf.Y()) / (vf.X() * vf.X() + vf.Y() * vf.Y());
|
|
return TVector3(si + tm * vf);
|
|
};
|
|
|
|
auto fillSuite = [&](const std::string &tag, double pcz_method, const TVector3 &vtx)
|
|
{
|
|
plotter->Fill1D("Benchmark_QQQ_VertexZ_" + tag, 800, -400, 400, vtx.Z(), "Benchmark_QQQ");
|
|
plotter->Fill1D("Benchmark_QQQ_VertexZ_" + tag + "_TC" + std::to_string(timecut) + "_PC" + std::to_string(phicut), 800, -400, 400, vtx.Z(), "Benchmark_QQQ");
|
|
plotter->Fill2D("Benchmark_QQQ_VertexXY_" + tag, 200, -100, 100, 200, -100, 100, vtx.X(), vtx.Y(), "Benchmark_QQQ");
|
|
plotter->Fill1D("Benchmark_QQQ_PCZ_" + tag, 600, -200, 200, pcz_method, "Benchmark_QQQ");
|
|
};
|
|
|
|
auto fillVsRef = [&](const std::string &tag, double pcz_method, const TVector3 &vtx, double pcz_ref, const TVector3 &vtx_ref)
|
|
{
|
|
plotter->Fill2D("Benchmark_QQQ_PCZ_" + tag + "_vs_ref", 400, -200, 200, 400, -200, 200, pcz_ref, pcz_method, "Benchmark_QQQ_ref");
|
|
plotter->Fill1D("Benchmark_QQQ_PCZ_" + tag + "_minus_ref", 400, -100, 100, pcz_method - pcz_ref, "Benchmark_QQQ_ref");
|
|
};
|
|
|
|
double pcz_ref = pcfix_func.Eval(pcevent.pos.Z());
|
|
TVector3 vtx_ref = vertexFrom(qqqevent.pos, TVector3(pcevent.pos.X(), pcevent.pos.Y(), pcz_ref));
|
|
|
|
auto pw_tuple = pwinstance.GetPseudoWire(aCl, "ANODE");
|
|
std::pair<TVector3, TVector3> apwire_bm = std::get<0>(pw_tuple);
|
|
|
|
auto cMaxWire = *std::max_element(cCl.begin(), cCl.end(), [](const auto &a, const auto &b)
|
|
{ return std::get<1>(a) < std::get<1>(b); });
|
|
std::vector<std::tuple<int, double, double>> cOne = {cMaxWire};
|
|
|
|
auto xo_tuple = pwinstance.FindCrossoverProperties(aCl, cOne);
|
|
TVector3 xo_a1c1 = std::get<0>(xo_tuple);
|
|
double alpha_a1c1 = std::get<1>(xo_tuple);
|
|
bool a1c1Good = (alpha_a1c1 != 9999999 && std::get<2>(xo_tuple) != -1);
|
|
|
|
// --- A1C1 charge-fraction diagnostic ---
|
|
// One cathode in A1C1, so the z-sensitive variable is cathode-vs-anode
|
|
// charge (the V across each cell), not cathode/cathode. Use pseudo-wire
|
|
// sums for gain-consistent energies.
|
|
double aSumE_bm = std::get<1>(pw_tuple); // anode sum (reuse pw_tuple)
|
|
double cSumE_bm = std::get<1>(cMaxWire); // Extract the energy directly!
|
|
double ac_sum = aSumE_bm + cSumE_bm;
|
|
double cfrac = (ac_sum > 0.0) ? cSumE_bm / ac_sum : -1.0; // bounded [0,1]
|
|
|
|
// Cmax/anode vs Si phi for ALL events reaching this block (not just the
|
|
// A1C2 subset). Cmax = single max-E cathode wire (cSumE_bm), anode =
|
|
// anode pseudo-wire sum. Ratio is unbounded above (arbitrary cathode gains).
|
|
if (aSumE_bm > 0.0)
|
|
plotter->Fill2D("Benchmark_QQQ_CmaxOverAnode_vs_phi", 180, -180, 180, 250, 0, 5,
|
|
qqqevent.pos.Phi() * 180. / M_PI, cSumE_bm / aSumE_bm, "Benchmark_QQQ_ref");
|
|
|
|
// Smearing Setup
|
|
double qqq_wedge_pitch = (87.0 / 16.0) * (M_PI / 180.0);
|
|
double qqq_ring_pitch = 48.0 / 16.0;
|
|
double smeared_phi = qqqevent.pos.Phi() + rand.Uniform(-qqq_wedge_pitch / 2.0, qqq_wedge_pitch / 2.0);
|
|
double smeared_rho = qqqevent.pos.Perp() + rand.Uniform(-qqq_ring_pitch / 2.0, qqq_ring_pitch / 2.0);
|
|
|
|
TVector3 smeared_qqq_pos(smeared_rho * TMath::Cos(smeared_phi), smeared_rho * TMath::Sin(smeared_phi), qqqevent.pos.Z());
|
|
|
|
auto doA1C1 = [&](const std::string &tag, const TVector3 &si_point, bool dither, bool hybrid)
|
|
{
|
|
if (!a1c1Good)
|
|
return;
|
|
double pcz = dither ? rand.Gaus(xo_a1c1.Z(), dither_sigma) : xo_a1c1.Z();
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz));
|
|
fillSuite(tag, pcz, vtx);
|
|
fillVsRef(tag, pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
auto doAnodeOnly = [&](const std::string &tag, double phi_use, const TVector3 &si_point, bool dither, bool hybrid)
|
|
{
|
|
TVector3 pc = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, phi_use);
|
|
TVector3 vtx0 = vertexFrom(si_point, pc);
|
|
if (!(vtx0.Perp() <= 6.0 && vtx0.Z() >= -173.6))
|
|
return;
|
|
double sigma = hybrid ? (dither_sigma_c0 / 2.0) : dither_sigma_c0;
|
|
double pcz = dither ? rand.Gaus(pc.Z(), sigma) : pc.Z();
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(pc.X(), pc.Y(), pcz));
|
|
fillSuite(tag, pcz, vtx);
|
|
fillVsRef(tag, pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
// --- A1C1 with the cfrac sub-cell model (QQQ, linear centre-fold) ---
|
|
// Anchored on the fired cathode (xo_a1c1.Z()); cell from the anode-only
|
|
// z (z_a1c0, independent of the Si guess); |offset| from cfrac. REJECT
|
|
// when out of band or inconsistent with the fired wire.
|
|
auto doA1C1Model = [&](const std::string &tag, const TVector3 &si_point)
|
|
{
|
|
if (!a1c1Good || cfrac < 0.0)
|
|
return;
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, si_point.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, xo_a1c1.Z(), z_a1c0, std::get<0>(cMaxWire));
|
|
if (!(s.inband && s.pitchok))
|
|
return;
|
|
TVector3 vtx = vertexFrom(si_point, TVector3(xo_a1c1.X(), xo_a1c1.Y(), s.pcz));
|
|
fillSuite(tag, s.pcz, vtx);
|
|
fillVsRef(tag, s.pcz, vtx, pcz_ref, vtx_ref);
|
|
};
|
|
|
|
if (phicut && timecut)
|
|
{
|
|
if (pcevent.multi1 == 1 && pcevent.multi2 == 2)
|
|
{
|
|
fillSuite("A1C2", pcz_ref, vtx_ref);
|
|
doA1C1("A1C1", qqqevent.pos, true, false);
|
|
doAnodeOnly("A1C0", qqqevent.pos.Phi(), qqqevent.pos, true, false);
|
|
doA1C1("A1C1_Si", smeared_qqq_pos, false, false);
|
|
doAnodeOnly("A1C0_Si", smeared_phi, smeared_qqq_pos, false, false);
|
|
doA1C1("A1C1_Hyb", smeared_qqq_pos, true, true);
|
|
doAnodeOnly("A1C0_Hyb", smeared_phi, smeared_qqq_pos, true, true);
|
|
|
|
// --- Execute the cfrac-model for QQQ ---
|
|
doA1C1Model("A1C1_Cfrac", qqqevent.pos);
|
|
|
|
// --- A1C1 charge-fraction diagnostics ---
|
|
if (a1c1Good && cfrac >= 0.0)
|
|
{
|
|
plotter->Fill1D("Benchmark_QQQ_A1C1_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_A1C1_cfrac_vs_qqqpczguess", 400, -200, 200, 220, -0.05, 1.05,
|
|
pcz_guess_37, cfrac, "Benchmark_QQQ_ref");
|
|
|
|
// --- cfrac vs normalized cell position (collapses all cells) ---
|
|
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();
|
|
auto fillCfracS = [&](const char *name, double truth)
|
|
{
|
|
double sgn = (truth >= zp) ? 1.0 : -1.0;
|
|
double znb = (sgn > 0) ? 1.0e30 : -1.0e30;
|
|
for (int i = 0; i < 8; ++i)
|
|
{
|
|
if (sgn > 0 && zg[i] > zp + 1e-6 && zg[i] < znb)
|
|
znb = zg[i];
|
|
if (sgn < 0 && zg[i] < zp - 1e-6 && zg[i] > znb)
|
|
znb = zg[i];
|
|
}
|
|
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");
|
|
};
|
|
fillCfracS("Benchmark_QQQ_A1C1_cfrac_vs_s", pcz_ref);
|
|
fillCfracS("Benchmark_QQQ_A1C1_cfrac_vs_s_qqqpczguess", pcz_guess_37);
|
|
|
|
// --- cfrac vs cell-centre fold: f = |ref - z_center|/halfcell in [0,1] ---
|
|
for (int i = 0; i < 7; ++i)
|
|
{
|
|
if (pcz_ref <= zg[i] && pcz_ref > zg[i + 1])
|
|
{
|
|
double zc = 0.5 * (zg[i] + zg[i + 1]);
|
|
double half = 0.5 * (zg[i] - zg[i + 1]);
|
|
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");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- TRUE A1C1 (QQQ): genuine one-anode/one-cathode events. No A1C2
|
|
// crossover reference exists, so compare only to the geometric guess
|
|
// (pcz_guess_int). xo_a1c1 is the real measured crossover. ----
|
|
else if (pcevent.multi1 >= 1 && pcevent.multi2 == 1 && a1c1Good)
|
|
{
|
|
double pcz_raw = xo_a1c1.Z();
|
|
TVector3 vtx_raw = vertexFrom(qqqevent.pos, TVector3(xo_a1c1.X(), xo_a1c1.Y(), pcz_raw));
|
|
fillSuite("trueA1C1", pcz_raw, vtx_raw);
|
|
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C1_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_raw, "Benchmark_QQQ_trueA1C1");
|
|
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C1_minus_qqqpczguess", 400, -100, 100, pcz_raw - pcz_guess_int, "Benchmark_QQQ_trueA1C1");
|
|
|
|
// cfrac sub-cell reconstruction, anchored on the FIRED CATHODE.
|
|
// Vertex-independent (no pcz_guess_int): the cell is the one adjacent
|
|
// to the cathode that fired (zf), the side is taken from the anode z,
|
|
// and the hit must land within one cell pitch of zf to be valid.
|
|
if (cfrac >= 0.0)
|
|
{
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, qqqevent.pos.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, xo_a1c1.Z(), z_a1c0, std::get<0>(cMaxWire));
|
|
int cell = s.cell;
|
|
double f = s.f;
|
|
double pcz_cf = s.pcz;
|
|
bool valid = s.pitchok; // accept if consistent with the fired wire
|
|
|
|
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);
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_cfrac", 220, -0.05, 1.05, cfrac, "Benchmark_QQQ_trueA1C1");
|
|
// plotter->Fill1D("Benchmark_QQQ_trueA1C1_cfrac_cathode" + std::to_string(pcevent.Cathodech), 220, -0.05, 1.05, cfrac, "Benchmark_trueA1C1_cathode");
|
|
// reference-free per-cell cfrac (cell from geometry, no A1C2 ref): the
|
|
// offline fitter reads per-cell edges/percentiles to gain-match cfmin/k.
|
|
plotter->Fill2D("Benchmark_QQQ_trueA1C1_cfrac_vs_cell", 7, 0, 7, 220, -0.05, 1.05, cell + 0.5, cfrac, "Benchmark_QQQ_trueA1C1");
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_f", 260, -1.5, 2.5, f, "Benchmark_QQQ_trueA1C1");
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_valid", 2, 0, 2, valid ? 1.0 : 0.0, "Benchmark_QQQ_trueA1C1");
|
|
// failure-reason breakdown (why the cfrac estimate is / isn't usable):
|
|
// 0 valid & f in [0,1] ideal, inside the calibrated band
|
|
// 1 valid & f in [-1,0) below band but within the pitch tolerance
|
|
// 2 valid & f in (1,3] above band but within the pitch tolerance
|
|
// 3 invalid: f < -1 cfrac far below band (cfmin too high / cathode low?)
|
|
// 4 invalid: f > 3 cfrac far above band (cfmin too low / cathode high?)
|
|
// 5 cell k<=0 cell not autocalibrated
|
|
int reason;
|
|
if (a1c1_k_cell[cell] <= 0.0)
|
|
reason = 5;
|
|
else if (!valid)
|
|
reason = (f < 0.0) ? 3 : 4;
|
|
else if (f < 0.0)
|
|
reason = 1;
|
|
else if (f > 1.0)
|
|
reason = 2;
|
|
else
|
|
reason = 0;
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_failreason", 6, 0, 6, reason + 0.5, "Benchmark_QQQ_trueA1C1");
|
|
// valid-only breakdown (reason is 0/1/2 here): how clean the accepted
|
|
// sample is -- 0 ideal (f in [0,1]), 1 below-band, 2 above-band marginal.
|
|
if (valid)
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_validreason", 3, 0, 3, reason + 0.5, "Benchmark_QQQ_trueA1C1");
|
|
// which cfrac band reconstructed this event: 0 = main, 1 = low (incomplete integration)
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_band", 2, 0, 2, s.band + 0.5, "Benchmark_QQQ_trueA1C1");
|
|
if (valid)
|
|
plotter->Fill1D("Benchmark_QQQ_trueA1C1_band_valid", 2, 0, 2, s.band + 0.5, "Benchmark_QQQ_trueA1C1");
|
|
// source-run-only diagnostics: pcz_guess_int is meaningful only when the vertex is fixed
|
|
if (valid)
|
|
{
|
|
plotter->Fill1D("Benchmark_QQQ_PCZ_trueA1C1_Cfrac_minus_qqqpczguess_DIAG", 400, -100, 100, pcz_cf - pcz_guess_int, "Benchmark_QQQ_trueA1C1");
|
|
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C1_Cfrac_vs_qqqpczguess_DIAG", 400, -200, 200, 400, -200, 200, pcz_guess_int, pcz_cf, "Benchmark_QQQ_trueA1C1");
|
|
}
|
|
}
|
|
|
|
// anode-only (A1C0) estimate for the same events
|
|
{
|
|
TVector3 pc = pwinstance.getClosestWirePosAtWirePhi(apwire_bm, qqqevent.pos.Phi());
|
|
TVector3 vtx0 = vertexFrom(qqqevent.pos, pc);
|
|
if (vtx0.Perp() <= 6.0 && vtx0.Z() >= -173.6)
|
|
{
|
|
fillSuite("trueA1C0", pc.Z(), vtx0);
|
|
plotter->Fill2D("Benchmark_QQQ_PCZ_trueA1C0_vs_qqqpczguess", 400, -200, 200, 400, -200, 200, pcz_guess_int, pc.Z(), "Benchmark_QQQ_trueA1C1");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
double qqqrho = qqqevent.pos.Perp();
|
|
double qqqz = (qqqevent.pos - TVector3(0, 0, source_vertex)).Z();
|
|
double tan_theta = qqqrho / qqqz;
|
|
double pcz_guess_int2 = z_to_crossover_rho(pcevent.pos.Z()) / tan_theta + source_vertex;
|
|
plotter->Fill2D("pczguess_vs_pc_int2", 180, 0, 200, 150, 0, 200, pcz_guess_int2, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
double qqqz2 = (qqqevent.pos - r_rhoMin).Z();
|
|
double tan_theta2 = qqqrho / qqqz2;
|
|
double pcz_guess_int3 = z_to_crossover_rho(pcevent.pos.Z()) / tan_theta2 + r_rhoMin.Z();
|
|
plotter->Fill2D("pczguess_vs_pc_int3", 180, 0, 200, 150, 0, 200, pcz_guess_int3, pcevent.pos.Z(), "Z_Reconstruction");
|
|
|
|
double pcz_guess = pcz_guess_int;
|
|
plotter->Fill2D("pctheta_vs_qqqtheta_sv", 180, -360, 360, 180, -360, 360, (qqqevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, (pcevent.pos - TVector3(0, 0, source_vertex)).Theta() * 180 / M_PI, "Kinematics_Angles");
|
|
plotter->Fill2D("pctheta_vs_qqqtheta_rmz", 180, -360, 360, 180, -360, 360, (qqqevent.pos - TVector3(0, 0, r_rhoMin.Z())).Theta() * 180 / M_PI, (pcevent.pos - TVector3(0, 0, r_rhoMin.Z())).Theta() * 180 / M_PI, "Kinematics_Angles");
|
|
plotter->Fill2D("pctheta_vs_qqqtheta_rm", 180, -360, 360, 180, -360, 360, (qqqevent.pos - r_rhoMin).Theta() * 180 / M_PI, (pcevent.pos - r_rhoMin).Theta() * 180 / M_PI, "Kinematics_Angles");
|
|
plotter->Fill2D("pczguess_vs_pc_phi=" + std::to_string(qqqevent.pos.Phi() * 180. / M_PI), 300, 0, 200, 150, 0, 200, pcz_guess, pcevent.pos.Z(), "Z_Reconstruction");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// We pass plotter as a pointer, strings as const references (for speed),
|
|
// and the Kinematics object as a reference.
|
|
void FillExcitationHistograms(HistPlotter *plotter,
|
|
const std::string &targetName,
|
|
const std::string &anodeLabel,
|
|
Kinematics &kin,
|
|
double energyMeV,
|
|
double thetaRad)
|
|
{
|
|
// 1. Convert angle to degrees for your kinematics class
|
|
double thetaDeg = thetaRad * 180.0 / M_PI;
|
|
|
|
// 2. Calculate the Excitation Energy (Ex)
|
|
double Ex = kin.getExc(energyMeV, thetaDeg);
|
|
|
|
// 3. Dynamically build the histogram names
|
|
// Example: "27Al_Ex_a1" or "17F_Kinematics_aN"
|
|
std::string hNameEx = targetName + "_Ex_" + anodeLabel;
|
|
std::string hNameKin = targetName + "_Kinematics_" + anodeLabel;
|
|
|
|
// 4. Group them in a specific folder in the ROOT file
|
|
std::string folderName = "Excitation_" + targetName;
|
|
|
|
// 5. Fill the Histograms
|
|
// Fill 1D Excitation Energy plot
|
|
plotter->Fill1D(hNameEx, 400, -10, 20, Ex, folderName);
|
|
|
|
// Fill 2D Kinematics plot (Angle vs Energy)
|
|
plotter->Fill2D(hNameKin, 180, 0, 180, 500, 0, 25, thetaDeg, energyMeV, folderName);
|
|
}
|
|
|
|
void TrackRecon::OldAnalysis()
|
|
{
|
|
int aID = 0, cID = 0;
|
|
double aE = 0, cE = 0;
|
|
double aESum = 0, cESum = 0;
|
|
double aEMax = 0, cEMax = 0;
|
|
int aIDMax = 0, cIDMax = 0;
|
|
|
|
if (anodeHits.size() >= 1 && cathodeHits.size() >= 1)
|
|
{
|
|
// 2. CRITICAL FIX: Define reference vector 'a'
|
|
// In Analyzer.cxx, 'a' was left over from the loop. We use the first anode wire as reference here.
|
|
// (Assuming pwinstance.An is populated and wires are generally parallel).
|
|
TVector3 refAnode = pwinstance.An[0].first - pwinstance.An[0].second;
|
|
|
|
{
|
|
for (const auto &anode : anodeHits)
|
|
{
|
|
aID = anode.first;
|
|
aE = anode.second;
|
|
aESum += aE;
|
|
if (aE > aEMax)
|
|
{
|
|
aEMax = aE;
|
|
aIDMax = aID;
|
|
}
|
|
}
|
|
|
|
for (const auto &cathode : cathodeHits)
|
|
{
|
|
cID = cathode.first;
|
|
cE = cathode.second;
|
|
plotter->Fill2D("AnodeMax_Vs_Cathode_Coincidence_Matrix", 24, 0, 24, 24, 0, 24, aIDMax, cID, "hRawPC");
|
|
plotter->Fill2D("Anode_Vs_Cathode_Coincidence_Matrix", 24, 0, 24, 24, 0, 24, aID, cID, "hRawPC");
|
|
plotter->Fill2D("Anode_Vs_Cathode_Coincidence_Matrix_qqq" + std::to_string(HitNonZero), 24, 0, 24, 24, 0, 24, aID, cID, "hRawPC");
|
|
plotter->Fill2D("Anode_vs_CathodeE", 2000, 0, 30000, 2000, 0, 30000, aE, cE, "hGMPC");
|
|
plotter->Fill2D("CathodeMult_V_CathodeE", 6, 0, 6, 2000, 0, 30000, cathodeHits.size(), cE, "hGMPC");
|
|
/*for (int j = -4; j < 3; j++)
|
|
{
|
|
if ((aIDMax + 24 + j) % 24 == 23 - cID)
|
|
{
|
|
corrcatMax.push_back(std::pair<int, double>(cID, cE));
|
|
cESum += cE;
|
|
}
|
|
}*/
|
|
if (((aIDMax + cID) % 24) >= 20 || ((aIDMax + cID) % 24) <= 3)
|
|
{
|
|
corrcatMax.push_back(std::pair<int, double>(cID, cE));
|
|
cESum += cE;
|
|
if (cE > cEMax)
|
|
{
|
|
cEMax = cE;
|
|
cIDMax = cID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TVector3 anodeIntersection, vector_closest_to_z;
|
|
anodeIntersection.Clear();
|
|
vector_closest_to_z.Clear();
|
|
if (corrcatMax.size() > 0)
|
|
{
|
|
double x = 0, y = 0, z = 0;
|
|
for (const auto &corr : corrcatMax)
|
|
{
|
|
if (pwinstance.Crossover[aIDMax][corr.first][0].z > 9000000)
|
|
continue;
|
|
if (cESum > 0)
|
|
{
|
|
x += (corr.second) / cESum * pwinstance.Crossover[aIDMax][corr.first][0].x;
|
|
y += (corr.second) / cESum * pwinstance.Crossover[aIDMax][corr.first][0].y;
|
|
z += (corr.second) / cESum * pwinstance.Crossover[aIDMax][corr.first][0].z;
|
|
}
|
|
}
|
|
if (x == 0 && y == 0 && z == 0)
|
|
;
|
|
// to ignore events with no valid crossover points
|
|
else
|
|
{
|
|
anodeIntersection = TVector3(x, y, z);
|
|
// std::cout << "Anode Intersection: " << anodeIntersection.X() << ", " << anodeIntersection.Y() << ", " << anodeIntersection.Z() << " " << aIDMax << std::endl;
|
|
}
|
|
}
|
|
bool PCQQQPhiCut = false;
|
|
// flip the algorithm for cathode 1 multi anode events
|
|
if ((hitPos.Phi() > (anodeIntersection.Phi() - TMath::PiOver4())) && (hitPos.Phi() < (anodeIntersection.Phi() + TMath::PiOver4())))
|
|
{
|
|
PCQQQPhiCut = true;
|
|
}
|
|
|
|
if (anodeIntersection.Z() != 0 && anodeIntersection.Perp() > 0 && HitNonZero)
|
|
{
|
|
plotter->Fill1D("PC_Z_Projection", 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
plotter->Fill2D("Z_Proj_VsDelTime", 600, -300, 300, 200, -2000, 2000, anodeIntersection.Z(), anodeT - cathodeT, "hPCzQQQ");
|
|
plotter->Fill2D("IntPhi_vs_QQQphi", 100, -200, 200, 80, -200, 200, anodeIntersection.Phi() * 180. / TMath::Pi(), hitPos.Phi() * 180. / TMath::Pi(), "hPCQQQ");
|
|
// plotter->Fill2D("Inttheta_vs_QQQtheta", 90, 0, 180, 20, 0, 45, anodeIntersection.Theta() * 180. / TMath::Pi(), hitPos.Theta() * 180. / TMath::Pi(), "hPCQQQ");
|
|
// plotter->Fill2D("Inttheta_vs_QQQtheta_TC" + std::to_string(PCQQQTimeCut)+ "_PC"+std::to_string(PCQQQPhiCut), 90, 0, 180, 20, 0, 45, anodeIntersection.Theta() * 180. / TMath::Pi(), hitPos.Theta() * 180. / TMath::Pi(), "hPCQQQ");
|
|
plotter->Fill2D("IntPhi_vs_QQQphi_TC" + std::to_string(PCQQQTimeCut) + "PhiC" + std::to_string(PCQQQPhiCut), 100, -200, 200, 80, -200, 200, anodeIntersection.Phi() * 180. / TMath::Pi(), hitPos.Phi() * 180. / TMath::Pi(), "hPCQQQ");
|
|
}
|
|
|
|
if (anodeIntersection.Z() != 0 && anodeIntersection.Perp() > 0 && PCSX3TimeCut)
|
|
{
|
|
plotter->Fill1D("PC_Z_Projection_sx3", 600, -200, 200, anodeIntersection.Z(), "hPCZSX3");
|
|
}
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() >= 2)
|
|
plotter->Fill1D("PC_Z_Projection_TC" + std::to_string(PCQQQTimeCut) + "PhiC" + std::to_string(PCQQQPhiCut), 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() == 1)
|
|
{
|
|
plotter->Fill1D("PC_Z_proj_1C", 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
plotter->Fill2D("IntersectionPhi_vs_AnodeZ_1C", 400, -200, 200, 600, -300, 300, anodeIntersection.Phi() * 180. / TMath::Pi(), anodeIntersection.Z(), "hPCzQQQ");
|
|
}
|
|
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() == 2)
|
|
{
|
|
plotter->Fill1D("PC_Z_proj_2C", 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
plotter->Fill2D("IntersectionPhi_vs_AnodeZ_2C", 400, -200, 200, 600, -300, 300, anodeIntersection.Phi() * 180. / TMath::Pi(), anodeIntersection.Z(), "hGMPC");
|
|
}
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() > 2)
|
|
{
|
|
plotter->Fill1D("PC_Z_proj_nC", 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
plotter->Fill2D("IntersectionPhi_vs_AnodeZ_nC", 400, -200, 200, 600, -300, 300, anodeIntersection.Phi() * 180. / TMath::Pi(), anodeIntersection.Z(), "hGMPC");
|
|
}
|
|
if (anodeHits.size() > 0 && cathodeHits.size() > 0)
|
|
plotter->Fill2D("AHits_vs_CHits", 12, 0, 11, 6, 0, 5, anodeHits.size(), cathodeHits.size(), "hRawPC");
|
|
|
|
// make another plot with nearest neighbour constraint
|
|
bool hasNeighbourAnodes = false;
|
|
bool hasNeighbourCathodes = false;
|
|
|
|
// 1. Check Anodes for neighbours (including wrap-around 0-23)
|
|
for (size_t i = 0; i < anodeHits.size(); i++)
|
|
{
|
|
for (size_t j = i + 1; j < anodeHits.size(); j++)
|
|
{
|
|
int diff = std::abs(anodeHits[i].first - anodeHits[j].first);
|
|
if (diff == 1 || diff == 23)
|
|
{ // 23 handles the cylindrical wrap
|
|
hasNeighbourAnodes = true;
|
|
break;
|
|
}
|
|
}
|
|
if (hasNeighbourAnodes)
|
|
break;
|
|
}
|
|
|
|
// 2. Check Cathodes for neighbours (including wrap-around 0-23)
|
|
for (size_t i = 0; i < cathodeHits.size(); i++)
|
|
{
|
|
for (size_t j = i + 1; j < cathodeHits.size(); j++)
|
|
{
|
|
int diff = std::abs(cathodeHits[i].first - cathodeHits[j].first);
|
|
if (diff == 1 || diff == 23)
|
|
{
|
|
hasNeighbourCathodes = true;
|
|
break;
|
|
}
|
|
}
|
|
if (hasNeighbourCathodes)
|
|
break;
|
|
}
|
|
|
|
// ---------------------------------------------------------
|
|
// FILL PLOTS
|
|
// ---------------------------------------------------------
|
|
if (anodeHits.size() > 0 && cathodeHits.size() > 0)
|
|
{
|
|
#ifdef RAW_HISTOS
|
|
plotter->Fill2D("AHits_vs_CHits_NA" + std::to_string(hasNeighbourAnodes), 12, 0, 11, 6, 0, 5, anodeHits.size(), cathodeHits.size(), "hRawPC");
|
|
plotter->Fill2D("AHits_vs_CHits_NC" + std::to_string(hasNeighbourCathodes), 12, 0, 11, 6, 0, 5, anodeHits.size(), cathodeHits.size(), "hRawPC");
|
|
|
|
// Constraint Plot: Only fill if BOTH planes have adjacent hits
|
|
// This effectively removes events with only isolated single-wire hits (noise)
|
|
if (hasNeighbourAnodes && hasNeighbourCathodes)
|
|
{
|
|
plotter->Fill2D("AHits_vs_CHits_NN", 12, 0, 11, 6, 0, 5, anodeHits.size(), cathodeHits.size(), "hRawPC");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (HitNonZero && anodeIntersection.Z() != 0)
|
|
{
|
|
pwinstance.CalTrack2(hitPos, anodeIntersection);
|
|
plotter->Fill1D("VertexRecon", 600, -1300, 1300, pwinstance.GetZ0());
|
|
plotter->Fill1D("VertexRecon_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, pwinstance.GetZ0());
|
|
|
|
if (cathodeHits.size() == 2)
|
|
plotter->Fill1D("VertexRecon_2c_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, pwinstance.GetZ0());
|
|
|
|
TVector3 x2(anodeIntersection), x1(hitPos);
|
|
|
|
TVector3 v = x2 - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
vector_closest_to_z = x1 + t_minimum * v;
|
|
|
|
plotter->Fill1D("VertexRecon_Z_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, vector_closest_to_z.Z(), "customVertex");
|
|
|
|
if (qqqenergy < 4.0)
|
|
plotter->Fill1D("VertexRecon_Z(qqqE<4.0MeV)_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, vector_closest_to_z.Z(), "customVertex");
|
|
|
|
if (vector_closest_to_z.Perp() < 20)
|
|
{
|
|
plotter->Fill1D("VertexRecon_RadialCut_Z_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, vector_closest_to_z.Z(), "customVertex");
|
|
}
|
|
|
|
plotter->Fill2D("VertexRecon_XY_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 100, -100, 100, 100, -100, 100, vector_closest_to_z.X(), vector_closest_to_z.Y(), "customVertex");
|
|
if (cathodeHits.size() == 2)
|
|
{
|
|
plotter->Fill1D("VertexRecon2C_Z_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, vector_closest_to_z.Z(), "customVertex");
|
|
if (vector_closest_to_z.Perp() < 20)
|
|
{
|
|
plotter->Fill1D("VertexRecon2C_RadialCut_Z_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, vector_closest_to_z.Z(), "customVertex");
|
|
}
|
|
plotter->Fill2D("VertexRecon2C_XY_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 100, -100, 100, 100, -100, 100, vector_closest_to_z.X(), vector_closest_to_z.Y(), "customVertex");
|
|
plotter->Fill2D("VertexRecon2C_RhoZ_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 100, -100, 100, 600, -1300, 1300, vector_closest_to_z.Perp(), vector_closest_to_z.Z(), "customVertex");
|
|
plotter->Fill2D("VertexRecon2C_Z_vs_QQQE_TC" + std::to_string(PCQQQTimeCut) + "_PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, 800, 0, 20, vector_closest_to_z.Z(), qqqenergy, "customVertex");
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < qqq.multi; i++)
|
|
{
|
|
if (anodeIntersection.Perp() > 0)
|
|
{ // suppress x,y=0,0 events
|
|
if (PCQQQTimeCut)
|
|
{
|
|
plotter->Fill2D("PC_XY_Projection_QQQ_TimeCut" + std::to_string(qqq.id[i]), 400, -100, 100, 400, -100, 100, anodeIntersection.X(), anodeIntersection.Y(), "hPCQQQ");
|
|
plotter->Fill2D("PC_XY_Projection_QQQ_TimeCut" + std::to_string(qqq.id[i]), 400, -100, 100, 400, -100, 100, hitPos.X(), hitPos.Y(), "hPCQQQ");
|
|
}
|
|
plotter->Fill2D("PC_XY_Projection_QQQ" + std::to_string(qqq.id[i]), 400, -100, 100, 400, -100, 100, anodeIntersection.X(), anodeIntersection.Y(), "hPCQQQ");
|
|
}
|
|
for (int j = i + 1; j < qqq.multi; j++)
|
|
{
|
|
if (qqq.id[i] == qqq.id[j])
|
|
{
|
|
int chWedge = -1;
|
|
int chRing = -1;
|
|
double eWedge = 0.0;
|
|
double eWedgeMeV = 0.0;
|
|
double eRing = 0.0;
|
|
double eRingMeV = 0.0;
|
|
double tRing = 0.0;
|
|
int qqqID = -1;
|
|
if (qqq.ch[i] < 16 && qqq.ch[j] >= 16 && qqqGainValid[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16])
|
|
{
|
|
chWedge = qqq.ch[i];
|
|
eWedge = qqq.e[i] * qqqGain[qqq.id[i]][qqq.ch[i]][qqq.ch[j] - 16];
|
|
chRing = qqq.ch[j] - 16;
|
|
eRing = qqq.e[j];
|
|
tRing = static_cast<double>(qqq.t[j]);
|
|
qqqID = qqq.id[i];
|
|
}
|
|
else if (qqq.ch[j] < 16 && qqq.ch[i] >= 16 && qqqGainValid[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16])
|
|
{
|
|
chWedge = qqq.ch[j];
|
|
eWedge = qqq.e[j] * qqqGain[qqq.id[j]][qqq.ch[j]][qqq.ch[i] - 16];
|
|
chRing = qqq.ch[i] - 16;
|
|
tRing = static_cast<double>(qqq.t[i]);
|
|
eRing = qqq.e[i];
|
|
qqqID = qqq.id[i];
|
|
}
|
|
else
|
|
continue;
|
|
|
|
if (qqqCalibValid[qqq.id[i]][chWedge][chRing])
|
|
{
|
|
eWedgeMeV = eWedge * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
|
eRingMeV = eRing * qqqCalib[qqq.id[i]][chWedge][chRing] / 1000;
|
|
}
|
|
else
|
|
continue;
|
|
|
|
// if (anodeIntersection.Z() != 0)
|
|
{
|
|
plotter->Fill2D("PC_Z_vs_QQQRing", 600, -300, 300, 16, 0, 16, anodeIntersection.Z(), chRing, "hPCzQQQ");
|
|
plotter->Fill2D("PC_Z_vs_QQQRho", 600, -300, 300, 40, 40, 110, anodeIntersection.Z(), hitPos.Perp(), "hPCzQQQ");
|
|
}
|
|
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() == 2)
|
|
{
|
|
plotter->Fill2D("PC_Z_vs_QQQRing_2C", 600, -300, 300, 16, 0, 16, anodeIntersection.Z(), chRing, "hPCzQQQ");
|
|
plotter->Fill2D("PC_Z_vs_QQQRing_2C" + std::to_string(qqq.id[i]), 600, -300, 300, 16, 0, 16, anodeIntersection.Z(), chRing, "hPCzQQQ");
|
|
plotter->Fill2D("PC_Z_vs_QQQWedge_2C", 600, -300, 300, 16, 0, 16, anodeIntersection.Z(), chWedge, "hPCzQQQ");
|
|
}
|
|
plotter->Fill2D("VertexRecon_QQQRingTC" + std::to_string(PCQQQTimeCut) + "PhiC" + std::to_string(PCQQQPhiCut), 600, -1300, 1300, 16, 0, 16, vector_closest_to_z.Z(), chRing, "hPCQQQ");
|
|
double phi = TMath::ATan2(anodeIntersection.Y(), anodeIntersection.X()) * 180. / TMath::Pi();
|
|
plotter->Fill2D("PolarAngle_Vs_QQQWedge" + std::to_string(qqqID), 360, -360, 360, 16, 0, 16, phi, chWedge, "hPCQQQ");
|
|
// plotter->Fill2D("EdE_PC_vs_QQQ_timegate_ls1000"+std::to_string())
|
|
|
|
plotter->Fill2D("PC_Z_vs_QQQRing_Det" + std::to_string(qqqID), 600, -300, 300, 16, 0, 16, anodeIntersection.Z(), chRing, "hPCQQQ");
|
|
// double theta = -TMath::Pi() / 2 + 2 * TMath::Pi() / 16 / 4. * (qqq.id[i] * 16 + chWedge + 0.5);
|
|
// double rho = 50. + 40. / 16. * (chRing + 0.5);
|
|
|
|
for (int k = 0; k < pc.multi; k++)
|
|
{
|
|
if (pc.index[k] >= 24)
|
|
continue;
|
|
|
|
// double sinTheta = TMath::Sin((hitPos-vector_closest_to_z).Theta());
|
|
double sinTheta = TMath::Sin((anodeIntersection - TVector3(0, 0, 90.0)).Theta());
|
|
// double sinTheta = TMath::Sin((anodeIntersection-vector_closest_to_z).Theta());
|
|
// double sinTheta = TMath::Sin((hitPos-TVector3(0,0,30.0)).Theta());
|
|
// double sinTheta = TMath::Sin(hitPos.Theta());
|
|
|
|
if (cathodeHits.size() == 2 && PCQQQPhiCut)
|
|
{
|
|
plotter->Fill2D("CalibratedQQQE_RvsCPCE_TC" + std::to_string(PCQQQTimeCut), 400, 0, 10, 400, 0, 30000, eRingMeV, pc.e[k] * sinTheta, "hPCQQQ");
|
|
plotter->Fill2D("CalibratedQQQE_WvsCPCE_TC" + std::to_string(PCQQQTimeCut), 400, 0, 10, 400, 0, 30000, eWedgeMeV, pc.e[k] * sinTheta, "hPCQQQ");
|
|
plotter->Fill2D("CalibratedQQQE_RvsPCE_TC" + std::to_string(PCQQQTimeCut), 400, 0, 10, 400, 0, 30000, eRingMeV, pc.e[k], "hPCQQQ");
|
|
plotter->Fill2D("CalibratedQQQE_WvsPCE_TC" + std::to_string(PCQQQTimeCut), 400, 0, 10, 400, 0, 30000, eWedgeMeV, pc.e[k], "hPCQQQ");
|
|
plotter->Fill2D("PCQQQ_dTimevsdPhi", 200, -2000, 2000, 80, -200, 200, tRing - static_cast<double>(pc.t[k]), (hitPos.Phi() - anodeIntersection.Phi()) * 180. / TMath::Pi(), "hTiming");
|
|
}
|
|
}
|
|
} /// qqq i==j case end
|
|
} // j loop end
|
|
} // qqq i loop end
|
|
|
|
for (int i = 0; i < sx3.multi; i++)
|
|
{
|
|
// plotting sx3 strip hits vs anode phi
|
|
if (sx3.ch[i] < 8 && anodeIntersection.Perp() > 0)
|
|
plotter->Fill2D("PCPhi_vs_SX3Strip", 100, -200, 200, 8 * 24, 0, 8 * 24, anodeIntersection.Phi() * 180. / TMath::Pi(), sx3.id[i] * 8 + sx3.ch[i]);
|
|
}
|
|
|
|
if (anodeIntersection.Z() != 0 && cathodeHits.size() == 3)
|
|
{
|
|
plotter->Fill1D("PC_Z_proj_3C", 600, -300, 300, anodeIntersection.Z(), "hPCzQQQ");
|
|
}
|
|
|
|
if (anodeIntersection.Perp() != 0)
|
|
{
|
|
plotter->Fill2D("AnodeMaxE_Vs_Cathode_Sum_Energy", 2000, 0, 20000, 2000, 0, 10000, aEMax, cESum, "hGMPC");
|
|
plotter->Fill2D("AnodeSumE_Vs_Cathode_Max_Energy", 800, 0, 20000, 800, 0, 10000, aESum, cEMax, "hGMPC");
|
|
plotter->Fill2D("AnodeMaxE_Vs_Cathode_Max_Energy", 800, 0, 20000, 800, 0, 10000, aEMax, cEMax, "hGMPC");
|
|
// double sinTheta = TMath::Sin((anodeIntersection - TVector3(0,0,source_vertex)).Theta());///TMath::Sin((TVector3(51.5,0,128.) - TVector3(0,0,85)).Theta());
|
|
// plotter->Fill2D("AnodeMaxE_Vs_Cathode_Max_Energy_path_corrected", 800, 0, 20000, 800, 0, 10000, aEMax*sinTheta, cEMax*sinTheta, "hGMPC");
|
|
plotter->Fill2D("AnodeSumE_Vs_Cathode_Sum_Energy", 800, 0, 20000, 800, 0, 10000, aESum, cESum, "hGMPC");
|
|
plotter->Fill2D("AnodeSumE_Vs_Cathode_Max_Energy_TC" + std::to_string(PCQQQTimeCut) + "_PC" + std::to_string(PCQQQPhiCut), 800, 0, 20000, 800, 0, 10000, aESum, cEMax, "hGMPC");
|
|
// plotter->Fill2D("AnodeSumE_Vs_Cathode_Max_Energy_path_corrected"+std::to_string(PCQQQTimeCut)+"_PC"+std::to_string(PCQQQPhiCut), 800, 0, 20000, 800, 0, 10000, aESum*sinTheta, cEMax*sinTheta, "hGMPC");
|
|
// plotter->Fill2D("AnodeSumE_Vs_Cathode_Max_Energy_path_corrected", 800, 0, 20000, 800, 0, 10000, aESum*sinTheta, cEMax*sinTheta, "hGMPC");
|
|
if (aEMax > 0)
|
|
{
|
|
double ratio = cEMax / aEMax;
|
|
std::string folder = "Diagnostics_CMax";
|
|
|
|
// 1. Summary 2D Plots
|
|
plotter->Fill2D("CMax_over_Anode_vs_Z", 600, -300, 300, 200, 0, 2.0, anodeIntersection.Z(), ratio, folder);
|
|
plotter->Fill2D("CMax_over_Anode_vs_AnodeID", 24, 0, 24, 200, 0, 2.0, aIDMax, ratio, folder);
|
|
plotter->Fill2D("CMax_over_Anode_vs_CathodeID", 24, 0, 24, 200, 0, 2.0, cIDMax, ratio, folder);
|
|
|
|
// 2. Individual 1D Histogram for this SPECIFIC Anode-Cathode Pair
|
|
std::string pairName = "Ratio_A" + std::to_string(aIDMax) + "_C" + std::to_string(cIDMax);
|
|
plotter->Fill1D(pairName, 200, 0, 2.0, ratio, folder + "/Pairs");
|
|
|
|
// (Optional) If you also still want the independent ones:
|
|
plotter->Fill1D("Ratio_A" + std::to_string(aIDMax), 200, 0, 2.0, ratio, folder + "/PerAnode");
|
|
plotter->Fill1D("Ratio_C" + std::to_string(cIDMax), 200, 0, 2.0, ratio, folder + "/PerCathode");
|
|
}
|
|
|
|
if (PCQQQTimeCut && PCQQQPhiCut)
|
|
{
|
|
plotter->Fill2D("AnodeSumE_Vs_Cathode_Max_Energy_TC" + std::to_string(PCQQQTimeCut) + "_PC" + std::to_string(PCQQQPhiCut) + "_cMax" + std::to_string(cIDMax), 800, 0, 20000, 800, 0, 10000, aESum, cEMax, "hGMPC");
|
|
}
|
|
// plotter->Fill2D("AnodeSumE_Vs_CathodeSum_Energy_path_corrected", 800, 0, 20000, 800, 0, 10000, aESum*sinTheta, cESum*sinTheta, "hGMPC");
|
|
// plotter->Fill2D("AnodeSumE_Vs_CathodeSum_Energy_path_corrected_TC"+std::to_string(PCQQQTimeCut)+"_PC"+std::to_string(PCQQQPhiCut), 800, 0, 20000, 800, 0, 10000, aESum*sinTheta, cESum*sinTheta, "hGMPC"); */
|
|
}
|
|
plotter->Fill1D("Correlated_Cathode_MaxAnode", 6, 0, 5, corrcatMax.size(), "hGMPC");
|
|
plotter->Fill2D("Correlated_Cathode_VS_MaxAnodeEnergy", 6, 0, 5, 2000, 0, 30000, corrcatMax.size(), aEMax, "hGMPC");
|
|
plotter->Fill1D("AnodeHits", 12, 0, 11, anodeHits.size(), "hGMPC");
|
|
plotter->Fill2D("AnodeMaxE_vs_AnodeHits", 12, 0, 11, 2000, 0, 30000, anodeHits.size(), aEMax, "hGMPC");
|
|
|
|
if (anodeHits.size() < 1)
|
|
{
|
|
plotter->Fill1D("NoAnodeHits_CathodeHits", 6, 0, 5, cathodeHits.size(), "hGMPC");
|
|
}
|
|
|
|
for (auto cwevent : cWireEvents)
|
|
{
|
|
// plotter->Fill1D("cwdtqqq_vs_cw"+std::to_string(PCQQQTimeCut),800,-2000,2000,24,0,24,std::get<2>(cwevent)-qqqtimestamp,std::get<0>(cwevent));
|
|
for (auto awevent : aWireEvents)
|
|
{
|
|
plotter->Fill2D("aw_vs_cw", 24, 0, 24, 24, 0, 24, std::get<0>(awevent), std::get<0>(cwevent));
|
|
plotter->Fill2D("aw_vs_cw_dtq" + std::to_string(PCQQQTimeCut), 24, 0, 24, 24, 0, 24, std::get<0>(awevent), std::get<0>(cwevent));
|
|
}
|
|
}
|
|
for (auto awevent : aWireEvents)
|
|
{
|
|
// plotter->Fill1D("awdtqqq_vs_aw"+std::to_string(PCQQQTimeCut),800,-2000,2000,24,0,24,std::get<2>(awevent)-qqqtimestamp,std::get<0>(awevent));
|
|
}
|
|
}
|
|
|
|
void miscHistograms_oneWire(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<std::vector<std::tuple<int, double, double>>> aClusters)
|
|
{
|
|
// consider the 'proton-like' QQQ branch seen in a,p data
|
|
TRandom3 rand;
|
|
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, 1 gain)
|
|
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 gain)
|
|
// 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, 1 gain)
|
|
|
|
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, initial_energy);
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
if (qqqevent.Energy1 < 0.6)
|
|
continue; // coarse gating
|
|
// if(qqqevent.Energy1 > 5.0) continue; //coarse gating
|
|
for (const auto acluster : aClusters)
|
|
{
|
|
auto [apwire, apSumE, apMaxE, apTSMaxE] = pwinstance.GetPseudoWire(acluster, "ANODE");
|
|
// if(apSumE<6000) continue;
|
|
int a_number = acluster.size();
|
|
TVector3 pc_closest = pwinstance.getClosestWirePosAtWirePhi(apwire, qqqevent.pos.Phi());
|
|
plotter->Fill1D("dt_anode_interp_qqq", 800, -2000, 2000, qqqevent.Time1 - apTSMaxE, "ainterp_noc");
|
|
if (qqqevent.Time1 - apTSMaxE < 150)
|
|
{
|
|
bool phicut = qqqevent.pos.Phi() <= pc_closest.Phi() + TMath::Pi() / 4. && qqqevent.pos.Phi() >= pc_closest.Phi() - TMath::Pi() / 4.;
|
|
|
|
TVector3 x2(pc_closest), x1(qqqevent.pos);
|
|
TVector3 v = x2 - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
|
|
double theta_q = (qqqevent.pos - r_rhoMin_fix).Theta();
|
|
double sinTheta2 = TMath::Sin(theta_q);
|
|
|
|
if (r_rhoMin_fix.Perp() > 6.0)
|
|
continue;
|
|
if (r_rhoMin_fix.Z() < -173.6 || r_rhoMin_fix.Z() > 100)
|
|
continue;
|
|
if (!phicut)
|
|
continue;
|
|
plotter->Fill1D("dt_anode_ainterp_qqq_gated", 800, -2000, 2000, qqqevent.Time1 - apTSMaxE, "ainterp_noc");
|
|
plotter->Fill2D("dt_anode_ainterp_qqq_gated_vs_qqqE", 800, -2000, 2000, 800, 0, 10, qqqevent.Time1 - apTSMaxE, qqqevent.Energy1, "ainterp_noc");
|
|
plotter->Fill2D("dEa_ainterp_Eqqq_TC1_ignC_a" + std::to_string(acluster.size()), 400, 0, 10, 800, 0, 40000, qqqevent.Energy1, apSumE, "ainterp_noc");
|
|
plotter->Fill2D("pcPhi_ainterp_qqqPhi_TC1_ignC_a" + std::to_string(acluster.size()), 120, -360, 360, 120, -360, 360, pc_closest.Phi() * 180. / M_PI, qqqevent.pos.Phi() * 180. / M_PI, "ainterp_noc");
|
|
plotter->Fill2D("pcZ_ainterp_qqqZ_TC1_ignC_a" + std::to_string(acluster.size()) + "_PC" + std::to_string(phicut), 300, -100, 200, 400, -200, 200, qqqevent.pos.Z(), pc_closest.Z(), "ainterp_noc");
|
|
|
|
// plotter->Fill2D("pcZ_ainterp_qqqpczguess_TC1_ignC_a"+std::to_string(acluster.size()),300,-100,200,400,-200,200,pczguess,pc_closest.Z(),"ainterp_noc");
|
|
plotter->Fill2D("dEa3_ainterp_Eqqq_TC1_ignC_a" + std::to_string(acluster.size()) + "_PC" + std::to_string(phicut), 1200, 0, 30, 800, 0, 30000, qqqevent.Energy1, apSumE * sinTheta2 * 3., "ainterp_noc");
|
|
|
|
plotter->Fill2D("vertexZ_ainterp_qqqZ_TC1_ignC_a" + std::to_string(acluster.size()), 300, -100, 200, 800, -400, 400, qqqevent.pos.Z(), r_rhoMin_fix.Z(), "ainterp_noc");
|
|
plotter->Fill1D("vertexZ1d_ainterp_qqqZ_TC1_ignC_a" + std::to_string(acluster.size()), 800, -400, 400, r_rhoMin_fix.Z(), "ainterp_noc");
|
|
plotter->Fill2D("vertexXY_ainterp_TC1_ignC_a" + std::to_string(acluster.size()), 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), "ainterp_noc");
|
|
|
|
double path_length_q = (qqqevent.pos - r_rhoMin_fix).Mag() * 0.1;
|
|
double qqqEfix;
|
|
qqqEfix = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy1) - path_length_q);
|
|
plotter->Fill1D("pmisc_ow_Ex_from_alpha", 200, -10, 10, apkin_a.getExc(qqqEfix, theta_q * 180 / M_PI), "ainterp_noc");
|
|
plotter->Fill2D("pmisc_ow_Ef_vs_theta_qqq", 100, 0, 180, 800, 0, 20, theta_q * 180 / M_PI, qqqEfix, "ainterp_noc");
|
|
plotter->Fill2D("pmisc_ow_VertexReconZ_vs_Ef", 800, -400, 400, 800, 0, 20, r_rhoMin_fix.Z(), qqqEfix, "ainterp_noc");
|
|
}
|
|
}
|
|
/*
|
|
for(int i=0; i<24; i++) {
|
|
if(aWireEvents.find(i) != aWireEvents.end()) {
|
|
auto awire = aWireEvents[i];
|
|
if(qqqevent.Time1 -(double)std::get<2>(awire)< 150) {
|
|
//plotter->Fill2D("qqq_z_phi2_awire"+std::to_string(std::get<0>(awire)), 400,-100,100, 100, -200,200,qqqevent.pos.Z(), qqqevent.pos.Phi()*180/M_PI );
|
|
//plotter->Fill2D("qqq_z_strip#_awire"+std::to_string(std::get<0>(awire)), 400,-100,100, 100, -50,50,qqqevent.pos.Z(), qqqevent.ch2);
|
|
plotter->Fill2D("anodeNum_vs_stripNum",64,0,64,24,0,24,qqqevent.ch2,i,"onewire");
|
|
bool qqqdiagonalphi = (!plotter->FindCut("anode_qqq_diag1")->IsInside(qqqevent.ch2,i)) || (!plotter->FindCut("anode_qqq_diag2")->IsInside(qqqevent.ch2,i));
|
|
plotter->Fill2D("anodeNum_vs_stripNum_diag"+std::to_string(qqqdiagonalphi),64,0,64,24,0,24,qqqevent.ch2,i,"onewire");
|
|
plotter->Fill2D("onewire_dEa_Eqqq_TC1_fullev"+std::to_string(PC_Events.size()>0)+"_PC"+std::to_string(qqqdiagonalphi),400,0,10,800,0,40000,qqqevent.Energy1,std::get<1>(awire),"onewire");
|
|
//plotter->Fill2D("onewire_aNum_qqqPhi_TC1_fullev"+std::to_string(PC_Events.size()>0),24,0,24,120,-360,360,i,qqqevent.pos.Phi()*180./M_PI,"onewire");
|
|
//plotter->Fill2D("qqq_z_phi_ow_awire"+std::to_string(anodeIndex)+"_qqqstrip"+std::to_string(qqqevent.ch2), 400,-100,100, 200, -200,200,qqqevent.pos.Z(), qqqevent.pos.Phi()*180/M_PI );
|
|
}
|
|
}
|
|
|
|
if(cWireEvents.find(i) != cWireEvents.end()) {
|
|
auto cwire = cWireEvents[i];
|
|
if(qqqevent.Time1 -(double)std::get<2>(cwire) < 150) {
|
|
//plotter->Fill2D("qqq_z_phi2_cwire"+std::to_string(std::get<0>(cwire)),400,-100,100, 100, -200,200,qqqevent.pos.Z(), qqqevent.pos.Phi()*180/M_PI );
|
|
//plotter->Fill2D("qqq_z_strip#_cwire"+std::to_string(std::get<0>(cwire)),400,-100,100, 100, -50,50,qqqevent.pos.Z(), qqqevent.ch2 );
|
|
plotter->Fill2D("onewire_dEc_Eqqq_fullev"+std::to_string(PC_Events.size()>0),400,0,10,800,0,40000,qqqevent.Energy1,std::get<1>(cwire),"onewire");
|
|
plotter->Fill2D("onewire_cNum_qqqPhi_TC1_fullev"+std::to_string(PC_Events.size()>0),24,0,24,120,-360,360,i,qqqevent.pos.Phi()*180./M_PI,"onewire");
|
|
}
|
|
}
|
|
}//for 'i' loop*/
|
|
} // end QQQEvents loop
|
|
}
|
|
|
|
void protonMiscHistograms(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events)
|
|
{
|
|
// consider the 'proton-like' QQQ branch seen in a,p data
|
|
TRandom3 rand;
|
|
rand.SetSeed(); // random seed set
|
|
double initial_energy = 7.0;
|
|
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, 1 gain)
|
|
if (dataset == "17F")
|
|
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, 1 gain)
|
|
|
|
Kinematics apkin_a(1.008664916, 4.002603254, 4.002603254, 1.008664916, initial_energy); // m3 is alpha
|
|
|
|
for (auto qqqevent : QQQ_Events)
|
|
{
|
|
if (qqqevent.Energy1 < 0.6)
|
|
continue; // coarse gating
|
|
// if(qqqevent.Energy1 > 5.0) continue; //coarse gating
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
if (!(pcevent.multi1 == 1 && pcevent.multi2 <= 2))
|
|
continue;
|
|
// if(pcevent.Energy1 > 11000) continue; //coarse gating
|
|
bool phicut = qqqevent.pos.Phi() <= pcevent.pos.Phi() + TMath::Pi() / 4. && qqqevent.pos.Phi() >= pcevent.pos.Phi() - TMath::Pi() / 4.;
|
|
if (!phicut)
|
|
continue;
|
|
// if(pcevent.Time1-qqqevent.Time1<-150 || pcevent.Time1-qqqevent.Time1 >850) continue;
|
|
|
|
double pcz_fix, pcz_dith = pcevent.pos.Z();
|
|
if (pcevent.multi2 == 2)
|
|
pcz_fix = pcfix_func.Eval(pcevent.pos.Z());
|
|
else
|
|
{
|
|
pcz_fix = rand.Gaus(pcevent.pos.Z(), 8.0); // dither for a1c1 events
|
|
pcz_dith = pcz_fix;
|
|
}
|
|
|
|
// --- A1C1 dither vs cfrac comparison (keep BOTH methods) ---
|
|
// For genuine A1C1 events, reconstruct the PC z two ways and fill a parallel
|
|
// set of excitation observables (suffix _dither / _cfrac) so the methods can
|
|
// be overlaid directly. This runs independently of the main-flow vertex cut
|
|
// below; each method applies its own vertex gate. The dither value also
|
|
// feeds the main (untagged) histograms exactly as before. cfrac =
|
|
// cpMax/(apSum+cpMax) = Energy2/(Energy1+Energy2): cell anchored on the fired
|
|
// cathode, side from the anode-only z, offset from the per-cell autocal.
|
|
if (pcevent.multi2 == 1 && pcevent.Energy2 > 1400)
|
|
{
|
|
// wire-topology category: _true1w (no dead neighbour) vs _missingw
|
|
// (a neighbouring wire is dead -> possible masked two-wire event).
|
|
const std::string wcat = a1c1_missing_neighbor(pcevent.Anodech, pcevent.Cathodech) ? "_missingw" : "_true1w";
|
|
auto fillCmp = [&](double pcz, const std::string &m)
|
|
{
|
|
TVector3 x2(pcevent.pos.X(), pcevent.pos.Y(), pcz);
|
|
TVector3 vv = x2 - qqqevent.pos;
|
|
double tm = -1.0 * (qqqevent.pos.X() * vv.X() + qqqevent.pos.Y() * vv.Y()) / (vv.X() * vv.X() + vv.Y() * vv.Y());
|
|
TVector3 rv = qqqevent.pos + tm * vv;
|
|
if (rv.Perp() > 6.0 || rv.Z() < -173.6 || rv.Z() > 100)
|
|
return;
|
|
double th = (qqqevent.pos - rv).Theta();
|
|
double pl = (qqqevent.pos - rv).Mag() * 0.1;
|
|
double Ef = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy1) - pl);
|
|
double Ex = apkin_a.getExc(Ef, th * 180 / M_PI);
|
|
std::string lbl = "proton+misc_a1c1cmp";
|
|
// fill "all" (existing names) plus the wire-topology split (_true1w/_missingw)
|
|
for (const std::string &w : {std::string(""), wcat})
|
|
{
|
|
plotter->Fill1D("pmisc_a1c1cmp_pcz_" + m + w, 600, -300, 300, pcz, lbl);
|
|
plotter->Fill1D("pmisc_a1c1cmp_Ex_" + m + w, 200, -10, 10, Ex, lbl);
|
|
plotter->Fill1D("pmisc_a1c1cmp_VertexZ_" + m + w, 800, -400, 400, rv.Z(), lbl);
|
|
plotter->Fill2D("pmisc_a1c1cmp_VertexZ_vs_Ef_" + m + w, 800, -400, 400, 800, 0, 20, rv.Z(), Ef, lbl);
|
|
plotter->Fill2D("pmisc_a1c1cmp_Ef_vs_theta_" + m + w, 100, 0, 180, 800, 0, 20, th * 180 / M_PI, Ef, lbl);
|
|
}
|
|
};
|
|
|
|
fillCmp(pcz_dith, "dither"); // method 1: Gaussian dither (main-flow value)
|
|
|
|
// method 2: cfrac sub-cell linear centre-fold (side ref = anode-only z
|
|
// rebuilt from the fired anode wire)
|
|
double ac = pcevent.Energy1 + pcevent.Energy2;
|
|
double cfrac = (ac > 0.0) ? pcevent.Energy2 / ac : -1.0;
|
|
if (cfrac >= 0.0)
|
|
{
|
|
std::vector<std::tuple<int, double, double>> aOne = {std::make_tuple(pcevent.Anodech, 1.0, 0.0)};
|
|
auto apw = pwinstance.GetPseudoWire(aOne, "ANODE");
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(std::get<0>(apw), qqqevent.pos.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, pcevent.pos.Z(), z_a1c0, pcevent.Cathodech);
|
|
// ungated: every alpha A1C1 event, f clamped to the cell (same stats as
|
|
// dither) -- shows the cfrac method without the acceptance cut.
|
|
fillCmp(s.pcz_clamped, "cfrac_all");
|
|
if (s.inband && s.pitchok)
|
|
{
|
|
fillCmp(s.pcz, "cfrac");
|
|
plotter->Fill2D("pmisc_a1c1cmp_pcz_cfrac_vs_dither", 600, -300, 300, 600, -300, 300, pcz_dith, s.pcz, "proton+misc_a1c1cmp");
|
|
}
|
|
}
|
|
}
|
|
|
|
TVector3 x2f(pcevent.pos.X(), pcevent.pos.Y(), pcz_fix);
|
|
TVector3 x1(qqqevent.pos);
|
|
TVector3 v = x2f - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
double vertex_z = r_rhoMin_fix.Z();
|
|
// double theta_q = (qqqevent.pos - TVector3(0,0,vertex_z)).Theta();
|
|
double theta_q = (qqqevent.pos - r_rhoMin_fix).Theta();
|
|
double sinTheta_customV = TMath::Sin(theta_q);
|
|
// if(r_rhoMin_fix.Perp()>6) continue;
|
|
bool cathode_alpha_select = (pcevent.Energy2 > 1400);
|
|
if (vertex_z < -173.6 || vertex_z > 100)
|
|
continue;
|
|
|
|
// What's below: radial cut, time coincident, phi-correlated events with possible energy selection applied to both E_si and dE_Anodes
|
|
auto plot_with_tag = [&](std::string tag = "")
|
|
{
|
|
std::string pmlabel = "proton+misc" + tag;
|
|
plotter->Fill2D("pmisc_dE_E_AnodeQQQ" + tag, 400, 0, 10, 800, 0, 40000, qqqevent.Energy1, pcevent.Energy1, pmlabel);
|
|
plotter->Fill2D("pmisc_dE_E_CathodeQQQ" + tag, 400, 0, 10, 800, 0, 10000, qqqevent.Energy1, pcevent.Energy2, pmlabel);
|
|
plotter->Fill2D("pmisc_dE3_E_AnodeQQQ" + tag, 400, 0, 10, 400, 0, 40000, qqqevent.Energy1, pcevent.Energy1 * sinTheta_customV * 3., pmlabel);
|
|
plotter->Fill2D("pmisc_dE3_E_CathodeQQQ" + tag, 400, 0, 10, 400, 0, 10000, qqqevent.Energy1, pcevent.Energy2 * sinTheta_customV, pmlabel);
|
|
plotter->Fill2D("pmisc_dPhi_QQQ_PC" + tag, 180, -360, 360, 180, -360, 360, pcevent.pos.Phi() * 180 / M_PI, qqqevent.pos.Phi() * 180 / M_PI, pmlabel);
|
|
plotter->Fill1D("pmisc_dt_Anode_QQQ_PC" + std::to_string(phicut) + tag, 600, -2000, 2000, pcevent.Time1 - qqqevent.Time1, pmlabel);
|
|
plotter->Fill1D("pmisc_dt_Cathode_QQQ" + tag, 600, -2000, 2000, pcevent.Time2 - qqqevent.Time1, pmlabel);
|
|
plotter->Fill2D("pmisc_dt_Anode_E_QQQ_PC" + std::to_string(phicut) + tag, 600, -2000, 2000, 400, 0, 10, pcevent.Time1 - qqqevent.Time1, qqqevent.Energy1, pmlabel);
|
|
plotter->Fill2D("pmisc_dt_AnodeQQQ_vsPCPhi" + tag, 600, -2000, 2000, 180, -360, 360, pcevent.Time1 - qqqevent.Time1, pcevent.pos.Phi() * 180. / M_PI, pmlabel);
|
|
plotter->Fill2D("pmisc_dt_Cathode_E_QQQ" + tag, 600, -2000, 2000, 400, 0, 10, pcevent.Time2 - qqqevent.Time1, qqqevent.Energy1, pmlabel);
|
|
plotter->Fill2D("pmisc_dt_CathodeQQQ_vsPCPhi" + tag, 600, -2000, 2000, 180, -360, 360, pcevent.Time2 - qqqevent.Time1, pcevent.pos.Phi() * 180. / M_PI, pmlabel);
|
|
plotter->Fill1D("pmisc_pczfix" + tag, 600, -300, 300, pcz_fix, pmlabel);
|
|
if (pcevent.multi2 == 2)
|
|
{
|
|
plotter->Fill1D("pmisc_pcz" + tag, 600, -300, 300, pcevent.pos.Z(), pmlabel);
|
|
plotter->Fill1D("pmisc_pcz2" + tag, 600, -300, 300, pcevent.pos.Z(), pmlabel);
|
|
}
|
|
if (pcevent.multi2 == 1)
|
|
{
|
|
plotter->Fill1D("pmisc_pcz" + tag, 600, -300, 300, pcz_fix, pmlabel);
|
|
plotter->Fill1D("pmisc_pcz1" + tag, 600, -300, 300, pcevent.pos.Z(), pmlabel);
|
|
plotter->Fill1D("pmisc_pcz_dith" + tag, 600, -300, 300, pcz_dith, pmlabel);
|
|
}
|
|
|
|
// double path_length_q = (qqqevent.pos-TVector3(0,0,vertex_z)).Mag()*0.1;
|
|
// double path_length_s = (sx3event.pos-TVector3(0,0,vertex_z)).Mag()*0.1;
|
|
double path_length_q = (qqqevent.pos - r_rhoMin_fix).Mag() * 0.1;
|
|
double qqqEfix;
|
|
if (tag == "_cathode_alphas")
|
|
{ // satisfied when find succeeds
|
|
qqqEfix = cm_to_MeV->Eval(MeV_to_cm->Eval(qqqevent.Energy1) - path_length_q);
|
|
plotter->Fill1D("pmisc_Ex_from_alpha", 200, -10, 10, apkin_a.getExc(qqqEfix, theta_q * 180 / M_PI), pmlabel);
|
|
}
|
|
else
|
|
qqqEfix = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(qqqevent.Energy1) - path_length_q);
|
|
// plotter->Fill2D("qqqEf_sx3E_matrix_all"+tag,400,0,10,400,0,10,qqqEfix,sx3event.Energy1,pmlabel);
|
|
plotter->Fill2D("pmisc_dE3_Ef_AnodeQQQ" + tag, 400, 0, 10, 400, 0, 40000, qqqEfix, pcevent.Energy1 * sinTheta_customV * 3, pmlabel);
|
|
plotter->Fill2D("pmisc_dE3_Ef_CathodeQQQ" + tag, 400, 0, 10, 400, 0, 10000, qqqEfix, pcevent.Energy2 * sinTheta_customV, pmlabel);
|
|
|
|
plotter->Fill1D("pmisc_VertexReconZ" + tag, 800, -400, 400, vertex_z, pmlabel);
|
|
plotter->Fill2D("pmisc_VertexReconXY" + tag, 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), pmlabel);
|
|
plotter->Fill2D("pmisc_VertexReconZ_vs_Ef" + tag, 800, -400, 400, 800, 0, 20, vertex_z, qqqEfix, pmlabel);
|
|
plotter->Fill2D("pmisc_VertexReconZ_vs_Ef" + tag + "_a" + std::to_string(pcevent.multi1), 800, -400, 400, 800, 0, 20, vertex_z, qqqEfix, pmlabel);
|
|
|
|
plotter->Fill2D("pmisc_Ef_vs_theta_qqq" + tag, 100, 0, 180, 800, 0, 20, theta_q * 180 / M_PI, qqqEfix, pmlabel);
|
|
if (pcevent.multi2 == 1)
|
|
{
|
|
plotter->Fill2D("pmisc_Ef_vs_theta_qqq_a1c1" + tag, 100, 0, 180, 800, 0, 20, theta_q * 180 / M_PI, qqqEfix, pmlabel);
|
|
plotter->Fill2D("pmisc_VertexReconZ_vs_Ef_a1c1" + tag, 800, -400, 400, 800, 0, 20, vertex_z, qqqEfix, pmlabel);
|
|
}
|
|
};
|
|
|
|
if (cathode_alpha_select)
|
|
plot_with_tag("_cathode_alphas");
|
|
// else
|
|
// plot_with_tag("_cathode_protons");
|
|
// plot_with_tag();
|
|
|
|
// plotter->Fill1D("pmisc_Ex_from_protons",200,-10,10,apkin_p.getExc(qqqEfix,theta_s*180/M_PI),pmlabel);
|
|
|
|
} // end PCEvents loop
|
|
} // end QQQEvents loop
|
|
}
|
|
|
|
void protonMiscHistograms_sx3(HistPlotter *plotter, std::vector<Event> QQQ_Events, std::vector<Event> SX3_Events, std::vector<Event> PC_Events)
|
|
{
|
|
// consider the 'proton-like' QQQ branch seen in a,p data
|
|
TRandom3 rand;
|
|
rand.SetSeed(); // for the A1C1 dither baseline in the dither-vs-cfrac comparison
|
|
for (auto sx3event : SX3_Events)
|
|
{
|
|
if (sx3event.Energy1 < 1.2)
|
|
continue; // coarse gating
|
|
// if(sx3event.Energy1 > 5.0) continue; //coarse gating
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
if (!(pcevent.multi1 == 1 && pcevent.multi2 == 2))
|
|
continue;
|
|
// if(pcevent.Energy1 > 11000) continue; //coarse gating
|
|
bool phicut = sx3event.pos.Phi() <= pcevent.pos.Phi() + TMath::Pi() / 3. && sx3event.pos.Phi() >= pcevent.pos.Phi() - TMath::Pi() / 3.;
|
|
if (!phicut)
|
|
continue;
|
|
// if(pcevent.Time1-sx3event.Time1<-150 || pcevent.Time1-sx3event.Time1 >850) continue;
|
|
|
|
double pcz_fix = pcfix_func.Eval(pcevent.pos.Z());
|
|
TVector3 x2f(pcevent.pos.X(), pcevent.pos.Y(), pcz_fix);
|
|
TVector3 x1(sx3event.pos);
|
|
TVector3 v = x2f - x1;
|
|
double t_minimum = -1.0 * (x1.X() * v.X() + x1.Y() * v.Y()) / (v.X() * v.X() + v.Y() * v.Y());
|
|
TVector3 r_rhoMin_fix = x1 + t_minimum * v;
|
|
double vertex_z = r_rhoMin_fix.Z();
|
|
// double theta_q = (sx3event.pos - TVector3(0,0,vertex_z)).Theta();
|
|
|
|
if (r_rhoMin_fix.Perp() > 10.0)
|
|
continue;
|
|
double theta_s = (sx3event.pos - r_rhoMin_fix).Theta();
|
|
double sinTheta_customV = TMath::Sin(theta_s);
|
|
bool cathode_alpha_select = (pcevent.Energy2 > 1400);
|
|
|
|
// What's below: radial cut, time coincident, phi-correlated events with possible energy selection applied to both E_si and dE_Anodes
|
|
auto plot_with_tag = [&](std::string tag = "")
|
|
{
|
|
std::string pmlabel = "proton+miscsx3" + tag;
|
|
plotter->Fill2D("pmiscs_dE_E_Anodesx3" + tag, 400, 0, 10, 800, 0, 40000, sx3event.Energy1, pcevent.Energy1, pmlabel);
|
|
plotter->Fill2D("pmiscs_dE_E_Cathodesx3" + tag, 400, 0, 10, 800, 0, 10000, sx3event.Energy1, pcevent.Energy2, pmlabel);
|
|
plotter->Fill2D("pmiscs_dE3_E_Anodesx3" + tag, 400, 0, 10, 400, 0, 40000, sx3event.Energy1, pcevent.Energy1 * sinTheta_customV * 3., pmlabel);
|
|
plotter->Fill2D("pmiscs_dE3_E_Cathodesx3" + tag, 400, 0, 10, 400, 0, 10000, sx3event.Energy1, pcevent.Energy2 * sinTheta_customV, pmlabel);
|
|
plotter->Fill2D("pmiscs_dPhi_sx3_PC" + tag, 180, -360, 360, 180, -360, 360, pcevent.pos.Phi() * 180 / M_PI, sx3event.pos.Phi() * 180 / M_PI, pmlabel);
|
|
plotter->Fill1D("pmiscs_dt_Anode_sx3_PC" + std::to_string(phicut) + tag, 600, -2000, 2000, pcevent.Time1 - sx3event.Time1, pmlabel);
|
|
plotter->Fill1D("pmiscs_dt_Cathode_sx3" + tag, 600, -2000, 2000, pcevent.Time2 - sx3event.Time1, pmlabel);
|
|
plotter->Fill2D("pmiscs_dt_Anode_E_sx3_PC" + std::to_string(phicut) + tag, 600, -2000, 2000, 400, 0, 10, pcevent.Time1 - sx3event.Time1, sx3event.Energy1, pmlabel);
|
|
plotter->Fill2D("pmiscs_dt_Cathode_E_sx3" + tag, 600, -2000, 2000, 400, 0, 10, pcevent.Time2 - sx3event.Time1, sx3event.Energy1, pmlabel);
|
|
plotter->Fill2D("pmiscs_dt_Cathodesx3_vsPCPhi" + tag, 600, -2000, 2000, 180, -360, 360, pcevent.Time2 - sx3event.Time1, pcevent.pos.Phi() * 180. / M_PI, pmlabel);
|
|
plotter->Fill1D("pmiscs_pczfix" + tag, 600, -300, 300, pcz_fix, pmlabel);
|
|
plotter->Fill1D("pmiscs_pcz" + tag, 600, -300, 300, pcevent.pos.Z(), pmlabel);
|
|
|
|
// double path_length_q = (sx3event.pos-TVector3(0,0,vertex_z)).Mag()*0.1;
|
|
// double path_length_s = (sx3event.pos-TVector3(0,0,vertex_z)).Mag()*0.1;
|
|
double path_length_s = (sx3event.pos - r_rhoMin_fix).Mag() * 0.1;
|
|
double sx3Efix = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(sx3event.Energy1) - path_length_s);
|
|
|
|
// plotter->Fill2D("sx3Ef_sx3E_matrix_all"+tag,400,0,10,400,0,10,sx3Efix,sx3event.Energy1,pmlabel);
|
|
plotter->Fill2D("pmiscs_dE3_Ef_Anodesx3" + tag, 400, 0, 10, 400, 0, 40000, sx3Efix, pcevent.Energy1 * sinTheta_customV * 3, pmlabel);
|
|
plotter->Fill2D("pmiscs_dE3_Ef_Cathodesx3" + tag, 400, 0, 10, 400, 0, 10000, sx3Efix, pcevent.Energy2 * sinTheta_customV, pmlabel);
|
|
|
|
plotter->Fill2D("pmiscs_Ef_vs_theta_sx3" + tag, 100, 0, 180, 800, 0, 20, theta_s * 180 / M_PI, sx3Efix, pmlabel);
|
|
plotter->Fill1D("pmiscs_VertexReconZ" + tag, 800, -400, 400, vertex_z, pmlabel);
|
|
plotter->Fill2D("pmiscs_VertexReconXY" + tag, 200, -100, 100, 200, -100, 100, r_rhoMin_fix.X(), r_rhoMin_fix.Y(), pmlabel);
|
|
plotter->Fill2D("pmiscs_VertexReconZ_vs_Ef" + tag, 800, -400, 400, 800, 0, 20, vertex_z, sx3Efix, pmlabel);
|
|
plotter->Fill2D("pmiscs_VertexReconZ_vs_Ef" + tag + "_a" + std::to_string(pcevent.multi1), 800, -400, 400, 800, 0, 20, vertex_z, sx3Efix, pmlabel);
|
|
};
|
|
|
|
plot_with_tag();
|
|
if (cathode_alpha_select)
|
|
plot_with_tag("_cathode_alphas");
|
|
else
|
|
plot_with_tag("_cathode_protons");
|
|
|
|
// plotter->Fill1D("pmisc_Ex_from_protons",200,-10,10,apkin_p.getExc(sx3Efix,theta_s*180/M_PI),pmlabel);
|
|
|
|
} // end PCEvents loop (A1C2 main flow)
|
|
|
|
// --- A1C1 dither vs cfrac comparison (keep BOTH methods) ---
|
|
// The A1C2 loop above does not touch A1C1 events. Here we reconstruct the PC z
|
|
// for genuine A1C1 events two ways and fill a parallel set of excitation
|
|
// observables (suffix _dither / _cfrac) so the methods can be overlaid. The
|
|
// dither is a Gaussian-smeared crossover z baseline; cfrac is the linear
|
|
// centre-fold sub-cell model (cell anchored on the fired cathode, side from
|
|
// the anode-only z at the SX3 phi, offset from the per-cell autocal).
|
|
for (auto pcevent : PC_Events)
|
|
{
|
|
if (!(pcevent.multi1 == 1 && pcevent.multi2 == 1))
|
|
continue;
|
|
bool phicut = sx3event.pos.Phi() <= pcevent.pos.Phi() + TMath::Pi() / 3. && sx3event.pos.Phi() >= pcevent.pos.Phi() - TMath::Pi() / 3.;
|
|
if (!phicut)
|
|
continue;
|
|
if (!(pcevent.Energy2 > 1400))
|
|
continue;
|
|
|
|
// wire-topology category: _true1w (no dead neighbour) vs _missingw
|
|
// (a neighbouring wire is dead -> possible masked two-wire event).
|
|
const std::string wcat = a1c1_missing_neighbor(pcevent.Anodech, pcevent.Cathodech) ? "_missingw" : "_true1w";
|
|
auto fillCmp = [&](double pcz, const std::string &m)
|
|
{
|
|
TVector3 x2(pcevent.pos.X(), pcevent.pos.Y(), pcz);
|
|
TVector3 vv = x2 - sx3event.pos;
|
|
double tm = -1.0 * (sx3event.pos.X() * vv.X() + sx3event.pos.Y() * vv.Y()) / (vv.X() * vv.X() + vv.Y() * vv.Y());
|
|
TVector3 rv = sx3event.pos + tm * vv;
|
|
if (rv.Perp() > 10.0)
|
|
return;
|
|
double th = (sx3event.pos - rv).Theta();
|
|
double pl = (sx3event.pos - rv).Mag() * 0.1;
|
|
double Ef = cm_to_MeVp->Eval(MeV_to_cm_p->Eval(sx3event.Energy1) - pl);
|
|
std::string lbl = "proton+miscsx3_a1c1cmp";
|
|
// fill "all" (existing names) plus the wire-topology split (_true1w/_missingw)
|
|
for (const std::string &w : {std::string(""), wcat})
|
|
{
|
|
plotter->Fill1D("pmiscs_a1c1cmp_pcz_" + m + w, 600, -300, 300, pcz, lbl);
|
|
plotter->Fill1D("pmiscs_a1c1cmp_VertexZ_" + m + w, 800, -400, 400, rv.Z(), lbl);
|
|
plotter->Fill2D("pmiscs_a1c1cmp_VertexZ_vs_Ef_" + m + w, 800, -400, 400, 800, 0, 20, rv.Z(), Ef, lbl);
|
|
plotter->Fill2D("pmiscs_a1c1cmp_Ef_vs_theta_" + m + w, 100, 0, 180, 800, 0, 20, th * 180 / M_PI, Ef, lbl);
|
|
}
|
|
};
|
|
|
|
fillCmp(rand.Gaus(pcevent.pos.Z(), 8.0), "dither"); // method 1: Gaussian dither baseline
|
|
|
|
// method 2: cfrac sub-cell linear centre-fold (side ref = anode-only z
|
|
// rebuilt from the fired anode wire)
|
|
double ac = pcevent.Energy1 + pcevent.Energy2;
|
|
double cfrac = (ac > 0.0) ? pcevent.Energy2 / ac : -1.0;
|
|
if (cfrac >= 0.0)
|
|
{
|
|
std::vector<std::tuple<int, double, double>> aOne = {std::make_tuple(pcevent.Anodech, 1.0, 0.0)};
|
|
auto apw = pwinstance.GetPseudoWire(aOne, "ANODE");
|
|
double z_a1c0 = pwinstance.getClosestWirePosAtWirePhi(std::get<0>(apw), sx3event.pos.Phi()).Z();
|
|
A1C1Sol s = a1c1_solve(cfrac, pcevent.pos.Z(), z_a1c0, pcevent.Cathodech);
|
|
// ungated: every alpha A1C1 event, f clamped to the cell (same stats as
|
|
// dither) -- shows the cfrac method without the acceptance cut.
|
|
fillCmp(s.pcz_clamped, "cfrac_all");
|
|
if (s.inband && s.pitchok)
|
|
fillCmp(s.pcz, "cfrac");
|
|
}
|
|
} // end A1C1 comparison loop
|
|
} // end sx3Events loop
|
|
} |