Commit Graph
6 Commits
Author SHA1 Message Date
Christian Svensson 60fd0998f5 lbm-gpu: stream in place with the AA pattern
The collide and stream passes each moved the whole population field
through two buffers (304 bytes per cell update). The AA pattern (Bailey
et al. 2009; Wittmann et al. 2013) keeps one buffer: even steps collide
each cell in place with its directions swapped, odd steps read a cell's
incoming populations at its neighbours and write the outgoing ones where
the next step reads them, so each thread writes the addresses it read
and streaming happens inside the collision (152 bytes). A cell's
coordinates are computed once and neighbours wrap with a compare and an
add. The per-link wall rules gather their sources before a scatter
writes them, so no link overwrites another's post-collision value, and
the momentum exchange is taken in the gather of the step whose forces
are read back.

Options::streaming picks the layout; Auto, what a solve uses, streams
in place, which measured faster on every device tried: 1.5x on an RTX
4090, 1.85x on the GB10 and 1.45x on an M4 Max at periodic 128^3.
Options::profile times each kernel with timestamp queries for the
throughput test, which now runs both layouts. The verification tests
run in both layouts, with two new ones comparing fields, mass, forces
and heat with the CPU after odd and even step counts across submission
boundaries.

Part of #562.
2026-10-17 10:45:09 -04:00
Christian Svensson 74dce59c7d lbm-gpu: sum the Zou-He deviations before adding the weights
The open boundaries summed the tangential and outgoing populations as
deviations from their weights plus the weights (2/3 and 1/6), in single
precision; at low speed the weights round away the deviations' last
digits. A low-speed case through an inlet, an outlet and a slip face
came out 2.26e-5 of the largest speed from the CPU under DXC, against
the 2e-5 the tests allow. The deviations are now summed first and the
total weight, exactly 1, added once: the same Zou-He rule, so results
change only at the round-off level.

Part of #562.
2026-10-17 10:45:09 -04:00
Christian Svensson 99b0b0cfa4 lbm-gpu: the temperature lattice on the GPU
The D3Q7 scalar lattice steps on the device after the flow's step, in
its own A–B pair of buffers: BGK collision with the velocity the
momentum collision leaves in the moments buffer, pull streaming with
the links' rules read through each group's thermal condition
(anti-bounce-back for a held temperature, bounce-back for an adiabatic
face, a flux added to the bounced population), inlets at their held
temperature and outlets copying their neighbour. The populations are
stored as deviations from w_q T₀ so single precision keeps the digits
of the differences. The heat each link exchanged goes into the fourth
component of the momentum-exchange contributions and the group sums;
the buoyant acceleration −β g θ is read per cell in the collision, the
open boundaries and the moments. A temperature that stops being a
number ends the chunk with Error::Unstable like the flow's checks.

Flow exposes what the backend needs to build the device state (the
thermal settings and populations).

Verification against the CPU on lavapipe: plates held at 20 and 21
(temperatures within 1.4e-5 of the range, the wall heats within 6e-5
and both against k ΔT A / H), a heated channel with a parabolic inlet
carrying its temperature, a flux wall, a held wall and an outlet
(temperature 3e-7, heat 1e-7) and a buoyant cavity at Ra 10³
(velocity 1.3e-6, temperature 6e-7, heat 2.4e-6). The tests now fail
on a shader that does not compile instead of skipping; only a missing
device skips.

Part of #157.
2026-10-17 08:32:30 -04:00
Christian Svensson 939b64e660 lbm: a temperature lattice on the flow
A D3Q7 passive scalar rides on the D3Q19 flow: it is advected by the
cell velocity, diffuses with the fluid's thermal diffusivity (the
relaxation time follows from k / (rho c)), and feeds back into the
momentum through a per-cell Boussinesq force when the domain has an
expansion coefficient and gravity. Walls and obstacle faces hold a
temperature (anti-bounce-back), a heat flux (added to the bounced
population) or are adiabatic (bounce-back); velocity inlets carry the
held temperature in, pressure outlets copy their neighbour.

The heat crossing each face group is accumulated during streaming and
reported in physical units (W) in the result's heat histories, next to
the force histories; frames gain a temperature field and the stats
report the thermal relaxation time.

Closed-form checks: linear conduction between held plates, a flux wall
against q H / k with the wall's heat balance, an inlet temperature
carried through a plug flow, and a periodic blob that conserves its
total and returns after one lap. The de Vahl Davis cavity (Ra 1e3 and
1e4 against the published Nusselt numbers) is there as an ignored test
because it takes minutes.

The GPU backend declines domains with heat for now and asks for the
CPU; its kernels follow.

Part of #157.
2026-10-17 08:32:30 -04:00
Christian Svensson 1e0c21d088 lbm-gpu: one atomic per workgroup for the chunk's largest speed
Every cell did an atomicMax on the same word; a workgroup reduction first
leaves one per 256 cells. Same result, less contention on a single
address.

Part of #529.
2026-10-17 07:23:40 -04:00
Christian Svensson b123f31d47 lbm-gpu: the lattice Boltzmann step on the GPU, checked against the CPU
A new L7 crate: the CPU scheme's collision (TRT with Guo forcing), the
per-link streaming rules (bounce-back with Ladd's term, slip reflection,
open placeholders), the Zou–He boundaries and the momentum exchange per
force group as wgpu compute shaders, stepping the flow fieldcraft_lbm's
Flow prepared, through its Backend trait: populations stay on the device,
a chunk of steps is one submission, and only frames, forces and the
stability check come back. Populations are stored as deviations from
their weights, which keeps single precision honest: the verification
cases (Poiseuille with Λ = 3/16, Couette with wall forces, Taylor–Green,
a sphere's drag, a Zou–He channel) agree with the CPU to 2–5e-6 in
velocity, 1e-6 in density and 5e-5 in force, with those tolerances stated
in the tests. The rules are applied one thread per link rather than in a
per-cell loop, which lavapipe's JIT miscompiles (and which diverges on
real GPUs anyway). Native only for now; the browser registers nothing.

Part of #529.
2026-10-17 07:23:40 -04:00