Subsections

Arbeiten mit ROOT - C++

ROOT initialisieren und starten

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

ROOT beenden:
.q


C++ Operationen - ROOT als Taschenrechner

ROOT Datentypen

ROOT definiert (typedef) eigene Datentypen um einheitliche Byte-Länge auf unterschiedlichen Systemen zu gewährleisten.
C++ FORTRAN Bytes ROOT Bytes
char CHARACTER*1 1 Char_t 1
int INTEGER 2/4 Int_t 4
long   4/8 Long_t 8
float REAL*4 4 Float_t 4
double REAL*8 8 Double_t 8


\ensuremath{\Rightarrow} Wichtig für Datenaustausch zwischen unterschiedlichen Systemen.



 root [0] 5**2
 (int)25
 root [1] 25.1*3.5
 (double)8.78500000000000085e+01
 root [2] 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: 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.Draw(); // Draw Histogramm
}

Schleifen


 root [40] Double_t s = 1;
 root [41] for ( Int_t i=1; i<70; i++ ) s *= i  // Fakultaet
 root [42] s
 (Double_t)1.71122452428141297e+98
 root [57] TMath::Gamma(70)   // Dasselbe mit Gamma Fkt
 (Double_t)1.71122452441969184e+98

Macros




 // file fak.C
 Double_t fak( Int_t n = 1 )
 {
   Double_t t = 1;
   for ( Int_t 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.C(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 = new TF1("f1","sin(x)", 0, 10 );
 root [61] f1->Draw();
 root [68] TF1 *f2 = new TF1("f2","cos(x)", 0, 10 );
 root [69] f2->Draw();         
 root [70] f1->Draw("same");         
 root [76] TF1 *f4 = new TF1("f4","abs(f1)*exp(x)", 0, 10 );   
 root [77] f4->Draw();                                      
 root [78] TF2 *ff1 = new TF2("f5","abs(f1)*y", 0, 10, -10, 10 );
 root [79] ff1->Draw()


Image funcs

Histogramme und Zufallszahlen




 root [83] TH1F * h1 = new TH1F("h1","mytitle",100,0,1);                
 root [84] for ( Float_t x = 0; x<1; x += 1e-6 ) h1->Fill(x,x**3);
 root [85] h1->Draw();                                            
 root [80] TH1F * h2 = new TH1F("h2","mytitle",100,0,1);
 root [81] for ( Int_t i = 0; i<100000; i++ ) h2->Fill(gRandom->Rndm());
 root [82] h2->Draw();
 root [87] TH1F * h3 = new TH1F("h3","Random Gauss",100,-4,4);
 root [88] for ( Int_t i = 0; i<100000; i++ ) h3->Fill(gRandom->Gaus());
 root [89] h3->Draw();                                                  


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 = new TH1F("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 = new TCanvas("ce", "ce");
   TGraphErrors *tg = new TGraphErrors( nit, xv, mean, ex, emean );
   tg->Draw("AP");
 }
 root [1] .x gausf.C


Pointer in C++ und Root

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 <#1141#>& 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.

    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:


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

NTuple und Tree Ausgabe

GDuckeck 2018-04-10