检查 time-walk 修正

先检查各 cluster 内高能参考及全能区的分布,再检查不同 cluster 的高能时间差。比较相同能量选择下的峰位和宽度;全能区出现尾部时,应回到能量—时间差图查找来源。

%jsroot on
%%cpp -d
#include <iostream>

const int MAXHIT = 1024;
const int NCLUSTER = 12;
const int NSEG = 7;

// high-reference walk:
//   E_i vs t_i-t_j, with E_i > EminTarget and E_j > EminRef
TH2F *h_walk_cluster[NCLUSTER] = {0};

// all-energy walk:
//   E_i vs t_i-t_j, no energy gate
TH2F *h_walk_cluster_allE[NCLUSTER] = {0};

// high-energy time difference:
//   t_i-t_j, with E_i > EminRef and E_j > EminRef
TH1F *h_dt_cluster[NCLUSTER] = {0};
TH1F *h_dt_all = 0;

// all-energy time difference:
//   t_i-t_j, no energy gate
TH1F *h_dt_cluster_allE[NCLUSTER] = {0};
TH1F *h_dt_all_allE = 0;

TF1 *f_dt_cluster[NCLUSTER] = {0};
TF1 *f_dt_cluster_allE[NCLUSTER] = {0};

TF1 *f_dt_all = 0;
TF1 *f_dt_all_allE = 0;

// Make corrected walk plots and time-difference spectra

一次读取数据,同时填充高能参考和全能区的检查谱。二维图使用有方向的 i–j 组合;一维时间差按晶体编号排序,每一对只填一次。

%%cpp -d
void make_corrected_cluster_walk(
    const char *filename = "eurica_time_pair.root",
    double EminTarget = 30.0,
    double EminRef = 600.0)
{
  TH1::AddDirectory(kFALSE);
  for (int c = 0; c < NCLUSTER; c++) {
    if (h_walk_cluster[c]) {
      delete h_walk_cluster[c];
      h_walk_cluster[c] = 0;
    }

    h_walk_cluster[c] =
      new TH2F(Form("h_walk_cluster_%d", c),
               Form("cluster %d: corrected walk, E_{ref}>%.0f; t_i-t_j (ns); E_i",
                    c, EminRef),
               120, -600, 600,
               130, 0, 1300);

    h_walk_cluster[c]->SetDirectory(0);
    if (h_walk_cluster_allE[c]) {
      delete h_walk_cluster_allE[c];
      h_walk_cluster_allE[c] = 0;
    }

    h_walk_cluster_allE[c] =
      new TH2F(Form("h_walk_cluster_allE_%d", c),
               Form("cluster %d: corrected walk, all energy; t_i-t_j (ns); E_i",
                    c),
               120, -600, 600,
               130, 0, 1300);

    h_walk_cluster_allE[c]->SetDirectory(0);
    if (h_dt_cluster[c]) {
      delete h_dt_cluster[c];
      h_dt_cluster[c] = 0;
    }

    h_dt_cluster[c] =
      new TH1F(Form("h_dt_cluster_%d", c),
               Form("cluster %d: high-energy time difference; t_i-t_j (ns); counts",
                    c),
               240, -600, 600);

    h_dt_cluster[c]->SetDirectory(0);
    if (h_dt_cluster_allE[c]) {
      delete h_dt_cluster_allE[c];
      h_dt_cluster_allE[c] = 0;
    }

    h_dt_cluster_allE[c] =
      new TH1F(Form("h_dt_cluster_allE_%d", c),
               Form("cluster %d: all-energy time difference; t_i-t_j (ns); counts",
                    c),
               240, -600, 600);

    h_dt_cluster_allE[c]->SetDirectory(0);
  }
  if (h_dt_all) {
    delete h_dt_all;
    h_dt_all = 0;
  }

  h_dt_all =
    new TH1F("h_dt_all",
             "high-energy time difference: all clusters; t_i-t_j (ns); counts",
             240, -600, 600);

  h_dt_all->SetDirectory(0);
  if (h_dt_all_allE) {
    delete h_dt_all_allE;
    h_dt_all_allE = 0;
  }

  h_dt_all_allE =
    new TH1F("h_dt_all_allE",
             "all-energy time difference: all clusters; t_i-t_j (ns); counts",
             240, -600, 600);

  h_dt_all_allE->SetDirectory(0);

  TFile *fin = new TFile(filename);
  if (!fin || fin->IsZombie()) {
    std::cout << "cannot open " << filename << std::endl;
    return;
  }

  TTree *tree = (TTree*)fin->Get("tree");
  if (!tree) {
    std::cout << "cannot find tree in " << filename << std::endl;
    fin->Close();
    return;
  }

  int ghit;
  int gid[MAXHIT];
  double ge[MAXHIT];
  double gt[MAXHIT];
  if (tree->GetMaximum("ghit") > MAXHIT)
    throw std::runtime_error("Increase MAXHIT before reading branches");
  tree->SetBranchAddress("ghit", &ghit);
  tree->SetBranchAddress("gid", gid);
  tree->SetBranchAddress("ge", ge);
  tree->SetBranchAddress("gt", gt);

  Long64_t nentries = tree->GetEntries();
  for (Long64_t ientry = 0; ientry < nentries; ientry++) {

    tree->GetEntry(ientry);
    for (int a = 0; a < ghit; a++) {

      int cluster_a = gid[a] / 7;
      int seg_a = gid[a] % 7;
      if (cluster_a < 0 || cluster_a >= NCLUSTER) continue;
      if (seg_a < 0 || seg_a >= NSEG) continue;
      for (int b = 0; b < ghit; b++) {
        if (a == b) continue;

        int cluster_b = gid[b] / 7;
        int seg_b = gid[b] % 7;
        if (cluster_b != cluster_a) continue;
        if (seg_b < 0 || seg_b >= NSEG) continue;
        if (seg_b == seg_a) continue;

        double dt = gt[a] - gt[b];

        // Walk plots

        // all-energy walk: no energy gate
        h_walk_cluster_allE[cluster_a]->Fill(dt, ge[a]);

        // high-reference walk:
        // target hit can be low energy; reference hit is high energy
        if (ge[a] >= EminTarget && ge[b] >= EminRef) {
          h_walk_cluster[cluster_a]->Fill(dt, ge[a]);
        }

        // Time-difference spectra
        // Fill each detector pair once.
        // Use gid ordering, not array ordering.
        if (gid[a] >= gid[b]) continue;

        // all-energy time difference
        h_dt_cluster_allE[cluster_a]->Fill(dt);
        h_dt_all_allE->Fill(dt);

        // high-energy time difference
        if (ge[a] >= EminRef && ge[b] >= EminRef) {
          h_dt_cluster[cluster_a]->Fill(dt);
          h_dt_all->Fill(dt);
        }
      }
    }
  }

  fin->Close();
  for (int c = 0; c < NCLUSTER; c++) {
    std::cout << "cluster " << c
              << " high-ref walk entries = "
              << h_walk_cluster[c]->GetEntries()
              << ", all-energy walk entries = "
              << h_walk_cluster_allE[c]->GetEntries()
              << ", high-energy dt entries = "
              << h_dt_cluster[c]->GetEntries()
              << ", all-energy dt entries = "
              << h_dt_cluster_allE[c]->GetEntries()
              << std::endl;
  }

  std::cout << "all high-energy dt entries = "
            << h_dt_all->GetEntries()
            << std::endl;

  std::cout << "all all-energy dt entries = "
            << h_dt_all_allE->GetEntries()
            << std::endl;
}
%%cpp -d
void draw_corrected_cluster_walk()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_cluster_walk");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_cluster_walk",
                           "corrected walk by cluster: high-reference",
                           900, 600);

  c->Divide(4, 3);
  gStyle->SetOptStat(0);
  for (int ic = 0; ic < NCLUSTER; ic++) {
    c->cd(ic + 1);
    gPad->SetLogz(0);
    if (h_walk_cluster[ic]) {
      h_walk_cluster[ic]->Draw("colz");
    }
  }

  c->Draw();
}
%%cpp -d
void draw_corrected_cluster_walk_allE()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_cluster_walk_allE");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_cluster_walk_allE",
                           "corrected walk by cluster: all energy",
                           900, 600);

  c->Divide(4, 3);
  gStyle->SetOptStat(0);
  for (int ic = 0; ic < NCLUSTER; ic++) {
    c->cd(ic + 1);
    gPad->SetLogz(0);
    if (h_walk_cluster_allE[ic]) {
      h_walk_cluster_allE[ic]->Draw("colz");
    }
  }

  c->Draw();
}

逐 cluster 拟合高能时间差的中心峰;初值由最高 bin 给出。拟合窗口只描述 prompt 核心。

%%cpp -d
void draw_corrected_dt_by_cluster()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_dt_cluster");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_dt_cluster",
                           "high-energy time difference by cluster",
                           900, 600);

  c->Divide(4, 3);
  gStyle->SetOptStat(0);
  for (int ic = 0; ic < NCLUSTER; ic++) {

    c->cd(ic + 1);
    gPad->SetLogy(0);
    if (!h_dt_cluster[ic]) continue;
    if (f_dt_cluster[ic]) {
      delete f_dt_cluster[ic];
      f_dt_cluster[ic] = 0;
    }
    if (h_dt_cluster[ic]->GetEntries() < 20) {
      h_dt_cluster[ic]->Draw("hist");
      continue;
    }

    int maxbin = h_dt_cluster[ic]->GetMaximumBin();
    double x0 = h_dt_cluster[ic]->GetBinCenter(maxbin);

    f_dt_cluster[ic] =
      new TF1(Form("f_dt_cluster_%d", ic),
              "gaus",
              x0 - 50.0,
              x0 + 50.0);

    f_dt_cluster[ic]->SetParameter(0, h_dt_cluster[ic]->GetMaximum());
    f_dt_cluster[ic]->SetParameter(1, x0);
    f_dt_cluster[ic]->SetParameter(2, 60.0);

    h_dt_cluster[ic]->Fit(f_dt_cluster[ic], "RQ0");

    h_dt_cluster[ic]->Draw("hist");
    f_dt_cluster[ic]->SetLineColor(kRed);
    f_dt_cluster[ic]->Draw("same");

    std::cout << "high-energy cluster " << ic
              << ": mean = " << f_dt_cluster[ic]->GetParameter(1)
              << " ns, sigma = " << f_dt_cluster[ic]->GetParameter(2)
              << " ns"
              << std::endl;
  }

  c->Draw();
}

用相同流程检查全能区时间差,与高能结果比较低能 time walk 的影响。

%%cpp -d
void draw_corrected_dt_by_cluster_allE()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_dt_cluster_allE");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_dt_cluster_allE",
                           "all-energy time difference by cluster",
                           900, 600);

  c->Divide(4, 3);
  gStyle->SetOptStat(0);
  for (int ic = 0; ic < NCLUSTER; ic++) {

    c->cd(ic + 1);
    gPad->SetLogy(0);
    if (!h_dt_cluster_allE[ic]) continue;
    if (f_dt_cluster_allE[ic]) {
      delete f_dt_cluster_allE[ic];
      f_dt_cluster_allE[ic] = 0;
    }
    if (h_dt_cluster_allE[ic]->GetEntries() < 20) {
      h_dt_cluster_allE[ic]->Draw("hist");
      continue;
    }

    int maxbin = h_dt_cluster_allE[ic]->GetMaximumBin();
    double x0 = h_dt_cluster_allE[ic]->GetBinCenter(maxbin);

    f_dt_cluster_allE[ic] =
      new TF1(Form("f_dt_cluster_allE_%d", ic),
              "gaus",
              x0 - 100.0,
              x0 + 100.0);

    f_dt_cluster_allE[ic]->SetParameter(0, h_dt_cluster_allE[ic]->GetMaximum());
    f_dt_cluster_allE[ic]->SetParameter(1, x0);
    f_dt_cluster_allE[ic]->SetParameter(2, 60.0);

    h_dt_cluster_allE[ic]->Fit(f_dt_cluster_allE[ic], "RQ0");

    h_dt_cluster_allE[ic]->Draw("hist");
    f_dt_cluster_allE[ic]->SetLineColor(kRed);
    f_dt_cluster_allE[ic]->Draw("same");

    std::cout << "all-energy cluster " << ic
              << ": mean = " << f_dt_cluster_allE[ic]->GetParameter(1)
              << " ns, sigma = " << f_dt_cluster_allE[ic]->GetParameter(2)
              << " ns"
              << std::endl;
  }

  c->Draw();
}

将各 cluster 的高能时间差合并,显示拟合参数。

%%cpp -d
void draw_corrected_dt_all()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_dt_all");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_dt_all",
                           "cumulative high-energy time difference",
                           800, 600);

  c->SetLogy();
  if (!h_dt_all) {
    std::cout << "h_dt_all does not exist. Run make_corrected_cluster_walk() first."
              << std::endl;
    return;
  }
  if (f_dt_all) {
    delete f_dt_all;
    f_dt_all = 0;
  }
  if (h_dt_all->GetEntries() < 20) {
    h_dt_all->Draw("hist");
    c->Draw();
    return;
  }

  int maxbin = h_dt_all->GetMaximumBin();
  double x0 = h_dt_all->GetBinCenter(maxbin);

  f_dt_all =
    new TF1("f_dt_all",
            "gaus",
            x0 - 100.0,
            x0 + 100.0);

  f_dt_all->SetParameter(0, h_dt_all->GetMaximum());
  f_dt_all->SetParameter(1, x0);
  f_dt_all->SetParameter(2, 60.0);

  h_dt_all->Fit(f_dt_all, "RQ0");

  h_dt_all->Draw("hist");
  f_dt_all->SetLineColor(kRed);
  f_dt_all->Draw("same");

  c->Draw();

  std::cout << "all clusters high-energy:"
            << " mean = " << f_dt_all->GetParameter(1)
            << " ns, sigma = " << f_dt_all->GetParameter(2)
            << " ns"
            << std::endl;
}

将全能区时间差合并,与高能参考的结果比较。

%%cpp -d
void draw_corrected_dt_all_allE()
{
  TCanvas *c_old = (TCanvas*)gROOT->FindObject("c_corr_dt_all_allE");
  if (c_old) delete c_old;

  TCanvas *c = new TCanvas("c_corr_dt_all_allE",
                           "cumulative all-energy time difference",
                           800, 600);

  c->SetLogy();
  if (!h_dt_all_allE) {
    std::cout << "h_dt_all_allE does not exist. Run make_corrected_cluster_walk() first."
              << std::endl;
    return;
  }
  if (f_dt_all_allE) {
    delete f_dt_all_allE;
    f_dt_all_allE = 0;
  }
  if (h_dt_all_allE->GetEntries() < 20) {
    h_dt_all_allE->Draw("hist");
    c->Draw();
    return;
  }

  int maxbin = h_dt_all_allE->GetMaximumBin();
  double x0 = h_dt_all_allE->GetBinCenter(maxbin);

  f_dt_all_allE =
    new TF1("f_dt_all_allE",
            "gaus",
            x0 - 200.0,
            x0 + 200.0);

  f_dt_all_allE->SetParameter(0, h_dt_all_allE->GetMaximum());
  f_dt_all_allE->SetParameter(1, x0);
  f_dt_all_allE->SetParameter(2, 60.0);

  h_dt_all_allE->Fit(f_dt_all_allE, "RQ0");

  h_dt_all_allE->Draw("hist");
  f_dt_all_allE->SetLineColor(kRed);

  f_dt_all_allE->Draw("same");

  c->Draw();

  std::cout << "all clusters all-energy:"
            << " mean = " << f_dt_all_allE->GetParameter(1)
            << " ns, sigma = " << f_dt_all_allE->GetParameter(2)
            << " ns"
            << std::endl;
}

Corrected walk plots : high-energy reference

make_corrected_cluster_walk("eurica_time_pair.root");
draw_corrected_cluster_walk();
cluster 0 high-ref walk entries = 39878, all-energy walk entries = 257318, high-energy dt entries = 1175, all-energy dt entries = 128659
cluster 1 high-ref walk entries = 16848, all-energy walk entries = 119404, high-energy dt entries = 600, all-energy dt entries = 59702
cluster 2 high-ref walk entries = 38320, all-energy walk entries = 305710, high-energy dt entries = 1203, all-energy dt entries = 152855
cluster 3 high-ref walk entries = 33064, all-energy walk entries = 228046, high-energy dt entries = 1005, all-energy dt entries = 114023
cluster 4 high-ref walk entries = 39754, all-energy walk entries = 283122, high-energy dt entries = 1266, all-energy dt entries = 141561
cluster 5 high-ref walk entries = 44449, all-energy walk entries = 328816, high-energy dt entries = 1418, all-energy dt entries = 164408
cluster 6 high-ref walk entries = 66762, all-energy walk entries = 447384, high-energy dt entries = 1915, all-energy dt entries = 223692
cluster 7 high-ref walk entries = 0, all-energy walk entries = 0, high-energy dt entries = 0, all-energy dt entries = 0
cluster 8 high-ref walk entries = 13768, all-energy walk entries = 94514, high-energy dt entries = 393, all-energy dt entries = 47257
cluster 9 high-ref walk entries = 37841, all-energy walk entries = 272640, high-energy dt entries = 1418, all-energy dt entries = 136320
cluster 10 high-ref walk entries = 38395, all-energy walk entries = 257078, high-energy dt entries = 1281, all-energy dt entries = 128539
cluster 11 high-ref walk entries = 50814, all-energy walk entries = 356328, high-energy dt entries = 1558, all-energy dt entries = 178164
all high-energy dt entries = 13232
all all-energy dt entries = 1.47518e+06

Time-difference spectra : high-energy reference

以下谱只统计同一 cluster 内的 pair;“all”表示把这些谱合并,不是跨 cluster 的时间差。Gaussian 拟合描述 prompt 峰核心,不能代表全部尾部的效率。

draw_corrected_dt_by_cluster();
draw_corrected_dt_all();
high-energy cluster 0: mean = 4.00458 ns, sigma = 22.4145 ns
high-energy cluster 1: mean = -2.98153 ns, sigma = 26.1133 ns
high-energy cluster 2: mean = 1.86119 ns, sigma = 21.7397 ns
high-energy cluster 3: mean = -2.30874 ns, sigma = 24.7694 ns
high-energy cluster 4: mean = 3.52715 ns, sigma = 24.2324 ns
high-energy cluster 5: mean = 1.45389 ns, sigma = 24.155 ns
high-energy cluster 6: mean = -1.15531 ns, sigma = 23.2892 ns
high-energy cluster 8: mean = -2.49556 ns, sigma = 22.8304 ns
high-energy cluster 9: mean = 3.77452 ns, sigma = 23.9822 ns
high-energy cluster 10: mean = -3.91272 ns, sigma = 25.485 ns
high-energy cluster 11: mean = -3.9237 ns, sigma = 25.3389 ns
all clusters high-energy: mean = 0.0868766 ns, sigma = 26.5682 ns

Corrected walk plots : all-energy

draw_corrected_cluster_walk_allE();

Time-difference spectra : all-energy

draw_corrected_dt_by_cluster_allE();
draw_corrected_dt_all_allE();
all-energy cluster 0: mean = 2.04605 ns, sigma = 42.8448 ns
all-energy cluster 1: mean = -2.16476 ns, sigma = 43.941 ns
all-energy cluster 2: mean = 4.45549 ns, sigma = 43.5069 ns
all-energy cluster 3: mean = -2.79493 ns, sigma = 45.3416 ns
all-energy cluster 4: mean = 3.68107 ns, sigma = 43.1379 ns
all-energy cluster 5: mean = 1.05046 ns, sigma = 45.4297 ns
all-energy cluster 6: mean = -0.384232 ns, sigma = 44.037 ns
all-energy cluster 8: mean = 1.51517 ns, sigma = 43.4519 ns
all-energy cluster 9: mean = -2.03786 ns, sigma = 44.4928 ns
all-energy cluster 10: mean = -1.00156 ns, sigma = 44.2354 ns
all-energy cluster 11: mean = -4.23082 ns, sigma = 48.3964 ns
all clusters all-energy: mean = 0.0134643 ns, sigma = 50.1657 ns

跨 cluster 的高能时间差

固定 cluster 0 为参考,画 t(cluster)−t(cluster 0),两条能量均取 600–3000 keV。每个无序 pair 只填一次,不补相反符号。若某个 cluster 的 prompt ridge 系统性偏离零,需要先校正该常数 offset,再应用共同的 prompt gate。

TFile *fcross=TFile::Open("eurica_time_pair.root");
TTree *tcross=(TTree*)fcross->Get("tree");
int nh, id[MAXHIT]; double energy[MAXHIT], time[MAXHIT];
tcross->SetBranchAddress("ghit",&nh);
tcross->SetBranchAddress("gid",id);
tcross->SetBranchAddress("ge",energy);
tcross->SetBranchAddress("gt",time);
TH2F *hcross=new TH2F("hcross","High-energy cross-cluster timing;Cluster;#Deltat (ns)",11,0.5,11.5,160,-800,800);
for (Long64_t n=0;n<tcross->GetEntries();++n) {
    tcross->GetEntry(n);
    for (int i=0;i<nh;++i) {
        if (id[i]/7!=0 || energy[i]<600 || energy[i]>3000) continue;
        for (int j=0;j<nh;++j) {
            if (id[j]/7==0 || energy[j]<600 || energy[j]>3000) continue;
            hcross->Fill(id[j]/7,time[j]-time[i]);
        }
    }
}
TCanvas *ccross=new TCanvas("ccross","Cross-cluster timing",800,450);
hcross->Draw("colz");
ccross->Draw();