Tutorials
Basics
Geant4

GEANT4 Simulation Toolkit Tutorial

Overview

GEANT4 is a powerful and versatile physics simulation toolkit that enables users to model complex particle interactions in various materials and geometries. It has a wide range of applications, including high-energy physics, astrophysics, medical physics, and radiation protection.

Tutorial Objective

In this tutorial, we will walk you through the following steps:

  • Download and install GEANT4 using conda
  • Build and use the B1 example
  • Modify the B1 example by changing solids, materials, and detector geometry
  • Add a way to track energy deposition in a sensitive volume
  • Output the tracking data into a ROOT file

Tutorial Limitations

This tutorial is intended for beginners and will cover only the basic concepts of GEANT4. More advanced features and applications can be found in the official GEANT4 documentation and other resources provided in the "Other Resources" section.

Other Resources

Prerequisites

It is strongly suggested that you have worked through the previous tutorials before attempting this one. The following tutorials are recommended:

It is strongly recommended that you have a Mac or Linux opereating system. If you are using Windows you should consider using a virtual machine or dual booting your computer. You find more information on the those here Virtual Machine (opens in a new tab) and Dual Boot (opens in a new tab).

Download and Install with Conda

To install GEANT4 using conda, first, create a new environment and activate it:

💡

If you dont have conda installed, you can install it with Homebrew (opens in a new tab) on mac, run brew install --cask miniconda. For Linux see here (opens in a new tab). For more information on conda see here (opens in a new tab).

conda create --name geant4_env
conda activate geant4_env

Then install GEANT4 and its dependencies:

conda install -c conda-forge geant4

Tutorial

Part 1: Build and Use the B1 Example

Build the B1 Example

With your geant4_env copy the B1 example from the GEANT4 installation directory to your current working directory:

cp -r $CONDA_PREFIX/share/Geant4-<version>/examples/basic/B1 $PWD/
💡

Replace <version> with the version of GEANT4 you installed. If you are not sure what version you installed, you can find it by cd $CONDA_PREFIX/share/ and then ls. Or just hit tab as you're typing the cp command.

Create a build directory, navigate to it, and run cmake:

mkdir B1-build
cd B1-build
cmake ../B1
💡

I sometimes get an error when running cmake. It can not find ZLIB. If this happens to you, you can fix it by running conda install -c conda-forge zlib and then running export ZLIB_ROOT=$CONDA_PREFIX. rm -r B1-Build then try everything again.

Build the example:

make

Use the B1 Example

To run the B1 example, execute the following command:

./exampleB1

Now you should see this:





Alright! Go ahead and play around with the example by clicking on the green play button. This will run a simulation. In the command line at the bottom of the screen where it says Session: type /run/beamOn 100 and press enter.





There we told it to run 100 events.

Now that we know what it looks like let's modify the example so we can better understand how G4 works.

Part 2: Explaning the B1 Example

B1DetectorConstruction.cc Layout

Creating an object in Geant4 involves defining the material and solid geometry, creating a logical volume that combines them, and then creating a physical volume to place the logical volume in the detector geometry. Logical volumes describe the volume's shape and material properties, while physical volumes define its position and orientation relative to its parent volume.

The first thing we will do is take a look at the detector construction file to better understand how making these volumes works. All of the volumes are (usaully) created and defined in the detector construction files. Let's open the B1DetectorConstruction.cc it is located in the B1/src directory.

When you open the file you should see this:

#include "DetectorConstruction.hh"
 
#include "G4RunManager.hh"
#include "G4NistManager.hh"
#include "G4Box.hh"
#include "G4Cons.hh"
#include "G4Orb.hh"
#include "G4Sphere.hh"
#include "G4Trd.hh"
#include "G4LogicalVolume.hh"
#include "G4PVPlacement.hh"
#include "G4SystemOfUnits.hh"
 
namespace B1
{
 
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
 
DetectorConstruction::DetectorConstruction()
{}
 
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
 
DetectorConstruction::~DetectorConstruction()
{}
 
//....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo......
 
G4VPhysicalVolume* DetectorConstruction::Construct()
{}}

At the top are the header files (.hh) and the namespace. The header files contain the definitions of the classes that are used in the code. And the namespace is just a way to organize the code. It is not necessary but it is good practice. The next thing we see is the constructor DetectorConstruction::DetectorConstruction() and destructor DetectorConstruction::~DetectorConstruction(). These are empty for this basic example. The last thing we see is the Construct() method. This is where the materials, geometry, etc are defined.

The next line of code:

61  G4NistManager* nist = G4NistManager::Instance();

Creates a pointer to the NIST material manager. The NIST material manager is used to create materials for the world, envelope, and shapes.

From this point lets look at just one object, the envolope volume, to understand how G4 handles volumes.

in this line of code we have some dimensions defined:

65  G4double env_sizeXY = 20*cm, env_sizeZ = 30*cm;

Then the material is defined:

66  G4Material* env_mat = nist->FindOrBuildMaterial("G4_WATER");

Then skipping a few lines down, the solid geometry is defined:

101  G4Box* solidEnv =
102     new G4Box("Envelope",                    //its name
103         0.5*env_sizeXY, 0.5*env_sizeXY, 0.5*env_sizeZ); //its size

Here the G4Box is used to create a box-shaped volume. We use the varibles that were defined earlier on line 65. Half lengths have to be used, that is what the 0.5 * is for. So if we wanted it to be 10 cm long we would actually use 5 cm, or multiply 10 cm by 0.5. Notice that we also give the shape a name.

Next the logical volume:

105  G4LogicalVolume* logicEnv =
106    new G4LogicalVolume(solidEnv,            //its solid
107                       env_mat,             //its material
108                       "Envelope");         //its name

With this we put the material together with the solid geometry. We also give it a name, it's usaully the same name as the solid geometry.

Finally the physical volume:

110  new G4PVPlacement(0,                       //no rotation
111                    G4ThreeVector(),         //at (0,0,0)
112                    logicEnv,                //its logical volume
113                    "Envelope",              //its name
114                    logicWorld,              //its mother  volume
115                    false,                   //no boolean operation
116                    0,                       //copy number
117                    checkOverlaps);          //overlaps checking

With G4PVPlacement a few things are happening. First we have options to rotate the volume and place it in a specific location. We assign a logical volume. We also assign it a mother volume, in this case we put it inside of the world volume. This is standard practice to make a world volume and then place everything inside of it. We also have the option to check for overlaps with other volumes. This is a good idea to do, but it can slow down the simulation.

Thats it! That is how a volume is created. Now lets look at all of the pieces together.

Materials:

G4NistManager* nist = G4NistManager::Instance();
G4Material* world_mat = nist->FindOrBuildMaterial("G4_AIR");
G4Material* env_mat = nist->FindOrBuildMaterial("G4_WATER");
G4Material* shape1_mat = nist->FindOrBuildMaterial("G4_A-150_TISSUE");
G4Material* shape2_mat = nist->FindOrBuildMaterial("G4_BONE_COMPACT_ICRU");

World volume: A box-shaped world volume is created using the G4Box class, and the corresponding logical and physical volumes are defined.

G4Box* solidWorld = new G4Box("World", 0.5*world_sizeXY, 0.5*world_sizeXY, 0.5*world_sizeZ);
G4LogicalVolume* logicWorld = new G4LogicalVolume(solidWorld, world_mat, "World");
G4VPhysicalVolume* physWorld = new G4PVPlacement(0, G4ThreeVector(), logicWorld, "World", 0, false, 0, checkOverlaps);

Envelope volume: An envelope volume is created as a box shape, and its logical and physical volumes are defined.

G4Box* solidEnv = new G4Box("Envelope", 0.5*env_sizeXY, 0.5*env_sizeXY, 0.5*env_sizeZ);
G4LogicalVolume* logicEnv = new G4LogicalVolume(solidEnv, env_mat, "Envelope");
new G4PVPlacement(0, G4ThreeVector(), logicEnv, "Envelope", logicWorld, false, 0, checkOverlaps);

Shape 1: A conical section is created using the G4Cons class, and its logical and physical volumes are defined.

G4Cons* solidShape1 = new G4Cons("Shape1", shape1_rmina, shape1_rmaxa, shape1_rminb, shape1_rmaxb, shape1_hz, shape1_phimin, shape1_phimax);
G4LogicalVolume* logicShape1 = new G4LogicalVolume(solidShape1, shape1_mat, "Shape1");
new G4PVPlacement(0, pos1, logicShape1, "Shape1", logicEnv, false , 0, checkOverlaps);

Shape 2: A trapezoid shape is created using the G4Trd class, and its logical and physical volumes are defined.

G4Trd* solidShape2 = new G4Trd("Shape2", 0.5*shape2_dxa, 0.5*shape2_dxb, 0.5*shape2_dya, 0.5*shape2_dyb, 0.5*shape2_dz); 
G4LogicalVolume* logicShape2 = new G4LogicalVolume(solidShape2, shape2_mat, "Shape2");
new G4PVPlacement(0, pos2, logicShape2, "Shape2", logicEnv, false, 0, checkOverlaps); 

Scoring volume: Shape 2 is set as the scoring volume.

fScoringVolume = logicShape2; 

We haven't talked about scoring yet. The scoring volume is set to track the physical processes that happen inside of it. We will talk about this in more detail later.

Return physical world: The Construct() method returns the physical world volume and all of it's child volumes.

return physWorld; 

So, we can see this simulation is meant to simulate human bone and tissue. Everything is placed in water, as a proxy for the human body. And the process inside of the bone are tracked. This could be helpful in understanding the dose a patient could receive from a medical treatment.

Now lets make a few changes to the simulation.

Part 3: Modify the simulation

Change and Add geometry

In this section, we will modify the B1 example by changing the solids, materials, and detector geometry. We will replace the conical section (Shape 1) with a sphere and change the material of Shape 2 to aluminum.

Locate the code for Shape 1, which is a conical section. Comment out the following lines (132-147):

💡

To easily comment out multiple lines, highlight the lines and press Ctrl+/ on Linux or Cmd+/ on Mac.

G4Cons* solidShape1 = new G4Cons("Shape1", shape1_rmina, shape1_rmaxa, shape1_rminb, shape1_rmaxb, shape1_hz, shape1_phimin, shape1_phimax);
G4LogicalVolume* logicShape1 = new G4LogicalVolume(solidShape1, shape1_mat, "Shape1");
new G4PVPlacement(0, pos1, logicShape1, "Shape1", logicEnv, false , 0, checkOverlaps);

Define a new sphere solid and its logical volume, and place it in the envelope:

G4double sphere_radius = 4*cm;
G4Sphere* solidSphere = new G4Sphere("Sphere", 0, sphere_radius, 0, 360*deg, 0, 180*deg);
G4LogicalVolume* logicSphere = new G4LogicalVolume(solidSphere, shape1_mat, "Sphere");
new G4PVPlacement(0, pos1, logicSphere, "Sphere", logicEnv, false , 0, checkOverlaps);

Save the file and rebuild the B1 example by running make in the B1-build directory.

cd B1-build
make

Now let's check to make sure it worked. Run the modified B1 example with ./exampleB1.

./exampleB1




Great! You should see the new geometry with a sphere instead of the conical section.

Now locate the code for Shape 2 (line 160), which is a trapezoid. Change the material of Shape 2 to aluminum:

G4Material* shape2_mat = nist->FindOrBuildMaterial("G4_Al");

Save the changes and rebuild the B1 example by running make in the B1-build directory. Run the modified B1 example with ./exampleB1.

Thats it! Those are the basics on how to make and modify volumes in GEANT4.

Part 4: Add a root output file

Now we want to track the physical processes in the scoring volume and output that data into a ROOT file. We will use the G4AnalysisManager class to do this.

Go to the RunAction.cc file and add this to the header section:

#include "G4AnalysisManager.hh"

Then add this code to in the RunAction::RunAction():

💡

Always add code in between the main curly {} braces. Also add it to the end of the end of the current code.

  auto analysisManager = G4AnalysisManager::Instance();
  G4cout << "Using " << analysisManager->GetType() << G4endl;
  analysisManager->SetVerboseLevel(1);
  analysisManager->SetNtupleMerging(true);
 
  analysisManager->CreateNtuple("Rec", "Edep and Position");
  analysisManager->CreateNtupleDColumn("Edep");
  analysisManager->FinishNtuple();

And add this the the Runaction::~RunAction() destructor:

delete G4AnalysisManager::Instance();

Add this the the RunAction::BeginOfRunAction() method:

  auto analysisManager = G4AnalysisManager::Instance();
 
  G4String fileName = "Doseoutput.root";
  analysisManager->OpenFile(fileName);

Add this the the RunAction::EndOfRunAction():

  auto analysisManager = G4AnalysisManager::Instance();
  analysisManager->Write();
  analysisManager->CloseFile();

Now go to the SteppingAction.cc file and add this to the header section:

#include "G4AnalysisManager.hh"

Then add this code to in the SteppingAction::UserSteppingAction():

  auto analysisManager = G4AnalysisManager::Instance();
  analysisManager->FillNtupleDColumn(0, edepStep);
  analysisManager->AddNtupleRow();

Save the changes and rebuild the B1 example by running make in the B1-build directory. Then run the modified B1 example with ./exampleB1.

./exampleB1

From the Session: command line run /run/beamOn 100 to generate 100 events. Now look in the B1-build directory for the Doseoutput.root file. Open it with ROOT and plot the energy deposition.

root -l Doseoutput.root
TBrowser b

You should see something close to this plot:





Great! There is just one more key element to using GEANT4 and that is running a macro file.

Running a macro file

From your build directory open the run1.mac in an editor.





And delete these lines:

# 
# proton 210 MeV to the direction (0.,0.,1.)
#
/gun/particle proton
/gun/energy 210 MeV
/tracking/verbose 2
#
/run/beamOn 1

Now cHange these lines:

/gun/particle gamma
/gun/energy 6 MeV
#
/run/beamOn 5

To this:

/gun/particle e-
/gun/energy 6 MeV
#
/run/beamOn 1000

This chagnes our particle to an electron and the number of events to 1000. Save your changes and run the macro file with the following command:

./exampleB1 run1.mac

Open it up in ROOT agian and plot the energy deposition.

root -l Doseoutput.root
TBrowser b

You should have something like this:





Wrap Up

Congratulations! You have successfully completed the GEANT4 simulation tutorial. You have learned how to download and install GEANT4, build and use the B1 example, modify the example by removing and adding solids, track energy deposition in a sensitive volume, and output the tracking data into a ROOT file, as well as run a macro file. If you want to track other physical process you can add them to the SteppingAction and RunAction files. Please note the when tracking the position of energy deposits you have to be careful of the pre or post step position you use. But that is a more advanced topic for another time.

From here, you can explore more advanced features of GEANT4 and apply the toolkit to your specific projects. The official GEANT4 documentation, mailing list, and forums are great resources for expanding your knowledge and getting help with any issues you may encounter.