ROOT beenden:
.q
+, --, *, /
&, |, <<, >>, ~
&&, ||, !
C++ | FORTRAN | Bytes | Bytes | |
char | CHARACTER*1 | 1 | 1 | |
int | INTEGER | 2/4 | 4 | |
long | 4/8 | 8 | ||
float | REAL*4 | 4 | 4 | |
double | REAL*8 | 8 | 8 |
root [0] pow(5,2) (int)25 root [1] 25.1*3.5 (double)8.78500000000000085e+01 root [2] pow(25.1,3.5) (double)7.92242296929665754e+04 root [3] sin(2) (double)9.09297426825681709e-01 root [4] tan(1) (double)1.55740772465490229e+00 root [5] atan(1) (double)7.85398163397448279e-01 root [6] atan(1)*4 (double)3.14159265358979312e+00 root [7] log(2) (double)6.93147180559945286e-01 root [8] exp(10) (double)2.20264657948067179e+04 root [9] exp(0) (double)1.00000000000000000e+00 root [10] exp(1) (double)2.71828182845904509e+00 root [14] 2<<10 (int)2048 root [15] 3<<10 (int)3072 root [16] sqrt(2) (double)1.41421356237309515e+00 root [17] atan2(1,1) (double)7.85398163397448279e-01 root [18] atan2(1,0) (double)1.57079632679489656e+00
Arbeiten mit ROOT heisst in der Praxis, dass man Objekte der jeweiligen ROOT Klassen erzeugt und dann Methoden dieser Objekte aufruft.
Einfaches Beispiel:
{ // book histo, // arguments: tag, title, N-channels, xlow, x-high TH1F myhist("h1","Gauss Random Numbers",100,-5.,5.); // random generator object TRandom rng; for ( int i = 0; i<100000; i++ ) { double xrnd = rng.Gaus(); // Gaussian distributed Random number myhist.Fill( xrnd ); // Fill random number in histogram } myhist.DrawClone(); // Draw Histogramm }
root [40] double s = 1; root [41] for ( int i=1; i<70; i++ ) s *= i // Fakultaet root [42] s (double) 1.71122452428141297e+98 root [57] TMath::Gamma(70) // Dasselbe mit Gamma Fkt (double) 1.7112245e+98
// file fak.C double fak( Int_t n = 1 ) { double t = 1; for ( int i = 1; i <= n; i++ ) { t *= i; } return(t); } root [63] .x fak.C(99) // direkt ausfuehren 9.33262e+155 root [64] .L fak.C // oder erst Laden root [65] fak(99) // dann aufrufen 9.33262e+155
Zwei Arten von Macros:
TH1F fillH1( int n=100000 ) { TH1F h2("h2","mytitle",100,0,1); for ( int i = 0; i<n; i++ ) { h2.Fill(gRandom->Rndm()); } h2.Draw(); return h2; // return histo }
Optional können Argumente übergeben werden.
Alle Variablen, die in der Funktion angelegt werden, sind nach Ausführung wieder verschwunden (local scope).
{ int n=100000; TH1F h2("h2","mytitle",100,0,1); for ( int i = 0; i<n; i++ ) { h2.Fill(gRandom->Rndm()); } h2.Draw(); }
Äquivalent zur direkten Eingabe dieser Anweisungen auf der Root Kommandozeile, insbesondere bleiben die Variablen erhalten (global scope)!
Datei wird von Cling interpretiert (aber nicht ausgeführt!), es können mehrere Funktionen deklariert werden, Funktionsnamen beliebig, unabhängig von Dateiname.
Die Funktionen sind anschliessend bekannt und können direkt gerufen werden, z.B.
fillH1( 10000 )
Falls es doch Probleme gibt kann es helfen vor dem Neu-Laden von Makros explizit ein
un-load zu machen:
.U myMacro.C
Wenn das auch nicht hilft kann man gROOT->Reset() versuchen oder gleich Beenden mit
.q und Neustart.
.L myMacro.C+ bzw. .L myMacro.C++
Jetzt wird die Datei mit dem Standard C++ Compiler kompiliert und anschliessend eine shared-object library erzeugt und dynamisch geladen ( myMacro_d.so).
#include <TH1F.h> #include <TRandom.h> TH1F fillH1( int n=100000 ) { TH1F h2("h2","mytitle",100,0,1); for ( int i = 0; i<n; i++ ) { h2.Fill(gRandom->Rndm()); } h2.DrawClone(); return h2; // return histo pointer }
Bei dieser Methode müssen alle verwendeten Klassen/Funktionen über die entsprechenden Header Dateien deklariert werden! (Bei ROOT/Cling sind Standard header für Histogramme, Funktionen, etc. schon bekannt).
Jetzt gilt üblicher C++ Standard. Der kompilierte Code hat sehr gute Performance.
root [60] TF1 f1("f1","sin(x)", 0, 10 ); root [61] f1.Draw(); root [68] TF1 f2("f2","cos(x)", 0, 10 ); root [69] f2.Draw(); root [70] f1.Draw("same"); root [76] TF1 f4("f4","abs(f1)*exp(x)", 0, 10 ); root [77] f4.Draw(); root [78] TF2 ff1("f5","abs(f1)*y", 0, 10, -10, 10 ); root [79] ff1.Draw()
root [83] TH1F h1("h1","mytitle",100,0,1); root [84] for ( double x = 0; x<1; x += 1e-6 ) h1.Fill(x,x**3); root [85] h1.Draw(); root [80] TH1F h2("h2","mytitle",100,0,1); root [81] for ( int i = 0; i<100000; i++ ) h2.Fill(gRandom->Rndm()); root [82] h2.Draw(); root [87] TH1F h3("h3","Random Gauss",100,-4,4); root [88] for ( int i = 0; i<100000; i++ ) h3.Fill(gRandom->Gaus()); root [89] h3.Draw(); root [89] h3.Fit("gaus"); // Simple Gauss Fit
// file gausf.C { Int_t nit = 10, nrnd, i, j; Float_t mean[10], emean[10], xv[10], ex[10]; TH1F h3("h3","Random Gauss",100,-4,4); j =0; for ( i =0; i<nit; i++ ) { nrnd = 100*pow(2,i); xv[i] = i; ex[i] = 0.1; for ( ; j<nrnd; j++ ) { h3.Fill(gRandom->Gaus()); } h3.Fit("gaus","eq"); h3.Draw(); TF1 *fit = h3.GetFunction("gaus"); mean[i] = fit->GetParameter(1); emean[i] = fit->GetParError(1); cout << i << " Mean = " << mean[i] << " +- " << emean[i] << endl; } TCanvas ce("ce", "ce"); TGraphErrors tg( nit, xv, mean, ex, emean ); tg.Draw("AP"); } root [1] .x gausf.C
In ROOT wird noch oft mit Pointern gearbeitet, d.h. ROOT Objekte und ihre Methoden werden über Pointer initialisiert, angesprochen und zurück gegeben.
Was sind Pointer?
Ein Pointer ist eine Variable, die eine Speicheradresse enthält.
int b = 42; int *pb; // pb deklariert als pointer auf int pb = &b; // & ist Adress operator, liefert Adresse von b cout << b << endl; cout << pb << endl; // Kryptische Adresse cout << *pb << endl; // De-referenzieren: *pb == b double x = 7.256; double *px = &x; // px deklariert als pointer auf double px = &b; // illegal pointer auf double kann nicht Adresse von int nehmen
Direkte Variable vs Pointer in ROOT
{ // histogram variable direct TH1F h1("h1","mytitle",100,0,1); // h1 Variable vom Type TH1F // Object-method call: objectname.method( ... ) for ( int i = 0; i<1000000; i++ ) h1.Fill(gRandom->Rndm()); // Fill h1.Draw(); // Draw // histogram variable as pointer to hist, histo object created with new ... TH1F * h2 = new TH1F("h2","mytitle",100,0,1); // Object-method call: objectpointer->method( ... ) for ( int i = 0; i<1000000; i++ ) h2->Fill(gRandom->Rndm()); // Fill h2->Draw(); // Draw (*h2).Draw(); // altenative call (de-ref pointer).method( ... ) }
Warum Pointer?
Gültigkeitsbereich (Scope und Lifetime) von Variablen:
TH1F Makehist( int n = 100000) { // histogram variable direct TH1F h1("h1","mytitle",100,0,1); // h1 Variable vom Type TH1F // Object-method call: objectname.method( ... ) for ( int i = 0; i<n; i++ ) h1.Fill(gRandom->Rndm()); // Fill return( h1 ); // Object h1 will be deleted when function ends // returning h1 to caller requires complex copy operations --> discouraged } TH1F * Makehistp( int n = 100000) { // histogram variable direct TH1F * h1 = new TH1F("h1","mytitle",100,0,1); // h1 Variable vom Type TH1F * // Object-method call: objectname.method( ... ) for ( int i = 0; i<n; i++ ) h1->Fill(gRandom->Rndm()); // Fill return( h1 ); // Object h1 points to will stay beyond function end, // --> programmer's responsibility to delete it // returning pointer h1 to caller fine, no extra copy }
Wesentlich einfacher in Python: Alle Variablen sind quasi Pointer auf Objekte, automatisches Python Memory Management, ...