1.4 TTree 的 MakeClass 方法¶

  • MakeClass方法提供了访问ROOT文件的TTree内部数据所需的类(class)的结构

1. 手动编写逐事件读取ROOT文件分析¶

  • 用户需要手动管理内存变量与磁盘数据分支(Branch)的映射, 这种方式在分支较少时可行。。
void Analyze() {
    // 1. 声明本地工作区变量
    Int_t raw_pid; 
    Double_t raw_tL, raw_tR; 

    // 2. 手动绑定地址:将磁盘 Branch 映射到内存变量地址
    tIn->SetBranchAddress("pid", &raw_pid);
    tIn->SetBranchAddress("tL",  &raw_tL);
    // ... 若有几十个分支,此处将极其冗长
}
  • 但当处理复杂的 ROOT 文件(如包含多维数组或数十个物理量分支)时,手动编写变量声明和 SetBranchAddress 既繁琐又极易出错。
In [2]:
TFile *ipf = new TFile("run0005.root"); 
TTree *tree = (TTree*)  ipf->Get("tree");
tree->Print();
******************************************************************************
*Tree    :tree      : tree must2                                             *
*Entries :   511950 : Total =       917722277 bytes  File  Size =  306046164 *
*        :          : Tree compression factor =   3.00                       *
******************************************************************************
*Br    0 :beamTrig  : beamTrig/I                                             *
*Entries :   511950 : Total  Size=    2049732 bytes  File Size  =      93041 *
*Baskets :       17 : Basket Size=     519680 bytes  Compression=  22.02     *
*............................................................................*
*Br    1 :must2Trig : must2Trig[8]/I                                         *
*Entries :   511950 : Total  Size=   16389351 bytes  File Size  =    1021856 *
*Baskets :       69 : Basket Size=    1369600 bytes  Compression=  16.04     *
*............................................................................*
*Br    2 :PPACF5    : PPACF5[5][5]/F                                         *
*Entries :   511950 : Total  Size=   51211689 bytes  File Size  =   21646784 *
*Baskets :      176 : Basket Size=    3433472 bytes  Compression=   2.37     *
*............................................................................*
*Br    3 :PPACF8    : PPACF8[5][5]/F                                         *
*Entries :   511950 : Total  Size=   51211689 bytes  File Size  =   27255795 *
*Baskets :      176 : Basket Size=    3433472 bytes  Compression=   1.88     *
*............................................................................*
*Br    4 :PPACF9    : PPACF9[5][5]/F                                         *
*Entries :   511950 : Total  Size=   51211689 bytes  File Size  =   14832700 *
*Baskets :      176 : Basket Size=    3433472 bytes  Compression=   3.45     *
*............................................................................*
*Br    5 :FPPosition : FPPosition[12][4]/F                                   *
*Entries :   511950 : Total  Size=   98325682 bytes  File Size  =   30590368 *
*Baskets :      319 : Basket Size=    6226944 bytes  Compression=   3.21     *
*............................................................................*
*Br    6 :F5PPACRawData : F5PPACRawData[5][10]/F                             *
*Entries :   511950 : Total  Size=  102423551 bytes  File Size  =   32627987 *
*Baskets :      332 : Basket Size=    6470144 bytes  Compression=   3.14     *
*............................................................................*
*Br    7 :F8PPACRawData : F8PPACRawData[5][10]/F                             *
*Entries :   511950 : Total  Size=  102423551 bytes  File Size  =   40257142 *
*Baskets :      332 : Basket Size=    6470144 bytes  Compression=   2.54     *
*............................................................................*
*Br    8 :F9PPACRawData : F9PPACRawData[5][10]/F                             *
*Entries :   511950 : Total  Size=  102423551 bytes  File Size  =   27991240 *
*Baskets :      332 : Basket Size=    6470144 bytes  Compression=   3.66     *
*............................................................................*
*Br    9 :F10PPACRawData : F10PPACRawData[5][10]/F                           *
*Entries :   511950 : Total  Size=  102423887 bytes  File Size  =   28125061 *
*Baskets :      332 : Basket Size=    6470656 bytes  Compression=   3.64     *
*............................................................................*
*Br   10 :F11PPACRawData : F11PPACRawData[5][10]/F                           *
*Entries :   511950 : Total  Size=  102423887 bytes  File Size  =   30127047 *
*Baskets :      332 : Basket Size=    6470656 bytes  Compression=   3.40     *
*............................................................................*
*Br   11 :F3PlaRawData : F3PlaRawData[4]/F                                   *
*Entries :   511950 : Total  Size=    8195004 bytes  File Size  =    3689434 *
*Baskets :       35 : Basket Size=     884224 bytes  Compression=   2.22     *
*............................................................................*
*Br   12 :F7PlaRawData : F7PlaRawData[4]/F                                   *
*Entries :   511950 : Total  Size=    8195004 bytes  File Size  =    3170206 *
*Baskets :       35 : Basket Size=     884224 bytes  Compression=   2.58     *
*............................................................................*
*Br   13 :F11PlaRawData : F11PlaRawData[4]/F                                 *
*Entries :   511950 : Total  Size=    8195043 bytes  File Size  =    2787872 *
*Baskets :       35 : Basket Size=     884224 bytes  Compression=   2.94     *
*............................................................................*
*Br   14 :F11ICRawData : F11ICRawData[10]/F                                  *
*Entries :   511950 : Total  Size=   20486459 bytes  File Size  =    6282467 *
*Baskets :       82 : Basket Size=    1612800 bytes  Compression=   3.26     *
*............................................................................*
*Br   15 :TOF       : TOF[8]/F                                               *
*Entries :   511950 : Total  Size=   16388913 bytes  File Size  =    6714974 *
*Baskets :       69 : Basket Size=    1369600 bytes  Compression=   2.44     *
*............................................................................*
*Br   16 :AoQ       : AoQ[9][4]/F                                            *
*Entries :   511950 : Total  Size=   73743159 bytes  File Size  =   28803633 *
*Baskets :      245 : Basket Size=    4768768 bytes  Compression=   2.56     *
*............................................................................*

2. MakeClass:自动化代码生成器¶

MakeClass 的核心作用是自动化数据映射。它通过扫描 TTree 的拓扑结构,自动生成一个包含所有变量定义、底层数据连接以及分析循环框架的 C++ 类。

生成方式: 在 ROOT 终端执行:

In [4]:
tree->MakeClass("ppac");
Info in <TTreePlayer::MakeClass>: Files: ppac.h and ppac.C generated from TTree: tree

ROOT 会生成 ppac.h 和 ppac.C。类 ppac 定义了该实验数据的结构及其操作接口,可以被视为一个自定义的高级数据类型。

3. ppac.h:类的声明与成员定义¶

ppac.h 负责定义数据的“模样”。根据源代码,其实际结构如下:

A. 成员变量(数据存放区) 类内部自动声明了与 Tree 分支完全对应的变量。相比传统做法,它能自动处理复杂的数组维度。

#ifndef ppac_h
#define ppac_h

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

class ppac {
public :
   TTree          *fChain;   //! 指向当前 TTree 或 TChain 的指针
   Int_t           fCurrent; //! 当前 Tree 在 Chain 中的编号

   // --- 自动生成的 Leaf (叶子) 变量 ---
   Int_t           beamTrig;
   Int_t           must2Trig[8];
   Float_t         F8PPACRawData[5][10]; // 自动处理了数组维度
   Float_t         AoQ[9][4];
   // ... 

   // 【用户扩展】:你可以在此处手动添加自定义变量
   // 例如:Double_t x_cal; 它们将成为 ppac 类型的一部分

B. 成员函数(操作接口声明) 类不仅包含数据,还定义了操作数据的函数:

   // --- 自动生成的成员函数 ---
   ppac(TTree *tree=0);           // 构造函数:用于初始化
   virtual ~ppac();               // 析构函数:用于清理资源
   virtual Int_t    GetEntry(Long64_t entry);      // 读取数据
   virtual void     Init(TTree *tree);             // 核心:执行地址绑定
   virtual void     Loop();                        // 主分析循环
   virtual void     Show(Long64_t entry = -1);     // 打印数据
   
   // 【用户扩展】:你可以在此处声明新的成员函数,例如 virtual void Calibrate();
};

构造函数与 Init()¶

构造函数 (Constructor) 在 ppac.h 的实现部分(#ifdef ppac_cxx),定义了类的构造函数。

  • 含义:它是类诞生的“初始化程序”。
  • 触发时机:一旦类被实例化(创建对象),构造函数就会立即自动运行。
  • 源代码实现:如果实例化时不传入任何参数(tree == 0),构造函数会默认去打开生成该类时的原始文件(run0005.root),并立即调用 Init(tree) 自动完成所有的 SetBranchAddress 映射工作。
ppac::ppac(TTree *tree) : fChain(0) 
{
   if (tree == 0) {
      // 自动连接生成代码时的原始文件 run0005.root
      TFile *f = (TFile*)gROOT->GetListOfFiles()->FindObject("run0005.root");
      if (!f || !f->IsOpen()) { f = new TFile("run0005.root"); }
      f->GetObject("tree",tree);
   }
   Init(tree); // 实例化时立即执行关键的地址绑定
}

Init() 函数 Init() 实现了传统做法中所有的 SetBranchAddress 操作,将磁盘数据流导向类成员变量。

void ppac::Init(TTree *tree)
{
   if (!tree) return;
   fChain = tree;
   fChain->SetMakeClass(1);

   // 自动生成的地址绑定代码:将 Branch 名称与类成员变量地址关联
   fChain->SetBranchAddress("beamTrig", &beamTrig, &b_beamTrig);
   fChain->SetBranchAddress("F8PPACRawData", F8PPACRawData, &b_F8PPACRawData);
   // ... 
}

fChain:它是类内部访问 Tree 的全局接口。所有的读取操作(如 GetEntry)最终都通过 fChain 执行。

Loop() 函数

  • 读入事件信息进行进一步处理。其具体实现在ppac.C

4. ppac.C:物理逻辑实现¶

ppac.C 提供了物理逻辑的存放位置。

  • #include "ppac.h":在 ppac.C 开头引入头文件。这意味着 ppac.C 获得了 ppac.h 中定义的所有变量和函数接口。

原始 Loop() 框架

生成的原始代码是一个空循环,展示了标准的数据读取流程:

#define ppac_cxx
#include "ppac.h"

void ppac::Loop() {
   if (fChain == 0) return;
   Long64_t nentries = fChain->GetEntriesFast(); // 获取总事件数

   for (Long64_t jentry=0; jentry<nentries;jentry++) {
      Long64_t ientry = LoadTree(jentry); // 确定当前 Entry 在哪个 Tree
      if (ientry < 0) break;
      fChain->GetEntry(jentry); // 核心:将该行数据真正读入 .h 中声明的变量中
      
      // 用户在此处扩展代码...
   }
}

用户物理逻辑扩展:

用户通过在 Loop() 中添加输出流和计算逻辑,将原始数据转化为物理量:

下列代码保存到ppac1.C中

#define ppac_cxx
#include "ppac.h"
#include <TH2.h>
#include <TStyle.h>
#include <TCanvas.h>

TH1D *h1 = new TH1D("h1", "h1", 2000, 0, 2000);
void ppac::Loop()
{   
  //new tree and root file  
  TFile *opf = new TFile("ppac.root", "recreate");
  TTree *tree = new TTree("tree", "ppac");
  
  Double_t txl, txr, tyd, tyu, ta;
  Double_t x, y;
  tree->Branch("txl", &txl, "txl/D");
  tree->Branch("txr", &txr, "txr/D");
  tree->Branch("tyd", &tyd, "tyd/D");
  tree->Branch("tyu", &tyu, "tyu/D");
  tree->Branch("ta",  &ta,  "ta/D");
  tree->Branch("x",   &x,   "x/D");
  tree->Branch("y",   &y,   "y/D");
    
    //TTree *fChain - Pointer pointing to the tree 
   if (fChain == 0) return;

   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;
      
     //user code
     if(jentry % 10 ==0) 
         h1->Fill(F8PPACRawData[0][1] + F8PPACRawData[0][0] - 2*F8PPACRawData[0][4]);
       
     txl = F8PPACRawData[0][0];
     txr = F8PPACRawData[0][1];
     tyd = F8PPACRawData[0][2];
     tyu = F8PPACRawData[0][3];
     ta = F8PPACRawData[0][4];
       
     bool bx = txl<4900 && txr<4900;
     bool by = tyu<4900 && tyd<4900;
     bool ba = ta<600 && ta>250; 
       
     x =- 5000;
     y =- 5000;
     if(bx && by && ba) {
         x = txr - txl;
         y = tyu - tyd;
         tree->Fill();
     }
        if(jentry%100000 == 0) cout << "processing " << jentry << endl;
   }
    tree->Write();
    opf->Close();
}

5. 运行流程:从“定义”到“执行”¶

在 ROOT 交互式终端中,操作逻辑如下:

  1. 加载定义:
In [6]:
.L ppac1.C
  1. 实例化(Instantiation):
    • ppac 是自定义数据类型,t 是该类型的对象实例。
    • 关键机制:运行此行时,构造函数自动运行。它自动执行了 Init()。此时 t 内部的变量(如 F8PPACRawData)已经与磁盘数据连接完毕。
    • 如果没有参数(root文件名),默认去打开生成该类时的原始文件(run0005.root)
    • 如果指定参数: ppac t("run0006.root"),则打开指定的文件run0006.root。
In [8]:
ppac t
(ppac &) @0x107db80a8
  1. 调用方法:
  • 调用内置成员函数 Show,快速打印第 13 个事件的原始数据。
In [10]:
t.Show(13);
======> EVENT:13
 beamTrig        = 1
 must2Trig       = 0, 
                  0, 0, 0, 0, 0, 0, 0
 PPACF5          = 63.2696, 
                  -7.9987, -418.7, -410.1, 75.8291, 62.9437, 
                  -8.3079, -381.3, -389.9, 63.184, 72.3041, 
                  -10.2965, 231.3, 239.9, 58.0741, 64.6791, 
                  -9.92849, 268.7, 260.1, 58.4691
 PPACF8          = 4.79569, 
                  -39.693, -1750.7, -1742.1, 43.7375, 4.19174, 
                  -34.1586, -1713.3, -1721.9, 54.5781, 3.61046, 
                  -32.6964, -1250.7, -1242.1, 58.1063, 4.50848, 
                  -31.5826, -1213.3, -1221.9, 51.2145
 PPACF9          = 1.18403, 
                  26.8555, -18.7, -10.1, 423.968, 0.852346, 
                  -1000, 18.7, 10.1, 406.757, -1000, 
                  18.8675, 681.3, 689.9, 415.817, 2.42226, 
                  18.6448, 718.7, 710.1, 420.787
 FPPosition      = 0, 
                  0, 0, 0, 0, 0, 
                  0, 0, 0, 0, 0, 
                  0, -1000, -1000, -1000, -1000, 
                  0, 0, 0, 0
 F5PPACRawData   = 608, 
                  1220, 734, 663, 540, 0, 
                  0, 0, 0, 0, 634, 
                  1269, 709, 797, 445, 0, 
                  0, 0, 0, 0
 F8PPACRawData   = 870, 
                  903, 402, 878, 298, 0, 
                  0, 0, 0, 0, 881, 
                  932, 525, 807, 368, 0, 
                  0, 0, 0, 0
 F9PPACRawData   = 3016, 
                  2988, 3086, 2719, 2927, 0, 
                  0, 0, 0, 0, 3044, 
                  2978, 2855, 2649, 2887, 0, 
                  0, 0, 0, 0
 F10PPACRawData  = 3008, 
                  2931, 2807, 2955, 2763, 0, 
                  0, 0, 0, 0, 2878, 
                  2901, 2632, 2840, 2850, 0, 
                  0, 0, 0, 0
 F11PPACRawData  = 3031, 
                  3123, 2917, 2713, 3554, 0, 
                  0, 0, 0, 0, 3090, 
                  2854, 2938, 2568, 3465, 0, 
                  0, 0, 0, 0
 F3PlaRawData    = 1643, 
                  1507, 0, 0
 F7PlaRawData    = 785, 
                  733, 0, 0
 F11PlaRawData   = 2329, 
                  2116, 0, 0
 F11ICRawData    = 0, 
                  1418, 1348, 1358, 1299, 1229, 
                  0, 0, 0, 0
 TOF             = -1000, 
                  -1000, -1000, -1000, -1000, -1000, 
                  -1000, -1000
 AoQ             = 7.03884, 
                  -1000, -1000, -1000, 7.03884, -1000, 
                  -1000, -1000, 6.85817, -1000, -1000, 
                  -1000, 6.8509, -1000, -1000, -1000, 
                  7.03884, -1000, -1000, -1000
  • 调用对象 t 内部封装的 Loop 方法,开始事件处理循环。
In [12]:
t.Loop();
processing 0
processing 100000
processing 200000
processing 300000
processing 400000
processing 500000
In [13]:
TCanvas *c1 = new TCanvas;
h1->Draw();
c1->Draw();
In [14]:
TFile *ipf1 = new TFile("ppac.root");
TTree *tree1 = (TTree*) ipf1->Get("tree");
tree1->Draw("x:y>>(1000,-800,800,1000,-800,800)","","colz");
c1->Draw();

TChain:处理多文件数据链¶

在物理实验中,数据通常分散在多个 run 文件中(如 run0001.root 到 run0010.root)。

TChain 对象是一个包含相同结构 TTree 的多个 ROOT 文件的列表。 由于 TChain 类继承自 TTree 类,在任何需要 TTree* 指针的地方(包括 ppac 类的构造函数),都可以直接使用 TChain* 指针。

组装 TChain 示例:

TChain *chain = new TChain("tree"); // "tree" 是文件内部的 TTree 名称
chain->Add("run0001.root");
chain->Add("run0002.root");
chain->Add("run0003.root");

// TChain 可直接调用 TTree 的方法,例如跨文件画图:
// chain->Draw("beamTrig");

由于 ppac 类已经定义好了固定的数据结构,只要其他文件的内部 TTree 结构与 ppac 内定义的完全一致,我们就可以直接复用这个类来分析新文件,而无需重新生成代码。

使用 TChain 处理文件链¶

结合第 4 节的内容,将整个 TChain 传给分析类,实现自动化批处理。

root [0] .L ppac.C
// ... (此处省略定义 chain 和 Add 文件的代码)
root [1] ppac t(chain); // 实例化:将包含了多个文件的链条传给构造函数
root [2] t.Loop();      // 自动跨文件遍历整个链条的所有事件

机制解析:此时类内部的 fChain 指针指向整个文件链。在 Loop() 循环内部调用的 LoadTree(entry) 函数,会在当前文件读取完毕时,自动、无缝地打开下一个文件并更新地址状态。


总结¶

  • 封装性:ppac 类将数百个变量和 Init、Loop 等方法封装在一起,形成一个独立的数据处理单元。
  • 可扩展性:用户可以在 ppac.h 中添加成员变量/函数,在 ppac.C 中实现逻辑。
  • 自动化:通过构造函数机制,实现了“实例化即绑定数据”,极大简化了分析流程。

作业:¶

  • 用MakeClass方法读入1.2作业中生成的ROOT文件,将实验变量(已进行walk修正,幅度增益修正)的tL,tR,AL,AR,转换成4096道的ADC和TDC参数保存成新的ROOT文件。
  • 读入新的ROOT文件,用itL,itR等数字化参数选择合适的变量范围计算tx等参数。
  • 对数字化参数进行连续化重复上一步,对比计算结果。
In [ ]: