COMPASS  5.4.4
End-to-end AO simulation tool using GPU acceleration
calibration.py
1 
37 import numpy as np
38 from rich.progress import track
39 
40 class Calibration(object):
41  """ This optimizer class handles all the modal basis and DM Influence functions
42  related operations.
43 
44  Attributes:
45  _config : (config) : Configuration parameters module
46 
47  _tel : (TelescopeCompass) : TelescopeCompass instance
48 
49  _atmos : (AtmosScompass) : AtmosCompass instance
50 
51  _dms : (DmCompass) : DmCompass instance
52 
53  _target : (TargetCompass) : TargetCompass instance
54 
55  _rtc : (RtcCompass) : RtcCompass instance
56 
57  _wfs : (WfsCompass) : WfsCompass instance
58  """
59  def __init__(self, config, tel, atmos, dms, target, rtc, wfs):
60  """ Instantiate a ModalBasis object
61 
62  Args:
63  config : (config) : Configuration parameters module
64 
65  tel : (TelescopeCompass) : TelescopeCompass instance
66 
67  atmos : (AtmosScompass) : AtmosCompass instance
68 
69  dms : (DmCompass) : DmCompass instance
70 
71  target : (TargetCompass) : TargetCompass instance
72 
73  rtc : (RtcCompass) : RtcCompass instance
74 
75  wfs : (WfsCompass) : WfsCompass instance
76  """
77  self._config_config = config
78  self._tel_tel = tel
79  self._atmos_atmos = atmos
80  self._dms_dms = dms
81  self._target_target = target
82  self._rtc_rtc = rtc
83  self._wfs_wfs = wfs
84 
85  def apply_volts_and_get_slopes(self, controller_index: int, *, noise: bool = False,
86  turbu: bool = False, reset: bool = True):
87  """ Apply voltages, raytrace, compute WFS image, compute slopes and returns it
88 
89  Args:
90  controller_index : (int) : Controller index
91 
92  Kwargs:
93  noise : (bool) : Flag to enable noise for WFS image computation. Default is False
94 
95  turbu : (bool) : Flag to enable atmosphere for WFS phase screen raytracing.
96  Default is False
97 
98  reset : (bool) : Flag to reset previous phase screen before raytracing.
99  Default is True
100  """
101  self._rtc_rtc.apply_control(controller_index)
102  for w in range(len(self._config_config.p_wfss)):
103  if (turbu):
104  self._wfs_wfs.raytrace(w, tel=self._tel_tel, atm=self._atmos_atmos, dms=self._dms_dms)
105  else:
106  self._wfs_wfs.raytrace(w, dms=self._dms_dms, reset=reset)
107  self._wfs_wfs.compute_wfs_image(w, noise=noise)
108  return self._rtc_rtc.compute_slopes(controller_index)
109 
110  def do_imat_modal(self, controller_index : int, ampli : np.ndarray, modal_basis : np.ndarray,
111  *, noise : bool=False, nmodes_max : int=0, with_turbu : bool=False, push_pull : bool=False) -> np.ndarray:
112  """ Computes an interaction matrix from provided modal basis
113 
114  Args:
115  controller_index : (int) : Controller index
116 
117  ampli : (np.ndarray) : amplitude to apply on each mode
118 
119  modal_basis : (np.ndarray) : modal basis matrix
120 
121  Kwargs:
122  noise : (bool) : Flag to enable noise for WFS image compuation. Default is False
123 
124  nmodes_max : (int) : Default is 0. TODO : description
125 
126  with_turbu : (bool) : Flag to enable atmosphere for WFS phase screen raytracing.
127  Default is False
128 
129  push_pull : (bool) : If True, imat is computed as an average of push and pull ampli
130  on each mode
131 
132  Returns:
133  modal_imat : (np.ndarray) : Modal interaction matrix
134  """
135  modal_imat = np.zeros((self._config_config.p_controllers[controller_index].nslope, modal_basis.shape[1]))
136  print("Starting Modal imat...")
137  if (nmodes_max == 0):
138  nmodes_max = modal_basis.shape[1]
139  v_old = self._rtc_rtc.get_command(controller_index)
140  self._rtc_rtc.open_loop(controller_index, reset=False)
141  for m in track(range(nmodes_max)):
142  v = ampli[m] * modal_basis[:, m]
143  if ((push_pull is True) or
144  (with_turbu is True)): # with turbulence/aberrations => push/pull
145  self._rtc_rtc.set_perturbation_voltage(
146  controller_index, "imat_modal",
147  v_old + v) # Adding Perturbation voltage on current iteration
148  devpos = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index,
149  turbu=with_turbu, noise=noise)
150  self._rtc_rtc.set_perturbation_voltage(controller_index, "imat_modal", v_old - v)
151  devmin = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index,
152  turbu=with_turbu, noise=noise)
153  modal_imat[:, m] = (devpos - devmin) / (2. * ampli[m])
154  else: # No turbulence => push only
155  self._rtc_rtc.open_loop(controller_index) # open_loop
156  self._rtc_rtc.set_perturbation_voltage(controller_index, "imat_modal", v)
157  modal_imat[:, m] = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index, noise=noise) / ampli[m]
158  self._rtc_rtc.remove_perturbation_voltage(controller_index, "imat_modal")
159  if ((push_pull is True) or (with_turbu is True)):
160  self._rtc_rtc.close_loop(controller_index) # We are supposed to be in close loop now
161  return modal_imat
162 
163  def do_imat_phase(self, controller_index: int, cube_phase: np.ndarray, *, noise : bool=False,
164  nmodes_max : int=0, with_turbu : bool=False, push_pull : bool=False, wfs_index : int=0) -> np.ndarray:
165  """ Computes an interaction matrix with the provided cube phase
166 
167  Args:
168  controller_index : (int) : Controller index
169 
170  cube_phase : (np.ndarray) : Cube of phase to insert as NCPA
171 
172  Kwargs:
173  noise : (bool) : Flag to enable noise for WFS image compuation. Default is False
174 
175  nmodes_max : (int) : Default is 0. TODO : description
176 
177  with_turbu : (bool) : Flag to enable atmosphere for WFS phase screen raytracing.
178  Default is False
179 
180  push_pull : (bool) : If True, imat is computed as an average of push and pull ampli
181  on each mode
182 
183  wfs_index : (int) : WFS index. Default is 0
184 
185  Returns:
186  phase_imat : (np.ndarray) : Phase interaction matrix
187  """
188  imat_phase = np.zeros((cube_phase.shape[0], self._config_config.p_controllers[controller_index].nslope))
189  for nphase in range(cube_phase.shape[0]):
190  if ((push_pull is True) or (with_turbu is True)
191  ): # with turbulence/aberrations => push/pullADOPT/projects/cosmic/
192  self._wfs_wfs.set_ncpa_wfs(wfs_index, cube_phase[nphase, :, :])
193  devpos = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index,
194  turbu=with_turbu, noise=noise)
195  self._wfs_wfs.set_ncpa_wfs(wfs_index, -cube_phase[nphase, :, :])
196  devmin = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index,
197  turbu=with_turbu, noise=noise)
198  imat_phase[nphase, :] = (devpos - devmin) / 2
199  else: # No turbulence => push only
200  self._rtc_rtc.open_loop(controller_index) # open_loop
201  self._wfs_wfs.set_ncpa_wfs(wfs_index, cube_phase[nphase, :, :])
202  imat_phase[nphase, :] = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(
203  controller_index, noise=noise)
204  self._wfs_wfs.set_ncpa_wfs(wfs_index,
205  cube_phase[nphase, :, :] * 0.) # Remove the Phase on WFS
206  _ = self.apply_volts_and_get_slopesapply_volts_and_get_slopes(controller_index, turbu=with_turbu,
207  noise=noise)
208 
209  return imat_phase
210 
211  def compute_modal_residuals(self, projection_matrix : np.ndarray,
212  *, selected_actus : np.ndarray=None) -> np.ndarray:
213  """ Computes the modal residual coefficients of the residual phase.
214 
215  It supposed that roket is enabled, and the associated GEO controller is index 1.
216 
217  Uses the projection matrix computed from compute_modes_to_volts_basis (modalBasis module)
218 
219  Args:
220  projection_matrix : (np.ndarray) : Modal projection matrix
221 
222  Kwargs:
223  selected_actus : (np.ndarray) : TODO : description
224 
225  Returns:
226  ai : (np.ndarray) : Modal coefficients
227  """
228  try:
229  self._rtc_rtc.do_control(1, sources=self._target_target.sources)
230  except:
231  return [0]
232  v = self._rtc_rtc.get_command(1) # We compute here the residual phase on the DM modes. Gives the Equivalent volts to apply/
233  if (selected_actus is None):
234  ai = projection_matrix.dot(v) * 1000. # np rms units
235  else: # Slaving actus case
236  v2 = v[:-2][list(
237  selected_actus)] # If actus are slaved then we select them.
238  v3 = v[-2:]
239  ai = projection_matrix.dot(np.concatenate((v2, v3))) * 1000.
240  return ai
This optimizer class handles all the modal basis and DM Influence functions related operations.
Definition: calibration.py:45
np.ndarray compute_modal_residuals(self, np.ndarray projection_matrix, *np.ndarray selected_actus=None)
Computes the modal residual coefficients of the residual phase.
Definition: calibration.py:258
def __init__(self, config, tel, atmos, dms, target, rtc, wfs)
Definition: calibration.py:96
np.ndarray do_imat_phase(self, int controller_index, np.ndarray cube_phase, *bool noise=False, int nmodes_max=0, bool with_turbu=False, bool push_pull=False, int wfs_index=0)
Computes an interaction matrix with the provided cube phase.
Definition: calibration.py:215
def apply_volts_and_get_slopes(self, int controller_index, *bool noise=False, bool turbu=False, bool reset=True)
Apply voltages, raytrace, compute WFS image, compute slopes and returns it.
Definition: calibration.py:124
np.ndarray do_imat_modal(self, int controller_index, np.ndarray ampli, np.ndarray modal_basis, *bool noise=False, int nmodes_max=0, bool with_turbu=False, bool push_pull=False)
Computes an interaction matrix from provided modal basis.
Definition: calibration.py:160