Stochastic Monte Carlo Ray Tracing system for ocean optical sensor simulation built in Rust with Python bindings.
The writeup for this project is available at in the docs/caerula_writeup_capstone.pdf file. The presentation slides are also available at docs/caerula_presentation_slides.pdf.
Since this is a library for research purposes it does not have built wheels available for use just yet. So the process to use this library with its python bindings is a bit of an involved project. The process should be as follows:
Create a virtual environment (recommended)
python -m venv venv
source venv/bin/activate # On Windows use `venv\Scripts\activate`Then install maturin in that environment
pip install maturinAfter that you can build and install the python bindings with
maturin develop --releaseNote: the above command must be run where the pyproject.toml file is located which is caerula-py.
This creates an editable install of the python bindings in your virtual environment. You can then use the library in your python scripts or jupyter notebooks by importing caerula_py.
from caerula_py import CaerulaEngineSome basic usage examples can be found in the jupyter_notebook folder.
Note: the jupyter notebook requires plotly and pandas to be installed in the environment.
Example usage:
from caerula_py import CaerulaEngine
import pandas as pd
# Initialize the engine
# num_rays per batch
engine = CaerulaEngine(name="basic_scene", num_rays=500000, max_bounces=10, max_dist=100.0)
# Set medium properties
engine.set_medium(turbidity=10, chla=40)
# Add a sunlight source
# Position is relative, direction is normalized vector
engine.set_sunlight(
name="sun",
position=(0.0, 2.0, 0.0),
direction=(0.0, -1.0, 0.0),
intensity=1000.0,
spread_angle=30 # degrees
)
# Add a sensor
engine.add_sensor(
name="upward_sensor",
position=(0.0, 0.0, 0.0),
direction=(0.0, 1.0, 0.0), # Looking up
dimensions=(0.5, 0.5, 0.5),
half_angle=45.0, # degrees
min_w=400.0,
max_w=700.0,
bin_size=5.0
)
# Callback for convergence
def on_step(i, err):
if i % 5 == 0:
print(f"Step {i}: Max Std Err = {err:.6f}")
if err < 0.01 and i > 100: # Convergence threshold
print(f"Converged at step {i} with error {err:.6f}")
return True
return False
# Run simulation
engine.run(max_iters=1000, callback=on_step)
# Collect results
results = []
for sensor_name, spectrum in engine.results.items():
for wl, intensity in spectrum:
results.append({"Wavelength (nm)": wl, "Intensity": intensity, "Sensor": sensor_name})
df = pd.DataFrame(results)
display(df)Below is the current project structure, please note that this README is a work in progress and the project structure may change as development continues.
.
├── caerula-cli \\ Command line interface for testing and running scenes
│ ├── Cargo.toml
│ └── src
│ ├── config.rs
│ └── main.rs
├── caerula-engine \\ Core ray tracing engine
│ ├── Cargo.toml
│ ├── engine.puml
│ └── src
│ ├── core \\ Core engine components
│ │ ├── constants.rs
│ │ ├── mod.rs
│ │ ├── scene.rs
│ │ └── simrunner.rs
│ ├── elements \\ Scene elements like sensors, media, sources
│ │ ├── medium.rs
│ │ ├── mod.rs
│ │ ├── sensor.rs
│ │ ├── source.rs
│ │ └── sunlight.rs
│ ├── geometry \\ Geometric primitives and spatial structures
│ │ ├── mod.rs
│ │ ├── simbox.rs
│ │ ├── spectralray.rs
│ │ └── triangle.rs
│ └── lib.rs
├── caerula-py \\ Python bindings for the engine
│ ├── Cargo.toml
│ ├── pyproject.toml
│ ├── python
│ │ └── caerula_py
│ │ └── __init__.py
│ └── src
│ └── lib.rs
├── Cargo.toml
├── example.json
├── jupyter_notebook \\ Jupyter notebook examples
│ ├── main.ipynb
│ └── test.py
└── README.md
The project is structured into three main components:
caerula-engine: The core ray tracing engine implemented in Rust.caerula-cli: A command-line interface for testing and running scenes in Rust.caerula-py: Python bindings for the engine to allow easy integration and usage in Python environments.
The engine uses a Monte Carlo ray tracing approach to simulate light propagation in underwater environments. Naively this model traces from the source to the sensor, but this is very inefficient since most light does not reach the sensor. However, this approach is easier to conceptualize and easier to adapt existing mathematical models for underwater light propagation for.
The scattering can be depicted as in the figure below:
Rays are traced from the light source through the medium, undergoing scattering and absorption events based on the medium properties. When rays intersect with sensors, their contributions are recorded to build up the sensor's received spectrum. A flow chart of the ray's path is shown below:
Refer to the writeup PDF for more details on the implementation and algorithms used along with more detailed references.
- No surface model yet (just medium)
- No refraction / reflection at medium boundaries
- CPU bound, not optimized for GPU
- Not available as a package yet
- No complex geometries yet (just boxes)
- Naive implementation, lots of optimizations possible
The primary source used for the mathematical model of this project was https://www.oceanopticsbook.info/view/introduction/overview


