42 from scipy.sparse.csr
import csr_matrix
46 """ This optimizer class handles all the modal basis and DM Influence functions
50 config : (config) : Configuration parameters module
52 dms : (DmCompass) : DmCompass instance
54 target : (TargetCompass) : TargetCompass instance
56 slaved_actus : TODO : docstring
58 selected_actus : TODO : docstring
60 couples_actus : TODO : docstring
62 index_under_spiders : TODO : docstring
64 modal_basis : (np.ndarray) : Last modal basis computed
66 projection_matrix : (np.ndarray) : Last projection_matrix computed
68 def __init__(self, config, dms, target):
69 """ Instantiate a ModalBasis object
72 config : (config) : Configuration parameters module
74 dms : (DmCompass) : DmCompass instance
76 target : (TargetCompass) : TargetCompass instance
89 """ Computes and return the influence function phase basis of the specified DM
93 dm_index : (int) : Index of the DM
96 influ_sparse : (csr_matrix) : influence function phases
98 return basis.compute_dm_basis(self.
dms.dms.d_dms[dm_index],
99 self.
config.p_dms[dm_index],
103 nbpairs: int =
None, return_delta: bool =
False) -> np.ndarray:
104 """ Computes a given modal basis ("KL2V", "Btt", "Btt_petal") and return the 2 transfer matrices
107 modal_basis_type : (str) : modal basis to compute ("KL2V", "Btt", "Btt_petal")
109 merged : (bool, optional) :
111 nbpairs : (int, optional) :
114 modal_basis : (np.ndarray) : modes to volts matrix
116 projection_matrix : (np.ndarray) : volts to modes matrix (None if "KL")
118 if (modal_basis_type ==
"KL2V"):
119 print(
"Computing KL2V basis...")
121 self.
config.p_controllers[0], self.
dms.dms,
124 fnz = util.first_non_zero(self.
modal_basis, axis=0)
131 projection_matrix =
None
132 elif (modal_basis_type ==
"Btt"):
133 print(
"Computing Btt basis...")
135 merged=merged, nbpairs=nbpairs,
136 return_delta=return_delta)
137 fnz = util.first_non_zero(self.
modal_basis, axis=0)
144 elif (modal_basis_type ==
"Btt_petal"):
145 print(
"Computing Btt with a Petal basis...")
148 raise ArgumentError(
"Unsupported modal basis")
153 return_delta: bool =
False) -> np.ndarray:
154 """ Computes the so-called Btt modal basis. The <merged> flag allows merto merge
155 2x2 the actuators influence functions for actuators on each side of the spider (ELT case)
158 merged : (bool, optional) : If True, merge 2x2 the actuators influence functions for
159 actuators on each side of the spider (ELT case). Default
162 nbpairs : (int, optional) : Default is None. TODO : description
164 return_delta : (bool, optional) : If False (default), the function returns
165 Btt (modes to volts matrix),
166 and P (volts to mode matrix).
167 If True, returns delta = IF.T.dot(IF) / N
171 Btt : (np.ndarray) : Btt modes to volts matrix
173 projection_matrix : (np.ndarray) : volts to Btt modes matrix
175 dms_basis = basis.compute_IFsparse(self.
dms.dms, self.
config.p_dms, self.
config.p_geom)
176 influ_basis = dms_basis[:-2,:]
177 tt_basis = dms_basis[-2:,:].toarray()
181 influ_basis2 = influ_basis.copy()
182 index_remove = index_under_spiders.copy()
183 index_remove += list(couples_actus[:, 1])
184 print(
"Pairing Actuators...")
185 for i
in tqdm(range(couples_actus.shape[0])):
186 influ_basis2[couples_actus[i, 0], :] += influ_basis2[
187 couples_actus[i, 1], :]
188 print(
"Pairing Done")
189 boolarray = np.zeros(influ_basis2.shape[0], dtype=np.bool)
190 boolarray[index_remove] =
True
195 influ_basis2 = influ_basis2[~boolarray, :]
196 influ_basis = influ_basis2
198 self.
btt, self.
projection_matrix = basis.compute_btt(influ_basis.T, tt_basis.T, return_delta=return_delta)
201 btt2 = np.zeros((len(boolarray) + 2, self.
btt.shape[1]))
202 btt2[np.r_[~boolarray,
True,
True], :] = self.
btt
203 btt2[couples_actus[:, 1], :] = btt2[couples_actus[:, 0], :]
205 P2 = np.zeros((self.
btt.shape[1], len(boolarray) + 2))
207 P2[:, couples_actus[:, 1]] = P2[:, couples_actus[:, 0]]
214 """ Used to compute merged IF from each side of the spider
215 for an ELT case (Petalling Effect)
218 dm_index : (int) : DM index
220 nbpairs : (int, optional) : Default is None. TODO : description
223 pairs : (np.ndarray) : TODO description
225 discard : (list) : TODO description
227 p_geom = self.
config.p_geom
230 cent = p_geom.pupdiam / 2. + 0.5
232 p_tel.t_spiders = 0.51
233 spup = mkP.make_pupil(p_geom.pupdiam, p_geom.pupdiam, p_tel, cent,
234 cent).astype(np.float32).T
237 spup2 = mkP.make_pupil(p_geom.pupdiam, p_geom.pupdiam, p_tel, cent,
238 cent).astype(np.float32).T
240 spiders = spup2 - spup
242 (spidersID, k) = scipy.ndimage.label(spiders)
243 spidersi = util.pad_array(spidersID, p_geom.ssize).astype(np.float32)
244 px_list_spider = [np.where(spidersi == i)
for i
in range(1, k + 1)]
247 dm_posx = self.
config.p_dms[dm_index]._xpos - 0.5
248 dm_posy = self.
config.p_dms[dm_index]._ypos - 0.5
249 dm_pos_mat = np.c_[dm_posx, dm_posy].T
251 pitch = self.
config.p_dms[dm_index]._pitch
252 discard = np.zeros(len(dm_posx), dtype=np.bool)
256 for k, px_list
in enumerate(px_list_spider):
257 pts = np.c_[px_list[1],
261 line_eq = np.linalg.pinv(pts).dot(np.ones(pts.shape[0]))
262 aa, bb = line_eq[0], line_eq[1]
266 if np.abs(bb) < np.abs(aa):
267 one_point = np.array([1 / aa, 0.])
269 one_point = np.array([0., 1 / bb])
272 rotation = np.array([[-bb, aa], [-aa, -bb]]) / (aa**2 + bb**2)**.5
275 rotated_px = rotation.dot(pts.T - one_point[:,
None])
278 min_u, max_u = rotated_px[0].min() - 5. * pitch, rotated_px[0].max(
282 rotated_actus = rotation.dot(dm_pos_mat - one_point[:,
None])
283 sel_good_side = (rotated_actus[0] > min_u) & (rotated_actus[0] < max_u)
286 sel_discard = (np.abs(rotated_actus[1]) < threshold * pitch) & sel_good_side
287 discard |= sel_discard
290 sel_pairable = (np.abs(rotated_actus[1]) > threshold * pitch) & \
291 (np.abs(rotated_actus[1]) < 1. * pitch) & \
294 pairable_index = np.where(sel_pairable)[0]
295 u_coord = rotated_actus[
298 order = np.sort(u_coord)
299 order_index = pairable_index[np.argsort(
304 if (nbpairs
is None):
308 i = len(order) // 2 - nbpairs
309 ii = len(order) // 2 + nbpairs
313 if np.abs(order[i] - order[i + 1]) < .2 * pitch:
314 pairs += [(order_index[i], order_indx[i + 1])]
318 print(
'To discard: %u actu' % np.sum(discard))
319 print(
'%u pairs to slave' % len(pairs))
320 if np.sum(discard) == 0:
323 list(np.where(discard)[0])
324 return np.asarray(pairs), list(np.where(discard)[0])
327 """ Computes a Btt modal basis with Pistons filtered
330 Btt : (np.ndarray) : Btt modes to volts matrix
332 P : (np.ndarray) : volts to Btt modes matrix
334 pzt_index = np.where([d.type
is scons.DmType.PZT
for d
in self.
config.p_dms])[0][0]
336 petal_dm_index = np.where([
337 d.influ_type
is scons.InfluType.PETAL
for d
in self.
config.p_dms
340 tt_index = np.where([d.type
is scons.DmType.TT
for d
in self.
config.p_dms])[0][0]
347 """ Return the phase to modes matrix by using the given modal basis
350 modal_basis : (np.ndarray) : Modal basis matrix
353 phase_to_modes : (np.ndarray) : phase to modes matrix
355 nbmode = modal_basis.shape[1]
356 phase = self.
target.get_tar_phase(0)
357 phase_to_modes = np.zeros((nbmode, phase.shape[0], phase.shape[1]))
358 S = np.sum(self.
config.p_geom._spupil)
359 for i
in range(nbmode):
360 self.
dms.set_command((modal_basis[:, i]).copy())
362 self.
target.raytrace(0, dms=self.
dms, ncpa=
False, reset=
True)
363 phase = self.
target.get_tar_phase(0, pupil=
True)
365 norm = np.sqrt(np.sum((phase)**2) / S)
366 phase_to_modes[i] = phase / norm
367 return phase_to_modes