21.2. Setting Random Seeds
Table of Contents
Reproducible simulations
For Monte Carlo simulations, reproducibility means that you can re-run exactly the same code and obtain exactly the same sequence of “random” numbers, and therefore the same physics results, as long as nothing relevant has changed. In Geant4 this is controlled mainly by the choice of random engine and the random seed.
Geant4 uses random number engines provided by CLHEP, such as CLHEP::HepRandom, CLHEP::RanluxEngine, or CLHEP::MTwistEngine. The engine generates a deterministic sequence of numbers, but the starting point in that sequence, the seed, determines which particular sequence you get. If you use the same engine and the same seed, the sequence of random numbers will be identical on each run.
You normally set the seed before you start a run. After that, Geant4 uses the engine internally for all stochastic choices, such as interaction points, scattering angles, secondary energies, and so on. If you change the seed between runs, you get statistically independent runs. If you keep it fixed, you can reproduce the run later.
To obtain reproducible results you must keep all of the following identical:
- Geant4 version and compiler.
- Random engine type.
- Random seed.
- Geometry, physics list, materials, and user code.
- Number of events, and their primary configurations.
Changing any of these can change the event history even if the seed is the same.
Setting seeds in C++ code
The most direct way to control seeds is in your main program, before you create and use the run manager. A typical pattern with the CLHEP random interface is:
#include "Randomize.hh" // CLHEP / Geant4 random interface
int main(int argc, char** argv)
{
// Choose a random engine once at the start of the program
CLHEP::HepRandom::setTheEngine(new CLHEP::MTwistEngine);
// Set a fixed seed for reproducibility
long seed = 123456; // any positive integer
CLHEP::HepRandom::setTheSeed(seed);
// or set multiple seeds for engines that support it
// long seeds[2] = {123456, 654321};
// CLHEP::HepRandom::setTheSeeds(seeds);
// Now construct the run manager and the rest of the application
auto* runManager = new G4RunManager;
// ... user initialization classes ...
runManager->Initialize();
// Start a run
runManager->BeamOn(1000);
delete runManager;
return 0;
}The exact engine you choose is not important for a beginner, but you should pick one engine and stick to it if you care about reproducing previous results. If you later switch from one engine to another, the same seed will give a completely different sequence.
For some engines, especially those based on long integer states, you can also set an array of seeds with setTheSeeds(). This can provide a larger state space, but for most simple uses a single call to setTheSeed() is enough.
Always set the random engine and seed before you create or initialize the G4RunManager. Otherwise some parts of Geant4 might already have consumed random numbers using default settings, and you will not get reproducible behavior.
Setting seeds with macro commands
Geant4 also lets you configure the random number engine and seeds using macro commands. This is convenient when you want to change seeds without recompiling. These commands must be executed before you initialize the run.
You can place them in a macro file that you run at startup, or type them interactively in the Geant4 UI:
# Choose the random engine
#/random/setEngineName Ranlux64
/random/setEngineName MTwist
# Set a single seed
/random/setSeeds 123456
# Or set multiple seeds (depends on engine)
/random/setSeeds 123456 654321 789012
# Initialize the run after setting the seeds
/run/initializeThe exact list of available engines and the syntax may depend on your Geant4 version, so you should check the Geant4 Application Developers Guide for the current set of supported engine names.
The macro approach is particularly useful when performing parameter scans or batch runs where each run needs a different seed. Instead of recompiling for each seed, you can prepare different macro files, or pass a seed via an environment variable and write a short macro that uses it.
When using macro commands for seeds, make sure that:
- You call
/random/setEngineNameand/random/setSeedsbefore/run/initialize. - You do not also set conflicting seeds in C++ after these commands.
Use either a C++ configuration or a macro configuration for each run to avoid confusion.
Choosing seed values
For reproducing a specific run, you should record the exact seed that you used. The seed can be any positive integer within the range supported by the engine, so the particular number does not matter, but you must store it in your run logs, together with other configuration information.
If you want each run to be different, you need a way to choose different seeds each time. There are several approaches:
You can hard-code a different seed in each macro file, such as seed_1.mac, seed_2.mac, and so on. You can derive the seed from the system time, for example in C++ by using time(NULL) or a higher resolution clock. You can pass a seed on the command line to your program, parse it, and call setTheSeed() with that value.
A simple example of reading a seed from the command line is:
#include "Randomize.hh"
#include <cstdlib>
int main(int argc, char** argv)
{
CLHEP::HepRandom::setTheEngine(new CLHEP::MTwistEngine);
long seed = 123456; // default
if (argc > 1) {
seed = std::strtol(argv[1], nullptr, 10);
}
CLHEP::HepRandom::setTheSeed(seed);
// ... create run manager, initialize, beamOn ...
}You can then run:
./myApp 1001
./myApp 1002
./myApp 1003
Each execution uses a different seed, but if you later run ./myApp 1002 again, you will reproduce that particular random sequence.
Avoid using a seed of 0 unless you are sure the engine supports it. Some engines interpret 0 as a request to choose a seed automatically, which may prevent strict reproducibility.
Recording seeds for later use
To make your simulations reproducible, it is not enough to set seeds. You also need a record of which seed was used for which run, so that you can repeat it months or years later.
A practical approach is to store the seed in your run metadata. For example, in your RunAction you can store the current seed in a text file, or include it in the name of your output file. This way you can quickly find the seed that corresponds to a given set of results.
You can retrieve the current seed or seeds from the CLHEP engine using methods such as getTheSeed() or getTheSeeds(), depending on the engine. Then you can print or save them at the start of each run.
If you combine this with documentation of your Geant4 version and the random engine type, you build a complete description that allows you to recreate the same random sequence in the future.
Seeds and multithreading
In multithreaded Geant4 applications, there is one master thread and several worker threads. Each worker thread needs its own independent sequence of random numbers. Geant4 handles this by giving each thread its own random engine state. The initial seeds for the workers are derived from the master seed in a way that reduces correlations.
For reproducible multithreaded runs, you only need to set the master seed once at the start, in C++ or with macros. As long as the number of threads, the random engine, and the seed are the same, Geant4 will assign the same per-thread seeds and you will obtain the same results.
However, you should not expect bit-perfect reproducibility when you change the number of threads. The order of events and random number usage can change, which leads to different sequences even with the same master seed. The run will still be statistically equivalent, but not identical.
In multithreaded runs:
- Set the master seed once, before creating or configuring worker threads.
- Do not manually assign seeds to individual threads unless you fully understand Geant4’s threading model.
- Keep the number of threads fixed if you want strict reproducibility of a past run.
Summary
To control random numbers in Geant4 you choose a random engine, set a seed before initializing the run manager, and record that seed together with your run configuration. Using the same engine and seed with identical code and configuration will reproduce the same random sequence and the same simulation behavior. For independent runs you vary the seed. For multithreaded runs you set a single master seed and let Geant4 manage per-thread seeds, while keeping the number of threads fixed if you need exact reproducibility.
Views: 10
KAHIBARO