Exercise2.3-练习李老师课间内容

  • 本节主要目的:利用MakcClass生成的文件,编写可编译执行的分析程序

文件和目录的组织

主目录: ./tracking

  ./tracking 
      - main.cpp  - 主程序
      - Makefile -编译方法

  ./tracking/include/  
      - tracking.h (*.h, *.hh)  - 头文件

  ./tracking/src/ 
      - tracking.C(*.cc, *.cpp) -源文件

利用TTree的MakeClass()生成.h 和.C文件

  • 将tracking.h 移至 tracking/include目录, 将tracking.C 移至 tracking/src目录

main.cpp -- main 函数中写程序的主要流程。

  • 在./tracking目录编辑main.c
#include <iostream> 
#include <TFile.h>
#include <TTree.h>
#include <TString.h>
#include "tracking.h"
using namespace std;

int main(int argc, char** argv)
{
   if(argc !=2) {
       cout<<"Usage: ./"<<argv[0]<<" run_number "<<endl;
       return -1;
   }
   int run_number = atoi(argv[1]);
   TString InputPath, OutputPath, infile, outfile;  
    InputPath = "./";
    OutputPath = "./";
    infile.Form("%sf8ppac%03d.root", InputPath.Data(), run_number);
    outfile.Form("%sout%03d.root", OutputPath.Data(), run_number);
    //input
    TFile *ipf = new TFile(infile);
    if(!ipf->IsOpen()) {
        cout<<"Cannot open input file: "<<infile<<endl;
        return -1;
    }
    TTree *ipt = (TTree*)ipf->Get("tree");

    //output
    TFile *opf = new TFile(outfile,"RECREATE");
    TTree *opt = new TTree("tree","ppac tracking");

    //
    tracking *tk=new tracking(ipt);//当traking构造函数中传入的ipt已存在时,构造函数内部的tree指针就指向ipt
    tk->Loop(opt);

    //
    ipf->Close();
    opf->Close();
    return 1;        
}

头文件(.h):写类的声明(包括成员和方法的声明)、函数原型、#define常数等,但一般来说不写出具体的实现。

在写头文件时需要注意,在开头和结尾处必须按照如下样式加上预编译语句(如下):

#ifndef tracking_h

   #define tracking_h

   //你的代码写在这里

   #endif

这样做是为了防止重复编译,不这样做就有可能出错。 至于tracking_h这个名字实际上是无所谓的,你叫什么都行,只要符合规范都行。原则上来说,非常建议把它写成这种形式,因为比较容易和头文件的名字对应。

修改tracking.h

按照上述原则应将原tracking.h目录下的成员函数的具体实现部分挪至src/tracking.cpp文件内,但tracking.h中部分成员函数的实现是MakeClass自动生成的,$\color{red}{为了尽量少修改自动生成的代码,不改动原traking.h的代码}$。

//////////////////////////////////////////////////////////
// This class has been automatically generated on
// Wed Mar 11 09:58:39 2020 by ROOT version 6.18/04
// from TTree tree/tree
// found on file: f8ppac001.root
//////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////
// This class has been automatically generated on
// Wed Mar 11 09:58:39 2020 by ROOT version 6.18/04
// from TTree tree/tree
// found on file: f8ppac001.root
//////////////////////////////////////////////////////////

#ifndef tracking_h
#define tracking_h

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

// Header file for the classes stored in the TTree if any.

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

// Fixed size dimensions of array or collections stored in the TTree if any.

   // Declaration of leaf types
   Float_t         PPACF8[5][5];
   Float_t         F8PPACRawData[5][5];
   Int_t           beamTrig;
   Int_t           must2Trig;
   Float_t         targetX,targetY;

     //by user
   Double_t xx[3],xz[3],yy[3],yz[3];//1A,2A,3
   Double_t xx2b[2],yy2b[2],xz2b,yz2b;//2B x,y, 0-measured, 1- fitted.
   Double_t dx[3],dy[3];//residual
   Double_t tx,ty;//target position
   Double_t c2nx,c2ny;//chi2/ndf for xfit,yfit

   // List of branches
   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(TTree *tree);
   virtual void     SetOutBranch(TTree *tree);//by user
   virtual void     TrackInit();//by user
   virtual void     SetTrace(TH2D *h,Double_t k,Double_t b,Int_t min,Int_t max);
   virtual Bool_t   Notify();
   virtual void     Show(Long64_t entry = -1);
};

#endif

#ifdef tracking_cxx
tracking::tracking(TTree *tree) : fChain(0) 
{
// if parameter tree is not specified (or zero), connect the file
// used to generate this class and read the Tree.
   if (tree == 0) {
      TFile *f = (TFile*)gROOT->GetListOfFiles()->FindObject("f8ppac001.root");
      if (!f || !f->IsOpen()) {
         f = new TFile("f8ppac001.root");
      }
      f->GetObject("tree",tree);

   }
   Init(tree);
}

tracking::~tracking()
{
   if (!fChain) return;
   delete fChain->GetCurrentFile();
}

Int_t tracking::GetEntry(Long64_t entry)
{
// Read contents of entry.
   if (!fChain) return 0;
   return fChain->GetEntry(entry);
}
Long64_t tracking::LoadTree(Long64_t entry)
{
// Set the environment to read one entry
   if (!fChain) return -5;
   Long64_t centry = fChain->LoadTree(entry);
   if (centry < 0) return centry;
   if (fChain->GetTreeNumber() != fCurrent) {
      fCurrent = fChain->GetTreeNumber();
      Notify();
   }
   return centry;
}

void tracking::Init(TTree *tree)
{
   // The Init() function is called when the selector needs to initialize
   // a new tree or chain. Typically here the branch addresses and branch
   // pointers of the tree will be set.
   // It is normally not necessary to make changes to the generated
   // code, but the routine can be extended by the user if needed.
   // Init() will be called many times when running on PROOF
   // (once per file to be processed).

   // Set branch addresses and branch pointers
   if (!tree) return;
   fChain = tree;
   fCurrent = -1;
   fChain->SetMakeClass(1);

   fChain->SetBranchAddress("PPACF8", PPACF8, &b_PPACF8);
   fChain->SetBranchAddress("F8PPACRawData",  F8PPACRawData, &b_F8PPACRawData);
   fChain->SetBranchAddress("beamTrig", &beamTrig, &b_beamTrig);
   fChain->SetBranchAddress("must2Trig", &must2Trig, &b_must2Trig);
   fChain->SetBranchAddress("targetX",&targetX,&b_targetX);
   fChain->SetBranchAddress("targetY",&targetY,&b_targetY);
   Notify();
}

Bool_t tracking::Notify()
{
   // The Notify() function is called when a new file is opened. This
   // can be either for a new TTree in a TChain or when when a new TTree
   // is started when using PROOF. It is normally not necessary to make changes
   // to the generated code, but the routine can be extended by the
   // user if needed. The return value is currently not used.

   return kTRUE;
}

void tracking::Show(Long64_t entry)
{
// Print contents of entry.
// If entry is not specified, print current entry
   if (!fChain) return;
   fChain->Show(entry);
}
Int_t tracking::Cut(Long64_t entry)
{
// This function may be called from Loop.
// returns  1 if entry is accepted.
// returns -1 otherwise.
   return 1;
}
#endif // #ifdef tracking_cxx

源文件(.C): 主要写实现头文件中已经声明的那些函数的具体代码

  • 在原MakeClass生成的tracking.C的代码的基础上进行修改。需要注意的是,开头必须#include一下实现的头文件,以及要用到的头文件。

修改tracking.C目录

#define tracking_cxx
#include <TH2.h>
#include <TStyle.h>
#include <TCanvas.h>
#include <TF1.h>
#include <TFitResult.h>
#include <TGraph.h>
#include "tracking.h"
using namespace std;

void tracking::SetBranch(TTree *tree)
{
  //measured pos
  tree->Branch("xx",&xx,"xx[3]/D");//1A,2A,3
  tree->Branch("xz",&xz,"xz[3]/D");
  tree->Branch("yy",&yy,"yy[3]/D");
  tree->Branch("yz",&yz,"yz[3]/D");

  //difference between measured and calculated -for pos resolution.
  tree->Branch("dx",&dx,"dx[3]/D");
  tree->Branch("dy",&dy,"dy[3]/D");

  //for efficiency calculation -2B
  tree->Branch("p2bx",&p2bx,"p2bx/D");
  tree->Branch("p2by",&p2by,"p2by/D");

  //target x-y
  tree->Branch("tx",&tx,"tx/D");
  tree->Branch("ty",&ty,"ty/D");

  //ch2/ndf for linear fitting.
  tree->Branch("c2nx",&c2nx,"c2nx/D");
  tree->Branch("c2ny",&c2ny,"c2ny/D");
}

void tracking::TrackInit()
{
  tx=-999;
  ty=-999;

  //1A
  xx[0]=PPACF8[0][0];
  yy[0]=PPACF8[0][1];
  xz[0]=PPACF8[0][2];
  yz[0]=PPACF8[0][3];

  //2A
  xx[1]=PPACF8[2][0];
  yy[1]=PPACF8[2][1];
  xz[1]=PPACF8[2][2];
  yz[1]=PPACF8[2][3];

  //3
  xx[2]=PPACF8[4][0];
  yy[2]=PPACF8[4][1];
  xz[2]=PPACF8[4][2];
  yz[2]=PPACF8[4][3];

  //2B
  p2bx=PPACF8[3][0];
  p2by=PPACF8[3][1];

}

void tracking::SetTrace(TH2D *h,Double_t k,Double_t b,Int_t min,Int_t max){
    if(h==0) return;
    if(min>=max) return;

    for(int i=min;i<max;i++){
        h->Fill(i,(Int_t)(i*k+b));
    }
}


void tracking::Loop(TTree *tree)
{
   TH2D *htf8x=new TH2D("htf8x","x trace by ppac",2200,-2000,200,300,-150,150);
   TH2D *htf8y=new TH2D("htf8y","y trace by ppac",2200,-2000,200,300,-150,150);
   TH2D *htar=new TH2D("htar","distribution on target",100,-50,50,100,-50,50);

   SetBranch(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;

      TrackInit();
      bool b1a=xx[0]>-999 && yy[0]>-999;
      bool b2a=xx[1]>-999 && yy[1]>-999;
      bool b3=xx[2]>-999 && yy[2]>-999;
      if(!b1a || !b2a || !b3) continue;

      //fit x-z trajectory
      TFitResultPtr r;
      TGraph *grx=new TGraph(3,xz,xx);
      TF1 *fx=new TF1("fx","pol1",-2000,0);
      r=grx->Fit(fx,"SQ");
      tx=fx->Eval(0);
      SetTrace(htf8x,fx->GetParameter(1),fx->GetParameter(0),-1800,0);
      for(int i=0;i<3;i++) dx[i]=xx[i]-fx->Eval(xz[i]);
      c2nx=r->Chi2()/r->Ndf();
      delete grx;
      delete fx;

      //fit y-z trajectory      
      TGraph *gry=new TGraph(3,yz,yy);
      TF1 *fy=new TF1("fy","pol1",-2000,0);
      r=gry->Fit(fy,"SQ");      
      ty=fy->Eval(0);
      SetTrace(htf8y,fy->GetParameter(1),fy->GetParameter(0),-1800,0);
      for(int i=0;i<3;i++) dy[i]=yy[i]-fy->Eval(yz[i]);
      c2ny=r->Chi2()/r->Ndf();
      delete gry;
      delete fy;

      htar->Fill(tx,ty);

      tree->Fill();
      if(jentry%10000==0) cout<<"processing "<<jentry<<endl;

   }
   htf8x->Write();
   htf8y->Write();
   htar->Write();
   tree->Write();
}

Makefile: 告诉make命令如何编译和链接程序

参考链接:跟我一起写Makefile:https://blog.csdn.net/xiaoshuai537/article/details/79340153

下面的Makefile中ROOTCFLAGS,ROOTLIBS,ROOTGLIBS是编译和链接ROOT环境的必要语句。

OBJ = 程序编译后的名称

#############################################################################

OBJ = tracking

MainFile = main.cpp

###############################################################################

SourceFile := $(wildcard $(shell pwd)/src/*.c $(shell pwd)/src/*.cc $(shell pwd)/src/*.C $(shell pwd)/src/*.cpp $(shell pwd)/src/*.cxx)

IncludeFile := $(wildcard $(shell pwd)/include/*.h $(shell pwd)/include/*.hh $(shell pwd)/include/*.hpp)

###############################################################################

ROOTCFLAGS  = $(shell root-config --cflags)
ROOTLIBS    = $(shell root-config --libs)
ROOTGLIBS = $(shell root-config --glibs)

GXX = g++ 
# -Wl ,--no-as-needed
DIR_INC = -I$(ROOTSYS)/include  -I$(shell pwd)/include
CFLAGS = -Wall -O2 $(DIR_INC) -I$(ROOTSYS)/include  $(ROOTLIBS) -lSpectrum -lXMLParser  -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64

###############################################################################

all:$(OBJ)
$(OBJ): $(MainFile) $(SourceFile)
    $(GXX) $(CFLAGS) $(ROOTCFLAGS) $(ROOTLIBS) $(ROOTGLIBS) -o $@ $(MainFile) $(SourceFile)
    @echo "=============================================================="
    @echo "$@ done !"
    @echo "=============================================================="
clean:
    rm -f *.o *.d $(OBJ)

使用方法

make clean

make

  • 与在ROOT环境内直接用.x myClass.C运行时不同(ROOT自动载入内部的库),用make编译时会遇到很多Errors和Warnings信息。

出现的主要错误:

  • 在.h,.C文件的开头没有include 代码中使用的ROOT内函数相应的.h文件时,编译报错。如程序内使用了TGraph,则需要在头部加入
#include <TGraph.h>
  • c++或ROOT:include 头文件用尖括号<>:
#include <iostream>
#include <TGraph.h>
  • 用户编写的的头文件用双引号”“:如
#include "mylib.h"
  • 直接使用cout,或cin等在std的namespace上定义的函数也会出错,使用std::cout, 或在头部加入 using namespace std;
  • C++编译器编译报错给出一堆错误信息时,一般只修正出现的第一个错误信息,然后再次编译。按上述步骤依次进行修改。
In [1]:
!jupyter nbconvert exercise2.3 --to html
[NbConvertApp] Converting notebook exercise2.3.ipynb to html

[NbConvertApp] Writing 350464 bytes to exercise2.3.html