2.5 实验数据处理过程
%jsroot on
实验数据处理分为两阶段:先完成解码、信号整理与刻度,再进行粒子重建和物理分析。自触发读出还需要按时间关联建立事件,见后面的 event building。
模块化处理与阶段性保存
将解码、刻度、粒子重建和物理量计算分别放在独立模块中,每个主要阶段都将结果保存为新的 ROOT 文件,作为下一阶段的输入。原始数据保持不变。调整 PID cut 或最终谱的拟合区间时,直接读取已经完成刻度与重建的结果;更改刻度时,则从相应的上游文件重算。
每个输出保存 run、源事例编号和使用的刻度参数,便于从异常结果追溯到原始信号。这样的组织既避免反复解码,也让每一阶段都能单独检查。
第一阶段:从原始读数到可分析的信号
DAQ 原始文件按电子学插件协议保存数据块,包含 ADC/TDC 道值、通道地址及 overflow 等状态。读取前先确认本实验使用的插件协议和 mapping。
解码(decoding or mapping):
- 解码程序通过读取数据中每个插件的特定标识,按对应协议进行解码。随后,根据实验设定的“探测器信号-插件通道”对应表(Mapping Map),将上述硬件数据转换成每个物理事件中具体探测器的测量值。经过此步骤生成的文件通常称为原始 ROOT 文件 (Raw ROOT File)。
事件重构(event building):
- 根据探测器信号之间的时间逻辑关系,重新组织和对齐事件结构(具体算法机制见第 5 章)。
步骤一:有效道值与连续化(Dithering)
保留原始 ADC/TDC 整数码和状态,先排除缺失、overflow 及有效区间外的读数。随后按照 1.3 的方法进行 dithering,再进入刻度和组合运算;这是本讲义逐事件处理的标准步骤。
采用 $N\leftrightarrow[N,N+1)$ 的约定时,生成 $N+U(0,1)$。每个事例的每路有效测量只随机化一次,后续能量、位置与时间计算复用这一连续值,避免重复随机化改变变量间的对应关系。
步骤二:根据有效信号重建物理量
信号不完整时,先判断缺少的是哪一路、哪些物理量仍可重建,不因一个无效参数就舍弃整件事例。缺失读数与真实零值分开保存,无效标记不参与刻度、求和和拟合。
例如,PPAC 的 anode 缺失但两端位置信号有效时,仍可由时间差重建位置,再用其他 PPAC 的径迹检验其一致性。这类事例无法应用需要 anode 的 time-sum 条件,应单独记录重建类别,并检查位置 residual 和效率。
长条闪烁体一端电荷缺失、两端时间有效时,可先由时间差求位置,再用已标定的光衰减关系,从另一端幅度估计沉积信号。这保留了部分原本会损失的事例,但其分辨不同于双端重建,应分别检查和标记。
步骤三:关键信息的直方图化存储 (Histograms)
在处理每个 run 的事件循环中,同时填充常用的原始谱、hit 分布和关联谱,并随结果保存。这样检查数据质量时可以直接打开 histogram,不必反复扫描整个 TTree。直方图可与树保存在同一 ROOT 文件中,也可集中写入 hist001.root;逐事件信息仍由 TTree 保留。
- 常用信息包括:ADC/TDC 各通道的原始能谱和时间谱、同类型探测器的 Hit 分布图、探测器间的二维关联谱(如 $\Delta E-E$, $TOF-\Delta E$)等。
利用目录结构组织直方图:
当直方图数量庞大时,可使用 TDirectoryFile 在 ROOT 文件内部创建类似文件系统的目录结构,以便于在 TBrowser 中快速浏览。
代码示例:在 ROOT 文件中创建目录并分类存储直方图
root -l mg18.root
root[0] new TBrowser
- 在左侧栏中双击文件名,在Draw Option栏中填入“colz”等绘图选项。

gRandom->SetSeed(2501); // 固定示例的随机数种子
// 1. 创建用于存储直方图的 ROOT 文件
TFile *fout = new TFile("hist_test.root", "RECREATE");
// 2. 在根目录下创建一级目录 dir1 和 dir2
TDirectoryFile *dir1 = new TDirectoryFile("dir1", "ADC Spectra");
TDirectoryFile *dir2 = new TDirectoryFile("dir2", "TDC Spectra");
// 3. 在 dir1 下创建子目录 dir1sub
dir1->cd();
TDirectoryFile *dir1sub = new TDirectoryFile("dir1sub", "PPAC Detail");
// 4. 声明并初始化直方图 (注意在 new 之前切换到对应的目录)
TH1I *h0, *h1, *h2, *h1sub;
fout->cd(); // 回到根目录
h0 = new TH1I("h0", "Global Hist", 100, -3, 3);
dir1->cd(); // 进入 dir1
h1 = new TH1I("h1", "ADC Channel 1", 100, -3, 3);
dir1sub->cd(); // 进入子目录
h1sub = new TH1I("h1sub", "PPAC X pos", 100, -3, 3);
dir2->cd(); // 进入 dir2
h2 = new TH1I("h2", "TDC Channel 1", 100, -3, 3);
// 5. 填充数据 (实际分析中通常在 Event Loop 中进行)
h0->FillRandom("gaus", 10000);
h1->FillRandom("gaus", 10000);
h1sub->FillRandom("gaus", 10000);
h2->FillRandom("gaus", 10000);
// 6. 一次性将所有内存中的直方图按目录结构写入文件
fout->Write();
fout->Close();
写入后的目录结构:
Read hist from the file
- root环境直接读取(见上图)
root -l hist_test.root
>new TBrowser
在左边树形目录结构中找到文件,鼠标点击进入目录结构,点击相应hist
- 代码读取
TFile *fin=new TFile("hist_test.root");
fin->ls();
TFile** hist_test.root TFile* hist_test.root KEY: TDirectoryFile dir1;1 ADC Spectra KEY: TDirectoryFile dir2;1 TDC Spectra KEY: TH1I h0;1 Global Hist
TH1I* h0a=(TH1I*) fin->Get("h0");
TH1I* h1a=(TH1I*) fin->Get("dir1/h1");
TH1I* h2a=(TH1I*) fin->Get("dir2/h2");
TH1I* h1suba=(TH1I*) fin->Get("dir1/dir1sub/h1sub");
TCanvas *c1 = new TCanvas("c1","Directory example");
h1suba->Draw();
c1->Draw();
步骤四:实验参数一致性检验 (Drift Check)
由于探测器工作条件的变化(温度波动、噪声引入等)以及电子学参数的长期漂移,实验设备的增益或基线可能会随时间发生改变。 在进入正式物理刻度和设定数据切割条件(Cut)之前,先检查基础参数的时间稳定性,并进行必要的动态修正。
- 文件内变化:观察参数值随事件编号(Event Number)或绝对时间戳(Timestamp)的变化趋势。
- 全局变化:观察参数的统计平均值(如某特征峰的位置、探测器效率、时间分辨)随 Run Number(文件编号)的宏观漂移情况。
下图显示某次实验中TDC值、位置探测器分辨、效率等参数随着文件号的变化。

第二阶段:事件重建与物理分析
用刻度后的信号构建粒子 hit,处理相邻条的 charge sharing、front-back 对应关系和探测器间符合,再计算位置、能量及时间。第三章将逐步处理 DSSD 的这些问题。
物理选择常来自 ΔE–E 或 TOF–ΔE 的 PID、prompt 时间区间以及几何或运动学约束。选择后的数据仍可能含本底,且 cut 会改变效率与接受度。
在确定的反应假设下,用能量和方向计算 Q 值、激发能等物理量。截面还需要束流归一、靶厚、探测效率与接受度。重建和选择参数保存在结果旁,后续改变 cut 时就能知道哪些步骤需要重算。
物理条件的选择
用 $\Delta E$–$E$ 或 TOF–$\Delta E$ 的粒子带作 PID;用符合时间窗减少不相关信号;用探测器几何、靶点和守恒关系检验反应候选。prompt 窗口内仍含偶然符合,必要时以时间 sideband 估计。每项 cut 都可能改变效率或接受度,不是“选后数据必然纯净”。
运动学重建
由靶点和击中位置求出射方向,结合刻度后的能量计算 Q 值、激发能或不变质量。多粒子实验还需先确定各层信号属于哪个粒子。保存中间 hit 与事例编号,能把最终谱中的异常追溯到信号层。
谱分析与截面
峰的拟合给出中心、宽度和净计数,但观测峰宽同时包含探测器分辨与物理展宽;只有分离这些贡献后,才可能由自然宽度讨论寿命。Gaussian、Breit–Wigner 及连续本底模型应根据具体反应和响应选择。
反应截面需要将净计数对束流总数、靶核面密度、探测效率及几何接受度归一化。蒙特卡罗可计算给定装置和分析条件下的接受度,但其结果也依赖输入模型;数据质量检查和模型验证仍要保留在这一链条中。
用 Bash 组织多个 run 的处理
先从终端循环开始
下面在 code/compile2 目录运行。同一程序依次接收不同 run 号:
for run in {1..10}; do
./tracking "$run"
donerun 是当前编号,do ... done 是循环体,"$run" 取出变量的值。单个 run 已验证后,再把循环保存成脚本并增加参数及错误检查。
给脚本传入范围和目录
$1、$2 表示调用脚本时的前两个参数,$# 是参数个数。下面的完整脚本还检查输入文件是否存在、输出是否已有,并为每个 run 留下日志。
批量处理多个 run
先确认单个文件能正常处理,再用 Bash 循环调用 2.4 的程序。下面脚本接收起止 run、输入目录、输出目录和程序路径;每个 run 单独留日志,失败时继续处理其他 run,最后返回失败状态。
#!/usr/bin/env bash
set -u
if [ "$#" -ne 5 ]; then
echo "Usage: bash run_batch.sh first last input_dir output_dir program" >&2
exit 1
fi
first=$1; last=$2; input_dir=$3; output_dir=$4; program=$5
if ! [[ $first =~ ^[0-9]+$ && $last =~ ^[0-9]+$ ]] || (( first > last )); then
echo "Invalid run range" >&2
exit 1
fi
mkdir -p "$output_dir"
failed=0
for ((run=first; run<=last; run++)); do
printf -v input_file '%s/f8ppac%03d.root' "$input_dir" "$run"
printf -v output_file '%s/out%03d.root' "$output_dir" "$run"
if [ ! -f "$input_file" ] || [ -e "$output_file" ]; then
echo "Skip run $run: missing input or existing output" >&2
failed=1
continue
fi
if "$program" "$run" "$input_dir" "$output_dir" >"$output_dir/run$run.log" 2>&1; then
echo "Run $run completed"
else
echo "Run $run failed; see $output_dir/run$run.log" >&2
failed=1
fi
done
exit "$failed"在章目录运行:
bash run_batch.sh 1 1 . batch_output code/compile2/tracking"$var" 保留路径中的空格;>log 2>&1 把正常输出和错误都写入日志。输入文件缺失或程序返回非零状态时,该 run 不被当作成功结果。再次运行前先检查输出目录,避免覆盖此前结果。
后台运行与日志
长时间处理可在服务器上用 nohup 启动:
nohup bash run_batch.sh 1 100 . batch_output code/compile2/tracking > analysis.log 2>&1 &
tail -f analysis.log& 使命令在后台运行,nohup 忽略终端挂断信号;它不保证机器休眠、关机或作业被系统终止后仍继续。tail -f 跟随日志的新内容,按 Ctrl+C 只退出日志查看,不停止分析程序。
