本页补充逐事件分析中常用的文件与 TTree 操作。普通绘图、Scan 和 Cut 见 ROOT Tutorial II。下面的片段按小节独立使用,文件名、tree 名和变量名应与实际输入一致。
打开文件后先检查是否成功,再取出所需对象。创建输出文件后,将要保存的对象写入该文件。
TFile *input = TFile::Open("input.root");
if (!input || input->IsZombie()) return;
TH1 *spectrum = input->Get<TH1>("spectrum");
if (!spectrum) return;
TFile output("selected.root", "RECREATE");
spectrum->Write("spectrum");
output.Close();
input->Close();RECREATE 会覆盖同名文件;不希望覆盖时用 NEW。文件读出的直方图通常由输入文件管理,关闭文件后不能继续使用原指针。若要继续使用,关闭前调用 spectrum->SetDirectory(nullptr),并自行管理对象寿命。
TChain 将多个文件中的同名 tree 当作一条树顺序读取,不生成新的合并文件;各文件的分支名称和类型应一致。
TChain chain("tree");
chain.Add("run001.root");
chain.Add("run002.root");
cout << "Total entries = " << chain.GetEntries() << endl;
chain.Draw("energy>>hEnergy(400,0,20)", "energy>0", "hist");hadd 在终端执行,生成一个合并文件:同名直方图相加,兼容的 tree 接续事件。避免把同一输入重复加入;不同刻度版本的文件不能未经检查就合并。
hadd merged.root run001.root run002.root逐 run 执行分析与日志管理见 实验数据处理过程。
CopyTree 按条件复制事例。新树应属于输出文件,因此先切换到输出文件。
TFile input("input.root");
TTree *tree = input.Get<TTree>("tree");
if (!tree) return;
TFile output("selected.root", "RECREATE");
TTree *selected = tree->CopyTree("energy>2 && energy<8");
cout << "Selected entries = " << selected->GetEntries() << endl;
selected->Write();若逐事件计算只需少数分支,可以关闭其余分支的读取。数组分支使用的长度变量也要启用。
tree->SetBranchStatus("*", 0);
tree->SetBranchStatus("hit", 1);
tree->SetBranchStatus("energy", 1);
// 随后按通常方式 SetBranchAddress、GetEntry。
// 恢复全部读取:tree->SetBranchStatus("*", 1);预先定义直方图可以明确 bin 数和范围。>>h 重新填充,>>+h 在已有内容上累加;不要误把重复运行当成新的数据。
TH1D *h = new TH1D("h", "Energy;Energy (MeV);Counts", 400, 0, 20);
tree->Draw("energy>>h", "energy>0", "hist");用 goff 只取数值,不自动作图。Draw("y:x") 中 GetV1 对应 y,GetV2 对应 x。
tree->SetEstimate(tree->GetEntries()+1);
tree->Draw("y:x", "energy>2", "goff");
TGraph *graph = new TGraph(tree->GetSelectedRows(), tree->GetV2(), tree->GetV1());
graph->Draw("AP");这里假设 x、y 是每个事件各一个数值。数组展开后的行数可能多于事件数,应设置足够的缓冲容量。GetV1/GetV2 指向的缓冲区会被后续 Draw 更新;TGraph 构造时会复制这些数值。
默认 TH1F 用 Float_t 存储 bin content,单位计数累加超过 224 后不能保持逐整数精度;高统计量可预先使用 TH1D。GetEntries() 与 bin content 的和不等价,加权和积分的例子见 TH1 与 TF1 的积分。
参考:ROOT Trees · Object ownership。
Scan 用于直接检查少量事例;数组展开时,同一 Row 下不同 Instance 是该事例中的不同元素。最后两个参数依次是最多扫描的事例数与起始事例号。
tree->Scan("hit:energy", "hit>0", "", 10, 0);
h->Print("base"); // bin 数及范围
h->Print("range"); // 当前显示范围的 bin content 和 error
h->Print("all"); // 包含 underflow、overflow
h->Draw("hist"); // 用直方图轮廓显示
h->Draw("E"); // 显示误差棒hist 和 E 改变显示方式,不改变直方图中储存的计数及误差。
若要保存 Draw 得到的值供后续计算,可在下一次 Draw 前复制到 TVectorD:
tree->Draw("energy", "energy>0", "goff");
TVectorD values(tree->GetSelectedRows(), tree->GetV1());
// 为已有的图添加一个点:
graph->SetPoint(graph->GetN(), 5.0, 12.0);把 TGraph 的点填入 TH2 时,每个点贡献一次 Fill,得到的是点的密度分布,而不是自动恢复原实验的计数或权重。
TH2D hxy("hxy", "Graph points;x;y", 100,0,10, 100,0,20);
for (int i=0; i<graph->GetN(); ++i) {
double x, y;
graph->GetPoint(i,x,y);
hxy.Fill(x,y);
}gROOT->ProcessLine(".L analysis.C+"); // 用 ACLiC 编译并载入
if (!gSystem->AccessPathName("input.root")) {
cout << "Input file exists" << endl;
}AccessPathName 在文件可访问时返回 false,因此这里有一个逻辑取反。独立程序的编译、头文件和链接说明见2.3 编译执行。