Wavefront Reconstruction

Overview

The wavefront reconstruction component in daolite (Durham Adaptive Optics Latency Inspection and Timing Estimator) is responsible for converting wavefront slope measurements into commands for deformable mirrors. This is a critical computational step in adaptive optics systems, translating Shack-Hartmann wavefront sensor measurements into the shape required for the deformable mirror to correct wavefront aberrations.

Key Reconstruction Features

  • Single Algorithm: Implementation of the Minimum Variance Reconstruction (MVR) technique

  • Performance Modeling: Accurate timing estimates based on algorithm complexity and hardware capabilities

  • GPU Acceleration: Models for GPU-accelerated reconstruction operations

  • Tomographic Reconstruction: Planned support for multi-conjugate and multi-object AO configurations

Using Reconstruction Components

Adding a reconstructor to your AO pipeline:

from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.pipeline.reconstruction import Reconstruction
from daolite.compute import hardware
import numpy as np

# Create a pipeline
pipeline = Pipeline()

# Define a GPU resource for reconstruction
gpu = hardware.nvidia_a100_80gb()

# Define centroid agenda (matching centroider output)
n_valid_subaps = 6400
n_groups = 50
centroid_agenda = np.ones(n_groups, dtype=int) * (n_valid_subaps // n_groups)

# Add reconstructor component
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.RECONSTRUCTOR,
    name="Reconstructor",
    compute=gpu,
    function=Reconstruction,
    params={
        "centroid_agenda": centroid_agenda,
        "n_acts": 81*81,  # 81×81 actuator grid
        "n_workers": 1
    },
    dependencies=["Centroider"]
))

Reconstruction Configuration

The reconstruction component accepts the following parameters:

params={
    # Required parameters
    "centroid_agenda": centroid_agenda,  # Processing agenda (np.ndarray)
    "n_acts": 81*81,                     # Number of actuators

    # Optional parameters
    "flop_scale": 1.0,    # FLOP scaling factor (default: 1.0)
    "mem_scale": 1.0,     # Memory scaling factor (default: 1.0)
    "n_workers": 1,       # Number of parallel workers (default: 1)
    "debug": False        # Enable debug output (default: False)
}

Understanding Reconstruction

The Reconstruction function models the computational latency of matrix-vector multiplication (MVM) operations that convert wavefront slopes into actuator commands. This corresponds to the operation:

\[\begin{split}\\mathbf{a} = \\mathbf{R} \\cdot \\mathbf{s}\end{split}\]

where:

  • \(\\mathbf{s}\) is the slope vector (from centroiding)

  • \(\\mathbf{R}\) is the reconstruction matrix (e.g., pseudo-inverse of interaction matrix)

  • \(\\mathbf{a}\) is the actuator command vector

The function uses an agenda-based API where you specify how many slopes to process in each iteration through the centroid_agenda parameter.

Practical Examples

Example: Complete AO Pipeline with Reconstruction

from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.pipeline.camera import PCOCamLink
from daolite.pipeline.calibration import PixelCalibration
from daolite.pipeline.centroider import Centroider
from daolite.pipeline.reconstruction import Reconstruction
from daolite.compute import hardware
import numpy as np

# Create pipeline
pipeline = Pipeline()

# Define compute resources
cpu = hardware.amd_epyc_7763()
gpu = hardware.nvidia_a100_80gb()

# System parameters
n_subaps = 74 * 74
n_acts = 75 * 75
n_pixels = 1024 * 1024

# Define agendas
n_groups = 50
centroid_agenda = np.ones(n_groups, dtype=int) * (n_subaps // n_groups)
pixel_agenda = np.ones(n_groups, dtype=int) * (n_pixels // n_groups)

# Add camera
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CAMERA,
    name="Camera",
    compute=cpu,
    function=PCOCamLink,
    params={"n_pixels": n_pixels, "group": n_groups, "readout": "rolling"}
))

# Add calibration
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CALIBRATION,
    name="Pixel Calibration",
    compute=cpu,
    function=PixelCalibration,
    params={"pixel_agenda": pixel_agenda, "bit_depth": 16},
    dependencies=["Camera"]
))

# Add centroider
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CENTROIDER,
    name="Centroider",
    compute=gpu,
    function=Centroider,
    params={"centroid_agenda": centroid_agenda, "n_pix_per_subap": 16*16},
    dependencies=["Pixel Calibration"]
))

# Add reconstructor
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.RECONSTRUCTOR,
    name="Reconstructor",
    compute=gpu,
    function=Reconstruction,
    params={
        "centroid_agenda": centroid_agenda,
        "n_acts": n_acts
    },
    dependencies=["Centroider"]
))

# Run and analyze
results = pipeline.run()
print(f"Reconstruction time: {results['Reconstructor'].duration:.2f} µs")

Performance Considerations

Several factors affect reconstruction performance:

Matrix Size

The reconstruction matrix size scales with:

  • Number of slopes: Each subaperture produces 2 slopes (x and y), so \(n_{slopes} = 2 \\times n_{subaps}\)

  • Number of actuators: Typically \(n_{acts} = (n_{subaps}^{1/2} + 1)^2\) for a square DM

The computational complexity is \(O(n_{slopes} \\times n_{acts})\) for matrix-vector multiplication.

GPU Acceleration

When using GPU acceleration, consider:

  • Memory Bandwidth: Reconstruction operations are often memory-bound

  • Data Transfers: GPU memory transfers should be minimized

  • Parallelism: GPUs excel with large matrix operations that utilize all cores

Tuning Performance

You can tune the performance model using the flop_scale and mem_scale parameters:

# Scale computational model
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.RECONSTRUCTOR,
    name="Tuned Reconstructor",
    compute=gpu,
    function=Reconstruction,
    params={
        "centroid_agenda": centroid_agenda,
        "n_acts": n_acts,
        "flop_scale": 1.2,  # Increase FLOPs by 20%
        "mem_scale": 0.8,   # Reduce memory ops by 20%
        "n_workers": 4      # Use 4 parallel workers
    },
    dependencies=["Centroider"]
))

Troubleshooting

Common performance considerations:

  • High Latency: Use GPU acceleration for large systems, consider reducing precision

  • Memory Limitations: For very large systems, GPU memory may be a constraint

  • Scaling: Computational complexity is \(O(n_{slopes} \\times n_{acts})\)

API Reference

For complete API details, see the Reconstruction API Reference section.