SCAO Calibration Tutorial
This tutorial demonstrates how to calibrate an interaction matrix for a Single Conjugate Adaptive Optics (SCAO) system using SynIM, integrated with a SPECULA simulation.
Note
This tutorial focuses on interaction matrix computation. It assumes you have already configured Modal basis or influence functions and ShSubapCalibrator (see SPECULA calibration documentation)
Prerequisites
Before starting this tutorial, you should have:
SPECULA simulation configuration: A working YAML file defining your SCAO system (telescope, atmosphere, WFS, DM, etc.)
Python environment with SPECULA and SynIM installed
Basic knowledge of adaptive optics concepts
System Overview
We’ll use a simple SCAO configuration with:
Telescope: 1m diameter (64 pixels @ 0.015625 m/pixel)
Deformable Mirror: Ground-conjugated (height = 0 m), 40 Zernike modes
Wavefront Sensor: 8×8 Shack-Hartmann, 600 nm wavelength
Guide Star: On-axis Natural Guide Star (NGS)
YAML Configuration Files
The SPECULA SCAO calibration workflow uses three YAML files:
Base simulation (
params_scao.yml): Main system configurationSubaperture calibration (
params_scao_subap.yml): WFS geometry calibrationInteraction matrix calibration (
params_scao_rec.yml): IM computation settings
Here we will focus on SynIM which requires only the first parameter file for interaction matrix generation. A prerequisite is the subaperture geometry calibration (Step 1) which produces a data object that is referenced in the main YAML file. For a description of the subaperture calibration, see the SPECULA SCAO basic tutorial.
Base Simulation Configuration
The main configuration file defines the complete SCAO system. Below is an excerpt showing the key components for interaction matrix calibration:
# params_scao.yml (excerpt)
main:
class: 'SimulParams'
root_dir: './calib/'
pixel_pupil: 64
pixel_pitch: 0.015625 # meters
total_time: 0.010
time_step: 0.001
on_axis_source:
class: 'Source'
polar_coordinates: [0.0, 0.0] # On-axis
magnitude: 5
wavelengthInNm: 600
pupilstop:
class: 'Pupilstop'
simul_params_ref: 'main'
sh:
class: 'SH'
subap_on_diameter: 8
subap_wanted_fov: 4.0 # arcsec
sensor_pxscale: 0.5 # arcsec/pix
subap_npx: 8
wavelengthInNm: 600
inputs:
in_ef: 'prop.out_on_axis_source_ef'
detector:
class: 'CCD'
simul_params_ref: 'main'
size: [64, 64]
dt: 0.001
bandw: 300
quantum_eff: 0.3
photon_noise: True
readout_noise: True
readout_level: 1.0
inputs:
in_i: 'sh.out_i'
slopec:
class: 'ShSlopec'
subapdata_object: 'scao_subaps_n8_th0.5' # From Step 1
weightedPixRad: 4.0
inputs:
in_pixels: 'detector.out_pixels'
dm:
class: 'DM'
simul_params_ref: 'main'
type_str: 'zernike'
nmodes: 40
obsratio: 0.1
height: 0 # Ground conjugated
inputs:
in_command: 'control.out_comm'
Note
This is a simplified configuration showing only the components needed for interaction matrix computation. A complete SCAO simulation would also include:
atmo: Atmospheric turbulence layersprop: Wavefront propagationrec: Mode reconstructorcontrol: AO loop controller (e.g., integrator)psf: PSF computation for performance metrics
See the full example file for a complete configuration.
Workflow Steps
Step 1: Generate Subaperture Geometry
First, calibrate the WFS subaperture geometry using SPECULA. For a description of this step, see the SPECULA SCAO basic tutorial.
Step 2: Compute Interaction Matrix with SynIM
Now compute the same IM using SynIM’s synthetic approach:
from synim.params_manager import ParamsManager
base_yml = 'params_scao.yml'
# Initialize ParamsManager
params_mgr = ParamsManager(
base_yml,
root_dir=calib_dir,
verbose=True
)
# Compute interaction matrix
print("Computing IM with SynIM...")
synim_im = params_mgr.compute_interaction_matrix(
wfs_type='ngs', # WFS type: 'ngs', 'lgs', or 'ref'
wfs_index=None, # Auto-detect first NGS WFS
dm_index=1, # DM index from YAML
verbose=True,
display=False # Set True to visualize
)
print(f"Interaction matrix shape: {synim_im.shape}")
print(f" Slopes: {synim_im.shape[0]}")
print(f" Modes: {synim_im.shape[1]}")
Output:
Interaction matrix shape: (100, 40)
Slopes: 100 # 2 × (number of valid subapertures)
Modes: 40 # Number of DM modes
Note
SynIM automatically selects the optimal computation workflow (SEPARATED or COMBINED) based on your system geometry. For on-axis SCAO with no WFS transformations, the SEPARATED workflow is typically used.
For details on workflow selection logic and when each is optimal, see Computation Workflows in the General Documentation.
By default, slope extraction uses slope_method='derivatives'.
If needed, you can use the optional telescoping sum mode by passing slope_method='telsum' to low-level synim.interaction_matrix() or synim.interaction_matrices_multi_wfs() calls.
Step 3: Generate Reconstruction Matrix
Create the reconstructor from the interaction matrix:
from specula.data_objects.intmat import Intmat
from specula.data_objects.recmat import Recmat
# Create Intmat object
intmat_obj = Intmat(
synim_im,
pupdata_tag='scao_synim_n8_th0.5',
norm_factor=1.0
)
# Save interaction matrix
im_output = os.path.join(calib_dir, 'im', 'scao_im_synim.fits')
intmat_obj.save(im_output, overwrite=True)
print(f"Saved IM: {im_output}")
# Generate reconstruction matrix (pseudoinverse)
recmat_obj = intmat_obj.generate_rec(
nmodes=40, # Number of modes to reconstruct
cut_modes=0, # Modes to exclude (e.g., piston)
interactive=False # Set True for interactive mode selection
)
# Save reconstruction matrix
rec_output = os.path.join(calib_dir, 'rec', 'scao_rec_synim.fits')
recmat_obj.save(rec_output, overwrite=True)
print(f"Saved reconstructor: {rec_output}")
print(f"Reconstructor shape: {recmat_obj.recmat.shape}")
Use in SPECULA Simulation
Update your YAML to reference the computed reconstructor:
rec:
class: 'Modalrec'
recmat_object: 'scao_rec_synim' # Without .fits extension
inputs:
in_slopes: 'slopec.out_slopes'
outputs: ['out_modes']
Advanced Topics
WFS Transformations
For systems with WFS rotation or pupil shifts:
sh:
class: 'SH'
# ... other parameters ...
rotAnglePhInDeg: 15.0 # WFS rotation [degrees]
xShiftPhInPixel: 5.0 # X shift [pixels]
yShiftPhInPixel: 2.0 # Y shift [pixels]
SynIM automatically handles these transformations during IM computation.
Example: Test with rotation
# Use rotated configuration
base_yml_rot = 'params_scao_rot.yml'
params_mgr_rot = ParamsManager(
base_yml_rot,
root_dir=calib_dir,
verbose=True
)
synim_im_rot = params_mgr_rot.compute_interaction_matrix(
wfs_type='ngs',
dm_index=1
)
GPU Acceleration
For large systems, enable GPU:
import synim
synim.init(device_idx=0, precision=1) # GPU 0, single precision
# ParamsManager will automatically use GPU
params_mgr = ParamsManager(base_yml, root_dir=calib_dir)
Troubleshooting
Debug Mode
Enable detailed logging:
params_mgr = ParamsManager(
base_yml,
root_dir=calib_dir,
verbose=True # Detailed progress
)
im = params_mgr.compute_interaction_matrix(
wfs_type='ngs',
dm_index=1,
verbose=True,
display=True # Show intermediate plots
)
Summary
This tutorial covered:
YAML configuration for SCAO system
Subaperture calibration with SPECULA
Interaction matrix computation with SynIM
Reconstructor generation for closed-loop control
Advanced topics: transformations, GPU acceleration
For more complex systems (MCAO, LTAO), see:
MORFEO workflow example for multi-WFS tomographic calibration
Parameters Manager for API details
SPECULA Documentation for calibration prerequisites