simulation

Functions for differentiable fluid simulation.

All units are given in grid squares per unit time.

High-level API

The high-level simulation API consists of two functions:
  • simulation.simulate() – run a multi-timestep simulation starting from some initial conditions.

  • simulation.step() – advance a simulation by a single step, given the state at the last step.

Both are differentiable with respect to their vector field inputs, letting you backpropagate through the whole simulation process.

The high-level API is available through deltaflow.* as well as deltaflow.simulation.*.

simulation.simulate(timesteps: int, color: jax.numpy.ndarray, velocity: Optional[jax.numpy.ndarray] = None, force: Optional[jax.numpy.ndarray] = None, config: simulation.SimulationConfig = SimulationConfig(delta_t=0.05, density_coeff=1.0, diffusion_coeff=0.001, pressure_iterations=16), return_frames: bool = True, disable_progress_bar: bool = False) → Tuple[jax.numpy.ndarray, jax.numpy.ndarray]

Run a multi-timestep fluid simulation.

The value of return_frames determines what this function does:
  • If True (default), every frame’s color and velocity are returned. This enables animation.

  • If False, only the last frame’s color and velocity are returned. This is faster and supports computing gradients through the whole simulation.

All fields must match in their spatial dimensions.

Parameters
  • timesteps – The number of timesteps to run the simulation for.

  • color – The RGB color field of the fluid at the first frame. Shape: [y, x, 3].

  • velocity – The velocity field of the fluid at the first frame. Shape: [y, x, y/x]. If None (default), initialize a zero velocity field.

  • force – A static force field to apply at each frame. Shape: [y, x, y/x]. If None (default), initialize a zero force field.

  • config – The simulation’s physical configuration. Defaults to a reasonable default configuration.

  • return_frames – If True (default), return all color and velocity values as numpy arrays, with timesteps on axis 0. If False, return only the last color and velocity fields.

  • disable_progress_bar – If True, disable the printed progress bar.

Returns

  • color (jnp.ndarray) – If return_frames is False: the final color field, with shape [y, x, 3]. Otherwise: each frame’s color field, with shape [timesteps, y, x, 3].

  • velocity (jnp.ndarray) – If return_frames is False: the final velocity field, with shape [y, x, 2]. Otherwise: each frame’s velocity field, with shape [timesteps, y, x, 2].

simulation.step(color: jax.numpy.ndarray, velocity: jax.numpy.ndarray, force: jax.numpy.ndarray, pressure: jax.numpy.ndarray, config: simulation.SimulationConfig = SimulationConfig(delta_t=0.05, density_coeff=1.0, diffusion_coeff=0.001, pressure_iterations=16)) → Tuple[jax.numpy.ndarray, jax.numpy.ndarray, jax.numpy.ndarray]

Advance the simulation by a single timestep.

Supports differentiation through all arguments except config. All fields must match in their spatial dimensions.

This function is JITted, since compiling multiple steps together doesn’t yield much speedup. Using a different spatial resolution or config will trigger recompilation; doing this often will slow down simulation dramatically.

Parameters
  • color – The RGB color field of the fluid at the last frame. Shape: [y, x, 3].

  • velocity – The velocity field of the fluid at the last frame. Shape: [y, x, y/x].

  • force – A static force field to apply at this frame. Shape: [y, x, y/x].

  • pressure – The pressure field of the fluid at the last frame. Shape: [y, x]. If this was the first frame, zeroes may be passed instead.

  • config – The simulation’s physical configuration. Statically traced, so changing this argument triggers recompilation. Defaults to a reasonable default configuration.

Returns

  • color (jnp.ndarray) – The RGB color field of the fluid at the next frame. Shape: [y, x, 3].

  • velocity (jnp.ndarray) – The velocity field of the fluid at the next frame. Shape: [y, x, y/x].

  • pressure (jnp.ndarray) – The pressure field of the fluid at the next frame. Shape: [y, x].

class simulation.SimulationConfig(delta_t: float = 0.05, density_coeff: float = 1.0, diffusion_coeff: float = 0.001, pressure_iterations: int = 16)

A configuration object determining a simulation’s physical properties.

Parameters
  • delta_t (float) – The time elapsed in each timestep. Smaller values produce more accurate results but advance the simulation slower.

  • density_coeff (float) – A coefficient on fluid density. Higher values cause fluids to respond slower to pressure gradients. Values too far from 1.0 may produce unrealistic effects.

  • diffusion_coeff (float) – A coefficient on diffusion rate and viscosity. Higher values cause faster diffusion and greater viscosity; values should be very small. A value of 0.0 simulates an inviscid flow and simulates faster.

  • pressure_iterations (int) – The number of iterations used to compute pressure. Larger values produce more accurate results but simulate slower.

Low-level simulation functions

This API exposes the internals used for individual simulation components. Use at your own risk!

simulation._get_predecessor_coordinates(velocity: jax.numpy.ndarray, delta_t: float) → jax.numpy.ndarray

For each point on the grid, get the fractional coordinates of a particle that would move to the center of that gridsquare at the next timestep.

Parameters
  • velocity – The fluid velocity field. Shape: [y, x, y/x].

  • delta_t – The time elapsed in each timestep.

Returns

For each grid square, the (fractional) coordinates of the grid square that would move to its center at the next timestep. Shape: [y/x, y, x].

Return type

jnp.ndarray

simulation._advect(field: jax.numpy.ndarray, predecessor_coords: jax.numpy.ndarray) → jax.numpy.ndarray

Transport a vector field by reading values moving into the center of each square.

Parameters
  • field – The vector field to transport. Shape: [y, x, any].

  • predecessor_coords – The predecessor coordinates computed from the velocity. Shape: [y/x, y, x].

Returns

The field, advected by one timestep.

Return type

jnp.ndarray

simulation._divergence_2d(field: jax.numpy.ndarray) → jax.numpy.ndarray

Compute the divergence of a 2D vector field.

Parameters

field – Any 2D vector field. Shape: [y, x, 2].

Returns

The divergence of field as a scalar field. Shape: [y, x].

Return type

jnp.ndarray

simulation._compute_pressure(advected_velocity: jax.numpy.ndarray, pressure: jax.numpy.ndarray, pressure_iterations: int) → jax.numpy.ndarray

Compute the (unitless) pressure of a fluid.

Uses Jacobi iteration, initialized with the last frame’s pressure.

Parameters
  • advected_velocity – The fluid’s velocity field for this frame, advected but without pressure correction. Shape: [y, x, 2].

  • pressure – Last frame’s pressure field, used as an initial guess for the pressure solver. If no such estimate is available (e.g. for the first frame), a zero field may be passed. Shape: [y, x].

  • pressure_iterations – The number of iterations used to compute pressure. Must be static during JIT tracing.

Returns

The estimated pressure field for the fluid at this frame. Shape: [y, x].

Return type

jnp.ndarray

simulation._diffuse(field: jax.numpy.ndarray, diffusion_coeff: float, delta_t: float) → jax.numpy.ndarray

Average each value in a vector field closer to its neighbors to simulate diffusion and viscosity.

Parameters
  • field – The vector field to diffuse. Shape: [y, x, any].

  • diffusion_coeff – A coefficient determining the amount of diffusion at each frame. Must be static during JIT tracing.

  • delta_t – The time elapsed in each timestep. Must be static during JIT tracing.

Returns

field, with diffusion applied for this frame.

Return type

jnp.ndarray