specula 0.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 (225) hide show
  1. specula-0.0.0/LICENSE +21 -0
  2. specula-0.0.0/PKG-INFO +86 -0
  3. specula-0.0.0/README.md +57 -0
  4. specula-0.0.0/pyproject.toml +50 -0
  5. specula-0.0.0/requirements.txt +11 -0
  6. specula-0.0.0/setup.cfg +4 -0
  7. specula-0.0.0/specula/__init__.py +282 -0
  8. specula-0.0.0/specula/base_data_obj.py +135 -0
  9. specula-0.0.0/specula/base_processing_obj.py +267 -0
  10. specula-0.0.0/specula/base_time_obj.py +115 -0
  11. specula-0.0.0/specula/base_value.py +75 -0
  12. specula-0.0.0/specula/calib_manager.py +146 -0
  13. specula-0.0.0/specula/connections.py +151 -0
  14. specula-0.0.0/specula/data_objects/__init__.py +0 -0
  15. specula-0.0.0/specula/data_objects/convolution_kernel.py +427 -0
  16. specula-0.0.0/specula/data_objects/electric_field.py +338 -0
  17. specula-0.0.0/specula/data_objects/gaussian_convolution_kernel.py +108 -0
  18. specula-0.0.0/specula/data_objects/ifunc.py +210 -0
  19. specula-0.0.0/specula/data_objects/ifunc_inv.py +100 -0
  20. specula-0.0.0/specula/data_objects/iir_filter_data.py +1191 -0
  21. specula-0.0.0/specula/data_objects/infinite_phase_screen.py +233 -0
  22. specula-0.0.0/specula/data_objects/intensity.py +75 -0
  23. specula-0.0.0/specula/data_objects/intmat.py +265 -0
  24. specula-0.0.0/specula/data_objects/laser_launch_telescope.py +105 -0
  25. specula-0.0.0/specula/data_objects/layer.py +104 -0
  26. specula-0.0.0/specula/data_objects/lenslet.py +76 -0
  27. specula-0.0.0/specula/data_objects/m2c.py +94 -0
  28. specula-0.0.0/specula/data_objects/pixels.py +135 -0
  29. specula-0.0.0/specula/data_objects/pupdata.py +144 -0
  30. specula-0.0.0/specula/data_objects/pupilstop.py +139 -0
  31. specula-0.0.0/specula/data_objects/recmat.py +100 -0
  32. specula-0.0.0/specula/data_objects/simul_params.py +52 -0
  33. specula-0.0.0/specula/data_objects/slopes.py +280 -0
  34. specula-0.0.0/specula/data_objects/source.py +182 -0
  35. specula-0.0.0/specula/data_objects/subap_data.py +83 -0
  36. specula-0.0.0/specula/data_objects/time_history.py +54 -0
  37. specula-0.0.0/specula/field_analyser.py +670 -0
  38. specula-0.0.0/specula/loop_control.py +178 -0
  39. specula-0.0.0/specula/processing_objects/__init__.py +0 -0
  40. specula-0.0.0/specula/processing_objects/atmo_evolution.py +290 -0
  41. specula-0.0.0/specula/processing_objects/atmo_infinite_evolution.py +248 -0
  42. specula-0.0.0/specula/processing_objects/atmo_propagation.py +285 -0
  43. specula-0.0.0/specula/processing_objects/atmo_random_phase.py +152 -0
  44. specula-0.0.0/specula/processing_objects/avc.py +16 -0
  45. specula-0.0.0/specula/processing_objects/base_generator.py +62 -0
  46. specula-0.0.0/specula/processing_objects/base_operation.py +141 -0
  47. specula-0.0.0/specula/processing_objects/base_slicer.py +40 -0
  48. specula-0.0.0/specula/processing_objects/ccd.py +249 -0
  49. specula-0.0.0/specula/processing_objects/data_buffer.py +74 -0
  50. specula-0.0.0/specula/processing_objects/data_print.py +92 -0
  51. specula-0.0.0/specula/processing_objects/data_source.py +77 -0
  52. specula-0.0.0/specula/processing_objects/data_store.py +148 -0
  53. specula-0.0.0/specula/processing_objects/demodulator.py +188 -0
  54. specula-0.0.0/specula/processing_objects/display_server.py +347 -0
  55. specula-0.0.0/specula/processing_objects/distributed_sh.py +137 -0
  56. specula-0.0.0/specula/processing_objects/dm.py +142 -0
  57. specula-0.0.0/specula/processing_objects/double_roof_slopec.py +44 -0
  58. specula-0.0.0/specula/processing_objects/electric_field_combinator.py +63 -0
  59. specula-0.0.0/specula/processing_objects/electric_field_reflection.py +37 -0
  60. specula-0.0.0/specula/processing_objects/ext_source_pyramid.py +169 -0
  61. specula-0.0.0/specula/processing_objects/extended_source.py +722 -0
  62. specula-0.0.0/specula/processing_objects/focal_plane_filter.py +263 -0
  63. specula-0.0.0/specula/processing_objects/gain_optimizer.py +386 -0
  64. specula-0.0.0/specula/processing_objects/ideal_derivative_sensor.py +266 -0
  65. specula-0.0.0/specula/processing_objects/iir_filter.py +197 -0
  66. specula-0.0.0/specula/processing_objects/im_calibrator.py +194 -0
  67. specula-0.0.0/specula/processing_objects/integrator.py +52 -0
  68. specula-0.0.0/specula/processing_objects/linear_combination.py +103 -0
  69. specula-0.0.0/specula/processing_objects/low_pass_filter.py +32 -0
  70. specula-0.0.0/specula/processing_objects/mirror_commands_combinator.py +99 -0
  71. specula-0.0.0/specula/processing_objects/modal_analysis.py +165 -0
  72. specula-0.0.0/specula/processing_objects/modalrec.py +253 -0
  73. specula-0.0.0/specula/processing_objects/modalrec_implicit_polc.py +110 -0
  74. specula-0.0.0/specula/processing_objects/modulated_double_roof.py +219 -0
  75. specula-0.0.0/specula/processing_objects/modulated_pyramid.py +658 -0
  76. specula-0.0.0/specula/processing_objects/multi_im_calibrator.py +159 -0
  77. specula-0.0.0/specula/processing_objects/multi_rec_calibrator.py +83 -0
  78. specula-0.0.0/specula/processing_objects/mvm.py +71 -0
  79. specula-0.0.0/specula/processing_objects/optical_gain_estimator.py +130 -0
  80. specula-0.0.0/specula/processing_objects/phase_flattening.py +63 -0
  81. specula-0.0.0/specula/processing_objects/poly_chrom_sh.py +68 -0
  82. specula-0.0.0/specula/processing_objects/poly_chrom_wfs.py +182 -0
  83. specula-0.0.0/specula/processing_objects/poly_crom_pyramid.py +82 -0
  84. specula-0.0.0/specula/processing_objects/power_loss.py +44 -0
  85. specula-0.0.0/specula/processing_objects/psf.py +119 -0
  86. specula-0.0.0/specula/processing_objects/psf_coronagraph.py +160 -0
  87. specula-0.0.0/specula/processing_objects/push_pull_generator.py +67 -0
  88. specula-0.0.0/specula/processing_objects/pyr_pupdata_calibrator.py +346 -0
  89. specula-0.0.0/specula/processing_objects/pyr_slopec.py +132 -0
  90. specula-0.0.0/specula/processing_objects/random_generator.py +64 -0
  91. specula-0.0.0/specula/processing_objects/rec_calibrator.py +86 -0
  92. specula-0.0.0/specula/processing_objects/schedule_generator.py +52 -0
  93. specula-0.0.0/specula/processing_objects/sh.py +557 -0
  94. specula-0.0.0/specula/processing_objects/sh_slopec.py +314 -0
  95. specula-0.0.0/specula/processing_objects/sh_subap_calibrator.py +110 -0
  96. specula-0.0.0/specula/processing_objects/slopec.py +132 -0
  97. specula-0.0.0/specula/processing_objects/sn_calibrator.py +53 -0
  98. specula-0.0.0/specula/processing_objects/spot_monitor.py +263 -0
  99. specula-0.0.0/specula/processing_objects/time_history_generator.py +31 -0
  100. specula-0.0.0/specula/processing_objects/vibration_generator.py +135 -0
  101. specula-0.0.0/specula/processing_objects/wave_generator.py +75 -0
  102. specula-0.0.0/specula/processing_objects/windowed_integration.py +55 -0
  103. specula-0.0.0/specula/processing_objects/zernike_sensor.py +111 -0
  104. specula-0.0.0/specula/simul.py +1020 -0
  105. specula-0.0.0/specula/template_processing_obj.py +85 -0
  106. specula-0.0.0/specula.egg-info/PKG-INFO +86 -0
  107. specula-0.0.0/specula.egg-info/SOURCES.txt +223 -0
  108. specula-0.0.0/specula.egg-info/dependency_links.txt +1 -0
  109. specula-0.0.0/specula.egg-info/entry_points.txt +4 -0
  110. specula-0.0.0/specula.egg-info/requires.txt +14 -0
  111. specula-0.0.0/specula.egg-info/top_level.txt +1 -0
  112. specula-0.0.0/test/test_atmo_evolution.py +430 -0
  113. specula-0.0.0/test/test_atmo_infinite_evolution.py +557 -0
  114. specula-0.0.0/test/test_atmo_propagation.py +362 -0
  115. specula-0.0.0/test/test_atmo_random_phase.py +212 -0
  116. specula-0.0.0/test/test_atmo_simulation.py +202 -0
  117. specula-0.0.0/test/test_base_data_obj.py +118 -0
  118. specula-0.0.0/test/test_base_operation.py +476 -0
  119. specula-0.0.0/test/test_base_processing_obj.py +246 -0
  120. specula-0.0.0/test/test_base_slicer.py +57 -0
  121. specula-0.0.0/test/test_base_time_obj.py +195 -0
  122. specula-0.0.0/test/test_base_value.py +165 -0
  123. specula-0.0.0/test/test_calc_psf.py +58 -0
  124. specula-0.0.0/test/test_calib_manager.py +101 -0
  125. specula-0.0.0/test/test_ccd.py +153 -0
  126. specula-0.0.0/test/test_compute_ifs_covmat.py +733 -0
  127. specula-0.0.0/test/test_compute_zonal_ifunc.py +46 -0
  128. specula-0.0.0/test/test_connections.py +148 -0
  129. specula-0.0.0/test/test_convolution_kernel.py +418 -0
  130. specula-0.0.0/test/test_cov_elong.py +109 -0
  131. specula-0.0.0/test/test_data_buffer.py +231 -0
  132. specula-0.0.0/test/test_data_objects.py +42 -0
  133. specula-0.0.0/test/test_data_print.py +222 -0
  134. specula-0.0.0/test/test_data_source.py +157 -0
  135. specula-0.0.0/test/test_data_store.py +215 -0
  136. specula-0.0.0/test/test_demodulator.py +70 -0
  137. specula-0.0.0/test/test_diagram.py +130 -0
  138. specula-0.0.0/test/test_display.py +611 -0
  139. specula-0.0.0/test/test_display_server.py +40 -0
  140. specula-0.0.0/test/test_distributed_sh.py +197 -0
  141. specula-0.0.0/test/test_dm.py +119 -0
  142. specula-0.0.0/test/test_double_roof.py +145 -0
  143. specula-0.0.0/test/test_double_roof_calibration.py +136 -0
  144. specula-0.0.0/test/test_double_roof_slopec.py +89 -0
  145. specula-0.0.0/test/test_electric_field.py +370 -0
  146. specula-0.0.0/test/test_elt_m1_ifunc.py +54 -0
  147. specula-0.0.0/test/test_ext_source_pyr.py +278 -0
  148. specula-0.0.0/test/test_extended_source.py +372 -0
  149. specula-0.0.0/test/test_extrapolation_2d.py +237 -0
  150. specula-0.0.0/test/test_field_analyser.py +288 -0
  151. specula-0.0.0/test/test_focal_plane_filter.py +390 -0
  152. specula-0.0.0/test/test_fsoc_lib.py +78 -0
  153. specula-0.0.0/test/test_gain_optimizer.py +420 -0
  154. specula-0.0.0/test/test_generators.py +468 -0
  155. specula-0.0.0/test/test_ideal_derivative_sensor.py +232 -0
  156. specula-0.0.0/test/test_ifunc.py +130 -0
  157. specula-0.0.0/test/test_ifunc_inv.py +103 -0
  158. specula-0.0.0/test/test_iircontrol.py +144 -0
  159. specula-0.0.0/test/test_iirfilter.py +785 -0
  160. specula-0.0.0/test/test_im_rec_calibrator.py +362 -0
  161. specula-0.0.0/test/test_import_class.py +162 -0
  162. specula-0.0.0/test/test_infinite_phase_screen.py +213 -0
  163. specula-0.0.0/test/test_init.py +37 -0
  164. specula-0.0.0/test/test_intensity.py +134 -0
  165. specula-0.0.0/test/test_interp2d.py +70 -0
  166. specula-0.0.0/test/test_intmat.py +412 -0
  167. specula-0.0.0/test/test_laser_launch_telescope.py +137 -0
  168. specula-0.0.0/test/test_layer.py +86 -0
  169. specula-0.0.0/test/test_lenslet.py +98 -0
  170. specula-0.0.0/test/test_linear_combination.py +284 -0
  171. specula-0.0.0/test/test_local_mean_rebin.py +54 -0
  172. specula-0.0.0/test/test_loop_control.py +70 -0
  173. specula-0.0.0/test/test_low_pass_filter_simulation.py +74 -0
  174. specula-0.0.0/test/test_m2c.py +124 -0
  175. specula-0.0.0/test/test_make_mask.py +73 -0
  176. specula-0.0.0/test/test_make_xy.py +76 -0
  177. specula-0.0.0/test/test_mask.py +154 -0
  178. specula-0.0.0/test/test_mmse_reconstructor.py +244 -0
  179. specula-0.0.0/test/test_modal_analysis_simulation.py +58 -0
  180. specula-0.0.0/test/test_modal_basis.py +104 -0
  181. specula-0.0.0/test/test_modal_pushpull_signal.py +302 -0
  182. specula-0.0.0/test/test_modalrec.py +156 -0
  183. specula-0.0.0/test/test_multi_im_calibrator.py +834 -0
  184. specula-0.0.0/test/test_multi_rec_calibrator.py +501 -0
  185. specula-0.0.0/test/test_mvm.py +285 -0
  186. specula-0.0.0/test/test_optical_gain_estimator.py +103 -0
  187. specula-0.0.0/test/test_phase_flattening.py +183 -0
  188. specula-0.0.0/test/test_physical_propagation.py +65 -0
  189. specula-0.0.0/test/test_pixels.py +92 -0
  190. specula-0.0.0/test/test_platescale.py +48 -0
  191. specula-0.0.0/test/test_poly_chrom_pyramid.py +118 -0
  192. specula-0.0.0/test/test_poly_chrom_sh.py +282 -0
  193. specula-0.0.0/test/test_power_loss.py +79 -0
  194. specula-0.0.0/test/test_process_utils.py +48 -0
  195. specula-0.0.0/test/test_psf.py +435 -0
  196. specula-0.0.0/test/test_pupdata.py +121 -0
  197. specula-0.0.0/test/test_pupilstop.py +207 -0
  198. specula-0.0.0/test/test_pyr.py +553 -0
  199. specula-0.0.0/test/test_pyr_calibration.py +167 -0
  200. specula-0.0.0/test/test_pyr_simulation.py +181 -0
  201. specula-0.0.0/test/test_pyr_slopec.py +153 -0
  202. specula-0.0.0/test/test_rec_calibrator.py +789 -0
  203. specula-0.0.0/test/test_recmat.py +99 -0
  204. specula-0.0.0/test/test_sh.py +147 -0
  205. specula-0.0.0/test/test_sh_calibration.py +159 -0
  206. specula-0.0.0/test/test_sh_simulation.py +160 -0
  207. specula-0.0.0/test/test_sh_slopec.py +400 -0
  208. specula-0.0.0/test/test_sh_slopec_morfeo.py +687 -0
  209. specula-0.0.0/test/test_sh_subap_calibrator.py +105 -0
  210. specula-0.0.0/test/test_simul.py +247 -0
  211. specula-0.0.0/test/test_simul_params.py +49 -0
  212. specula-0.0.0/test/test_slopes.py +302 -0
  213. specula-0.0.0/test/test_sn_calibrator.py +47 -0
  214. specula-0.0.0/test/test_source.py +89 -0
  215. specula-0.0.0/test/test_spot_monitor.py +220 -0
  216. specula-0.0.0/test/test_subapdata.py +77 -0
  217. specula-0.0.0/test/test_time_history.py +77 -0
  218. specula-0.0.0/test/test_timehistory_integration.py +66 -0
  219. specula-0.0.0/test/test_to_xp.py +102 -0
  220. specula-0.0.0/test/test_toccd.py +99 -0
  221. specula-0.0.0/test/test_trigger_zero.py +29 -0
  222. specula-0.0.0/test/test_utils.py +120 -0
  223. specula-0.0.0/test/test_windowed_integration.py +266 -0
  224. specula-0.0.0/test/test_zernike.py +113 -0
  225. specula-0.0.0/test/test_zernike_sensor.py +192 -0
specula-0.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 FabioRossiArcetri
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
specula-0.0.0/PKG-INFO ADDED
@@ -0,0 +1,86 @@
1
+ Metadata-Version: 2.4
2
+ Name: specula
3
+ Version: 0.0.0
4
+ Summary: PYramid Simulator Software for Adaptive OpTics Arcetri
5
+ Author: Alfio Puglisi, Guido Agapito, INAF Arcetri Adaptive Optics group
6
+ Author-email: Fabio Rossi <fabio.rossi@inaf.it>
7
+ License-Expression: MIT
8
+ Keywords: Adaptive Optics,Astrophysics,INAF,Arcetri
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Operating System :: POSIX :: Linux
11
+ Classifier: Programming Language :: Python :: 3
12
+ Requires-Python: >=3.8
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Requires-Dist: numpy
16
+ Requires-Dist: scipy
17
+ Requires-Dist: astropy
18
+ Requires-Dist: matplotlib
19
+ Requires-Dist: symao>=1.0.1
20
+ Requires-Dist: astro-seeing>=1.1
21
+ Requires-Dist: flask-socketio
22
+ Requires-Dist: python-socketio
23
+ Requires-Dist: requests
24
+ Requires-Dist: pytest
25
+ Requires-Dist: scikit-image
26
+ Provides-Extra: control
27
+ Requires-Dist: iircontrol; extra == "control"
28
+ Dynamic: license-file
29
+
30
+ # SPECULA
31
+ Python AO end-to-end simulator
32
+
33
+ SPECULA is a Python-based, object-oriented software derived from [PASSATA](https://arxiv.org/abs/1607.07624) and developed
34
+ by the Adaptive Optics group at the Arcetri Observatory for end-to-end Monte-Carlo simulations of adaptive optics systems.
35
+ It can be accelerated using GPU-CUDA via CuPy.
36
+
37
+ See the documentation here: [specula.readthedocs.io](https://specula.readthedocs.io/en/latest/)
38
+
39
+ ## Directories
40
+
41
+ - **docs**: contains the documentation.
42
+ - **main**: contains functions and parameter files to calibrate and run a closed loop of an adaptive optics system (single-conjugated, multi-conjugated, natural, laser, ...).
43
+ - **specula**: the main library, structured as follows:
44
+ - **data_objects**: classes that wrap the data and provide methods to access them.
45
+ - **display**: classes for data visualization.
46
+ - **lib**: utility functions used by multiple objects.
47
+ - **processing_objects**: classes that model the simulation elements as a function of inputs and time.
48
+ - **scripts**: various scripts.
49
+ - **test**: contains functions to test SPECULA using the `unittest` framework.
50
+
51
+ ## Requirements
52
+
53
+ - Python 3.8+
54
+ - numpy
55
+ - scipy
56
+ - matplotlib
57
+ - flask
58
+ - flask-socketio
59
+ - socketio
60
+ - scikit-image (for physical propagation)
61
+ - cupy (for GPU acceleration, optional)
62
+
63
+ ### Optional libraries
64
+
65
+ Some features require additional libraries:
66
+ - **pycairo**: needed for block diagram generation with orthogram
67
+ - **orthogram**: for automatic block diagram creation (see [orthogram](https://pypi.org/project/orthogram/))
68
+ - **control**: for conversion of transfer function system in SPECULA format and vice-versa and analysis of transfer function
69
+
70
+ Install optional dependencies (pycairo will be installed as dependency of orthogram) with:
71
+ ```bash
72
+ pip install orthogram
73
+ pip install control
74
+ ```
75
+
76
+ ## Contributing to SPECULA
77
+ To contribute to SPECULA, follow these steps:
78
+
79
+ 1. Fork this repository.
80
+ 2. Create a branch: `git checkout -b <branch_name>`
81
+ 3. Make your changes and **add tests for the new functionality.**
82
+ 4. Commit your changes: `git commit -m '<commit_message>'`
83
+ 5. Push to the branch: `git push`
84
+ 6. Create the pull request.
85
+
86
+ We require tests for all new features to ensure the stability of the project.
@@ -0,0 +1,57 @@
1
+ # SPECULA
2
+ Python AO end-to-end simulator
3
+
4
+ SPECULA is a Python-based, object-oriented software derived from [PASSATA](https://arxiv.org/abs/1607.07624) and developed
5
+ by the Adaptive Optics group at the Arcetri Observatory for end-to-end Monte-Carlo simulations of adaptive optics systems.
6
+ It can be accelerated using GPU-CUDA via CuPy.
7
+
8
+ See the documentation here: [specula.readthedocs.io](https://specula.readthedocs.io/en/latest/)
9
+
10
+ ## Directories
11
+
12
+ - **docs**: contains the documentation.
13
+ - **main**: contains functions and parameter files to calibrate and run a closed loop of an adaptive optics system (single-conjugated, multi-conjugated, natural, laser, ...).
14
+ - **specula**: the main library, structured as follows:
15
+ - **data_objects**: classes that wrap the data and provide methods to access them.
16
+ - **display**: classes for data visualization.
17
+ - **lib**: utility functions used by multiple objects.
18
+ - **processing_objects**: classes that model the simulation elements as a function of inputs and time.
19
+ - **scripts**: various scripts.
20
+ - **test**: contains functions to test SPECULA using the `unittest` framework.
21
+
22
+ ## Requirements
23
+
24
+ - Python 3.8+
25
+ - numpy
26
+ - scipy
27
+ - matplotlib
28
+ - flask
29
+ - flask-socketio
30
+ - socketio
31
+ - scikit-image (for physical propagation)
32
+ - cupy (for GPU acceleration, optional)
33
+
34
+ ### Optional libraries
35
+
36
+ Some features require additional libraries:
37
+ - **pycairo**: needed for block diagram generation with orthogram
38
+ - **orthogram**: for automatic block diagram creation (see [orthogram](https://pypi.org/project/orthogram/))
39
+ - **control**: for conversion of transfer function system in SPECULA format and vice-versa and analysis of transfer function
40
+
41
+ Install optional dependencies (pycairo will be installed as dependency of orthogram) with:
42
+ ```bash
43
+ pip install orthogram
44
+ pip install control
45
+ ```
46
+
47
+ ## Contributing to SPECULA
48
+ To contribute to SPECULA, follow these steps:
49
+
50
+ 1. Fork this repository.
51
+ 2. Create a branch: `git checkout -b <branch_name>`
52
+ 3. Make your changes and **add tests for the new functionality.**
53
+ 4. Commit your changes: `git commit -m '<commit_message>'`
54
+ 5. Push to the branch: `git push`
55
+ 6. Create the pull request.
56
+
57
+ We require tests for all new features to ensure the stability of the project.
@@ -0,0 +1,50 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "specula"
7
+ description = "PYramid Simulator Software for Adaptive OpTics Arcetri"
8
+ readme = "README.md"
9
+ requires-python = ">=3.8"
10
+ license = "MIT"
11
+ license-files = ["LICENSE"]
12
+
13
+ authors = [
14
+ { name = "Fabio Rossi", email = "fabio.rossi@inaf.it" },
15
+ { name = "Alfio Puglisi" },
16
+ { name = "Guido Agapito" },
17
+ { name = "INAF Arcetri Adaptive Optics group" }
18
+ ]
19
+ keywords = ["Adaptive Optics", "Astrophysics", "INAF", "Arcetri"]
20
+ classifiers = [
21
+ "Development Status :: 4 - Beta",
22
+ "Operating System :: POSIX :: Linux",
23
+ "Programming Language :: Python :: 3",
24
+ ]
25
+
26
+ # IMPORTANT: version must now be declared explicitly or dynamically
27
+ dynamic = ["version", "dependencies"]
28
+
29
+ [project.scripts]
30
+ specula_frontend_start = "specula.scripts.web_frontend:start"
31
+ specula_frontend_stop = "specula.scripts.web_frontend:stop"
32
+ specula = "specula.scripts.specula_main:main"
33
+
34
+ [tool.setuptools]
35
+ packages = [
36
+ "specula",
37
+ "specula.data_objects",
38
+ "specula.processing_objects"
39
+ ]
40
+ include-package-data = true
41
+
42
+ [tool.setuptools.dynamic]
43
+ dependencies = { file = "requirements.txt" }
44
+
45
+ [project.optional-dependencies]
46
+ control = ["iircontrol"]
47
+
48
+ [tool.setuptools_scm]
49
+ write_to = "specula/_version.py"
50
+
@@ -0,0 +1,11 @@
1
+ numpy
2
+ scipy
3
+ astropy
4
+ matplotlib
5
+ symao >= 1.0.1
6
+ astro-seeing>=1.1
7
+ flask-socketio
8
+ python-socketio
9
+ requests
10
+ pytest
11
+ scikit-image
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,282 @@
1
+ import numpy as np
2
+ import os
3
+ import functools
4
+ from functools import wraps
5
+
6
+ cpu_float_dtype_list = [np.float64, np.float32]
7
+ cpu_complex_dtype_list = [np.complex128, np.complex64]
8
+ array_types = []
9
+
10
+ gpuEnabled = False
11
+ cp = None
12
+ xp = None
13
+ global_precision = None
14
+ float_dtype_list = None
15
+ complex_dtype_list = None
16
+ gpu_float_dtype_list = cpu_float_dtype_list
17
+ gpu_complex_dtype_list = cpu_complex_dtype_list
18
+ float_dtype = None
19
+ complex_dtype = None
20
+ default_target_device_idx = None
21
+ default_target_device = None
22
+ process_comm = None
23
+ process_rank = None
24
+ ASEC2RAD = np.pi / (3600 * 180)
25
+ RAD2ASEC = 1.0 / ASEC2RAD
26
+ MPI_DBG = False
27
+
28
+ MPI_SEND_DBG = False
29
+
30
+ # precision = 0 -> double precision
31
+ # precision = 1 -> single precision
32
+
33
+ # target_device = -1 -> CPU
34
+ # target_device = i>-1 -> GPUi
35
+
36
+ # you might have a GPU working and cupy installed
37
+ # and still want to use the CPU (idx==-1) as default_target_device
38
+ # in this case you might still want to allocate some objects on
39
+ # a GPU device (idx>=0).
40
+ # This can be checked later looking at the value of gpuEnabled.
41
+
42
+ def init(device_idx=-1, precision=0, rank=None, comm=None, mpi_dbg=False):
43
+ global xp
44
+ global cp
45
+ global gpuEnabled
46
+ global global_precision
47
+ global float_dtype_list
48
+ global complex_dtype_list
49
+ global gpu_float_dtype_list
50
+ global gpu_complex_dtype_list
51
+ global array_types
52
+ global float_dtype
53
+ global complex_dtype
54
+ global default_target_device_idx
55
+ global default_target_device
56
+ global process_comm
57
+ global process_rank
58
+ global MPI_DBG
59
+
60
+ MPI_DBG = mpi_dbg
61
+ process_comm = comm
62
+ process_rank = rank
63
+
64
+ default_target_device_idx = device_idx
65
+ systemDisable = os.environ.get('SPECULA_DISABLE_GPU', 'FALSE')
66
+ if systemDisable=='FALSE':
67
+ try:
68
+ import cupy as cp
69
+ print("Cupy import successfull. Installed version is:", cp.__version__)
70
+ gpuEnabled = True
71
+ cp = cp
72
+ except:
73
+ print("Cupy import failed. SPECULA will fall back to CPU use.")
74
+ cp = None
75
+ xp = np
76
+ default_target_device_idx=-1
77
+ else:
78
+ print("env variable SPECULA_DISABLE_GPU prevents using the GPU.")
79
+ cp = None
80
+ xp = np
81
+ default_target_device_idx=-1
82
+
83
+
84
+ if default_target_device_idx>=0:
85
+ xp = cp
86
+ gpu_float_dtype_list = [cp.float64, cp.float32]
87
+ gpu_complex_dtype_list = [cp.complex128, cp.complex64]
88
+ default_target_device = cp.cuda.Device(default_target_device_idx)
89
+ default_target_device.use()
90
+ print('Default device is GPU number ', default_target_device_idx)
91
+ # print('Using device: ', cp.cuda.runtime.getDeviceProperties(default_target_device)['name'])
92
+ # attributes = default_target_device.attributes
93
+ # properties = cp.cuda.runtime.getDeviceProperties(default_target_device)
94
+ # print('Number of multiprocessors:', attributes['MultiProcessorCount'])
95
+ # print('Global memory size (GB):', properties['totalGlobalMem'] / (1024**3))
96
+ else:
97
+ print('Default device is CPU')
98
+ xp = np
99
+
100
+ if cp is not None:
101
+ array_types = [np.ndarray, cp.ndarray]
102
+ else:
103
+ array_types = [np.ndarray]
104
+
105
+ float_dtype_list = [xp.float64, xp.float32]
106
+ complex_dtype_list = [xp.complex128, xp.complex64]
107
+ global_precision = precision
108
+ float_dtype = float_dtype_list[global_precision]
109
+ complex_dtype = complex_dtype_list[global_precision]
110
+
111
+ # Patch cupy's missing RandomState.random() method
112
+ if cp is not None:
113
+ cp.random.RandomState.random = cp.random.RandomState.random_sample
114
+
115
+
116
+ # should be used as less as a possible and preferably outside time critical computations
117
+ def cpuArray(v, dtype=None, force_copy=False):
118
+ return to_xp(np, v, dtype=dtype, force_copy=force_copy)
119
+
120
+
121
+ def to_xp(xp, v, dtype=None, force_copy=False):
122
+ '''
123
+ Make sure that v is allocated as an array on this object's device.
124
+ Works for all combinations of np and cp, whether installed or not.
125
+
126
+ Optionally casts to the required dtype (no copy is made if
127
+ the dtype is already the correct one)
128
+
129
+ The main trigger for this function is that np.array() cannot
130
+ be used on a cupy array.
131
+ '''
132
+ if xp is cp:
133
+ if isinstance(v, cp.ndarray) and not force_copy:
134
+ retval = v
135
+ else:
136
+ retval = cp.array(v)
137
+ else:
138
+ if cp is not None and isinstance(v, cp.ndarray):
139
+ retval = v.get()
140
+ elif isinstance(v, np.ndarray) and not force_copy:
141
+ # Avoid extra copy (enabled by numpy default)
142
+ retval = v
143
+ else:
144
+ retval = np.array(v)
145
+ if dtype is None and not force_copy:
146
+ return retval
147
+ else:
148
+ return retval.astype(dtype, copy=force_copy)
149
+
150
+
151
+ class DummyDecoratorAndContextManager():
152
+ def __init__(self):
153
+ pass
154
+ def __enter__(self):
155
+ pass
156
+ def __exit__(self, *args):
157
+ pass
158
+ def __call__(self, f):
159
+ def caller(*args, **kwargs):
160
+ return f(*args, **kwargs)
161
+ return caller
162
+
163
+
164
+ def show_in_profiler(message=None, color_id=None, argb_color=None, sync=False):
165
+ '''
166
+ Decorator to allow using cupy's TimeRangeDecorator
167
+ in a safe way even when cupy is not installed
168
+ Parameters are the same as TimeRangeDecorator
169
+ '''
170
+ try:
171
+ from cupyx.profiler import time_range
172
+
173
+ return time_range(message=message,
174
+ color_id=color_id,
175
+ argb_color=argb_color,
176
+ sync=sync)
177
+
178
+ except ImportError:
179
+ return DummyDecoratorAndContextManager()
180
+
181
+
182
+ def fuse(kernel_name=None):
183
+ '''
184
+ Replacement of cupy.fuse() allowing runtime
185
+ dispatch to cupy or numpy.
186
+
187
+ Fused function takes an xp argument that will
188
+ cause it to run as a fused kernel or a standard
189
+ numpy function. The xp argument can be used
190
+ inside the function as usual.
191
+
192
+ Parameters are the same as cp.fuse()
193
+ '''
194
+ def decorator(f):
195
+ f_cp = functools.partial(f, xp=cp)
196
+ f_np = functools.partial(f, xp=np)
197
+ f_cpu = f_np
198
+ if cp:
199
+ f_gpu = cp.fuse(kernel_name=kernel_name)(f_cp)
200
+ else:
201
+ f_gpu = None
202
+ @wraps(f)
203
+ def wrapper(*args, xp, **kwargs):
204
+ if xp == cp:
205
+ return f_gpu(*args, **kwargs)
206
+ else:
207
+ return f_cpu(*args, **kwargs)
208
+ return wrapper
209
+ return decorator
210
+
211
+
212
+ def main_simul(yml_files: list,
213
+ nsimul = 1,
214
+ cpu: bool=False,
215
+ overrides: str=None,
216
+ target: int=0,
217
+ profile: bool=False,
218
+ mpi: bool=False,
219
+ mpidbg: bool=False,
220
+ stepping: bool=False,
221
+ diagram: bool=False,
222
+ diagram_title: str=None,
223
+ diagram_filename: str=None,
224
+ diagram_colors_on: bool=False):
225
+
226
+ if mpi:
227
+ try:
228
+ from mpi4py import MPI
229
+ from mpi4py.util import pkl5
230
+ print("mpi4py import successfull. Installed version is:", MPI.Get_version())
231
+ except ImportError:
232
+ print("mpi4py import failed.")
233
+ raise
234
+
235
+ comm = pkl5.Intracomm(MPI.COMM_WORLD)
236
+ rank = comm.Get_rank()
237
+ N = 10000000
238
+ datatype = MPI.FLOAT
239
+ num_bytes = N * (datatype.Pack_size(count=1, comm=comm) + MPI.BSEND_OVERHEAD)
240
+
241
+ print(f'MPI buffer size: {num_bytes/1024**2:.2f} MB')
242
+ attached_buf = bytearray(num_bytes)
243
+ MPI.Attach_buffer(attached_buf)
244
+ else:
245
+ rank = None
246
+ comm = None
247
+
248
+ if cpu:
249
+ target_device_idx = -1
250
+ else:
251
+ target_device_idx = target
252
+
253
+ init(target_device_idx, precision=1, rank=rank, comm=comm, mpi_dbg=mpidbg)
254
+ from specula.simul import Simul
255
+
256
+ if profile:
257
+ import cProfile
258
+ import pstats
259
+ pr = cProfile.Profile()
260
+ pr.enable()
261
+
262
+ for simul_idx in range(nsimul):
263
+ print(yml_files)
264
+ Simul(*yml_files,
265
+ simul_idx=simul_idx,
266
+ overrides=overrides,
267
+ stepping=stepping,
268
+ diagram=diagram,
269
+ diagram_filename=diagram_filename,
270
+ diagram_title=diagram_title,
271
+ diagram_colors_on=diagram_colors_on
272
+ ).run()
273
+
274
+ if profile:
275
+ pr.disable
276
+ stats = pstats.Stats(pr).sort_stats("cumtime")
277
+ stats.print_stats(r"\((?!\_).*\)$", 200)
278
+
279
+ if mpi:
280
+ MPI.Detach_buffer()
281
+
282
+
@@ -0,0 +1,135 @@
1
+
2
+ import warnings
3
+ from copy import copy
4
+ from functools import lru_cache
5
+
6
+ from specula import cp, np, array_types
7
+ from specula.base_time_obj import BaseTimeObj
8
+
9
+
10
+ # We use lru_cache() instead of cache() for python 3.8 compatibility
11
+ @lru_cache(maxsize=None)
12
+ def get_properties(cls):
13
+ result = []
14
+ classlist = cls.__mro__
15
+ for cc in classlist:
16
+ result.extend([attr for attr, value in vars(cc).items() if isinstance(value, property) ])
17
+ return result
18
+
19
+
20
+ class BaseDataObj(BaseTimeObj):
21
+ def __init__(self, target_device_idx=None, precision=None):
22
+ """
23
+ Initialize the base data object.
24
+
25
+ Parameters:
26
+ target_device_idx: int, optional
27
+ device to be targeted for data storage. Set to -1 for CPU,
28
+ to 0 for the first GPU device, 1 for the second GPU device, etc.
29
+ precision: int, optional
30
+ if None will use the global_precision, otherwise set to 0 for double, 1 for single
31
+ """
32
+ super().__init__(target_device_idx, precision)
33
+ self.generation_time = -1
34
+ self.tag = ''
35
+
36
+ def transferDataTo(self, destobj, force_reallocation=False):
37
+ '''
38
+ Copy CPU/GPU arrays into an existing data object:
39
+ iterate over all self attributes and, if a CPU or GPU array
40
+ is detected, copy data into *destobj* without reallocating.
41
+
42
+ Destination (CPU or GPU device) is inferred by *destobj.target_device_idx*,
43
+ which must be set correctly before calling this method.
44
+ '''
45
+ # Get a list of all attributes, but skip properties
46
+ pp = get_properties(type(self))
47
+ attr_list = [attr for attr in dir(self) if attr not in pp]
48
+
49
+ for attr in attr_list:
50
+ self_attr = getattr(self, attr)
51
+ self_type = type(self_attr)
52
+ if self_type not in array_types:
53
+ continue
54
+
55
+ dest_attr = getattr(destobj, attr)
56
+ dest_type = type(dest_attr)
57
+
58
+ if dest_type not in array_types:
59
+ print(f'Warning: destination attribute is not a cupy/numpy array, forcing reallocation ({destobj}.{attr})')
60
+ force_reallocation = True
61
+
62
+ # Destination array had the correct type: perform in-place data copy
63
+ if not force_reallocation:
64
+ # Detect whether the array types are correct for all four cases:
65
+ # Device to CPU, CPU to device, device-to-device, and CPU-CPU. Also check whether
66
+ # the target_device_idx is set correctly for the destination object.
67
+ DtD = cp is not None and (self_type == cp.ndarray) and (dest_type == cp.ndarray) and destobj.target_device_idx >= 0
68
+ DtH = cp is not None and (self_type == cp.ndarray) and (dest_type == np.ndarray) and destobj.target_device_idx == -1
69
+ HtD = cp is not None and (self_type == np.ndarray) and (dest_type == cp.ndarray) and destobj.target_device_idx >= 0
70
+ HtH = (self_type == np.ndarray) and (dest_type == np.ndarray) and destobj.target_device_idx == -1
71
+ if DtD:
72
+ # Performance warnings here are expected, because we might
73
+ # trigger a peer-to-peer transfer between devices
74
+ with warnings.catch_warnings():
75
+ if self.PerformanceWarning:
76
+ warnings.simplefilter("ignore", category=self.PerformanceWarning)
77
+ try:
78
+ dest_attr[:] = self_attr
79
+ except:
80
+ dest_attr = self_attr
81
+ elif DtH:
82
+ # Do not set blocking=True for cupy 12.x compatibility.
83
+ # Blocking is True by default in later versions anyway
84
+ self_attr.get(out=dest_attr)
85
+ elif HtD:
86
+ dest_attr.set(self_attr)
87
+ elif HtH:
88
+ dest_attr[:] = self_attr
89
+ else:
90
+ print(f'Warning: mismatch between target_device_idx and array allocation, forcing reallocation ({destobj}.{attr})')
91
+ force_reallocation = True
92
+
93
+ # Otherwise, reallocate
94
+ if force_reallocation:
95
+ DtD = cp is not None and (self_type == cp.ndarray) and destobj.target_device_idx >= 0
96
+ DtH = cp is not None and (self_type == cp.ndarray) and destobj.target_device_idx == -1
97
+ HtD = (self_type == np.ndarray) and destobj.target_device_idx >= 0
98
+ HtH = (self_type == np.ndarray) and destobj.target_device_idx == -1
99
+
100
+ if DtD:
101
+ # Performance warnings here are expected, because we might
102
+ # trigger a peer-to-peer transfer between devices
103
+ with warnings.catch_warnings():
104
+ if self.PerformanceWarning:
105
+ warnings.simplefilter("ignore", category=self.PerformanceWarning)
106
+ setattr(destobj, attr, cp.asarray(self_attr))
107
+ if DtH:
108
+ # Do not set blocking=True for cupy 12.x compatibility.
109
+ # Blocking is True by default in later versions anyway
110
+ setattr(destobj, attr, self_attr.get())
111
+ if HtD:
112
+ setattr(destobj, attr, cp.asarray(self_attr))
113
+ if HtH:
114
+ setattr(destobj, attr, np.asarray(self_attr))
115
+
116
+ destobj.generation_time = self.generation_time
117
+
118
+ def copyTo(self, target_device_idx):
119
+ '''
120
+ Duplicate a data object on another device,
121
+ alllocating all CPU/GPU arrays on the new device.
122
+ '''
123
+ if target_device_idx == self.target_device_idx:
124
+ return self
125
+ else:
126
+ cloned = copy(self)
127
+
128
+ if target_device_idx >= 0:
129
+ cloned.xp = cp
130
+ else:
131
+ cloned.xp = np
132
+ cloned.target_device_idx = target_device_idx
133
+
134
+ self.transferDataTo(cloned, force_reallocation=True)
135
+ return cloned