OOPAO 1.0.0__py3-none-any.whl

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 (63) hide show
  1. OOPAO/Asterism.py +287 -0
  2. OOPAO/Atmosphere.py +1457 -0
  3. OOPAO/BioEdge.py +1363 -0
  4. OOPAO/DeformableMirror.py +949 -0
  5. OOPAO/Detector.py +528 -0
  6. OOPAO/FieldTransformer.py +178 -0
  7. OOPAO/GainSensingCamera.py +205 -0
  8. OOPAO/InfluenceFunctions.py +123 -0
  9. OOPAO/LiFT.py +349 -0
  10. OOPAO/MisRegistration.py +133 -0
  11. OOPAO/NCPA.py +187 -0
  12. OOPAO/OPD_map.py +51 -0
  13. OOPAO/Pyramid.py +1308 -0
  14. OOPAO/SPRINT.py +246 -0
  15. OOPAO/ShackHartmann.py +1301 -0
  16. OOPAO/Source.py +548 -0
  17. OOPAO/SpatialFilter.py +95 -0
  18. OOPAO/Telescope.py +663 -0
  19. OOPAO/Zernike.py +148 -0
  20. OOPAO/__init__.py +66 -0
  21. OOPAO/calibration/CalibrationVault.py +142 -0
  22. OOPAO/calibration/InteractionMatrix.py +336 -0
  23. OOPAO/calibration/__init__.py +7 -0
  24. OOPAO/calibration/ao_calibration.py +384 -0
  25. OOPAO/calibration/ao_cockpit_psim.py +2163 -0
  26. OOPAO/calibration/compute_KL_modal_basis.py +333 -0
  27. OOPAO/calibration/getFittingError.py +81 -0
  28. OOPAO/calibration/get_fast_atmosphere.py +42 -0
  29. OOPAO/calibration/get_modal_basis.py +159 -0
  30. OOPAO/calibration/initialization_AO.py +110 -0
  31. OOPAO/calibration/initialization_AO_PWFS.py +155 -0
  32. OOPAO/calibration/initialization_AO_SHWFS.py +176 -0
  33. OOPAO/closed_loop/__init__.py +6 -0
  34. OOPAO/closed_loop/run_cl.py +330 -0
  35. OOPAO/closed_loop/run_cl_first_stage.py +224 -0
  36. OOPAO/closed_loop/run_cl_from_phase_screens.py +179 -0
  37. OOPAO/closed_loop/run_cl_long_push_pull.py +283 -0
  38. OOPAO/closed_loop/run_cl_sinusoidal_modulation.py +525 -0
  39. OOPAO/closed_loop/run_cl_two_stages.py +248 -0
  40. OOPAO/closed_loop/run_cl_two_stages_atm_change.py +300 -0
  41. OOPAO/mis_registration_identification_algorithm/__init__.py +7 -0
  42. OOPAO/mis_registration_identification_algorithm/applyMisRegistration.py +159 -0
  43. OOPAO/mis_registration_identification_algorithm/computeMetaSensitivityMatrix_Pyramid.py +210 -0
  44. OOPAO/mis_registration_identification_algorithm/computeMetaSensitivyMatrix.py +248 -0
  45. OOPAO/mis_registration_identification_algorithm/estimateMisRegistration.py +232 -0
  46. OOPAO/mis_registration_identification_algorithm/estimateMisRegistration_Pyramid.py +188 -0
  47. OOPAO/phaseStats.py +285 -0
  48. OOPAO/runtime.py +140 -0
  49. OOPAO/tools/OopaoGUI.py +123 -0
  50. OOPAO/tools/__init__.py +7 -0
  51. OOPAO/tools/displayTools.py +523 -0
  52. OOPAO/tools/gpuTransforms.py +52 -0
  53. OOPAO/tools/interpolateGeometricalTransformation.py +373 -0
  54. OOPAO/tools/interpolate_influence_functions.py +100 -0
  55. OOPAO/tools/separableInterpolation.py +108 -0
  56. OOPAO/tools/set_paralleling_setup.py +126 -0
  57. OOPAO/tools/tools.py +785 -0
  58. OOPAO/tools/user_defined_influence_functions.py +565 -0
  59. oopao-1.0.0.dist-info/METADATA +182 -0
  60. oopao-1.0.0.dist-info/RECORD +63 -0
  61. oopao-1.0.0.dist-info/WHEEL +5 -0
  62. oopao-1.0.0.dist-info/licenses/LICENSE +674 -0
  63. oopao-1.0.0.dist-info/top_level.txt +1 -0
OOPAO/Asterism.py ADDED
@@ -0,0 +1,287 @@
1
+ # -*- coding: utf-8 -*-
2
+ """
3
+ Created on Tue Aug 23 14:35:32 2022
4
+
5
+ @author: cheritie
6
+ """
7
+ import numpy as np
8
+ import matplotlib.pyplot as plt
9
+ from OOPAO.tools.displayTools import makeSquareAxes
10
+ from OOPAO.runtime import array_backend
11
+
12
+ _xp, _gpu_flag = array_backend()
13
+
14
+
15
+ def _on_gpu(items):
16
+ return _gpu_flag and any(isinstance(item, _xp.ndarray) for item in items)
17
+
18
+
19
+ def _stack(items):
20
+ """np.array(items), or a CuPy stack when the sources hold GPU-resident arrays (np.array would refuse them)."""
21
+ if _on_gpu(items):
22
+ return _xp.stack([_xp.asarray(item) for item in items])
23
+ return np.array(items)
24
+
25
+
26
+ def _object_array(items):
27
+ """np.array(items, dtype=object); with GPU-resident arrays, a 1D object array holding them."""
28
+ if _on_gpu(items):
29
+ out = np.empty(len(items), dtype=object)
30
+ for i, item in enumerate(items):
31
+ out[i] = item
32
+ return out
33
+ return np.array(items, dtype=object)
34
+
35
+
36
+ class Asterism:
37
+ def __init__(self,
38
+ list_src: list):
39
+ """
40
+ ************************** REQUIRED PARAMETERS **************************
41
+
42
+ An Asterism object is an asterism of Source objects. It requires the following parameters:
43
+
44
+ _ list_src : a list of Source objects that can combine NGS and LGS types
45
+
46
+ ************************** COUPLING AN ASTERISM OBJECT **************************
47
+
48
+ Once generated, an asterism object "ast" can be coupled to a Telescope "tel" that contains the OPD.
49
+ _ This is achieved using the * operator : ast*tel
50
+ _ It can be accessed using : tel.src
51
+
52
+
53
+ ************************** MAIN PROPERTIES **************************
54
+
55
+ The main properties of a Telescope object are listed here:
56
+ _ ast.coordinates : coordinates of the source objects
57
+ _ ast.altitude : altitude of the source objects
58
+ _ ast.nPhoton : nPhoton property of the source objects
59
+
60
+ ************************** EXEMPLE **************************
61
+
62
+ Create a list of source object in H band with a magnitude 8 and combine it to the telescope
63
+
64
+ src_1 = Source(opticalBand = 'H', magnitude = 8, coordinates=[0,0])
65
+ src_2 = Source(opticalBand = 'H', magnitude = 8, coordinates=[60,0])
66
+ src_3 = Source(opticalBand = 'H', magnitude = 8, coordinates=[60,120])
67
+ src_4 = Source(opticalBand = 'H', magnitude = 8, coordinates=[60,240])
68
+
69
+ ast = Asterism([src_1,src_2,src_3, src_4])
70
+
71
+
72
+ """
73
+
74
+ self.n_source = len(list_src)
75
+ self.src = list_src
76
+
77
+ for i, src in enumerate(self.src):
78
+ src.inAsterism = True
79
+ src.ast_idx = i
80
+ self.chromatic_shift = None
81
+ print(self)
82
+ self.tag = 'asterism'
83
+ self.type = 'asterism'
84
+ self.wavelength = self.src[0].wavelength
85
+
86
+ @property
87
+ def laser_coordinates(self):
88
+ _laser_coordinates = []
89
+ for src in self.src:
90
+ _laser_coordinates.append(src.laser_coordinates)
91
+ return _laser_coordinates
92
+
93
+ @property
94
+ def Na_profile(self):
95
+ _Na_profile = []
96
+ for src in self.src:
97
+ _Na_profile.append(src.Na_profile)
98
+ return _Na_profile
99
+
100
+ @property
101
+ def fwhm_spot_up(self):
102
+ _fwhm_spot_up = []
103
+ for src in self.src:
104
+ _fwhm_spot_up.append(src.fwhm_spot_up)
105
+ return _fwhm_spot_up
106
+
107
+ @property
108
+ def fluxMap(self):
109
+ _fluxMap = []
110
+ for src in self.src:
111
+ _fluxMap.append(src.fluxMap)
112
+ return _fluxMap
113
+
114
+ @property
115
+ def phase(self):
116
+ _phase = []
117
+ for src in self.src:
118
+ _phase.append(src.phase)
119
+ return _phase
120
+
121
+ @phase.setter
122
+ def phase(self, val):
123
+ for src in self.src:
124
+ src.phase = val[src.ast_idx]
125
+
126
+ @property
127
+ def phase_no_pupil(self):
128
+ _phase_no_pupil = []
129
+ for src in self.src:
130
+ _phase_no_pupil.append(src.phase_no_pupil)
131
+ return _phase_no_pupil
132
+
133
+ @property
134
+ def coordinates(self):
135
+ _coordinates = []
136
+ for src in self.src:
137
+ _coordinates.append(src.coordinates)
138
+ return _coordinates
139
+
140
+ @property
141
+ def altitude(self):
142
+ _altitude = []
143
+ for src in self.src:
144
+ _altitude.append(src.altitude)
145
+ return _altitude
146
+
147
+ @property
148
+ def nPhoton(self):
149
+ _nPhoton = []
150
+ for src in self.src:
151
+ _nPhoton.append(src.nPhoton)
152
+ return _nPhoton
153
+
154
+ @property
155
+ def OPD(self):
156
+ _OPD = []
157
+ for src in self.src:
158
+ _OPD.append(src.OPD)
159
+ return _stack(_OPD)
160
+
161
+ @property
162
+ def OPD_no_pupil(self):
163
+ _OPD_no_pupil = []
164
+ for src in self.src:
165
+ _OPD_no_pupil.append(src.OPD_no_pupil)
166
+ return _stack(_OPD_no_pupil)
167
+
168
+ @property
169
+ def scintillation(self):
170
+ _scintillation = []
171
+ for src in self.src:
172
+ _scintillation.append(getattr(src, 'scintillation', None))
173
+ return _object_array(_scintillation)
174
+
175
+ @scintillation.setter
176
+ def scintillation(self, val):
177
+ for src in self.src:
178
+ src.scintillation = val[src.ast_idx]
179
+
180
+ @property
181
+ def scintillation_no_pupil(self):
182
+ _scintillation_no_pupil = []
183
+ for src in self.src:
184
+ _scintillation_no_pupil.append(getattr(src, 'scintillation_no_pupil', None))
185
+ return _object_array(_scintillation_no_pupil)
186
+
187
+ @property
188
+ def intensity(self):
189
+ # Aggregated field intensity (fluxMap * scintillation) of each source.
190
+ # This is the energy-bearing quantity; the per-source intensity is kept
191
+ # as a SINGLE array on each Source so a FieldTransformer warp conserves
192
+ # energy (no W(flux)*W(scint) cross-term). dtype=object mirrors the
193
+ # scintillation aggregation since sources may have None / differently
194
+ # shaped intensities (e.g. before propagation, or NGS vs LGS).
195
+ _intensity = []
196
+ for src in self.src:
197
+ _intensity.append(getattr(src, 'intensity', None))
198
+ return _object_array(_intensity)
199
+
200
+ @intensity.setter
201
+ def intensity(self, val):
202
+ # Distribute a per-source intensity back to each Source so a
203
+ # FieldTransformer can write warped intensities the same way it does
204
+ # for scintillation. After such a write the per-source fluxMap /
205
+ # scintillation are stale relative to intensity -- intensity is the
206
+ # authoritative amplitude path (amplitude = sqrt(intensity)).
207
+ for src in self.src:
208
+ src.intensity = val[src.ast_idx]
209
+
210
+ def __pow__(self, obj):
211
+ # Re-propagation function. Same as .* in OOMAO
212
+ obj.src = self
213
+ for src in self.src:
214
+ src.optical_path = [[src.type + '('+src.optBand+')', src]]
215
+ self.reset()
216
+ self*obj
217
+ return self
218
+
219
+ def __mul__(self, obj):
220
+ # Propagation function.
221
+ if obj is not None:
222
+ if isinstance(obj, list):
223
+ for i in range(len(obj)):
224
+ self*obj[i]
225
+ else:
226
+ obj.relay(self)
227
+ return self
228
+
229
+ def reset(self):
230
+ for src in self.src:
231
+ src.reset()
232
+
233
+ def print_optical_path(self):
234
+ for src in self.src:
235
+ src.print_optical_path()
236
+
237
+ # for backward compatibility
238
+ def print_properties(self):
239
+ print(self)
240
+
241
+ def display_asterism(self):
242
+ plt.figure()
243
+ x = np.linspace(0, 2*np.pi, 100, endpoint=True)
244
+ r = np.linspace(0, max(self.coordinates)[0], 6, endpoint=True)
245
+ for i_circle in range(1, 6):
246
+ plt.plot(r[i_circle]*np.cos(x), r[i_circle]*np.sin(x), '--k')
247
+ cm = plt.get_cmap('gist_rainbow')
248
+ for i_src in range(self.n_source):
249
+ if self.src[i_src].type == "NGS":
250
+ marker = '*'
251
+ size_marker = 20
252
+ else:
253
+ marker = '*'
254
+ size_marker = 10
255
+ plt.plot(self.src[i_src].coordinates[0]*np.cos(np.deg2rad(self.src[i_src].coordinates[1])),
256
+ self.src[i_src].coordinates[0]*np.sin(np.deg2rad(self.src[i_src].coordinates[1])),
257
+ marker,
258
+ markersize=size_marker,
259
+ color=cm(1.*i_src/self.n_source),
260
+ markeredgecolor='k',
261
+ label=self.src[i_src].type+'_'+str(i_src))
262
+ makeSquareAxes()
263
+ plt.xlabel('[Arcsec]')
264
+ plt.ylabel('[Arcsec]')
265
+ plt.legend()
266
+
267
+ def properties(self) -> dict:
268
+ self.prop = dict()
269
+ self.prop['parameters'] = f"{'Source':^8s}|{'Wavelength':^12s}|{'Zenith':^8s}|{'Azimuth':^9s}|{'Altitude':^10s}|{'Magnitude':^11s}|{'Flux':^11s}|"
270
+ self.prop['units'] = f"{'':^8s}|{'[m]':^12s}|{'[arcsec]':^8s}|{'[°]':^9s}|{'[m]':^10s}|{'':^11s}|{'[ph/m²/s]':^11s}|"
271
+ for i in range(self.n_source):
272
+ if i % 2 == 0:
273
+ self.prop['layer_%02d'%i] = f"\033[00m{'%3d-%s'%(i+1,self.src[i].type):^8s}|{self.src[i].wavelength:^12.1e}|{self.src[i].coordinates[0]:^8.2f}|{self.src[i].coordinates[1]:^9.2f}|{self.src[i].altitude:^10.2f}|{self.src[i]._magnitude:^11.2f}|{self.src[i]._nPhoton:^11.1e}|"
274
+ else:
275
+ self.prop['layer_%02d'%i] = f"\033[47m{'%3d-%s'%(i+1,self.src[i].type):^8s}|{self.src[i].wavelength:^12.1e}|{self.src[i].coordinates[0]:^8.2f}|{self.src[i].coordinates[1]:^9.2f}|{self.src[i].altitude:^10.2f}|{self.src[i]._magnitude:^11.2f}|{self.src[i]._nPhoton:^11.1e}|"
276
+ return self.prop
277
+
278
+ def __repr__(self):
279
+ self.properties()
280
+ str_prop = str()
281
+ n_char = len(max(self.prop.values(), key=len)) - len('\033[00m')
282
+ for i in range(len(self.prop.values())):
283
+ str_prop += list(self.prop.values())[i] + '\n'
284
+ title = f'\n{" Asterism ":-^{n_char}}\n'
285
+ end_line = f'\033[00m{"":-^{n_char}}\n'
286
+ table = title + str_prop + end_line
287
+ return table