物理のベンチ by mitta

学んだことを発信します。備忘録も書きます。間違いがあればコメントください。

【超初心者向け】C++とPyROOTの2方法でテキストデータをrootファイルに変換する

今回やること

C++Pythonでテキストデータをrootファイルに変換する。

方法

今回はC++を使ったオーソドックスな方法とPyROOTを使った方法を紹介する。
ちなみに完成系はこのような形になる。
f:id:salamence_andrias:20210718022321p:plain

C++を使った方法

筆者はCentOS8を使用。真似したい場合は以下の記事を参照。
phbe.hateblo.jp
CERN ROOTを入れいない人は以下の記事を参照。
phbe.hateblo.jp

作成の流れ
  1. データの準備
  2. makefileの作成
  3. コードの作成
  4. コンパイル、実行
データの準備

今回は以下の記事で作ったテキストデータをそのまま使用する。
phbe.hateblo.jp

makefileの作成

C++で作ったコードは一度コンパイルしてから実行する。
コンパイルを楽にするおまじない的なものがmakefileだ。
ファイルの名前もそのままmakefileにして作成する。
よくわからない人は丸写しを推奨する。

TARGET = mkNT

SRCS = $(TARGET).cc OBJS = $(TARGET).o

ROOTFLAGS = $(shell root-config --cflags) ROOTLIBS = $(shell
root-config --libs) CXXFLAGS = -Wall -O2 $(ROOTFLAGS) CXXLIBS =
$(ROOTLIBS) CC = g++

all: $(TARGET)

$(TARGET): $(OBJS) $(CC) $(CXXLIBS) $(OBJS) -o $@
	$(TARGET).o:$(TARGET).cc $(CC) $(CXXFLAGS) -c $< clean: rm -f
	$(TARGET) $(OBJS)

1行目の「mkNT」というのが今回作りたいコードの名前である。
「mkNT.cc」という名前のC++コードをコンパイルできるようになった。
もちろん別の名前のコードをコンパイルしたい場合は「mkNT」の部分をその名前に変えて実行する。
複数のコードを同時にコンパイルする方法もあるが、今回は触れない。


コードの作成

コード作成前に今回のデータを整理する。
今回のデータは以下のように書かれている

イベント番号 ADC値 TDC値1~3 ランダム数1~6
整数 整数 整数 小数

このうちTDC値を1次元配列に、ランダム数を2次元配列にしてみる

#include "TApplication.h"
#include <math.h>
#include <TROOT.h>
#include <TNtuple.h>
#include <TStyle.h>
#include <TFile.h>
#include <fstream>
#include <iostream>

using namespace std;
const char kNameInFile[20] = "output.dat";
const char kNameOutFile[20] = "output.root";


int main(){
  cout << "Input File  = " << kNameInFile << endl;
  cout << "Output File = " << kNameOutFile << endl;

  // define value
  Int_t ieve;
  Int_t adc;
  Int_t tdc[3];
  Double_t rand[2][3];

  //open root
  gROOT->SetStyle("Plain");
  gStyle->SetOptStat("neiuoMR");
  gStyle->SetPalette(1);
  TTree *tree = new TTree("raw", "raw tree");
  tree->Branch("ieve", &ieve, "ieve/I");
  tree->Branch("adc", &adc, "adc/I");
  tree->Branch("tdc",tdc, "tdc[3]/I");
  tree->Branch("rand",rand, "rand[2][3]/D");

  // open dat file
  ifstream indata(kNameInFile);
  
  // file exist check
  if (indata.fail()) {
    cout << "### program err ###" << endl;
    std::cerr << "!!!Failed to open file!!! \n!!!Not found this file!!!"
	      << std::endl;
    cout << "### program close ###" << endl;
        return -1;
    }
  
  // get data & write
  while (indata >>
	 ieve >> adc >> tdc[0] >> tdc[1] >> tdc[2] >>
	 rand[0][0]>> rand[0][1]>> rand[0][2]>>
	 rand[1][0]>> rand[1][1]>> rand[1][2] ) {
    tree->Fill();
     if (ieve%1000 == 0){
      cout << "event_loop = "<< ieve << endl;}
  }
  cout << "event num = " << ieve+1 << endl;
  indata.close();

  // write rootfile
  TFile *fout = new TFile(kNameOutFile, "recreate");
  tree->Write();   
  fout->Close();

  // end
  cout << "F!!!!!!!!!!!!!!!!!!!!!!" << endl;
  return 0;
}


コンパイル、実行

C++のコードが書けたら、terminalからコンパイル

[***@localhost **] $ make

エラーとかが出なければコンパイル完了なのでそのまま実行。

[***@localhost **] $ ./mkNT
Input File  = output.dat
Output File = output.root
event_loop = 0
event_loop = 1000
event_loop = 2000
event_loop = 3000
event_loop = 4000
event_loop = 5000
event_loop = 6000
event_loop = 7000
event_loop = 8000
event_loop = 9000
event num = 10000
F!!!!!!!!!!!!!!!!!!!

このような出力になれば成功。




PyROOTを使った方法

PyROOTはC++で書いたものよりも浸透していないので記事が少ない。
特に同じことをするためのコードを比較するような記事が少ないので是非確認してほしい。

作成の流れ
  1. データの準備
  2. コードの作成
  3. 実行
データ準備

C++のやり方と同じ方法で行う

コードの作成

今回は「mkNT.py」という名前で作成する。

from ROOT import TTree , std , TH1F, TFile
import numpy as np

kNameInFile = "output.dat"
kNameOutFile = "output.root"

print("Input File  = "+kNameInFile)
print("Output File = "+kNameOutFile)

# define value
ieve = np.empty((1), dtype="int32")  # この書き方でないとバグる
adc = np.empty((1), dtype="int32") # この書き方でないとバグる
tdc = np.zeros((3,), dtype="int32") # この書き方でないとバグる
rand = np.zeros((2,3), dtype="float") # この書き方でないとバグる

# open root
rawtree = TTree("raw","raw_tree")
rawtree.Branch('ieve',ieve,'ieve/I')
rawtree.Branch('adc',adc,'adc/I')
rawtree.Branch('tdc',tdc,'tdc[3]/I')
rawtree.Branch('rand',rand,'rand[2][3]/D')

# open dat file
fin = open(kNameInFile,"r")

# get data & wtite
for line in fin:
    tmp = line.split()
    if len(tmp)==0:
        continue
    ieve[0] = int(tmp[0])
    adc[0] = int(tmp[1])
    tdc[0] = int(tmp[2])
    tdc[1] = int(tmp[3])
    tdc[2] = int(tmp[4])
    rand[0][0] = float(tmp[5])
    rand[0][1] = float(tmp[6])
    rand[0][2] = float(tmp[7])
    rand[1][0] = float(tmp[8])
    rand[1][1] = float(tmp[9])
    rand[1][2] = float(tmp[10])
    rawtree.Fill()

    if ieve%1000 == 0:
        print("event_loop = "+str(ieve))
print("event num = "+str(ieve+1))
fin.close()

# write rootfile
fout = TFile(kNameOutFile,"recreate")
rawtree.Write()
fout.Close()
# end
print("F!!!!!!!!")

実行

Pythonの場合は簡単で、terminal上で以下のコマンドを入力するだけである。

[***@localhost **] $ python3 mkNT.py

Input File  = output.dat
Output File = output.root
event_loop = [0]
event_loop = [1500]
event_loop = [3000]
event_loop = [4500]
event_loop = [6000]
event_loop = [7500]
event_loop = [9000]
event num = [10000]
F!!!!!!!!

このような出力になれば成功。


確認

後は好きなように完成したrootファイルを確認すればよい。

[***@localhost **] $ root output.root
root [] .ls
root [] raw->Print()
root [] raw->Draw("neve")
root [] raw->Draw("adc")
root [] raw->Draw("tdc[0]")
root [] raw->Draw("rand[0][0]")
root [] raw->Draw("adc:tdc[0]")
root [] raw->Draw("adc:tdc[0]-tdc[1]-tdc[2]")

まとめ

C++ :記事が多いため、ROOTそのものを扱いやすい。
Python:PyROOTの記事は少ないが、Pythonなのでファイルを扱いやすいなどの研究活動にはおすすめ。