ユーザ用ツール

サイト用ツール


サイドバー

Menu

Latest

study:software:root:io

I/O

I/O関係のクラスが一番rootを特徴付けている。

TFile

  • rootでは解析を行う対象のデータや、その解析結果を記録したヒストグラムなどのrootのオブジェクトをひとまとめにして、.rootファイル形式で保存することができる
  • root上で、このrootファイルを開いたり、保存するときに使うのがTFileクラス
  • rootはunixのようなディレクトリ構造を仮想的に持っていて、TFileはrootの世界でのディレクトリにあたる
  • rootのクラスはほとんどTObjectというクラスを継承していて、どのクラスも同様の方法でrootファイルに保存することができる

rootファイルを読み込む

rootで得られたヒストグラムなどのデータは拡張子.rootのファイルに記録されているので、読み込んで情報を引き出す必要がある。

rootはインタープリター上で実行する方法と、マクロを読みこませる二通りの方法があるが、その二種類の方法を書く。

インタープリター

下のようにすると、rootファイルを読み込むことが出来る。

$ root hoge.root

これはファイルを読み込み専用で開いていることになる。

コマンドラインで上カーソルボタンを押して履歴を表示すると、次のコマンドが残っているのが確認できる。

root [] TFile *_file0 = TFile::Open("hoge.root") //自動的に_file0というポインタが生成されている

つまりrootファイルを開くという行為は、rootでTFile型のオブジェクトを作っていることにあたる。macroで.root fileを読み込みたい場合は、このようにして自分でTFileを生成する必要がある。

ファイルのもつオブジェクトは.lsコマンドで確認できる。この場合はTH1F型のhistというオブジェクトが保存されている。

$ root hoge.root
root [0]
Attaching file hoge.root as _file0...
root [1] .ls
TFile**		hoge.root
 TFile*		hoge.root
  KEY: TH1F	hist;1	hist

KEYというのは、TKeyというクラスでオブジェクトのインデックス情報を格納するクラスである。

実はファイルを読み込んだ時点ではオブジェクトはメモリに呼ばれない。実際に使用する段階で、自動的にTKeyの情報を使ってオブジェクトの実体がメモリに呼ばれる。

この仕組みによって、ユーザは巨大なrootファイルのうち、実際にアクセスしたいデータ一つに効率良くアクセスできる。

下のようにhistというオブジェクトがあれば、インタープリター上でそのメソッドを使用することができる。

root [2] hist->Draw(); //オブジェクトのメソッドを使用した

fileを読み込んだ時点ではオブジェクトのアドレスをいれたポインタは存在しないので(それどころかオブジェクトはメモリに存在していない!)、通常はこれにアクセスはできないが、cintならば上のようにオブジェクト名をポインタのように使ってアクセスすることができる。

もう一度.lsをしてみよう。

root [3] .ls
TFile**		hoge.root
 TFile*		hoge.root
  OBJ: TH1F	hist	hist : 0 at: 0x7f92db4bb1b0
  KEY: TH1F	hist;1	hist

前述したように使用するまでは、オブジェクトはメモリに呼ばれないが、DrawしたことでOBJのhistが追加されていることが確認できる。これはメモリにオブジェクトが呼ばれていることを示す。

マクロ

インタープリターで出来るのはせいぜいrootファイルの中身を確認するぐらいで、本格的な解析はマクロを使う。下のようにマクロを作っておいて、それを読みこむのが普通だと思う。

$ root macro.cxx

マクロでrootファイルを読み込み見たい場合は、マクロの中でrootファイルを読み込むコードを書いておく必要がある。

しかし、ファイルを読み込んだだけではファイルに保存されたオブジェクトのアドレスを格納したポインタがないので、どうにかする必要がある。

サンプルコードだけを下に書いておくので、詳細は次からの項目に譲る。

#include <TFile.h>
#include <TH1.h>
//cintで実行するならincludeは必要ない

void macro(){  //マクロ名と関数名は同じにする

    TFile *file = new TFile("hoge.root");
    TH1F *h1 = (TH1F*)file->Get("hist");
    h1->Draw();
    
}

ちなみに上のマクロはCINTで読み込むだけであれば

{

    TFile::Open("hoge.root");
    hist->Draw();
    
}

などと簡潔に書くことができるが、次からの説明はCINTの機能を使わずに正確にc++の文法に従って書く。

TFileオブジェクトを作る

下のようなオプションが選べる。

TFIle *file = new TFile("hoge.root","オプション"); //

READ デフォルト値なので省略可能。読み込み専用でファイルを開く
UPDATE 書き込み可能で開く。ファイルが存在しない場合は作成
NEW/CREATE 新規ファイルを開く。すでにファイルが存在していた場合は開かない
RECREATE 新規ファイルを開く。すでにファイルが存在していた場合、上書き

オブジェクトを探す、使用する

rootファイルに保存されているオブジェクトは使用するためには、そのオブジェクトのアドレスを格納したポインタを生成させる必要がある。

ここにはオブジェクトのアドレスを返す方法を書く。

TFileから呼び出す

カレントディレクトリにあるオブジェクトは下のいずれかの方法でアドレスを返すことが出来る。

file->Get("name");
file->GetList()->FindObject("name");
gDirectory->Get("name");//gDirectoryはカレントディレクトリへのポインタ

ここでfileはTFile型のポインタ、nameは呼び出したいオブジェクトの名前である。


実際的にはオブジェクトのメソッドにアクセスしたいので、

TH1F* h = (TH1F*)file->Get("hist");//Getで得られるのはTObject型なのでキャストしている。
h->Draw();

のようにポインタを作ってからアクセスするか、又は

((TH1F*)file->Get("hist"))->Draw();

のようにする。

後者の方法ではアクセスコストが高いので(つまり二重にメンバ関数を使用するので)、マクロの中で何度も使用するのはおすすめしない。

gROOTでオブジェクトを見つける

ほとんどのオブジェクトはgROOTによってアクセスすることができる。

gROOTはTDirectoryを継承しているのでrootにおけるのディレクトリの一種である(ただし、ファイルの保存はできない)。 rootファイルを読み込まずにrootを立ち上げた場合のカレントディレクトリはgROOTになる。

gROOTは存在しているTCanvas、TFile、TF1などのリストをもっている。

gROOTのFindObjectはgROOTがもつリスト+カレントディレクトリのもつTListの中からオブジェクトを検索して、アドレスを返すということができる(それぞれの個別のリストを呼び出すこともできる)。

TH1F *h = (TH1F*)gROOT->FindObject("hist");

(マクロをコンパイルするならば)gROOTを使う場合はTROOT.hをincludeすること

同名のオブジェクトを呼び出す

.lsしたときの出力をよく見ると、オブジェクト名に数字がくっついていると思う。通常は;1と記されているはずだが、中には下のように;2や;3が書かれているものもある。

root [] .ls
TFile**		hoge.root
 TFile*		hoge.root
  KEY: TH1F	hist;2 title
  KEY: TH1F	hist;1 title

これは同名のオブジェクトを区別するためのものでnamecycleと呼ばれる。たぶん(私もよくわかってない)。

この場合は下のようにnamecycleをくっつけて呼び出せばいい。

file->Get("hist;1")
file->Get("hist;2")

そもそも同名のオブジェクトは作れないのでは?と疑問がおこるかもしれないが、同名オブジェクトが作れないのはOBJの状態のときだけである。

別の言い方をすると、保存するだけなら同じ名前でもできるが、同じディレクトリに同じ名前のオブジェクトをいれることはできない。

もっと厳密に言うと同じTListに同じ名前のオブジェクトは作れない。例えば、TH1とTCanvasなら同じ名前でも問題ない。

TFileではおそらく同名オブジェクトを保存できるようにnamecycleという仕組みがあるのではないかと思っている。

オーナーシップについて

rootにはTListというオブジェクトのコンテナクラスがあり、TFileはこのクラスをメンバーに持っている。

root fileに保存されたオブジェクトというのは、TFileオブジェクトのメンバー変数のTListにそのオブジェクトが入っている状態である。

rootのオブジェクトは生成されると同時にカレントディレクトリのTListに加わるものもあり、そうでないものもある。

ディレクトリのListに加わる TH1,TTree,TEventList
gROOTが専用のListを持っている TCanvas,TF1,TFile,TColor,TStyle,TBrowser,etc…
どちらにもあてはまらない TGraph,TLegend,TLine,TArrow,TBox,etc,…

.lsでコマンドで一覧できるのはTH1やTTreeのみで、TGraphは表示されないことを経験的に知っていると思うが、それはTGraphなどのオブジェクトはデフォルトではカレントディレクトリに加わらないためである。

Add ・オブジェクトをディレクトリのリストに加える

ディレクトリに属さないオブジェクトでかつ、gROOTのリストにもないようなオブジェクト(例えばTGraph)はポインタを失えば、もはやアクセスできなくなる。

それが困る場合は、下のようにしてオブジェクトをディレクトリのリストに加えておけばいい。

file->Add(obj);
file->Add("name");//オブジェクト名を引数にすることもできる

ちなみにAddしただけでは保存はされない。

Write ・オブジェクトをファイルに書き込む、rootファイルを保存する

rootでは単にオブジェクトを生成させただけではfileには書き込まれない。そのままrootを終了するとオブジェクトは失われる。

詳しく言うと、.lsしたときに

root [23] .ls
TFile**		hoge.root	
 TFile*		hoge.root	
  OBJ: TH1F	hist	title : 0 at: 0x7ffd21d8ddc0

のようにOBJとしか書いてなくて、KEYと書かれていない場合はそのオブジェクトはまたfileに保存されていない。

ファイルを保存したい場合は下のようにすればいい。

//fileはTFile型
file->Write(); //fileにおかれている全てのオブジェクトを保存する

上の場合は、カレントディレクトリにあるオブジェクトをすべてを保存するが、選択的に保存したい場合には下のようにすればいい。

obj->Write(); //カレントディレクトリに書き込む
obj->Write("name"); //"name"というオブジェクト名で保存

上の方法では、そのディレクトリに属していないオブジェクトも保存することができる。

書き込み可能でファイルを開いていないと書き込めない

cd・カレントディレクトリを変える

TFileはTDirectoryというクラスを継承した、rootの空間におけるDirectoryである。

rootファイルを複数読み込んだ場合は、Directoryが複数あるのと同じでカレントディレクトリを意識する必要がある。

カレントディレクトリはTFileを宣言したときに、そこに自動的に移る。

ところで、異なるTFileにおかれたオブジェクトは.lsで見ることができない。

cd()メソッドを使うと、カレントディレクトリを変えることができる。

//hoge1.root(file1),hoge2.root(file2)というファイルを開いている状況で今hoge1.rootにいるとする
root [] .ls
TFile**		hoge1.root
 TFile*		hoge1.root
  KEY: TCanvas	c1;1	c1
root [] //上ではhoge1.rootのオブジェクト一覧が表示された
root [] file2->cd(); //hoge2.rootに移動した
root [] .ls
TFile**		hoge2.root
 TFile*		hoge2.root
  KEY: TH1F	hist;1	hist
root [] //今度はhoge2.rootのオブジェクト一覧が表示される
root [] hist->Draw(); //ヒストグラムが表示される

Delete・ファイルからオブジェクトを削除する

実際にファイルからオブジェクトを削除しなければならない状況はほとんど皆無だと思う

<追記> そんな状況に出会った

下のようにすればできる。

root [] file = new TFile(hoge.root","update") //updateで読み込まないと削除できない
root [] .ls
TFile**		hoge.root	
 TFile*		hoge.root	
  KEY: TTree	name;1	name
  KEY: TTree	name;2	name
root [] file->Delete("name;1"); //nameはオブジェクト名
root [] file->Delete("name;2"); //namecycleが2なら;2をつける
root [] file->Delete("name"); //namecycleを何も付けないと、TOBJだけ消えてTKEYは残る(つまりメモリから消えるだけ)

上の方法で問題なくできるが、こんな方法も考えた。でも、なにか残っている気もするなあ。

TFileからオブジェクトを削除するには、TObjectではなく、TKeyをDeleteする必要がある。

file->FindKey("オブジェクト名")->Delete(); //FindKeyで削除したいKeyを呼び出し、Deleteで削除している

下のようにしても、オブジェクトがメモリから取り除かれるだけで、TKeyはそのままファイルに残ってしまい、ファイルからオブジェクトを消すことはできない。

root [] object->Delete();

つまり、Deleteは基本的にはc++の普通のdeleteを行わせるメソッド。

あるファイルのオブジェクトを別のファイルに保存する

例えばfile1からfile2にTH1F"hist"をコピーする場合。

root [] TFile *fin = new TFile("file1.root");
root [] TFile *fout = new TFile("file2.root","recreate");
root [] TH1F *hist  = ((TH1F*)fin->Get("hist"))->Clone();
root [] hist->Write(); //ここで.lsすると、histのKeyとObjが生成しているはずである。

あるいはCloneを使わずに、これでも出来る。

root [] TFile *fin = new TFile("file1.root");
root [] TFile *fout = new TFile("file2.root","recreate");
root [] fin->Get("hist")->Write();

ここでは(なんだかバグみたいな話だが)file1.rootとfile2.rootで同じhistというオブジェクトを共有していることになる(.lsでアドレスを確認してみるといい)。

実はrootでは2つのディレクトリに同じオブジェクトが所属することが許されている。

もっとも、TFileは内部的にはlistにオブジェクトを加えているだけなので、おかしなことではないのだが。

TFileのDrawとPaintについて・すべてのオブジェクトをDrawしたい

大量のヒストグラムが保存されたrootファイルの一つ一つDrawするのは面倒くさいと思う。

TFileにはDrawメソッドが用意されて、そのファイルに書き込まれているオブジェクトをすべてDrawできる。

が、しかし、このメソッドの対象はObjの状態でTFileに存在するオブジェクトだけ(内部的にはTFile::GetList()で呼び出されているので)。TKeyの状態ではだめ。

つまり一度DrawするなりGetするなりして、メモリにオブジェクトを呼び出していないとDrawできない。一体このメソッドは何に使うのだろうか。

あと、描くCanvasの変更もやり方がわからない。一体どう使うことを想定しているのか。


TFile *file = new TFile("ファイル名");

TCanvas *c1 = new TCanvas("c1","c1");
TKey *key = 0;
const char* str = "画像名.pdf";

c1->Print(Form("%s(",str)); //ページの最初に白紙を追加。

TIter next(file->GetListOfKeys());
while((key = (TKey*)next())){
  key->ReadObj()->Draw();
  c1->Print(str);
  c1->Clear();
}

c1->Print(Form("%s)",str)); //ページの最後に白紙を追加
c1->Close(); //ついでにCanvasを閉じる

例えば上のようなマクロなら全てのオブジェクトをDrawして画像にできる(これだと前後に白紙が入るけど)。

ディレクトリー構造について

rootにはTDirectoryとそれを継承したTFileとTROOT、TDirectoryFileというものがある。さらにTFolderなんてものもある。TFolderについてはTDirectoryと関係はないし、積極的に使う人間が地球にいるかどうか未確認だし、私もよく知らない。

rootはunixに近いディレクトリー構造を持つ。

$ root hoge.root

とした場合のカレントディレクトリはTFile型のhoge.rootである。このことは.pwdコマンドで確認できる。

$ root hoge.root
[ ] .pwd
Current directory: hoge.root:/
Current style:     Modern

関係ないことだけど、ModernというのはCanvasのStyleのこと。デフォルト値は.rootrcに記述されている。

ではfileを開かずに、rootのshellを起動させた場合はどうなるかというと、

$ root
[ ] .pwd
Current directory: Rint:/
Current style:     Modern

Rintとなっている。これはgROOT(TROOTのインスタンスをさしたグローバル変数)のオブジェクト名である。

ちなみに、hoge.rootにも、Rintにも:/と書いてあるので、これらはともにルートディレクトリであることが分かる。つまり、ファイルを読み込んだ場合は並列に2つのディレクトリ、hoge.rootとRint(gROOT)が存在していることになる。

TDirectoryのmkdir()を使えばサブディレクトリも作れる

$ root hoge.root
[ ] _file0->mkdir("sub");
[ ] .ls
TFile**		hoge.root
 TFile*		hoge.root
  TDirectoryFile*		sub	sub
[ ] sub->cd()
[ ] .pwd
Current directory: hoge.root:/sub
Current style:     Modern

サブディレクトリはTDirectoryFile型になるようである。名前がややこしすぎる。

gDirectoryはカレントディレクトリを指すグローバル変数である。

$ root hoge.root
[ ] _file0->mkdir("sub");
[ ] sub->cd()
[ ] gDirectory->cd("..")
[ ] .pwd
Current directory: hoge.root:/
Current style:     Modern

まとめに入ると、

  • TFileは、rootにおけるもっとも基本的なディレクトリで、rootファイルとしてオブジェクトを保存する機能がある
    • TH1やTTreeはTFile以下におかれるオブジェクトになる
  • gROOTは存在できるTROOT型の唯一のポインタで、TFileやTCanvasなどの存在しているほとんどのオブジェクトのリストをもっている
    • 一方でgROOTにはディレクトリとしての側面もあり、rootを普通に立ち上げた場合のカレントディレクトリはgROOTである。そのオブジェクト名はRint
    • gROOTにはROOTのオブジェクトを作ることはできるが、rootファイルとして保存する機能はない
  • gDirectoryはカレントディレクトリを指すポインタである。
  • TDirectoryFileは(TFileの親クラスでもあるが)rootファイルに保存する機能はなく、おそらくサブディレクトリ用のクラスである
  • TDirectoryは単なる抽象クラスだと思う

以上、まったくもって、誰にも役に立たない知識でした。

TObject

rootのだいたいのオブジェクトはTObjectを継承している。

ここには全般的に言えることだけ書いておく。

なぜインスタンス化するときにnewを使うのか

c++の話で、root関係ないです

私はrootを使い始めた頃、c/c++の知識がすかすかでnewが何なのかもあまり理解してなかった。

なので多くのrootの解説書を見ると、下のように

TH1F *hist = new TH1F("hist","title",100,-10,10);

と書いてあって、なぜ

TH1F hist("hist","title",100,-10,10);

としないのかよくわからなかった。

そもそもnew演算子はヒープ領域に動的メモリを確保してインスタンスを生成している。よく言われるメリットは

  • 変数はスコープから抜けると破棄されるが、newでヒープに確保すれば生き残る
  • 派生クラスのインスタンスを親クラスのポインタにいれることができる
  • というかそもそも、ポインタの方がいろいろ扱いやすい
    • 例えば関数の返り値をインスタンスにするよりもアドレスを返すほうがいい


rootの場合はnewを使わない場合にとくに問題になってくるのはインタープリターとmacroのスコープは違うということ。

なので、下のように、macroでスタック領域に作ったオブジェクトは関数終了時点ですべて破棄されてしまう。

$ root macro.cxx
root [0] 
Processing macro.cxx...
 /*
macro.cxxでは
TH1F h1("h","title",100,0,100);
などとしている
*/
root [1] .ls
 /*
何も出ません
*/

つまり、オブジェクトはスタックに作ってしまうと、例えマクロを走らせても解析結果を保持したオブジェクトは消滅してしまう(なので、マクロの中でファイルに保存する必要がある)。

たぶん多くの人はマクロとコマンドラインを行き来しながらマクロを調整したりして解析をするので、これは大きな問題だと思う。


ちなみに話がそれるが、newによってインスタンス化を行なっても、macro終了時にはそのアドレスを格納したポインタ自体は当然失われる。

$ root macro.cxx
root [0] 
Processing macro.cxx...
 /*
TH1F *h1 = new TH1F("h","title",100,0,100);
をしている
*/
root [1] .ls
 OBJ: TH1F	h	title : 0 at: 0x7ffa9437db30
root [2] h1->Draw();
Error: Symbol h1 is not defined in current scope  (tmpfile):1:
Error: Failed to evaluate h1->Draw()
*** Interpreter error recovered ***

上のようにポインタを使ってオブジェクトにアクセスすることはできない。

普通はこの状況はただのメモリリークになると思うけど、rootではオブジェクト名によって再びアドレスを呼び出すことが可能になっている。

SaveAs・オブジェクトを直接rootファイルとして保存する

一つのオブジェクトだけを保存したファイルを作る場合、いちいちTFileを作るのは面倒である。SaveAsを使えば、直接rootファイルを作れる

obj->SaveAs("hoge.root"); //.rootで終わるとそのオブジェクトを保存したrootファイルが生成される
obj->SaveAs("hoge.xml"); //.xmlとして保存
obj->SaveAs("hoge"); //c++のマクロとして保存?よく知らない

SaveAsでrootファイルを作るのは何かとエラーが出てきやすい。マクロのなかではきちんとTFileを作って書き込んだほうが安全

Clone・コピーして、もう一つ同じオブジェクトを作る

Cloneするだけなのだが、引数を指定せずにCloneするとオブジェクト名がかぶるので、自分で名前を変えなくてはならない。

例として、TH1Fをコピーする。

TH1F *h_new = (TH1F*)h->Clone();
h_new->SetName("h_new");
//又は
TH1F *h_new = (TH1F*)h->Clone("h_new");

マクロ:for loopで同じ設定のオブジェクトをたくさん作る

http://heptech.web.fc2.com/root/string.htmlからのコピー。

  • 普通にTH1Fの配列を宣言するだけなんだけど
  • オブジェクト名がかぶるとエラーを吐くので、下のようにTStringのFormを利用して違うオブジェクト名をつける

#include "TString.h"

TH1F *h[10];
for (int i=0;i<10;i++){
    h[i]=new TH1F(Form("h[%d]",i),"",100,0.,100.);
}

TNamed

  • THoge("name","title")のように、オブジェクト名とtitleをもつクラスはTNamedを継承している
  • もちろんTNamed自体もTObjectを継承する

SetName,SetTitle,SetNameTitle,GetName,GetTitle

説明はしない。

study/software/root/io.txt · 最終更新: 2015/08/13 13:36 by kamo

ページ用ツール