Rheon

17 A staged Rust implementation

Production should begin in Rust, with Zig and Odin following a stable behavioral contract. This research package is not that production solver. The checked standards snapshot dcc56f26e884ade260770beceba2501d3746200d emphasizes owned state, explicit failures and claim-matched evidence [S20].

Core ownership

GridShape validates dimensions, spacing, origin, strides and capacity. BoundarySnapshot owns classification and prescribed data for one version. FluidState owns accepted fields and time. Workspace owns reusable scratch. Stepper produces a candidate and diagnostics. A separate renderer consumes an immutable accepted snapshot.

The numerical core should not depend on GUI, model weights or windowing. A demonstration application is one consumer. Physics configuration and presentation configuration remain separate.

Geometry and pressure first

Implement checked indexing, shapes, coordinates and boundary classification. Test uneven dimensions, minimum grids, overflow, every orientation and linear pressure ramps. Compare independently assembled small matrices with production stencils. Verify adjointness, nullspace and connected-component compatibility.

Then add a typed pressure solver, initially diagonal-preconditioned CG on a gauge-fixed system if selected. Exercise zero residual, incompatible flux, isolated cells, disconnected domains, invalid coefficients and budget exhaustion. Compare direct small solves and hand calculations. Compute residual and divergence through separate paths.

Binary64 simplifies diagnosis; binary32 may be required for memory but needs independent qualification. The real-arithmetic proofs select neither production precision nor hardware.

Transport and forces

Implement component-aware sampling, tracing, collisions and source scheduling. Bounded scalar transport precedes optional correction. Test constants, bounds, translation and limiter behavior. Gravity and constant viscosity follow with hydrostatic and decay tests. Projection and boundary reapplication follow the derived stage order, not buffer convenience.

A step publishes a complete accepted interval or an explicit incomplete outcome. Cancellation preserves accepted state and identifies completed physical time. Renderers must not see half-updated arrays.

Guidance and application

Export calibrated silhouette, depth and normals for primitives and state. Retain camera and state IDs so hosts cannot combine stale images with new metadata. A lagging renderer may be intentional but must consume a coherent snapshot.

Build the demonstration around the core. Acceptance exercises create, advance, pause, reset, parameter changes, cancellation and export. None is proved by the research scripts or Lean algebra.

Liquid and performance

Select an interface method after comparing volume drift, silhouette accuracy and memory. Add its boundary and surface tests before advertising liquid support. Then profile complete frames under intended host model allocation. Record hardware, build, scene, warm-up, variability and peak memory. Optimize measured contributors while retaining diagnostics.

Ports share fixtures, units, serialization and failure meanings. Matching signatures is insufficient. Stabilize Rust first so three languages do not evolve three interpretations of one equation.

Concrete type and function contracts

The following Rust-shaped interfaces are a design specification, not compiled library code. They make coordinate domains and ownership visible:

struct CellShape { nx: usize, ny: usize, nz: usize, len: usize }
struct FaceShape { axis: Axis, dims: [usize; 3], len: usize }
struct GridGeometry { cells: CellShape, faces: [FaceShape; 3],
                      origin: [f64; 3], spacing: [f64; 3] }
struct BoundarySnapshot { version: u64, cell_kind: Vec<CellKind>,
                          face_data: [Vec<FaceBoundary>; 3] }
struct FaceField { x: Vec<f32>, y: Vec<f32>, z: Vec<f32> }
struct FluidState { id: u64, time: f64, velocity: FaceField,
                    tracer: Vec<f32> }
struct PressureWorkspace { p: Vec<f64>, rhs: Vec<f64>,
                           residual: Vec<f64>, direction: Vec<f64>,
                           product: Vec<f64>, preconditioned: Vec<f64> }

Private constructors validate lengths and finite geometry. A CellIndex cannot be constructed from an unchecked integer through the public API. FaceIndex includes axis or comes from an axis-specific view. Avoid retaining both an authoritative len and independently mutable dimensions; derived capacities remain immutable after construction.

A proposed apply_pressure_operator takes immutable topology, immutable pressure and a mutable output slice of validated length. It must overwrite every active output, including isolated or boundary rows according to classification. A divergence function consumes velocity and the same boundary snapshot but uses an independent direct face-flux traversal for validation. A project function returns a PressureOutcome containing diagnostics and never silently reclassifies invalid geometry.

A sample_velocity function accepts a world position and boundary policy and returns either a finite vector with validity metadata or a typed sample failure. Advection accepts immutable old fields and separate outputs. A step function accepts a physical interval and returns either an accepted snapshot or a failure carrying the last complete time. It must not expose mutable workspace through callbacks.

Storage and invariants

Use x-fastest contiguous arrays initially. For cells, strides are 1,nx,nxny1,n_x,n_xn_y; for x-faces they are 1,nx+1,(nx+1)ny1,n_x+1,(n_x+1)n_y. The y- and z-face strides follow their own dimensions. Validate the multiplication when building shapes, then retain strides for kernel use.

Boundary data should distinguish fluid neighbor, prescribed normal speed, prescribed pressure and inactive face. A floating coefficient of zero is not enough to encode all four meanings. PressureTopology owns reduced indices, component IDs, gauge choices and face transmissibilities. Its version depends on geometry, density policy and active-fluid classification.

State publication increments a version only after diagnostics accept the candidate. A renderer borrows an immutable accepted snapshot. Reset constructs a new coherent state rather than zeroing arrays piecemeal while consumers read them. Parameter changes that alter array shape or topology invalidate dependent workspace and preconditioners explicitly.

Executable acceptance fixtures

The companion provides expected mathematical results for a production test port:

Add integration fixtures for cancellation after each stage, resource rejection before mutation and stale boundary versions. Those are not mathematical experiments; they require the actual Rust state owner and host boundary.

Dependency and implementation choices

Use established sparse and numerical libraries for reference solves where licensing and deployment allow, while retaining a transparent matrix-free production operator if justified by the memory contract. Pin toolchains and dependencies for qualification, and record the exact compiler and feature set used in numerical comparisons.

No unsafe code is required for the first bounded CPU implementation. Introduce unsafe indexing or SIMD only after profiling identifies a meaningful contributor and the validated shape contract supports a reviewed safety argument. Bounds-check elimination by the compiler is preferable to a manually duplicated unchecked path when performance is equivalent.

GPU kernels require a separate buffer-layout and dispatch contract, including alignment, workgroup limits, synchronization and readback. A CPU test cannot establish a shader’s binding layout or race freedom. Keep backend-independent fixtures but qualify each backend through its real execution path before advertising support.

Current material and desktop owners

The Rust-shaped interfaces earlier in this chapter remain a design specification. The current repository also has compiled owners with narrower, explicit contracts. ParticleFluidSimulation owns the bounded material preview, while the original carrier simulation and its pressure implementations remain a separate selectable mode. Switching the desktop’s material mode changes the physical model; it is not a different preconditioner for the same pressure problem.

A desktop run takes an immutable copy of settings and creates one joinable worker with a scoped Rayon pool. That worker exclusively owns the numerical simulation. The renderer consumes immutable accepted PreviewFrame values published to history; it never borrows candidate particles. Editing settings for the next run therefore does not reinterpret the physical scale of frames already recorded. The desktop contract documents startup, cancellation, joining and retained history.

Follow one particle step through publication

The material owner first validates the requested interval and emitter. It chooses an interval constrained by its acoustic, motion and material scales, copies accepted particles to candidate storage, and prepares source parcels using candidate reservoir credits. Neighbor bins, density and forces are then computed from that candidate. A force check can request preparation again at a shorter interval. There are at most 16 preparation attempts in one call; exhausting them returns an explicit stability failure.

Only a successful preparation commits the particle arrays, accepted clock, generation, source credits and emission-sequence state. This last pair matters: if a cancelled attempt kept its source credit or disk-sampling progress, a retry could inject a different amount of material or emit it elsewhere even when the visible particles had been restored. Scratch may change during a failed attempt; accepted state must remain coherent. The material tests exercise cancellation, capacity failure and retry through the real public owner.

After commit, the worker captures the accepted state for display. Scrubbing history selects an earlier immutable frame while new frames arrive; Follow live selects the newest accepted frame. The material image splats parcels onto its stored preview grid. It is a visualization, not a full numerical checkpoint from which the particle state can be recovered exactly.

A small acceptance sequence for the new owners

Begin with a vacuum scene with no source and zero gravity: there should be no particle mass to display. A filled scene instead starts with mass determined by its physical volume and reference density. Enable the source, record actual accepted time and emitted mass, then cancel and inspect the retained frames. The measurable question is whether retained mass equals initial mass plus accepted emitted mass within the declared floating tolerance. A plausible jet image alone cannot answer it.

Next keep the physical inputs fixed while changing worker count. Compare accepted fields and reports through the numerical fixtures, not only preview pixels. The run-owned CpuScheduler may choose different batch boundaries from timing feedback, but that does not authorize different per-item arithmetic or reordered reductions. Finally raise only preview resolution and check the stated payload budget: numerical parcel spacing should remain fixed. These three exercises isolate physical state, execution order and display cost.

The material-preview contracts and CPU-scheduling contracts give the bounded formulas, source links and proof boundaries. They do not upgrade a native desktop preview into a new browser solver or establish equivalence to the paper designs earlier in this chapter.

Draft checkpoint: borrow one accepted transport snapshot

Unmerged PR48, reviewed at f06a1270935b2ac285c68c0e594ab0742220913e, adds an engine-neutral SimulationSession for the existing full-box transport path. It owns the boundary workspace and borrows unchanged native step inputs for each synchronous call. A snapshot binds grid, publication number, immutable state, identity and the last accepted report. Stages distinguish constructor state from an accepted interval. Pressure is the last interval’s projection pressure with its existing gauge; it is not a reconstructed end-surface pressure.

Try the draft’s headless and game-loop examples. Predict which clock should advance after a CFL-shortened request: the host must use accepted report dt, not requested dt. Next cancel an attempt and compare the published frame before retry. The draft’s contract tests check publication preservation, retry, quotas and equivalence to existing encoding. Deadlines are checked cooperatively at native stage checkpoints and around callbacks; a long kernel or callback can overrun them. They provide no preemption, maximum wall time or frame-rate guarantee.

The snapshot borrows its owner. Retention requires an explicit consumer copy or encoding; there is no hidden frame archive. Stop is terminal but leaves inspection available. Caller-supplied run/revision strings are declarations, not verified provenance. Encoded frames alone do not satisfy the separately validated dense3D dataset-publication contract. The API is transport-only: it does not yet wrap PR47 particle diagnostics, PR45 experimental Stokes, CAD measurement or AI providers, and the Rust game-loop sample is not a Godot binding. These are separate unmerged drafts; the current embedded browser lesson continues to use its existing adapter.

Chapter introduction

Loading 3D…