Wavefront Sensing

Overview

The wavefront sensing (centroiding) component in daolite (Durham Adaptive Optics Latency Inspection and Timing Estimator) handles the calculation of wavefront slopes from Shack-Hartmann sensor images. These slopes represent the local tilts of the wavefront across the pupil and are crucial inputs for wavefront reconstruction in adaptive optics systems.

Key Centroiding Features

  • Cross-Correlation Algorithm: Optimized for extended sources like in solar AO

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

  • Flexibility: Support for different subaperture configurations and detector parameters

  • GPU Acceleration: Models for GPU-accelerated centroiding operations

Using Centroiding Components

Adding a centroider to your AO pipeline:

from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.pipeline.centroider import Centroider
from daolite.compute import hardware
import numpy as np

# Create a pipeline
pipeline = Pipeline()

# Define a GPU resource for centroiding
gpu = hardware.nvidia_rtx_4090()

# Define centroid agenda (how many subapertures per iteration)
n_valid_subaps = 6400
n_groups = 50
centroid_agenda = np.ones(n_groups, dtype=int) * (n_valid_subaps // n_groups)

# Add centroider component
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,  # 16×16 pixels per subaperture
        "n_workers": 1,
        "sort": False
    },
    dependencies=["Pixel Calibration"]  # Depends on output from calibration
))

Centroiding Functions

daolite provides several centroiding functions for different wavefront sensor types:

Point Source Centroiding (Shack-Hartmann)

The Centroider function is used for standard Shack-Hartmann wavefront sensors with point sources:

from daolite.pipeline.centroider import Centroider

# Add Shack-Hartmann centroider to pipeline
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CENTROIDER,
    name="SH Centroider",
    compute=gpu,
    function=Centroider,
    params={
        "centroid_agenda": centroid_agenda,  # Processing agenda
        "n_pix_per_subap": 16*16,            # Pixels per subaperture
        "n_workers": 1,                       # Parallel workers
        "sort": False,                        # Sorting (for brightest pixel)
        "flop_scale": 1.0,                    # FLOP scaling factor
        "mem_scale": 1.0                      # Memory scaling factor
    }
))

Extended Source Centroiding

For extended sources (solar AO), use the extended source centroider module:

from daolite.pipeline.extended_source_centroider import ExtendedSourceCentroider

# Add extended source centroider
pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CENTROIDER,
    name="Extended Source Centroider",
    compute=gpu,
    function=ExtendedSourceCentroider,
    params={
        "centroid_agenda": centroid_agenda,
        "n_pix_per_subap": 20*20,
        "n_combine": 4  # Number of images to combine
    }
))

Practical Examples

Example 1: High-Order Adaptive Optics System

Centroider configuration for a high-order AO system with many subapertures:

import numpy as np

# High-order AO centroider for 85×85 subapertures
n_valid_subaps = 85 * 85
n_groups = 100  # Process in 100 groups
centroid_agenda = np.ones(n_groups, dtype=int) * (n_valid_subaps // n_groups)

pipeline.add_component(PipelineComponent(
    component_type=ComponentType.CENTROIDER,
    name="High-Order Centroider",
    compute=gpu,
    function=Centroider,
    params={
        "centroid_agenda": centroid_agenda,
        "n_pix_per_subap": 16*16,  # 16×16 pixels per subaperture
        "n_workers": 1,
        "sort": False
    },
    dependencies=["Pixel Calibration"]
))

Performance Considerations

Several factors affect centroiding performance:

Algorithm Complexity

The cross-correlation algorithm has computational complexity:

  • Correlation: O(n log n) with FFT or O(n²) without FFT

Subaperture Count

More subapertures increase compute load linearly for most algorithms, but may have additional overhead for data transfers.

Pixels Per Subaperture

Increasing pixel count per subaperture affects performance:

  • Correlation methods: Quadratic scaling in naive implementation, n log n with FFT

GPU Acceleration Factors

When using GPU acceleration, consider:

  • Data Transfer: Time to move pixel data to GPU memory

  • Kernel Launch Overhead: Fixed overhead per GPU kernel launch

  • Parallelism: GPUs benefit from high subaperture counts that can utilize all cores

  • Memory Pattern: Coalesced memory access patterns are crucial for performance

Example: Performance Analysis

import matplotlib.pyplot as plt
import numpy as np
from daolite import Pipeline, PipelineComponent, ComponentType
from daolite.compute import hardware
from daolite.pipeline.centroider import Centroider

# Compare performance across different subaperture counts
def compare_centroider_performance():
    # Create GPU resource
    gpu = hardware.nvidia_rtx_4090()

    # Subaperture counts to test
    subap_counts = [20*20, 40*40, 60*60, 80*80, 100*100]
    pixels_per_subap = 16*16

    # Store results
    centroid_times = []

    # Test each subaperture count
    for n_subaps in subap_counts:
        # Define centroid agenda
        n_groups = 50
        centroid_agenda = np.ones(n_groups, dtype=int) * (n_subaps // n_groups)

        # Test Centroider
        pipeline = Pipeline()
        pipeline.add_component(PipelineComponent(
            component_type=ComponentType.CENTROIDER,
            name="Centroider",
            compute=gpu,
            function=Centroider,
            params={
                "centroid_agenda": centroid_agenda,
                "n_pix_per_subap": pixels_per_subap,
                "n_workers": 1,
                "sort": False
            }
        ))
        results = pipeline.run()
        centroid_times.append(results["Centroider"].duration)

    # Plot results
    plt.figure(figsize=(10, 6))
    plt.plot(subap_counts, centroid_times, 's-', label='Centroider')
    plt.xlabel('Number of Subapertures')
    plt.ylabel('Execution Time (microseconds)')
    plt.title('Centroiding Performance vs. Subaperture Count')
    plt.legend()
    plt.grid(True)
    plt.savefig('centroider_performance.png')
    plt.show()

# Run the comparison
compare_centroider_performance()

Troubleshooting

Common issues and solutions:

  • High Latency: - Reduce pixels per subaperture - Use FFT-based correlation instead of direct computation - Ensure input data is optimally arranged in memory

  • GPU Memory Limitations: - Process subapertures in batches - Reduce template or search region size - Use a more memory-efficient algorithm

  • Poor Accuracy: - For extended sources, correlation methods typically outperform simpler methods - Adjust thresholds or weighting parameters

API Reference

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