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.
Files changed (105) hide show
  1. {synim-1.2.4 → synim-2.0.0}/PKG-INFO +2 -1
  2. {synim-1.2.4 → synim-2.0.0}/README.md +1 -0
  3. {synim-1.2.4 → synim-2.0.0}/docs/api/index.rst +1 -0
  4. synim-2.0.0/docs/api/registration.rst +42 -0
  5. {synim-1.2.4 → synim-2.0.0}/docs/conf.py +4 -0
  6. {synim-1.2.4 → synim-2.0.0}/docs/general.rst +63 -41
  7. {synim-1.2.4 → synim-2.0.0}/docs/index.rst +3 -0
  8. synim-2.0.0/docs/registration.rst +212 -0
  9. {synim-1.2.4 → synim-2.0.0}/docs/scripts/generate_api_docs.py +34 -0
  10. {synim-1.2.4 → synim-2.0.0}/docs/tutorials/mcao_calibration_tutorial.rst +1 -1
  11. synim-2.0.0/docs/tutorials/registration_morfeo_tutorial.rst +183 -0
  12. {synim-1.2.4 → synim-2.0.0}/docs/tutorials/scao_calibration_tutorial.rst +1 -3
  13. {synim-1.2.4 → synim-2.0.0}/examples/run_synim.py +2 -0
  14. {synim-1.2.4 → synim-2.0.0}/examples/run_synim_from_yaml_proj_aof.py +2 -0
  15. {synim-1.2.4 → synim-2.0.0}/synim/__init__.py +61 -11
  16. {synim-1.2.4 → synim-2.0.0}/synim/_version.py +3 -3
  17. {synim-1.2.4 → synim-2.0.0}/synim/params_manager.py +84 -43
  18. {synim-1.2.4 → synim-2.0.0}/synim/params_utils.py +40 -27
  19. synim-2.0.0/synim/registration/__init__.py +14 -0
  20. synim-2.0.0/synim/registration/analysis.py +330 -0
  21. synim-2.0.0/synim/registration/geometry.py +282 -0
  22. synim-2.0.0/synim/registration/model.py +283 -0
  23. synim-2.0.0/synim/registration/reconstruction.py +191 -0
  24. synim-2.0.0/synim/registration/viz.py +544 -0
  25. {synim-1.2.4 → synim-2.0.0}/synim/resource_monitor.py +4 -4
  26. synim-2.0.0/synim/synim.py +1017 -0
  27. {synim-1.2.4 → synim-2.0.0}/synim/synpm.py +30 -15
  28. {synim-1.2.4 → synim-2.0.0}/synim/utils.py +182 -126
  29. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/PKG-INFO +2 -1
  30. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/SOURCES.txt +31 -1
  31. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/scm_file_list.json +31 -1
  32. synim-2.0.0/synim.egg-info/scm_version.json +8 -0
  33. synim-2.0.0/test/__init__.py +40 -0
  34. synim-2.0.0/test/data/generate_regression_reference.py +198 -0
  35. synim-2.0.0/test/data/regression_reference.npz +0 -0
  36. synim-2.0.0/test/test_analysis.py +239 -0
  37. synim-2.0.0/test/test_ascii_output.py +30 -0
  38. synim-2.0.0/test/test_component_loading.py +139 -0
  39. synim-2.0.0/test/test_dtype_and_fixes.py +211 -0
  40. {synim-1.2.4 → synim-2.0.0}/test/test_extrapolation_cube.py +6 -1
  41. synim-2.0.0/test/test_fractional_subapertures.py +298 -0
  42. synim-2.0.0/test/test_geometry_vs_pixel_synim.py +184 -0
  43. synim-2.0.0/test/test_init.py +152 -0
  44. {synim-1.2.4 → synim-2.0.0}/test/test_intmat.py +22 -17
  45. synim-2.0.0/test/test_memory.py +60 -0
  46. synim-2.0.0/test/test_model_from_params_manager.py +62 -0
  47. synim-2.0.0/test/test_model_geometry.py +157 -0
  48. {synim-1.2.4 → synim-2.0.0}/test/test_morfeo_illumination.py +8 -6
  49. synim-2.0.0/test/test_morfeo_intmat.py +203 -0
  50. synim-2.0.0/test/test_multi_wfs_groups.py +78 -0
  51. {synim-1.2.4 → synim-2.0.0}/test/test_projection.py +20 -12
  52. {synim-1.2.4 → synim-2.0.0}/test/test_rebin.py +10 -13
  53. synim-2.0.0/test/test_reconstruction.py +119 -0
  54. synim-2.0.0/test/test_registration_geometry.py +90 -0
  55. synim-2.0.0/test/test_regression_reference.py +120 -0
  56. {synim-1.2.4 → synim-2.0.0}/test/test_rotshiftzoom.py +5 -1
  57. synim-2.0.0/test/test_sh_fractional_specula.py +143 -0
  58. {synim-1.2.4 → synim-2.0.0}/test/test_sh_intmat.py +6 -6
  59. {synim-1.2.4 → synim-2.0.0}/test/test_slope_method.py +9 -6
  60. synim-2.0.0/test/test_slopes_inputs.py +64 -0
  61. {synim-1.2.4 → synim-2.0.0}/test/test_subap_ordering_specula_vs_synim.py +15 -10
  62. synim-2.0.0/test/test_viz.py +168 -0
  63. synim-2.0.0/test/test_wfs_transformations.py +119 -0
  64. synim-1.2.4/synim/synim.py +0 -1245
  65. synim-1.2.4/synim.egg-info/scm_version.json +0 -8
  66. synim-1.2.4/test/__init__.py +0 -0
  67. {synim-1.2.4 → synim-2.0.0}/.github/workflows/publish.yml +0 -0
  68. {synim-1.2.4 → synim-2.0.0}/.github/workflows/pythonpackage.yml +0 -0
  69. {synim-1.2.4 → synim-2.0.0}/.gitignore +0 -0
  70. {synim-1.2.4 → synim-2.0.0}/.readthedocs.yaml +0 -0
  71. {synim-1.2.4 → synim-2.0.0}/LICENSE +0 -0
  72. {synim-1.2.4 → synim-2.0.0}/calibration_workflows/calib_morfeo_simplified_subaps.yml +0 -0
  73. {synim-1.2.4 → synim-2.0.0}/calibration_workflows/params_morfeo_simplified.yml +0 -0
  74. {synim-1.2.4 → synim-2.0.0}/calibration_workflows/params_workflow_morfeo.yml +0 -0
  75. {synim-1.2.4 → synim-2.0.0}/calibration_workflows/run_calib_workflow_morfeo.py +0 -0
  76. {synim-1.2.4 → synim-2.0.0}/docs/api/params_manager.rst +0 -0
  77. {synim-1.2.4 → synim-2.0.0}/docs/api/params_utils.rst +0 -0
  78. {synim-1.2.4 → synim-2.0.0}/docs/api/synim.rst +0 -0
  79. {synim-1.2.4 → synim-2.0.0}/docs/api/synpm.rst +0 -0
  80. {synim-1.2.4 → synim-2.0.0}/docs/api/utils.rst +0 -0
  81. {synim-1.2.4 → synim-2.0.0}/docs/configuration.rst +0 -0
  82. {synim-1.2.4 → synim-2.0.0}/docs/installation.rst +0 -0
  83. {synim-1.2.4 → synim-2.0.0}/docs/requirements.txt +0 -0
  84. {synim-1.2.4 → synim-2.0.0}/examples/calib_aof_subaps.yml +0 -0
  85. {synim-1.2.4 → synim-2.0.0}/examples/params_aof.yml +0 -0
  86. {synim-1.2.4 → synim-2.0.0}/examples/run_synim_from_yaml_aof.py +0 -0
  87. {synim-1.2.4 → synim-2.0.0}/pyproject.toml +0 -0
  88. {synim-1.2.4 → synim-2.0.0}/requirements.txt +0 -0
  89. {synim-1.2.4 → synim-2.0.0}/setup.cfg +0 -0
  90. {synim-1.2.4 → synim-2.0.0}/synim/convert_pro_file.py +0 -0
  91. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/dependency_links.txt +0 -0
  92. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/requires.txt +0 -0
  93. {synim-1.2.4 → synim-2.0.0}/synim.egg-info/top_level.txt +0 -0
  94. {synim-1.2.4 → synim-2.0.0}/test/data/cov_zern_100m_128p_8m_0.2r0_50L0.fits +0 -0
  95. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_lgs_alt_test.yml +0 -0
  96. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_offaxis_alt_test.yml +0 -0
  97. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_rot_test.yml +0 -0
  98. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_shift_test.yml +0 -0
  99. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test.yml +0 -0
  100. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test_rec.yml +0 -0
  101. {synim-1.2.4 → synim-2.0.0}/test/params_scao_sh_test_subap.yml +0 -0
  102. {synim-1.2.4 → synim-2.0.0}/test/test_covariance.py +0 -0
  103. {synim-1.2.4 → synim-2.0.0}/test/test_multirate_lut.py +0 -0
  104. {synim-1.2.4 → synim-2.0.0}/test/test_reconstructor.py +0 -0
  105. {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: 1.2.4
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
 
@@ -9,4 +9,5 @@ API Reference
9
9
  params_manager
10
10
  params_utils
11
11
  utils
12
+ registration
12
13
 
@@ -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
- .. _computation_workflows:
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
- Computation Workflows
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
- SynIM implements two main workflows for interaction matrix computation, automatically selected based on system geometry to optimize accuracy and performance.
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
- **COMBINED Workflow**
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
- **Automatic Workflow Selection**
102
+ Computation Workflows
103
+ ~~~~~~~~~~~~~~~~~~~~~
124
104
 
125
- The workflow is automatically selected based on system geometry analyzing transformation parameters.
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's directory structure for seamless integration. The ``ParamsManager`` automatically creates and manages these directories based on the ``root_dir`` parameter in your configuration:
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's directories.
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's 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.
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.