|
| 1 | +# Grid |
| 2 | + |
| 3 | +The `xlb.grid` module provides the fundamental tools for defining and managing the structured, Cartesian grids used in Lattice Boltzmann simulations. A `Grid` object represents the spatial layout of your simulation domain and is the first component you typically create when setting up a simulation. |
| 4 | + |
| 5 | +It is tightly integrated with the selected compute backend, ensuring that all data structures are allocated on the correct device (e.g., a JAX device or an NVIDIA GPU for Warp). |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Creating a Grid |
| 10 | + |
| 11 | +The primary way to create a grid is with the `grid_factory` function. This function automatically returns the correct grid object (`JaxGrid` or `WarpGrid`) based on the selected compute backend. |
| 12 | + |
| 13 | +```python |
| 14 | +from xlb.grid import grid_factory |
| 15 | +from xlb.compute_backend import ComputeBackend |
| 16 | +import xlb |
| 17 | + |
| 18 | +# Assuming xlb.init() has been called with a default backend... |
| 19 | + |
| 20 | +# Create a 2D grid for the JAX backend |
| 21 | +grid_2d = grid_factory(shape=(500, 500), compute_backend=ComputeBackend.JAX) |
| 22 | + |
| 23 | +# Create a 3D grid for the Warp backend |
| 24 | +grid_3d = grid_factory(shape=(256, 128, 128), compute_backend=ComputeBackend.WARP) |
| 25 | +``` |
| 26 | + |
| 27 | +### `grid_factory(shape, compute_backend)` |
| 28 | +- **`shape: Tuple[int, ...]`**: A tuple defining the grid dimensions. For example, `(nx, ny)` for 2D or `(nx, ny, nz)` for 3D. |
| 29 | +- **`compute_backend: ComputeBackend`**: The backend to use (`ComputeBackend.JAX` or `ComputeBackend.WARP`). If not provided, it defaults to the backend set in `xlb.init()`. |
| 30 | + |
| 31 | + |
| 32 | +--- |
| 33 | + |
| 34 | +## Using a Grid Instance |
| 35 | + |
| 36 | +Once you have a `grid` object, you can use its attributes and methods to define boundary regions and create data fields for your simulation. |
| 37 | + |
| 38 | +### Attributes |
| 39 | + |
| 40 | +- **`grid.shape`**: The full domain shape passed during creation (e.g., `(256, 128, 128)`). |
| 41 | +- **`grid.dim`**: The number of spatial dimensions, inferred from the shape (2 for 2D, 3 for 3D). |
| 42 | + |
| 43 | +### Methods |
| 44 | + |
| 45 | +#### `grid.bounding_box_indices()` |
| 46 | + |
| 47 | +This is a crucial helper method for defining boundary conditions. It returns a dictionary containing the integer coordinates for each face of the grid's bounding box. |
| 48 | + |
| 49 | +```python |
| 50 | +faces = grid_2d.bounding_box_indices(remove_edges=True) |
| 51 | + |
| 52 | +# faces is a dictionary with keys: 'bottom', 'top', 'left', 'right' |
| 53 | +inlet_indices = faces["left"] |
| 54 | +outlet_indices = faces["right"] |
| 55 | + |
| 56 | +# Combine multiple faces to define all stationary walls |
| 57 | +wall_indices = faces["bottom"] + faces["top"] |
| 58 | +``` |
| 59 | + |
| 60 | +- **`remove_edges: bool = False`**: If set to `True`, the corner/edge nodes where faces meet are excluded. This is highly recommended to prevent applying conflicting boundary conditions to the same node (e.g., treating a corner as both a "left" wall and a "bottom" wall). |
| 61 | + |
| 62 | +#### `grid.create_field()` |
| 63 | + |
| 64 | +This method allocates a data array (a "field") on the grid, using the appropriate backend (e.g., `jax.numpy` array or `warp` array). This is used to create storage for all simulation data, such as the particle distribution functions ($f_i$) or macroscopic quantities ($\rho, \vec{u}$). |
| 65 | + |
| 66 | +```python |
| 67 | +# Create a field to store the D2Q9 particle distribution functions (f_i) |
| 68 | +# Cardinality is 9 because there are 9 velocities in D2Q9. |
| 69 | +f = grid_2d.create_field(cardinality=9) |
| 70 | + |
| 71 | +# Create a scalar field for density (rho) |
| 72 | +rho = grid_2d.create_field(cardinality=1) |
| 73 | + |
| 74 | +# Create a 2D vector field for velocity (u) |
| 75 | +u = grid_2d.create_field(cardinality=2) |
| 76 | +``` |
| 77 | + |
| 78 | +- **`cardinality: int`**: The number of data values to store at each grid node. For a scalar field like density, `cardinality=1`. For a vector field like 2D velocity, `cardinality=2`. For the LBM particle populations $f_i$, `cardinality` is equal to $q$, the number of discrete velocities in your `VelocitySet`. |
| 79 | +- **`dtype: Precision = None`**: The numerical precision for the field data (e.g., `Precision.FP32`). Defaults to the global precision policy. `Precision.BOOL` is only supported by `JaxGrid`. |
| 80 | +- **`fill_value: float = None`**: An optional value to initialize all elements of the array. Defaults to `0`. |
| 81 | + |
| 82 | +--- |
| 83 | + |
| 84 | +## Backend-Specific Details |
| 85 | + |
| 86 | +### `JaxGrid` |
| 87 | +The `JaxGrid` object is designed to be compatible with JAX's features, including multi-device parallelization via sharding. |
| 88 | + |
| 89 | +### `WarpGrid` |
| 90 | +The `WarpGrid` is optimized for single-GPU execution with NVIDIA Warp. For 2D grids, it automatically adds a singleton `z` dimension (e.g., a `(500, 500)` shape becomes a `(500, 500, 1)` array). This is done to maintain consistency, allowing the same Warp kernels to be used for both 2D and 3D simulations. |
0 commit comments