Subsections

Arbeiten mit ROOT – C++

ROOT initialisieren und starten

Vor dem ersten Mal: ROOT starten:
// Pfade setzen (evt. in .bashrc kopieren)
module load root/6.20.04
cd root
root

ROOT beenden:
.q


C++ Operationen – ROOT als Taschenrechner

ROOT Datentypen

ROOT benutzt i.W. die Standard-C++ Datentypen:

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 – einfache interaktive Kommandos


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

ROOT Klassen

ROOT stellt den Anwendern eine riesige Klassenbibliothek zur Verfügung für Funktionen, Histogramme, Zufallszahlen, Statistik, I/O, etc.

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
}

Schleifen

Auch einfache Schleifen kann man interaktiv ausführen ...

 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

Macros

Aufruf/Ausführen von C++ Code in externen Files (=Macros):




 // 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

Macro Variationen

In ROOT gibt es verschiedene Möglichkeiten Macros zu definieren und auszuführen

Funktionen


 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()

Image funcs

Histogramme und Zufallszahlen



 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

Image histo

Fitten




 // 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

Pointer in C++ und Root

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.

Deklaration
Type * name, d.h. auch hier Typ-Angabe erforderlich, Unterschied zu normaler Variablen–Deklaration ist * vor dem Identifier.
Zuweisung
name = & var, Address-Zuweisung mittels Adress Operator <#928#>& bei existierenden Objekten oder ...
Zuweisung
name = new Type( ... ), Address-Zuweisung als Ergebnis von new ... bei neu angelegten Objekten.
Verwendung (1)
Wert (=Inhalt der Pointer Variablen) ist Adresse
Verwendung (2)
De–Referenzieren, d.h. Auslesen des Wertes an der Adresse mittels * vor Namen.

Member-Variablen/Methoden
Bei Pointern auf Objekte erfolgt der Zugriff auf Member-Variablen/Methoden mit spezieller Syntax ->, z.B. histoptr->Fill() statt histo.Fill()

    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


Einfache Variable:
Name ist “Label” mit dem Inhalt der Variablen angesprochen wird:
Image var

Pointer:
Auch Variable, aber Inhalt ist Adresse und Typ einer anderen Variablen:
Image pointer

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:

Obiges gilt strikt in kompilierten C++ Programmen. Beim interaktiven Arbeiten mit ROOT/Cling-Interpreter bleiben Histogramme und andere ROOT-spezifische Objekte oft über Funktionsaufrufe hinweg erhalten.


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, ...