2.2 PPAC 信号处理-II¶

1. Tracking¶

利用多个 PPAC 的位置 (x,y,z) 信息进行 tracking 的原理如下图所示。由于 PPAC 的探测效率通常小于 100%,因此对于每个入射粒子,往往只有部分 PPAC 能够在 x 或 y 方向给出有效的位置信息。

假设对于某一入射粒子,有两个或两个以上探测器给出了有效的测量位置信息(如图中的红点所示,例如 1Ax, 2Ax 等),入射粒子在 x-z 或 y-z 平面上的飞行径迹即可由这些有效点进行线性拟合得到。

得到径迹的线性方程后,束流线上其他任意位置 (z) 处的 (x,y) 坐标,均可利用上述方程通过内插或外推计算得出。图中黑点即代表由线性方程计算/拟合得到的位置(它代表了我们所能推测出的粒子实际飞行路径)。

setup


本实验中采用的重建策略¶

在实际实验中,若强制要求单层探测器必须同时给出 $(x,y)$ 二维有效信号才参与拟合,会导致严重的整体探测效率损失。因此实际上$x$ 方向与 $y$ 方向的径迹重建是完全独立进行的。即:只要某层探测器测得了有效的 $x$ 坐标,即便其 $y$ 信号缺失,该点仍独立参与 $x-z$ 平面的径迹拟合。

2. 径迹有效条件 对于 $x$ 或 $y$ 径迹,必须满足以下逻辑条件之一才被视为有效重建:

  • 情况一: 最靠近下游的 F8PPAC3 具有有效信号

    • 同时要求上游的 F8PPAC1 和 F8PPAC2 中,至少有 1 层 PPAC 提供有效信号。
  • 情况二: 最靠近下游的 F8PPAC3 无有效信号

    • 则完全依赖上游探测器,需满足以下 a) 或 b):
      • a) F8PPAC1 和 F8PPAC2 中,合计有 3 层或 3 层以上的 PPAC 提供有效信号。
      • b) 若合计仅有 2 层 PPAC 提供有效信号,则要求这两层必须分别来自 F8PPAC1 和 F8PPAC2。 (若仅有的2层信号来自同一位置(如都在 F8PPAC1 内),因两点间距过近,向靶点外推时会产生巨大的角度误差)

束流径迹重建结果(所有PPAC的信息都有x/y位置信息)¶

setup

2. ROOT 文件中 TTree 的 Branch 说明¶

本实验使用的ROOT文件中,已完成位置刻度的 PPAC 数据存储在二维数组 PPACF8[i][j] 中。

  • $z$ 坐标:代表各探测器平面在束流线上的绝对 $z$ 方向位置,由实验室几何测量事先给定。
  • $x/y/z$坐标:单位mm。
  • 当出现位置计算超界、信号堆积 (Pile-up) 或缺失时,数据统一用 -999 或 -1000 填充。

数据映射关系表(从原始数据到物理分析变量):

探测器层级 原始 Branch 物理意义 (已刻度) 讲义代码对应变量
PPAC 1 Layer A PPACF8[0][0]
PPACF8[0][1]
PPACF8[0][2]
PPACF8[0][3]
$X$ 坐标 (mm)
$Y$ 坐标 (mm)
$X$ 平面对应的 $Z$ 坐标
$Y$ 平面对应的 $Z$ 坐标
xx[0]
yy[0]
xz[0]
yz[0]
PPAC 1 Layer B PPACF8[1][0~4] (同上结构,含 Anode 时间) (本示例暂未使用)
PPAC 2 Layer A PPACF8[2][0~4] (同上结构) xx[1], yy[1]
xz[1], yz[1]
PPAC 2 Layer B
(作为待测层 DUT)
PPACF8[3][0~4] (同上结构) xx2b[0], yy2b[0]
xz2b, yz2b
anode2b
PPAC 3 PPACF8[4][0~4] (同上结构) xx[2], yy[2]
xz[2], yz[2]


3. 径迹重建 (Tracking) 事例代码¶

下面示例代码 假设 1A, 2A, 3 这三个探测器均给出了有效位置信息,利用这三点进行直线拟合,并将径迹外推,用来评估 2B 探测器的各项探测效率及系统分辨率。

算法步骤如下:

  • 1. 空间独立直线拟合:
    • $x-z$ 平面拟合直线方程 $x = f_x(z)$,使用已知数据点:(xx[0],xz[0]), (xx[1],xz[1]), (xx[2],xz[2])
    • $y-z$ 平面拟合直线方程 $y = f_y(z)$,使用已知数据点:(yy[0],yz[0]), (yy[1],yz[1]), (yy[2],yz[2])
  • 2. 残差 (Residual) 计算(用于评估位置分辨率):
    • 测量值与拟合值的差:$dx[i] = xx[i] - f_x(xz[i])$ ; $dy[i] = yy[i] - f_y(yz[i])$ $(i=0,1,2)$
  • 3. 待测层 F8PPAC2_B (2B) 的对比:
    • 实验测量坐标: (xx2b[0], xz2b) 和 (yy2b[0], yz2b)
    • 外推预期坐标: 利用拟合直线算得 (xx2b[1], xz2b) 和 (yy2b[1], yz2b)
    • 阳极触发信号: anode2b(用于求阳极本征效率)
  • 4. 物理靶位坐标外推:
    • 利用拟合直线外推至靶点平面(假设靶位于 $z=0$),求得靶上坐标 (tx, ty)。

靶点坐标 $(t_x, t_y)$ 与出射角 $\theta$ 的误差评估¶

1. 物理动机:为什么要逐事件评估追踪误差?¶

在真实的物理实验中,分析中会采用不同的径迹重建(Tracking)策略。例如:

  • 策略 A: 3 层探测器完美击中 $\to$ 杠杆臂长,拟合精度极高。
  • 策略 B: 仅有 2 层探测器击中 $\to$ 勉强定迹,拟合误差巨大。

当我们为每一个事件计算出专属的物理误差($\sigma_{t_x}, \sigma_\theta$)并存入 TTree 后,在后续的物理分析中,可以通过以下 三种具体的编程手段 来统一处理这些质量参差不齐的数据:

用法一:Quality Cut¶

有了误差后,不同 tracking 策略的好坏被统一量化。在 ROOT 中画图或提取数据时,通过TCut选择不同误差的事件:

// 只要外推到靶上的误差小于 1.5 mm 的事件
TCut good_track_cut = "sigma_tx < 1.5 && sigma_theta < 0.05";
tree->Draw("tx >> h1", good_track_cut);

用法二:Inverse-Variance Weighting¶

如果物理目标是测量一个确定的中心值(例如:寻找束流打在靶上的绝对中心位置,或者拟合某个共振态的确定散射角),不能对所有事件取简单的算术平均。 应该使用逆方差(误差的平方倒数)作为每个事件的权重 $w_i$: $$ w_i = \frac{1}{\sigma_i^2} $$ 数学原理实现:

double sum_weight = 0;
double sum_value  = 0;

for (int i=0; i<nentries; i++) {
    tree->GetEntry(i);
    if (sigma_tx <= 0) continue; // 排除无效点
    
    double weight = 1.0 / (sigma_tx * sigma_tx); // 误差越小,权重呈指数级暴增
    sum_value  += tx * weight;
    sum_weight += weight;
}
double final_tx_center = sum_value / sum_weight;

2. 协方差矩阵 (Covariance Matrix)¶

假设在 $X-Z$ 平面内用线性方程 $x(z) = p_0 + p_1 \cdot z$ 拟合粒子径迹。 最小二乘法在给出截距 $p_0$ 和斜率 $p_1$ 的同时,必然会给出一个 $2 \times 2$ 的协方差矩阵 $C$:

$$ C = \begin{pmatrix} \sigma_{p_0}^2 & Cov(p_0, p_1) \\ Cov(p_1, p_0) & \sigma_{p_1}^2 \end{pmatrix} $$

  • 对角线元素: 分别是截距和斜率自身误差的平方(方差)。
  • 非对角线元素 $Cov$: 极其重要!它代表了截距和斜率的相关性。在径迹拟合中,如果你稍微改变直线的倾角(斜率变大),直线在 $Z=0$ 处的截距必然会随之反向移动。因此 $p_0$ 和 $p_1$ 通常是强负相关的。忽略这个负的协方差,会导致最终外推误差被严重高估!

3. 原理推导:外推靶点 $(t_x)$ 与出射角 $(\theta)$ 的误差传递¶

假设物理靶位于 $z = z_{target}$ 处。

A. 靶上位置 $t_x$ 的误差推导(线性传递)¶

根据直线方程,外推点的计算公式为:$t_x = p_0 + p_1 \cdot z_{target}$。 利用多元函数误差传递公式: $$ \sigma_{f(p_0, p_1)}^2 = \left( \frac{\partial f}{\partial p_0} \right)^2 \sigma_{p_0}^2 + \left( \frac{\partial f}{\partial p_1} \right)^2 \sigma_{p_1}^2 + 2 \left( \frac{\partial f}{\partial p_0} \right) \left( \frac{\partial f}{\partial p_1} \right) Cov(p_0, p_1) $$

代入偏导数($\frac{\partial t_x}{\partial p_0} = 1$, $\frac{\partial t_x}{\partial p_1} = z_{target}$),得到靶点位置的真实方差: $$ \mathbf{\sigma_{t_x}^2 = \sigma_{p_0}^2 + (z_{target})^2 \cdot \sigma_{p_1}^2 + 2 \cdot z_{target} \cdot Cov(p_0, p_1)} $$ (注:Y 方向的 $t_y$ 误差推导与 X 方向完全相同,只需将 X 平面的拟合参数代入即可。)

B. 物理出射角 $\theta$ 的误差推导(非线性传递)¶

拟合出的斜率 $p_1$ 等于角度的正切值,真正的物理角度为:$\theta = \arctan(p_1)$。 利用一元非线性误差传递公式: $$ \sigma_\theta^2 = \left( \frac{d\theta}{dp_1} \right)^2 \sigma_{p_1}^2 $$ 根据微积分导数公式 $\frac{d}{dx}\arctan(x) = \frac{1}{1+x^2}$,得到角度的真实绝对误差: $$ \mathbf{\sigma_\theta = \frac{\sigma_{p_1}}{1 + p_1^2}} $$


4. 位置分辨率¶

在实验中,测定探测器的本征位置分辨率是评估径迹重建质量和动量/角度分辨率的前提。通常有两种测定方法:

2.1 准直束刻度方法(离线测试 / 放射源)¶

该方法通常在离线实验室中,利用低能放射源和准直狭缝(Collimator)进行。

  • 理想高斯展宽模型: 通过准直孔将粒子准直后打入探测器。此时测得的表观位置分辨率 $\sigma$ 是探测器本征分辨率 $\sigma_0$ 与准直孔几何展宽 $\sigma_{geo}$ 的卷积(近似为高斯叠加): $$ \sigma^2 = \sigma_0^2 + \sigma_{geo}^2 $$ 其中,$\sigma_{geo}$ 由准直孔的物理孔径引入(见图 a)。通过反推即可得到 $\sigma_0$。
  • 局限性一:源与真实束流的电离差异 气体探测器的本征分辨率 $\sigma_0$ 强烈依赖于入射粒子的比电离能损 ($dE/dx$)、原初电离统计涨落以及产生 $\delta$ 电子的射程。因此,用低能 $\alpha$ 放射源测出的 $\sigma_0$,只能证明探测器硬件完好,不能直接等同于高能重离子束流条件下的真实分辨率。
  • 局限性二:高能束流的“边缘散射(Slit Scattering)” 如果在真实束流线上强行使用准直孔(图 b),高能粒子在穿过准直孔边缘时会发生严重的多重库仑散射(MCS)。这不仅会使得 $\sigma_{geo}$ 的几何关联失效,更会在位置谱上产生巨大的非高斯拖尾(Tail / Halo),彻底淹没探测器真实的 $\sigma_0$。

2.2 多探测器径迹残差法¶

鉴于准直方法的局限性,在真实的高能物理实验中,可靠的方法是利用多层探测器系统,在真实束流和真实数据获取条件下,通过径迹残差(Residuals)来逆推分辨率。

  • 残差(Residual)的定义: 选择被全部 PPAC(如 5 层)同时探测到的“好事件”进行空间直线拟合。定义第 $i$ 层探测器的测量坐标 $x^i$ 与拟合径迹在该层的预期坐标 $x_{track}^i$ 之差为残差 $\Delta x^i$: $$ \Delta x^i = x^i - x_{track}^i $$

  • 在 $z_i$ 位置观察到的残差 $\Delta x^i$,实际上是由两个独立成分叠加而成的:

  1. 探测器本身的测量涨落 $\sigma_0$,即位置分辨。
  2. 拟合直线的误差 $\sigma_{track}$:这正是通过上一节协方差矩阵在 $z_i$ 处算出的不确定度。

提取 $\sigma_0$,实验上常用“排除拟合法”:即在拟合直线时,不使用第 $i$ 层的数据,而只用其他层连成直线。

此时,由于第 $i$ 层的测量值与外推直线完全独立,残差的方差是两者方差的直接相加: $$\sigma_{residual}^2 = \sigma_0^2 + \sigma_{track}^2(z_i)$$


5. PPAC 探测效率¶

理想情况下,若有 $N$ 个粒子真实穿过了探测器的灵敏区域(Active Area),而探测器实际成功记录并解析出位置信息的事件数为 $N_{det}$,则该探测器的绝对探测效率定义为: $$\epsilon = \frac{N_{det}}{N}$$

在实际束流实验中,由于我们无法预知真实的 $N$,通常采用以下两种典型的数据分析方法来评估效率:

方法一:位置读出效率(基于内部信号符合,对应图 a)

  • 物理意义: 评估当气体发生有效雪崩后,阴极成功读取出位置坐标的概率。
  • 计算方法: 将 PPAC 的阳极 (Anode) 触发信号计数作为分母(代表测到粒子确实穿过探测器),将阴极 (Cathode) 具有位置信息的计数作为分子。

方法二:基于径迹外推的绝对探测效率(参考探测器法,对应图 b)

  • 物理意义: 利用其他探测器作为基准,评估待测探测器的实际探测能力。
  • 计算方法:
    1. 选择任意两层(或多层)具有位置信号的 PPAC 作为参考探测器(图b中的 i, j),拟合出粒子径迹。
    2. 分母 ($N_{ij}$): 将上述径迹外推至待测探测器(图b中的 k)的位置,统计预期穿过其灵敏面积的所有事件总数。
    3. 分子 ($N_{kij}$): 在上述 $N_{ij}$ 个事件的前提下,去检查待测探测器 k 是否确实给出了位置信号,记录成功探测的事件数。
    4. 计算公式: $\epsilon = \frac{N_{kij}}{N_{ij}}$
  • 拓展:阳极探测效率评估
    • 利用类似的径迹外推做法,也可以求得每个探测器阳极(Anode)的本征探测效率。只需保持分母(预期事件总数 $N_{ij}$)不变,将分子替换为:在上述事件中,待测探测器 k 的阳极成功产生触发信号的事件数即可。

维度独立性: 由于 PPAC 内部 $x$ 和 $y$ 方向的阴极读出结构通常是独立的,利用上述“径迹外推法”,可以分别独立评估该探测器单一维度的 $x$ 效率、$y$ 效率,以及要求两者同时有效的二维 $(x,y)$ 联合探测效率。


6. 分析代码框架¶

利用 ROOT 的 MakeClass 功能,生成用于遍历 TTree 的 C++ 框架代码:

# 在终端中启动 ROOT 并加载数据文件
root -l f8ppac001.root
root [1] tree->MakeClass("tracking");
root [2] .q

提示:执行后会生成 tracking.h 和 tracking.C,按照下文修改这两个文件。

修改后运行分析的命令:

root -l 
root [1] .L tracking.C
root [2] tracking t;
root [3] t.Loop();
root [4] .q

运行结束后,生成 tracking.root 。


tracking.h¶

修改说明: 在 MakeClass 自动生成的基础上,补充了用于径迹外推分析的用户自定义变量与成员函数声明。

#ifndef tracking_h
#define tracking_h

#include <TROOT.h>
#include <TChain.h>
#include <TFile.h>

class tracking {
public :
   TTree          *fChain;   //!pointer to the analyzed TTree or TChain
   Int_t           fCurrent; //!current Tree number in a TChain

   // ================= 原始数据 Branch 变量 =================
   Float_t         PPACF8[5][5];
   Float_t         F8PPACRawData[5][5];
   Int_t           beamTrig;
   Int_t           must2Trig;
   Float_t         targetX, targetY;

   // ================= 用户自定义物理分析变量 =================
   Double_t        xx[3], xz[3], yy[3], yz[3];    // 参考层 1A, 2A, 3 的位置坐标
   Double_t        xx2b[2], yy2b[2], xz2b, yz2b;  // 待测层 2B: [0]实际测量值, [1]拟合外推值
   Double_t        anode2b;                       // 待测层 2B 阳极信号
   Double_t        dx[3], dy[3];                  // 拟合残差 (Residual)
   
   // 运动学物理量与本征误差
   Double_t        tx, ty;                        // 靶上外推坐标 (Target pos)
   Double_t        theta_x, theta_y;              // 粒子出射角 (单位: rad)
   Double_t        sigma_tx, sigma_ty;            // 靶上坐标的绝对物理误差
   Double_t        sigma_thetax, sigma_thetay;    // 出射角的绝对物理误差
   
   Double_t        c2nx, c2ny;                    // 直线拟合质量评估 chi2/ndf

   // 原始数据 Branch 指针 
   TBranch        *b_PPACF8;          //!
   TBranch        *b_F8PPACRawData;   //!
   TBranch        *b_beamTrig;        //!
   TBranch        *b_must2Trig;       //!
   TBranch        *b_targetX;         //!   
   TBranch        *b_targetY;         //!
   
   // ================= 标准系统函数 =================

   tracking(TTree *tree=0);
   virtual ~tracking();
   virtual Int_t    Cut(Long64_t entry);
   virtual Int_t    GetEntry(Long64_t entry);
   virtual Long64_t LoadTree(Long64_t entry);
   virtual void     Init(TTree *tree);
   virtual void     Loop();
   virtual Bool_t   Notify();
   virtual void     Show(Long64_t entry = -1);

   // ================= 用户自定义成员函数 =================
   virtual void     SetBranch(TTree *tree);       
   virtual void     TrackInit();                  
   virtual void     SetTrace(TH2D *h, Double_t k, Double_t b, Int_t min, Int_t max); 
};

// ... (下方的 tracking_cxx 实现部分保持原样不变) ...
#endif

tracking.C¶

#define tracking_cxx
#include "tracking.h"
#include <TH2.h>
#include <TStyle.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TGraphErrors.h>   // 【修改】必须包含带有误差的图类
#include <TFitResult.h>
#include <TMatrixDSym.h>    // 【新增】用于接收协方差矩阵
#include <iostream>
#include <cmath>

using namespace std;

void tracking::SetBranch(TTree *tree)
{
    tree->Branch("xx", xx, "xx[3]/D");
    tree->Branch("xz", xz, "xz[3]/D");
    tree->Branch("yy", yy, "yy[3]/D");
    tree->Branch("yz", yz, "yz[3]/D");
    tree->Branch("dx", dx, "dx[3]/D");
    tree->Branch("dy", dy, "dy[3]/D");
    tree->Branch("xx2b", xx2b, "xx2b[2]/D");
    tree->Branch("yy2b", yy2b, "yy2b[2]/D");
    tree->Branch("anode2b", &anode2b, "anode2b/D");  
  
    // 【新增】注册所有运动学中心值及其物理误差
    tree->Branch("tx", &tx, "tx/D");
    tree->Branch("ty", &ty, "ty/D");
    tree->Branch("theta_x", &theta_x, "theta_x/D");
    tree->Branch("theta_y", &theta_y, "theta_y/D");
    tree->Branch("sigma_tx", &sigma_tx, "sigma_tx/D");
    tree->Branch("sigma_ty", &sigma_ty, "sigma_ty/D");
    tree->Branch("sigma_thetax", &sigma_thetax, "sigma_thetax/D");
    tree->Branch("sigma_thetay", &sigma_thetay, "sigma_thetay/D");
  
    tree->Branch("c2nx", &c2nx, "c2nx/D");
    tree->Branch("c2ny", &c2ny, "c2ny/D");
    tree->Branch("beamTrig", &beamTrig, "beamTrig/I");
    tree->Branch("must2Trig", &must2Trig, "must2Trig/I");
    tree->Branch("targetX", &targetX, "targetX");
    tree->Branch("targetY", &targetY, "targetY");  
}

void tracking::TrackInit()
{
    // 初始化所有计算变量为无效值,防止上一个事件的数据污染
    tx = -999; ty = -999;
    theta_x = -999; theta_y = -999;
    sigma_tx = -1; sigma_ty = -1;             // 误差初始化为负数代表无效
    sigma_thetax = -1; sigma_thetay = -1;
  
    xx[0] = PPACF8[0][0];  yy[0] = PPACF8[0][1];  xz[0] = PPACF8[0][2];  yz[0] = PPACF8[0][3];
    xx[1] = PPACF8[2][0];  yy[1] = PPACF8[2][1];  xz[1] = PPACF8[2][2];  yz[1] = PPACF8[2][3];
    xx[2] = PPACF8[4][0];  yy[2] = PPACF8[4][1];  xz[2] = PPACF8[4][2];  yz[2] = PPACF8[4][3];

    xx2b[0] = PPACF8[3][0]; yy2b[0] = PPACF8[3][1];
    xz2b    = PPACF8[3][2]; yz2b    = PPACF8[3][3];
    anode2b = PPACF8[3][4];
  
    xx2b[1] = -1000; yy2b[1] = -1000;
}

void tracking::SetTrace(TH2D *h, Double_t k, Double_t b, Int_t min, Int_t max){
    if(h == 0 || min >= max) return;
    for(int i = min; i < max; i++){
        h->Fill(i, (Int_t)(i * k + b));
    }
}

void tracking::Loop()
{
    if (fChain == 0) return;

    TFile *opf = new TFile("tracking.root", "RECREATE");
    TTree *tree = new TTree("tree", "PPAC Tracking Data");
    SetBranch(tree);

    TH2D *htf8xz = new TH2D("htf8xz", "X-Z Plane Trace; Z (mm); X (mm)", 2200, -2000, 200, 300, -150, 150);
    TH2D *htf8yz = new TH2D("htf8yz", "Y-Z Plane Trace; Z (mm); Y (mm)", 2200, -2000, 200, 300, -150, 150);

    // 【核心修改】使用 TGraphErrors 替代 TGraph,赋予探测器真实的本征分辨率
    TGraphErrors *grx = new TGraphErrors(3);
    TGraphErrors *gry = new TGraphErrors(3);
    TF1 *fx = new TF1("fx", "pol1", -2000, 0); 
    TF1 *fy = new TF1("fy", "pol1", -2000, 0);

    // 假设:所有PPAC每层的本征位置分辨率为 1.0 mm
    const double det_resolution = 1.0; 
    const double z_target = 0.0; // 物理靶所在Z坐标位置

    Long64_t nentries = fChain->GetEntriesFast();
    Long64_t nbytes = 0, nb = 0;
    
    for (Long64_t jentry = 0; jentry < nentries; jentry++) {
        Long64_t ientry = LoadTree(jentry);
        if (ientry < 0) break;
        nb = fChain->GetEntry(jentry);   nbytes += nb;
       
        TrackInit();  
       
        bool b1a = abs(xx[0]) < 150 && abs(yy[0]) < 150; 
        bool b2a = abs(xx[1]) < 150 && abs(yy[1]) < 150;
        bool b3  = abs(xx[2]) < 100 && abs(yy[2]) < 100;
        if(!b1a || !b2a || !b3) continue; 
      
        // ================= X-Z 平面径迹拟合与误差计算 =================
        for(int i=0; i<3; i++) {
            grx->SetPoint(i, xz[i], xx[i]); 
            grx->SetPointError(i, 0.0, det_resolution); // 关键:输入Z和X的误差
        }
        
        // 【核心修改】去除 "W" 选项。S=保存结果(以获取矩阵), Q=静默模式
        TFitResultPtr rx = grx->Fit(fx, "SQ"); 
        
        if (rx->IsValid()) {
            double p0_x = fx->GetParameter(0);
            double p1_x = fx->GetParameter(1);
            
            // 提取中心值
            xx2b[1] = fx->Eval(xz2b);   
            tx      = p0_x + p1_x * z_target;
            theta_x = atan(p1_x); // 物理出射角 (rad)
            
            // 提取协方差矩阵并计算严谨物理误差
            TMatrixDSym cov_x = rx->GetCovarianceMatrix();
            double var_p0 = cov_x(0, 0);
            double var_p1 = cov_x(1, 1);
            double cov_p0_p1 = cov_x(0, 1);
            
            // 外推位置误差传递公式
            double err2_tx = var_p0 + (z_target * z_target * var_p1) + (2.0 * z_target * cov_p0_p1);
            sigma_tx = sqrt(err2_tx);
            
            // 角度非线性误差传递公式
            sigma_thetax = sqrt(var_p1) / (1.0 + p1_x * p1_x);
            
            c2nx = rx->Chi2() / rx->Ndf();
            SetTrace(htf8xz, p1_x, p0_x, -1800, 0);
            for(int i=0; i<3; i++) dx[i] = xx[i] - fx->Eval(xz[i]); 
        }

        // ================= Y-Z 平面径迹拟合与误差计算 =================
        for(int i=0; i<3; i++) {
            gry->SetPoint(i, yz[i], yy[i]);
            gry->SetPointError(i, 0.0, det_resolution); 
        }
        
        TFitResultPtr ry = gry->Fit(fy, "SQ");
        
        if (ry->IsValid()) {
            double p0_y = fy->GetParameter(0);
            double p1_y = fy->GetParameter(1);
            
            yy2b[1] = fy->Eval(yz2b);
            ty      = p0_y + p1_y * z_target;
            theta_y = atan(p1_y);
            
            TMatrixDSym cov_y = ry->GetCovarianceMatrix();
            double var_p0 = cov_y(0, 0);
            double var_p1 = cov_y(1, 1);
            double cov_p0_p1 = cov_y(0, 1);
            
            double err2_ty = var_p0 + (z_target * z_target * var_p1) + (2.0 * z_target * cov_p0_p1);
            sigma_ty = sqrt(err2_ty);
            
            sigma_thetay = sqrt(var_p1) / (1.0 + p1_y * p1_y);
            
            c2ny = ry->Chi2() / ry->Ndf(); 
            SetTrace(htf8yz, p1_y, p0_y, -1800, 0);
            for(int i=0; i<3; i++) dy[i] = yy[i] - fy->Eval(yz[i]);
        }
     
        // 将本事件结果写入 Tree (包括新算出的误差)
        tree->Fill();
        
        if(jentry % 10000 == 0) cout << "Processing Event: " << jentry << " / " << nentries << endl;
    }
    
    // 释放内存并保存结果
    delete grx; delete gry;
    delete fx;  delete fy;

    htf8xz->Write();
    htf8yz->Write();
    tree->Write();
    opf->Close();
    
    cout << "Tracking finished. Output saved to tracking.root" << endl;
}
In [7]:
//%jsroot on
TFile *ipf = new TFile("tracking.root");
TTree *tree = (TTree*) ipf->Get("tree");
TCanvas *c1 = new TCanvas("c1","c1");

束流径迹¶

In [9]:
TH2D *hxz = (TH2D*) ipf->Get("htf8xz");
hxz->Draw("colz");
c1->Draw();
In [10]:
TH2D *hyz = (TH2D*) ipf->Get("htf8yz");
hyz->Draw("colz");
c1->Draw();

束流在靶上投影¶

  • 假设靶与束流线垂直。
In [12]:
tree->Draw("ty:tx>>htx(120,-60,60,120,-60,60)","must2Trig","colz");
c1->Draw();
In [13]:
tree->Draw("tx:ty>>htx(120,-60,60,120,-60,60)","beamTrig","colz");
c1->Draw();

$\chi^2/Ndf : tx$

残差,chi2/NDF¶

  • 下图中呈现的残差分布并不是单一的gaus分布,有一个窄的gauss重叠在宽的gauss之上。这种现象在气体探测器中比较常见,其物理根源有:
  1. 多重库仑散射 (MCS): 高能束流穿过 PPAC 的镀铝Mylar或工作气体时,发生的卢瑟福散射天然具有非高斯的大角度拖尾。
  2. $\delta$ 电子 : 入射粒子打出的高能次级电子在气体中横向游走,使得雪崩电荷的重心偏离了粒子真实的穿透点。
  3. 电子学与束流本底: Pile-up 或电子学的噪声。

在实际的离线数据分析中,会使用 双高斯函数:窄核 + 宽尾对分布进行拟合。

  • 宽尾(Tail): 吸收由上述多重散射和 $\delta$ 电子造成的物理本底。
  • 窄核(Core): 剥离本底后,只有这个窄核的高斯宽度($\sigma_{core}$)才代表探测器真实的响应。
In [16]:
TF1 *total2 = new TF1("total2", "gaus(0)+gaus(3)");
In [17]:
TF1 *g1 = new TF1("g1", "gaus");
TF1 *g2 = new TF1("g2", "gaus");
TF1 *total = new TF1("total", "gaus(0) + gaus(3)");
TH1F *hdx;
Double_t sigma;
In [18]:
tree->Draw("dx[0]>>hdx(200,-5,5)");
hdx = (TH1F*)gROOT->FindObject("hdx");
hdx->Fit("g1","","",-0.5,0.5);
sigma = g1->GetParameter(2);
total->SetParameter(2,sigma);
total->SetParameter(5,7*sigma);//初始化,估计半宽为2*sigma;
hdx->Fit("total");
gPad->SetLogy(0);
c1->Draw();//residual
****************************************
Minimizer is Minuit2 / Migrad
Chi2                      =      1890.97
NDf                       =           17
Edm                       =  5.74935e-07
NCalls                    =           70
Constant                  =      16429.2   +/-   53.2958     
Mean                      =   -0.0644376   +/-   0.000586192 
Sigma                     =     0.225004   +/-   0.000603074  	 (limited)
****************************************
Minimizer is Minuit2 / Migrad
Chi2                      =      4054.38
NDf                       =          194
Edm                       =  8.03563e-06
NCalls                    =          327
p0                        =      15539.3   +/-   54.0504     
p1                        =   -0.0690443   +/-   0.000592113 
p2                        =     0.216952   +/-   0.000650672 
p3                        =      852.983   +/-   8.11293     
p4                        =   -0.0868271   +/-   0.00572421  
p5                        =      1.36888   +/-   0.00718881  
In [20]:
tree->Draw("c2ny:dy[0]>>hh(40,-10,10,200,0,1000)","","colz");
c1->SetLogy(0);
c1->Draw();//从chi2/ndf图上可看出,部分事件的径迹拟合误差很大,这部分要在后续数据处理中去掉。
In [21]:
tree->Draw("c2ny>>hh(200,0,1000)","","");
gPad->SetLogy();
c1->Draw();//从chi2/ndf图上可看出,部分事件的径迹拟合误差很大,这部分要在后续数据处理中去掉。
In [22]:
gPad->SetLogy(0);
tree->Draw("tx:ty>>(120,-60,60)","c2nx<10 && c2ny<10 && beamTrig ","colz");
c1->Draw();//
In [23]:
tree->Draw("tx:ty>>(120,-60,60,120,-60,60)","(c2nx>20 || c2ny>20) && beamTrig ","colz");
c1->Draw();//

PPAC2B x,y,x-y的探测效率¶

在 ROOT 中,利用 TCut(条件切割)和 TTree::GetEntries(selection) 来快速统计满足特定物理条件的事件数,从而计算效率。

0. 定义物理筛选条件 (TCut)¶

首先,将复杂的逻辑判断定义为直观的 TCut 变量:

In [26]:
// ================= 入射粒子 =================
// 1. 预期穿过:拟合径迹外推至 2B 平面,且落在 200x200 mm 的灵敏面积内
TCut c2btrack = "abs(xx2b[1])<100 && abs(yy2b[1])<100"; 

// 2. 2B 探测器的阳极 (Anode) 确实给出了有效的时间信号
TCut c2ba     = "anode2b>-1000"; 

// ================= 探测数目 =================
// 测得有效位置:实际测量值落在正常范围内 (排除 -1000 等无效值)
TCut c2bx     = "abs(xx2b[0])<100";  // X 有信号
TCut c2by     = "abs(yy2b[0])<100";  // Y 信号

1. 计算穿过探测器灵敏面积的预期粒子数¶

利用刚才定义的 c2btrack,可以画出径迹在外推平面上的二维投影,并获取总入射事件数。

In [28]:
// 绘制外推位置的 2D 分布 (预期击中位置)
tree->Draw("yy2b[1]:xx2b[1]>>h_expected(200,-100,100,200,-100,100)", c2btrack, "colz");
c1->Draw();
// 统计预期穿过灵敏区域的总数
Long64_t N_track = tree->GetEntries(c2btrack);
cout << "预期穿过 PPAC2B 灵敏区的粒子总数 N_track = " << N_track << endl;
预期穿过 PPAC2B 灵敏区的粒子总数 N_track = 232296

2. 计算并对比两类探测效率¶

实际气态探测器的效率与入射粒子的种类 $(A, Z)$、能量以及击中位置均有关,示例代码求的是全局平均探测效率。

In [30]:
Long64_t Na;
Long64_t Nx, Ny, Nxy;
Double_t effa;
Double_t effx1, effy1, effxy1;
Double_t effx2, effy2, effxy2;
In [31]:
Long64_t Na, Nx, Ny, Nxy;

// 统计各项实际响应的事件数
Na  = tree->GetEntries(c2ba && c2btrack);       // 阳极触发数
Nx  = tree->GetEntries(c2bx && c2btrack);       // 仅要求 X 
Ny  = tree->GetEntries(c2by && c2btrack);       // 仅要求 Y 
Nxy = tree->GetEntries(c2bx && c2by && c2btrack); // 要求 X 和 Y 

// ==========================================================
// 方法一:径迹外推的“绝对探测效率” 
// ==========================================================
Double_t eff_a  = Double_t(Na)  / N_track; // 阳极本征效率
Double_t eff_x1 = Double_t(Nx)  / N_track; // X 绝对效率
Double_t eff_y1 = Double_t(Ny)  / N_track; // Y 绝对效率
Double_t eff_xy1 = Double_t(Nxy) / N_track; // (X,Y) 二维绝对效率


// ==========================================================
// 方法二:以探测器阳极信号为基准
// ==========================================================
Double_t eff_x2  = Double_t(Nx)  / Na; 
Double_t eff_y2  = Double_t(Ny)  / Na; 
Double_t eff_xy2 = Double_t(Nxy) / Na; 

// 格式化输出结果
printf("\n--- PPAC2B 绝对探测效率 (基于 Tracking) ---\n");
printf("阳极触发效率   eff_a  = %5.2f%%\n", eff_a * 100);
printf(" X 绝对效率   eff_x1 = %5.2f%%\n", eff_x1 * 100);
printf(" Y 绝对效率   eff_y1 = %5.2f%%\n", eff_y1 * 100);
printf(" X-Y 联合效率 eff_xy1 = %5.2f%%\n", eff_xy1 * 100);

printf("\n--- PPAC2B 位置读出效率 (基于 Anode) ---\n");
printf(" X 读出效率   eff_x2 = %5.2f%%\n", eff_x2 * 100);
printf(" Y 读出效率   eff_y2 = %5.2f%%\n", eff_y2 * 100);
printf(" X-Y 联合效率 eff_xy2 = %5.2f%%\n", eff_xy2 * 100);
c1->Draw();
--- PPAC2B 绝对探测效率 (基于 Tracking) ---
阳极触发效率   eff_a  = 99.23%
 X 绝对效率   eff_x1 = 93.11%
 Y 绝对效率   eff_y1 = 92.98%
 X-Y 联合效率 eff_xy1 = 86.87%

--- PPAC2B 位置读出效率 (基于 Anode) ---
 X 读出效率   eff_x2 = 93.84%
 Y 读出效率   eff_y2 = 93.70%
 X-Y 联合效率 eff_xy2 = 87.54%

讨论:¶

在实际的离线数据分析中,评估 PPAC 的位置探测效率是否引入“阳极(Anode)信号有效”作为符合条件,主要基于对本底抑制机制与物理统计量的权衡:

1. 不要求阳极符合 此方式客观反映了探测器提供可用空间坐标的概率,避免在后续计算截面时对效率进行过度扣除(Double Counting)。其劣势在于,阴极延迟线易受环境电磁干扰,仅依赖位置解算容易将纯电子学噪声误判为有效坐标。不过,在实际的多探测器径迹重建(Tracking)中,通常可依靠多层探测器之间的空间共线关联(通过拟合的 $\chi^2$ 判据)来自然剔除此类噪声影响。

2. 要求阳极符合 引入阳极符合能有效压制延迟线上的虚假噪声;同时,阳极的快时间信号在识别和剔除高束流下的多重击中(Pile-up)事件时不可或缺。当实验束流强度较高、Pile-up 影响显著时,必须引入此符合条件,其代价是整体探测效率会有所降低。

作业¶

Task A: 物理靶点外推与重建信息记录¶

示例代码演示了固定的 3 层探测器(1A, 2A, 3)拟合。但在实际物理分析中,为了最大化统计量,我们需要利用 F8PPAC1a, 1b, 2a, 2b, 3 这 5 层 的所有有效位置信息,进行动态径迹重建。

修改示例代码:

1. 径迹拟合与状态记录¶

  • 筛选有效点: 对每一个事件,逐一检查 5 层 PPAC 是否给出了有效的位置坐标(排除 -999 或越界噪声)。
  • 状态记录: 引入变量 Ndet_x 和 Ndet_y 记录实际参与 $X$ 和 $Y$ 平面拟合的探测器层数(需满足 $N \ge 2$ 才可拟合);引入数组 DetHit[5] 记录具体是哪几层参与了拟合(1代表参与,0代表未参与)。

2. 靶位坐标¶

  • 实验中,物理靶并非垂直放置,而是朝向望远镜倾斜了 $45^\circ$(靶的平面方程x+z=0 )。

  • 求解靶上坐标: 请将空间直线方程 $x = f_x(z)$ 和 $y = f_y(z)$ 与倾斜的靶平面方程联立,解出束流打在靶面上的真实三维交点坐标 targetX, targetY, targetZ。

  • 计算出靶上交点坐标的物理误差 ,以及束流出射角误差 。

  • 将上述所有拟合卡方值(chi2/ndf)、残差(Residual)、交点坐标及物理误差作为新 Branch 存入 TTree。

3. 数据分析¶

  1. 传输效率计算: 在 beamTrig(束流触发)条件下,统计“重建的束流打在给定物理靶尺寸范围内”的事件数比例(即靶的几何接受度/传输效率 $\varepsilon_{target}$)。
  2. 分别画出不同策略下的事件在靶位上的位置以及散射角分布,以及各项的误差分布。

Task B: PPAC 探测效率的多重验证¶

计算位于最上游的 PPAC1a 和最下游的 PPAC3 的 $x$ 效率、$y$ 效率以及 $(x,y)$ 二维联合效率。

要求进行交叉验证:

  1. 对比效率类型: 分别计算它们的绝对探测效率(方法一:Tracking 外推作为分母)和位置读出效率(方法二:自身阳极信号作为分母)。
  2. 在使用 Tracking 方法时,尝试改变“参考探测器”的组合(例如:测 PPAC3 时,分别尝试用 1a+2a 拟合,或用 1b+2b 拟合),验证在不同基准下求出的效率值是否具有自洽性(结果应相近)。
In [ ]: