38 from abc
import abstractmethod
42 from typing
import Iterable
47 """ This class defines generic methods and behavior of a supervisor
48 It is not intended to be instantiated as it is : prefer to build
49 a supervisor class inheriting from it. This approach allows to build multiple
50 supervisors with various components and less effort
53 context : (CarmaContext) : a CarmaContext instance
55 config : (config) : Parameters structure
57 telescope : (TelescopeComponent) : a TelescopeComponent instance
59 atmos : (AtmosComponent) : An AtmosComponent instance
61 target : (targetComponent) : A TargetComponent instance
63 wfs : (WfsComponent) : A WfsComponent instance
65 dms : (DmComponent) : A DmComponent instance
67 rtc : (RtcComponent) : A Rtc component instance
69 is_init : (bool) : Flag equals to True if the supervisor has already been initialized
71 iter : (int) : Frame counter
74 """ Init the a supervisor
77 config : (config module) : Configuration module
92 """ Returns the configuration in use, in a supervisor specific format ?
95 config : (config module) : Current supervisor configuration
100 """Return the current iteration number of the loop
103 framecounter : (int) : Number of iteration already performed
108 """ Active all the GPU devices specified in the parameters file
112 for device
in range(len(self.
config.p_loop.devices)):
116 def _init_components(self) -> None:
117 """ Initialize all the components
119 if (self.
config.p_loop.devices.size > 1):
120 self.
context = carmaWrap_context.get_instance_ngpu(
121 self.
config.p_loop.devices.size, self.
config.p_loop.devices)
123 self.
context = carmaWrap_context.get_instance_1gpu(
124 self.
config.p_loop.devices[0])
127 if self.
config.p_tel
is None or self.
config.p_geom
is None:
128 raise ValueError(
"Telescope geometry must be defined (p_geom and p_tel)")
131 if self.
config.p_atmos
is not None:
135 if self.
config.p_targets
is not None:
137 if self.
config.p_wfss
is not None:
139 if self.
config.p_controllers
is not None or self.
config.p_centroiders
is not None:
146 """ Initialize the telescope component of the supervisor
151 def _init_atmos(self):
152 """ Initialize the atmos component of the supervisor
158 """ Initialize the dms component of the supervisor
163 def _init_target(self):
164 """ Initialize the target component of the supervisor
170 """ Initialize the wfs component of the supervisor
176 """ Initialize the rtc component of the supervisor
180 def next(self, *, move_atmos: bool =
True, nControl: int = 0,
181 tar_trace: Iterable[int] =
None, wfs_trace: Iterable[int] =
None,
182 do_control: bool =
True, apply_control: bool =
True,
183 compute_tar_psf: bool =
True) ->
None:
184 """Iterates the AO loop, with optional parameters
187 move_atmos: (bool), optional: move the atmosphere for this iteration. Default is True
189 nControl: (int, optional): Controller number to use. Default is 0 (single control configuration)
191 tar_trace: (List, optional): list of targets to trace. None is equivalent to all (default)
193 wfs_trace: (List, optional): list of WFS to trace. None is equivalent to all (default)
195 do_control : (bool, optional) : Performs RTC operations if True (Default)
197 apply_control: (bool): (optional) if True (default), apply control on DMs
199 compute_tar_psf : (bool, optional) : If True (default), computes the PSF at the end of the iteration
201 if tar_trace
is None and self.target
is not None:
202 tar_trace = range(len(self.config.p_targets))
203 if wfs_trace
is None and self.wfs
is not None:
204 wfs_trace = range(len(self.config.p_wfss))
206 if move_atmos
and self.atmos
is not None:
207 self.atmos.move_atmos()
209 if tar_trace
is not None:
211 if self.atmos.is_enable:
212 self.target.raytrace(t, tel=self.tel, atm=self.atmos, dms=self.dms)
214 self.target.raytrace(t, tel=self.tel, dms=self.dms)
216 if wfs_trace
is not None:
218 if self.atmos.is_enable:
219 self.wfs.raytrace(w, tel=self.tel, atm=self.atmos)
221 self.wfs.raytrace(w, tel=self.tel)
223 if not self.config.p_wfss[w].open_loop
and self.dms
is not None:
224 self.wfs.raytrace(w, dms=self.dms, reset=
False)
225 self.wfs.compute_wfs_image(w)
226 if do_control
and self.rtc
is not None:
227 for ncontrol
in range(len(self.config.p_controllers)):
228 self.rtc.do_centroids(ncontrol)
229 self.rtc.do_control(ncontrol)
230 self.rtc.do_clipping(ncontrol)
233 self.rtc.apply_control(ncontrol)
236 for tar_index
in tar_trace:
237 self.target.comp_tar_image(tar_index)
238 self.target.comp_strehl(tar_index)
242 def _print_strehl(self, monitoring_freq: int, iters_time: float, total_iters: int,
243 *, tar_index: int=0):
244 """ Print the Strehl ratio SE and LE from a target on the terminal, the estimated remaining time and framerate
247 monitoring_freq : (int) : Number of frames between two prints
249 iters_time : (float) : time elapsed between two prints
251 total_iters : (int) : Total number of iterations
253 tar_index : (int, optional) : Index of the target. Default is 0
255 framerate = monitoring_freq / iters_time
256 strehl = self.
target.get_strehl(tar_index)
257 etr = (total_iters - self.
iter) / framerate
258 print(
"%d \t %.3f \t %.3f\t %.1f \t %.1f" % (self.
iter + 1, strehl[0], strehl[1],
261 def loop(self, number_of_iter: int, *, monitoring_freq: int = 100, compute_tar_psf: bool =
True,
263 """ Perform the AO loop for <number_of_iter> iterations
266 number_of_iter: (int) : Number of iteration that will be done
268 monitoring_freq: (int, optional) : Monitoring frequency [frames]. Default is 100
270 compute_tar_psf : (bool, optional) : If True (default), computes the PSF at each iteration
271 Else, only computes it each <monitoring_freq> frames
273 if not compute_tar_psf:
274 print(
"WARNING: Target PSF will be computed (& accumulated) only during monitoring"
277 print(
"----------------------------------------------------")
278 print(
"iter# | S.E. SR | L.E. SR | ETR (s) | Framerate (Hz)")
279 print(
"----------------------------------------------------")
283 if number_of_iter == -1:
285 self.
next(compute_tar_psf=compute_tar_psf, **kwargs)
286 if ((self.
iter + 1) % monitoring_freq == 0):
287 if not compute_tar_psf:
288 self.comp_tar_image(0)
290 self.
_print_strehl(monitoring_freq, time.time() - t1, number_of_iter)
293 for _
in range(number_of_iter):
294 self.
next(compute_tar_psf=compute_tar_psf, **kwargs)
295 if ((self.
iter + 1) % monitoring_freq == 0):
296 if not compute_tar_psf:
297 self.
target.comp_tar_image(0)
298 self.
target.comp_strehl(0)
299 self.
_print_strehl(monitoring_freq, time.time() - t1, number_of_iter)
302 print(
" loop execution time:", t1 - t0,
" (", number_of_iter,
"iterations), ", (t1 - t0) / number_of_iter,
303 "(mean) ", number_of_iter / (t1 - t0),
"Hz")
306 """ Reset the simulation to return to its original state
308 self.
atmos.reset_turbu()
309 self.
wfs.reset_noise()
310 for tar_index
in range(len(self.
config.p_targets)):
311 self.
target.reset_strehl(tar_index)
314 self.
rtc.close_loop()