Hands on 4: A first physics measurments

Back to Agenda


Introduction

Important Note: The code of this tutorial is an adaptation of Geant4 example B5. Thus you can review almost all concepts from this tutorial in the example that can be found under: <g4-source-tree>/examples/basic/B5.

In this third hands-on you will learn:

The code for this example can be found here, while the complete solution is here. Download the exercise code and unpack it:
  $ cd exercises
  $ tar xzf HandsOn4.tar.gz

Follow the instructions of Hands On 1 to configure with cmake the example and build it.
Try out the application:
  $ source <where-G4-was-installed>/bin/geant4.[c]sh
  $ mkdir build-HandsOn4
  $ cmake -DGeant4_DIR=<tutorial>/lib/Geant4-10.0.1 <tutorial>/HandsOn4
  $ [g]make [-jN]
  $ ./SlacTut

The geometry is the same obtained at the end of the previous hands on.

We will not modify the geometry anymnore, also several sensitive detectors and hits classes have been added to the setup. Take a moment to look at the classes wich name ends in SD and Hit. In particular it is important that you understand how the calorimeter hits work. In the exercise number 2 you will calculate a very simple physics quantity (a partial shower shape) from the energy released in calorimeters.
The goal of these exercises is to show how to interact with Geant4 kernel to extract physics quantities. In complex applications you will probably rely on experimental framework to implement the analysis and recording of data. Additionaly you may need digitization (i.e. simulation of detector read-out) and interface to persistency libraries for data storage.
Geant4 does not provide/recommend specific utilities or for these opeartions because these are strongly user-dependentent. However we provide light-weight histogramming and ntuple utilities. These are compatible with AIDA and ROOT output format (they do not require any library installed on the system). They can also dump ntuples in tabular form in text files (CSV) to import data in (virtually) any analysis system (pylab, R, Octave, Excel, Matlab, Mathematica, ...). If you do not have neither an AIDA-compliant tool or ROOT installed on your system you will not be able to display histograms, but you will still be able to read ntuples written in CSV format.

For your interest here are some links (my personal preferences):


User Actions II

In Exercise 3 of Hands On 3 you have printed on screen, for each simulated event, the hits collected in the hodosope. In this exercise we will show how to accumualte some information (the energy deposited in the calorimeters) over the entire run. We will also show how to merge (e.g. reduce, combine) the results in a multi-threaded application.

Goal of these two exercises is to calculate the average energy released in the electromagnetic and hadronic calorimeter and the average partial shower shape (a shower shape is a quantity that somehow describes the charactersitics of spatial dimensions of particle showers in calorimeters. In this example we will calcualte the fraction of energy released in the electromagnetic calorimeter. These quantities are useful to determine properties of the impinging particle. For example an electron or gamma has the em fraction very close to 1, while an hadron will have a smaller em fraction a muon will have even a smaller value. It is possible to develop algorithms to identify the impinging particle from these quantities. Clearly this is an over-simplified example...).

During Exercise 1 you will modify the application to accumulate the energy released in calorimeters in each event. You will modify 2 files: Run.hh and Run.cc, implementing a user-defined G4Run/

During Exercise 2 you will modify the file RunAction.cc that implements the user defined G4UserRunAction. You willretrieve the information collected in the first exercise and dump on screen the results of your data analysis: energy in calorimeters and shower shape.

During the simulation an instance of a G4Run exists and is managed by Geant4 kernel. User can extend this class to accumulate user data.

Exercise 1 Step 1

Create a user-defined run class

Modify the file Run.hh that defines a class inheriting from G4Run. Extend the class to contain the information to be stored: the total energy deposited in calorimeters and the accumulated shower shape (all of double type). Since you will need to access hits collections from calorimeters, add two integer data members to keep track of the hits collection ids.

Extra question: what are the data members of the base class G4Run?

Solution

Run.hh File:

 class Run : public G4Run {
 public:
  Run();
  virtual ~Run() {};
  virtual void RecordEvent(const G4Event*);
  virtual void Merge(const G4Run*);
  G4double GetEmEnergy() const { return em_ene; }
  G4double GetHadEnergy() const { return had_ene; }
  G4double GetShowerShape() const { return shower_shape; }
 private:
  G4double em_ene; //accumulated energy in EM calo
  G4double had_ene;//accumulated energy in HAD calo
  G4double shower_shape;//accumulated shower shape ( f=EM/(EM+HAD) )
  G4int ECHCID; //ID for EM hits collection
  G4int HCHCID; //ID for HAD hits collection
 };

Exercise 1 Step 2

Accumualte physics quantities

Modify file Run.cc in the method RecordEvent. This method is called by Geant4 kernel at the end of each event passing current event pointer. In this method retrieve the hits collections of calorimeters, loop on all hits and calculate physics quantities. In the constructor of Run class initialize the class data members to an initial value (0 for energy and shape and -1 for ids).

Hint 1: Note that the initial value of -1 for hits id allows you to be efficient in searching the hits by string: if id==-1 you need to search the collections, if not you already did this opeartion and you can skip it.

Solution

Run.cc

 void Run::RecordEvent(const G4Event* evt)
 {
  //Forward call to base class
  G4Run::RecordEvent(evt);
 
  if ( ECHCID == -1 || HCHCID == -1) {
    G4SDManager* sdManager = G4SDManager::GetSDMpointer();
    ECHCID = sdManager->GetCollectionID("EMcalorimeter/EMcalorimeterColl");
    HCHCID = sdManager->GetCollectionID("HadCalorimeter/HadCalorimeterColl");
  }
  G4HCofThisEvent* hce = evt->GetHCofThisEvent();
  if (!hce) {
    G4ExceptionDescription msg;
    msg << "No hits collection of this event found.\n";
    G4Exception("Run::RecordEvent()","Code001", JustWarning, msg);
    return;
  }
  const EmCalorimeterHitsCollection* emHC = static_cast<const EmCalorimeterHitsCollection*>(hce->GetHC(ECHCID));
  const HadCalorimeterHitsCollection* hadHC = static_cast<const HadCalorimeterHitsCollection*>(hce->GetHC(HCHCID));
  if ( !emHC || !hadHC )
  {
    G4ExceptionDescription msg;
     msg << "Some of hits collections of this event not found.\n";
    G4Exception("Run::RecordEvent()","Code001", JustWarning, msg);
     return;
  }
  G4double em = 0;
  G4double had = 0;
  for (size_t i=0;i<emHC->GetSize();i++)
  {
    EmCalorimeterHit* hit = (*emHC)[i];
    em += hit->GetEdep();
  }
  for (size_t i=0;i<hadHC->GetSize();i++)
  {
    HadCalorimeterHit* hit = (*hadHC)[i];
    had += hit->GetEdep();
  }
  had_ene += had;
  em_ene += em;
  if ( had+em > 0 ) //Protect agains the case had+em=0
    shower_shape += ( em/(had+em) );
 }

Exercise 1 Step 3

Implement reduction for multi-threading.

This step is optional if you do not have a multi-threading enabled application. However in this case the code is so simple that it's worth to do it (and in a sequential application this code will simply be not executed).

Why you need this? Remember in a multi-threaded application each worker thread has its own instance of class G4Run. Events are distributed and you finish up with many run objects (one per worker thread). Geant4 provides a way to merge these sub-runs into a single one. This is done implementing a Merge method. Geant4 kernel works in way that the worker threads will call the Merge method of the master run object passing a pointer to the worker run object. This simple animation explains what is happening under the hood (note that Geant4 kernel will take care of synchronizing the threads):

Solution

Run.cc File:

 void Run::Merge(const G4Run* aRun)
 {
  const Run* localRun = static_cast<const Run*>(aRun);
  em_ene += localRun->GetEmEnergy();
  had_ene += localRun->GetHadEnergy();
  shower_shape += localRun->GetShowerShape();
  //Forward call to base-class
  G4Run::Merge(localRun);
 }

Exercise 1 Step 4

Create an instance of user-defined run class at each new run.

Now that you have extended G4Run you need to tell Geant4 kernel to use it instead of the default one. To do so you need to modify method RunAction::GenerateRun and return an instance of Run instead of the default (this method is called by Geant4 at the beginning of each run). The method is implemented in RunAction.cc file.

Solution

RunAction.cc File

 G4Run* RunAction::GenerateRun() {
  return new Run;
 }

Exercise 2

Calculate physics quantities and pring them on screen.

Now that Run class has been modified to include user data we can print out our simple data analysis at the end of the run. To do that we modify the method EndOfRunAction of the RunAction class (RunAction.cc file). Retrieve from the run object the information you need and calculate the average energy release in calorimeters and the shower shape.

Hint 1: Note that Geant4 will pass you an object of type G4Run (the base class). You need to make an appropriate cast to access your data.

Hint 2: The total number of events is a data member of base class G4Run. Check in online documentation how to get it.

Hint 3: The quantity have been stored in Geant4 natural units. A useful function G4BestUnit can be used to print on screen a variable with a dimension. For example:

G4double someValue = 0.001*GeV;
G4cout<< G4BestUnit( someValue , "Energy" )<<G4endl; //Will print "1 MeV"


Solution

RunAction.cc File

 void RunAction::EndOfRunAction(const G4Run* run)
 {
  const Run* myrun = dynamic_cast<const Run*>(run);
  if ( myrun )
  {
    G4int nEvets = myrun->GetNumberOfEvent();
    if ( nEvets < 1 )
    {
      G4ExceptionDescription msg;
      msg << "Run consists of 0 events";
      G4Exception("RunAction::EndOfRunAction()","Code001", JustWarning, msg);
      nEvets=1;
    }
    G4double em_ene = myrun->GetEmEnergy();
    G4double had_ene = myrun->GetHadEnergy();
    G4double shower_shape = myrun->GetShowerShape();
    G4cout<<"Run["<<myrun->GetRunID()<<"] With: "<<nEvets<<"Events\n"
      <<" <E_em>="<<G4BestUnit(em_ene/nEvets,"Energy")<<"\n"
      <<" <E_had>="<<G4BestUnit(had_ene/nEvets,"Energy")<<"\n"
      <<" <E>="<<G4BestUnit((em_ene+had_ene)/nEvets,"Energy")<<"\n"
      <<" <ShowerShape>="<<shower_shape/nEvets<<G4endl;
  } else {
  G4ExceptionDescription msg;
  msg << "Run is not of correct type, skypping analysis via RunAction";
  G4Exception("RunAction::EndOfRunAction()","Code001", JustWarning, msg);
  }
 }

Using g4analysis to store results

In these exercises we will use G4AnalysisManager to store in ntuples and histograms the content of hits collections. The goal of the g4analysis module is to provide light-weight support for simple storage of data. You may skip this example if you already know that you will not use g4analysis. You need an AIDA-compliant tool or ROOT to visualize histograms.
With ROOT this is shows how histograms look like:

While this is how they look like in JAS3:

Hint 1: Histograms are automatically merged from all worker threads. With a concept similar to what shown in Exercise 1 Step 3 histograms are summed at the end of the run. A single histograms file "SlacTutorial.[root|xml]" exists. For ntuples it has not so much sense the automatic merging, because what you will do for analysis is to process one file after the other. In ROOT terminology you will create a TChain ( ITupleFactory::createChained in AIDA); with CSV format the merging is as simple as: cat *_t*.csv > merged.csv.

Exercise 3 Step 1 (Optional)

Define content of the output file(s).

Create output file(s) and define their content: four histograms and one nutple.

The content of the output file can be defined in the constructor of RunAction since it does not change between runs.

Solution

RunAction.cc file:
 RunAction::RunAction()
  : G4UserRunAction()
 {
  // Create analysis manager
  // The choice of analysis technology is done via selectin of a namespace
  // in Analysis.hh
  G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();
  G4cout << "Using " << analysisManager->GetType() << G4endl;
 
  // Default settings
  analysisManager->SetVerboseLevel(1);
  analysisManager->SetFileName("SlacTutorial");
 
  // Book histograms, ntuple
  //
 
  // Creating 1D histograms
  analysisManager->CreateH1("Chamber1","Drift Chamber 1 # Hits", 50, 0., 50); // h1 Id = 0
  analysisManager->CreateH1("Chamber2","Drift Chamber 2 # Hits", 50, 0., 50); // h1 Id = 1
 
  // Creating 2D histograms
  analysisManager->CreateH2("Chamber1 XY","Drift Chamber 1 X vs Y",50, -1000., 1000, 50, -300., 300.); // h2 Id = 0
  analysisManage->CreateH2("Chamber2 XY","Drift Chamber 2 X vs Y",50, -1500., 1500, 50, -300., 300.); // h2 Id = 1
 
  // Creating ntuple
  //
  analysisManager->CreateNtuple("SlacTutorial", "Hits");
  analysisManager->CreateNtupleIColumn("Dc1Hits"); // column Id = 0
  analysisManager->CreateNtupleIColumn("Dc2Hits"); // column Id = 1
  analysisManager->CreateNtupleDColumn("ECEnergy"); // column Id = 2
  analysisManager->CreateNtupleDColumn("HCEnergy"); // column Id = 3
  analysisManager->CreateNtupleDColumn("Time1"); // column Id = 4
  analysisManager->CreateNtupleDColumn("Time2"); // column Id = 5
  analysisManager->FinishNtuple(); //Do not forget this line!
 }

Exercise 3 Step 2 (Optional)

Open the output file at each new run.

Defining an output file and its content, it is not enough, you need to explicitly open it when needed. The best is to open it at the beginning of a new run. In more complex setups you can change the file name at each new run (e.g. via UI commands), so you can produce one file output for each run.

Solution

RunAction.cc file:
  void RunAction::BeginOfRunAction(const G4Run* /*run*/)
 {
  // Get analysis manager
  G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();
  // Open an output file
  // The default file name is set in RunAction::RunAction(),
  // it can be overwritten in a macro
  analysisManager->OpenFile();
 }

Exercise 3 Step 3 (Optional)

Write out the file.

Output files must be explicitly written to disk and closed. It is a good idea to do that at the end of the run.

Solution

RunAction.cc file:
 void RunAction::EndOfRunAction(const G4Run* run)
 {
  const Run* myrun = dynamic_cast<const Run*>(run);
  if ( myrun )
  {
    G4int nEvets = myrun->GetNumberOfEvent();
    if ( nEvets < 1 )
    {
      G4ExceptionDescription msg;
      msg << "Run consists of 0 events";
      G4Exception("RunAction::EndOfRunAction()",
      "Code001", JustWarning, msg);
      nEvets=1;
    }
    G4double em_ene = myrun->GetEmEnergy();
    G4double had_ene = myrun->GetHadEnergy();
    G4double shower_shape = myrun->GetShowerShape();
    G4cout<<"Run["<<myrun->GetRunID()<<"] With: "<<nEvets<<"Events\n"
      <<" <E_em>="<<G4BestUnit(em_ene/nEvets,"Energy")<<"\n"
      <<" <E_had>="<<G4BestUnit(had_ene/nEvets,"Energy")<<"\n"
      <<" <E>="<<G4BestUnit((em_ene+had_ene)/nEvets,"Energy")<<"\n"
      <<" <ShowerShape>="<<shower_shape/nEvets<<G4endl;
  } else {
    G4ExceptionDescription msg;
    msg << "Run is not of correct type, skypping analysis via RunAction";
    G4Exception("RunAction::EndOfRunAction()","Code001", JustWarning, msg);
  }
 
  //=================================
  // Exercise 3 Step 3:
  // Write and close output file
  // save histograms & ntuple
  //
  G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();
  analysisManager->Write();
  analysisManager->CloseFile();
 }

Exercise 3 Step 4 (Optional)

Fill histograms and ntuple with corresponding data.

At the end of each event you should retrieve informaiton from hits collection and fill the histograms and ntuple objects.

You can access filled hits at the end of each event in EventAction class.

Solution

EventAction.cc file:
 void EventAction::EndOfEventAction(const G4Event* event)
 {
  G4HCofThisEvent* hce = event->GetHCofThisEvent();
  if (!hce)
  {
    G4ExceptionDescription msg;
    msg << "No hits collection of this event found.\n";
    G4Exception("EventAction::EndOfEventAction()","Code001", JustWarning, msg);
    return;
  }
 
  // Get hits collections
  HodoscopeHitsCollection* hHC1 = static_cast<HodoscopeHitsCollection*>(hce->GetHC(fHHC1ID));
 
  HodoscopeHitsCollection* hHC2 = static_cast<HodoscopeHitsCollection*>(hce->GetHC(fHHC2ID));
 
  DriftChamberHitsCollection* dHC1 = static_cast<DriftChamberHitsCollection*>(hce->GetHC(fDHC1ID));
 
  DriftChamberHitsCollection* dHC2 = static_cast<DriftChamberHitsCollection*>(hce->GetHC(fDHC2ID));
 
  EmCalorimeterHitsCollection* ecHC = static_cast<EmCalorimeterHitsCollection*>(hce->GetHC(fECHCID));
 
  HadCalorimeterHitsCollection* hcHC = static_cast<HadCalorimeterHitsCollection*>(hce->GetHC(fHCHCID));
 
  if ( (!hHC1) || (!hHC2) || (!dHC1) || (!dHC2) || (!ecHC) || (!hcHC) )
  {
    G4ExceptionDescription msg;
    msg << "Some of hits collections of this event not found.\n";
    G4Exception("EventAction::EndOfEventAction()","Code001", JustWarning, msg);
    return;
  }
 
  //
  // Fill histograms & ntuple
  //
  //=================================
  // Exercise 3 Step 4:
  // Fill histograms & ntuple
 
  // Get analysis manager
  G4AnalysisManager* analysisManager = G4AnalysisManager::Instance();
 
  // Fill histograms
 
  G4int n_hit = dHC1->entries();
  analysisManager->FillH1(0, n_hit);
 
  for (G4int i=0;i<n_hit;i++)
  {
    DriftChamberHit* hit = (*dHC1)[i];
    G4ThreeVector localPos = hit->GetLocalPos();
    analysisManager->FillH2(0, localPos.x(), localPos.y());
  }
 
  n_hit = dHC2->entries();
  analysisManager->FillH1(1, n_hit);
 
  for (G4int i=0;i<n_hit;i++)
  {
    DriftChamberHit* hit = (*dHC2)[i];
    G4ThreeVector localPos = hit->GetLocalPos();
    analysisManager->FillH2(1, localPos.x(), localPos.y());
  }
 
 
  // Fill ntuple
 
  // Dc1Hits
  analysisManager->FillNtupleIColumn(0, dHC1->entries());
  // Dc2Hits
  analysisManager->FillNtupleIColumn(1, dHC1->entries());
 
  // ECEnergy
  G4int totalEmHit = 0;
  G4double totalEmE = 0.;
  for (G4int i=0;i<80;i++)
  {
    EmCalorimeterHit* hit = (*ecHC)[i];
    G4double eDep = hit->GetEdep();
    if (eDep>0.)
    {
      totalEmHit++;
      totalEmE += eDep;
    }
  }
  analysisManager->FillNtupleDColumn(2, totalEmE);
 
  // HCEnergy
  G4int totalHadHit = 0;
  G4double totalHadE = 0.;
  for (G4int i=0;i<20;i++)
  {
    HadCalorimeterHit* hit = (*hcHC)[i];
    G4double eDep = hit->GetEdep();
    if (eDep>0.)
    {
      totalHadHit++;
      totalHadE += eDep;
    }
  }
  analysisManager->FillNtupleDColumn(3, totalHadE);
 
  // Time 1
  for (G4int i=0;i<hHC1->entries();i++)
  {
    analysisManager->FillNtupleDColumn(4,(*hHC1)[i]->GetTime());
  }
 
  // Time 2
  for (G4int i=0;i<hHC2->entries();i++)
  {
    analysisManager->FillNtupleDColumn(5,(*hHC2)[i]->GetTime());
  }
 
  analysisManager->AddNtupleRow();
 
  //
  // Print diagnostics: UI command /run/printProgress can be used
  // to set frequency of how often info should be dumpled
  //
 
  G4int printModulo = G4RunManager::GetRunManager()->GetPrintProgress();
  if ( printModulo==0 || event->GetEventID() % printModulo != 0) return;
 
  G4PrimaryParticle* primary = event->GetPrimaryVertex(0)->GetPrimary(0);
  G4cout << G4endl
      << ">>> Event " << event->GetEventID() << " >>> Simulation truth : "
      << primary->GetG4code()->GetParticleName()
       << " " << primary->GetMomentum() << G4endl;
 
  // Hodoscope 1
  n_hit = hHC1->entries();
  G4cout << "Hodoscope 1 has " << n_hit << " hits." << G4endl;
  for (G4int i=0;i<n_hit;i++)
  {
    HodoscopeHit* hit = (*hHC1)[i];
    hit->Print();
  }
 
  // Hodoscope 2
  n_hit = hHC2->entries();
  G4cout << "Hodoscope 2 has " << n_hit << " hits." << G4endl;
  for (G4int i=0;i<n_hit;i++)
  {
    HodoscopeHit* hit = (*hHC2)[i];
    hit->Print();
  }
 
  // Drift chamber 1
  n_hit = dHC1->entries();
  G4cout << "Drift Chamber 1 has " << n_hit << " hits." << G4endl;
  for (G4int i2=0;i2<5;i2++)
  {
    for (G4int i=0;i<n_hit;i++)
    {
      DriftChamberHit* hit = (*dHC1)[i];
      if (hit->GetLayerID()==i2) hit->Print();
    }
  }
 
  // Drift chamber 2
  n_hit = dHC2->entries();
  G4cout << "Drift Chamber 2 has " << n_hit << " hits." << G4endl;
  for (G4int i2=0;i2<5;i2++)
  {
    for (G4int i=0;i<n_hit;i++)
    {
      DriftChamberHit* hit = (*dHC2)[i];
      if (hit->GetLayerID()==i2) hit->Print();
    }
  }
 
  // EM calorimeter
  G4cout << "EM Calorimeter has " << totalEmHit << " hits. Total Edep is "
  << totalEmE/MeV << " (MeV)" << G4endl;
 
  // Had calorimeter
  G4cout << "Hadron Calorimeter has " << totalHadHit << " hits. Total Edep is "
  << totalHadE/MeV << " (MeV)" << G4endl;
 }

Exercise 3 Step 5 (Optional)

Select output file format.

You can select the output file format including one of g4root.hh, g4xml.hh or g4csv.hh headers file. To keep code clean a very simple file Analysis.hh is used to define the persistency format.

Solution

Analysis.hh File:
  //Uncomment one of the three
  #include "g4root.hh"
 //#include "g4xml.hh"
 //#include "g4csv.hh"

Physics Lists

Assembling physics models into processes and then into physics lists it is a error-prone process. It may be necessary, for your specific use-case to assemble your own physics list. However most often you can start from an already pre-packaged physics list and modify it according to your needs. The easier way to proceed is to add components or replace them from physics lists using constructors.

In these exercises you will learn how to instantiate one of the pre-packaged physics lists via the physics list factory mechanism and how to modify an existing physics list via constructors.

For example in this exercise an additional construct to the physics list is used: G4StepLimiterPhysics that limits the step size to a user defined value in logical volumes that have a limiter attached (in our case the magnetic field volume, see DetectorConstruction.cc file).

Exercise 4.a

Use physics list factory mechanism.

Modify the tutorial.cc file and replace the explicit instance of FTFP_BERT with the use of G4PhysListFactory class.

Hint 1: Do not forget to include the correct .hh file.
Hint 2: G4PhysListFactory::ReferencePhysList() method can be used to instantiate the physics list corresponding to the value of the environment value PHYSLIST, if the variable is not defined FTFP_BERT is used. Run few times the application changing the physics list.
Hint 3: For any given physics list, you can activate a different electro-magnetic physics adding a prefix to physics list name:

Solution

tutorial.cc file:
  //=======================
  // Exercise 4.a and 4.b
  // Use physics list factory and replace default ion pysics
  G4PhysListFactory pls;
  G4VModularPhysicsList* physicsList = pls.ReferencePhysList();
  physicsList->RegisterPhysics(new G4StepLimiterPhysics());
  runManager->SetUserInitialization(physicsList);

Exercise 4.b

Replace a constructor for a specific physics via G4VModularPhysicsList interface.

Try replace the default hadronics ion physics modelling with the alternative one based on QMD.

Hint 1: Do not forget to include the G4IonQMDPhsyics header file.
Hint 2: Pay attention to the application output just after the Geant4 banner, it will print out modifications to the physics list.
Hint 3: The method G4VModularPhysicsList::ReplacePhysics is what you need.

Solution

tutorial.cc file:
  //=======================
  // Exercise 4.a and 4.b
  // Use physics list factory and replace default ion pysics
  G4PhysListFactory pls;
  G4VModularPhysicsList* physicsList = pls.ReferencePhysList();
  physicsList->ReplacePhysics(new G4IonQMDPhysics());
  physicsList->RegisterPhysics(new G4StepLimiterPhysics());
  runManager->SetUserInitialization(physicsList);

Tutorial by:
Andrea Dotti (adotti AT slac DOT stanford DOT edu)

3 March 2014