1.2:Time-Walk 效应的校准与修正¶
1. 目的¶
- timewalk修正方法,时间刻度方法:
- 掌握TTree 逐事件处理
2. 原理¶
2.1 Time-Walk 的数学描述¶
信号幅度 $A$ 对触发时间 $t$ 的附加延迟遵循反比定律: $$t_{measured} = t_{true} + \frac{W}{\sqrt{A}}$$ 当粒子击中长条闪烁体时,测量到的时间包含飞行时间、光传导延迟和 Walk 效应: $$t_L = TOF + \frac{L+x}{v_{sc}} + t_{0L} + \frac{W_{L}}{\sqrt{A_L}}$$ $$t_R = TOF + \frac{L-x}{v_{sc}} + t_{0R} + \frac{W_{R}}{\sqrt{A_R}}$$
$t$ 与入射位置 $x$ 、TOF 以及 A 三者同时相关。
2.2 切片法¶
利用两个物理约束来“冻结”无关变量:
- 利用位置切片(Position Cut): 限制入射位置在探测器的某一位置附近(利用$\log{A_L/A_R}$)。
- 利用 Gamma 射线: 选出 $\gamma$ 的事件,此时真实 $TOF$ 是固定的常数(光速)。
- $ x_q = \frac{\lambda}{2}\frac{g_R}{g_L}\ln \frac{A_L}{A_R} $
在上述两个条件下,测量时间简化为:$t_{L/R} \approx \text{Constant} + \frac{W_{{L/R}}}{\sqrt{A_{L/R}}}$。
2.3 观察 Walk 效应¶
- 上图: Gamma 峰非常弥散,且由于 Walk 效应存在严重的长尾(低能端延迟更大)。
- 下图:
tL随1/sqrt(AL)呈现弥散的带状。由于位置 $x$ 的存在,数据点分布在一个很宽的范围内,无法直接看到清晰的直线。
TCanvas *c1 = new TCanvas;
TFile *f = new TFile("tree_hw.root");
TTree *tree = (TTree*)f->Get("tree");
tree->Draw("(tL+tR)/2.0 >> htof(200, 10, 90)", "pid==0");
c1->Draw();
tree->Draw("tL : 1/sqrt(AL) >> h2(100,0,0.6, 100, 20, 80)", "pid==0", "colz");
c1->Draw();
3. 切片法 提取walk系数 $W_L, W_R$¶
TCut center_cut = "pid==0 && abs(log(AL/AR)+0.5) < 0.05";
tree->Draw("log(AL/AR)>>hqxcut(100,-1.2,0.4)", center_cut, "");
((TH1F*)gPad->GetPrimitive("hqxcut"))->SetFillColor(kGreen);
tree->Draw("log(AL/AR)>>hqx(100,-1.2,0.4)", "pid==0", "same");
c1->Draw();
TF1 *fFit = new TF1("fFit", "pol1", 0.05, 0.6);
TCanvas *c2 = new TCanvas("c2", "Calibration", 800, 400);
c2->Clear();
c2->Divide(2,1);
c2->cd(1);
tree->Draw("tL : 1.0/sqrt(AL) >> walkL(50, 0, 0.6)", center_cut, "colz");
c2->cd(2);
TProfile *profL = walkL->ProfileX("profL");
profL->Fit(fFit, "RQ");
c2->Draw();
Double_t WL = fFit->GetParameter(1);
Double_t err_WL = fFit->GetParError(1);
Double_t CL = fFit->GetParameter(0);
Right¶
c2->Clear();
c2->Divide(2,1);
c2->cd(1);
tree->Draw("tR : 1.0/sqrt(AR) >> walkR(50, 0, 0.6)", center_cut, "colz");
c2->cd(2);
TProfile *profR = walkR->ProfileX("profR");
profR->Fit(fFit, "RQ");
c2->Draw();
Double_t WR = fFit->GetParameter(1);
Double_t err_WR = fFit->GetParError(1);
Double_t CR = fFit->GetParameter(0);
std::cout << Form(">>> 提取参数: WL=%.2f ± %.2f | WR=%.2f ± %.2f", WL, err_WL, WR, err_WR) << std::endl;
>>> 提取参数: WL=40.29 ± 0.19 | WR=45.15 ± 0.23
TCanvas *c3 = new TCanvas("c3", "Verification");
tree->SetLineColor(kBlue);
// 定义 Walk 修正后的时间
TString tL_corr = Form("(tL - %f/sqrt(AL))", WL);//"tL-45.2/sqrt(AL)"
TString tR_corr = Form("(tR - %f/sqrt(AR))", WR);
TString tof = Form("(%s + %s)/2.", tL_corr.Data(),tR_corr.Data());
TString tx = Form("(%s - %s)/2.", tL_corr.Data(),tR_corr.Data());
TString draw_tof = Form("%s >> h_new(200,30,70)", tof.Data());
tree->SetLineColor(kRed);
tree->Draw(draw_tof, "pid==0", "");
htof->SetLineColor(kBlue);
htof->Draw("same");
TLegend *leg = new TLegend(0.6, 0.7, 0.9, 0.9);
leg->AddEntry("h_old", "Uncorrected", "l");
leg->AddEntry("h_new", "Corrected", "l");
leg->Draw();
c3->Draw();
3.2 时间偏移修正¶
在完成 Time-Walk 修正后,获得了反映粒子入射位置的“干净”时间差信息。本节的目标是确定时间差与物理空间坐标 $x$ 的映射关系。
3.2.1 利用微分法进行位置刻度¶
探测器位置x与两侧时间差(修正后)之间有如下关系:
$$ x_{t} = \frac{v_{sc}}{2} (t_L - t_R - (t_{0L}-t_{0R})) $$
- 通过下列步骤得到刻度系数:$v_{sc}/{2}$ 和 $t_{0L}-t_{0R}$.
1. 物理思想:寻找“消失的边缘” 由于探测器在空间上是有限的(长度为 $2L$),当粒子均匀入射时,时间差 $\Delta t = (t_L - t_R)$ 的分布理论上是一个矩形。但由于时间分辨率的存在,计数的分布在边缘处并非垂直下降,而是带有高斯卷积的阶梯。因此,导数的峰值位置才是物理边界最可靠的估计。
- 建立时间差的直方图(TH1) $h1$
微分法的逻辑:对 $h1$ 分布求导(数值微分),计数下降最快的地方(导数的极值点)即对应探测器的物理边界。
刻度目标:识别左边界 $\Delta t_{left}$ 和右边界 $\Delta t_{right}$,建立线性映射:$x = k \cdot \Delta t + b$,使得重建出的 $x$ 恰好落在 $[-L, L]$ 区间。
获取 X 轴的 Bin 总数(不含 Underflow 和 Overflow):
Int_t nBins = h1->GetNbinsX();
注:ROOT 的直方图索引从
1到nBins。0号 Bin 是 Underflow(低于范围),nBins+1号 Bin 是 Overflow(高于范围)。在循环中遍历所有 Bin:
for (int i = 1; i <= h1->GetNbinsX(); i++) { Double_t content = h1->GetBinContent(i); // ... 处理代码 }
数值微分:最简单的实现是
diff = h1->GetBinContent(i+1) - h1->GetBinContent(i),将微分结果存入一个新的直方图。寻找“向下的峰”:在右边缘,导数是负的(计数在减少)。当你使用
gaus拟合这个负峰时,必须手动给定初值,否则拟合会失败: , 需设定拟合参数的合理初值。TF1 *f1 = new TF1("f1","[0]*TMath::Exp(-0.5*((x-[1])/[2])^2)",xbin1,xbin2);//定义函数的方法 TF1 //gaus:f(x) = p0*exp(-0.5*((x-p1)/p2)^2) f1->SetParameters(-A0_guess, x_guess, sigma_guess); // 振幅设为负(-A0_guess),给定位置初值(x_guess)和宽度初值(sigma_guess)
3.2.3 位置重建验证¶
在完成 Time-Walk 修正后,获得了反映粒子入射位置的“干净”的时间差信息。
物理公式: 利用两侧光传导的时间差重建位置: $$x = \frac{V_{sc}}{2} \cdot \left( (t_{L,corr} - t_{R,corr}) - \Delta t_{0} \right)$$ 其中,$\Delta t_{0}$ 是通过微分法或几何中心确定的时间偏移。
飞行路径: 对于距离靶心垂直距离为 $D$ 的探测器,打在位置 $x$ 处的事件,其理论飞行距离为 $z = \sqrt{D^2 + x^2}$。
下面的分析直接采用分析代码中的参数设定:
const Double_t Vsc = 0.075; // m/ns
const Double_t t0L - t0R = 5.5 - 20.4; //ns
- 图像分析(左图): 绘制重建后的位置 $x$ 的一维分布。可以看到在 $[-1.0, 1.0]$ 米范围内,计数分布接近平直的矩形。这说明探测器对均匀入射的粒子具有良好的空间响应,且物理边界(条长 2m)在刻度后得到了正确还原。
- 图像分析(右图): 绘制单位距离的 TOF(
tof/z)随位置 $x$ 的二维关联图。此时尚未进行绝对时间校准,可以看到 $\gamma$ 射线带($pid==0$)分布在 $y \approx 8.5$ ns/m 附近。数值不对,而且进行飞行距离归一化的tof与 $x$ 存在依赖关系,说明tof还需进一步修正。
const Double_t D = 5.0; // m, 靶到探测器距离
const Double_t L = 1.0; // m, 探测器半长
const Double_t Vsc = 0.075; // m/ns
const Double_t t0L_t0R = 5.5 - 20.4; //ns
//TString tof = Form("(%s + %s)/2.", tL_corr.Data(),tR_corr.Data());
//TString tx = Form("(%s - %s)/2.", tL_corr.Data(),tR_corr.Data());
TString x = Form("%f * ((%s - %s) - %f)/2.", Vsc, tL_corr.Data(), tR_corr.Data(), t0L_t0R);
TString z = Form("sqrt(pow(%f,2) + pow(%s,2))", D, x.Data());
TString tof_norm = Form("%s / %s", tof.Data(), z.Data());
TString draw_x_tof_norm = Form("%s:%s >> htofx(100,-1,1, 100,7,10)", tof_norm.Data(), x.Data());
TCanvas *c4 = new TCanvas("c4", "Calibrate", 1000, 500);
c4->Divide(2,1);
c4->cd(1);
tree->Draw(x, "pid==1", "colz");
c4->cd(2);
tree->Draw(draw_x_tof_norm, "pid==0 ", "colz");
c4->Draw();
3.3 TOF刻度¶
位置刻度完成后,由于电子学系统的固有延迟(如电缆长度、触发延迟),测量到的 $TOF$ 并非绝对物理时间。
$$ \text{TOF}_{rec} = \frac{t_L + t_R}{2} + c_{tof}$$
- $c_{tof} = -L/v_{sc} + (t_{0L}+t_{0R})$ 为待求刻度系数。
校准原理: $\gamma$ 射线以光速 $c$ 飞行。已知靶心到入射点的几何距离为 $z$,则理论飞行时间应为 $z/c$(约 $3.33 \times z$ ns)。测量值与理论值的差值即为全局时间偏移(Time Offset)。 $$ TOF_{\gamma}= 3.33 * z $$
$$ c_{tof} = 3.33*z - \frac{t_L + t_R}{2} $$
- 图像分析(左图): 绘制 $3.33 \cdot z - TOF_{meas}$ 的分布图并进行高斯拟合。拟合得到的均值 $mean \approx -26.27$ ns 即为该系统的绝对时间偏移量。
- 图像分析(右图): 应用偏移修正,归一化 $TOF$($ns/m$)随位置 $x$ 的变化: 可以看到 $\gamma$ 射线带现在精固定在 $3.33$ ns/m 线上,且不再随位置 $x$ 发生倾斜或弯曲。
TString c_tof = Form("3.33* %s - %s >> hconst(100, -30, -22)", z.Data(),tof.Data());
TCanvas *c5 = new TCanvas("c5", "tof Calibration", 800, 400);
c5->Divide(2,1);
c5->cd(1);
tree->Draw(c_tof,"pid==0");
TH1F *hconst = (TH1F*)gROOT->FindObject("hconst");
hconst->Fit("gaus","Q");
TF1 *fitFunc = hconst->GetFunction("gaus");
double mean = fitFunc->GetParameter(1);
cout << "time offset of tof = "<<mean<<endl;
c5->cd(2);
TString tof_norm_corr = Form("(%s+%f)/%s", tof.Data(), mean, z.Data());
TString draw_x_tof_norm_corr = Form("%s:%s >> htofx(100,-1,1,100,1.5,5)", tof_norm_corr.Data(), x.Data());
tree->Draw(draw_x_tof_norm_corr, "pid==0 ", "colz");
c5->Draw();
time offset of tof = -26.2577
3.4. TOF刻度验证¶
- 将所有入射粒子的测量 TOF 按照其实际飞行路径 $z$ 进行归一化。
- 图像分析:
- $\gamma$ 射线峰(左侧尖峰): 位于 $3.33$ ns/m 处。修正后的 $\gamma$ 峰(红色)比修正前(蓝色)更宽,峰宽的大小直接反映了探测器的时间分辨率。
- 中子分布(右侧宽峰): 位于 $6$ ns/m 之后。由于中子质量大、速度慢,其飞行时间远大于 $\gamma$ 射线。
- 结论: 经过 Walk 修正、位置刻度、偏移校准后的数据,与模拟中的飞行时间一致。
TCanvas *c6 = new TCanvas("tof","tof",1000,400);
c6->Divide(2,1);
c6->cd(1);
tree->Draw("tof_true/sqrt(x_true*x_true+5*5)>>htof_all(200,0,15)","","");
TH1F *htof_all =(TH1F*) gROOT->FindObject("htof_all");
htof_all->SetLineColor(kBlue);
TString draw_tof_corr = Form("%s>>htof_all_corr(200,0,15)",tof_norm_corr.Data());
tree->Draw(draw_tof_corr,"","same");
c6->cd(2);
TString draw_tof_corr_q = Form("sqrt(AL*AR):%s>>htof_all_corr_q(200,0,15,200,0,700)",tof_norm_corr.Data());
tree->Draw(draw_tof_corr_q,"","colz");
c6->Draw();
4. TTree 的逐事件分析、筛选与二次存储¶
实验数据分析的一般流程:从原始数据(Raw Data)中提取信息,通过计算生成新的物理量,并根据特定的物理判据(如能量阈值)筛选有效事件,最终将结果保存到新的文件中。
语法¶
1. 数据读取绑定 (SetBranchAddress):
tree->SetBranchAddress("BranchName", &Variable);这是写入操作Branch()的逆过程。它将现有的数据内TTree分支(BranchName)与本地内存变量(Variable)的地址绑定,相当于在数据源与程序内存之间建立了一根专门的“数据管道”。2. 按需提取事件 (GetEntry):
tree->GetEntry(i);的作用是加载第i个事件的数据。GetEntry解压并读取那些通过SetBranchAddress挂载过的分支。执行后,绑定的本地变量会同步更新为该事件的真实数值。
示例代码:Analyze.C¶
#include <iostream>
#include <TFile.h>
#include <TTree.h>
#include <TMath.h>
void Analyze() {
// 1. 输入配置:打开原始文件并绑定地址映射
TFile *fIn = new TFile("tree_hw.root", "READ");
TTree *tIn = (TTree*)fIn->Get("tree");
// 内存工作区:声明本地变量接收原始数据
Int_t raw_pid; Double_t raw_tL, raw_tR, raw_qL, raw_qR;
tIn->SetBranchAddress("pid", &raw_pid);
tIn->SetBranchAddress("tL", &raw_tL);
tIn->SetBranchAddress("tR", &raw_tR);
tIn->SetBranchAddress("qL", &raw_qL);
tIn->SetBranchAddress("qR", &raw_qR);
// 2. 输出配置:创建新文件与定标树
TFile *fOut = new TFile("calibrated.root", "RECREATE");
TTree *tOut = new TTree("tree_cal", "Filtered & Calibrated Data");
// 定义定标物理量并建立输出分支绑定
Double_t xt, xq;
tOut->Branch("xt", &xt, "xt/D"); // 计算后的时间差位置
tOut->Branch("xq", &xq, "xq/D"); // 计算后的电荷比对数
tOut->Branch("pid", &raw_pid, "pid/I"); // 同时转存原始标识
// 3. 事件循环:逐事件进行读取、定标与筛选
Long64_t nEvents = tIn->GetEntries();
std::cout << ">>> 开始处理并筛选事件,总计: " << nEvents << std::endl;
for (Long64_t i = 0; i < nEvents; i++) {
// 将当前事件的所有原始分支数据提取到本地变量中
tIn->GetEntry(i);
// --- 物理筛选条件:仅记录两端电荷响应均大于 10 的有效事件 ---
if (raw_qL > 10 && raw_qR > 10) {
// 执行物理校准算法
xt = raw_tL - raw_tR;
xq = TMath::Log(raw_qL / raw_qR);
// 【条件填充】:只有满足 if 条件的事件才会写入新树的缓冲区
tOut->Fill();
}
// 实时进度反馈
if (i % (nEvents / 20) == 0 || i == nEvents - 1) {
std::cout << "\rProcessing: " << (int)((double)i/(nEvents-1)*100) << "%" << std::flush;
}
}
// 4. 持久化存储与资源清理
fOut->cd(); // 确保进入输出文件目录
tOut->Write(); //
fOut->Close(); // 关闭并保存输出文件
fIn->Close(); // 关闭输入文件
std::cout << "\n>>> 处理完成,校准数据已保存至 calibrated.root" << std::endl;
}
作业:¶
- 利用Graphic cut选出gamma和中子条件,保存为cutGamma.cc, cutNeutron.cc文件。
- 在下面的分析中调用保存的Graphic cut来选择粒子种类,不得使用
pid。 - 下列分析中所需变量通过逐事件遍历生成,不得使用讲义的做法。
- example:
TH1F *htL_corr = new TH1F("htl_corr","htl_corr", 200, 10, 200);
for (Long64_t i = 0; i < t->GetEntries(); i++) {
t->GetEntry(i);
if (qL <= 0 || qR <= 0) continue;
Double_t tL_cor = tL - A_L / TMath::Sqrt(qL) - B_L;
htl_corr->Fill(tL_corr);
...
}
- 用位置的多个切片,对时间信号进行 Time Walk 修正,得到两侧的平均walk系数,给出系数的误差。
- 利用微分法进行 位置刻度,刻度系数与模拟代码中的数值进行比较。
- 计算AL,AR两端信号刻度系数比值$g_L/g_R$ 以及闪烁体中光的衰减系数$\lambda$.
- 计算中子能量(En)与模拟数据进行比较。
- 将刻度后的事件信息(
tL_cal,tR_cal,x_cal,tof_cal, En_cal) 新文件。