|
1 | | -# Operator |
| 1 | +# The Operator Concept |
2 | 2 |
|
3 | | -Base class for all operators, including collision, streaming, equilibrium, etc. |
4 | | -Responsible for handling compute backends like JAX and NVIDIA Warp. |
| 3 | +In XLB, an **Operator** is a fundamental building block that performs a single, well-defined action within a simulation. Think of operators as the "verbs" of the Lattice Boltzmann Method. Any distinct step in the algorithm, such as collision, streaming, or calculating equilibrium, is encapsulated within its own callable operator object. |
5 | 4 |
|
6 | | -## Overview |
7 | | - |
8 | | -The `Operator` class acts as the foundational interface for all lattice Boltzmann operators in XLB. It manages backend selection and provides a unified API to call backend-specific implementations transparently. It also facilitates registering backend implementations and handles precision policies and compute types. |
9 | | - |
10 | | -## Usage |
11 | | - |
12 | | -```python |
13 | | -from xlb.operator import Operator |
14 | | -from xlb.compute_backend import ComputeBackend |
15 | | -from xlb.precision_policy import PrecisionPolicy |
16 | | - |
17 | | -op = Operator( |
18 | | - velocity_set=None, # or specify velocity set |
19 | | - precision_policy=PrecisionPolicy.FP32FP32, |
20 | | - compute_backend=ComputeBackend.JAX, |
21 | | -) |
22 | | - |
23 | | -# Call the operator (calls backend-specific implementation) |
24 | | -result = op(some_input_data) |
25 | | -``` |
26 | | - |
27 | | -## Constructor |
| 5 | +--- |
28 | 6 |
|
29 | | -```python |
30 | | -Operator( |
31 | | - velocity_set=None, |
32 | | - precision_policy=None, |
33 | | - compute_backend=None |
34 | | -) |
35 | | -``` |
| 7 | +## Common Features (The Operator Base Class) |
36 | 8 |
|
37 | | -- velocity_set: (optional) Velocity set used by the operator; defaults to global config. |
38 | | -- precision_policy: (optional) Precision policy for compute and storage. |
39 | | -- compute_backend: (optional) Backend to run on (e.g., JAX or Warp). |
| 9 | +The `Operator` base class provides a unified interface and a set of shared features for all operators in XLB. This ensures that every part of the simulation behaves consistently, regardless of its specific function. When you use any operator in XLB, you can rely on the following features: |
40 | 10 |
|
41 | | -Raises `ValueError` if the specified backend is unsupported. |
| 11 | +#### 1. Backend Management |
| 12 | +Every operator is aware of the compute backend (`JAX` or `Warp`). When an operator is called, it automatically dispatches the execution to the correct, highly optimized backend implementation without any extra effort from the user. |
42 | 13 |
|
43 | | -## Methods and Properties |
| 14 | +#### 2. Precision Policy Awareness |
| 15 | +Operators automatically respect the globally or locally defined `PrecisionPolicy`. They handle the data types for computation (`.compute_dtype`) and storage (`.store_dtype`) transparently, helping to balance performance and numerical accuracy. |
44 | 16 |
|
45 | | -`register_backend(backend_name)` |
46 | | -Decorator to register backend implementations for subclasses. |
| 17 | +#### 3. Standardized Calling Convention |
| 18 | +All operators are **callable** (using `()`). This provides a clean, functional API that makes it simple to compose operators and build a custom simulation loop. |
47 | 19 |
|
48 | | -`__call__(*args, callback=None, **kwargs)` |
49 | | -Calls the appropriate backend method, matching the subclass and backend, and passes args/kwargs. |
50 | | -- callback (optional): Callable to be called with the result. |
| 20 | +### Conceptual Usage |
51 | 21 |
|
52 | | -`supported_compute_backend` |
53 | | -Returns a list of supported backend keys registered for this operator. |
| 22 | +While you rarely instantiate the base `Operator` directly, all its subclasses follow the same pattern of creation and use. You first instantiate a specific operator (e.g., for BGK collision), configuring it with a velocity set and policies. Then, you call that instance with the required data to perform its action. |
54 | 23 |
|
55 | | -`backend` |
56 | | -Returns the actual backend module (jax.numpy or warp), depending on the current backend. |
| 24 | +```python |
| 25 | +from xlb.operator.collision import BGKCollision # A concrete operator subclass |
| 26 | +from xlb.precision_policy import PrecisionPolicy |
| 27 | +from xlb.compute_backend import ComputeBackend |
57 | 28 |
|
58 | | -`compute_dtype` |
59 | | -Returns the compute data type (e.g., float32, float64) according to the precision policy and backend. |
| 29 | +# Instantiate a specific operator (e.g., for BGK collision) |
| 30 | +collision_op = BGKCollision( |
| 31 | + velocity_set=my_velocity_set, |
| 32 | + precision_policy=PrecisionPolicy.FP32FP32, |
| 33 | + compute_backend=ComputeBackend.JAX |
| 34 | +) |
60 | 35 |
|
61 | | -`store_dtype` |
62 | | -Returns the storage data type according to the precision policy and backend. |
| 36 | +# Call the operator with the required data |
| 37 | +# It automatically runs the JAX implementation in FP32. |
| 38 | +f_post_collision = collision_op(f_pre_collision, omega=1.8) |
| 39 | +``` |
63 | 40 |
|
64 | 41 | --- |
65 | 42 |
|
66 | | -## Precision Caster |
| 43 | +## Utility Operator: `PrecisionCaster` |
67 | 44 |
|
68 | | -The **PrecisionCaster** is a utility operator for converting lattice Boltzmann data between different numeric precisions. |
| 45 | +The `PrecisionCaster` is a prime example of a simple yet powerful utility operator provided by XLB. Its sole purpose is to convert the numerical precision of simulation data fields. |
69 | 46 |
|
70 | 47 | ### Overview |
71 | 48 |
|
72 | | -Precision plays an important role in balancing **accuracy** and **performance** during simulations. |
73 | | -For example, some steps may require high precision (`float64`) for stability, while others can run efficiently in lower precision (`float32`). |
| 49 | +Precision plays an important role in balancing **accuracy** and **performance**. Some simulation steps may require high precision (`float64`) for stability, while many others can run much faster in lower precision (`float32`), especially on GPUs. The `PrecisionCaster` handles this conversion seamlessly. |
74 | 50 |
|
75 | | -The `PrecisionCaster` operator handles this conversion seamlessly for both supported backends. |
| 51 | +### Use Cases |
76 | 52 |
|
77 | | -### Features |
| 53 | +- **Performance Optimization**: Run the bulk of a simulation in `FP32` for speed, but cast data to `FP64` before critical calculations. |
| 54 | +- **Mixed-Precision Workflows**: Adapt precision dynamically based on runtime stability needs. |
| 55 | +- **Memory Management**: Store data in a lower precision to reduce memory footprint, casting to a higher precision only when needed for computation. |
78 | 56 |
|
79 | | -- Converts distribution functions between precisions (e.g., FP32 → FP64). |
80 | | -- Available for **JAX** and **Warp** backends. |
81 | | -- Works transparently with any chosen velocity set (e.g., D2Q9, D3Q19, D3Q27). |
82 | | -- Can be used before or after key operators to ensure data is in the desired format. |
| 57 | +### Usage |
| 58 | +To use the `PrecisionCaster`, you create an instance configured with the *target* precision policy you want to convert to. You then apply this operator to a data field, and it will return a new field with the converted precision. |
83 | 59 |
|
84 | | -### Use Cases |
| 60 | +```python |
| 61 | +from xlb.operator import PrecisionCaster |
85 | 62 |
|
86 | | -- **Performance optimization**: Run most of the simulation in FP32 for speed, while critical calculations use FP64. |
87 | | -- **Mixed-precision workflows**: Adapt precision dynamically depending on stability needs. |
88 | | -- **GPU acceleration**: Exploit lower-precision compute on GPUs while preserving accuracy where needed. |
| 63 | +# Assume 'f_low_precision' is a field with FP32 data |
| 64 | +# Create a caster to convert data to a higher precision (FP64) |
| 65 | +caster_to_fp64 = PrecisionCaster( |
| 66 | + velocity_set=my_velocity_set, |
| 67 | + precision_policy=PrecisionPolicy.FP64FP64 # Target policy |
| 68 | +) |
| 69 | + |
| 70 | +# Apply the operator to cast the field |
| 71 | +f_high_precision = caster_to_fp64(f_low_precision) |
| 72 | +``` |
0 commit comments