Repository navigation
r.sim: Lock the infiltration capacity of a cell - #8001
Merged
Merged
Conversation
With nprocs > 1, walkers on different threads checked and reduced the remaining infiltration capacity of a cell without synchronization. This is a data race: two walkers could both be absorbed by the capacity left for one, or a reduction could be lost. Run the unchanged infiltration code in a critical section, entered only when an atomic read shows capacity left in the cell, so that the lock is taken only for the few cells which still have some. Within the lock, the code checks the capacity again, as another thread may have used it up. With one thread, the depth maps computed from the elev_lid792_1m elevation, with and without infiltration, are byte-identical to those before this change. With more threads, the walkers still reach a cell in an order which depends on thread scheduling, as before. Written with the help of Claude Code.
With infiltration only in cells where it rains, and at a lower rate than the rain, the walkers created in each cell use up its infiltration capacity in the first step, before any walker moves, and the capacity is not restored. The total water accumulated over the simulation is then the fraction (rain - infiltration) / rain of the total without infiltration, independent of the walker paths and the number of threads. Check that fraction for 1 and 4 threads. The test checks the infiltration accounting, not the infiltration race: a cell's walkers have consecutive indices and are processed by one thread while the capacity lasts, so two threads rarely reach the same cell's capacity before it is used up. Layouts with capacity where walkers from many threads arrive did not show the race either. ThreadSanitizer does detect it. Written with the help of Claude Code.
petrasovaa
approved these changes
Oct 2, 2026
petrasovaa
left a comment
Contributor
There was a problem hiding this comment.
The change makes sense. For testing it could reuse an existing test to save time.
wenzeslaus
enabled auto-merge (squash)
October 2, 2026 19:49
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
When r.sim.water runs with nprocs > 1 and infiltration, two threads can check and use the same cell's infiltration capacity at the same time, so a cell can absorb more water than its capacity, or lose track of what it absorbed. The unsynchronized access is undefined behavior in C.
This runs the existing infiltration code unchanged in a critical section, entered only for cells which still have capacity. Single-threaded maps stay byte-identical to main, and the run time is the same within noise. Please review the diff with whitespace changes hidden: the original code only gets indented, and what remains is the lock, the flag replacing the two
continuestatements, and the two atomic writes.Details: approach, physics, measurements, and tests (AI generated)
omp atomic read. Only when some is left, it entersomp critical(infiltration), in which the original code runs, starting with its own check of the capacity, since another thread may have used it up.continuestatements set a flag checked after the critical section, becausecontinuecannot leave it, and that the two writes to the capacity are atomic, because the read before the lock is outside it.landclass96, 201 of 496,626 active walkers in step 2 and 2 of 347,836 in step 710; with infiltration above the rain on most cells, 88 of 25,706 in step 2.infil_value=50, and with bothlandclass96maps below. All tests pass.Timing
Doc example (
elev_lid792_1m, 525k cells and walkers,rain_value=100,duration=5), median and range of 3 interleaved runs. I0: no infiltration. I50:infil_value=50. L1:landclass96forest 150, other 50. L2: forest 50, other 120. Water cells 0 in both.The machine load varied during the runs; the differences go both ways and the ranges overlap except for I0 at nprocs=4, where this PR differs from main only by an atomic read.
Squash-merge commit message
Title:
Body:
The fix and the measurements were done with AI assistance (Claude Code with Opus 5.5) and reviewed by me.