[GRASS-git] [OSGeo/grass] ad9ce6: r.sim: Give each walker random numbers of its own ...
Vaclav Petras
noreply at github.com
Tue Oct 6 20:59:06 PDT 2026
Branch: refs/heads/main
Home: https://github.com/OSGeo/grass
Commit: ad9ce6ba2d6d4f7c683c7e5d42186fb458d0d0a4
https://github.com/OSGeo/grass/commit/ad9ce6ba2d6d4f7c683c7e5d42186fb458d0d0a4
Author: Vaclav Petras <wenzeslaus at gmail.com>
Date: 2026-10-06 (Tue, 06 Oct 2026)
Changed paths:
M raster/r.sim/r.sim.sediment/main.c
M raster/r.sim/r.sim.sediment/r.sim.sediment.html
M raster/r.sim/r.sim.sediment/r.sim.sediment.md
A raster/r.sim/r.sim.sediment/tests/r_sim_sediment_random_test.py
M raster/r.sim/r.sim.sediment/tests/r_sim_sediment_summary_test.py
M raster/r.sim/r.sim.sediment/testsuite/data/reference_erdep.pack
M raster/r.sim/r.sim.sediment/testsuite/data/reference_sedflux.pack
M raster/r.sim/r.sim.water/main.c
M raster/r.sim/r.sim.water/r.sim.water.html
M raster/r.sim/r.sim.water/r.sim.water.md
M raster/r.sim/r.sim.water/tests/r_sim_water_landforms_test.py
A raster/r.sim/r.sim.water/tests/r_sim_water_random_test.py
M raster/r.sim/r.sim.water/tests/r_sim_water_summary_test.py
M raster/r.sim/r.sim.water/tests/r_sim_water_test.py
M raster/r.sim/r.sim.water/testsuite/data/depth_complex.pack
M raster/r.sim/r.sim.water/testsuite/data/depth_default.pack
M raster/r.sim/r.sim.water/testsuite/data/discharge_complex.pack
M raster/r.sim/r.sim.water/testsuite/data/discharge_default.pack
M raster/r.sim/r.sim.water/testsuite/test_r_sim_water.py
M raster/r.sim/simlib/hydro.c
M raster/r.sim/simlib/input.c
M raster/r.sim/simlib/output.c
M raster/r.sim/simlib/random.c
M raster/r.sim/simlib/simlib.h
M raster/r.sim/simlib/summary.c
Log Message:
-----------
r.sim: Give each walker random numbers of its own (#7980)
Each walker draws from a random number state of its own, placed on the
walker's stream of a bounded layout of the seed, instead of from the
shared generator. A walker's random numbers now depend only on the
seed and the walker's number, not on the thread which moves it or on
the order of the walkers, so walker paths are the same for any nprocs
as long as the depth-dependent diffusion and infiltration do not come
into play. The water depth and the sediment concentration still differ
slightly at nprocs > 1 because the order of the atomic additions to the
gama grid and the order in which walkers reach a cell with infiltration
capacity depend on the threads, which this does not change.
The streams hold 2 + 8 * steps + 48 values, over twice what a walker
draws on average: two values at placement and, per step, a normal pair
by the polar method, 8 / pi values on average, plus one in a cell of
the flow_control map (a trap in the code). The 8 per step hold the sum
over many steps, and the 48 allow a run of 24 rejected attempts within
one step, so that a walker exceeds its stream with a probability below
1e-16 and a run of 2^31 walkers, the most the int counts hold, with a
probability below 1e-6. In counted runs on the nc_spm elevation, the
most a walker drew over 145 or 158 steps was 628 values, at most 51% of
its stream. A run whose walkers and steps need more than one seed's
span provides gets a warning. The states are placed in parallel before
the walkers are, each a member of its walker's record, for which the
walkers get a type of their own, struct walker. A first version kept
the states in an array of their own beside the walkers, which cost 7 to
9% of the wall time at every thread count, likely for the extra stream
of memory reads and writes in each walker's step; in the record, the
state shares the cache line with the walker's position and weight and
the cost is gone. Without the contention on the shared generator,
nprocs > 1 is now faster than nprocs = 1: on the whole nc_spm elevation
(6 million walkers), 50 s at nprocs=1, 13 s at 4 and 4.8 s at 16,
against 68 s, 143 s and 159 s before. The build without OpenMP now
gives the same results as nprocs=1.
Without random_seed, the seed is generated, as in r.mapcalc, instead
of being 12345, and recorded in the history of the output maps and in
the -p summary, so that a run can be repeated. The results therefore
differ from run to run unless random_seed is set. The -s flag is
deprecated, and random_seed with -s is an error. The seed is parsed
with strtoll() and checked right after parsing, so a seed outside
-2^31 to 2^32 - 1 is an error instead of being silently reduced to 32
bits. The tool docs get a section on the random numbers, the seed and
reproducibility with more than one thread.
This change has the price of changed outputs also at nprocs=1, but
invests into results independent of the thread count where the model
allows it. The alternative, one state per thread, would keep the
values at nprocs=1 and give a different result for each thread count.
The outputs of r.sim.water and r.sim.sediment are sums over millions
of walkers, so a different realization of the same seed moves them
within their sampling noise, and from the user's perspective they do
not change much anyway. The gunittest references and the pinned
landform arrays are regenerated; the commands used reproduce the old
references exactly with the previous code.
Written with the help of Claude Code (Fable and Opus).
To unsubscribe from these emails, change your notification settings at https://github.com/OSGeo/grass/settings/notifications
More information about the grass-commit
mailing list