KAHIBARO
Discord Login Register

26.3. Creating a Proton Beam

Concept of a Proton Beam in Geant4

In this example you want to model a clinical or experimental proton beam that enters a water phantom. In Geant4, a beam is not a built‑in object. It is simply a stream of primary particles whose properties you define in your primary generator. The beam characteristics, such as energy, direction, spatial spread, and energy spread, are encoded in the way you configure and sample the primary protons for each event.

For this chapter we focus on how to represent a monoenergetic or quasi‑monoenergetic proton beam in code, and how to control its basic properties through C++ and, when useful, macro commands. Detailed dose scoring in the phantom is described in later chapters, so here you only prepare the beam that will create that dose distribution.

In Geant4 you do not model a continuous current directly. You define one or more primary protons per event, with a given phase space, and the event rate and number of events together represent the beam intensity.

Choosing the Particle and Basic Beam Parameters

To create a proton beam you first need to decide on the main beam parameters. For a depth‑dose study in water, the usual inputs are the kinetic energy, beam direction, and starting point in front of the water phantom.

The proton definition is provided by Geant4 itself. You will obtain it by name from the particle table, rather than defining any new particle. A typical configuration for a simple, narrow clinical‑like beam might be:

Proton type: $p$ (proton from the Geant4 particle table).

Kinetic energy: for example $100\ \text{MeV}$, $150\ \text{MeV}$, $200\ \text{MeV}$ or another clinically relevant value. In Geant4 code you would use something like 150.*MeV.

Beam direction: usually along the $+z$ axis, represented by a unit vector $(0, 0, 1)$. This means the beam travels from negative $z$ toward positive $z$.

Beam starting position: a point just before the entrance face of the water phantom. If your phantom starts at $z = 0$ and is centered around the $x$ and $y$ axes, you might start protons at $(0, 0, -5\ \text{cm})$ or another small distance upstream.

Later you can add more realism by including a Gaussian spread in energy and position, but the simplest beam is a single proton per event, fired from one point, in a fixed direction, with a fixed energy.

Always use Geant4 units for beam parameters. Combine numbers with unit symbols, for example 150.MeV, 5.cm, mm and so on. Never use raw SI numbers without units.

Implementing the Proton Beam in PrimaryGeneratorAction

The proton beam is implemented in your PrimaryGeneratorAction class. For this example you typically use G4ParticleGun, since it is simple and sufficient for a single, well defined beam.

In the class constructor you create and configure the particle gun. The actual primary is generated in the GeneratePrimaries method, which is called once per event.

A minimal structure is as follows, focusing only on what is specific to a proton beam.

First, in the header you hold a pointer to the gun:

cpp
// PrimaryGeneratorAction.hh
#include "G4VUserPrimaryGeneratorAction.hh"
#include "G4ThreeVector.hh"
class G4ParticleGun;
class G4Event;
class PrimaryGeneratorAction : public G4VUserPrimaryGeneratorAction
{
  public:
    PrimaryGeneratorAction();
    ~PrimaryGeneratorAction() override;
    void GeneratePrimaries(G4Event* event) override;
  private:
    G4ParticleGun* fParticleGun;
};

Then in the source file you configure the beam properties. In the constructor you choose one proton per event, the proton definition, the energy, and the default position and direction.

cpp
// PrimaryGeneratorAction.cc
#include "PrimaryGeneratorAction.hh"
#include "G4ParticleGun.hh"
#include "G4ParticleTable.hh"
#include "G4Proton.hh"
#include "G4SystemOfUnits.hh"
#include "G4Event.hh"
PrimaryGeneratorAction::PrimaryGeneratorAction()
: G4VUserPrimaryGeneratorAction(),
  fParticleGun(nullptr)
{
  // Number of primary particles per event
  G4int nParticle = 1;
  fParticleGun = new G4ParticleGun(nParticle);
  // Get the proton definition
  G4ParticleDefinition* proton
    = G4ParticleTable::GetParticleTable()->FindParticle("proton");
  fParticleGun->SetParticleDefinition(proton);
  // Set kinetic energy, direction, and initial position
  fParticleGun->SetParticleEnergy(150.*MeV);
  fParticleGun->SetParticleMomentumDirection(G4ThreeVector(0., 0., 1.));
  fParticleGun->SetParticlePosition(G4ThreeVector(0., 0., -5.*cm));
}
PrimaryGeneratorAction::~PrimaryGeneratorAction()
{
  delete fParticleGun;
}
void PrimaryGeneratorAction::GeneratePrimaries(G4Event* event)
{
  // Create primary vertex and primary particle for this event
  fParticleGun->GeneratePrimaryVertex(event);
}

This configuration gives you a monoenergetic pencil beam, centered on the $z$ axis, that enters the water phantom from upstream. You can later adjust the starting $z$ position once your phantom geometry is fixed.

Always check that your initial proton position is outside the phantom volume. Starting inside the phantom can lead to incorrect entrance conditions and makes it harder to interpret depth‑dose data.

Beam Direction and Alignment with the Phantom

For depth‑dose studies the beam direction is usually perpendicular to the entrance surface of the phantom. In the previous code the direction (0, 0, 1) assumes that the water phantom is placed so that its entrance face is orthogonal to the $z$ axis.

If in your geometry the phantom is oriented differently, for example its entrance face is perpendicular to $x$, you adjust the momentum direction accordingly, for example (1, 0, 0). The choice must be consistent with how you defined the phantom in DetectorConstruction.

In some studies you might want to tilt the beam or scan over angles. In that case you still use SetParticleMomentumDirection, but with vectors representing the desired beam angle. For example a beam 10 degrees tilted in the $xz$ plane can be set using a normalized direction vector derived from simple trigonometry:
$$
\mathbf{\hat{u}} = (\sin\theta, 0, \cos\theta),
$$
with $\theta = 10^\circ$ converted to radians.

Beam Position and Lateral Profile

So far the beam position was fixed at (0, 0, -5*cm), which describes a pencil beam of zero transverse size. To approximate a realistic clinical beam or experimental spot, you often give it a finite lateral spread, for example a Gaussian profile in $x$ and $y$.

You can implement this directly in GeneratePrimaries. One simple way is to use the Geant4 random number generator and sample a Gaussian distribution for each event:

cpp
#include "Randomize.hh"
// In GeneratePrimaries:
G4double sigmaX = 2.*mm;
G4double sigmaY = 2.*mm;
G4double x0 = G4RandGauss::shoot(0., sigmaX);
G4double y0 = G4RandGauss::shoot(0., sigmaY);
G4double z0 = -5.*cm;
fParticleGun->SetParticlePosition(G4ThreeVector(x0, y0, z0));
fParticleGun->GeneratePrimaryVertex(event);

Here sigmaX and sigmaY are the beam standard deviations in $x$ and $y$. This gives you a Gaussian beam spot centered on the origin at the phantom entrance. You can also shift the mean if you want an off‑axis beam.

When adding a transverse spread, always check that the full spot, for example within $3\sigma$, still lies inside the phantom transverse dimensions. Otherwise some protons will miss the water and your depth‑dose curve will not represent the intended field.

Beam Energy and Energy Spread

For an idealized Bragg peak study you may want a strictly monoenergetic beam. In that case the constructor setting

cpp
fParticleGun->SetParticleEnergy(150.*MeV);

is all you need. However, real beams have an energy spread, often approximated by a Gaussian distribution around a nominal energy $E_0$ with standard deviation $\sigma_E$.

To model this, you can again use G4RandGauss::shoot inside GeneratePrimaries:

cpp
G4double E0     = 150.*MeV;
G4double sigmaE = 1.*MeV;
G4double E = G4RandGauss::shoot(E0, sigmaE);
if (E < 0.) E = 0.;  // protect against pathological random values
fParticleGun->SetParticleEnergy(E);

The size of the spread depends on the accelerator and beamline. For demonstration and simple exercises, you can choose a small spread that you later vary to observe its effect on the Bragg peak broadening.

The proton kinetic energy determines the Bragg peak depth. Even small changes in energy can shift the peak significantly. Keep careful track of the nominal energy and any spread you introduce.

Using Macros to Control Proton Beam Parameters

Although you define the proton beam in C++, you will usually want to adjust its parameters without recompiling. This is one of the main advantages of Geant4 macro commands.

When you base your primary generator on G4ParticleGun, Geant4 already provides standard UI commands in the /gun/ directory. For a proton beam these are particularly useful:

To set the particle to proton:
/gun/particle proton

To set the kinetic energy:
/gun/energy 150 MeV

To set the beam direction:
/gun/direction 0 0 1

To set the starting position:
/gun/position 0 0 -5 cm

You can place these commands in a macro file, for example beam.mac, which you run at startup. This allows you to switch between different beam energies or directions simply by editing the macro file, without changing the source code.

If you add your own wrapper commands, you can expose higher‑level parameters, such as a user‑defined beam sigma or energy spread, but that requires additional code that connects your PrimaryGeneratorAction data members to the Geant4 UI system. The basic idea is to define a messenger and associated commands, then let the messenger update variables such as sigmaX or sigmaE. For beginner examples you can often stay with the built‑in /gun/ commands.

When you change beam properties via macros, always reinitialize the run (/run/initialize or /run/reinitializeGeometry and /run/initialize as needed) before starting a new beamOn. Otherwise some changes may not take effect in a consistent way.

Extending the Beam Model for Advanced Studies

Once the basic proton beam is working and you can produce a clear Bragg peak in water, you can extend the beam model if your study requires more realism. Some common extensions are:

Introduce angular divergence by sampling small angles around the main beam direction, for example with a Gaussian distribution in the transverse angles.

Create a scanning beam by varying the beam position for groups of events, which can approximate pencil beam scanning in clinical proton therapy.

Use G4GeneralParticleSource instead of G4ParticleGun if you need more complex position, angular, or energy distributions defined from spectra or from external data files. The overall concept remains the same, but configuration is then driven more by macro commands in the /gps/ directory.

These refinements are not required to start studying basic proton transport in water. For the core example in this course, a monoenergetic or slightly broadened pencil beam, implemented as shown above, is sufficient and will clearly show the depth‑dose behavior and Bragg peak that you will analyze in the following chapters.

Views: 10

Comments

Please login to add a comment.

Don't have an account? Register now!