Gromacs
2025dev20241003bd59e46

Provides efficient and portable random generators and distributions.
GROMACS relies on random numbers in several different modules, and in particular for methods that influence the integration we both require the generation to be very fast and the resulting numbers of high quality. In addition, it is highly desirable that we generate the same trajectories in parallel as for a singlecore run.
To realize this, we have implemented the ThreeFry2x64 counterbased random engine. In contrast to a normal random engine that is seeded and then keeps an internal state, ThreeFry2x64 is derived from cryptographic applications where we use a key to turn a highly regular counter int a stream of random numbers. This makes it possible to quickly set the counter in the random engine based e.g. on the timestep and atom index, and get the same random numbers regardless of parallelization.
The TreeFry2x64 engine has been implemented to be fully compatible with standard C++11 random engines. There is a gmx::ThreeFry2x64General class that allows full control over the accuracy (more iterations means higher quality), and gmx::ThreeFry2x64 and gmx::ThreeFry2x64Fast that are specialized to 20 and 13 iterations, respectively. With 20 iterations this engine passes all tests in the standard BigCrush test, and with 13 iterations only a single test fails (in comparision, Mersenne Twister fails two).
All these engines take a template parameter that specifies the number of bits to reserve for an internal counter. This is based on an idea of John Salmon, and it makes it possible to set your external counter based on two simple values (usually timestep and particle index), but then it is still possible to draw more than one value for this external counter since the internal counter increments. If you run out of internal counter space the class will throw an exception to make sure you don't silently end up with corrupted/overlapping random data.
We've thought about that. Just use the gmx::DefaultRandomEngine class and forget everything about counters. Initialize the class with a single value for the seed (up to 64 bits), and you are good to go.
The ThreeFry random engine is fully compatible with all distributions from the C++11 standard library, but unfortunately implementation differences (and bugs) mean you will typically not get the same sequence of numbers from two different library implementations. Since this causes problems for our unit tests, we prefer to use our own implementations  they should work exactly like the corresponding C++11 versions.
The normal distribution is frequently used in integration, and it can be a performance bottleneck. To avoid this, we use a special tabulated distribution gmx::TabulatedNormalDistribution that provides very high performance at the cost of slightly discretized values; the default 14bit table gives us 16,384 unique values, but this has been thoroughly tested to be sufficient for all integration usage.
Files  
file  exponentialdistribution.h 
The exponential distribution.  
file  gammadistribution.h 
The gamma distribution.  
file  normaldistribution.h 
The normal distribution.  
file  seed.h 
Random seed and domain utilities.  
file  tabulatednormaldistribution.h 
Tabulated normal distribution.  
file  exponentialdistribution.cpp 
Tests for GROMACS exponential distribution.  
file  gammadistribution.cpp 
Tests for GROMACS gamma distribution.  
file  normaldistribution.cpp 
Tests for GROMACS normal distribution.  
file  seed.cpp 
Tests for random seed functions.  
file  tabulatednormaldistribution.cpp 
Tests for GROMACS tabulated normal distribution.  
file  threefry.cpp 
Tests for the ThreeFry random engine.  
file  uniformintdistribution.cpp 
Tests for GROMACS uniform integer distribution.  
file  uniformrealdistribution.cpp 
Tests for GROMACS uniform real distribution.  
file  threefry.h 
Implementation of the 2x64 ThreeFry random engine.  
file  uniformintdistribution.h 
The uniform integer distribution.  
file  uniformrealdistribution.h 
The uniform real distribution.  