UP | HOME

心蛛的 ROOT 笔记

Table of Contents

基本信息

Debian 下的 ROOT

Debian 7 (Wheezy):

ROOT 文档:

PyROOT

PyROOT 的基本使用

注意: ROOT 6.0 以后将放弃使用 CINT, 而采用基于 LLVM 的 CLING, 因此,与 CINT 相关的内容只限于 ROOT 5 及以前的版本。

在 Python 中调用 ROOT 类

在调用之前,需要加载 ROOT 包中的模块。示例:

from ROOT import TCanvas # available at startup
                                c=TCanvas()

                                from ROOT import TLorentzVector  # triggers auto-load of libPhysics
                                l = TLorentzVector()
                                

或者 (不推荐) 这样用:

from ROOT import *
                                c = TCanvas()
                                l = TLorentzVector()
                                

最佳用法:

import ROOT

                                c = ROOT.TCanvas()
                                l = ROOT.TLorentzVector()
                                

使用 STL 类

不推荐在 Python 中使用 STL 类,因为 Python 中有相似的类存在,且功能之间有差异,易出错,在使用中需特别小心。

使用模板类的方法很简单,将通用模板当成 meta class, 使用使用相应的参数实现这个 meta class 即可,然后将实现的 (instantiated) 类当成普通的新类使用。示例:

                                >>> from ROOT import std
                                >>> v = std.vector(int)()
                                >>> for i in range(0,10):
                                ...    v.push_back(i)
                                ...
                                >>> for i in v:
                                ...     print i,
                                1 2 3 4 5 6 7 8 9
                                >>>
                                >>> list(v)
                                [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
                                >>>
                            

STL 的模板参数使用复杂类,比如 TCanvas 等,会出错。最好使用简单的数据类型,比如 int, double 等。

访问 ROOT 全局变量

大部分全局变量可以直接从 ROOT.py 中导入,但一些通用模块 (通常如 gMinuit 等) 在程序开始时变未加载,只在相应的模块后续载入时才会存在 (也就是说,通过自动加载机制)。例如:

                                >>> from ROOT import *
                                >>> gROOT                              # directly available
                                <ROOT.TROOT object at 0x399c30>
                                >>> gMinuit                            # library not yet loaded: not available
                                Traceback (most recent call last):
                                File "<stdin>", line 1, in ?
                                NameError: name 'gMinuit' is not defined
                                >>> TMinuit                            # use of TMinuit class forces auto-loading
                                <class '__main__.TMinuit'>
                                >>> gMinuit                            # now gMinuit is available
                                <__main__.TMinuit object at 0x1458c70>
                                >>> not not gMinuit                    # but it is the null pointer, until set
                                False
                                >>> g = TMinuit()
                                >>> not not gMinuit
                                True
                            

可交互式地创建全局变量,也可以通过执行 CINT 宏,或者调用 gROOT.ProcessLine(). 全局变量的使用与类相似:直接通过 from ROOT import *~加载,或者从 ROOT 名称空间 (namespace) 中通过 ~import ROOT 获得。

5.08 版本以后, ROOT 全局变量的表现与 python 全局变量相同,这有时会有点反直观。由于它们是引用 (references), 它们只可以直接通过包含它们的模块进行改变。如下示例进行了展示:

                                >>> from ROOT import *
                                >>> print gDebug
                                0
                                >>> gROOT.ProcessLine( 'gDebug = 7;' )
                                >>> print gDebug
                                0                                 # local gDebug is unchanged
                                >>> gDebug = 5                    # changes _local_ reference only
                                >>> print gDebug
                                5                                 # locally correct, but ...
                                >>> gROOT.ProcessLine( 'cout << gDebug << endl;' )
                                7                                 # ... ROOT global unchanged
                                >>> import ROOT
                                >>> print ROOT.gDebug
                                7                                 # still the old value (not '5')
                                >>> ROOT.gDebug = 3               # changes ROOT module reference
                                >>> gROOT.ProcessLine( 'cout << gDebug << endl;' )
                                3                                 # ROOT global properly changed
                                >>>
                            

上例同样说明 import ROOTfrom ROOT import * 要更好些。

从 C++ (CINT) 中访问 Python

访问 Python 可通过 TPython 类,或者当 Python 对象或类在作用域内时直接使用。 TPython 类大致如下:

class TPython {
                                public:
                                // load a Python script as if it were a macro
                                static void LoadMacro(const char* name);
                                // execute a Python statement (e.g. "import ROOT")
                                static void Exec(const char* cmd);
                                // evaluate a Python expression (e.g. "1+1")
                                static const TPyReturn& Eval(const char* expr);
                                // bind a ROOT object with, at the Python side, the name "label"
                                static bool Bind(TObject* obj,const char* label);
                                // enter an interactive Python session (exit with ^D)
                                static void Prompt();
                                };

                                

TPython 的成员函数的详细解释:

  • LoadMacro(const char* name) – 参数为 Python 文件名,载入后其中的新类自动可被 CINT 访问。由于 non-selective, 小心使用!
  • ExecScript(const char* name, int argc=0, const char** argv=0) – 参数为 Python 文件名,在私用的名字空间内调用以避免边界效应 (side-effects)。另外,可以通过添加命令行形式的参数,与在脚本中使用 sys.argv 参数相似。
  • Exec(const char* cmd) – 参数为可执行的 Python 代码。没有返回值,但如果有问题(比如语法错误)会输出错误信息。
  • Eval(const char* expr) – 参数为可执行的 Python 代码字符串 (表达式). 如果是内嵌类型 (int, long, float, double, const char*), 可访问的 Python 类型或者 ROOT 类型,则返回表达式执行的结果。如果返回的是 ROOT 类型,需要对 void* 进行显式的强制转换 (explicit cast), 再赋值给本地指针 (可能存在类型不匹配), 而内嵌类型是自动转换 (如果可能) 成本地变量所需要的类型。
  • Bind(TObject* obj, const char* label) – 将 CINT 中的 ROOT 对象转换成 Python 解析器可接收的,在 Python 中变量名为 label
  • Prompt() – 切换到 Python 提示符。

ROOT v4.00/06 版本及以后, TPython 类在使用时自动加载,此前版本则需要在使用前先通过 gSystem->Load() 加载 libPyROOT.so 模块。

使用一例: Python 模块如下

print 'creating class MyPyClass ... '
                                class MyPyClass:
                                def __init__(self):
                                print 'in MyPyClass.__init__'
                                self._browser = None
                                def gime(self,what):
                                return what
                                

在 CINT session 中这样调用:

                                root[] TPython::LoadMacro("MyPyClass.py");
                                creating class MyPyClass ...
                                root[] MyPyClass m;
                                in MyPyClass.__init__
                                root[] char* s = m.gime("aap");
                                root[] s
                                (char* 0x41ee7754)"aap"
                            

注意, LoadMacro() 调用使用类自动可用。否则需要先调用 gROOT->GetClass()

回调 (callbacks)

从 CINT 中对 Python 进行回调,简单的方法是执行字符串。参见 tutorials/pyroot/demo.py 示例。

CINT commands

在交互模式下, 使用 Python exception hook 来模拟一些 CINT 命令。这些命令有

                                .q, .!, .x, .L, .cd, .ls, .pwd, .?, .help

                            

注意, .x 命令对应的是 Python 的 execfile(), 因此只接收 Python 文件,而不是 CINT 宏。

内存管理

在 Python 解析器中对用户内存通过引用计数和垃圾回收 (对新类型对象,包括~PyROOT~ 对象) 进行管理。在 C++ 中,内存管理是手动进行的,或者通过应用程序特定的、定制的机制 (如 ROOT 中所使用的) 进行。尽管 PyROOT 理解 ROOT 的内存管理机制,仍然会有一些边界条件需要手动进行处理。 并且 PyROOT 对内存管理所采用的试探法 (heuristics) 并非绝对可靠。对方法的一些详细了解因此变得重要。

自动内存管理

有两种全局策略可用:试探式 (heuristics) 和严格式 (strict)。

默认使用的是试探式,它采用如下两种规则:

  1. 在 Python 解析器中创建的 ROOT 对象由 Python 拥有,当最后一次的 Python 引用消除后会被删除。然而,如果,该对象由非常量地址作为参数传递给 C++ 函数 (除非该函数象成员函数中 self 那样使用它), 则放弃其所有权。
  2. 来自 ROOT 调用的 ROOT 对象并非 Python 拥有,但在它传递给 Python 解析器之前,如果该类型是 TObject 类的派生类,它的 must cleanup 位必须置位。当对象离开 C++ 作用范围时, Python 对象必须改变类型,通常是 None。

严格式策略与试探式不同,当对象作为参数传递给函数时, Python 从不放弃对象的所有权。这样则需要由开发者来阻止双边删除 (double deletes) 的发生。

可通过如下命令

                                ROOT.SetMemoryPolicy( ROOT.kMemoryStrict )

                            

选择严格式策略,或通过如下选择试探式

                                ROOT.SetMemoryPolicy( ROOT.kMemoryHeuristics )

                            

对于图象对象需要特别注意: 当在当前 pad 上绘图时,对图象的引用保留着,但 PyROOT 却不会立即知道,这需要开发者保留着对象甚至某个 Python 引用激活。参见 $ROOTSYS/tutorials/pyroot/zdemo.py 中的示例。

另外,也可以直接告诉 Python 放弃对某个独立实现的拥有权:

                                o = ROOT.TObject()
                                ROOT.SetOwnership( o, False ) # True to own, False to release

                            

手动内存管理

如果需要,可通过相关联的 TClass 显示式摧毁一个你拥有的 ROOT 对象:

                                myobject.IsA().Destructor(myobject)

                            

该命令向系统发送一个删除通知 (因此你不用再关心此时 Python 的引用计数之类的,对象会被删除,即使它的引用计数可能不会零), 并且释放相应内存。

性能

使用 Python 函数

使用 Python 函数绘图

from ROOT import TF1, TCanvas

                                class Linear:
                                def __call__( self, x, par ):
                                return par[0] + x[0]*par[1]

                                # create a linear function with offset 5, and pitch 2
                                f = TF1('pyf2',Linear(),-1.,1.,2)
                                f.SetParameters(5.,2.)

                                # plot the function
                                c = TCanvas()
                                f.Draw()
                                

拟合谱

from ROOT import TF1, TH1F, TCanvas

                                class Linear:
                                def __call__( self, x, par ):
                                return par[0] + x[0]*par[1]

                                # create a linear function for fitting
                                f = TF1('pyf3',Linear(),-1.,1.,2)

                                # create and fill a histogram
                                h = TH1F('h','test',100,-1.,1.)
                                f2 = TF1('cf2','6.+x*4.5',-1.,1.)
                                h.FillRandom('cf2',10000)

                                # fit the histo with the python 'linear' function
                                h.Fit(f)

                                # print results
                                par = f.GetParameters()
                                print 'fit results: const =',par[0],',pitch =',par[1]
                                

使用 Tree

访问现有的 Tree

from ROOT import TFile
                                from ROOT import gDirectory

                                # open the file
                                myfile = TFile('tree1.root')

                                # retrieve the ntuple of interest
                                mychain = gDirectory.Get('t1')
                                entries = mychain.GetEntriesFast()

                                for jentry in xrange(entries):
                                # get the next tree in the chain and verify
                                ientry = mychain.LoadTree(jentry)
                                if ientry < 0:
                                break

                                # copy next entry into memory and verify
                                nb = mychain.GetEntry(jentry)
                                if nb<=0:
                                continue
                                # use the values directly from the tree
                                nEvent = int(mychain.ev)
                                if nEvent<0:
                                continue

                                print mychain.pz, '=', mychain.px*mychain.px, '+', mychain.py*mychain.py
                                

写入 Tree

写入 TTree 在 Python 中有点复杂,不仅因为你需要 C++ 类以保证数据成员可以映射过去,除非你只使用内嵌类型。

from ROOT import TFile, TTree
                                from array import array

                                h = TH1F('h1','test',100,-10.,10.)
                                f = TFile('test.root','recreate')
                                t = TTree('t1','tree with histos')
                                maxn = 10
                                n = array('i',[0])
                                d = array('f',maxn*[0.])
                                t.Branch('mynum',n,'mynum/I')
                                t.Branch('myval',d,'myval[mynum]/F')

                                for i in range(25):
                                n[0] = min(i,maxn)
                                for j in range(n[0]):
                                d[j] = i*0.1+j
                                t.Fill()

                                f.Write()
                                f.Close()
                                

使用自己的类

我的 FAQ

88x31.png

版权所有 ©2012-2018: Vivodo Lio | 日期:

Generated by Emacs 25.3.1 (Org mode 9.1.7), Validate