synim 1.2.4__tar.gz → 2.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {synim-1.2.4 → synim-2.0.0}/PKG-INFO +2 -1
- {synim-1.2.4 → synim-2.0.0}/README.md +1 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/index.rst +1 -0
- synim-2.0.0/docs/api/registration.rst +42 -0
- {synim-1.2.4 → synim-2.0.0}/docs/conf.py +4 -0
- {synim-1.2.4 → synim-2.0.0}/docs/general.rst +63 -41
- {synim-1.2.4 → synim-2.0.0}/docs/index.rst +3 -0
- synim-2.0.0/docs/registration.rst +212 -0
- {synim-1.2.4 → synim-2.0.0}/docs/scripts/generate_api_docs.py +34 -0
- {synim-1.2.4 → synim-2.0.0}/docs/tutorials/mcao_calibration_tutorial.rst +1 -1
- synim-2.0.0/docs/tutorials/registration_morfeo_tutorial.rst +183 -0
- {synim-1.2.4 → synim-2.0.0}/docs/tutorials/scao_calibration_tutorial.rst +1 -3
- {synim-1.2.4 → synim-2.0.0}/examples/run_synim.py +2 -0
- {synim-1.2.4 → synim-2.0.0}/examples/run_synim_from_yaml_proj_aof.py +2 -0
- {synim-1.2.4 → synim-2.0.0}/synim/__init__.py +61 -11
- {synim-1.2.4 → synim-2.0.0}/synim/_version.py +3 -3
- {synim-1.2.4 → synim-2.0.0}/synim/params_manager.py +84 -43
- {synim-1.2.4 → synim-2.0.0}/synim/params_utils.py +40 -27
- synim-2.0.0/synim/registration/__init__.py +14 -0
- synim-2.0.0/synim/registration/analysis.py +330 -0
- synim-2.0.0/synim/registration/geometry.py +282 -0
- synim-2.0.0/synim/registration/model.py +283 -0
- synim-2.0.0/synim/registration/reconstruction.py +191 -0
- synim-2.0.0/synim/registration/viz.py +544 -0
- {synim-1.2.4 → synim-2.0.0}/synim/resource_monitor.py +4 -4
- synim-2.0.0/synim/synim.py +1017 -0
- {synim-1.2.4 → synim-2.0.0}/synim/synpm.py +30 -15
- {synim-1.2.4 → synim-2.0.0}/synim/utils.py +182 -126
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/PKG-INFO +2 -1
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/SOURCES.txt +31 -1
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/scm_file_list.json +31 -1
- synim-2.0.0/synim.egg-info/scm_version.json +8 -0
- synim-2.0.0/test/__init__.py +40 -0
- synim-2.0.0/test/data/generate_regression_reference.py +198 -0
- synim-2.0.0/test/data/regression_reference.npz +0 -0
- synim-2.0.0/test/test_analysis.py +239 -0
- synim-2.0.0/test/test_ascii_output.py +30 -0
- synim-2.0.0/test/test_component_loading.py +139 -0
- synim-2.0.0/test/test_dtype_and_fixes.py +211 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_extrapolation_cube.py +6 -1
- synim-2.0.0/test/test_fractional_subapertures.py +298 -0
- synim-2.0.0/test/test_geometry_vs_pixel_synim.py +184 -0
- synim-2.0.0/test/test_init.py +152 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_intmat.py +22 -17
- synim-2.0.0/test/test_memory.py +60 -0
- synim-2.0.0/test/test_model_from_params_manager.py +62 -0
- synim-2.0.0/test/test_model_geometry.py +157 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_morfeo_illumination.py +8 -6
- synim-2.0.0/test/test_morfeo_intmat.py +203 -0
- synim-2.0.0/test/test_multi_wfs_groups.py +78 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_projection.py +20 -12
- {synim-1.2.4 → synim-2.0.0}/test/test_rebin.py +10 -13
- synim-2.0.0/test/test_reconstruction.py +119 -0
- synim-2.0.0/test/test_registration_geometry.py +90 -0
- synim-2.0.0/test/test_regression_reference.py +120 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_rotshiftzoom.py +5 -1
- synim-2.0.0/test/test_sh_fractional_specula.py +143 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_sh_intmat.py +6 -6
- {synim-1.2.4 → synim-2.0.0}/test/test_slope_method.py +9 -6
- synim-2.0.0/test/test_slopes_inputs.py +64 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_subap_ordering_specula_vs_synim.py +15 -10
- synim-2.0.0/test/test_viz.py +168 -0
- synim-2.0.0/test/test_wfs_transformations.py +119 -0
- synim-1.2.4/synim/synim.py +0 -1245
- synim-1.2.4/synim.egg-info/scm_version.json +0 -8
- synim-1.2.4/test/__init__.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/.github/workflows/publish.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/.github/workflows/pythonpackage.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/.gitignore +0 -0
- {synim-1.2.4 → synim-2.0.0}/.readthedocs.yaml +0 -0
- {synim-1.2.4 → synim-2.0.0}/LICENSE +0 -0
- {synim-1.2.4 → synim-2.0.0}/calibration_workflows/calib_morfeo_simplified_subaps.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/calibration_workflows/params_morfeo_simplified.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/calibration_workflows/params_workflow_morfeo.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/calibration_workflows/run_calib_workflow_morfeo.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/params_manager.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/params_utils.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/synim.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/synpm.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/api/utils.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/configuration.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/installation.rst +0 -0
- {synim-1.2.4 → synim-2.0.0}/docs/requirements.txt +0 -0
- {synim-1.2.4 → synim-2.0.0}/examples/calib_aof_subaps.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/examples/params_aof.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/examples/run_synim_from_yaml_aof.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/pyproject.toml +0 -0
- {synim-1.2.4 → synim-2.0.0}/requirements.txt +0 -0
- {synim-1.2.4 → synim-2.0.0}/setup.cfg +0 -0
- {synim-1.2.4 → synim-2.0.0}/synim/convert_pro_file.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/dependency_links.txt +0 -0
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/requires.txt +0 -0
- {synim-1.2.4 → synim-2.0.0}/synim.egg-info/top_level.txt +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/data/cov_zern_100m_128p_8m_0.2r0_50L0.fits +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_lgs_alt_test.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_offaxis_alt_test.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_rot_test.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_shift_test.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test_rec.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test_subap.yml +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_covariance.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_multirate_lut.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_reconstructor.py +0 -0
- {synim-1.2.4 → synim-2.0.0}/test/test_sa_illumination.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: synim
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Synthetic interaction matrix generator for adaptive optics systems
|
|
5
5
|
Author-email: Guido Agapito <guido.agapito@inaf.it>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -53,6 +53,7 @@ It also supports GPU acceleration. Some of its functionalities are provided by [
|
|
|
53
53
|
- 💾 **SPECULA-compatible** FITS format for data exchange
|
|
54
54
|
- 🎯 **Smart caching** to minimize redundant computations
|
|
55
55
|
- 🚀 **GPU acceleration** via CuPy for high-performance computation
|
|
56
|
+
- 🧭 **Mis-registration geometry** (`synim.registration`): a geometric (not pixel-based) model of the relation between global (WFS/DM/guide-star) and local (per-pair) mis-registration in MCAO/GLAO systems, its inversion, and sensitivity/noise analysis - see the [documentation](https://synim.readthedocs.io/en/latest/registration.html)
|
|
56
57
|
|
|
57
58
|
## Documentation
|
|
58
59
|
|
|
@@ -19,6 +19,7 @@ It also supports GPU acceleration. Some of its functionalities are provided by [
|
|
|
19
19
|
- 💾 **SPECULA-compatible** FITS format for data exchange
|
|
20
20
|
- 🎯 **Smart caching** to minimize redundant computations
|
|
21
21
|
- 🚀 **GPU acceleration** via CuPy for high-performance computation
|
|
22
|
+
- 🧭 **Mis-registration geometry** (`synim.registration`): a geometric (not pixel-based) model of the relation between global (WFS/DM/guide-star) and local (per-pair) mis-registration in MCAO/GLAO systems, its inversion, and sensitivity/noise analysis - see the [documentation](https://synim.readthedocs.io/en/latest/registration.html)
|
|
22
23
|
|
|
23
24
|
## Documentation
|
|
24
25
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Mis-registration Geometry (registration)
|
|
2
|
+
========================================
|
|
3
|
+
|
|
4
|
+
Affine geometry primitives
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
.. automodule:: synim.registration.geometry
|
|
8
|
+
:members:
|
|
9
|
+
:undoc-members:
|
|
10
|
+
:show-inheritance:
|
|
11
|
+
|
|
12
|
+
Global mis-registration model (GuideStar, DM, WFS, System)
|
|
13
|
+
----------------------------------------------------------
|
|
14
|
+
|
|
15
|
+
.. automodule:: synim.registration.model
|
|
16
|
+
:members:
|
|
17
|
+
:undoc-members:
|
|
18
|
+
:show-inheritance:
|
|
19
|
+
|
|
20
|
+
Global-from-local reconstruction
|
|
21
|
+
--------------------------------
|
|
22
|
+
|
|
23
|
+
.. automodule:: synim.registration.reconstruction
|
|
24
|
+
:members:
|
|
25
|
+
:undoc-members:
|
|
26
|
+
:show-inheritance:
|
|
27
|
+
|
|
28
|
+
Sensitivity and noise-propagation analysis
|
|
29
|
+
------------------------------------------
|
|
30
|
+
|
|
31
|
+
.. automodule:: synim.registration.analysis
|
|
32
|
+
:members:
|
|
33
|
+
:undoc-members:
|
|
34
|
+
:show-inheritance:
|
|
35
|
+
|
|
36
|
+
Visualization
|
|
37
|
+
-------------
|
|
38
|
+
|
|
39
|
+
.. automodule:: synim.registration.viz
|
|
40
|
+
:members:
|
|
41
|
+
:undoc-members:
|
|
42
|
+
:show-inheritance:
|
|
@@ -11,6 +11,10 @@ import sys
|
|
|
11
11
|
import re
|
|
12
12
|
sys.path.insert(0, os.path.abspath('..'))
|
|
13
13
|
|
|
14
|
+
# autodoc imports the SynIM submodules, which require synim.init() first
|
|
15
|
+
import synim # noqa: E402
|
|
16
|
+
synim.init(device_idx=-1, precision=1)
|
|
17
|
+
|
|
14
18
|
project = 'SynIM'
|
|
15
19
|
copyright = '2025, Guido Agapito'
|
|
16
20
|
author = 'Guido Agapito'
|
|
@@ -74,55 +74,70 @@ SynIM is organized into several main modules:
|
|
|
74
74
|
|
|
75
75
|
**utils.py**
|
|
76
76
|
General utility functions:
|
|
77
|
-
|
|
77
|
+
|
|
78
78
|
- Array rebinning and masking
|
|
79
79
|
- Geometric transformations
|
|
80
80
|
- Zernike polynomials
|
|
81
81
|
- FITS I/O helpers
|
|
82
82
|
|
|
83
|
-
|
|
83
|
+
**registration/**
|
|
84
|
+
Unlike every module above, this does not compute a pixel/influence-
|
|
85
|
+
function interaction or projection matrix. It is a purely geometric
|
|
86
|
+
(2D affine) model of how a DM actuator grid lines up with a WFS
|
|
87
|
+
sub-aperture grid in a system with several DMs and WFSs at different
|
|
88
|
+
conjugation altitudes (MCAO/GLAO), following
|
|
89
|
+
`Agapito, Plantet & Heritier (2024) <https://doi.org/10.48550/arXiv.2406.15336>`_:
|
|
84
90
|
|
|
85
|
-
|
|
86
|
-
|
|
91
|
+
- ``geometry.py``: affine transforms (shift, rotation, magnification, anamorphosis)
|
|
92
|
+
- ``model.py``: ``GuideStar``, ``DM``, ``WFS`` and ``System`` - the global mis-registration model
|
|
93
|
+
- ``reconstruction.py``: global parameters from local measurements, by Gauss-Newton inversion
|
|
94
|
+
- ``analysis.py``: sensitivity, degeneracy and noise-propagation analysis
|
|
95
|
+
- ``viz.py``: altitude schematic, mis-registration table, top-down footprint views
|
|
87
96
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
**SEPARATED Workflow**
|
|
91
|
-
Used when transformations exist only in DM or WFS (not both):
|
|
92
|
-
|
|
93
|
-
**Process:**
|
|
94
|
-
|
|
95
|
-
1. Apply DM transformations to influence functions (rotation, magnification, source altitude projection)
|
|
96
|
-
2. Compute numerical derivatives or G-tilts with edge extrapolation
|
|
97
|
-
3. Apply WFS transformations to derivatives or G-tilts (rotation, translation, magnification)
|
|
98
|
-
4. Bin derivatives or G-tilts to subaperture resolution
|
|
99
|
-
5. Extract slopes for valid subapertures
|
|
100
|
-
|
|
101
|
-
**Advantages:**
|
|
102
|
-
|
|
103
|
-
- Single interpolation step per transformation type
|
|
104
|
-
- Maximum accuracy (no cumulative interpolation errors)
|
|
105
|
-
- Can reuse derivatives or G-tilts for multiple WFS with same geometry
|
|
97
|
+
See :doc:`registration` for a full introduction and
|
|
98
|
+
:doc:`tutorials/registration_morfeo_tutorial` for a worked example.
|
|
106
99
|
|
|
107
|
-
|
|
108
|
-
Used when both DM and WFS have transformations:
|
|
109
|
-
|
|
110
|
-
**Process:**
|
|
111
|
-
|
|
112
|
-
1. Combine all DM and WFS transformations into single composite operation
|
|
113
|
-
2. Apply combined transformation to influence functions in one interpolation step
|
|
114
|
-
3. Compute derivatives or G-tilts on final transformed grid
|
|
115
|
-
4. Bin and extract slopes
|
|
116
|
-
|
|
117
|
-
**Advantages:**
|
|
118
|
-
|
|
119
|
-
- Avoids double interpolation artifacts that occur when transformations are coupled
|
|
120
|
-
- Single interpolation step when both DM and WFS rotate/translate
|
|
121
|
-
- Consistent handling of complex geometric configurations
|
|
100
|
+
.. _computation_workflows:
|
|
122
101
|
|
|
123
|
-
|
|
102
|
+
Computation Workflows
|
|
103
|
+
~~~~~~~~~~~~~~~~~~~~~
|
|
124
104
|
|
|
125
|
-
|
|
105
|
+
**Interaction matrices**
|
|
106
|
+
All the geometric transformations (DM shift, rotation and magnification from
|
|
107
|
+
the guide star direction and height, WFS shift, rotation, magnification and
|
|
108
|
+
anamorphosis) are combined into a single affine transformation, applied to
|
|
109
|
+
the influence functions in one interpolation step:
|
|
110
|
+
|
|
111
|
+
1. Combine all DM and WFS transformations into a single composite operation
|
|
112
|
+
2. Apply it to the influence functions (the phase) in one interpolation step
|
|
113
|
+
3. Compute derivatives or G-tilts (telescoping sum) with edge repair on the
|
|
114
|
+
transformed grid
|
|
115
|
+
4. Bin to subaperture resolution and extract the slopes of the valid subapertures
|
|
116
|
+
|
|
117
|
+
The slope of a subaperture is the mean over its pupil pixels (derivatives)
|
|
118
|
+
or over its pairs of adjacent pupil pixels (telescoping sum), so partially
|
|
119
|
+
illuminated subapertures are averaged over their illuminated area. When the
|
|
120
|
+
number of pixels is not a multiple of the number of subapertures (e.g. 480
|
|
121
|
+
pixels and 68 subapertures), a pixel shared by two subapertures contributes
|
|
122
|
+
to each with the fraction of its area inside it (``utils.rebin_matrix``):
|
|
123
|
+
no interpolation of the phase or of the derivatives is needed.
|
|
124
|
+
|
|
125
|
+
WFS transformations are never applied to derivative maps: the gradient of
|
|
126
|
+
the transformed phase includes the Jacobian of the transformation (rotation
|
|
127
|
+
and 45° anamorphosis mix the x and y components, magnification and 90°
|
|
128
|
+
anamorphosis scale them), which a resampling of the derivative maps does not
|
|
129
|
+
account for.
|
|
130
|
+
|
|
131
|
+
With several WFS (``interaction_matrices_multi_wfs``), the WFS with the same
|
|
132
|
+
guide star, WFS transformations, number of subapertures and slope method
|
|
133
|
+
share the transformed influence functions and the derivatives, which are
|
|
134
|
+
computed once for the group (see ``derivatives_info['groups']``).
|
|
135
|
+
|
|
136
|
+
**Projection matrices**
|
|
137
|
+
Projection matrices transform scalar fields (DM modes and the projection
|
|
138
|
+
basis), so the DM and basis transformations can be applied separately
|
|
139
|
+
(SEPARATED workflow) or combined in one interpolation step (COMBINED workflow,
|
|
140
|
+
used when both the DM and the basis are transformed).
|
|
126
141
|
|
|
127
142
|
GPU Architecture
|
|
128
143
|
~~~~~~~~~~~~~~~~
|
|
@@ -140,6 +155,13 @@ GPU support is implemented through a flexible backend system:
|
|
|
140
155
|
# GPU backend (cupy + cupyx.scipy)
|
|
141
156
|
synim.init(device_idx=0, precision=1)
|
|
142
157
|
|
|
158
|
+
``synim.init()`` must be called once, before importing the SynIM submodules
|
|
159
|
+
(``synim.synim``, ``synim.synpm``, ``synim.utils``, ``synim.params_utils``,
|
|
160
|
+
``synim.params_manager``): they bind the array library and the data types
|
|
161
|
+
when they are imported. Importing them before ``synim.init()`` raises a
|
|
162
|
+
``RuntimeError``, as does calling ``synim.init()`` again with a different
|
|
163
|
+
configuration after they have been imported.
|
|
164
|
+
|
|
143
165
|
**Memory Management**
|
|
144
166
|
- Automatic data transfer between CPU and GPU
|
|
145
167
|
- Graceful fallback to CPU on memory errors
|
|
@@ -217,7 +239,7 @@ Common validation checks:
|
|
|
217
239
|
File Organization
|
|
218
240
|
-----------------
|
|
219
241
|
|
|
220
|
-
SynIM follows SPECULA
|
|
242
|
+
SynIM follows SPECULA directory structure for seamless integration. The ``ParamsManager`` automatically creates and manages these directories based on the ``root_dir`` parameter in your configuration:
|
|
221
243
|
|
|
222
244
|
.. code-block:: python
|
|
223
245
|
|
|
@@ -259,7 +281,7 @@ SynIM follows SPECULA's directory structure for seamless integration. The ``Para
|
|
|
259
281
|
└── rec/ # Reconstructors and Projection matrices (SPECULA format)
|
|
260
282
|
|
|
261
283
|
|
|
262
|
-
**Note:** When using SynIM with SPECULA, both tools can share the same ``root_dir`` and ``ifunc/`` directory. SPECULA uses additional directories (``im/``, ``rec/``) which can coexist alongside SynIM
|
|
284
|
+
**Note:** When using SynIM with SPECULA, both tools can share the same ``root_dir`` and ``ifunc/`` directory. SPECULA uses additional directories (``im/``, ``rec/``) which can coexist alongside SynIM directories.
|
|
263
285
|
|
|
264
286
|
Filename Conventions
|
|
265
287
|
~~~~~~~~~~~~~~~~~~~~
|
|
@@ -23,6 +23,7 @@ Key Features
|
|
|
23
23
|
- 🎯 **Smart caching** to minimize redundant computations
|
|
24
24
|
- 🔀 **Multi-WFS optimization** for faster batch computation
|
|
25
25
|
- 🚀 **GPU acceleration** via CuPy for high-performance computation
|
|
26
|
+
- 🧭 **Mis-registration geometry**: a geometric (not pixel-based) model linking global (WFS/DM/guide-star) and local (per-pair) registration in MCAO/GLAO systems, with inversion and sensitivity analysis
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
User Guide
|
|
@@ -34,6 +35,7 @@ User Guide
|
|
|
34
35
|
Installation <installation>
|
|
35
36
|
Basics <general>
|
|
36
37
|
Configuration & Legacy Migration <configuration>
|
|
38
|
+
Mis-registration Geometry <registration>
|
|
37
39
|
|
|
38
40
|
Tutorials
|
|
39
41
|
---------
|
|
@@ -42,6 +44,7 @@ Tutorials
|
|
|
42
44
|
|
|
43
45
|
SCAO calibration tutorial <tutorials/scao_calibration_tutorial>
|
|
44
46
|
MCAO calibration tutorial (MORFEO) <tutorials/mcao_calibration_tutorial>
|
|
47
|
+
Mis-registration geometry tutorial (MORFEO LGS) <tutorials/registration_morfeo_tutorial>
|
|
45
48
|
|
|
46
49
|
API Reference
|
|
47
50
|
-------------
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
.. _registration:
|
|
2
|
+
|
|
3
|
+
Mis-registration Geometry
|
|
4
|
+
==========================
|
|
5
|
+
|
|
6
|
+
The ``synim.registration`` subpackage models the geometric relation
|
|
7
|
+
between the **global** mis-registration of a wide-field AO (WFAO)
|
|
8
|
+
system - the position of each wavefront sensor (WFS) and each
|
|
9
|
+
deformable mirror (DM) relative to the pupil, and of each guide star
|
|
10
|
+
relative to its assumed position - and the **local** mis-registration
|
|
11
|
+
of each WFS-DM pair - the shift, rotation, magnification and
|
|
12
|
+
anamorphosis a local estimator measures between one sub-aperture grid
|
|
13
|
+
and one actuator grid.
|
|
14
|
+
|
|
15
|
+
It follows the geometry of Agapito, Plantet & Heritier (2024),
|
|
16
|
+
*SPRINT for WFAO systems*, Proc. SPIE 13097
|
|
17
|
+
(`arXiv:2406.15336 <https://doi.org/10.48550/arXiv.2406.15336>`_),
|
|
18
|
+
generalizing the SCAO registration method of Heritier et al. (2021) to
|
|
19
|
+
systems with several WFSs and DMs (MCAO/GLAO).
|
|
20
|
+
|
|
21
|
+
.. important::
|
|
22
|
+
Despite living in SynIM, this is **not** the pixel/influence-function
|
|
23
|
+
interaction matrix computed by :func:`synim.synim.interaction_matrix`
|
|
24
|
+
(or the projection matrices in ``synpm.py``): it never touches a
|
|
25
|
+
phase screen, a WFS signal or an influence function. It works
|
|
26
|
+
entirely on 2D affine geometry - the shift, rotation, magnification
|
|
27
|
+
and anamorphosis relating one coordinate grid to another - which is
|
|
28
|
+
what "interaction" means here: how the actuator grid of a DM lines up
|
|
29
|
+
with the sub-aperture grid of a WFS in a system where several DMs and
|
|
30
|
+
WFSs, conjugated at different altitudes, do not share one simple
|
|
31
|
+
pairing (an MCAO/GLAO system, unlike SCAO).
|
|
32
|
+
|
|
33
|
+
This subpackage also does **not** estimate local mis-registration
|
|
34
|
+
itself. It takes local measurements as given - produced elsewhere,
|
|
35
|
+
for example by SPRINT (already implemented in `SPECULA
|
|
36
|
+
<https://github.com/ArcetriAdaptiveOptics/SPECULA>`_) - and provides
|
|
37
|
+
the forward map from global parameters to local measurements, its
|
|
38
|
+
inversion, and tools to study its conditioning and noise sensitivity.
|
|
39
|
+
It does not touch on-sky calibration either.
|
|
40
|
+
|
|
41
|
+
Local and global parameters
|
|
42
|
+
----------------------------
|
|
43
|
+
|
|
44
|
+
Local mis-registration, per WFS-DM pair:
|
|
45
|
+
|
|
46
|
+
- X and Y shift between sub-apertures and actuators
|
|
47
|
+
- rotation between sub-apertures and actuators
|
|
48
|
+
- magnification and anamorphosis between sub-apertures and actuators
|
|
49
|
+
|
|
50
|
+
Global mis-registration, one set per element:
|
|
51
|
+
|
|
52
|
+
- shift, rotation and magnification of a WFS relative to the pupil
|
|
53
|
+
- shift, rotation, magnification and anamorphosis of a DM relative to
|
|
54
|
+
the pupil
|
|
55
|
+
- shift and altitude error of a guide star relative to its assumed
|
|
56
|
+
position (altitude error applies to laser guide stars only)
|
|
57
|
+
|
|
58
|
+
A single DM at the pupil (for example an adaptive secondary mirror)
|
|
59
|
+
makes the two levels coincide for that DM: see `System.local_transform`
|
|
60
|
+
below.
|
|
61
|
+
|
|
62
|
+
Building a system
|
|
63
|
+
------------------
|
|
64
|
+
|
|
65
|
+
A system is a set of ``WFS`` objects (each with its own ``GuideStar``)
|
|
66
|
+
and a set of ``DM`` objects, collected in a ``System``:
|
|
67
|
+
|
|
68
|
+
.. code-block:: python
|
|
69
|
+
|
|
70
|
+
from synim.registration.model import GuideStar, DM, WFS, System
|
|
71
|
+
|
|
72
|
+
system = System(pixel_pitch=0.0802) # meters per shift unit
|
|
73
|
+
|
|
74
|
+
gs = GuideStar(name="lgs1", position=(45.0, 22.5), height=90000.0)
|
|
75
|
+
system.add_wfs(WFS(name="wfs1", guide_star=gs, rotation=6.2))
|
|
76
|
+
|
|
77
|
+
system.add_dm(DM(name="dm1", height=600.0))
|
|
78
|
+
system.add_dm(DM(name="dm2", height=6500.0))
|
|
79
|
+
|
|
80
|
+
Every shift is expressed in the same unit as ``pixel_pitch`` (matching
|
|
81
|
+
the ``xShiftPhInPixel``/``yShiftPhInPixel`` convention of a SPECULA
|
|
82
|
+
YAML file); angles are in degrees, guide star positions in arcsec.
|
|
83
|
+
Nominal (zero mis-registration) values are the default, so a freshly
|
|
84
|
+
built system already represents a perfectly aligned instrument.
|
|
85
|
+
|
|
86
|
+
The forward model: from global to local
|
|
87
|
+
------------------------------------------
|
|
88
|
+
|
|
89
|
+
``System.local_params`` composes a WFS own mis-registration, the
|
|
90
|
+
guide-star parallax at a DM altitude, and that DM own
|
|
91
|
+
mis-registration into the local shift/rotation/magnification/
|
|
92
|
+
anamorphosis of one pair:
|
|
93
|
+
|
|
94
|
+
.. code-block:: python
|
|
95
|
+
|
|
96
|
+
shift, rotation, magnification, anamorphosis = system.local_params("wfs1", "dm1")
|
|
97
|
+
|
|
98
|
+
``System.pairs()`` lists every WFS-DM pair; restrict it to a subset
|
|
99
|
+
(for example the LGS WFSs only) when some sensors carry no useful
|
|
100
|
+
shape information - see the :ref:`MORFEO tutorial
|
|
101
|
+
<registration_morfeo_tutorial>`.
|
|
102
|
+
|
|
103
|
+
Loading a system from a configuration file
|
|
104
|
+
---------------------------------------------
|
|
105
|
+
|
|
106
|
+
``System.from_params_manager`` builds a system directly from an already
|
|
107
|
+
parsed SPECULA/SynIM YAML configuration, reusing
|
|
108
|
+
:func:`synim.params_utils.extract_wfs_list` and related helpers:
|
|
109
|
+
|
|
110
|
+
.. code-block:: python
|
|
111
|
+
|
|
112
|
+
import synim
|
|
113
|
+
synim.init(device_idx=-1, precision=1)
|
|
114
|
+
from synim.params_utils import parse_params_file
|
|
115
|
+
from synim.registration.model import System
|
|
116
|
+
|
|
117
|
+
config = parse_params_file("params_mcao.yml")
|
|
118
|
+
system = System.from_params_manager(config)
|
|
119
|
+
|
|
120
|
+
Only the WFS fields already used elsewhere in SynIM (``rotation``,
|
|
121
|
+
``xShiftPhInPixel``/``yShiftPhInPixel``, ``magnification``,
|
|
122
|
+
``anamorph45``) are read this way; a DM own shift/magnification and a
|
|
123
|
+
guide star position/altitude error have no dedicated key in that
|
|
124
|
+
format and default to zero - set them directly on the returned objects,
|
|
125
|
+
or through the reconstruction tools below.
|
|
126
|
+
|
|
127
|
+
Inverting: global parameters from local measurements
|
|
128
|
+
--------------------------------------------------------
|
|
129
|
+
|
|
130
|
+
Given a set of local measurements (from SPRINT, or - as in the example
|
|
131
|
+
above - injected for testing), :mod:`synim.registration.reconstruction`
|
|
132
|
+
recovers the global parameters behind them by Gauss-Newton iteration:
|
|
133
|
+
|
|
134
|
+
.. code-block:: python
|
|
135
|
+
|
|
136
|
+
from synim.registration.reconstruction import ParameterSpec, gauss_newton_invert
|
|
137
|
+
|
|
138
|
+
specs = [
|
|
139
|
+
ParameterSpec("dm", "dm2", "shift", 0),
|
|
140
|
+
ParameterSpec("dm", "dm2", "rotation"),
|
|
141
|
+
ParameterSpec("wfs", "wfs1", "shift", 0),
|
|
142
|
+
]
|
|
143
|
+
alpha_hat, system_hat, history = gauss_newton_invert(
|
|
144
|
+
system, specs, system.pairs(), local_measurements)
|
|
145
|
+
|
|
146
|
+
A ``ParameterSpec`` names one scalar unknown (an object, a field, and -
|
|
147
|
+
for a shift - which component); ``history`` is the residual norm at
|
|
148
|
+
each iteration, for a convergence check. At each step, a sensitivity
|
|
149
|
+
matrix (the same role as an interaction matrix, mapping global
|
|
150
|
+
parameters to local measurements) is built by finite differences and
|
|
151
|
+
inverted by pseudo-inverse.
|
|
152
|
+
|
|
153
|
+
Sensitivity and degeneracy
|
|
154
|
+
----------------------------
|
|
155
|
+
|
|
156
|
+
Not every choice of unknowns is well posed. Two cases the paper points
|
|
157
|
+
out, and that :mod:`synim.registration.analysis` can detect directly
|
|
158
|
+
from the sensitivity matrix built above:
|
|
159
|
+
|
|
160
|
+
- a common shift applied to every WFS together with every DM
|
|
161
|
+
(each scaled by its own parallax factor) leaves every local
|
|
162
|
+
measurement unchanged - an exact gauge freedom, since only the
|
|
163
|
+
*relative* WFS-DM alignment is observable;
|
|
164
|
+
- a guide star position error and the shift of the WFS affect a
|
|
165
|
+
single WFS-DM pair identically, and can only be told apart using
|
|
166
|
+
several DMs at different altitudes.
|
|
167
|
+
|
|
168
|
+
.. code-block:: python
|
|
169
|
+
|
|
170
|
+
from synim.registration.analysis import analyze
|
|
171
|
+
|
|
172
|
+
report = analyze(system, specs, system.pairs(), sigma=0.02)
|
|
173
|
+
print(report["condition_number"], report["modes"][-1])
|
|
174
|
+
|
|
175
|
+
``report["modes"]`` lists, from best to worst determined, which
|
|
176
|
+
``ParameterSpec`` entries dominate each direction; passing ``sigma``
|
|
177
|
+
(the local-measurement noise, one value or one per degree of freedom)
|
|
178
|
+
also returns the resulting global-parameter uncertainty, both from the
|
|
179
|
+
pseudo-inverse directly (``analytic_std``) and from a Monte Carlo
|
|
180
|
+
cross-check (``montecarlo_std``, via `monte_carlo_noise_propagation`).
|
|
181
|
+
|
|
182
|
+
.. important::
|
|
183
|
+
That Monte Carlo check reconstructs with a single linear step,
|
|
184
|
+
linearized exactly at the true values used to generate ``report``
|
|
185
|
+
sensitivity matrix - the same linear model behind ``analytic_std``.
|
|
186
|
+
Its expectation therefore equals those true values by construction,
|
|
187
|
+
so it can validate the analytic UNCERTAINTY, but it is unbiased by
|
|
188
|
+
construction and cannot reveal an estimation BIAS. To check for a
|
|
189
|
+
real bias, run `monte_carlo_gauss_newton` instead: it repeats the
|
|
190
|
+
actual iterative estimator (`gauss_newton_invert`) from a given
|
|
191
|
+
starting system - typically the nominal one, as an operational
|
|
192
|
+
estimator would, not knowing the true values in advance. Compare its
|
|
193
|
+
returned mean to the true values, in units of ``std / sqrt(n_trials)``
|
|
194
|
+
(the standard error of that mean), to tell a real bias from Monte
|
|
195
|
+
Carlo sampling noise.
|
|
196
|
+
|
|
197
|
+
Visualization
|
|
198
|
+
--------------
|
|
199
|
+
|
|
200
|
+
:mod:`synim.registration.viz` provides an altitude schematic, a
|
|
201
|
+
mis-registration table, and a top-down footprint view per DM, all
|
|
202
|
+
colour-matched and combinable through ``plot_system_overview``. See the
|
|
203
|
+
:ref:`MORFEO tutorial <registration_morfeo_tutorial>` for example
|
|
204
|
+
output.
|
|
205
|
+
|
|
206
|
+
See Also
|
|
207
|
+
--------
|
|
208
|
+
|
|
209
|
+
- :ref:`registration_morfeo_tutorial` - a worked example on a MORFEO-like geometry
|
|
210
|
+
- :doc:`api/registration` - full API reference
|
|
211
|
+
- Agapito, Plantet & Heritier (2024), *SPRINT for WFAO systems*,
|
|
212
|
+
`arXiv:2406.15336 <https://doi.org/10.48550/arXiv.2406.15336>`_
|
|
@@ -16,6 +16,32 @@ def generate_module_doc(module_name, title=None):
|
|
|
16
16
|
"""
|
|
17
17
|
return content
|
|
18
18
|
|
|
19
|
+
def generate_registration_doc():
|
|
20
|
+
"""
|
|
21
|
+
Generate the RST page for the `synim.registration` subpackage: one
|
|
22
|
+
`automodule` block per submodule, since it is a subpackage (not a
|
|
23
|
+
single flat module like the others), so `generate_module_doc`'s
|
|
24
|
+
single-module template does not apply.
|
|
25
|
+
"""
|
|
26
|
+
title = "Mis-registration Geometry (registration)"
|
|
27
|
+
submodules = [
|
|
28
|
+
("geometry", "Affine geometry primitives"),
|
|
29
|
+
("model", "Global mis-registration model (GuideStar, DM, WFS, System)"),
|
|
30
|
+
("reconstruction", "Global-from-local reconstruction"),
|
|
31
|
+
("analysis", "Sensitivity and noise-propagation analysis"),
|
|
32
|
+
("viz", "Visualization"),
|
|
33
|
+
]
|
|
34
|
+
sections = "\n".join(
|
|
35
|
+
f"{heading}\n{'-' * len(heading)}\n\n"
|
|
36
|
+
f".. automodule:: synim.registration.{name}\n"
|
|
37
|
+
" :members:\n"
|
|
38
|
+
" :undoc-members:\n"
|
|
39
|
+
" :show-inheritance:\n"
|
|
40
|
+
for name, heading in submodules
|
|
41
|
+
)
|
|
42
|
+
return f"{title}\n{'=' * len(title)}\n\n{sections}"
|
|
43
|
+
|
|
44
|
+
|
|
19
45
|
def generate_api_index():
|
|
20
46
|
"""Generate API index page"""
|
|
21
47
|
content = """API Reference
|
|
@@ -29,6 +55,7 @@ def generate_api_index():
|
|
|
29
55
|
params_manager
|
|
30
56
|
params_utils
|
|
31
57
|
utils
|
|
58
|
+
registration
|
|
32
59
|
|
|
33
60
|
"""
|
|
34
61
|
return content
|
|
@@ -64,6 +91,13 @@ def main():
|
|
|
64
91
|
f.write(content)
|
|
65
92
|
print(" ✓ Generated")
|
|
66
93
|
|
|
94
|
+
# The registration subpackage has several submodules, so it gets its
|
|
95
|
+
# own page with one automodule block each (see generate_registration_doc).
|
|
96
|
+
print("Generating api/registration.rst...")
|
|
97
|
+
with open(api_docs_path / "registration.rst", 'w') as f:
|
|
98
|
+
f.write(generate_registration_doc())
|
|
99
|
+
print(" ✓ Generated")
|
|
100
|
+
|
|
67
101
|
print("\n" + "="*50)
|
|
68
102
|
print("Done! Generated files:")
|
|
69
103
|
for rst_file in sorted(api_docs_path.glob("*.rst")):
|
|
@@ -20,7 +20,7 @@ This tutorial explains the calibration logic and the sequence of mathematical pr
|
|
|
20
20
|
|
|
21
21
|
While SynIM supports dual slope-computation engines, they model the sensor physics differently:
|
|
22
22
|
|
|
23
|
-
* **'derivatives' (Default & Recommended)**: Computes local gradients using finite differences and averages them over the subaperture. Thanks to SynIM
|
|
23
|
+
* **'derivatives' (Default & Recommended)**: Computes local gradients using finite differences and averages them over the subaperture. Thanks to SynIM strict sub-pixel geometric alignment during spatial resampling, this method natively absorbs the diffractive effects and cross-talk of real Shack-Hartmann sensors. It provides superior physical fidelity and resilience, especially for high-spatial-frequency modes.
|
|
24
24
|
* **'telsum'**: Uses a boundary-driven telescoping sum to evaluate the exact phase difference strictly at the subaperture edges. While mathematically exact for purely geometric boundaries, it struggles to map the continuous diffractive reality of the physical wavefront sensor compared to the aligned numerical derivatives.
|
|
25
25
|
|
|
26
26
|
Because of this, the 'derivatives' engine guarantees both a significantly more accurate slope estimation and a substantial computational speed-up on GPU.
|